From 545240065ab323fabc708435bf535e62d238cdd8 Mon Sep 17 00:00:00 2001
From: James Gowdy <jgowdy@elastic.co>
Date: Mon, 17 Feb 2020 10:47:50 +0000
Subject: [PATCH 001/174] [ML] Categorization examples privilege check (#57375)

* [ML] Categorization examples privilege check

* adding privileges check to endpoint

* fixing typo

* moving privilege check to router

* removing unused variable

* rebasing master

* removing comment

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../ml/server/models/job_service/index.js     |  7 +---
 .../new_job/categorization/examples.ts        |  7 ++--
 .../server/new_platform/job_service_schema.ts | 14 +-------
 .../plugins/ml/server/routes/job_service.ts   | 36 +++++++++++++++++--
 4 files changed, 41 insertions(+), 23 deletions(-)

diff --git a/x-pack/legacy/plugins/ml/server/models/job_service/index.js b/x-pack/legacy/plugins/ml/server/models/job_service/index.js
index 6f409e70e68b8..70b855e80a770 100644
--- a/x-pack/legacy/plugins/ml/server/models/job_service/index.js
+++ b/x-pack/legacy/plugins/ml/server/models/job_service/index.js
@@ -8,11 +8,7 @@ import { datafeedsProvider } from './datafeeds';
 import { jobsProvider } from './jobs';
 import { groupsProvider } from './groups';
 import { newJobCapsProvider } from './new_job_caps';
-import {
-  newJobChartsProvider,
-  categorizationExamplesProvider,
-  topCategoriesProvider,
-} from './new_job';
+import { newJobChartsProvider, topCategoriesProvider } from './new_job';
 
 export function jobServiceProvider(callAsCurrentUser) {
   return {
@@ -21,7 +17,6 @@ export function jobServiceProvider(callAsCurrentUser) {
     ...groupsProvider(callAsCurrentUser),
     ...newJobCapsProvider(callAsCurrentUser),
     ...newJobChartsProvider(callAsCurrentUser),
-    ...categorizationExamplesProvider(callAsCurrentUser),
     ...topCategoriesProvider(callAsCurrentUser),
   };
 }
diff --git a/x-pack/legacy/plugins/ml/server/models/job_service/new_job/categorization/examples.ts b/x-pack/legacy/plugins/ml/server/models/job_service/new_job/categorization/examples.ts
index 76473bd55db7f..ea2c71b04f56d 100644
--- a/x-pack/legacy/plugins/ml/server/models/job_service/new_job/categorization/examples.ts
+++ b/x-pack/legacy/plugins/ml/server/models/job_service/new_job/categorization/examples.ts
@@ -17,7 +17,10 @@ import { ValidationResults } from './validation_results';
 
 const CHUNK_SIZE = 100;
 
-export function categorizationExamplesProvider(callWithRequest: callWithRequestType) {
+export function categorizationExamplesProvider(
+  callWithRequest: callWithRequestType,
+  callWithInternalUser: callWithRequestType
+) {
   const validationResults = new ValidationResults();
 
   async function categorizationExamples(
@@ -109,7 +112,7 @@ export function categorizationExamplesProvider(callWithRequest: callWithRequestT
   }
 
   async function loadTokens(examples: string[], analyzer: CategorizationAnalyzer) {
-    const { tokens }: { tokens: Token[] } = await callWithRequest('indices.analyze', {
+    const { tokens }: { tokens: Token[] } = await callWithInternalUser('indices.analyze', {
       body: {
         ...getAnalyzer(analyzer),
         text: examples,
diff --git a/x-pack/legacy/plugins/ml/server/new_platform/job_service_schema.ts b/x-pack/legacy/plugins/ml/server/new_platform/job_service_schema.ts
index b37fcba737802..deb62678a777c 100644
--- a/x-pack/legacy/plugins/ml/server/new_platform/job_service_schema.ts
+++ b/x-pack/legacy/plugins/ml/server/new_platform/job_service_schema.ts
@@ -6,18 +6,6 @@
 
 import { schema } from '@kbn/config-schema';
 
-const analyzerSchema = {
-  tokenizer: schema.string(),
-  filter: schema.maybe(
-    schema.arrayOf(
-      schema.object({
-        type: schema.string(),
-        stopwords: schema.arrayOf(schema.maybe(schema.string())),
-      })
-    )
-  ),
-};
-
 export const categorizationFieldExamplesSchema = {
   indexPatternTitle: schema.string(),
   query: schema.any(),
@@ -26,7 +14,7 @@ export const categorizationFieldExamplesSchema = {
   timeField: schema.maybe(schema.string()),
   start: schema.number(),
   end: schema.number(),
-  analyzer: schema.object(analyzerSchema),
+  analyzer: schema.any(),
 };
 
 export const chartSchema = {
diff --git a/x-pack/legacy/plugins/ml/server/routes/job_service.ts b/x-pack/legacy/plugins/ml/server/routes/job_service.ts
index 9aa3960e59e4c..5ddbd4cdfd5a5 100644
--- a/x-pack/legacy/plugins/ml/server/routes/job_service.ts
+++ b/x-pack/legacy/plugins/ml/server/routes/job_service.ts
@@ -4,10 +4,13 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+import Boom from 'boom';
 import { schema } from '@kbn/config-schema';
+import { IScopedClusterClient } from 'src/core/server';
 import { licensePreRoutingFactory } from '../new_platform/licence_check_pre_routing_factory';
 import { wrapError } from '../client/error_wrapper';
 import { RouteInitialization } from '../new_platform/plugin';
+import { isSecurityDisabled } from '../lib/security_utils';
 import {
   categorizationFieldExamplesSchema,
   chartSchema,
@@ -21,11 +24,31 @@ import {
 } from '../new_platform/job_service_schema';
 // @ts-ignore no declaration module
 import { jobServiceProvider } from '../models/job_service';
+import { categorizationExamplesProvider } from '../models/job_service/new_job';
 
 /**
  * Routes for job service
  */
 export function jobServiceRoutes({ xpackMainPlugin, router }: RouteInitialization) {
+  async function hasPermissionToCreateJobs(
+    callAsCurrentUser: IScopedClusterClient['callAsCurrentUser']
+  ) {
+    if (isSecurityDisabled(xpackMainPlugin) === true) {
+      return true;
+    }
+
+    const resp = await callAsCurrentUser('ml.privilegeCheck', {
+      body: {
+        cluster: [
+          'cluster:admin/xpack/ml/job/put',
+          'cluster:admin/xpack/ml/job/open',
+          'cluster:admin/xpack/ml/datafeeds/put',
+        ],
+      },
+    });
+    return resp.has_all_requested;
+  }
+
   /**
    * @apiGroup JobService
    *
@@ -545,8 +568,17 @@ export function jobServiceRoutes({ xpackMainPlugin, router }: RouteInitializatio
     },
     licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
       try {
-        const { validateCategoryExamples } = jobServiceProvider(
-          context.ml!.mlClient.callAsCurrentUser
+        // due to the use of the _analyze endpoint which is called by the kibana user,
+        // basic job creation privileges are required to use this endpoint
+        if ((await hasPermissionToCreateJobs(context.ml!.mlClient.callAsCurrentUser)) === false) {
+          throw Boom.forbidden(
+            'Insufficient privileges, the machine_learning_admin role is required.'
+          );
+        }
+
+        const { validateCategoryExamples } = categorizationExamplesProvider(
+          context.ml!.mlClient.callAsCurrentUser,
+          context.ml!.mlClient.callAsInternalUser
         );
         const {
           indexPatternTitle,

From 54f5cc1ce384e6bcc9d431e0b60f095778ab1490 Mon Sep 17 00:00:00 2001
From: Walter Rafelsberger <walter@elastic.co>
Date: Mon, 17 Feb 2020 11:53:42 +0100
Subject: [PATCH 002/174] [ML] Anomaly Detection: Fixes hiding date picker for
 settings pages. (#57544)

- Fixes hiding the global date picker on anomaly detection settings pages.
- Consolidates various timefilter usages for enabling/disabling the date picker into a useTimefilter() hook.
---
 .../application/contexts/kibana/index.ts      |  1 +
 .../contexts/kibana/use_timefilter.test.ts    | 65 +++++++++++++++++++
 .../contexts/kibana/use_timefilter.ts         | 38 +++++++++++
 .../datavisualizer_selector.tsx               |  7 +-
 .../file_based/file_datavisualizer.tsx        |  7 +-
 .../datavisualizer/index_based/page.tsx       | 15 ++---
 .../application/routing/routes/explorer.tsx   |  8 +--
 .../application/routing/routes/jobs_list.tsx  |  8 +--
 .../application/routing/routes/overview.tsx   |  2 +
 .../routing/routes/settings/calendar_list.tsx |  3 +
 .../routes/settings/calendar_new_edit.tsx     |  3 +
 .../routing/routes/settings/filter_list.tsx   |  3 +
 .../routes/settings/filter_list_new_edit.tsx  |  3 +
 .../routing/routes/settings/settings.tsx      |  3 +
 .../routes/timeseriesexplorer.test.tsx        | 30 +--------
 .../routing/routes/timeseriesexplorer.tsx     | 10 +--
 .../settings/calendars/edit/new_calendar.js   |  3 -
 17 files changed, 139 insertions(+), 70 deletions(-)
 create mode 100644 x-pack/legacy/plugins/ml/public/application/contexts/kibana/use_timefilter.test.ts
 create mode 100644 x-pack/legacy/plugins/ml/public/application/contexts/kibana/use_timefilter.ts

diff --git a/x-pack/legacy/plugins/ml/public/application/contexts/kibana/index.ts b/x-pack/legacy/plugins/ml/public/application/contexts/kibana/index.ts
index 7ebbd45fd372a..2add1b6ea161c 100644
--- a/x-pack/legacy/plugins/ml/public/application/contexts/kibana/index.ts
+++ b/x-pack/legacy/plugins/ml/public/application/contexts/kibana/index.ts
@@ -6,3 +6,4 @@
 
 export { useMlKibana, StartServices, MlKibanaReactContextValue } from './kibana_context';
 export { useUiSettings } from './use_ui_settings_context';
+export { useTimefilter } from './use_timefilter';
diff --git a/x-pack/legacy/plugins/ml/public/application/contexts/kibana/use_timefilter.test.ts b/x-pack/legacy/plugins/ml/public/application/contexts/kibana/use_timefilter.test.ts
new file mode 100644
index 0000000000000..98ddd874c695c
--- /dev/null
+++ b/x-pack/legacy/plugins/ml/public/application/contexts/kibana/use_timefilter.test.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 { renderHook } from '@testing-library/react-hooks';
+import { useTimefilter } from './use_timefilter';
+
+jest.mock('./kibana_context', () => ({
+  useMlKibana: () => {
+    return {
+      services: {
+        data: {
+          query: {
+            timefilter: {
+              timefilter: {
+                disableTimeRangeSelector: jest.fn(),
+                disableAutoRefreshSelector: jest.fn(),
+                enableTimeRangeSelector: jest.fn(),
+                enableAutoRefreshSelector: jest.fn(),
+              },
+            },
+          },
+        },
+      },
+    };
+  },
+}));
+
+describe('useTimefilter', () => {
+  test('will not trigger any date picker settings by default', () => {
+    const { result } = renderHook(() => useTimefilter());
+    const timefilter = result.current;
+
+    expect(timefilter.disableTimeRangeSelector).toHaveBeenCalledTimes(0);
+    expect(timefilter.disableAutoRefreshSelector).toHaveBeenCalledTimes(0);
+    expect(timefilter.enableTimeRangeSelector).toHaveBeenCalledTimes(0);
+    expect(timefilter.enableTimeRangeSelector).toHaveBeenCalledTimes(0);
+  });
+
+  test('custom disabled overrides', () => {
+    const { result } = renderHook(() =>
+      useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false })
+    );
+    const timefilter = result.current;
+
+    expect(timefilter.disableTimeRangeSelector).toHaveBeenCalledTimes(1);
+    expect(timefilter.disableAutoRefreshSelector).toHaveBeenCalledTimes(1);
+    expect(timefilter.enableTimeRangeSelector).toHaveBeenCalledTimes(0);
+    expect(timefilter.enableTimeRangeSelector).toHaveBeenCalledTimes(0);
+  });
+
+  test('custom enabled overrides', () => {
+    const { result } = renderHook(() =>
+      useTimefilter({ timeRangeSelector: true, autoRefreshSelector: true })
+    );
+    const timefilter = result.current;
+
+    expect(timefilter.disableTimeRangeSelector).toHaveBeenCalledTimes(0);
+    expect(timefilter.disableAutoRefreshSelector).toHaveBeenCalledTimes(0);
+    expect(timefilter.enableTimeRangeSelector).toHaveBeenCalledTimes(1);
+    expect(timefilter.enableTimeRangeSelector).toHaveBeenCalledTimes(1);
+  });
+});
diff --git a/x-pack/legacy/plugins/ml/public/application/contexts/kibana/use_timefilter.ts b/x-pack/legacy/plugins/ml/public/application/contexts/kibana/use_timefilter.ts
new file mode 100644
index 0000000000000..374e101f63dc8
--- /dev/null
+++ b/x-pack/legacy/plugins/ml/public/application/contexts/kibana/use_timefilter.ts
@@ -0,0 +1,38 @@
+/*
+ * 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 { useEffect } from 'react';
+
+import { useMlKibana } from './kibana_context';
+
+interface UseTimefilterOptions {
+  timeRangeSelector?: boolean;
+  autoRefreshSelector?: boolean;
+}
+
+export const useTimefilter = ({
+  timeRangeSelector,
+  autoRefreshSelector,
+}: UseTimefilterOptions = {}) => {
+  const { services } = useMlKibana();
+  const { timefilter } = services.data.query.timefilter;
+
+  useEffect(() => {
+    if (timeRangeSelector === true) {
+      timefilter.enableTimeRangeSelector();
+    } else if (timeRangeSelector === false) {
+      timefilter.disableTimeRangeSelector();
+    }
+
+    if (autoRefreshSelector === true) {
+      timefilter.enableAutoRefreshSelector();
+    } else if (autoRefreshSelector === false) {
+      timefilter.disableAutoRefreshSelector();
+    }
+  }, [timeRangeSelector, autoRefreshSelector]);
+
+  return timefilter;
+};
diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/datavisualizer_selector.tsx b/x-pack/legacy/plugins/ml/public/application/datavisualizer/datavisualizer_selector.tsx
index ae0c034f972d6..0f56f78c708ee 100644
--- a/x-pack/legacy/plugins/ml/public/application/datavisualizer/datavisualizer_selector.tsx
+++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/datavisualizer_selector.tsx
@@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n';
 
 import { FormattedMessage } from '@kbn/i18n/react';
 import { isFullLicense } from '../license/check_license';
-import { useMlKibana } from '../contexts/kibana';
+import { useTimefilter } from '../contexts/kibana';
 
 import { NavigationMenu } from '../components/navigation_menu';
 
@@ -49,10 +49,7 @@ function startTrialDescription() {
 }
 
 export const DatavisualizerSelector: FC = () => {
-  const { services } = useMlKibana();
-  const { timefilter } = services.data.query.timefilter;
-  timefilter.disableTimeRangeSelector();
-  timefilter.disableAutoRefreshSelector();
+  useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false });
 
   const startTrialVisible = isFullLicense() === false;
 
diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/file_datavisualizer.tsx b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/file_datavisualizer.tsx
index 9dcb9d25692e9..5c32d62c39f84 100644
--- a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/file_datavisualizer.tsx
+++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/file_datavisualizer.tsx
@@ -7,7 +7,7 @@
 import React, { FC, Fragment } from 'react';
 import { IUiSettingsClient } from 'src/core/public';
 
-import { useMlKibana } from '../../contexts/kibana';
+import { useTimefilter } from '../../contexts/kibana';
 import { NavigationMenu } from '../../components/navigation_menu';
 import { getIndexPatternsContract } from '../../util/index_utils';
 
@@ -19,10 +19,7 @@ export interface FileDataVisualizerPageProps {
 }
 
 export const FileDataVisualizerPage: FC<FileDataVisualizerPageProps> = ({ kibanaConfig }) => {
-  const { services } = useMlKibana();
-  const { timefilter } = services.data.query.timefilter;
-  timefilter.disableTimeRangeSelector();
-  timefilter.disableAutoRefreshSelector();
+  useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false });
   const indexPatterns = getIndexPatternsContract();
   return (
     <Fragment>
diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/index_based/page.tsx b/x-pack/legacy/plugins/ml/public/application/datavisualizer/index_based/page.tsx
index a6508ea868724..84c07651d323d 100644
--- a/x-pack/legacy/plugins/ml/public/application/datavisualizer/index_based/page.tsx
+++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/index_based/page.tsx
@@ -38,7 +38,7 @@ import { FullTimeRangeSelector } from '../../components/full_time_range_selector
 import { mlTimefilterRefresh$ } from '../../services/timefilter_refresh_service';
 import { useMlContext, SavedSearchQuery } from '../../contexts/ml';
 import { kbnTypeToMLJobType } from '../../util/field_types_utils';
-import { useMlKibana } from '../../contexts/kibana';
+import { useTimefilter } from '../../contexts/kibana';
 import { timeBasedIndexCheck, getQueryFromSavedSearch } from '../../util/index_utils';
 import { TimeBuckets } from '../../util/time_buckets';
 import { useUrlState } from '../../util/url_state';
@@ -97,11 +97,13 @@ function getDefaultPageState(): DataVisualizerPageState {
 }
 
 export const Page: FC = () => {
-  const { services } = useMlKibana();
   const mlContext = useMlContext();
 
-  const { timefilter } = services.data.query.timefilter;
   const { combinedQuery, currentIndexPattern, currentSavedSearch, kibanaConfig } = mlContext;
+  const timefilter = useTimefilter({
+    timeRangeSelector: currentIndexPattern.timeFieldName !== undefined,
+    autoRefreshSelector: true,
+  });
 
   const dataLoader = new DataLoader(currentIndexPattern, kibanaConfig);
   const [globalState, setGlobalState] = useUrlState('_g');
@@ -122,13 +124,6 @@ export const Page: FC = () => {
   const [lastRefresh, setLastRefresh] = useState(0);
 
   useEffect(() => {
-    if (currentIndexPattern.timeFieldName !== undefined) {
-      timefilter.enableTimeRangeSelector();
-    } else {
-      timefilter.disableTimeRangeSelector();
-    }
-
-    timefilter.enableAutoRefreshSelector();
     timeBasedIndexCheck(currentIndexPattern, true);
   }, []);
 
diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/explorer.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/explorer.tsx
index b0046f7b8d699..2c6726338d2f1 100644
--- a/x-pack/legacy/plugins/ml/public/application/routing/routes/explorer.tsx
+++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/explorer.tsx
@@ -29,7 +29,7 @@ import { useTableInterval } from '../../components/controls/select_interval';
 import { useTableSeverity } from '../../components/controls/select_severity';
 import { useUrlState } from '../../util/url_state';
 import { ANOMALY_DETECTION_BREADCRUMB, ML_BREADCRUMB } from '../breadcrumbs';
-import { useMlKibana } from '../../contexts/kibana';
+import { useTimefilter } from '../../contexts/kibana';
 
 const breadcrumbs = [
   ML_BREADCRUMB,
@@ -70,8 +70,7 @@ const ExplorerUrlStateManager: FC<ExplorerUrlStateManagerProps> = ({ jobsWithTim
   const [appState, setAppState] = useUrlState('_a');
   const [globalState, setGlobalState] = useUrlState('_g');
   const [lastRefresh, setLastRefresh] = useState(0);
-  const { services } = useMlKibana();
-  const { timefilter } = services.data.query.timefilter;
+  const timefilter = useTimefilter({ timeRangeSelector: true, autoRefreshSelector: true });
 
   const { jobIds } = useJobSelection(jobsWithTimeRange, getDateFormatTz());
 
@@ -111,9 +110,6 @@ const ExplorerUrlStateManager: FC<ExplorerUrlStateManagerProps> = ({ jobsWithTim
   }, [globalState?.time?.from, globalState?.time?.to]);
 
   useEffect(() => {
-    timefilter.enableTimeRangeSelector();
-    timefilter.enableAutoRefreshSelector();
-
     const viewByFieldName = appState?.mlExplorerSwimlane?.viewByFieldName;
     if (viewByFieldName !== undefined) {
       explorerService.setViewBySwimlaneFieldName(viewByFieldName);
diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/jobs_list.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/jobs_list.tsx
index ca2c0750397e5..c1d686d356dda 100644
--- a/x-pack/legacy/plugins/ml/public/application/routing/routes/jobs_list.tsx
+++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/jobs_list.tsx
@@ -14,8 +14,8 @@ import { MlRoute, PageLoader, PageProps } from '../router';
 import { useResolver } from '../use_resolver';
 import { basicResolvers } from '../resolvers';
 import { JobsPage } from '../../jobs/jobs_list';
+import { useTimefilter } from '../../contexts/kibana';
 import { ANOMALY_DETECTION_BREADCRUMB, ML_BREADCRUMB } from '../breadcrumbs';
-import { useMlKibana } from '../../contexts/kibana';
 
 const breadcrumbs = [
   ML_BREADCRUMB,
@@ -36,8 +36,7 @@ export const jobListRoute: MlRoute = {
 
 const PageWrapper: FC<PageProps> = ({ deps }) => {
   const { context } = useResolver(undefined, undefined, deps.config, basicResolvers(deps));
-  const { services } = useMlKibana();
-  const { timefilter } = services.data.query.timefilter;
+  const timefilter = useTimefilter({ timeRangeSelector: false, autoRefreshSelector: true });
 
   const [globalState, setGlobalState] = useUrlState('_g');
 
@@ -48,9 +47,6 @@ const PageWrapper: FC<PageProps> = ({ deps }) => {
   const blockRefresh = refreshValue === 0 || refreshPause === true;
 
   useEffect(() => {
-    timefilter.disableTimeRangeSelector();
-    timefilter.enableAutoRefreshSelector();
-
     // If the refreshInterval defaults to 0s/pause=true, set it to 30s/pause=false,
     // otherwise pass on the globalState's settings to the date picker.
     const refreshInterval =
diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/overview.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/overview.tsx
index 85227c11582d9..b1e00158efb94 100644
--- a/x-pack/legacy/plugins/ml/public/application/routing/routes/overview.tsx
+++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/overview.tsx
@@ -16,6 +16,7 @@ import { checkFullLicense } from '../../license/check_license';
 import { checkGetJobsPrivilege } from '../../privilege/check_privilege';
 import { getMlNodeCount } from '../../ml_nodes_check';
 import { loadMlServerInfo } from '../../services/ml_server_info';
+import { useTimefilter } from '../../contexts/kibana';
 import { ML_BREADCRUMB } from '../breadcrumbs';
 
 const breadcrumbs = [
@@ -41,6 +42,7 @@ const PageWrapper: FC<PageProps> = ({ deps }) => {
     getMlNodeCount,
     loadMlServerInfo,
   });
+  useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false });
 
   return (
     <PageLoader context={context}>
diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/calendar_list.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/calendar_list.tsx
index fdbfcb3397c75..c1bfaa2fe6c1e 100644
--- a/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/calendar_list.tsx
+++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/calendar_list.tsx
@@ -15,6 +15,7 @@ import { i18n } from '@kbn/i18n';
 import { MlRoute, PageLoader, PageProps } from '../../router';
 import { useResolver } from '../../use_resolver';
 
+import { useTimefilter } from '../../../contexts/kibana';
 import { checkFullLicense } from '../../../license/check_license';
 import { checkGetJobsPrivilege, checkPermission } from '../../../privilege/check_privilege';
 import { getMlNodeCount } from '../../../ml_nodes_check/check_ml_nodes';
@@ -45,6 +46,8 @@ const PageWrapper: FC<PageProps> = ({ deps }) => {
     getMlNodeCount,
   });
 
+  useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false });
+
   const canCreateCalendar = checkPermission('canCreateCalendar');
   const canDeleteCalendar = checkPermission('canDeleteCalendar');
 
diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/calendar_new_edit.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/calendar_new_edit.tsx
index 7f622a1bba62b..7af2e49e3a69e 100644
--- a/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/calendar_new_edit.tsx
+++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/calendar_new_edit.tsx
@@ -15,6 +15,7 @@ import { i18n } from '@kbn/i18n';
 import { MlRoute, PageLoader, PageProps } from '../../router';
 import { useResolver } from '../../use_resolver';
 
+import { useTimefilter } from '../../../contexts/kibana';
 import { checkFullLicense } from '../../../license/check_license';
 import { checkGetJobsPrivilege, checkPermission } from '../../../privilege/check_privilege';
 import { checkMlNodesAvailable } from '../../../ml_nodes_check/check_ml_nodes';
@@ -77,6 +78,8 @@ const PageWrapper: FC<NewCalendarPageProps> = ({ location, mode, deps }) => {
     checkMlNodesAvailable,
   });
 
+  useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false });
+
   const canCreateCalendar = checkPermission('canCreateCalendar');
   const canDeleteCalendar = checkPermission('canDeleteCalendar');
 
diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/filter_list.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/filter_list.tsx
index 6a4ce271bff17..9c5c06b76247c 100644
--- a/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/filter_list.tsx
+++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/filter_list.tsx
@@ -15,6 +15,7 @@ import { i18n } from '@kbn/i18n';
 import { MlRoute, PageLoader, PageProps } from '../../router';
 import { useResolver } from '../../use_resolver';
 
+import { useTimefilter } from '../../../contexts/kibana';
 import { checkFullLicense } from '../../../license/check_license';
 import { checkGetJobsPrivilege, checkPermission } from '../../../privilege/check_privilege';
 import { getMlNodeCount } from '../../../ml_nodes_check/check_ml_nodes';
@@ -46,6 +47,8 @@ const PageWrapper: FC<PageProps> = ({ deps }) => {
     getMlNodeCount,
   });
 
+  useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false });
+
   const canCreateFilter = checkPermission('canCreateFilter');
   const canDeleteFilter = checkPermission('canDeleteFilter');
 
diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/filter_list_new_edit.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/filter_list_new_edit.tsx
index 4fa15ebaac21a..752b889490e58 100644
--- a/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/filter_list_new_edit.tsx
+++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/filter_list_new_edit.tsx
@@ -15,6 +15,7 @@ import { i18n } from '@kbn/i18n';
 import { MlRoute, PageLoader, PageProps } from '../../router';
 import { useResolver } from '../../use_resolver';
 
+import { useTimefilter } from '../../../contexts/kibana';
 import { checkFullLicense } from '../../../license/check_license';
 import { checkGetJobsPrivilege, checkPermission } from '../../../privilege/check_privilege';
 import { checkMlNodesAvailable } from '../../../ml_nodes_check/check_ml_nodes';
@@ -77,6 +78,8 @@ const PageWrapper: FC<NewFilterPageProps> = ({ location, mode, deps }) => {
     checkMlNodesAvailable,
   });
 
+  useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false });
+
   const canCreateFilter = checkPermission('canCreateFilter');
   const canDeleteFilter = checkPermission('canDeleteFilter');
 
diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/settings.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/settings.tsx
index 846512503ede5..10efb2dcc60c7 100644
--- a/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/settings.tsx
+++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/settings.tsx
@@ -14,6 +14,7 @@ import React, { FC } from 'react';
 import { MlRoute, PageLoader, PageProps } from '../../router';
 import { useResolver } from '../../use_resolver';
 
+import { useTimefilter } from '../../../contexts/kibana';
 import { checkFullLicense } from '../../../license/check_license';
 import { checkGetJobsPrivilege, checkPermission } from '../../../privilege/check_privilege';
 import { getMlNodeCount } from '../../../ml_nodes_check/check_ml_nodes';
@@ -35,6 +36,8 @@ const PageWrapper: FC<PageProps> = ({ deps }) => {
     getMlNodeCount,
   });
 
+  useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false });
+
   const canGetFilters = checkPermission('canGetFilters');
   const canGetCalendars = checkPermission('canGetCalendars');
 
diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/timeseriesexplorer.test.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/timeseriesexplorer.test.tsx
index 0ae42aa44e089..8633947374a8b 100644
--- a/x-pack/legacy/plugins/ml/public/application/routing/routes/timeseriesexplorer.test.tsx
+++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/timeseriesexplorer.test.tsx
@@ -12,33 +12,7 @@ import { I18nProvider } from '@kbn/i18n/react';
 
 import { TimeSeriesExplorerUrlStateManager } from './timeseriesexplorer';
 
-jest.mock('ui/new_platform', () => ({
-  npStart: {
-    plugins: {
-      data: {
-        query: {
-          timefilter: {
-            timefilter: {
-              enableTimeRangeSelector: jest.fn(),
-              enableAutoRefreshSelector: jest.fn(),
-              getRefreshInterval: jest.fn(),
-              setRefreshInterval: jest.fn(),
-              getTime: jest.fn(),
-              isAutoRefreshSelectorEnabled: jest.fn(),
-              isTimeRangeSelectorEnabled: jest.fn(),
-              getRefreshIntervalUpdate$: jest.fn(),
-              getTimeUpdate$: jest.fn(),
-              getEnabledUpdated$: jest.fn(),
-            },
-            history: { get: jest.fn() },
-          },
-        },
-      },
-    },
-  },
-}));
-
-jest.mock('../../contexts/kibana', () => ({
+jest.mock('../../contexts/kibana/kibana_context', () => ({
   useMlKibana: () => {
     return {
       services: {
@@ -47,6 +21,8 @@ jest.mock('../../contexts/kibana', () => ({
           query: {
             timefilter: {
               timefilter: {
+                disableTimeRangeSelector: jest.fn(),
+                disableAutoRefreshSelector: jest.fn(),
                 enableTimeRangeSelector: jest.fn(),
                 enableAutoRefreshSelector: jest.fn(),
                 getRefreshInterval: jest.fn(),
diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/timeseriesexplorer.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/timeseriesexplorer.tsx
index 5bc2435db078c..f8a6f6c454fc0 100644
--- a/x-pack/legacy/plugins/ml/public/application/routing/routes/timeseriesexplorer.tsx
+++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/timeseriesexplorer.tsx
@@ -35,7 +35,7 @@ import { useRefresh } from '../use_refresh';
 import { useResolver } from '../use_resolver';
 import { basicResolvers } from '../resolvers';
 import { ANOMALY_DETECTION_BREADCRUMB, ML_BREADCRUMB } from '../breadcrumbs';
-import { useMlKibana } from '../../contexts/kibana';
+import { useTimefilter } from '../../contexts/kibana';
 
 export const timeSeriesExplorerRoute: MlRoute = {
   path: '/timeseriesexplorer',
@@ -88,8 +88,7 @@ export const TimeSeriesExplorerUrlStateManager: FC<TimeSeriesExplorerUrlStateMan
   const [lastRefresh, setLastRefresh] = useState(0);
   const previousRefresh = usePrevious(lastRefresh);
   const [selectedJobId, setSelectedJobId] = useState<string>();
-  const { services } = useMlKibana();
-  const { timefilter } = services.data.query.timefilter;
+  const timefilter = useTimefilter({ timeRangeSelector: true, autoRefreshSelector: true });
 
   const refresh = useRefresh();
   useEffect(() => {
@@ -106,11 +105,6 @@ export const TimeSeriesExplorerUrlStateManager: FC<TimeSeriesExplorerUrlStateMan
     }
   }, [refresh?.lastRefresh]);
 
-  useEffect(() => {
-    timefilter.enableTimeRangeSelector();
-    timefilter.enableAutoRefreshSelector();
-  }, []);
-
   // We cannot simply infer bounds from the globalState's `time` attribute
   // with `moment` since it can contain custom strings such as `now-15m`.
   // So when globalState's `time` changes, we update the timefilter and use
diff --git a/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/new_calendar.js b/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/new_calendar.js
index 0489528fa0f63..935e67ec05eff 100644
--- a/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/new_calendar.js
+++ b/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/new_calendar.js
@@ -50,9 +50,6 @@ class NewCalendarUI extends Component {
   }
 
   componentDidMount() {
-    const { timefilter } = this.props.kibana.services.data.query.timefilter;
-    timefilter.disableTimeRangeSelector();
-    timefilter.disableAutoRefreshSelector();
     this.formSetup();
   }
 

From 5c7af8656fea9642d70d9e3c6a4b8699a9969d18 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?=
 <alejandro.haro@elastic.co>
Date: Mon, 17 Feb 2020 11:21:01 +0000
Subject: [PATCH 003/174] [Telemetry] Fix bug introduced in #55859 (#57441)

* [Telemetry] Refactor to TS Monitoring telemetry_collection files

* [Telemetry] Fetch side documents generated by monitoring to build up the Kibana plugins stats

* Update x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_beats_stats.ts

Co-Authored-By: Ahmad Bamieh <ahmadbamieh@gmail.com>

* Fix import in test file

* Move mocha tests to Jest + TS

* Fix extended telemetry in functional tests

* Fix types

* [Telemetry] Fix bug in usage_collector wrong function override

* Revert integration tests (change not needed)

Co-authored-by: Ahmad Bamieh <ahmadbamieh@gmail.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../telemetry/server/collection_manager.ts    |   6 +-
 .../server/collector/collector.ts             |   2 +-
 .../server/collector/usage_collector.ts       |   6 +-
 .../fixtures/beats_stats_results.json         |   0
 .../create_query.js => create_query.test.ts}  |  11 +-
 .../{create_query.js => create_query.ts}      |  22 +-
 ...get_all_stats.js => get_all_stats.test.ts} |  58 +++--
 .../{get_all_stats.js => get_all_stats.ts}    |  60 +++--
 ...beats_stats.js => get_beats_stats.test.ts} |  52 ++---
 ...{get_beats_stats.js => get_beats_stats.ts} | 206 ++++++++++++++----
 ...ter_uuids.js => get_cluster_uuids.test.ts} |  21 +-
 .../get_es_stats.js => get_es_stats.test.ts}  |  21 +-
 .../{get_es_stats.js => get_es_stats.ts}      |  33 ++-
 ..._stats.js => get_high_level_stats.test.ts} |  27 ++-
 ...level_stats.js => get_high_level_stats.ts} | 148 ++++++++++---
 ...bana_stats.js => get_kibana_stats.test.ts} | 164 ++++++++------
 ...et_kibana_stats.js => get_kibana_stats.ts} | 117 +++++++---
 .../register_monitoring_collection.ts         |   1 -
 18 files changed, 673 insertions(+), 282 deletions(-)
 rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{__tests__ => __mocks__}/fixtures/beats_stats_results.json (100%)
 rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{__tests__/create_query.js => create_query.test.ts} (89%)
 rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{create_query.js => create_query.ts} (80%)
 rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{__tests__/get_all_stats.js => get_all_stats.test.ts} (78%)
 rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{get_all_stats.js => get_all_stats.ts} (66%)
 rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{__tests__/get_beats_stats.js => get_beats_stats.test.ts} (75%)
 rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{get_beats_stats.js => get_beats_stats.ts} (60%)
 rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{__tests__/get_cluster_uuids.js => get_cluster_uuids.test.ts} (77%)
 rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{__tests__/get_es_stats.js => get_es_stats.test.ts} (82%)
 rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{get_es_stats.js => get_es_stats.ts} (71%)
 rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{__tests__/get_high_level_stats.js => get_high_level_stats.test.ts} (91%)
 rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{get_high_level_stats.js => get_high_level_stats.ts} (66%)
 rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{__tests__/get_kibana_stats.js => get_kibana_stats.test.ts} (79%)
 rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{get_kibana_stats.js => get_kibana_stats.ts} (58%)

diff --git a/src/legacy/core_plugins/telemetry/server/collection_manager.ts b/src/legacy/core_plugins/telemetry/server/collection_manager.ts
index 933c249cd7279..0394dea343adf 100644
--- a/src/legacy/core_plugins/telemetry/server/collection_manager.ts
+++ b/src/legacy/core_plugins/telemetry/server/collection_manager.ts
@@ -41,8 +41,8 @@ export interface StatsCollectionConfig {
   usageCollection: UsageCollectionSetup;
   callCluster: CallCluster;
   server: any;
-  start: string;
-  end: string;
+  start: string | number;
+  end: string | number;
 }
 
 export type StatsGetterConfig = UnencryptedStatsGetterConfig | EncryptedStatsGetterConfig;
@@ -193,7 +193,7 @@ export class TelemetryCollectionManager {
         }
       } catch (err) {
         statsCollectionConfig.server.log(
-          ['debu', 'telemetry', 'collection'],
+          ['debug', 'telemetry', 'collection'],
           `Failed to collect any usage with registered collections.`
         );
         // swallow error to try next collection;
diff --git a/src/plugins/usage_collection/server/collector/collector.ts b/src/plugins/usage_collection/server/collector/collector.ts
index e102dc2a64ee8..91951aa2f3edf 100644
--- a/src/plugins/usage_collection/server/collector/collector.ts
+++ b/src/plugins/usage_collection/server/collector/collector.ts
@@ -85,7 +85,7 @@ export class Collector<T = unknown, U = T> {
   protected defaultFormatterForBulkUpload(result: T) {
     return {
       type: this.type,
-      payload: result,
+      payload: (result as unknown) as U,
     };
   }
 }
diff --git a/src/plugins/usage_collection/server/collector/usage_collector.ts b/src/plugins/usage_collection/server/collector/usage_collector.ts
index 05c701bd3abf4..bf861a94fccff 100644
--- a/src/plugins/usage_collection/server/collector/usage_collector.ts
+++ b/src/plugins/usage_collection/server/collector/usage_collector.ts
@@ -24,14 +24,14 @@ export class UsageCollector<T = unknown, U = { usage: { [key: string]: T } }> ex
   T,
   U
 > {
-  protected defaultUsageFormatterForBulkUpload(result: T) {
+  protected defaultFormatterForBulkUpload(result: T) {
     return {
       type: KIBANA_STATS_TYPE,
-      payload: {
+      payload: ({
         usage: {
           [this.type]: result,
         },
-      },
+      } as unknown) as U,
     };
   }
 }
diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/fixtures/beats_stats_results.json b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__mocks__/fixtures/beats_stats_results.json
similarity index 100%
rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/fixtures/beats_stats_results.json
rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/__mocks__/fixtures/beats_stats_results.json
diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/create_query.js b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/create_query.test.ts
similarity index 89%
rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/create_query.js
rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/create_query.test.ts
index 63c779ab4b520..a85d084f83d83 100644
--- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/create_query.js
+++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/create_query.test.ts
@@ -4,14 +4,13 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import expect from '@kbn/expect';
 import { set } from 'lodash';
-import { createTypeFilter, createQuery } from '../create_query.js';
+import { createTypeFilter, createQuery } from './create_query';
 
 describe('Create Type Filter', () => {
   it('Builds a type filter syntax', () => {
     const typeFilter = createTypeFilter('my_type');
-    expect(typeFilter).to.eql({
+    expect(typeFilter).toStrictEqual({
       bool: { should: [{ term: { _type: 'my_type' } }, { term: { type: 'my_type' } }] },
     });
   });
@@ -36,7 +35,7 @@ describe('Create Query', () => {
         ],
       },
     };
-    expect(result).to.be.eql(expected);
+    expect(result).toStrictEqual(expected);
   });
 
   it('Uses `type` option to add type filter with minimal fields', () => {
@@ -47,7 +46,7 @@ describe('Create Query', () => {
       { term: { _type: 'test-type-yay' } },
       { term: { type: 'test-type-yay' } },
     ]);
-    expect(result).to.be.eql(expected);
+    expect(result).toStrictEqual(expected);
   });
 
   it('Uses `type` option to add type filter with all other option fields', () => {
@@ -77,6 +76,6 @@ describe('Create Query', () => {
         ],
       },
     };
-    expect(result).to.be.eql(expected);
+    expect(result).toStrictEqual(expected);
   });
 });
diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/create_query.js b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/create_query.ts
similarity index 80%
rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/create_query.js
rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/create_query.ts
index 6fcbb677b307d..9a801094458bd 100644
--- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/create_query.js
+++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/create_query.ts
@@ -4,7 +4,6 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { defaults } from 'lodash';
 import moment from 'moment';
 
 /*
@@ -14,7 +13,7 @@ import moment from 'moment';
  * TODO: this backwards compatibility helper will only be supported for 5.x-6. This
  * function should be removed in 7.0
  */
-export const createTypeFilter = type => {
+export const createTypeFilter = (type: string) => {
   return {
     bool: {
       should: [{ term: { _type: type } }, { term: { type } }],
@@ -22,6 +21,18 @@ export const createTypeFilter = type => {
   };
 };
 
+export interface QueryOptions {
+  type?: string;
+  filters?: object[];
+  clusterUuid?: string;
+  start?: string | number;
+  end?: string | number;
+}
+
+interface RangeFilter {
+  range: { [key: string]: { format?: string; gte?: string | number; lte?: string | number } };
+}
+
 /*
  * Creates the boilerplace for querying monitoring data, including filling in
  * start time and end time, and injecting additional filters.
@@ -36,9 +47,8 @@ export const createTypeFilter = type => {
  * @param {Date} options.start - numeric timestamp (optional)
  * @param {Date} options.end - numeric timestamp (optional)
  */
-export function createQuery(options) {
-  options = defaults(options, { filters: [] });
-  const { type, clusterUuid, start, end, filters } = options;
+export function createQuery(options: QueryOptions) {
+  const { type, clusterUuid, start, end, filters = [] } = options;
 
   let typeFilter;
   if (type) {
@@ -50,7 +60,7 @@ export function createQuery(options) {
     clusterUuidFilter = { term: { cluster_uuid: clusterUuid } };
   }
 
-  let timeRangeFilter;
+  let timeRangeFilter: RangeFilter | undefined;
   if (start || end) {
     timeRangeFilter = {
       range: {
diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_all_stats.js b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_all_stats.test.ts
similarity index 78%
rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_all_stats.js
rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_all_stats.test.ts
index f27fde50242f4..470642f9dd8a3 100644
--- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_all_stats.js
+++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_all_stats.test.ts
@@ -4,12 +4,13 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import expect from '@kbn/expect';
 import sinon from 'sinon';
-import { addStackStats, getAllStats, handleAllStats } from '../get_all_stats';
+import { addStackStats, getAllStats, handleAllStats } from './get_all_stats';
+import { ESClusterStats } from './get_es_stats';
+import { KibanaStats } from './get_kibana_stats';
+import { ClustersHighLevelStats } from './get_high_level_stats';
 
-// FAILING: https://github.com/elastic/kibana/issues/51371
-describe.skip('get_all_stats', () => {
+describe('get_all_stats', () => {
   const size = 123;
   const start = 0;
   const end = 1;
@@ -100,9 +101,6 @@ describe.skip('get_all_stats', () => {
 
   describe('getAllStats', () => {
     it('returns clusters', async () => {
-      const clusterUuidsResponse = {
-        aggregations: { cluster_uuids: { buckets: [{ key: 'a' }] } },
-      };
       const esStatsResponse = {
         hits: {
           hits: [{ _id: 'a', _source: { cluster_uuid: 'a' } }],
@@ -177,15 +175,25 @@ describe.skip('get_all_stats', () => {
       callCluster
         .withArgs('search')
         .onCall(0)
-        .returns(Promise.resolve(clusterUuidsResponse))
-        .onCall(1)
         .returns(Promise.resolve(esStatsResponse))
-        .onCall(2)
+        .onCall(1)
         .returns(Promise.resolve(kibanaStatsResponse))
+        .onCall(2)
+        .returns(Promise.resolve(logstashStatsResponse))
         .onCall(3)
-        .returns(Promise.resolve(logstashStatsResponse));
+        .returns(Promise.resolve({})) // Beats stats
+        .onCall(4)
+        .returns(Promise.resolve({})); // Beats state
 
-      expect(await getAllStats({ callCluster, server, start, end })).to.eql(allClusters);
+      expect(
+        await getAllStats([{ clusterUuid: 'a' }], {
+          callCluster: callCluster as any,
+          usageCollection: {} as any,
+          server,
+          start,
+          end,
+        })
+      ).toStrictEqual(allClusters);
     });
 
     it('returns empty clusters', async () => {
@@ -195,21 +203,33 @@ describe.skip('get_all_stats', () => {
 
       callCluster.withArgs('search').returns(Promise.resolve(clusterUuidsResponse));
 
-      expect(await getAllStats({ callCluster, server, start, end })).to.eql([]);
+      expect(
+        await getAllStats([], {
+          callCluster: callCluster as any,
+          usageCollection: {} as any,
+          server,
+          start,
+          end,
+        })
+      ).toStrictEqual([]);
     });
   });
 
   describe('handleAllStats', () => {
     it('handles response', () => {
-      const clusters = handleAllStats(esClusters, { kibana: kibanaStats, logstash: logstashStats });
+      const clusters = handleAllStats(esClusters as ESClusterStats[], {
+        kibana: (kibanaStats as unknown) as KibanaStats,
+        logstash: (logstashStats as unknown) as ClustersHighLevelStats,
+        beats: {},
+      });
 
-      expect(clusters).to.eql(expectedClusters);
+      expect(clusters).toStrictEqual(expectedClusters);
     });
 
     it('handles no clusters response', () => {
-      const clusters = handleAllStats([], {});
+      const clusters = handleAllStats([], {} as any);
 
-      expect(clusters).to.have.length(0);
+      expect(clusters).toHaveLength(0);
     });
   });
 
@@ -230,9 +250,9 @@ describe.skip('get_all_stats', () => {
         },
       };
 
-      addStackStats(cluster, stats, 'xyz');
+      addStackStats(cluster as ESClusterStats, stats, 'xyz');
 
-      expect(cluster.stack_stats.xyz).to.be(stats.a);
+      expect((cluster as any).stack_stats.xyz).toStrictEqual(stats.a);
     });
   });
 });
diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_all_stats.js b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_all_stats.ts
similarity index 66%
rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_all_stats.js
rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_all_stats.ts
index 87281a19141ae..aa5e937387daf 100644
--- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_all_stats.js
+++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_all_stats.ts
@@ -6,22 +6,26 @@
 
 import { get, set, merge } from 'lodash';
 
+import { StatsGetter } from 'src/legacy/core_plugins/telemetry/server/collection_manager';
 import { LOGSTASH_SYSTEM_ID, KIBANA_SYSTEM_ID, BEATS_SYSTEM_ID } from '../../common/constants';
-import { getElasticsearchStats } from './get_es_stats';
-import { getKibanaStats } from './get_kibana_stats';
+import { getElasticsearchStats, ESClusterStats } from './get_es_stats';
+import { getKibanaStats, KibanaStats } from './get_kibana_stats';
 import { getBeatsStats } from './get_beats_stats';
 import { getHighLevelStats } from './get_high_level_stats';
 
+type PromiseReturnType<T extends (...args: any[]) => any> = ReturnType<T> extends Promise<infer R>
+  ? R
+  : T;
+
 /**
  * Get statistics for all products joined by Elasticsearch cluster.
+ * Returns the array of clusters joined with the Kibana and Logstash instances.
  *
- * @param {Object} server The Kibana server instance used to call ES as the internal user
- * @param {function} callCluster The callWithRequest or callWithInternalUser handler
- * @param {Date} start The starting range to request data
- * @param {Date} end The ending range to request data
- * @return {Promise} The array of clusters joined with the Kibana and Logstash instances.
  */
-export async function getAllStats(clustersDetails, { server, callCluster, start, end }) {
+export const getAllStats: StatsGetter = async (
+  clustersDetails,
+  { server, callCluster, start, end }
+) => {
   const clusterUuids = clustersDetails.map(clusterDetails => clusterDetails.clusterUuid);
 
   const [esClusters, kibana, logstash, beats] = await Promise.all([
@@ -32,7 +36,7 @@ export async function getAllStats(clustersDetails, { server, callCluster, start,
   ]);
 
   return handleAllStats(esClusters, { kibana, logstash, beats });
-}
+};
 
 /**
  * Combine the statistics from the stack to create "cluster" stats that associate all products together based on the cluster
@@ -41,9 +45,21 @@ export async function getAllStats(clustersDetails, { server, callCluster, start,
  * @param {Array} clusters The Elasticsearch clusters
  * @param {Object} kibana The Kibana instances keyed by Cluster UUID
  * @param {Object} logstash The Logstash nodes keyed by Cluster UUID
- * @return {Array} The clusters joined with the Kibana and Logstash instances under each cluster's {@code stack_stats}.
+ *
+ * Returns the clusters joined with the Kibana and Logstash instances under each cluster's {@code stack_stats}.
  */
-export function handleAllStats(clusters, { kibana, logstash, beats }) {
+export function handleAllStats(
+  clusters: ESClusterStats[],
+  {
+    kibana,
+    logstash,
+    beats,
+  }: {
+    kibana: KibanaStats;
+    logstash: PromiseReturnType<typeof getHighLevelStats>;
+    beats: PromiseReturnType<typeof getBeatsStats>;
+  }
+) {
   return clusters.map(cluster => {
     // if they are using Kibana or Logstash, then add it to the cluster details under cluster.stack_stats
     addStackStats(cluster, kibana, KIBANA_SYSTEM_ID);
@@ -62,8 +78,12 @@ export function handleAllStats(clusters, { kibana, logstash, beats }) {
  * @param {Object} allProductStats Product stats, keyed by Cluster UUID
  * @param {String} product The product name being added (e.g., 'kibana' or 'logstash')
  */
-export function addStackStats(cluster, allProductStats, product) {
-  const productStats = get(allProductStats, cluster.cluster_uuid);
+export function addStackStats<T extends { [clusterUuid: string]: K }, K>(
+  cluster: ESClusterStats & { stack_stats?: { [product: string]: K } },
+  allProductStats: T,
+  product: string
+) {
+  const productStats = allProductStats[cluster.cluster_uuid];
 
   // Don't add it if they're not using (or configured to report stats) this product for this cluster
   if (productStats) {
@@ -75,12 +95,20 @@ export function addStackStats(cluster, allProductStats, product) {
   }
 }
 
-export function mergeXPackStats(cluster, allProductStats, path, product) {
+export function mergeXPackStats<T extends { [clusterUuid: string]: unknown }>(
+  cluster: ESClusterStats & { stack_stats?: { xpack?: { [product: string]: unknown } } },
+  allProductStats: T,
+  path: string,
+  product: string
+) {
   const productStats = get(allProductStats, cluster.cluster_uuid + '.' + path);
 
   if (productStats || productStats === 0) {
-    if (!get(cluster, 'stack_stats.xpack')) {
-      set(cluster, 'stack_stats.xpack', {});
+    if (!cluster.stack_stats) {
+      cluster.stack_stats = {};
+    }
+    if (!cluster.stack_stats.xpack) {
+      cluster.stack_stats.xpack = {};
     }
 
     const mergeStats = {};
diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_beats_stats.js b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_beats_stats.test.ts
similarity index 75%
rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_beats_stats.js
rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_beats_stats.test.ts
index 522be71555fba..30888e1af3f53 100644
--- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_beats_stats.js
+++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_beats_stats.test.ts
@@ -4,10 +4,10 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { fetchBeatsStats, processResults } from '../get_beats_stats';
+import { fetchBeatsStats, processResults } from './get_beats_stats';
 import sinon from 'sinon';
-import expect from '@kbn/expect';
-import beatsStatsResultSet from './fixtures/beats_stats_results';
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+const beatsStatsResultSet = require('./__mocks__/fixtures/beats_stats_results');
 
 const getBaseOptions = () => ({
   clusters: {},
@@ -22,8 +22,8 @@ describe('Get Beats Stats', () => {
     const clusterUuids = ['aCluster', 'bCluster', 'cCluster'];
     const start = 100;
     const end = 200;
-    let server;
-    let callCluster;
+    let server = { config: () => ({ get: sinon.stub() }) };
+    let callCluster = sinon.stub();
 
     beforeEach(() => {
       const getStub = { get: sinon.stub() };
@@ -32,34 +32,34 @@ describe('Get Beats Stats', () => {
       callCluster = sinon.stub();
     });
 
-    it('should set `from: 0, to: 10000` in the query', () => {
-      fetchBeatsStats(server, callCluster, clusterUuids, start, end);
+    it('should set `from: 0, to: 10000` in the query', async () => {
+      await fetchBeatsStats(server, callCluster, clusterUuids, start, end, {} as any);
       const { args } = callCluster.firstCall;
       const [api, { body }] = args;
 
-      expect(api).to.be('search');
-      expect(body.from).to.be(0);
-      expect(body.size).to.be(10000);
+      expect(api).toEqual('search');
+      expect(body.from).toEqual(0);
+      expect(body.size).toEqual(10000);
     });
 
-    it('should set `from: 10000, from: 10000` in the query', () => {
-      fetchBeatsStats(server, callCluster, clusterUuids, start, end, { page: 1 });
+    it('should set `from: 10000, from: 10000` in the query', async () => {
+      await fetchBeatsStats(server, callCluster, clusterUuids, start, end, { page: 1 } as any);
       const { args } = callCluster.firstCall;
       const [api, { body }] = args;
 
-      expect(api).to.be('search');
-      expect(body.from).to.be(10000);
-      expect(body.size).to.be(10000);
+      expect(api).toEqual('search');
+      expect(body.from).toEqual(10000);
+      expect(body.size).toEqual(10000);
     });
 
-    it('should set `from: 20000, from: 10000` in the query', () => {
-      fetchBeatsStats(server, callCluster, clusterUuids, start, end, { page: 2 });
+    it('should set `from: 20000, from: 10000` in the query', async () => {
+      await fetchBeatsStats(server, callCluster, clusterUuids, start, end, { page: 2 } as any);
       const { args } = callCluster.firstCall;
       const [api, { body }] = args;
 
-      expect(api).to.be('search');
-      expect(body.from).to.be(20000);
-      expect(body.size).to.be(10000);
+      expect(api).toEqual('search');
+      expect(body.from).toEqual(20000);
+      expect(body.size).toEqual(10000);
     });
   });
 
@@ -68,9 +68,9 @@ describe('Get Beats Stats', () => {
       const resultsEmpty = undefined;
 
       const options = getBaseOptions();
-      processResults(resultsEmpty, options);
+      processResults(resultsEmpty as any, options);
 
-      expect(options.clusters).to.eql({});
+      expect(options.clusters).toStrictEqual({});
     });
 
     it('should summarize single result with some missing fields', () => {
@@ -92,9 +92,9 @@ describe('Get Beats Stats', () => {
       };
 
       const options = getBaseOptions();
-      processResults(results, options);
+      processResults(results as any, options);
 
-      expect(options.clusters).to.eql({
+      expect(options.clusters).toStrictEqual({
         FlV4ckTxQ0a78hmBkzzc9A: {
           count: 1,
           versions: {},
@@ -122,11 +122,11 @@ describe('Get Beats Stats', () => {
       const options = getBaseOptions();
 
       // beatsStatsResultSet is an array of many small query results
-      beatsStatsResultSet.forEach(results => {
+      beatsStatsResultSet.forEach((results: any) => {
         processResults(results, options);
       });
 
-      expect(options.clusters).to.eql({
+      expect(options.clusters).toStrictEqual({
         W7hppdX7R229Oy3KQbZrTw: {
           count: 5,
           versions: { '7.0.0-alpha1': 5 },
diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_beats_stats.js b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_beats_stats.ts
similarity index 60%
rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_beats_stats.js
rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_beats_stats.ts
index 5722228b60207..975a3bfee6333 100644
--- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_beats_stats.js
+++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_beats_stats.ts
@@ -5,6 +5,8 @@
  */
 
 import { get } from 'lodash';
+import { StatsCollectionConfig } from 'src/legacy/core_plugins/telemetry/server/collection_manager';
+import { SearchResponse } from 'elasticsearch';
 import { createQuery } from './create_query';
 import { INDEX_PATTERN_BEATS } from '../../common/constants';
 
@@ -33,6 +35,107 @@ const getBaseStats = () => ({
   },
 });
 
+export interface BeatsStats {
+  cluster_uuid: string;
+  beats_stats?: {
+    beat?: {
+      version?: string;
+      type?: string;
+      host?: string;
+    };
+    metrics?: {
+      libbeat?: {
+        output?: {
+          type?: string;
+        };
+        pipeline?: {
+          events?: {
+            published?: number;
+          };
+        };
+      };
+    };
+  };
+  beats_state?: {
+    beat?: {
+      type?: string;
+    };
+    state?: {
+      input?: {
+        names: string[];
+        count: number;
+      };
+      module?: {
+        names: string[];
+        count: number;
+      };
+      heartbeat?: HeartbeatBase;
+      functionbeat?: {
+        functions?: {
+          count?: number;
+        };
+      };
+      host?: {
+        architecture: string;
+        os: { platform: string };
+      };
+    };
+  };
+}
+
+interface HeartbeatBase {
+  monitors: number;
+  endpoints: number;
+  // I have to add the '| number' bit because otherwise TS complains about 'monitors' and 'endpoints' not being of type HeartbeatBase
+  [key: string]: HeartbeatBase | number | undefined;
+}
+
+export interface BeatsBaseStats {
+  // stats
+  versions: { [version: string]: number };
+  types: { [type: string]: number };
+  outputs: { [outputType: string]: number };
+  count: number;
+  eventsPublished: number;
+  hosts: number;
+  // state
+  input: {
+    count: number;
+    names: string[];
+  };
+  module: {
+    count: number;
+    names: string[];
+  };
+  architecture: {
+    count: number;
+    architectures: BeatsArchitecture[];
+  };
+  heartbeat?: HeartbeatBase;
+  functionbeat?: {
+    functions: {
+      count: number;
+    };
+  };
+}
+
+export interface BeatsProcessOptions {
+  clusters: { [clusterUuid: string]: BeatsBaseStats }; // the result object to be built up
+  clusterHostSets: { [clusterUuid: string]: Set<string> }; // passed to processResults for tracking state in the results generation
+  clusterInputSets: { [clusterUuid: string]: Set<string> }; // passed to processResults for tracking state in the results generation
+  clusterModuleSets: { [clusterUuid: string]: Set<string> }; // passed to processResults for tracking state in the results generation
+  clusterArchitectureMaps: {
+    // passed to processResults for tracking state in the results generation
+    [clusterUuid: string]: Map<string, BeatsArchitecture>;
+  };
+}
+
+export interface BeatsArchitecture {
+  name: string;
+  architecture: string;
+  count: number;
+}
+
 /*
  * Update a clusters object with processed beat stats
  * @param {Array} results - array of Beats docs from ES
@@ -41,12 +144,18 @@ const getBaseStats = () => ({
  * @param {Object} clusterModuleSets - the object keyed by cluster UUIDs to count the unique modules
  */
 export function processResults(
-  results = [],
-  { clusters, clusterHostSets, clusterInputSets, clusterModuleSets, clusterArchitectureMaps }
+  results: SearchResponse<BeatsStats>,
+  {
+    clusters,
+    clusterHostSets,
+    clusterInputSets,
+    clusterModuleSets,
+    clusterArchitectureMaps,
+  }: BeatsProcessOptions
 ) {
-  const currHits = get(results, 'hits.hits', []);
+  const currHits = results?.hits?.hits || [];
   currHits.forEach(hit => {
-    const clusterUuid = get(hit, '_source.cluster_uuid');
+    const clusterUuid = hit._source.cluster_uuid;
     if (clusters[clusterUuid] === undefined) {
       clusters[clusterUuid] = getBaseStats();
       clusterHostSets[clusterUuid] = new Set();
@@ -57,30 +166,30 @@ export function processResults(
 
     const processBeatsStatsResults = () => {
       const { versions, types, outputs } = clusters[clusterUuid];
-      const thisVersion = get(hit, '_source.beats_stats.beat.version');
+      const thisVersion = hit._source.beats_stats?.beat?.version;
       if (thisVersion !== undefined) {
         const thisVersionAccum = versions[thisVersion] || 0;
         versions[thisVersion] = thisVersionAccum + 1;
       }
 
-      const thisType = get(hit, '_source.beats_stats.beat.type');
+      const thisType = hit._source.beats_stats?.beat?.type;
       if (thisType !== undefined) {
         const thisTypeAccum = types[thisType] || 0;
         types[thisType] = thisTypeAccum + 1;
       }
 
-      const thisOutput = get(hit, '_source.beats_stats.metrics.libbeat.output.type');
+      const thisOutput = hit._source.beats_stats?.metrics?.libbeat?.output?.type;
       if (thisOutput !== undefined) {
         const thisOutputAccum = outputs[thisOutput] || 0;
         outputs[thisOutput] = thisOutputAccum + 1;
       }
 
-      const thisEvents = get(hit, '_source.beats_stats.metrics.libbeat.pipeline.events.published');
+      const thisEvents = hit._source.beats_stats?.metrics?.libbeat?.pipeline?.events?.published;
       if (thisEvents !== undefined) {
         clusters[clusterUuid].eventsPublished += thisEvents;
       }
 
-      const thisHost = get(hit, '_source.beats_stats.beat.host');
+      const thisHost = hit._source.beats_stats?.beat?.host;
       if (thisHost !== undefined) {
         const hostsMap = clusterHostSets[clusterUuid];
         hostsMap.add(thisHost);
@@ -89,7 +198,7 @@ export function processResults(
     };
 
     const processBeatsStateResults = () => {
-      const stateInput = get(hit, '_source.beats_state.state.input');
+      const stateInput = hit._source.beats_state?.state?.input;
       if (stateInput !== undefined) {
         const inputSet = clusterInputSets[clusterUuid];
         stateInput.names.forEach(name => inputSet.add(name));
@@ -97,8 +206,8 @@ export function processResults(
         clusters[clusterUuid].input.count += stateInput.count;
       }
 
-      const stateModule = get(hit, '_source.beats_state.state.module');
-      const statsType = get(hit, '_source.beats_state.beat.type');
+      const stateModule = hit._source.beats_state?.state?.module;
+      const statsType = hit._source.beats_state?.beat?.type;
       if (stateModule !== undefined) {
         const moduleSet = clusterModuleSets[clusterUuid];
         stateModule.names.forEach(name => moduleSet.add(statsType + '.' + name));
@@ -106,7 +215,7 @@ export function processResults(
         clusters[clusterUuid].module.count += stateModule.count;
       }
 
-      const heartbeatState = get(hit, '_source.beats_state.state.heartbeat');
+      const heartbeatState = hit._source.beats_state?.state?.heartbeat;
       if (heartbeatState !== undefined) {
         if (!clusters[clusterUuid].hasOwnProperty('heartbeat')) {
           clusters[clusterUuid].heartbeat = {
@@ -114,7 +223,7 @@ export function processResults(
             endpoints: 0,
           };
         }
-        const clusterHb = clusters[clusterUuid].heartbeat;
+        const clusterHb = clusters[clusterUuid].heartbeat!;
 
         clusterHb.monitors += heartbeatState.monitors;
         clusterHb.endpoints += heartbeatState.endpoints;
@@ -133,12 +242,12 @@ export function processResults(
               endpoints: 0,
             };
           }
-          clusterHb[proto].monitors += val.monitors;
-          clusterHb[proto].endpoints += val.endpoints;
+          (clusterHb[proto] as HeartbeatBase).monitors += val.monitors;
+          (clusterHb[proto] as HeartbeatBase).endpoints += val.endpoints;
         }
       }
 
-      const functionbeatState = get(hit, '_source.beats_state.state.functionbeat');
+      const functionbeatState = hit._source.beats_state?.state?.functionbeat;
       if (functionbeatState !== undefined) {
         if (!clusters[clusterUuid].hasOwnProperty('functionbeat')) {
           clusters[clusterUuid].functionbeat = {
@@ -148,14 +257,11 @@ export function processResults(
           };
         }
 
-        clusters[clusterUuid].functionbeat.functions.count += get(
-          functionbeatState,
-          'functions.count',
-          0
-        );
+        clusters[clusterUuid].functionbeat!.functions.count +=
+          functionbeatState.functions?.count || 0;
       }
 
-      const stateHost = get(hit, '_source.beats_state.state.host');
+      const stateHost = hit._source.beats_state?.state?.host;
       if (stateHost !== undefined) {
         const hostMap = clusterArchitectureMaps[clusterUuid];
         const hostKey = `${stateHost.architecture}/${stateHost.os.platform}`;
@@ -198,14 +304,14 @@ export function processResults(
  * @return {Promise}
  */
 async function fetchBeatsByType(
-  server,
-  callCluster,
-  clusterUuids,
-  start,
-  end,
-  { page = 0, ...options } = {},
-  type
-) {
+  server: StatsCollectionConfig['server'],
+  callCluster: StatsCollectionConfig['callCluster'],
+  clusterUuids: string[],
+  start: StatsCollectionConfig['start'],
+  end: StatsCollectionConfig['end'],
+  { page = 0, ...options }: { page?: number } & BeatsProcessOptions,
+  type: string
+): Promise<void> {
   const params = {
     index: INDEX_PATTERN_BEATS,
     ignoreUnavailable: true,
@@ -232,7 +338,7 @@ async function fetchBeatsByType(
           {
             bool: {
               must_not: { term: { [`${type}.beat.type`]: 'apm-server' } },
-              must: { term: { type: type } },
+              must: { term: { type } },
             },
           },
         ],
@@ -244,8 +350,8 @@ async function fetchBeatsByType(
     },
   };
 
-  const results = await callCluster('search', params);
-  const hitsLength = get(results, 'hits.hits.length', 0);
+  const results = await callCluster<SearchResponse<BeatsStats>>('search', params);
+  const hitsLength = results?.hits?.hits.length || 0;
   if (hitsLength > 0) {
     // further augment the clusters object with more stats
     processResults(results, options);
@@ -265,20 +371,40 @@ async function fetchBeatsByType(
   return Promise.resolve();
 }
 
-export async function fetchBeatsStats(...args) {
-  return fetchBeatsByType(...args, 'beats_stats');
+export async function fetchBeatsStats(
+  server: StatsCollectionConfig['server'],
+  callCluster: StatsCollectionConfig['callCluster'],
+  clusterUuids: string[],
+  start: StatsCollectionConfig['start'],
+  end: StatsCollectionConfig['end'],
+  options: { page?: number } & BeatsProcessOptions
+) {
+  return fetchBeatsByType(server, callCluster, clusterUuids, start, end, options, 'beats_stats');
 }
 
-export async function fetchBeatsStates(...args) {
-  return fetchBeatsByType(...args, 'beats_state');
+export async function fetchBeatsStates(
+  server: StatsCollectionConfig['server'],
+  callCluster: StatsCollectionConfig['callCluster'],
+  clusterUuids: string[],
+  start: StatsCollectionConfig['start'],
+  end: StatsCollectionConfig['end'],
+  options: { page?: number } & BeatsProcessOptions
+) {
+  return fetchBeatsByType(server, callCluster, clusterUuids, start, end, options, 'beats_state');
 }
 
 /*
  * Call the function for fetching and summarizing beats stats
  * @return {Object} - Beats stats in an object keyed by the cluster UUIDs
  */
-export async function getBeatsStats(server, callCluster, clusterUuids, start, end) {
-  const options = {
+export async function getBeatsStats(
+  server: StatsCollectionConfig['server'],
+  callCluster: StatsCollectionConfig['callCluster'],
+  clusterUuids: string[],
+  start: StatsCollectionConfig['start'],
+  end: StatsCollectionConfig['end']
+) {
+  const options: BeatsProcessOptions = {
     clusters: {}, // the result object to be built up
     clusterHostSets: {}, // passed to processResults for tracking state in the results generation
     clusterInputSets: {}, // passed to processResults for tracking state in the results generation
diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_cluster_uuids.js b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_cluster_uuids.test.ts
similarity index 77%
rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_cluster_uuids.js
rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_cluster_uuids.test.ts
index 1f62c677dbb21..4f952b9dec6da 100644
--- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_cluster_uuids.js
+++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_cluster_uuids.test.ts
@@ -4,13 +4,12 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import expect from '@kbn/expect';
 import sinon from 'sinon';
 import {
   getClusterUuids,
   fetchClusterUuids,
   handleClusterUuidsResponse,
-} from '../get_cluster_uuids';
+} from './get_cluster_uuids';
 
 describe('get_cluster_uuids', () => {
   const callCluster = sinon.stub();
@@ -35,20 +34,24 @@ describe('get_cluster_uuids', () => {
   const expectedUuids = response.aggregations.cluster_uuids.buckets
     .map(bucket => bucket.key)
     .map(expectedUuid => ({ clusterUuid: expectedUuid }));
-  const start = new Date();
-  const end = new Date();
+  const start = new Date().toISOString();
+  const end = new Date().toISOString();
 
   describe('getClusterUuids', () => {
     it('returns cluster UUIDs', async () => {
       callCluster.withArgs('search').returns(Promise.resolve(response));
-      expect(await getClusterUuids({ server, callCluster, start, end })).to.eql(expectedUuids);
+      expect(
+        await getClusterUuids({ server, callCluster, start, end, usageCollection: {} as any })
+      ).toStrictEqual(expectedUuids);
     });
   });
 
   describe('fetchClusterUuids', () => {
     it('searches for clusters', async () => {
       callCluster.returns(Promise.resolve(response));
-      expect(await fetchClusterUuids({ server, callCluster, start, end })).to.be(response);
+      expect(
+        await fetchClusterUuids({ server, callCluster, start, end, usageCollection: {} as any })
+      ).toStrictEqual(response);
     });
   });
 
@@ -56,12 +59,12 @@ describe('get_cluster_uuids', () => {
     // filterPath makes it easy to ignore anything unexpected because it will come back empty
     it('handles unexpected response', () => {
       const clusterUuids = handleClusterUuidsResponse({});
-      expect(clusterUuids.length).to.be(0);
+      expect(clusterUuids.length).toStrictEqual(0);
     });
 
     it('handles valid response', () => {
       const clusterUuids = handleClusterUuidsResponse(response);
-      expect(clusterUuids).to.eql(expectedUuids);
+      expect(clusterUuids).toStrictEqual(expectedUuids);
     });
 
     it('handles no buckets response', () => {
@@ -73,7 +76,7 @@ describe('get_cluster_uuids', () => {
         },
       });
 
-      expect(clusterUuids.length).to.be(0);
+      expect(clusterUuids.length).toStrictEqual(0);
     });
   });
 });
diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_es_stats.js b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_es_stats.test.ts
similarity index 82%
rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_es_stats.js
rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_es_stats.test.ts
index 536e831640fad..70ed2240b47d4 100644
--- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_es_stats.js
+++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_es_stats.test.ts
@@ -4,13 +4,12 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import expect from '@kbn/expect';
 import sinon from 'sinon';
 import {
   fetchElasticsearchStats,
   getElasticsearchStats,
   handleElasticsearchStats,
-} from '../get_es_stats';
+} from './get_es_stats';
 
 describe('get_es_stats', () => {
   const callWith = sinon.stub();
@@ -41,7 +40,9 @@ describe('get_es_stats', () => {
     it('returns clusters', async () => {
       callWith.withArgs('search').returns(Promise.resolve(response));
 
-      expect(await getElasticsearchStats(server, callWith, clusterUuids)).to.eql(expectedClusters);
+      expect(await getElasticsearchStats(server, callWith, clusterUuids)).toStrictEqual(
+        expectedClusters
+      );
     });
   });
 
@@ -49,28 +50,28 @@ describe('get_es_stats', () => {
     it('searches for clusters', async () => {
       callWith.returns(response);
 
-      expect(await fetchElasticsearchStats(server, callWith, clusterUuids)).to.be(response);
+      expect(await fetchElasticsearchStats(server, callWith, clusterUuids)).toStrictEqual(response);
     });
   });
 
   describe('handleElasticsearchStats', () => {
     // filterPath makes it easy to ignore anything unexpected because it will come back empty
     it('handles unexpected response', () => {
-      const clusters = handleElasticsearchStats({});
+      const clusters = handleElasticsearchStats({} as any);
 
-      expect(clusters.length).to.be(0);
+      expect(clusters.length).toStrictEqual(0);
     });
 
     it('handles valid response', () => {
-      const clusters = handleElasticsearchStats(response);
+      const clusters = handleElasticsearchStats(response as any);
 
-      expect(clusters).to.eql(expectedClusters);
+      expect(clusters).toStrictEqual(expectedClusters);
     });
 
     it('handles no hits response', () => {
-      const clusters = handleElasticsearchStats({ hits: { hits: [] } });
+      const clusters = handleElasticsearchStats({ hits: { hits: [] } } as any);
 
-      expect(clusters.length).to.be(0);
+      expect(clusters.length).toStrictEqual(0);
     });
   });
 });
diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_es_stats.js b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_es_stats.ts
similarity index 71%
rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_es_stats.js
rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_es_stats.ts
index 52d34258b5fa4..f0ae1163d3f52 100644
--- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_es_stats.js
+++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_es_stats.ts
@@ -4,7 +4,8 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { get } from 'lodash';
+import { StatsCollectionConfig } from 'src/legacy/core_plugins/telemetry/server/collection_manager';
+import { SearchResponse } from 'elasticsearch';
 import { INDEX_PATTERN_ELASTICSEARCH } from '../../common/constants';
 
 /**
@@ -13,10 +14,14 @@ import { INDEX_PATTERN_ELASTICSEARCH } from '../../common/constants';
  * @param {Object} server The server instance
  * @param {function} callCluster The callWithRequest or callWithInternalUser handler
  * @param {Array} clusterUuids The string Cluster UUIDs to fetch details for
- * @return {Promise} Array of the Elasticsearch clusters.
  */
-export function getElasticsearchStats(server, callCluster, clusterUuids) {
-  return fetchElasticsearchStats(server, callCluster, clusterUuids).then(handleElasticsearchStats);
+export async function getElasticsearchStats(
+  server: StatsCollectionConfig['server'],
+  callCluster: StatsCollectionConfig['callCluster'],
+  clusterUuids: string[]
+) {
+  const response = await fetchElasticsearchStats(server, callCluster, clusterUuids);
+  return handleElasticsearchStats(response);
 }
 
 /**
@@ -25,9 +30,14 @@ export function getElasticsearchStats(server, callCluster, clusterUuids) {
  * @param {Object} server The server instance
  * @param {function} callCluster The callWithRequest or callWithInternalUser handler
  * @param {Array} clusterUuids Cluster UUIDs to limit the request against
- * @return {Promise} Response for the aggregations to fetch details for the product.
+ *
+ * Returns the response for the aggregations to fetch details for the product.
  */
-export function fetchElasticsearchStats(server, callCluster, clusterUuids) {
+export function fetchElasticsearchStats(
+  server: StatsCollectionConfig['server'],
+  callCluster: StatsCollectionConfig['callCluster'],
+  clusterUuids: string[]
+) {
   const config = server.config();
   const params = {
     index: INDEX_PATTERN_ELASTICSEARCH,
@@ -67,13 +77,16 @@ export function fetchElasticsearchStats(server, callCluster, clusterUuids) {
   return callCluster('search', params);
 }
 
+export interface ESClusterStats {
+  cluster_uuid: string;
+  type: 'cluster_stats';
+}
+
 /**
  * Extract the cluster stats for each cluster.
- *
- * @return {Array} The Elasticsearch clusters.
  */
-export function handleElasticsearchStats(response) {
-  const clusters = get(response, 'hits.hits', []);
+export function handleElasticsearchStats(response: SearchResponse<ESClusterStats>) {
+  const clusters = response.hits?.hits || [];
 
   return clusters.map(cluster => cluster._source);
 }
diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_high_level_stats.js b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_high_level_stats.test.ts
similarity index 91%
rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_high_level_stats.js
rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_high_level_stats.test.ts
index 1c1f8dc888d01..76c80e2eb3d37 100644
--- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_high_level_stats.js
+++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_high_level_stats.test.ts
@@ -4,13 +4,12 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import expect from '@kbn/expect';
 import sinon from 'sinon';
 import {
   fetchHighLevelStats,
   getHighLevelStats,
   handleHighLevelStatsResponse,
-} from '../get_high_level_stats';
+} from './get_high_level_stats';
 
 describe('get_high_level_stats', () => {
   const callWith = sinon.stub();
@@ -244,9 +243,9 @@ describe('get_high_level_stats', () => {
     it('returns clusters', async () => {
       callWith.withArgs('search').returns(Promise.resolve(response));
 
-      expect(await getHighLevelStats(server, callWith, clusterUuids, start, end, product)).to.eql(
-        expectedClusters
-      );
+      expect(
+        await getHighLevelStats(server, callWith, clusterUuids, start, end, product)
+      ).toStrictEqual(expectedClusters);
     });
   });
 
@@ -254,30 +253,30 @@ describe('get_high_level_stats', () => {
     it('searches for clusters', async () => {
       callWith.returns(Promise.resolve(response));
 
-      expect(await fetchHighLevelStats(server, callWith, clusterUuids, start, end, product)).to.be(
-        response
-      );
+      expect(
+        await fetchHighLevelStats(server, callWith, clusterUuids, start, end, product)
+      ).toStrictEqual(response);
     });
   });
 
   describe('handleHighLevelStatsResponse', () => {
     // filterPath makes it easy to ignore anything unexpected because it will come back empty
     it('handles unexpected response', () => {
-      const clusters = handleHighLevelStatsResponse({}, product);
+      const clusters = handleHighLevelStatsResponse({} as any, product);
 
-      expect(clusters).to.eql({});
+      expect(clusters).toStrictEqual({});
     });
 
     it('handles valid response', () => {
-      const clusters = handleHighLevelStatsResponse(response, product);
+      const clusters = handleHighLevelStatsResponse(response as any, product);
 
-      expect(clusters).to.eql(expectedClusters);
+      expect(clusters).toStrictEqual(expectedClusters);
     });
 
     it('handles no hits response', () => {
-      const clusters = handleHighLevelStatsResponse({ hits: { hits: [] } }, product);
+      const clusters = handleHighLevelStatsResponse({ hits: { hits: [] } } as any, product);
 
-      expect(clusters).to.eql({});
+      expect(clusters).toStrictEqual({});
     });
   });
 });
diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_high_level_stats.js b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_high_level_stats.ts
similarity index 66%
rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_high_level_stats.js
rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_high_level_stats.ts
index b87f632308e4d..f67f80940d9f4 100644
--- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_high_level_stats.js
+++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_high_level_stats.ts
@@ -5,6 +5,8 @@
  */
 
 import { get } from 'lodash';
+import { StatsCollectionConfig } from 'src/legacy/core_plugins/telemetry/server/collection_manager';
+import { SearchResponse } from 'elasticsearch';
 import { createQuery } from './create_query';
 import {
   INDEX_PATTERN_KIBANA,
@@ -17,13 +19,40 @@ import {
   TELEMETRY_QUERY_SOURCE,
 } from '../../common/constants';
 
+export interface ClusterCloudStats {
+  name: string;
+  count: number;
+  vms: number;
+  regions: Array<{ region: string; count: number }>;
+  vm_types: Array<{ vm_type: string; count: number }>;
+  zones: Array<{ zone: string; count: number }>;
+}
+
+export interface ClusterHighLevelStats {
+  count: number;
+  versions: Array<{ version: string; count: number }>;
+  os: {
+    platforms: Array<{ platform: string; count: number }>;
+    platformReleases: Array<{ platformRelease: string; count: number }>;
+    distros: Array<{ distro: string; count: number }>;
+    distroReleases: Array<{ distroRelease: string; count: number }>;
+  };
+  cloud: ClusterCloudStats[] | undefined;
+}
+
+export interface ClustersHighLevelStats {
+  [clusterUuid: string]: ClusterHighLevelStats;
+}
+
+type Counter = Map<string, number>;
+
 /**
  * Update a counter associated with the {@code key}.
  *
  * @param {Map} map Map to update the counter for the {@code key}.
  * @param {String} key The key to increment a counter for.
  */
-function incrementByKey(map, key) {
+function incrementByKey(map: Counter, key?: string) {
   if (!key) {
     return;
   }
@@ -37,13 +66,29 @@ function incrementByKey(map, key) {
   map.set(key, count + 1);
 }
 
+interface InternalCloudMap {
+  count: number;
+  unique: Set<string>;
+  vm_type: Counter;
+  region: Counter;
+  zone: Counter;
+}
+
+interface CloudEntry {
+  id: string;
+  name: string;
+  vm_type: string;
+  region: string;
+  zone: string;
+}
+
 /**
  * Help to reduce Cloud metrics into unidentifiable metrics (e.g., count IDs so that they can be dropped).
  *
  * @param  {Map} clouds Existing cloud data by cloud name.
  * @param  {Object} cloud Cloud object loaded from Elasticsearch data.
  */
-function reduceCloudForCluster(cloudMap, cloud) {
+function reduceCloudForCluster(cloudMap: Map<string, InternalCloudMap>, cloud?: CloudEntry) {
   if (!cloud) {
     return;
   }
@@ -74,22 +119,48 @@ function reduceCloudForCluster(cloudMap, cloud) {
   incrementByKey(cloudByName.zone, cloud.zone);
 }
 
+interface InternalClusterMap {
+  count: number;
+  versions: Counter;
+  cloudMap: Map<string, InternalCloudMap>;
+  os: {
+    platforms: Counter;
+    platformReleases: Counter;
+    distros: Counter;
+    distroReleases: Counter;
+  };
+}
+
+interface OSData {
+  platform?: string;
+  platformRelease?: string;
+  distro?: string;
+  distroRelease?: string;
+}
+
 /**
  * Group the instances (hits) by clusters.
  *
  * @param  {Array} instances Array of hits from the request containing the cluster UUID and version.
  * @param {String} product The product to limit too ('kibana', 'logstash', 'beats')
- * @return {Map} A map of the Cluster UUID to an {@link Object} containing the {@code count} and {@code versions} {@link Map}
+ *
+ * Returns a map of the Cluster UUID to an {@link Object} containing the {@code count} and {@code versions} {@link Map}
  */
-function groupInstancesByCluster(instances, product) {
-  const clusterMap = new Map();
+function groupInstancesByCluster<T extends { cluster_uuid?: string }>(
+  instances: Array<{ _source: T }>,
+  product: string
+) {
+  const clusterMap = new Map<string, InternalClusterMap>();
 
   // hits are sorted arbitrarily by product UUID
   instances.map(instance => {
-    const clusterUuid = get(instance, '_source.cluster_uuid');
-    const version = get(instance, `_source.${product}_stats.${product}.version`);
-    const cloud = get(instance, `_source.${product}_stats.cloud`);
-    const os = get(instance, `_source.${product}_stats.os`);
+    const clusterUuid = instance._source.cluster_uuid;
+    const version: string | undefined = get(
+      instance,
+      `_source.${product}_stats.${product}.version`
+    );
+    const cloud: CloudEntry | undefined = get(instance, `_source.${product}_stats.cloud`);
+    const os: OSData | undefined = get(instance, `_source.${product}_stats.os`);
 
     if (clusterUuid) {
       let cluster = clusterMap.get(clusterUuid);
@@ -134,16 +205,12 @@ function groupInstancesByCluster(instances, product) {
  *   { [keyName]: key1, count: value1 },
  *   { [keyName]: key2, count: value2 }
  * ]
- *
- * @param  {Map} map     [description]
- * @param  {String} keyName [description]
- * @return {Array}         [description]
  */
-function mapToList(map, keyName) {
-  const list = [];
+function mapToList<T>(map: Map<string, number>, keyName: string): T[] {
+  const list: T[] = [];
 
   for (const [key, count] of map) {
-    list.push({ [keyName]: key, count });
+    list.push(({ [keyName]: key, count } as unknown) as T);
   }
 
   return list;
@@ -154,7 +221,7 @@ function mapToList(map, keyName) {
  *
  * @param {*} product The product id, which should be in the constants file
  */
-function getIndexPatternForStackProduct(product) {
+function getIndexPatternForStackProduct(product: string) {
   switch (product) {
     case KIBANA_SYSTEM_ID:
       return INDEX_PATTERN_KIBANA;
@@ -176,23 +243,41 @@ function getIndexPatternForStackProduct(product) {
  * @param {Date} start Start time to limit the stats
  * @param {Date} end End time to limit the stats
  * @param {String} product The product to limit too ('kibana', 'logstash', 'beats')
- * @return {Promise} Object keyed by the cluster UUIDs to make grouping easier.
+ *
+ * Returns an object keyed by the cluster UUIDs to make grouping easier.
  */
-export function getHighLevelStats(server, callCluster, clusterUuids, start, end, product) {
-  return fetchHighLevelStats(
+export async function getHighLevelStats(
+  server: StatsCollectionConfig['server'],
+  callCluster: StatsCollectionConfig['callCluster'],
+  clusterUuids: string[],
+  start: StatsCollectionConfig['start'],
+  end: StatsCollectionConfig['end'],
+  product: string
+) {
+  const response = await fetchHighLevelStats(
     server,
     callCluster,
     clusterUuids,
     start,
     end,
     product
-  ).then(response => handleHighLevelStatsResponse(response, product));
+  );
+  return handleHighLevelStatsResponse(response, product);
 }
 
-export async function fetchHighLevelStats(server, callCluster, clusterUuids, start, end, product) {
+export async function fetchHighLevelStats<
+  T extends { cluster_uuid?: string } = { cluster_uuid?: string }
+>(
+  server: StatsCollectionConfig['server'],
+  callCluster: StatsCollectionConfig['callCluster'],
+  clusterUuids: string[],
+  start: StatsCollectionConfig['start'] | undefined,
+  end: StatsCollectionConfig['end'] | undefined,
+  product: string
+): Promise<SearchResponse<T>> {
   const config = server.config();
   const isKibanaIndex = product === KIBANA_SYSTEM_ID;
-  const filters = [{ terms: { cluster_uuid: clusterUuids } }];
+  const filters: object[] = [{ terms: { cluster_uuid: clusterUuids } }];
 
   // we should supply this from a parameter in the future so that this remains generic
   if (isKibanaIndex) {
@@ -257,13 +342,17 @@ export async function fetchHighLevelStats(server, callCluster, clusterUuids, sta
  *
  * @param {Object} response The response from the aggregation
  * @param {String} product The product to limit too ('kibana', 'logstash', 'beats')
- * @return {Object} Object keyed by the cluster UUIDs to make grouping easier.
+ *
+ * Returns an object keyed by the cluster UUIDs to make grouping easier.
  */
-export function handleHighLevelStatsResponse(response, product) {
-  const instances = get(response, 'hits.hits', []);
+export function handleHighLevelStatsResponse(
+  response: SearchResponse<{ cluster_uuid?: string }>,
+  product: string
+) {
+  const instances = response.hits?.hits || [];
   const clusterMap = groupInstancesByCluster(instances, product);
 
-  const clusters = {};
+  const clusters: ClustersHighLevelStats = {};
 
   for (const [clusterUuid, cluster] of clusterMap) {
     // it's unlikely this will be an array of more than one, but it is one just incase
@@ -271,14 +360,15 @@ export function handleHighLevelStatsResponse(response, product) {
 
     // remap the clouds (most likely singular or empty)
     for (const [name, cloud] of cluster.cloudMap) {
-      clouds.push({
+      const cloudStats: ClusterCloudStats = {
         name,
         count: cloud.count,
         vms: cloud.unique.size,
         regions: mapToList(cloud.region, 'region'),
         vm_types: mapToList(cloud.vm_type, 'vm_type'),
         zones: mapToList(cloud.zone, 'zone'),
-      });
+      };
+      clouds.push(cloudStats);
     }
 
     // map stats for product by cluster so that it can be joined with ES cluster stats
diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_kibana_stats.js b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_kibana_stats.test.ts
similarity index 79%
rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_kibana_stats.js
rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_kibana_stats.test.ts
index 98e0afa28fba3..0092e848c827b 100644
--- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_kibana_stats.js
+++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_kibana_stats.test.ts
@@ -4,19 +4,25 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { getUsageStats, combineStats, rollUpTotals, ensureTimeSpan } from '../get_kibana_stats';
-import expect from '@kbn/expect';
+import {
+  getUsageStats,
+  combineStats,
+  rollUpTotals,
+  ensureTimeSpan,
+  KibanaUsageStats,
+} from './get_kibana_stats';
+import { SearchResponse } from 'elasticsearch';
 
 describe('Get Kibana Stats', () => {
   describe('Make a map of usage stats for each cluster', () => {
-    it('passes through if there are no kibana instances', () => {
-      const rawStats = {};
-      expect(getUsageStats(rawStats)).to.eql({});
+    test('passes through if there are no kibana instances', () => {
+      const rawStats = {} as SearchResponse<KibanaUsageStats>;
+      expect(getUsageStats(rawStats)).toStrictEqual({});
     });
 
     describe('with single cluster', () => {
       describe('single index', () => {
-        it('for a single unused instance', () => {
+        test('for a single unused instance', () => {
           const rawStats = {
             hits: {
               hits: [
@@ -39,7 +45,7 @@ describe('Get Kibana Stats', () => {
                 },
               ],
             },
-          };
+          } as any;
           const expected = {
             clusterone: {
               dashboard: { total: 0 },
@@ -53,10 +59,10 @@ describe('Get Kibana Stats', () => {
             },
           };
 
-          expect(getUsageStats(rawStats)).to.eql(expected);
+          expect(getUsageStats(rawStats)).toStrictEqual(expected);
         });
 
-        it('for a single instance of active usage', () => {
+        test('for a single instance of active usage', () => {
           const rawStats = {
             hits: {
               hits: [
@@ -79,7 +85,7 @@ describe('Get Kibana Stats', () => {
                 },
               ],
             },
-          };
+          } as any;
           const expected = {
             clusterone: {
               dashboard: { total: 1 },
@@ -92,11 +98,49 @@ describe('Get Kibana Stats', () => {
               plugins: {},
             },
           };
+          expect(getUsageStats(rawStats)).toStrictEqual(expected);
+        });
 
-          expect(getUsageStats(rawStats)).to.eql(expected);
+        test('it merges the plugin stats and kibana', () => {
+          const rawStats = {
+            hits: {
+              hits: [
+                {
+                  _source: {
+                    cluster_uuid: 'clusterone',
+                    kibana_stats: {
+                      kibana: { version: '7.0.0-alpha1-test02' },
+                      usage: {
+                        dashboard: { total: 1 },
+                        visualization: { total: 3 },
+                        search: { total: 1 },
+                        index_pattern: { total: 1 },
+                        graph_workspace: { total: 1 },
+                        timelion_sheet: { total: 1 },
+                        index: '.kibana-test-01',
+                      },
+                    },
+                  },
+                },
+              ],
+            },
+          } as any;
+          const expected = {
+            clusterone: {
+              dashboard: { total: 1 },
+              visualization: { total: 3 },
+              search: { total: 1 },
+              index_pattern: { total: 1 },
+              graph_workspace: { total: 1 },
+              timelion_sheet: { total: 1 },
+              indices: 1,
+              plugins: {},
+            },
+          };
+          expect(getUsageStats(rawStats)).toStrictEqual(expected);
         });
 
-        it('flattens x-pack stats', () => {
+        test('flattens x-pack stats', () => {
           const rawStats = {
             hits: {
               hits: [
@@ -126,8 +170,9 @@ describe('Get Kibana Stats', () => {
                 },
               ],
             },
-          };
-          expect(getUsageStats(rawStats)).to.eql({
+          } as any;
+
+          expect(getUsageStats(rawStats)).toStrictEqual({
             clusterone: {
               dashboard: { total: 1 },
               visualization: { total: 3 },
@@ -143,7 +188,7 @@ describe('Get Kibana Stats', () => {
       });
 
       describe('separate indices', () => {
-        it('with one unused instance', () => {
+        test('with one unused instance', () => {
           const rawStats = {
             hits: {
               hits: [
@@ -200,7 +245,7 @@ describe('Get Kibana Stats', () => {
                 },
               ],
             },
-          };
+          } as any;
           const expected = {
             clusterone: {
               dashboard: { total: 1 },
@@ -213,11 +258,10 @@ describe('Get Kibana Stats', () => {
               plugins: {},
             },
           };
-
-          expect(getUsageStats(rawStats)).to.eql(expected);
+          expect(getUsageStats(rawStats)).toStrictEqual(expected);
         });
 
-        it('with all actively used instances', () => {
+        test('with all actively used instances', () => {
           const rawStats = {
             hits: {
               hits: [
@@ -274,7 +318,7 @@ describe('Get Kibana Stats', () => {
                 },
               ],
             },
-          };
+          } as any;
           const expected = {
             clusterone: {
               dashboard: { total: 4 },
@@ -287,15 +331,14 @@ describe('Get Kibana Stats', () => {
               plugins: {},
             },
           };
-
-          expect(getUsageStats(rawStats)).to.eql(expected);
+          expect(getUsageStats(rawStats)).toStrictEqual(expected);
         });
       });
     });
 
     describe('with multiple clusters', () => {
       describe('separate indices', () => {
-        it('with all actively used instances', () => {
+        test('with all actively used instances', () => {
           const rawStats = {
             hits: {
               hits: [
@@ -369,7 +412,7 @@ describe('Get Kibana Stats', () => {
                 },
               ],
             },
-          };
+          } as any;
           const expected = {
             clusterone: {
               dashboard: { total: 4 },
@@ -392,29 +435,28 @@ describe('Get Kibana Stats', () => {
               plugins: {},
             },
           };
-
-          expect(getUsageStats(rawStats)).to.eql(expected);
+          expect(getUsageStats(rawStats)).toStrictEqual(expected);
         });
       });
     });
   });
 
   describe('Combines usage stats with high-level stats', () => {
-    it('passes through if there are no kibana instances', () => {
+    test('passes through if there are no kibana instances', () => {
       const highLevelStats = {};
       const usageStats = {};
 
-      expect(combineStats(highLevelStats, usageStats)).to.eql({});
+      expect(combineStats(highLevelStats, usageStats)).toStrictEqual({});
     });
 
     describe('adds usage stats to high-level stats', () => {
-      it('for a single cluster', () => {
+      test('for a single cluster', () => {
         const highLevelStats = {
           clusterone: {
             count: 2,
             versions: [{ count: 2, version: '7.0.0-alpha1-test12' }],
           },
-        };
+        } as any;
         const usageStats = {
           clusterone: {
             dashboard: { total: 1 },
@@ -428,7 +470,7 @@ describe('Get Kibana Stats', () => {
           },
         };
 
-        expect(combineStats(highLevelStats, usageStats)).to.eql({
+        expect(combineStats(highLevelStats, usageStats)).toStrictEqual({
           clusterone: {
             count: 2,
             dashboard: { total: 1 },
@@ -444,7 +486,7 @@ describe('Get Kibana Stats', () => {
         });
       });
 
-      it('for multiple single clusters', () => {
+      test('for multiple single clusters', () => {
         const highLevelStats = {
           clusterone: {
             count: 2,
@@ -454,7 +496,7 @@ describe('Get Kibana Stats', () => {
             count: 1,
             versions: [{ count: 1, version: '7.0.0-alpha1-test14' }],
           },
-        };
+        } as any;
         const usageStats = {
           clusterone: {
             dashboard: { total: 1 },
@@ -478,7 +520,7 @@ describe('Get Kibana Stats', () => {
           },
         };
 
-        expect(combineStats(highLevelStats, usageStats)).to.eql({
+        expect(combineStats(highLevelStats, usageStats)).toStrictEqual({
           clusterone: {
             count: 2,
             dashboard: { total: 1 },
@@ -508,16 +550,16 @@ describe('Get Kibana Stats', () => {
     });
 
     describe('if usage stats are empty', () => {
-      it('returns just high-level stats', () => {
+      test('returns just high-level stats', () => {
         const highLevelStats = {
           clusterone: {
             count: 2,
             versions: [{ count: 2, version: '7.0.0-alpha1-test12' }],
           },
-        };
+        } as any;
         const usageStats = undefined;
 
-        expect(combineStats(highLevelStats, usageStats)).to.eql({
+        expect(combineStats(highLevelStats, usageStats)).toStrictEqual({
           clusterone: {
             count: 2,
             versions: [{ count: 2, version: '7.0.0-alpha1-test12' }],
@@ -528,64 +570,64 @@ describe('Get Kibana Stats', () => {
   });
 
   describe('Rolls up stats when there are multiple Kibana indices for a cluster', () => {
-    it('by combining the `total` fields where previous was 0', () => {
-      const rollUp = { my_field: { total: 0 } };
+    test('by combining the `total` fields where previous was 0', () => {
+      const rollUp = { my_field: { total: 0 } } as any;
       const addOn = { my_field: { total: 1 } };
 
-      expect(rollUpTotals(rollUp, addOn, 'my_field')).to.eql({ total: 1 });
+      expect(rollUpTotals(rollUp, addOn, 'my_field' as any)).toStrictEqual({ total: 1 });
     });
 
-    it('by combining the `total` fields with > 1 for previous and addOn', () => {
-      const rollUp = { my_field: { total: 1 } };
+    test('by combining the `total` fields with > 1 for previous and addOn', () => {
+      const rollUp = { my_field: { total: 1 } } as any;
       const addOn = { my_field: { total: 3 } };
 
-      expect(rollUpTotals(rollUp, addOn, 'my_field')).to.eql({ total: 4 });
+      expect(rollUpTotals(rollUp, addOn, 'my_field' as any)).toStrictEqual({ total: 4 });
     });
   });
 
   describe('Ensure minimum time difference', () => {
-    it('should return start and end as is when none are provided', () => {
+    test('should return start and end as is when none are provided', () => {
       const { start, end } = ensureTimeSpan(undefined, undefined);
-      expect(start).to.be.undefined;
-      expect(end).to.be.undefined;
+      expect(start).toBe(undefined);
+      expect(end).toBe(undefined);
     });
 
-    it('should return start and end as is when only end is provided', () => {
+    test('should return start and end as is when only end is provided', () => {
       const initialEnd = '2020-01-01T00:00:00Z';
       const { start, end } = ensureTimeSpan(undefined, initialEnd);
-      expect(start).to.be.undefined;
-      expect(end).to.be.equal(initialEnd);
+      expect(start).toBe(undefined);
+      expect(end).toEqual(initialEnd);
     });
 
-    it('should return start and end as is because they are already 24h away', () => {
+    test('should return start and end as is because they are already 24h away', () => {
       const initialStart = '2019-12-31T00:00:00Z';
       const initialEnd = '2020-01-01T00:00:00Z';
       const { start, end } = ensureTimeSpan(initialStart, initialEnd);
-      expect(start).to.be.equal(initialStart);
-      expect(end).to.be.equal(initialEnd);
+      expect(start).toEqual(initialStart);
+      expect(end).toEqual(initialEnd);
     });
 
-    it('should return start and end as is because they are already 24h+ away', () => {
+    test('should return start and end as is because they are already 24h+ away', () => {
       const initialStart = '2019-12-31T00:00:00Z';
       const initialEnd = '2020-01-01T01:00:00Z';
       const { start, end } = ensureTimeSpan(initialStart, initialEnd);
-      expect(start).to.be.equal(initialStart);
-      expect(end).to.be.equal(initialEnd);
+      expect(start).toEqual(initialStart);
+      expect(end).toEqual(initialEnd);
     });
 
-    it('should modify start to a date 24h before end', () => {
+    test('should modify start to a date 24h before end', () => {
       const initialStart = '2020-01-01T00:00:00.000Z';
       const initialEnd = '2020-01-01T01:00:00.000Z';
       const { start, end } = ensureTimeSpan(initialStart, initialEnd);
-      expect(start).to.be.equal('2019-12-31T01:00:00.000Z');
-      expect(end).to.be.equal(initialEnd);
+      expect(start).toEqual('2019-12-31T01:00:00.000Z');
+      expect(end).toEqual(initialEnd);
     });
 
-    it('should modify start to a date 24h before now', () => {
+    test('should modify start to a date 24h before now', () => {
       const initialStart = new Date().toISOString();
       const { start, end } = ensureTimeSpan(initialStart, undefined);
-      expect(start).to.not.be.equal(initialStart);
-      expect(end).to.be.undefined;
+      expect(start).not.toBe(initialStart);
+      expect(end).toBe(undefined);
     });
   });
 });
diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_kibana_stats.js b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_kibana_stats.ts
similarity index 58%
rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_kibana_stats.js
rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_kibana_stats.ts
index 1e22507c5baf4..e2ad64ce04c6b 100644
--- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_kibana_stats.js
+++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_kibana_stats.ts
@@ -5,30 +5,78 @@
  */
 
 import moment from 'moment';
-import { get, isEmpty, omit } from 'lodash';
+import { isEmpty } from 'lodash';
+import { StatsCollectionConfig } from 'src/legacy/core_plugins/telemetry/server/collection_manager';
+import { SearchResponse } from 'elasticsearch';
 import { KIBANA_SYSTEM_ID, TELEMETRY_COLLECTION_INTERVAL } from '../../common/constants';
-import { fetchHighLevelStats, handleHighLevelStatsResponse } from './get_high_level_stats';
+import {
+  fetchHighLevelStats,
+  handleHighLevelStatsResponse,
+  ClustersHighLevelStats,
+  ClusterHighLevelStats,
+} from './get_high_level_stats';
 
-export function rollUpTotals(rolledUp, addOn, field) {
-  const rolledUpTotal = get(rolledUp, [field, 'total'], 0);
-  const addOnTotal = get(addOn, [field, 'total'], 0);
+export function rollUpTotals(
+  rolledUp: ClusterUsageStats,
+  addOn: { [key: string]: { total?: number } | undefined },
+  field: Exclude<keyof ClusterUsageStats, 'plugins' | 'indices'>
+) {
+  const rolledUpTotal = rolledUp[field]?.total || 0;
+  const addOnTotal = addOn[field]?.total || 0;
   return { total: rolledUpTotal + addOnTotal };
 }
-export function rollUpIndices(rolledUp) {
+export function rollUpIndices(rolledUp: ClusterUsageStats) {
   return rolledUp.indices + 1;
 }
 
+export interface KibanaUsageStats {
+  cluster_uuid: string;
+  kibana_stats?: {
+    usage?: {
+      index?: string;
+    } & {
+      [plugin: string]: {
+        total: number;
+      };
+    };
+  };
+}
+
+export interface ClusterUsageStats {
+  dashboard?: { total: number };
+  visualization?: { total: number };
+  search?: { total: number };
+  index_pattern?: { total: number };
+  graph_workspace?: { total: number };
+  timelion_sheet?: { total: number };
+  indices: number;
+  plugins?: {
+    xpack?: unknown;
+    [plugin: string]: unknown;
+  };
+}
+
+export interface ClustersUsageStats {
+  [clusterUuid: string]: ClusterUsageStats | undefined;
+}
+
+export interface KibanaClusterStat extends Partial<ClusterUsageStats>, ClusterHighLevelStats {}
+
+export interface KibanaStats {
+  [clusterUuid: string]: KibanaClusterStat;
+}
+
 /*
  * @param {Object} rawStats
  */
-export function getUsageStats(rawStats) {
+export function getUsageStats(rawStats: SearchResponse<KibanaUsageStats>) {
   const clusterIndexCache = new Set();
-  const rawStatsHits = get(rawStats, 'hits.hits', []);
+  const rawStatsHits = rawStats.hits?.hits || [];
 
   // get usage stats per cluster / .kibana index
   return rawStatsHits.reduce((accum, currInstance) => {
-    const clusterUuid = get(currInstance, '_source.cluster_uuid');
-    const currUsage = get(currInstance, '_source.kibana_stats.usage', {});
+    const clusterUuid = currInstance._source.cluster_uuid;
+    const currUsage = currInstance._source.kibana_stats?.usage || {};
     const clusterIndexCombination = clusterUuid + currUsage.index;
 
     // return early if usage data is empty or if this cluster/index has already been processed
@@ -39,7 +87,7 @@ export function getUsageStats(rawStats) {
 
     // Get the stats that were read from any number of different .kibana indices in the cluster,
     // roll them up into cluster-wide totals
-    const rolledUpStats = get(accum, clusterUuid, { indices: 0 });
+    const rolledUpStats = accum[clusterUuid] || { indices: 0 };
     const stats = {
       dashboard: rollUpTotals(rolledUpStats, currUsage, 'dashboard'),
       visualization: rollUpTotals(rolledUpStats, currUsage, 'visualization'),
@@ -51,21 +99,22 @@ export function getUsageStats(rawStats) {
     };
 
     // Get the stats provided by telemetry collectors.
-    const pluginsNested = omit(currUsage, [
-      'index',
-      'dashboard',
-      'visualization',
-      'search',
-      'index_pattern',
-      'graph_workspace',
-      'timelion_sheet',
-    ]);
+    const {
+      index,
+      dashboard,
+      visualization,
+      search,
+      index_pattern,
+      graph_workspace,
+      timelion_sheet,
+      xpack,
+      ...pluginsTop
+    } = currUsage;
 
     // Stats filtered by telemetry collectors need to be flattened since they're pulled in a generic way.
     // A plugin might not provide flat stats if it implements formatForBulkUpload in its collector.
     // e.g: we want `xpack.reporting` to just be `reporting`
-    const top = omit(pluginsNested, 'xpack');
-    const plugins = { ...top, ...pluginsNested.xpack };
+    const plugins = { ...pluginsTop, ...xpack };
 
     return {
       ...accum,
@@ -74,10 +123,13 @@ export function getUsageStats(rawStats) {
         plugins,
       },
     };
-  }, {});
+  }, {} as ClustersUsageStats);
 }
 
-export function combineStats(highLevelStats, usageStats = {}) {
+export function combineStats(
+  highLevelStats: ClustersHighLevelStats,
+  usageStats: ClustersUsageStats = {}
+) {
   return Object.keys(highLevelStats).reduce((accum, currClusterUuid) => {
     return {
       ...accum,
@@ -86,7 +138,7 @@ export function combineStats(highLevelStats, usageStats = {}) {
         ...usageStats[currClusterUuid],
       },
     };
-  }, {});
+  }, {} as KibanaStats);
 }
 
 /**
@@ -96,7 +148,10 @@ export function combineStats(highLevelStats, usageStats = {}) {
  * @param {date} [start] The start time from which to get the telemetry data
  * @param {date} [end] The end time from which to get the telemetry data
  */
-export function ensureTimeSpan(start, end) {
+export function ensureTimeSpan(
+  start?: StatsCollectionConfig['start'],
+  end?: StatsCollectionConfig['end']
+) {
   // We only care if we have a start date, because that's the limit that might make us lose the document
   if (start) {
     const duration = moment.duration(TELEMETRY_COLLECTION_INTERVAL, 'milliseconds');
@@ -117,9 +172,15 @@ export function ensureTimeSpan(start, end) {
  * Monkey-patch the modules from get_high_level_stats and add in the
  * specialized usage data that comes with kibana stats (kibana_stats.usage).
  */
-export async function getKibanaStats(server, callCluster, clusterUuids, start, end) {
+export async function getKibanaStats(
+  server: StatsCollectionConfig['server'],
+  callCluster: StatsCollectionConfig['callCluster'],
+  clusterUuids: string[],
+  start: StatsCollectionConfig['start'],
+  end: StatsCollectionConfig['end']
+) {
   const { start: safeStart, end: safeEnd } = ensureTimeSpan(start, end);
-  const rawStats = await fetchHighLevelStats(
+  const rawStats = await fetchHighLevelStats<KibanaUsageStats>(
     server,
     callCluster,
     clusterUuids,
diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/register_monitoring_collection.ts b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/register_monitoring_collection.ts
index 49a925d1dad0b..f0fda5229cb5c 100644
--- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/register_monitoring_collection.ts
+++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/register_monitoring_collection.ts
@@ -5,7 +5,6 @@
  */
 
 import { telemetryCollectionManager } from '../../../../../../src/legacy/core_plugins/telemetry/server';
-// @ts-ignore
 import { getAllStats } from './get_all_stats';
 import { getClusterUuids } from './get_cluster_uuids';
 

From f49581ce348743731121a6bed0d26fd14ca76580 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cau=C3=AA=20Marcondes?=
 <55978943+cauemarcondes@users.noreply.github.com>
Date: Mon, 17 Feb 2020 12:05:46 +0000
Subject: [PATCH 004/174] [APM] Divide "Actions menu" into sections to improve
 readability (#56623)

* transaction actions menu

* transaction actions menu

* fixing pr comments

* fixing pr comments

* fixing pr comments

* fixing pr comments

* fixing unit test

* fixing unit test

* using moment to calculate the timestamp

* renaming labels

* Changing section subtitle

* fixing unit tests

* replacing div for react fragment

* refactoring

* removing marginbottom property

* div is needed to remove the margin from the correct element
---
 .../Links/DiscoverLinks/DiscoverLink.tsx      |  29 +-
 .../components/shared/Links/InfraLink.tsx     |  22 +-
 .../TransactionActionMenu.tsx                 | 277 ++++------------
 .../__test__/TransactionActionMenu.test.tsx   |  32 +-
 .../__test__/sections.test.ts                 | 204 ++++++++++++
 .../shared/TransactionActionMenu/sections.ts  | 299 ++++++++++++++++++
 .../apm/public/context/ApmPluginContext.tsx   |   2 +
 .../public/components/action_menu.tsx         |   8 +-
 .../translations/translations/ja-JP.json      |   1 -
 .../translations/translations/zh-CN.json      |   1 -
 10 files changed, 631 insertions(+), 244 deletions(-)
 create mode 100644 x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts
 create mode 100644 x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/sections.ts

diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverLink.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverLink.tsx
index 51d8b43dac0ea..b58a450d26644 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverLink.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverLink.tsx
@@ -5,6 +5,7 @@
  */
 
 import { EuiLink } from '@elastic/eui';
+import { Location } from 'history';
 import React from 'react';
 import url from 'url';
 import rison, { RisonValue } from 'rison-node';
@@ -12,6 +13,7 @@ import { useLocation } from '../../../../hooks/useLocation';
 import { getTimepickerRisonData } from '../rison_helpers';
 import { APM_STATIC_INDEX_PATTERN_ID } from '../../../../../common/index_pattern_constants';
 import { useApmPluginContext } from '../../../../hooks/useApmPluginContext';
+import { AppMountContextBasePath } from '../../../../context/ApmPluginContext';
 
 interface Props {
   query: {
@@ -30,10 +32,15 @@ interface Props {
   children: React.ReactNode;
 }
 
-export function DiscoverLink({ query = {}, ...rest }: Props) {
-  const { core } = useApmPluginContext();
-  const location = useLocation();
-
+export const getDiscoverHref = ({
+  basePath,
+  location,
+  query
+}: {
+  basePath: AppMountContextBasePath;
+  location: Location;
+  query: Props['query'];
+}) => {
   const risonQuery = {
     _g: getTimepickerRisonData(location.search),
     _a: {
@@ -43,11 +50,23 @@ export function DiscoverLink({ query = {}, ...rest }: Props) {
   };
 
   const href = url.format({
-    pathname: core.http.basePath.prepend('/app/kibana'),
+    pathname: basePath.prepend('/app/kibana'),
     hash: `/discover?_g=${rison.encode(risonQuery._g)}&_a=${rison.encode(
       risonQuery._a as RisonValue
     )}`
   });
+  return href;
+};
+
+export function DiscoverLink({ query = {}, ...rest }: Props) {
+  const { core } = useApmPluginContext();
+  const location = useLocation();
+
+  const href = getDiscoverHref({
+    basePath: core.http.basePath,
+    query,
+    location
+  });
 
   return <EuiLink {...rest} href={href} />;
 }
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/InfraLink.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/InfraLink.tsx
index 8ff5e3010d6cc..e4f3557a2ce51 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Links/InfraLink.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/InfraLink.tsx
@@ -10,11 +10,13 @@ import React from 'react';
 import url from 'url';
 import { fromQuery } from './url_helpers';
 import { useApmPluginContext } from '../../../hooks/useApmPluginContext';
+import { AppMountContextBasePath } from '../../../context/ApmPluginContext';
 
 interface InfraQueryParams {
   time?: number;
   from?: number;
   to?: number;
+  filter?: string;
 }
 
 interface Props extends EuiLinkAnchorProps {
@@ -23,12 +25,24 @@ interface Props extends EuiLinkAnchorProps {
   children?: React.ReactNode;
 }
 
-export function InfraLink({ path, query = {}, ...rest }: Props) {
-  const { core } = useApmPluginContext();
+export const getInfraHref = ({
+  basePath,
+  query,
+  path
+}: {
+  basePath: AppMountContextBasePath;
+  query: InfraQueryParams;
+  path?: string;
+}) => {
   const nextSearch = fromQuery(query);
-  const href = url.format({
-    pathname: core.http.basePath.prepend('/app/infra'),
+  return url.format({
+    pathname: basePath.prepend('/app/infra'),
     hash: compact([path, nextSearch]).join('?')
   });
+};
+
+export function InfraLink({ path, query = {}, ...rest }: Props) {
+  const { core } = useApmPluginContext();
+  const href = getInfraHref({ basePath: core.http.basePath, query, path });
   return <EuiLink {...rest} href={href} />;
 }
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx
index 040d29aaa56dd..99f0b0d4fc223 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx
@@ -4,240 +4,85 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import {
-  EuiButtonEmpty,
-  EuiContextMenuItem,
-  EuiContextMenuPanel,
-  EuiFlexGroup,
-  EuiFlexItem,
-  EuiIcon,
-  EuiPopover,
-  EuiLink
-} from '@elastic/eui';
-import url from 'url';
+import { EuiButtonEmpty } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
-import React, { useState, FunctionComponent } from 'react';
-import { pick } from 'lodash';
+import React, { FunctionComponent, useState } from 'react';
+import {
+  ActionMenu,
+  ActionMenuDivider,
+  Section,
+  SectionLink,
+  SectionLinks,
+  SectionSubtitle,
+  SectionTitle
+} from '../../../../../../../plugins/observability/public';
 import { Transaction } from '../../../../typings/es_schemas/ui/Transaction';
-import { DiscoverTransactionLink } from '../Links/DiscoverLinks/DiscoverTransactionLink';
-import { InfraLink } from '../Links/InfraLink';
-import { useUrlParams } from '../../../hooks/useUrlParams';
-import { fromQuery } from '../Links/url_helpers';
 import { useApmPluginContext } from '../../../hooks/useApmPluginContext';
-
-function getInfraMetricsQuery(transaction: Transaction) {
-  const plus5 = new Date(transaction['@timestamp']);
-  const minus5 = new Date(plus5.getTime());
-
-  plus5.setMinutes(plus5.getMinutes() + 5);
-  minus5.setMinutes(minus5.getMinutes() - 5);
-
-  return {
-    from: minus5.getTime(),
-    to: plus5.getTime()
-  };
-}
-
-function ActionMenuButton({ onClick }: { onClick: () => void }) {
-  return (
-    <EuiButtonEmpty iconType="arrowDown" iconSide="right" onClick={onClick}>
-      {i18n.translate('xpack.apm.transactionActionMenu.actionsButtonLabel', {
-        defaultMessage: 'Actions'
-      })}
-    </EuiButtonEmpty>
-  );
-}
+import { useLocation } from '../../../hooks/useLocation';
+import { useUrlParams } from '../../../hooks/useUrlParams';
+import { getSections } from './sections';
 
 interface Props {
   readonly transaction: Transaction;
 }
 
-interface InfraConfigItem {
-  icon: string;
-  label: string;
-  condition?: boolean;
-  path: string;
-  query: Record<string, any>;
-}
-
-export const TransactionActionMenu: FunctionComponent<Props> = (
-  props: Props
-) => {
-  const { transaction } = props;
-
+const ActionMenuButton = ({ onClick }: { onClick: () => void }) => (
+  <EuiButtonEmpty iconType="arrowDown" iconSide="right" onClick={onClick}>
+    {i18n.translate('xpack.apm.transactionActionMenu.actionsButtonLabel', {
+      defaultMessage: 'Actions'
+    })}
+  </EuiButtonEmpty>
+);
+
+export const TransactionActionMenu: FunctionComponent<Props> = ({
+  transaction
+}: Props) => {
   const { core } = useApmPluginContext();
-
-  const [isOpen, setIsOpen] = useState(false);
-
+  const location = useLocation();
   const { urlParams } = useUrlParams();
 
-  const hostName = transaction.host?.hostname;
-  const podId = transaction.kubernetes?.pod.uid;
-  const containerId = transaction.container?.id;
-
-  const time = Math.round(transaction.timestamp.us / 1000);
-  const infraMetricsQuery = getInfraMetricsQuery(transaction);
-
-  const infraConfigItems: InfraConfigItem[] = [
-    {
-      icon: 'logsApp',
-      label: i18n.translate(
-        'xpack.apm.transactionActionMenu.showPodLogsLinkLabel',
-        { defaultMessage: 'Show pod logs' }
-      ),
-      condition: !!podId,
-      path: `/link-to/pod-logs/${podId}`,
-      query: { time }
-    },
-    {
-      icon: 'logsApp',
-      label: i18n.translate(
-        'xpack.apm.transactionActionMenu.showContainerLogsLinkLabel',
-        { defaultMessage: 'Show container logs' }
-      ),
-      condition: !!containerId,
-      path: `/link-to/container-logs/${containerId}`,
-      query: { time }
-    },
-    {
-      icon: 'logsApp',
-      label: i18n.translate(
-        'xpack.apm.transactionActionMenu.showHostLogsLinkLabel',
-        { defaultMessage: 'Show host logs' }
-      ),
-      condition: !!hostName,
-      path: `/link-to/host-logs/${hostName}`,
-      query: { time }
-    },
-    {
-      icon: 'logsApp',
-      label: i18n.translate(
-        'xpack.apm.transactionActionMenu.showTraceLogsLinkLabel',
-        { defaultMessage: 'Show trace logs' }
-      ),
-      condition: true,
-      path: `/link-to/logs`,
-      query: {
-        time,
-        filter: `trace.id:"${transaction.trace.id}" OR ${transaction.trace.id}`
-      }
-    },
-    {
-      icon: 'metricsApp',
-      label: i18n.translate(
-        'xpack.apm.transactionActionMenu.showPodMetricsLinkLabel',
-        { defaultMessage: 'Show pod metrics' }
-      ),
-      condition: !!podId,
-      path: `/link-to/pod-detail/${podId}`,
-      query: infraMetricsQuery
-    },
-    {
-      icon: 'metricsApp',
-      label: i18n.translate(
-        'xpack.apm.transactionActionMenu.showContainerMetricsLinkLabel',
-        { defaultMessage: 'Show container metrics' }
-      ),
-      condition: !!containerId,
-      path: `/link-to/container-detail/${containerId}`,
-      query: infraMetricsQuery
-    },
-    {
-      icon: 'metricsApp',
-      label: i18n.translate(
-        'xpack.apm.transactionActionMenu.showHostMetricsLinkLabel',
-        { defaultMessage: 'Show host metrics' }
-      ),
-      condition: !!hostName,
-      path: `/link-to/host-detail/${hostName}`,
-      query: infraMetricsQuery
-    }
-  ];
-
-  const infraItems = infraConfigItems.map(
-    ({ icon, label, condition, path, query }, index) => ({
-      icon,
-      key: `infra-link-${index}`,
-      child: (
-        <InfraLink path={path} query={query}>
-          {label}
-        </InfraLink>
-      ),
-      condition
-    })
-  );
+  const [isOpen, setIsOpen] = useState(false);
 
-  const uptimeLink = url.format({
-    pathname: core.http.basePath.prepend('/app/uptime'),
-    hash: `/?${fromQuery(
-      pick(
-        {
-          dateRangeStart: urlParams.rangeFrom,
-          dateRangeEnd: urlParams.rangeTo,
-          search: `url.domain:"${transaction.url?.domain}"`
-        },
-        (val: string) => !!val
-      )
-    )}`
+  const sections = getSections({
+    transaction,
+    basePath: core.http.basePath,
+    location,
+    urlParams
   });
 
-  const menuItems = [
-    ...infraItems,
-    {
-      icon: 'discoverApp',
-      key: 'discover-transaction',
-      condition: true,
-      child: (
-        <DiscoverTransactionLink transaction={transaction}>
-          {i18n.translate(
-            'xpack.apm.transactionActionMenu.viewSampleDocumentLinkLabel',
-            {
-              defaultMessage: 'View sample document'
-            }
-          )}
-        </DiscoverTransactionLink>
-      )
-    },
-    {
-      icon: 'uptimeApp',
-      key: 'uptime',
-      child: (
-        <EuiLink href={uptimeLink}>
-          {i18n.translate('xpack.apm.transactionActionMenu.viewInUptime', {
-            defaultMessage: 'View monitor status'
-          })}
-        </EuiLink>
-      ),
-      condition: transaction.url?.domain
-    }
-  ]
-    .filter(({ condition }) => condition)
-    .map(({ icon, key, child }) => (
-      <EuiContextMenuItem icon={icon} key={key}>
-        <EuiFlexGroup gutterSize="s">
-          <EuiFlexItem>{child}</EuiFlexItem>
-          <EuiFlexItem grow={false}>
-            <EuiIcon type="popout" />
-          </EuiFlexItem>
-        </EuiFlexGroup>
-      </EuiContextMenuItem>
-    ));
-
   return (
-    <EuiPopover
+    <ActionMenu
       id="transactionActionMenu"
-      button={<ActionMenuButton onClick={() => setIsOpen(!isOpen)} />}
-      isOpen={isOpen}
       closePopover={() => setIsOpen(false)}
+      isOpen={isOpen}
       anchorPosition="downRight"
-      panelPaddingSize="none"
+      button={<ActionMenuButton onClick={() => setIsOpen(!isOpen)} />}
     >
-      <EuiContextMenuPanel
-        items={menuItems}
-        title={i18n.translate('xpack.apm.transactionActionMenu.actionsLabel', {
-          defaultMessage: 'Actions'
-        })}
-      />
-    </EuiPopover>
+      {sections.map((section, idx) => {
+        const isLastSection = idx !== sections.length - 1;
+        return (
+          <div key={idx}>
+            {section.map(item => (
+              <Section key={item.key}>
+                {item.title && <SectionTitle>{item.title}</SectionTitle>}
+                {item.subtitle && (
+                  <SectionSubtitle>{item.subtitle}</SectionSubtitle>
+                )}
+                <SectionLinks>
+                  {item.actions.map(action => (
+                    <SectionLink
+                      key={action.key}
+                      label={action.label}
+                      href={action.href}
+                    />
+                  ))}
+                </SectionLinks>
+              </Section>
+            ))}
+            {isLastSection && <ActionMenuDivider />}
+          </div>
+        );
+      })}
+    </ActionMenu>
   );
 };
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx
index 2bfa5cf1274fa..e9f89034f58ee 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx
@@ -36,7 +36,7 @@ describe('TransactionActionMenu component', () => {
       Transactions.transactionWithMinimalData
     );
 
-    expect(queryByText('Show trace logs')).not.toBeNull();
+    expect(queryByText('Trace logs')).not.toBeNull();
   });
 
   it('should not render the pod links when there is no pod id', async () => {
@@ -44,8 +44,8 @@ describe('TransactionActionMenu component', () => {
       Transactions.transactionWithMinimalData
     );
 
-    expect(queryByText('Show pod logs')).toBeNull();
-    expect(queryByText('Show pod metrics')).toBeNull();
+    expect(queryByText('Pod logs')).toBeNull();
+    expect(queryByText('Pod metrics')).toBeNull();
   });
 
   it('should render the pod links when there is a pod id', async () => {
@@ -53,8 +53,8 @@ describe('TransactionActionMenu component', () => {
       Transactions.transactionWithKubernetesData
     );
 
-    expect(queryByText('Show pod logs')).not.toBeNull();
-    expect(queryByText('Show pod metrics')).not.toBeNull();
+    expect(queryByText('Pod logs')).not.toBeNull();
+    expect(queryByText('Pod metrics')).not.toBeNull();
   });
 
   it('should not render the container links when there is no container id', async () => {
@@ -62,8 +62,8 @@ describe('TransactionActionMenu component', () => {
       Transactions.transactionWithMinimalData
     );
 
-    expect(queryByText('Show container logs')).toBeNull();
-    expect(queryByText('Show container metrics')).toBeNull();
+    expect(queryByText('Container logs')).toBeNull();
+    expect(queryByText('Container metrics')).toBeNull();
   });
 
   it('should render the container links when there is a container id', async () => {
@@ -71,8 +71,8 @@ describe('TransactionActionMenu component', () => {
       Transactions.transactionWithContainerData
     );
 
-    expect(queryByText('Show container logs')).not.toBeNull();
-    expect(queryByText('Show container metrics')).not.toBeNull();
+    expect(queryByText('Container logs')).not.toBeNull();
+    expect(queryByText('Container metrics')).not.toBeNull();
   });
 
   it('should not render the host links when there is no hostname', async () => {
@@ -80,8 +80,8 @@ describe('TransactionActionMenu component', () => {
       Transactions.transactionWithMinimalData
     );
 
-    expect(queryByText('Show host logs')).toBeNull();
-    expect(queryByText('Show host metrics')).toBeNull();
+    expect(queryByText('Host logs')).toBeNull();
+    expect(queryByText('Host metrics')).toBeNull();
   });
 
   it('should render the host links when there is a hostname', async () => {
@@ -89,8 +89,8 @@ describe('TransactionActionMenu component', () => {
       Transactions.transactionWithHostData
     );
 
-    expect(queryByText('Show host logs')).not.toBeNull();
-    expect(queryByText('Show host metrics')).not.toBeNull();
+    expect(queryByText('Host logs')).not.toBeNull();
+    expect(queryByText('Host metrics')).not.toBeNull();
   });
 
   it('should not render the uptime link if there is no url available', async () => {
@@ -98,7 +98,7 @@ describe('TransactionActionMenu component', () => {
       Transactions.transactionWithMinimalData
     );
 
-    expect(queryByText('View monitor status')).toBeNull();
+    expect(queryByText('Status')).toBeNull();
   });
 
   it('should not render the uptime link if there is no domain available', async () => {
@@ -106,7 +106,7 @@ describe('TransactionActionMenu component', () => {
       Transactions.transactionWithUrlWithoutDomain
     );
 
-    expect(queryByText('View monitor status')).toBeNull();
+    expect(queryByText('Status')).toBeNull();
   });
 
   it('should render the uptime link if there is a url with a domain', async () => {
@@ -114,7 +114,7 @@ describe('TransactionActionMenu component', () => {
       Transactions.transactionWithUrlAndDomain
     );
 
-    expect(queryByText('View monitor status')).not.toBeNull();
+    expect(queryByText('Status')).not.toBeNull();
   });
 
   it('should match the snapshot', async () => {
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts
new file mode 100644
index 0000000000000..52c2d27eabb82
--- /dev/null
+++ b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts
@@ -0,0 +1,204 @@
+/*
+ * 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 { Location } from 'history';
+import { getSections } from '../sections';
+import { Transaction } from '../../../../../typings/es_schemas/ui/Transaction';
+import { AppMountContextBasePath } from '../../../../context/ApmPluginContext';
+
+describe('Transaction action menu', () => {
+  const basePath = ({
+    prepend: jest.fn()
+  } as unknown) as AppMountContextBasePath;
+  const date = '2020-02-06T11:00:00.000Z';
+  const timestamp = { us: new Date(date).getTime() };
+
+  it('shows required sections only', () => {
+    const transaction = ({
+      timestamp,
+      trace: { id: '123' },
+      transaction: { id: '123' },
+      '@timestamp': date
+    } as unknown) as Transaction;
+    expect(
+      getSections({
+        transaction,
+        basePath,
+        location: ({} as unknown) as Location,
+        urlParams: {}
+      })
+    ).toEqual([
+      [
+        {
+          key: 'traceDetails',
+          title: 'Trace details',
+          subtitle: 'View trace logs to get further details.',
+          actions: [
+            {
+              key: 'traceLogs',
+              label: 'Trace logs',
+              href:
+                '#/link-to/logs?time=1580986800&filter=trace.id:%22123%22%20OR%20123',
+              condition: true
+            }
+          ]
+        }
+      ],
+      [
+        {
+          key: 'kibana',
+          actions: [
+            {
+              key: 'sampleDocument',
+              label: 'View sample document',
+              href:
+                '#/discover?_g=(refreshInterval:(pause:true,value:\'0\'),time:(from:now-24h,to:now))&_a=(index:apm_static_index_pattern_id,interval:auto,query:(language:kuery,query:\'processor.event:"transaction" AND transaction.id:"123" AND trace.id:"123"\'))',
+              condition: true
+            }
+          ]
+        }
+      ]
+    ]);
+  });
+
+  it('shows pod and required sections only', () => {
+    const transaction = ({
+      kubernetes: { pod: { uid: '123' } },
+      timestamp,
+      trace: { id: '123' },
+      transaction: { id: '123' },
+      '@timestamp': date
+    } as unknown) as Transaction;
+    expect(
+      getSections({
+        transaction,
+        basePath,
+        location: ({} as unknown) as Location,
+        urlParams: {}
+      })
+    ).toEqual([
+      [
+        {
+          key: 'podDetails',
+          title: 'Pod details',
+          subtitle:
+            'View logs and metrics for this pod to get further details.',
+          actions: [
+            {
+              key: 'podLogs',
+              label: 'Pod logs',
+              href: '#/link-to/pod-logs/123?time=1580986800',
+              condition: true
+            },
+            {
+              key: 'podMetrics',
+              label: 'Pod metrics',
+              href:
+                '#/link-to/pod-detail/123?from=1580986500000&to=1580987100000',
+              condition: true
+            }
+          ]
+        },
+        {
+          key: 'traceDetails',
+          title: 'Trace details',
+          subtitle: 'View trace logs to get further details.',
+          actions: [
+            {
+              key: 'traceLogs',
+              label: 'Trace logs',
+              href:
+                '#/link-to/logs?time=1580986800&filter=trace.id:%22123%22%20OR%20123',
+              condition: true
+            }
+          ]
+        }
+      ],
+      [
+        {
+          key: 'kibana',
+          actions: [
+            {
+              key: 'sampleDocument',
+              label: 'View sample document',
+              href:
+                '#/discover?_g=(refreshInterval:(pause:true,value:\'0\'),time:(from:now-24h,to:now))&_a=(index:apm_static_index_pattern_id,interval:auto,query:(language:kuery,query:\'processor.event:"transaction" AND transaction.id:"123" AND trace.id:"123"\'))',
+              condition: true
+            }
+          ]
+        }
+      ]
+    ]);
+  });
+
+  it('shows host and required sections only', () => {
+    const transaction = ({
+      host: { hostname: 'foo' },
+      timestamp,
+      trace: { id: '123' },
+      transaction: { id: '123' },
+      '@timestamp': date
+    } as unknown) as Transaction;
+    expect(
+      getSections({
+        transaction,
+        basePath,
+        location: ({} as unknown) as Location,
+        urlParams: {}
+      })
+    ).toEqual([
+      [
+        {
+          key: 'hostDetails',
+          title: 'Host details',
+          subtitle: 'View host logs and metrics to get further details.',
+          actions: [
+            {
+              key: 'hostLogs',
+              label: 'Host logs',
+              href: '#/link-to/host-logs/foo?time=1580986800',
+              condition: true
+            },
+            {
+              key: 'hostMetrics',
+              label: 'Host metrics',
+              href:
+                '#/link-to/host-detail/foo?from=1580986500000&to=1580987100000',
+              condition: true
+            }
+          ]
+        },
+        {
+          key: 'traceDetails',
+          title: 'Trace details',
+          subtitle: 'View trace logs to get further details.',
+          actions: [
+            {
+              key: 'traceLogs',
+              label: 'Trace logs',
+              href:
+                '#/link-to/logs?time=1580986800&filter=trace.id:%22123%22%20OR%20123',
+              condition: true
+            }
+          ]
+        }
+      ],
+      [
+        {
+          key: 'kibana',
+          actions: [
+            {
+              key: 'sampleDocument',
+              label: 'View sample document',
+              href:
+                '#/discover?_g=(refreshInterval:(pause:true,value:\'0\'),time:(from:now-24h,to:now))&_a=(index:apm_static_index_pattern_id,interval:auto,query:(language:kuery,query:\'processor.event:"transaction" AND transaction.id:"123" AND trace.id:"123"\'))',
+              condition: true
+            }
+          ]
+        }
+      ]
+    ]);
+  });
+});
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/sections.ts b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/sections.ts
new file mode 100644
index 0000000000000..77445a2600960
--- /dev/null
+++ b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/sections.ts
@@ -0,0 +1,299 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import { i18n } from '@kbn/i18n';
+import { Location } from 'history';
+import { pick, isEmpty } from 'lodash';
+import moment from 'moment';
+import url from 'url';
+import { Transaction } from '../../../../typings/es_schemas/ui/Transaction';
+import { IUrlParams } from '../../../context/UrlParamsContext/types';
+import { getDiscoverHref } from '../Links/DiscoverLinks/DiscoverLink';
+import { getDiscoverQuery } from '../Links/DiscoverLinks/DiscoverTransactionLink';
+import { getInfraHref } from '../Links/InfraLink';
+import { fromQuery } from '../Links/url_helpers';
+import { AppMountContextBasePath } from '../../../context/ApmPluginContext';
+
+function getInfraMetricsQuery(transaction: Transaction) {
+  const timestamp = new Date(transaction['@timestamp']).getTime();
+  const fiveMinutes = moment.duration(5, 'minutes').asMilliseconds();
+
+  return {
+    from: timestamp - fiveMinutes,
+    to: timestamp + fiveMinutes
+  };
+}
+
+interface Action {
+  key: string;
+  label: string;
+  href: string;
+  condition: boolean;
+}
+
+interface Section {
+  key: string;
+  title?: string;
+  subtitle?: string;
+  actions: Action[];
+}
+
+type SectionRecord = Record<string, Section[]>;
+
+export const getSections = ({
+  transaction,
+  basePath,
+  location,
+  urlParams
+}: {
+  transaction: Transaction;
+  basePath: AppMountContextBasePath;
+  location: Location;
+  urlParams: IUrlParams;
+}) => {
+  const hostName = transaction.host?.hostname;
+  const podId = transaction.kubernetes?.pod.uid;
+  const containerId = transaction.container?.id;
+
+  const time = Math.round(transaction.timestamp.us / 1000);
+  const infraMetricsQuery = getInfraMetricsQuery(transaction);
+
+  const uptimeLink = url.format({
+    pathname: basePath.prepend('/app/uptime'),
+    hash: `/?${fromQuery(
+      pick(
+        {
+          dateRangeStart: urlParams.rangeFrom,
+          dateRangeEnd: urlParams.rangeTo,
+          search: `url.domain:"${transaction.url?.domain}"`
+        },
+        (val: string) => !isEmpty(val)
+      )
+    )}`
+  });
+
+  const podActions: Action[] = [
+    {
+      key: 'podLogs',
+      label: i18n.translate(
+        'xpack.apm.transactionActionMenu.showPodLogsLinkLabel',
+        { defaultMessage: 'Pod logs' }
+      ),
+      href: getInfraHref({
+        basePath,
+        path: `/link-to/pod-logs/${podId}`,
+        query: { time }
+      }),
+      condition: !!podId
+    },
+    {
+      key: 'podMetrics',
+      label: i18n.translate(
+        'xpack.apm.transactionActionMenu.showPodMetricsLinkLabel',
+        { defaultMessage: 'Pod metrics' }
+      ),
+      href: getInfraHref({
+        basePath,
+        path: `/link-to/pod-detail/${podId}`,
+        query: infraMetricsQuery
+      }),
+      condition: !!podId
+    }
+  ];
+
+  const containerActions: Action[] = [
+    {
+      key: 'containerLogs',
+      label: i18n.translate(
+        'xpack.apm.transactionActionMenu.showContainerLogsLinkLabel',
+        { defaultMessage: 'Container logs' }
+      ),
+      href: getInfraHref({
+        basePath,
+        path: `/link-to/container-logs/${containerId}`,
+        query: { time }
+      }),
+      condition: !!containerId
+    },
+    {
+      key: 'containerMetrics',
+      label: i18n.translate(
+        'xpack.apm.transactionActionMenu.showContainerMetricsLinkLabel',
+        { defaultMessage: 'Container metrics' }
+      ),
+      href: getInfraHref({
+        basePath,
+        path: `/link-to/container-detail/${containerId}`,
+        query: infraMetricsQuery
+      }),
+      condition: !!containerId
+    }
+  ];
+
+  const hostActions: Action[] = [
+    {
+      key: 'hostLogs',
+      label: i18n.translate(
+        'xpack.apm.transactionActionMenu.showHostLogsLinkLabel',
+        { defaultMessage: 'Host logs' }
+      ),
+      href: getInfraHref({
+        basePath,
+        path: `/link-to/host-logs/${hostName}`,
+        query: { time }
+      }),
+      condition: !!hostName
+    },
+    {
+      key: 'hostMetrics',
+      label: i18n.translate(
+        'xpack.apm.transactionActionMenu.showHostMetricsLinkLabel',
+        { defaultMessage: 'Host metrics' }
+      ),
+      href: getInfraHref({
+        basePath,
+        path: `/link-to/host-detail/${hostName}`,
+        query: infraMetricsQuery
+      }),
+      condition: !!hostName
+    }
+  ];
+
+  const logActions: Action[] = [
+    {
+      key: 'traceLogs',
+      label: i18n.translate(
+        'xpack.apm.transactionActionMenu.showTraceLogsLinkLabel',
+        { defaultMessage: 'Trace logs' }
+      ),
+      href: getInfraHref({
+        basePath,
+        path: `/link-to/logs`,
+        query: {
+          time,
+          filter: `trace.id:"${transaction.trace.id}" OR ${transaction.trace.id}`
+        }
+      }),
+      condition: true
+    }
+  ];
+
+  const uptimeActions: Action[] = [
+    {
+      key: 'monitorStatus',
+      label: i18n.translate('xpack.apm.transactionActionMenu.viewInUptime', {
+        defaultMessage: 'Status'
+      }),
+      href: uptimeLink,
+      condition: !!transaction.url?.domain
+    }
+  ];
+
+  const kibanaActions: Action[] = [
+    {
+      key: 'sampleDocument',
+      label: i18n.translate(
+        'xpack.apm.transactionActionMenu.viewSampleDocumentLinkLabel',
+        {
+          defaultMessage: 'View sample document'
+        }
+      ),
+      href: getDiscoverHref({
+        basePath,
+        query: getDiscoverQuery(transaction),
+        location
+      }),
+      condition: true
+    }
+  ];
+
+  const sectionRecord: SectionRecord = {
+    observability: [
+      {
+        key: 'podDetails',
+        title: i18n.translate('xpack.apm.transactionActionMenu.pod.title', {
+          defaultMessage: 'Pod details'
+        }),
+        subtitle: i18n.translate(
+          'xpack.apm.transactionActionMenu.pod.subtitle',
+          {
+            defaultMessage:
+              'View logs and metrics for this pod to get further details.'
+          }
+        ),
+        actions: podActions
+      },
+      {
+        key: 'containerDetails',
+        title: i18n.translate(
+          'xpack.apm.transactionActionMenu.container.title',
+          {
+            defaultMessage: 'Container details'
+          }
+        ),
+        subtitle: i18n.translate(
+          'xpack.apm.transactionActionMenu.container.subtitle',
+          {
+            defaultMessage:
+              'View logs and metrics for this container to get further details.'
+          }
+        ),
+        actions: containerActions
+      },
+      {
+        key: 'hostDetails',
+        title: i18n.translate('xpack.apm.transactionActionMenu.host.title', {
+          defaultMessage: 'Host details'
+        }),
+        subtitle: i18n.translate(
+          'xpack.apm.transactionActionMenu.host.subtitle',
+          {
+            defaultMessage: 'View host logs and metrics to get further details.'
+          }
+        ),
+        actions: hostActions
+      },
+      {
+        key: 'traceDetails',
+        title: i18n.translate('xpack.apm.transactionActionMenu.trace.title', {
+          defaultMessage: 'Trace details'
+        }),
+        subtitle: i18n.translate(
+          'xpack.apm.transactionActionMenu.trace.subtitle',
+          {
+            defaultMessage: 'View trace logs to get further details.'
+          }
+        ),
+        actions: logActions
+      },
+      {
+        key: 'statusDetails',
+        title: i18n.translate('xpack.apm.transactionActionMenu.status.title', {
+          defaultMessage: 'Status details'
+        }),
+        subtitle: i18n.translate(
+          'xpack.apm.transactionActionMenu.status.subtitle',
+          {
+            defaultMessage: 'View status to get further details.'
+          }
+        ),
+        actions: uptimeActions
+      }
+    ],
+    kibana: [{ key: 'kibana', actions: kibanaActions }]
+  };
+
+  // Filter out actions that shouldnt be shown and sections without any actions.
+  return Object.values(sectionRecord)
+    .map(sections =>
+      sections
+        .map(section => ({
+          ...section,
+          actions: section.actions.filter(action => action.condition)
+        }))
+        .filter(section => !isEmpty(section.actions))
+    )
+    .filter(sections => !isEmpty(sections));
+};
diff --git a/x-pack/legacy/plugins/apm/public/context/ApmPluginContext.tsx b/x-pack/legacy/plugins/apm/public/context/ApmPluginContext.tsx
index 86efd9b31974e..7a9aaa6dfb920 100644
--- a/x-pack/legacy/plugins/apm/public/context/ApmPluginContext.tsx
+++ b/x-pack/legacy/plugins/apm/public/context/ApmPluginContext.tsx
@@ -8,6 +8,8 @@ import { createContext } from 'react';
 import { AppMountContext, PackageInfo } from 'kibana/public';
 import { ApmPluginSetupDeps, ConfigSchema } from '../new-platform/plugin';
 
+export type AppMountContextBasePath = AppMountContext['core']['http']['basePath'];
+
 export interface ApmPluginContextValue {
   config: ConfigSchema;
   core: AppMountContext['core'];
diff --git a/x-pack/plugins/observability/public/components/action_menu.tsx b/x-pack/plugins/observability/public/components/action_menu.tsx
index 6e964dde3aecf..a5f59ecd5506f 100644
--- a/x-pack/plugins/observability/public/components/action_menu.tsx
+++ b/x-pack/plugins/observability/public/components/action_menu.tsx
@@ -16,6 +16,7 @@ import {
 
 import React, { HTMLAttributes } from 'react';
 import { EuiListGroupItemProps } from '@elastic/eui/src/components/list_group/list_group_item';
+import styled from 'styled-components';
 
 type Props = EuiPopoverProps & HTMLAttributes<HTMLDivElement>;
 
@@ -45,7 +46,12 @@ export const SectionLinks: React.FC<{}> = props => (
 
 export const SectionSpacer: React.FC<{}> = () => <EuiSpacer size={'l'} />;
 
-export const Section: React.FC<{}> = props => <>{props.children}</>;
+export const Section = styled.div`
+  margin-bottom: 24px;
+  &:last-of-type {
+    margin-bottom: 0;
+  }
+`;
 
 export type SectionLinkProps = EuiListGroupItemProps;
 export const SectionLink: React.FC<EuiListGroupItemProps> = props => (
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index ee2abeff74496..a59a6dbf12566 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -3945,7 +3945,6 @@
     "xpack.apm.tracesTable.tracesPerMinuteColumnLabel": "1 分あたりのトレース",
     "xpack.apm.tracesTable.tracesPerMinuteUnitLabel": "1分あたりトランザクション数",
     "xpack.apm.transactionActionMenu.actionsButtonLabel": "アクション",
-    "xpack.apm.transactionActionMenu.actionsLabel": "アクション",
     "xpack.apm.transactionActionMenu.showContainerLogsLinkLabel": "コンテナーログを表示",
     "xpack.apm.transactionActionMenu.showContainerMetricsLinkLabel": "コンテナーメトリックを表示",
     "xpack.apm.transactionActionMenu.showHostLogsLinkLabel": "ホストログを表示",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 3210c619d3e52..dbc9cab4261d8 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -3945,7 +3945,6 @@
     "xpack.apm.tracesTable.tracesPerMinuteColumnLabel": "每分钟追溯次数",
     "xpack.apm.tracesTable.tracesPerMinuteUnitLabel": "tpm",
     "xpack.apm.transactionActionMenu.actionsButtonLabel": "操作",
-    "xpack.apm.transactionActionMenu.actionsLabel": "操作",
     "xpack.apm.transactionActionMenu.showContainerLogsLinkLabel": "显示容器日志",
     "xpack.apm.transactionActionMenu.showContainerMetricsLinkLabel": "显示容器指标",
     "xpack.apm.transactionActionMenu.showHostLogsLinkLabel": "显示主机日志",

From 9388ff7b43d5743dfc8933690f1c67a7befef69d Mon Sep 17 00:00:00 2001
From: Joe Reuter <johannes.reuter@elastic.co>
Date: Mon, 17 Feb 2020 13:52:53 +0100
Subject: [PATCH 005/174] Fix auto refresh in visualizations and lens (#57667)

---
 .../visualize/np_ready/editor/editor.js       |  8 -----
 .../public/embeddable/visualize_embeddable.ts |  8 +++++
 .../visualize_embeddable_factory.tsx          |  7 +++-
 .../public/np_ready/public/mocks.ts           |  1 +
 .../public/np_ready/public/plugin.ts          | 13 +++++--
 .../timefilter/timefilter_service.mock.ts     |  3 +-
 .../embeddable/embeddable.test.tsx            | 35 ++++++++++++++++++-
 .../embeddable/embeddable.tsx                 | 15 +++++++-
 .../embeddable/embeddable_factory.ts          |  8 ++++-
 .../public/editor_frame_service/mocks.tsx     |  9 ++---
 .../public/editor_frame_service/service.tsx   |  1 +
 11 files changed, 86 insertions(+), 22 deletions(-)

diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js
index 27fb9b63843c4..657104344662f 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js
+++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js
@@ -441,14 +441,6 @@ function VisualizeAppController(
       })
     );
 
-    subscriptions.add(
-      subscribeWithScope($scope, timefilter.getAutoRefreshFetch$(), {
-        next: () => {
-          $scope.vis.forceReload();
-        },
-      })
-    );
-
     $scope.$on('$destroy', () => {
       if ($scope._handler) {
         $scope._handler.destroy();
diff --git a/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts
index 5e593398333c9..fddcf70c30605 100644
--- a/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts
+++ b/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts
@@ -34,6 +34,7 @@ import {
   esFilters,
   Filter,
   ISearchSource,
+  TimefilterContract,
 } from '../../../../../plugins/data/public';
 import {
   EmbeddableInput,
@@ -106,8 +107,10 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut
   private vis: Vis;
   private domNode: any;
   public readonly type = VISUALIZE_EMBEDDABLE_TYPE;
+  private autoRefreshFetchSubscription: Subscription;
 
   constructor(
+    timefilter: TimefilterContract,
     {
       savedVisualization,
       editUrl,
@@ -151,6 +154,10 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut
 
     this.vis._setUiState(this.uiState);
 
+    this.autoRefreshFetchSubscription = timefilter
+      .getAutoRefreshFetch$()
+      .subscribe(this.updateHandler.bind(this));
+
     this.subscriptions.push(
       Rx.merge(this.getOutput$(), this.getInput$()).subscribe(() => {
         this.handleChanges();
@@ -345,6 +352,7 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut
       this.handler.destroy();
       this.handler.getElement().remove();
     }
+    this.autoRefreshFetchSubscription.unsubscribe();
   }
 
   public reload = () => {
diff --git a/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx b/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx
index 03471174753fa..2f00467a85cda 100644
--- a/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx
+++ b/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx
@@ -38,6 +38,7 @@ import { VISUALIZE_EMBEDDABLE_TYPE } from './constants';
 
 import { getCapabilities, getHttp, getTypes, getUISettings } from '../np_ready/public/services';
 import { showNewVisModal } from '../np_ready/public/wizard';
+import { TimefilterContract } from '../../../../../plugins/data/public';
 
 interface VisualizationAttributes extends SavedObjectAttributes {
   visState: string;
@@ -51,7 +52,10 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory<
 > {
   public readonly type = VISUALIZE_EMBEDDABLE_TYPE;
 
-  constructor(private getSavedVisualizationsLoader: () => SavedVisualizations) {
+  constructor(
+    private timefilter: TimefilterContract,
+    private getSavedVisualizationsLoader: () => SavedVisualizations
+  ) {
     super({
       savedObjectMetaData: {
         name: i18n.translate('visualizations.savedObjectName', { defaultMessage: 'Visualization' }),
@@ -114,6 +118,7 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory<
       const indexPattern = await getIndexPattern(savedObject);
       const indexPatterns = indexPattern ? [indexPattern] : [];
       return new VisualizeEmbeddable(
+        this.timefilter,
         {
           savedVisualization: savedObject,
           indexPatterns,
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts
index a948757d7bd83..9fb87cadb2983 100644
--- a/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts
@@ -56,6 +56,7 @@ const createInstance = async () => {
   const plugin = new VisualizationsPlugin({} as PluginInitializerContext);
 
   const setup = plugin.setup(coreMock.createSetup(), {
+    data: dataPluginMock.createSetupContract(),
     expressions: expressionsPluginMock.createSetupContract(),
     embeddable: embeddablePluginMock.createStartContract(),
     usageCollection: usageCollectionPluginMock.createSetupContract(),
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts
index 36c04923e3fd0..20bed59faad88 100644
--- a/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts
@@ -36,7 +36,10 @@ import { ExpressionsSetup } from '../../../../../../plugins/expressions/public';
 import { IEmbeddableSetup } from '../../../../../../plugins/embeddable/public';
 import { visualization as visualizationFunction } from './expressions/visualization_function';
 import { visualization as visualizationRenderer } from './expressions/visualization_renderer';
-import { DataPublicPluginStart } from '../../../../../../plugins/data/public';
+import {
+  DataPublicPluginSetup,
+  DataPublicPluginStart,
+} from '../../../../../../plugins/data/public';
 import { UsageCollectionSetup } from '../../../../../../plugins/usage_collection/public';
 import {
   createSavedVisLoader,
@@ -65,6 +68,7 @@ export interface VisualizationsSetupDeps {
   expressions: ExpressionsSetup;
   embeddable: IEmbeddableSetup;
   usageCollection: UsageCollectionSetup;
+  data: DataPublicPluginSetup;
 }
 
 export interface VisualizationsStartDeps {
@@ -95,7 +99,7 @@ export class VisualizationsPlugin
 
   public setup(
     core: CoreSetup,
-    { expressions, embeddable, usageCollection }: VisualizationsSetupDeps
+    { expressions, embeddable, usageCollection, data }: VisualizationsSetupDeps
   ): VisualizationsSetup {
     setUISettings(core.uiSettings);
     setUsageCollector(usageCollection);
@@ -103,7 +107,10 @@ export class VisualizationsPlugin
     expressions.registerFunction(visualizationFunction);
     expressions.registerRenderer(visualizationRenderer);
 
-    const embeddableFactory = new VisualizeEmbeddableFactory(this.getSavedVisualizationsLoader);
+    const embeddableFactory = new VisualizeEmbeddableFactory(
+      data.query.timefilter.timefilter,
+      this.getSavedVisualizationsLoader
+    );
     embeddable.registerEmbeddableFactory(VISUALIZE_EMBEDDABLE_TYPE, embeddableFactory);
 
     return {
diff --git a/src/plugins/data/public/query/timefilter/timefilter_service.mock.ts b/src/plugins/data/public/query/timefilter/timefilter_service.mock.ts
index 2923cee60f898..80c13464ad98a 100644
--- a/src/plugins/data/public/query/timefilter/timefilter_service.mock.ts
+++ b/src/plugins/data/public/query/timefilter/timefilter_service.mock.ts
@@ -18,6 +18,7 @@
  */
 
 import { TimefilterService, TimeHistoryContract, TimefilterContract } from '.';
+import { Observable } from 'rxjs';
 
 export type TimefilterServiceClientContract = PublicMethodsOf<TimefilterService>;
 
@@ -28,7 +29,7 @@ const createSetupContractMock = () => {
     getEnabledUpdated$: jest.fn(),
     getTimeUpdate$: jest.fn(),
     getRefreshIntervalUpdate$: jest.fn(),
-    getAutoRefreshFetch$: jest.fn(),
+    getAutoRefreshFetch$: jest.fn(() => new Observable<unknown>()),
     getFetch$: jest.fn(),
     getTime: jest.fn(),
     setTime: jest.fn(),
diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx
index a07bd475cdfcb..55363ebe4d8f3 100644
--- a/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx
+++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx
@@ -4,10 +4,12 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+import { Subject } from 'rxjs';
 import { Embeddable } from './embeddable';
 import { ReactExpressionRendererProps } from 'src/plugins/expressions/public';
-import { Query, TimeRange, Filter } from 'src/plugins/data/public';
+import { Query, TimeRange, Filter, TimefilterContract } from 'src/plugins/data/public';
 import { Document } from '../../persistence';
+import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks';
 
 jest.mock('../../../../../../../src/plugins/inspector/public/', () => ({
   isAvailable: false,
@@ -44,6 +46,7 @@ describe('embeddable', () => {
 
   it('should render expression with expression renderer', () => {
     const embeddable = new Embeddable(
+      dataPluginMock.createSetupContract().query.timefilter.timefilter,
       expressionRenderer,
       {
         editUrl: '',
@@ -64,6 +67,7 @@ describe('embeddable', () => {
     const filters: Filter[] = [{ meta: { alias: 'test', negate: false, disabled: false } }];
 
     const embeddable = new Embeddable(
+      dataPluginMock.createSetupContract().query.timefilter.timefilter,
       expressionRenderer,
       {
         editUrl: '',
@@ -89,6 +93,7 @@ describe('embeddable', () => {
     const filters: Filter[] = [{ meta: { alias: 'test', negate: false, disabled: false } }];
 
     const embeddable = new Embeddable(
+      dataPluginMock.createSetupContract().query.timefilter.timefilter,
       expressionRenderer,
       {
         editUrl: '',
@@ -112,6 +117,7 @@ describe('embeddable', () => {
     const filters: Filter[] = [{ meta: { alias: 'test', negate: false, disabled: true } }];
 
     const embeddable = new Embeddable(
+      dataPluginMock.createSetupContract().query.timefilter.timefilter,
       expressionRenderer,
       {
         editUrl: '',
@@ -130,4 +136,31 @@ describe('embeddable', () => {
 
     expect(expressionRenderer).toHaveBeenCalledTimes(1);
   });
+
+  it('should re-render on auto refresh fetch observable', () => {
+    const timeRange: TimeRange = { from: 'now-15d', to: 'now' };
+    const query: Query = { language: 'kquery', query: '' };
+    const filters: Filter[] = [{ meta: { alias: 'test', negate: false, disabled: true } }];
+
+    const autoRefreshFetchSubject = new Subject();
+    const timefilter = ({
+      getAutoRefreshFetch$: () => autoRefreshFetchSubject.asObservable(),
+    } as unknown) as TimefilterContract;
+
+    const embeddable = new Embeddable(
+      timefilter,
+      expressionRenderer,
+      {
+        editUrl: '',
+        editable: true,
+        savedVis,
+      },
+      { id: '123', timeRange, query, filters }
+    );
+    embeddable.render(mountpoint);
+
+    autoRefreshFetchSubject.next();
+
+    expect(expressionRenderer).toHaveBeenCalledTimes(2);
+  });
 });
diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx
index a3a55f26ff7c2..252ba5c9bc0bc 100644
--- a/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx
+++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx
@@ -7,7 +7,13 @@
 import _ from 'lodash';
 import React from 'react';
 import { render, unmountComponentAtNode } from 'react-dom';
-import { Query, TimeRange, Filter, IIndexPattern } from 'src/plugins/data/public';
+import {
+  Query,
+  TimeRange,
+  Filter,
+  IIndexPattern,
+  TimefilterContract,
+} from 'src/plugins/data/public';
 import { Subscription } from 'rxjs';
 import { ReactExpressionRendererType } from '../../../../../../../src/plugins/expressions/public';
 import {
@@ -43,6 +49,7 @@ export class Embeddable extends AbstractEmbeddable<LensEmbeddableInput, LensEmbe
   private savedVis: Document;
   private domNode: HTMLElement | Element | undefined;
   private subscription: Subscription;
+  private autoRefreshFetchSubscription: Subscription;
 
   private currentContext: {
     timeRange?: TimeRange;
@@ -52,6 +59,7 @@ export class Embeddable extends AbstractEmbeddable<LensEmbeddableInput, LensEmbe
   } = {};
 
   constructor(
+    timefilter: TimefilterContract,
     expressionRenderer: ReactExpressionRendererType,
     { savedVis, editUrl, editable, indexPatterns }: LensEmbeddableConfiguration,
     initialInput: LensEmbeddableInput,
@@ -76,6 +84,10 @@ export class Embeddable extends AbstractEmbeddable<LensEmbeddableInput, LensEmbe
     this.savedVis = savedVis;
     this.subscription = this.getInput$().subscribe(input => this.onContainerStateChanged(input));
     this.onContainerStateChanged(initialInput);
+
+    this.autoRefreshFetchSubscription = timefilter
+      .getAutoRefreshFetch$()
+      .subscribe(this.reload.bind(this));
   }
 
   onContainerStateChanged(containerState: LensEmbeddableInput) {
@@ -125,6 +137,7 @@ export class Embeddable extends AbstractEmbeddable<LensEmbeddableInput, LensEmbe
     if (this.subscription) {
       this.subscription.unsubscribe();
     }
+    this.autoRefreshFetchSubscription.unsubscribe();
   }
 
   reload() {
diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts b/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts
index e8bb8914fa292..d30ad62b385c2 100644
--- a/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts
+++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts
@@ -11,7 +11,11 @@ import {
   SavedObjectsClientContract,
 } from 'kibana/public';
 import { i18n } from '@kbn/i18n';
-import { IndexPatternsContract, IndexPattern } from '../../../../../../../src/plugins/data/public';
+import {
+  IndexPatternsContract,
+  IndexPattern,
+  TimefilterContract,
+} from '../../../../../../../src/plugins/data/public';
 import { ReactExpressionRendererType } from '../../../../../../../src/plugins/expressions/public';
 import {
   EmbeddableFactory as AbstractEmbeddableFactory,
@@ -27,6 +31,7 @@ export class EmbeddableFactory extends AbstractEmbeddableFactory {
   type = DOC_TYPE;
 
   constructor(
+    private timefilter: TimefilterContract,
     private coreHttp: HttpSetup,
     private capabilities: RecursiveReadonly<Capabilities>,
     private savedObjectsClient: SavedObjectsClientContract,
@@ -85,6 +90,7 @@ export class EmbeddableFactory extends AbstractEmbeddableFactory {
     );
 
     return new Embeddable(
+      this.timefilter,
       this.expressionRenderer,
       {
         savedVis,
diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/mocks.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/mocks.tsx
index cd121a1f96a2b..e606c69c8c386 100644
--- a/x-pack/legacy/plugins/lens/public/editor_frame_service/mocks.tsx
+++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/mocks.tsx
@@ -14,6 +14,7 @@ import { embeddablePluginMock } from '../../../../../../src/plugins/embeddable/p
 import { expressionsPluginMock } from '../../../../../../src/plugins/expressions/public/mocks';
 import { DatasourcePublicAPI, FramePublicAPI, Datasource, Visualization } from '../types';
 import { EditorFrameSetupPlugins, EditorFrameStartPlugins } from './service';
+import { dataPluginMock } from '../../../../../../src/plugins/data/public/mocks';
 
 export function createMockVisualization(): jest.Mocked<Visualization> {
   return {
@@ -103,7 +104,7 @@ export function createExpressionRendererMock(): jest.Mock<
 
 export function createMockSetupDependencies() {
   return ({
-    data: {},
+    data: dataPluginMock.createSetupContract(),
     embeddable: embeddablePluginMock.createSetupContract(),
     expressions: expressionsPluginMock.createSetupContract(),
   } as unknown) as MockedSetupDependencies;
@@ -111,11 +112,7 @@ export function createMockSetupDependencies() {
 
 export function createMockStartDependencies() {
   return ({
-    data: {
-      indexPatterns: {
-        indexPatterns: {},
-      },
-    },
+    data: dataPluginMock.createSetupContract(),
     embeddable: embeddablePluginMock.createStartContract(),
     expressions: expressionsPluginMock.createStartContract(),
   } as unknown) as MockedStartDependencies;
diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/service.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/service.tsx
index 9a3d724705a1a..7a0bb3a2cc50f 100644
--- a/x-pack/legacy/plugins/lens/public/editor_frame_service/service.tsx
+++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/service.tsx
@@ -79,6 +79,7 @@ export class EditorFrameService {
     plugins.embeddable.registerEmbeddableFactory(
       'lens',
       new EmbeddableFactory(
+        plugins.data.query.timefilter.timefilter,
         core.http,
         core.application.capabilities,
         core.savedObjects.client,

From ca5e25c1395397a2599c1aec76f752d4fda8d4e8 Mon Sep 17 00:00:00 2001
From: Vadim Dalecky <streamich@users.noreply.github.com>
Date: Mon, 17 Feb 2020 14:59:47 +0100
Subject: [PATCH 006/174] Local actions (#57451)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat: 🎸 create UiActionsService

* feat: 🎸 add UiActionsServvice.fork() method

* feat: 🎸 instantiate UiActionsService in plugin

* feat: 🎸 add UiActionsService.registerTrigger(), remove old

* feat: 🎸 move attach/detachAction() methods to UiActionsService

* refactor: 💡 move remaining actions API to UiActionsService

* chore: 🤖 clean up /trigger folder

* test: 💍 move registry tests into UiActiosnService tests

* fix: 🐛 fix TypeScript typecheck errors

* test: 💍 add .fork() trigger tests

* feat: 🎸 remove actionIds from ui_actions Trigger interface

* fix: 🐛 remove usage of actionIds

* fix: 🐛 attach hello world action to trigger in plugin lifecycle

* feat: 🎸 fork also trigger to action attachments

* fix: 🐛 clear mapping registry in .clear(), improve type
---
 .../public/hello_world_trigger.ts             |   2 -
 examples/ui_action_examples/public/plugin.ts  |   7 +-
 .../ui_actions_explorer/public/plugin.tsx     |   3 -
 src/plugins/embeddable/public/bootstrap.ts    |  17 +-
 .../lib/panel/embeddable_panel.test.tsx       |   5 +-
 src/plugins/embeddable/public/lib/types.ts    |   1 -
 .../embeddable/public/tests/test_plugin.ts    |   8 +-
 src/plugins/ui_actions/README.md              |  14 +-
 .../incompatible_action_error.ts              |   0
 .../ui_actions/public/actions/index.ts        |   5 +-
 .../public/actions/register_action.ts         |  28 --
 src/plugins/ui_actions/public/api.ts          |  55 ---
 src/plugins/ui_actions/public/index.ts        |  29 +-
 src/plugins/ui_actions/public/mocks.ts        |  15 +-
 src/plugins/ui_actions/public/plugin.ts       |  39 +-
 .../{tests/helpers.ts => service/index.ts}    |  10 +-
 .../public/service/ui_actions_service.test.ts | 465 ++++++++++++++++++
 .../public/service/ui_actions_service.ts      | 194 ++++++++
 src/plugins/ui_actions/public/tests/README.md |   2 +
 .../execute_trigger_actions.test.ts           |  33 +-
 .../get_trigger_actions.test.ts               |   5 +-
 .../get_trigger_compatible_actions.test.ts    |  27 +-
 src/plugins/ui_actions/public/tests/index.ts  |   2 +-
 .../ui_actions/public/tests/test_plugin.ts    |  49 --
 .../public/triggers/attach_action.ts          |  37 --
 .../public/triggers/detach_action.ts          |  35 --
 .../triggers/execute_trigger_actions.ts       |  59 ---
 .../public/triggers/get_trigger.test.ts       |  48 --
 .../ui_actions/public/triggers/get_trigger.ts |  30 --
 .../public/triggers/get_trigger_actions.ts    |  29 --
 .../get_trigger_compatible_actions.ts         |  32 --
 .../ui_actions/public/triggers/index.ts       |   2 +-
 .../public/triggers/register_trigger.ts       |  28 --
 .../public/triggers/registry.test.ts          | 149 ------
 .../ui_actions/public/triggers/trigger.ts     |   1 -
 src/plugins/ui_actions/public/types.ts        |  35 +-
 36 files changed, 765 insertions(+), 735 deletions(-)
 rename src/plugins/ui_actions/public/{triggers => actions}/incompatible_action_error.ts (100%)
 delete mode 100644 src/plugins/ui_actions/public/actions/register_action.ts
 delete mode 100644 src/plugins/ui_actions/public/api.ts
 rename src/plugins/ui_actions/public/{tests/helpers.ts => service/index.ts} (76%)
 create mode 100644 src/plugins/ui_actions/public/service/ui_actions_service.test.ts
 create mode 100644 src/plugins/ui_actions/public/service/ui_actions_service.ts
 create mode 100644 src/plugins/ui_actions/public/tests/README.md
 rename src/plugins/ui_actions/public/{triggers => tests}/execute_trigger_actions.test.ts (86%)
 rename src/plugins/ui_actions/public/{triggers => tests}/get_trigger_actions.test.ts (93%)
 rename src/plugins/ui_actions/public/{triggers => tests}/get_trigger_compatible_actions.test.ts (82%)
 delete mode 100644 src/plugins/ui_actions/public/tests/test_plugin.ts
 delete mode 100644 src/plugins/ui_actions/public/triggers/attach_action.ts
 delete mode 100644 src/plugins/ui_actions/public/triggers/detach_action.ts
 delete mode 100644 src/plugins/ui_actions/public/triggers/execute_trigger_actions.ts
 delete mode 100644 src/plugins/ui_actions/public/triggers/get_trigger.test.ts
 delete mode 100644 src/plugins/ui_actions/public/triggers/get_trigger.ts
 delete mode 100644 src/plugins/ui_actions/public/triggers/get_trigger_actions.ts
 delete mode 100644 src/plugins/ui_actions/public/triggers/get_trigger_compatible_actions.ts
 delete mode 100644 src/plugins/ui_actions/public/triggers/register_trigger.ts
 delete mode 100644 src/plugins/ui_actions/public/triggers/registry.test.ts

diff --git a/examples/ui_action_examples/public/hello_world_trigger.ts b/examples/ui_action_examples/public/hello_world_trigger.ts
index 999a7d9864707..929c9aecab17b 100644
--- a/examples/ui_action_examples/public/hello_world_trigger.ts
+++ b/examples/ui_action_examples/public/hello_world_trigger.ts
@@ -18,11 +18,9 @@
  */
 
 import { Trigger } from '../../../src/plugins/ui_actions/public';
-import { HELLO_WORLD_ACTION_TYPE } from './hello_world_action';
 
 export const HELLO_WORLD_TRIGGER_ID = 'HELLO_WORLD_TRIGGER_ID';
 
 export const helloWorldTrigger: Trigger = {
   id: HELLO_WORLD_TRIGGER_ID,
-  actionIds: [HELLO_WORLD_ACTION_TYPE],
 };
diff --git a/examples/ui_action_examples/public/plugin.ts b/examples/ui_action_examples/public/plugin.ts
index ef0689227d6bd..bf62b4d973d4d 100644
--- a/examples/ui_action_examples/public/plugin.ts
+++ b/examples/ui_action_examples/public/plugin.ts
@@ -19,7 +19,7 @@
 
 import { Plugin, CoreSetup, CoreStart } from '../../../src/core/public';
 import { UiActionsSetup, UiActionsStart } from '../../../src/plugins/ui_actions/public';
-import { createHelloWorldAction } from './hello_world_action';
+import { createHelloWorldAction, HELLO_WORLD_ACTION_TYPE } from './hello_world_action';
 import { helloWorldTrigger } from './hello_world_trigger';
 
 interface UiActionExamplesSetupDependencies {
@@ -33,8 +33,9 @@ interface UiActionExamplesStartDependencies {
 export class UiActionExamplesPlugin
   implements
     Plugin<void, void, UiActionExamplesSetupDependencies, UiActionExamplesStartDependencies> {
-  public setup(core: CoreSetup, deps: UiActionExamplesSetupDependencies) {
-    deps.uiActions.registerTrigger(helloWorldTrigger);
+  public setup(core: CoreSetup, { uiActions }: UiActionExamplesSetupDependencies) {
+    uiActions.registerTrigger(helloWorldTrigger);
+    uiActions.attachAction(helloWorldTrigger.id, HELLO_WORLD_ACTION_TYPE);
   }
 
   public start(coreStart: CoreStart, deps: UiActionExamplesStartDependencies) {
diff --git a/examples/ui_actions_explorer/public/plugin.tsx b/examples/ui_actions_explorer/public/plugin.tsx
index 9c5f967a466bf..981ad97a31b46 100644
--- a/examples/ui_actions_explorer/public/plugin.tsx
+++ b/examples/ui_actions_explorer/public/plugin.tsx
@@ -56,15 +56,12 @@ export class UiActionsExplorerPlugin implements Plugin<void, void, {}, StartDeps
   public setup(core: CoreSetup<{ uiActions: UiActionsStart }>, deps: SetupDeps) {
     deps.uiActions.registerTrigger({
       id: COUNTRY_TRIGGER,
-      actionIds: [],
     });
     deps.uiActions.registerTrigger({
       id: PHONE_TRIGGER,
-      actionIds: [],
     });
     deps.uiActions.registerTrigger({
       id: USER_TRIGGER,
-      actionIds: [],
     });
     deps.uiActions.registerAction(lookUpWeatherAction);
     deps.uiActions.registerAction(viewInMapsAction);
diff --git a/src/plugins/embeddable/public/bootstrap.ts b/src/plugins/embeddable/public/bootstrap.ts
index 3ca84549c559d..9a364e84092ca 100644
--- a/src/plugins/embeddable/public/bootstrap.ts
+++ b/src/plugins/embeddable/public/bootstrap.ts
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-import { UiActionsSetup } from 'src/plugins/ui_actions/public';
+import { UiActionsSetup, Trigger } from 'src/plugins/ui_actions/public';
 import {
   CONTEXT_MENU_TRIGGER,
   APPLY_FILTER_TRIGGER,
@@ -34,35 +34,30 @@ import {
  * @param api
  */
 export const bootstrap = (uiActions: UiActionsSetup) => {
-  const triggerContext = {
+  const triggerContext: Trigger = {
     id: CONTEXT_MENU_TRIGGER,
     title: 'Context menu',
     description: 'Triggered on top-right corner context-menu select.',
-    actionIds: [],
   };
-  const triggerFilter = {
+  const triggerFilter: Trigger = {
     id: APPLY_FILTER_TRIGGER,
     title: 'Filter click',
     description: 'Triggered when user applies filter to an embeddable.',
-    actionIds: [],
   };
-  const triggerBadge = {
+  const triggerBadge: Trigger = {
     id: PANEL_BADGE_TRIGGER,
     title: 'Panel badges',
     description: 'Actions appear in title bar when an embeddable loads in a panel',
-    actionIds: [],
   };
-  const selectRangeTrigger = {
+  const selectRangeTrigger: Trigger = {
     id: SELECT_RANGE_TRIGGER,
     title: 'Select range',
     description: 'Applies a range filter',
-    actionIds: [],
   };
-  const valueClickTrigger = {
+  const valueClickTrigger: Trigger = {
     id: VALUE_CLICK_TRIGGER,
     title: 'Value clicked',
     description: 'Value was clicked',
-    actionIds: [],
   };
   const actionApplyFilter = createFilterAction();
 
diff --git a/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx b/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx
index 9982c632f36fb..79d59317767d9 100644
--- a/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx
+++ b/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx
@@ -25,7 +25,7 @@ import { nextTick } from 'test_utils/enzyme_helpers';
 import { findTestSubject } from '@elastic/eui/lib/test';
 import { I18nProvider } from '@kbn/i18n/react';
 import { CONTEXT_MENU_TRIGGER } from '../triggers';
-import { Action, UiActionsApi } from 'src/plugins/ui_actions/public';
+import { Action, UiActionsStart } from 'src/plugins/ui_actions/public';
 import { Trigger, GetEmbeddableFactory, ViewMode } from '../types';
 import { EmbeddableFactory, isErrorEmbeddable } from '../embeddables';
 import { EmbeddablePanel } from './embeddable_panel';
@@ -52,7 +52,6 @@ const getEmbeddableFactory: GetEmbeddableFactory = (id: string) => embeddableFac
 const editModeAction = createEditModeAction();
 const trigger: Trigger = {
   id: CONTEXT_MENU_TRIGGER,
-  actionIds: [editModeAction.id],
 };
 const embeddableFactory = new ContactCardEmbeddableFactory(
   {} as any,
@@ -177,7 +176,7 @@ test('HelloWorldContainer in view mode hides edit mode actions', async () => {
 
 const renderInEditModeAndOpenContextMenu = async (
   embeddableInputs: any,
-  getActions: UiActionsApi['getTriggerCompatibleActions'] = () => Promise.resolve([])
+  getActions: UiActionsStart['getTriggerCompatibleActions'] = () => Promise.resolve([])
 ) => {
   const inspector = inspectorPluginMock.createStartContract();
 
diff --git a/src/plugins/embeddable/public/lib/types.ts b/src/plugins/embeddable/public/lib/types.ts
index 1bd71163db44c..68ea5bc17f7c9 100644
--- a/src/plugins/embeddable/public/lib/types.ts
+++ b/src/plugins/embeddable/public/lib/types.ts
@@ -24,7 +24,6 @@ export interface Trigger {
   id: string;
   title?: string;
   description?: string;
-  actionIds: string[];
 }
 
 export interface PropertySpec {
diff --git a/src/plugins/embeddable/public/tests/test_plugin.ts b/src/plugins/embeddable/public/tests/test_plugin.ts
index d9e1a75d92bf3..1edc332780336 100644
--- a/src/plugins/embeddable/public/tests/test_plugin.ts
+++ b/src/plugins/embeddable/public/tests/test_plugin.ts
@@ -19,8 +19,8 @@
 
 import { CoreSetup, CoreStart } from 'src/core/public';
 // eslint-disable-next-line
-import { uiActionsTestPlugin } from 'src/plugins/ui_actions/public/tests';
-import { UiActionsApi } from 'src/plugins/ui_actions/public';
+import { uiActionsPluginMock } from 'src/plugins/ui_actions/public/mocks';
+import { UiActionsStart } from 'src/plugins/ui_actions/public';
 import { coreMock } from '../../../../core/public/mocks';
 import { EmbeddablePublicPlugin, IEmbeddableSetup, IEmbeddableStart } from '../plugin';
 
@@ -30,14 +30,14 @@ export interface TestPluginReturn {
   coreStart: CoreStart;
   setup: IEmbeddableSetup;
   doStart: (anotherCoreStart?: CoreStart) => IEmbeddableStart;
-  uiActions: UiActionsApi;
+  uiActions: UiActionsStart;
 }
 
 export const testPlugin = (
   coreSetup: CoreSetup = coreMock.createSetup(),
   coreStart: CoreStart = coreMock.createStart()
 ): TestPluginReturn => {
-  const uiActions = uiActionsTestPlugin(coreSetup, coreStart);
+  const uiActions = uiActionsPluginMock.createPlugin(coreSetup, coreStart);
   const initializerContext = {} as any;
   const plugin = new EmbeddablePublicPlugin(initializerContext);
   const setup = plugin.setup(coreSetup, { uiActions: uiActions.setup });
diff --git a/src/plugins/ui_actions/README.md b/src/plugins/ui_actions/README.md
index 02942b7d5b406..c4e02b551c884 100644
--- a/src/plugins/ui_actions/README.md
+++ b/src/plugins/ui_actions/README.md
@@ -1,10 +1,10 @@
 # UI Actions
 
-An API for: 
- - creating custom functionality (`actions`)
- - creating custom user interaction events (`triggers`)
- - attaching and detaching `actions` to `triggers`.
- - emitting `trigger` events
- - executing `actions` attached to a given `trigger`.
- - exposing a context menu for the user to choose the appropriate action when there are multiple actions attached to a single trigger.
+An API for:
 
+- creating custom functionality (`actions`)
+- creating custom user interaction events (`triggers`)
+- attaching and detaching `actions` to `triggers`.
+- emitting `trigger` events
+- executing `actions` attached to a given `trigger`.
+- exposing a context menu for the user to choose the appropriate action when there are multiple actions attached to a single trigger.
diff --git a/src/plugins/ui_actions/public/triggers/incompatible_action_error.ts b/src/plugins/ui_actions/public/actions/incompatible_action_error.ts
similarity index 100%
rename from src/plugins/ui_actions/public/triggers/incompatible_action_error.ts
rename to src/plugins/ui_actions/public/actions/incompatible_action_error.ts
diff --git a/src/plugins/ui_actions/public/actions/index.ts b/src/plugins/ui_actions/public/actions/index.ts
index feb9a8de62eb3..64bfd368e3dfa 100644
--- a/src/plugins/ui_actions/public/actions/index.ts
+++ b/src/plugins/ui_actions/public/actions/index.ts
@@ -17,5 +17,6 @@
  * under the License.
  */
 
-export { Action } from './action';
-export { createAction } from './create_action';
+export * from './action';
+export * from './create_action';
+export * from './incompatible_action_error';
diff --git a/src/plugins/ui_actions/public/actions/register_action.ts b/src/plugins/ui_actions/public/actions/register_action.ts
deleted file mode 100644
index 5738be63c9592..0000000000000
--- a/src/plugins/ui_actions/public/actions/register_action.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { UiActionsApiPure } from '../types';
-
-export const registerAction: UiActionsApiPure['registerAction'] = ({ actions }) => action => {
-  if (actions.has(action.id)) {
-    throw new Error(`Action [action.id = ${action.id}] already registered.`);
-  }
-
-  actions.set(action.id, action);
-};
diff --git a/src/plugins/ui_actions/public/api.ts b/src/plugins/ui_actions/public/api.ts
deleted file mode 100644
index 9a6fd04b14e10..0000000000000
--- a/src/plugins/ui_actions/public/api.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import {
-  UiActionsApi,
-  UiActionsDependenciesInternal,
-  UiActionsDependencies,
-  UiActionsApiPure,
-} from './types';
-import { attachAction } from './triggers/attach_action';
-import { detachAction } from './triggers/detach_action';
-import { executeTriggerActions } from './triggers/execute_trigger_actions';
-import { getTrigger } from './triggers/get_trigger';
-import { getTriggerActions } from './triggers/get_trigger_actions';
-import { getTriggerCompatibleActions } from './triggers/get_trigger_compatible_actions';
-import { registerAction } from './actions/register_action';
-import { registerTrigger } from './triggers/register_trigger';
-
-export const pureApi: UiActionsApiPure = {
-  attachAction,
-  detachAction,
-  executeTriggerActions,
-  getTrigger,
-  getTriggerActions,
-  getTriggerCompatibleActions,
-  registerAction,
-  registerTrigger,
-};
-
-export const createApi = (deps: UiActionsDependencies) => {
-  const partialApi: Partial<UiActionsApi> = {};
-  const depsInternal: UiActionsDependenciesInternal = { ...deps, api: partialApi };
-  for (const [key, fn] of Object.entries(pureApi)) {
-    (partialApi as any)[key] = fn(depsInternal);
-  }
-  Object.freeze(partialApi);
-  const api = partialApi as UiActionsApi;
-  return { api, depsInternal };
-};
diff --git a/src/plugins/ui_actions/public/index.ts b/src/plugins/ui_actions/public/index.ts
index 427dbecb7aee4..83a08b11fa4c2 100644
--- a/src/plugins/ui_actions/public/index.ts
+++ b/src/plugins/ui_actions/public/index.ts
@@ -19,19 +19,30 @@
 
 import { PluginInitializerContext } from '../../../core/public';
 import { UiActionsPlugin } from './plugin';
+import { UiActionsService } from './service';
 
 export function plugin(initializerContext: PluginInitializerContext) {
   return new UiActionsPlugin(initializerContext);
 }
 
 export { UiActionsSetup, UiActionsStart } from './plugin';
-export {
-  Action,
-  Trigger,
-  UiActionsApi,
-  GetActionsCompatibleWithTrigger,
-  ExecuteTriggerActions,
-} from './types';
-export { createAction } from './actions';
+export { UiActionsServiceParams, UiActionsService } from './service';
+export { Action, createAction, IncompatibleActionError } from './actions';
 export { buildContextMenuForActions } from './context_menu';
-export { IncompatibleActionError } from './triggers';
+export { Trigger } from './triggers';
+
+/**
+ * @deprecated
+ *
+ * Use `UiActionsStart['getTriggerCompatibleActions']` or
+ * `UiActionsService['getTriggerCompatibleActions']` instead.
+ */
+export type GetActionsCompatibleWithTrigger = UiActionsService['getTriggerCompatibleActions'];
+
+/**
+ * @deprecated
+ *
+ * Use `UiActionsStart['executeTriggerActions']` or
+ * `UiActionsService['executeTriggerActions']` instead.
+ */
+export type ExecuteTriggerActions = UiActionsService['executeTriggerActions'];
diff --git a/src/plugins/ui_actions/public/mocks.ts b/src/plugins/ui_actions/public/mocks.ts
index 273c5dcf83e81..d2ba901f1040d 100644
--- a/src/plugins/ui_actions/public/mocks.ts
+++ b/src/plugins/ui_actions/public/mocks.ts
@@ -17,9 +17,9 @@
  * under the License.
  */
 
+import { CoreSetup, CoreStart } from 'src/core/public';
 import { UiActionsSetup, UiActionsStart } from '.';
 import { plugin as pluginInitializer } from '.';
-// eslint-disable-next-line
 import { coreMock } from '../../../core/public/mocks';
 
 export type Setup = jest.Mocked<UiActionsSetup>;
@@ -45,17 +45,20 @@ const createStartContract = (): Start => {
     getTrigger: jest.fn(),
     getTriggerActions: jest.fn((id: string) => []),
     getTriggerCompatibleActions: jest.fn(),
+    clear: jest.fn(),
+    fork: jest.fn(),
   };
 
   return startContract;
 };
 
-const createPlugin = async () => {
+const createPlugin = (
+  coreSetup: CoreSetup = coreMock.createSetup(),
+  coreStart: CoreStart = coreMock.createStart()
+) => {
   const pluginInitializerContext = coreMock.createPluginInitializerContext();
-  const coreSetup = coreMock.createSetup();
-  const coreStart = coreMock.createStart();
   const plugin = pluginInitializer(pluginInitializerContext);
-  const setup = await plugin.setup(coreSetup);
+  const setup = plugin.setup(coreSetup);
 
   return {
     pluginInitializerContext,
@@ -63,7 +66,7 @@ const createPlugin = async () => {
     coreStart,
     plugin,
     setup,
-    doStart: async () => await plugin.start(coreStart),
+    doStart: (anotherCoreStart: CoreStart = coreStart) => plugin.start(anotherCoreStart),
   };
 };
 
diff --git a/src/plugins/ui_actions/public/plugin.ts b/src/plugins/ui_actions/public/plugin.ts
index 12a9b7cbc6526..0874803db7d37 100644
--- a/src/plugins/ui_actions/public/plugin.ts
+++ b/src/plugins/ui_actions/public/plugin.ts
@@ -17,43 +17,30 @@
  * under the License.
  */
 
-import { CoreStart, PluginInitializerContext, CoreSetup, Plugin } from 'src/core/public';
-import { UiActionsApi, ActionRegistry, TriggerRegistry } from './types';
-import { createApi } from './api';
-
-export interface UiActionsSetup {
-  attachAction: UiActionsApi['attachAction'];
-  detachAction: UiActionsApi['detachAction'];
-  registerAction: UiActionsApi['registerAction'];
-  registerTrigger: UiActionsApi['registerTrigger'];
-}
+import { CoreStart, CoreSetup, Plugin, PluginInitializerContext } from 'src/core/public';
+import { UiActionsService } from './service';
+
+export type UiActionsSetup = Pick<
+  UiActionsService,
+  'attachAction' | 'detachAction' | 'registerAction' | 'registerTrigger'
+>;
 
-export type UiActionsStart = UiActionsApi;
+export type UiActionsStart = PublicMethodsOf<UiActionsService>;
 
 export class UiActionsPlugin implements Plugin<UiActionsSetup, UiActionsStart> {
-  private readonly triggers: TriggerRegistry = new Map();
-  private readonly actions: ActionRegistry = new Map();
-  private api!: UiActionsApi;
+  private readonly service = new UiActionsService();
 
-  constructor(initializerContext: PluginInitializerContext) {
-    this.api = createApi({ triggers: this.triggers, actions: this.actions }).api;
-  }
+  constructor(initializerContext: PluginInitializerContext) {}
 
   public setup(core: CoreSetup): UiActionsSetup {
-    return {
-      registerTrigger: this.api.registerTrigger,
-      registerAction: this.api.registerAction,
-      attachAction: this.api.attachAction,
-      detachAction: this.api.detachAction,
-    };
+    return this.service;
   }
 
   public start(core: CoreStart): UiActionsStart {
-    return this.api;
+    return this.service;
   }
 
   public stop() {
-    this.actions.clear();
-    this.triggers.clear();
+    this.service.clear();
   }
 }
diff --git a/src/plugins/ui_actions/public/tests/helpers.ts b/src/plugins/ui_actions/public/service/index.ts
similarity index 76%
rename from src/plugins/ui_actions/public/tests/helpers.ts
rename to src/plugins/ui_actions/public/service/index.ts
index d1a4a71705a81..3998a2ea255cb 100644
--- a/src/plugins/ui_actions/public/tests/helpers.ts
+++ b/src/plugins/ui_actions/public/service/index.ts
@@ -17,12 +17,4 @@
  * under the License.
  */
 
-import { UiActionsDependencies } from '../types';
-
-export const createDeps = (): UiActionsDependencies => {
-  const deps: UiActionsDependencies = {
-    actions: new Map<any, any>(),
-    triggers: new Map<any, any>(),
-  };
-  return deps;
-};
+export * from './ui_actions_service';
diff --git a/src/plugins/ui_actions/public/service/ui_actions_service.test.ts b/src/plugins/ui_actions/public/service/ui_actions_service.test.ts
new file mode 100644
index 0000000000000..2bbe106c49a25
--- /dev/null
+++ b/src/plugins/ui_actions/public/service/ui_actions_service.test.ts
@@ -0,0 +1,465 @@
+/*
+ * 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 { UiActionsService } from './ui_actions_service';
+import { Action } from '../actions';
+import { createRestrictedAction, createHelloWorldAction } from '../tests/test_samples';
+import { ActionRegistry, TriggerRegistry } from '../types';
+import { Trigger } from '../triggers';
+
+const testAction1: Action = {
+  id: 'action1',
+  order: 1,
+  type: 'type1',
+  execute: async () => {},
+  getDisplayName: () => 'test1',
+  getIconType: () => '',
+  isCompatible: async () => true,
+};
+
+const testAction2: Action = {
+  id: 'action2',
+  order: 2,
+  type: 'type2',
+  execute: async () => {},
+  getDisplayName: () => 'test2',
+  getIconType: () => '',
+  isCompatible: async () => true,
+};
+
+describe('UiActionsService', () => {
+  test('can instantiate', () => {
+    new UiActionsService();
+  });
+
+  describe('.registerTrigger()', () => {
+    test('can register a trigger', () => {
+      const service = new UiActionsService();
+      service.registerTrigger({
+        id: 'test',
+      });
+    });
+  });
+
+  describe('.getTrigger()', () => {
+    test('can get Trigger from registry', () => {
+      const service = new UiActionsService();
+      service.registerTrigger({
+        description: 'foo',
+        id: 'bar',
+        title: 'baz',
+      });
+
+      const trigger = service.getTrigger('bar');
+
+      expect(trigger).toEqual({
+        description: 'foo',
+        id: 'bar',
+        title: 'baz',
+      });
+    });
+
+    test('throws if trigger does not exist', () => {
+      const service = new UiActionsService();
+
+      expect(() => service.getTrigger('foo')).toThrowError(
+        'Trigger [triggerId = foo] does not exist.'
+      );
+    });
+  });
+
+  describe('.registerAction()', () => {
+    test('can register an action', () => {
+      const service = new UiActionsService();
+      service.registerAction({
+        id: 'test',
+        execute: async () => {},
+        getDisplayName: () => 'test',
+        getIconType: () => '',
+        isCompatible: async () => true,
+        type: 'test',
+      });
+    });
+  });
+
+  describe('.getTriggerActions()', () => {
+    const action1: Action = {
+      id: 'action1',
+      order: 1,
+      type: 'type1',
+      execute: async () => {},
+      getDisplayName: () => 'test',
+      getIconType: () => '',
+      isCompatible: async () => true,
+    };
+    const action2: Action = {
+      id: 'action2',
+      order: 2,
+      type: 'type2',
+      execute: async () => {},
+      getDisplayName: () => 'test',
+      getIconType: () => '',
+      isCompatible: async () => true,
+    };
+
+    test('returns actions set on trigger', () => {
+      const service = new UiActionsService();
+
+      service.registerAction(action1);
+      service.registerAction(action2);
+      service.registerTrigger({
+        description: 'foo',
+        id: 'trigger',
+        title: 'baz',
+      });
+
+      const list0 = service.getTriggerActions('trigger');
+
+      expect(list0).toHaveLength(0);
+
+      service.attachAction('trigger', 'action1');
+      const list1 = service.getTriggerActions('trigger');
+
+      expect(list1).toHaveLength(1);
+      expect(list1).toEqual([action1]);
+
+      service.attachAction('trigger', 'action2');
+      const list2 = service.getTriggerActions('trigger');
+
+      expect(list2).toHaveLength(2);
+      expect(!!list2.find(({ id }: any) => id === 'action1')).toBe(true);
+      expect(!!list2.find(({ id }: any) => id === 'action2')).toBe(true);
+    });
+  });
+
+  describe('.getTriggerCompatibleActions()', () => {
+    test('can register and get actions', async () => {
+      const actions: ActionRegistry = new Map();
+      const service = new UiActionsService({ actions });
+      const helloWorldAction = createHelloWorldAction({} as any);
+      const length = actions.size;
+
+      service.registerAction(helloWorldAction);
+
+      expect(actions.size - length).toBe(1);
+      expect(actions.get(helloWorldAction.id)).toBe(helloWorldAction);
+    });
+
+    test('getTriggerCompatibleActions returns attached actions', async () => {
+      const service = new UiActionsService();
+      const helloWorldAction = createHelloWorldAction({} as any);
+
+      service.registerAction(helloWorldAction);
+
+      const testTrigger: Trigger = {
+        id: 'MY-TRIGGER',
+        title: 'My trigger',
+      };
+      service.registerTrigger(testTrigger);
+      service.attachAction('MY-TRIGGER', helloWorldAction.id);
+
+      const compatibleActions = await service.getTriggerCompatibleActions('MY-TRIGGER', {});
+
+      expect(compatibleActions.length).toBe(1);
+      expect(compatibleActions[0].id).toBe(helloWorldAction.id);
+    });
+
+    test('filters out actions not applicable based on the context', async () => {
+      const service = new UiActionsService();
+      const restrictedAction = createRestrictedAction<{ accept: boolean }>(context => {
+        return context.accept;
+      });
+
+      service.registerAction(restrictedAction);
+
+      const testTrigger: Trigger = {
+        id: 'MY-TRIGGER',
+        title: 'My trigger',
+      };
+
+      service.registerTrigger(testTrigger);
+      service.attachAction(testTrigger.id, restrictedAction.id);
+
+      const compatibleActions1 = await service.getTriggerCompatibleActions(testTrigger.id, {
+        accept: true,
+      });
+
+      expect(compatibleActions1.length).toBe(1);
+
+      const compatibleActions2 = await service.getTriggerCompatibleActions(testTrigger.id, {
+        accept: false,
+      });
+
+      expect(compatibleActions2.length).toBe(0);
+    });
+
+    test(`throws an error with an invalid trigger ID`, async () => {
+      const service = new UiActionsService();
+
+      await expect(service.getTriggerCompatibleActions('I do not exist', {})).rejects.toMatchObject(
+        new Error('Trigger [triggerId = I do not exist] does not exist.')
+      );
+    });
+
+    test('returns empty list if trigger not attached to any action', async () => {
+      const service = new UiActionsService();
+      const testTrigger: Trigger = {
+        id: '123',
+        title: '123',
+      };
+      service.registerTrigger(testTrigger);
+
+      const actions = await service.getTriggerCompatibleActions(testTrigger.id, {});
+
+      expect(actions).toEqual([]);
+    });
+  });
+
+  describe('.fork()', () => {
+    test('returns a new instance of the service', () => {
+      const service1 = new UiActionsService();
+      const service2 = service1.fork();
+
+      expect(service1).not.toBe(service2);
+      expect(service2).toBeInstanceOf(UiActionsService);
+    });
+
+    test('triggers registered in original service are available in original an forked services', () => {
+      const service1 = new UiActionsService();
+      service1.registerTrigger({
+        id: 'foo',
+      });
+      const service2 = service1.fork();
+
+      const trigger1 = service1.getTrigger('foo');
+      const trigger2 = service2.getTrigger('foo');
+
+      expect(trigger1.id).toBe('foo');
+      expect(trigger2.id).toBe('foo');
+    });
+
+    test('triggers registered in forked service are not available in original service', () => {
+      const service1 = new UiActionsService();
+      const service2 = service1.fork();
+
+      service2.registerTrigger({
+        id: 'foo',
+      });
+
+      expect(() => service1.getTrigger('foo')).toThrowErrorMatchingInlineSnapshot(
+        `"Trigger [triggerId = foo] does not exist."`
+      );
+
+      const trigger2 = service2.getTrigger('foo');
+      expect(trigger2.id).toBe('foo');
+    });
+
+    test('forked service preserves trigger-to-actions mapping', () => {
+      const service1 = new UiActionsService();
+
+      service1.registerTrigger({
+        id: 'foo',
+      });
+      service1.registerAction(testAction1);
+      service1.attachAction('foo', testAction1.id);
+
+      const service2 = service1.fork();
+
+      const actions1 = service1.getTriggerActions('foo');
+      const actions2 = service2.getTriggerActions('foo');
+
+      expect(actions1).toHaveLength(1);
+      expect(actions2).toHaveLength(1);
+      expect(actions1[0].id).toBe(testAction1.id);
+      expect(actions2[0].id).toBe(testAction1.id);
+    });
+
+    test('new attachments in fork do not appear in original service', () => {
+      const service1 = new UiActionsService();
+
+      service1.registerTrigger({
+        id: 'foo',
+      });
+      service1.registerAction(testAction1);
+      service1.registerAction(testAction2);
+      service1.attachAction('foo', testAction1.id);
+
+      const service2 = service1.fork();
+
+      expect(service1.getTriggerActions('foo')).toHaveLength(1);
+      expect(service2.getTriggerActions('foo')).toHaveLength(1);
+
+      service2.attachAction('foo', testAction2.id);
+
+      expect(service1.getTriggerActions('foo')).toHaveLength(1);
+      expect(service2.getTriggerActions('foo')).toHaveLength(2);
+    });
+
+    test('new attachments in original service do not appear in fork', () => {
+      const service1 = new UiActionsService();
+
+      service1.registerTrigger({
+        id: 'foo',
+      });
+      service1.registerAction(testAction1);
+      service1.registerAction(testAction2);
+      service1.attachAction('foo', testAction1.id);
+
+      const service2 = service1.fork();
+
+      expect(service1.getTriggerActions('foo')).toHaveLength(1);
+      expect(service2.getTriggerActions('foo')).toHaveLength(1);
+
+      service1.attachAction('foo', testAction2.id);
+
+      expect(service1.getTriggerActions('foo')).toHaveLength(2);
+      expect(service2.getTriggerActions('foo')).toHaveLength(1);
+    });
+  });
+
+  describe('registries', () => {
+    const HELLO_WORLD_ACTION_ID = 'HELLO_WORLD_ACTION_ID';
+
+    test('can register trigger', () => {
+      const triggers: TriggerRegistry = new Map();
+      const service = new UiActionsService({ triggers });
+
+      service.registerTrigger({
+        description: 'foo',
+        id: 'bar',
+        title: 'baz',
+      });
+
+      expect(triggers.get('bar')).toEqual({
+        description: 'foo',
+        id: 'bar',
+        title: 'baz',
+      });
+    });
+
+    test('can register action', () => {
+      const actions: ActionRegistry = new Map();
+      const service = new UiActionsService({ actions });
+
+      service.registerAction({
+        id: HELLO_WORLD_ACTION_ID,
+        order: 13,
+      } as any);
+
+      expect(actions.get(HELLO_WORLD_ACTION_ID)).toMatchObject({
+        id: HELLO_WORLD_ACTION_ID,
+        order: 13,
+      });
+    });
+
+    test('can attach an action to a trigger', () => {
+      const service = new UiActionsService();
+
+      const trigger: Trigger = {
+        id: 'MY-TRIGGER',
+      };
+      const action = {
+        id: HELLO_WORLD_ACTION_ID,
+        order: 25,
+      } as any;
+
+      service.registerTrigger(trigger);
+      service.registerAction(action);
+      service.attachAction('MY-TRIGGER', HELLO_WORLD_ACTION_ID);
+
+      const actions = service.getTriggerActions(trigger.id);
+
+      expect(actions.length).toBe(1);
+      expect(actions[0].id).toBe(HELLO_WORLD_ACTION_ID);
+    });
+
+    test('can detach an action to a trigger', () => {
+      const service = new UiActionsService();
+
+      const trigger: Trigger = {
+        id: 'MY-TRIGGER',
+      };
+      const action = {
+        id: HELLO_WORLD_ACTION_ID,
+        order: 25,
+      } as any;
+
+      service.registerTrigger(trigger);
+      service.registerAction(action);
+      service.attachAction(trigger.id, HELLO_WORLD_ACTION_ID);
+      service.detachAction(trigger.id, HELLO_WORLD_ACTION_ID);
+
+      const actions2 = service.getTriggerActions(trigger.id);
+      expect(actions2).toEqual([]);
+    });
+
+    test('detaching an invalid action from a trigger throws an error', async () => {
+      const service = new UiActionsService();
+
+      const action = {
+        id: HELLO_WORLD_ACTION_ID,
+        order: 25,
+      } as any;
+
+      service.registerAction(action);
+      expect(() => service.detachAction('i do not exist', HELLO_WORLD_ACTION_ID)).toThrowError(
+        'No trigger [triggerId = i do not exist] exists, for detaching action [actionId = HELLO_WORLD_ACTION_ID].'
+      );
+    });
+
+    test('attaching an invalid action to a trigger throws an error', async () => {
+      const service = new UiActionsService();
+
+      const action = {
+        id: HELLO_WORLD_ACTION_ID,
+        order: 25,
+      } as any;
+
+      service.registerAction(action);
+      expect(() => service.attachAction('i do not exist', HELLO_WORLD_ACTION_ID)).toThrowError(
+        'No trigger [triggerId = i do not exist] exists, for attaching action [actionId = HELLO_WORLD_ACTION_ID].'
+      );
+    });
+
+    test('cannot register another action with the same ID', async () => {
+      const service = new UiActionsService();
+
+      const action = {
+        id: HELLO_WORLD_ACTION_ID,
+        order: 25,
+      } as any;
+
+      service.registerAction(action);
+      expect(() => service.registerAction(action)).toThrowError(
+        'Action [action.id = HELLO_WORLD_ACTION_ID] already registered.'
+      );
+    });
+
+    test('cannot register another trigger with the same ID', async () => {
+      const service = new UiActionsService();
+
+      const trigger = { id: 'MY-TRIGGER' } as any;
+
+      service.registerTrigger(trigger);
+      expect(() => service.registerTrigger(trigger)).toThrowError(
+        'Trigger [trigger.id = MY-TRIGGER] already registered.'
+      );
+    });
+  });
+});
diff --git a/src/plugins/ui_actions/public/service/ui_actions_service.ts b/src/plugins/ui_actions/public/service/ui_actions_service.ts
new file mode 100644
index 0000000000000..a62d2aa356435
--- /dev/null
+++ b/src/plugins/ui_actions/public/service/ui_actions_service.ts
@@ -0,0 +1,194 @@
+/*
+ * 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 { TriggerRegistry, ActionRegistry, TriggerToActionsRegistry } from '../types';
+import { Action } from '../actions';
+import { Trigger } from '../triggers/trigger';
+import { buildContextMenuForActions, openContextMenu } from '../context_menu';
+
+export interface UiActionsServiceParams {
+  readonly triggers?: TriggerRegistry;
+  readonly actions?: ActionRegistry;
+
+  /**
+   * A 1-to-N mapping from `Trigger` to zero or more `Action`.
+   */
+  readonly triggerToActions?: TriggerToActionsRegistry;
+}
+
+export class UiActionsService {
+  protected readonly triggers: TriggerRegistry;
+  protected readonly actions: ActionRegistry;
+  protected readonly triggerToActions: TriggerToActionsRegistry;
+
+  constructor({
+    triggers = new Map(),
+    actions = new Map(),
+    triggerToActions = new Map(),
+  }: UiActionsServiceParams = {}) {
+    this.triggers = triggers;
+    this.actions = actions;
+    this.triggerToActions = triggerToActions;
+  }
+
+  public readonly registerTrigger = (trigger: Trigger) => {
+    if (this.triggers.has(trigger.id)) {
+      throw new Error(`Trigger [trigger.id = ${trigger.id}] already registered.`);
+    }
+
+    this.triggers.set(trigger.id, trigger);
+    this.triggerToActions.set(trigger.id, []);
+  };
+
+  public readonly getTrigger = (id: string) => {
+    const trigger = this.triggers.get(id);
+
+    if (!trigger) {
+      throw new Error(`Trigger [triggerId = ${id}] does not exist.`);
+    }
+
+    return trigger;
+  };
+
+  public readonly registerAction = (action: Action) => {
+    if (this.actions.has(action.id)) {
+      throw new Error(`Action [action.id = ${action.id}] already registered.`);
+    }
+
+    this.actions.set(action.id, action);
+  };
+
+  public readonly attachAction = (triggerId: string, actionId: string): void => {
+    const trigger = this.triggers.get(triggerId);
+
+    if (!trigger) {
+      throw new Error(
+        `No trigger [triggerId = ${triggerId}] exists, for attaching action [actionId = ${actionId}].`
+      );
+    }
+
+    const actionIds = this.triggerToActions.get(triggerId);
+
+    if (!actionIds!.find(id => id === actionId)) {
+      this.triggerToActions.set(triggerId, [...actionIds!, actionId]);
+    }
+  };
+
+  public readonly detachAction = (triggerId: string, actionId: string) => {
+    const trigger = this.triggers.get(triggerId);
+
+    if (!trigger) {
+      throw new Error(
+        `No trigger [triggerId = ${triggerId}] exists, for detaching action [actionId = ${actionId}].`
+      );
+    }
+
+    const actionIds = this.triggerToActions.get(triggerId);
+
+    this.triggerToActions.set(
+      triggerId,
+      actionIds!.filter(id => id !== actionId)
+    );
+  };
+
+  public readonly getTriggerActions = (triggerId: string) => {
+    // This line checks if trigger exists, otherwise throws.
+    this.getTrigger!(triggerId);
+
+    const actionIds = this.triggerToActions.get(triggerId);
+    const actions = actionIds!
+      .map(actionId => this.actions.get(actionId))
+      .filter(Boolean) as Action[];
+
+    return actions;
+  };
+
+  public readonly getTriggerCompatibleActions = async <C>(triggerId: string, context: C) => {
+    const actions = this.getTriggerActions!(triggerId);
+    const isCompatibles = await Promise.all(actions.map(action => action.isCompatible(context)));
+    return actions.reduce<Action[]>(
+      (acc, action, i) => (isCompatibles[i] ? [...acc, action] : acc),
+      []
+    );
+  };
+
+  private async executeSingleAction<A>(action: Action<A>, actionContext: A) {
+    const href = action.getHref && action.getHref(actionContext);
+
+    if (href) {
+      window.location.href = href;
+      return;
+    }
+
+    await action.execute(actionContext);
+  }
+
+  private async executeMultipleActions<C>(actions: Action[], actionContext: C) {
+    const panel = await buildContextMenuForActions({
+      actions,
+      actionContext,
+      closeMenu: () => session.close(),
+    });
+    const session = openContextMenu([panel]);
+  }
+
+  public readonly executeTriggerActions = async <C>(triggerId: string, actionContext: C) => {
+    const actions = await this.getTriggerCompatibleActions!(triggerId, actionContext);
+
+    if (!actions.length) {
+      throw new Error(
+        `No compatible actions found to execute for trigger [triggerId = ${triggerId}].`
+      );
+    }
+
+    if (actions.length === 1) {
+      await this.executeSingleAction(actions[0], actionContext);
+      return;
+    }
+
+    await this.executeMultipleActions(actions, actionContext);
+  };
+
+  /**
+   * Removes all registered triggers and actions.
+   */
+  public readonly clear = () => {
+    this.actions.clear();
+    this.triggers.clear();
+    this.triggerToActions.clear();
+  };
+
+  /**
+   * "Fork" a separate instance of `UiActionsService` that inherits all existing
+   * triggers and actions, but going forward all new triggers and actions added
+   * to this instance of `UiActionsService` are only available within this instance.
+   */
+  public readonly fork = (): UiActionsService => {
+    const triggers: TriggerRegistry = new Map();
+    const actions: ActionRegistry = new Map();
+    const triggerToActions: TriggerToActionsRegistry = new Map();
+
+    for (const [key, value] of this.triggers.entries()) triggers.set(key, value);
+    for (const [key, value] of this.actions.entries()) actions.set(key, value);
+    for (const [key, value] of this.triggerToActions.entries())
+      triggerToActions.set(key, [...value]);
+
+    return new UiActionsService({ triggers, actions, triggerToActions });
+  };
+}
diff --git a/src/plugins/ui_actions/public/tests/README.md b/src/plugins/ui_actions/public/tests/README.md
new file mode 100644
index 0000000000000..8ea3a89e7c120
--- /dev/null
+++ b/src/plugins/ui_actions/public/tests/README.md
@@ -0,0 +1,2 @@
+This folder contains integration tests for the `ui_actions` plugin and
+`test_samples` that other plugins can use in their tests.
diff --git a/src/plugins/ui_actions/public/triggers/execute_trigger_actions.test.ts b/src/plugins/ui_actions/public/tests/execute_trigger_actions.test.ts
similarity index 86%
rename from src/plugins/ui_actions/public/triggers/execute_trigger_actions.test.ts
rename to src/plugins/ui_actions/public/tests/execute_trigger_actions.test.ts
index 7f2506daee268..f8c196a623499 100644
--- a/src/plugins/ui_actions/public/triggers/execute_trigger_actions.test.ts
+++ b/src/plugins/ui_actions/public/tests/execute_trigger_actions.test.ts
@@ -19,7 +19,8 @@
 
 import { Action, createAction } from '../actions';
 import { openContextMenu } from '../context_menu';
-import { UiActionsTestPluginReturn, uiActionsTestPlugin } from '../tests/test_plugin';
+import { uiActionsPluginMock } from '../mocks';
+import { Trigger } from '../triggers';
 
 jest.mock('../context_menu');
 
@@ -37,14 +38,14 @@ function createTestAction<A>(id: string, checkCompatibility: (context: A) => boo
   });
 }
 
-let uiActions: UiActionsTestPluginReturn;
+let uiActions: ReturnType<typeof uiActionsPluginMock.createPlugin>;
 const reset = () => {
-  uiActions = uiActionsTestPlugin();
+  uiActions = uiActionsPluginMock.createPlugin();
 
   uiActions.setup.registerTrigger({
     id: CONTACT_USER_TRIGGER,
-    actionIds: ['SEND_MESSAGE_ACTION'],
   });
+  uiActions.setup.attachAction(CONTACT_USER_TRIGGER, 'SEND_MESSAGE_ACTION');
 
   executeFn.mockReset();
   openContextMenuSpy.mockReset();
@@ -53,14 +54,15 @@ beforeEach(reset);
 
 test('executes a single action mapped to a trigger', async () => {
   const { setup, doStart } = uiActions;
-  const trigger = {
+  const trigger: Trigger = {
     id: 'MY-TRIGGER',
     title: 'My trigger',
-    actionIds: ['test1'],
   };
   const action = createTestAction('test1', () => true);
+
   setup.registerTrigger(trigger);
   setup.registerAction(action);
+  setup.attachAction(trigger.id, 'test1');
 
   const context = {};
   const start = doStart();
@@ -72,12 +74,13 @@ test('executes a single action mapped to a trigger', async () => {
 
 test('throws an error if there are no compatible actions to execute', async () => {
   const { setup, doStart } = uiActions;
-  const trigger = {
+  const trigger: Trigger = {
     id: 'MY-TRIGGER',
     title: 'My trigger',
-    actionIds: ['testaction'],
   };
+
   setup.registerTrigger(trigger);
+  setup.attachAction(trigger.id, 'testaction');
 
   const context = {};
   const start = doStart();
@@ -88,14 +91,15 @@ test('throws an error if there are no compatible actions to execute', async () =
 
 test('does not execute an incompatible action', async () => {
   const { setup, doStart } = uiActions;
-  const trigger = {
+  const trigger: Trigger = {
     id: 'MY-TRIGGER',
     title: 'My trigger',
-    actionIds: ['test1'],
   };
   const action = createTestAction<{ name: string }>('test1', ({ name }) => name === 'executeme');
+
   setup.registerTrigger(trigger);
   setup.registerAction(action);
+  setup.attachAction(trigger.id, 'test1');
 
   const start = doStart();
   const context = {
@@ -108,16 +112,18 @@ test('does not execute an incompatible action', async () => {
 
 test('shows a context menu when more than one action is mapped to a trigger', async () => {
   const { setup, doStart } = uiActions;
-  const trigger = {
+  const trigger: Trigger = {
     id: 'MY-TRIGGER',
     title: 'My trigger',
-    actionIds: ['test1', 'test2'],
   };
   const action1 = createTestAction('test1', () => true);
   const action2 = createTestAction('test2', () => true);
+
   setup.registerTrigger(trigger);
   setup.registerAction(action1);
   setup.registerAction(action2);
+  setup.attachAction(trigger.id, 'test1');
+  setup.attachAction(trigger.id, 'test2');
 
   expect(openContextMenu).toHaveBeenCalledTimes(0);
 
@@ -134,7 +140,6 @@ test('passes whole action context to isCompatible()', async () => {
   const trigger = {
     id: 'MY-TRIGGER',
     title: 'My trigger',
-    actionIds: ['test'],
   };
   const action = createTestAction<{ foo: string }>('test', ({ foo }) => {
     expect(foo).toEqual('bar');
@@ -143,6 +148,8 @@ test('passes whole action context to isCompatible()', async () => {
 
   setup.registerTrigger(trigger);
   setup.registerAction(action);
+  setup.attachAction(trigger.id, 'test');
+
   const start = doStart();
 
   const context = { foo: 'bar' };
diff --git a/src/plugins/ui_actions/public/triggers/get_trigger_actions.test.ts b/src/plugins/ui_actions/public/tests/get_trigger_actions.test.ts
similarity index 93%
rename from src/plugins/ui_actions/public/triggers/get_trigger_actions.test.ts
rename to src/plugins/ui_actions/public/tests/get_trigger_actions.test.ts
index aef4114ffb4c6..e91acd4c7151b 100644
--- a/src/plugins/ui_actions/public/triggers/get_trigger_actions.test.ts
+++ b/src/plugins/ui_actions/public/tests/get_trigger_actions.test.ts
@@ -18,7 +18,7 @@
  */
 
 import { Action } from '../actions';
-import { uiActionsTestPlugin } from '../tests/test_plugin';
+import { uiActionsPluginMock } from '../mocks';
 
 const action1: Action = {
   id: 'action1',
@@ -32,11 +32,10 @@ const action2: Action = {
 } as any;
 
 test('returns actions set on trigger', () => {
-  const { setup, doStart } = uiActionsTestPlugin();
+  const { setup, doStart } = uiActionsPluginMock.createPlugin();
   setup.registerAction(action1);
   setup.registerAction(action2);
   setup.registerTrigger({
-    actionIds: [],
     description: 'foo',
     id: 'trigger',
     title: 'baz',
diff --git a/src/plugins/ui_actions/public/triggers/get_trigger_compatible_actions.test.ts b/src/plugins/ui_actions/public/tests/get_trigger_compatible_actions.test.ts
similarity index 82%
rename from src/plugins/ui_actions/public/triggers/get_trigger_compatible_actions.test.ts
rename to src/plugins/ui_actions/public/tests/get_trigger_compatible_actions.test.ts
index f4d2ea48ff6b9..a966003973aba 100644
--- a/src/plugins/ui_actions/public/triggers/get_trigger_compatible_actions.test.ts
+++ b/src/plugins/ui_actions/public/tests/get_trigger_compatible_actions.test.ts
@@ -18,35 +18,30 @@
  */
 
 import { createSayHelloAction } from '../tests/test_samples/say_hello_action';
-import { UiActionsTestPluginReturn, uiActionsTestPlugin } from '../tests/test_plugin';
+import { uiActionsPluginMock } from '../mocks';
 import { createRestrictedAction, createHelloWorldAction } from '../tests/test_samples';
 import { Action } from '../actions';
+import { Trigger } from '../triggers';
 
 let action: Action<{ name: string }>;
-let uiActions: UiActionsTestPluginReturn;
+let uiActions: ReturnType<typeof uiActionsPluginMock.createPlugin>;
 beforeEach(() => {
-  uiActions = uiActionsTestPlugin();
+  uiActions = uiActionsPluginMock.createPlugin();
   action = createSayHelloAction({} as any);
 
   uiActions.setup.registerAction(action);
   uiActions.setup.registerTrigger({
     id: 'trigger',
     title: 'trigger',
-    actionIds: [],
   });
   uiActions.setup.attachAction('trigger', action.id);
 });
 
-test('can register and get actions', async () => {
-  const { setup, plugin } = uiActions;
+test('can register action', async () => {
+  const { setup } = uiActions;
   const helloWorldAction = createHelloWorldAction({} as any);
-  const length = (plugin as any).actions.size;
 
   setup.registerAction(helloWorldAction);
-
-  expect((plugin as any).actions.size - length).toBe(1);
-  expect((plugin as any).actions.get(action.id)).toBe(action);
-  expect((plugin as any).actions.get(helloWorldAction.id)).toBe(helloWorldAction);
 });
 
 test('getTriggerCompatibleActions returns attached actions', async () => {
@@ -55,10 +50,9 @@ test('getTriggerCompatibleActions returns attached actions', async () => {
 
   setup.registerAction(helloWorldAction);
 
-  const testTrigger = {
+  const testTrigger: Trigger = {
     id: 'MY-TRIGGER',
     title: 'My trigger',
-    actionIds: [],
   };
   setup.registerTrigger(testTrigger);
   setup.attachAction('MY-TRIGGER', helloWorldAction.id);
@@ -78,13 +72,13 @@ test('filters out actions not applicable based on the context', async () => {
 
   setup.registerAction(restrictedAction);
 
-  const testTrigger = {
+  const testTrigger: Trigger = {
     id: 'MY-TRIGGER',
     title: 'My trigger',
-    actionIds: [restrictedAction.id],
   };
 
   setup.registerTrigger(testTrigger);
+  setup.attachAction(testTrigger.id, restrictedAction.id);
 
   const start = doStart();
   let actions = await start.getTriggerCompatibleActions(testTrigger.id, { accept: true });
@@ -107,10 +101,9 @@ test(`throws an error with an invalid trigger ID`, async () => {
 
 test(`with a trigger mapping that maps to an non-existing action returns empty list`, async () => {
   const { setup, doStart } = uiActions;
-  const testTrigger = {
+  const testTrigger: Trigger = {
     id: '123',
     title: '123',
-    actionIds: ['I do not exist'],
   };
   setup.registerTrigger(testTrigger);
 
diff --git a/src/plugins/ui_actions/public/tests/index.ts b/src/plugins/ui_actions/public/tests/index.ts
index 6f5610a7beb64..dbc34abb8acb4 100644
--- a/src/plugins/ui_actions/public/tests/index.ts
+++ b/src/plugins/ui_actions/public/tests/index.ts
@@ -17,4 +17,4 @@
  * under the License.
  */
 
-export { uiActionsTestPlugin } from './test_plugin';
+export * from './test_samples';
diff --git a/src/plugins/ui_actions/public/tests/test_plugin.ts b/src/plugins/ui_actions/public/tests/test_plugin.ts
deleted file mode 100644
index dcc42fd9f6fb2..0000000000000
--- a/src/plugins/ui_actions/public/tests/test_plugin.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { CoreSetup, CoreStart } from 'src/core/public';
-import { UiActionsPlugin, UiActionsSetup, UiActionsStart } from '../plugin';
-
-export interface UiActionsTestPluginReturn {
-  plugin: UiActionsPlugin;
-  coreSetup: CoreSetup;
-  coreStart: CoreStart;
-  setup: UiActionsSetup;
-  doStart: (anotherCoreStart?: CoreStart) => UiActionsStart;
-}
-
-export const uiActionsTestPlugin = (
-  coreSetup: CoreSetup = {} as CoreSetup,
-  coreStart: CoreStart = {} as CoreStart
-): UiActionsTestPluginReturn => {
-  const initializerContext = {} as any;
-  const plugin = new UiActionsPlugin(initializerContext);
-  const setup = plugin.setup(coreSetup);
-
-  return {
-    plugin,
-    coreSetup,
-    coreStart,
-    setup,
-    doStart: (anotherCoreStart: CoreStart = coreStart) => {
-      const start = plugin.start(anotherCoreStart);
-      return start;
-    },
-  };
-};
diff --git a/src/plugins/ui_actions/public/triggers/attach_action.ts b/src/plugins/ui_actions/public/triggers/attach_action.ts
deleted file mode 100644
index 6c0beeae2bcd7..0000000000000
--- a/src/plugins/ui_actions/public/triggers/attach_action.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { UiActionsApiPure } from '../types';
-
-export const attachAction: UiActionsApiPure['attachAction'] = ({ triggers }) => (
-  triggerId,
-  actionId
-) => {
-  const trigger = triggers.get(triggerId);
-
-  if (!trigger) {
-    throw new Error(
-      `No trigger [triggerId = ${triggerId}] exists, for attaching action [actionId = ${actionId}].`
-    );
-  }
-
-  if (!trigger.actionIds.find(id => id === actionId)) {
-    trigger.actionIds.push(actionId);
-  }
-};
diff --git a/src/plugins/ui_actions/public/triggers/detach_action.ts b/src/plugins/ui_actions/public/triggers/detach_action.ts
deleted file mode 100644
index 710dcf9f5621b..0000000000000
--- a/src/plugins/ui_actions/public/triggers/detach_action.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { UiActionsApiPure } from '../types';
-
-export const detachAction: UiActionsApiPure['detachAction'] = ({ triggers }) => (
-  triggerId,
-  actionId
-) => {
-  const trigger = triggers.get(triggerId);
-
-  if (!trigger) {
-    throw new Error(
-      `No trigger [triggerId = ${triggerId}] exists, for detaching action [actionId = ${actionId}].`
-    );
-  }
-
-  trigger.actionIds = trigger.actionIds.filter(id => id !== actionId);
-};
diff --git a/src/plugins/ui_actions/public/triggers/execute_trigger_actions.ts b/src/plugins/ui_actions/public/triggers/execute_trigger_actions.ts
deleted file mode 100644
index 71f69eb3bdc29..0000000000000
--- a/src/plugins/ui_actions/public/triggers/execute_trigger_actions.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { UiActionsApiPure } from '../types';
-import { buildContextMenuForActions, openContextMenu } from '../context_menu';
-import { Action } from '../actions';
-
-const executeSingleAction = async <A extends {} = {}>(action: Action<A>, actionContext: A) => {
-  const href = action.getHref && action.getHref(actionContext);
-
-  // TODO: Do we need a `getHref()` special case?
-  if (href) {
-    window.location.href = href;
-    return;
-  }
-
-  await action.execute(actionContext);
-};
-
-export const executeTriggerActions: UiActionsApiPure['executeTriggerActions'] = ({ api }) => async (
-  triggerId,
-  actionContext
-) => {
-  const actions = await api.getTriggerCompatibleActions!(triggerId, actionContext);
-
-  if (!actions.length) {
-    throw new Error(
-      `No compatible actions found to execute for trigger [triggerId = ${triggerId}].`
-    );
-  }
-
-  if (actions.length === 1) {
-    await executeSingleAction(actions[0], actionContext);
-    return;
-  }
-
-  const panel = await buildContextMenuForActions({
-    actions,
-    actionContext,
-    closeMenu: () => session.close(),
-  });
-  const session = openContextMenu([panel]);
-};
diff --git a/src/plugins/ui_actions/public/triggers/get_trigger.test.ts b/src/plugins/ui_actions/public/triggers/get_trigger.test.ts
deleted file mode 100644
index 88dd5a8990c9d..0000000000000
--- a/src/plugins/ui_actions/public/triggers/get_trigger.test.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { createApi } from '../api';
-import { createDeps } from '../tests/helpers';
-
-test('can get Trigger from registry', () => {
-  const deps = createDeps();
-  const { api } = createApi(deps);
-  api.registerTrigger({
-    actionIds: [],
-    description: 'foo',
-    id: 'bar',
-    title: 'baz',
-  });
-
-  const trigger = api.getTrigger('bar');
-
-  expect(trigger).toEqual({
-    actionIds: [],
-    description: 'foo',
-    id: 'bar',
-    title: 'baz',
-  });
-});
-
-test('throws if trigger does not exist', () => {
-  const deps = createDeps();
-  const { api } = createApi(deps);
-
-  expect(() => api.getTrigger('foo')).toThrowError('Trigger [triggerId = foo] does not exist.');
-});
diff --git a/src/plugins/ui_actions/public/triggers/get_trigger.ts b/src/plugins/ui_actions/public/triggers/get_trigger.ts
deleted file mode 100644
index 5c96200261a90..0000000000000
--- a/src/plugins/ui_actions/public/triggers/get_trigger.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { UiActionsApiPure } from '../types';
-
-export const getTrigger: UiActionsApiPure['getTrigger'] = ({ triggers }) => id => {
-  const trigger = triggers.get(id);
-
-  if (!trigger) {
-    throw new Error(`Trigger [triggerId = ${id}] does not exist.`);
-  }
-
-  return trigger;
-};
diff --git a/src/plugins/ui_actions/public/triggers/get_trigger_actions.ts b/src/plugins/ui_actions/public/triggers/get_trigger_actions.ts
deleted file mode 100644
index 37d7d5534c8c1..0000000000000
--- a/src/plugins/ui_actions/public/triggers/get_trigger_actions.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { UiActionsApiPure } from '../types';
-import { Action } from '../actions';
-
-export const getTriggerActions: UiActionsApiPure['getTriggerActions'] = ({
-  api,
-  actions,
-}) => id => {
-  const trigger = api.getTrigger!(id);
-  return trigger.actionIds.map(actionId => actions.get(actionId)).filter(Boolean) as Action[];
-};
diff --git a/src/plugins/ui_actions/public/triggers/get_trigger_compatible_actions.ts b/src/plugins/ui_actions/public/triggers/get_trigger_compatible_actions.ts
deleted file mode 100644
index 8be0db7561db9..0000000000000
--- a/src/plugins/ui_actions/public/triggers/get_trigger_compatible_actions.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { UiActionsApiPure } from '../types';
-import { Action } from '../actions/action';
-
-export const getTriggerCompatibleActions: UiActionsApiPure['getTriggerCompatibleActions'] = ({
-  api,
-}) => async (triggerId, context) => {
-  const actions = api.getTriggerActions!(triggerId);
-  const isCompatibles = await Promise.all(actions.map(action => action.isCompatible(context)));
-  return actions.reduce<Action[]>(
-    (acc, action, i) => (isCompatibles[i] ? [...acc, action] : acc),
-    []
-  );
-};
diff --git a/src/plugins/ui_actions/public/triggers/index.ts b/src/plugins/ui_actions/public/triggers/index.ts
index 3006a5428f45e..a34c6eda61ba0 100644
--- a/src/plugins/ui_actions/public/triggers/index.ts
+++ b/src/plugins/ui_actions/public/triggers/index.ts
@@ -17,4 +17,4 @@
  * under the License.
  */
 
-export { IncompatibleActionError } from './incompatible_action_error';
+export { Trigger } from './trigger';
diff --git a/src/plugins/ui_actions/public/triggers/register_trigger.ts b/src/plugins/ui_actions/public/triggers/register_trigger.ts
deleted file mode 100644
index c9a7bb211d05a..0000000000000
--- a/src/plugins/ui_actions/public/triggers/register_trigger.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { UiActionsApiPure } from '../types';
-
-export const registerTrigger: UiActionsApiPure['registerTrigger'] = ({ triggers }) => trigger => {
-  if (triggers.has(trigger.id)) {
-    throw new Error(`Trigger [trigger.id = ${trigger.id}] already registered.`);
-  }
-
-  triggers.set(trigger.id, trigger);
-};
diff --git a/src/plugins/ui_actions/public/triggers/registry.test.ts b/src/plugins/ui_actions/public/triggers/registry.test.ts
deleted file mode 100644
index 6edb2b19a95e4..0000000000000
--- a/src/plugins/ui_actions/public/triggers/registry.test.ts
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { createApi } from '../api';
-import { createDeps } from '../tests/helpers';
-
-const HELLO_WORLD_ACTION_ID = 'HELLO_WORLD_ACTION_ID';
-
-test('can register trigger', () => {
-  const deps = createDeps();
-  const { api } = createApi(deps);
-
-  api.registerTrigger({
-    actionIds: [],
-    description: 'foo',
-    id: 'bar',
-    title: 'baz',
-  });
-
-  expect(deps.triggers.get('bar')).toEqual({
-    actionIds: [],
-    description: 'foo',
-    id: 'bar',
-    title: 'baz',
-  });
-});
-
-test('can register action', () => {
-  const deps = createDeps();
-  const { api } = createApi(deps);
-
-  api.registerAction({
-    id: HELLO_WORLD_ACTION_ID,
-    order: 13,
-  } as any);
-
-  expect(deps.actions.get(HELLO_WORLD_ACTION_ID)).toMatchObject({
-    id: HELLO_WORLD_ACTION_ID,
-    order: 13,
-  });
-});
-
-test('can attach an action to a trigger', () => {
-  const deps = createDeps();
-  const { api } = createApi(deps);
-  const trigger = {
-    id: 'MY-TRIGGER',
-    actionIds: [],
-  };
-  const action = {
-    id: HELLO_WORLD_ACTION_ID,
-    order: 25,
-  } as any;
-
-  expect(trigger.actionIds).toEqual([]);
-
-  api.registerTrigger(trigger);
-  api.registerAction(action);
-  api.attachAction('MY-TRIGGER', HELLO_WORLD_ACTION_ID);
-
-  expect(trigger.actionIds).toEqual([HELLO_WORLD_ACTION_ID]);
-});
-
-test('can detach an action to a trigger', () => {
-  const deps = createDeps();
-  const { api } = createApi(deps);
-  const trigger = {
-    id: 'MY-TRIGGER',
-    actionIds: [],
-  };
-  const action = {
-    id: HELLO_WORLD_ACTION_ID,
-    order: 25,
-  } as any;
-
-  expect(trigger.actionIds).toEqual([]);
-
-  api.registerTrigger(trigger);
-  api.registerAction(action);
-  api.attachAction('MY-TRIGGER', HELLO_WORLD_ACTION_ID);
-  api.detachAction('MY-TRIGGER', HELLO_WORLD_ACTION_ID);
-
-  expect(trigger.actionIds).toEqual([]);
-});
-
-test('detaching an invalid action from a trigger throws an error', async () => {
-  const { api } = createApi({ actions: new Map<any, any>(), triggers: new Map<any, any>() });
-  const action = {
-    id: HELLO_WORLD_ACTION_ID,
-    order: 25,
-  } as any;
-
-  api.registerAction(action);
-  expect(() => api.detachAction('i do not exist', HELLO_WORLD_ACTION_ID)).toThrowError(
-    'No trigger [triggerId = i do not exist] exists, for detaching action [actionId = HELLO_WORLD_ACTION_ID].'
-  );
-});
-
-test('attaching an invalid action to a trigger throws an error', async () => {
-  const { api } = createApi({ actions: new Map<any, any>(), triggers: new Map<any, any>() });
-  const action = {
-    id: HELLO_WORLD_ACTION_ID,
-    order: 25,
-  } as any;
-
-  api.registerAction(action);
-  expect(() => api.attachAction('i do not exist', HELLO_WORLD_ACTION_ID)).toThrowError(
-    'No trigger [triggerId = i do not exist] exists, for attaching action [actionId = HELLO_WORLD_ACTION_ID].'
-  );
-});
-
-test('cannot register another action with the same ID', async () => {
-  const { api } = createApi({ actions: new Map<any, any>(), triggers: new Map<any, any>() });
-  const action = {
-    id: HELLO_WORLD_ACTION_ID,
-    order: 25,
-  } as any;
-
-  api.registerAction(action);
-  expect(() => api.registerAction(action)).toThrowError(
-    'Action [action.id = HELLO_WORLD_ACTION_ID] already registered.'
-  );
-});
-
-test('cannot register another trigger with the same ID', async () => {
-  const { api } = createApi({ actions: new Map<any, any>(), triggers: new Map<any, any>() });
-  const trigger = { id: 'MY-TRIGGER' } as any;
-
-  api.registerTrigger(trigger);
-  expect(() => api.registerTrigger(trigger)).toThrowError(
-    'Trigger [trigger.id = MY-TRIGGER] already registered.'
-  );
-});
diff --git a/src/plugins/ui_actions/public/triggers/trigger.ts b/src/plugins/ui_actions/public/triggers/trigger.ts
index 3db11953053d5..ba83f5619e250 100644
--- a/src/plugins/ui_actions/public/triggers/trigger.ts
+++ b/src/plugins/ui_actions/public/triggers/trigger.ts
@@ -21,5 +21,4 @@ export interface Trigger {
   id: string;
   title?: string;
   description?: string;
-  actionIds: string[];
 }
diff --git a/src/plugins/ui_actions/public/types.ts b/src/plugins/ui_actions/public/types.ts
index ed4728342b751..9bd6ffdef2af3 100644
--- a/src/plugins/ui_actions/public/types.ts
+++ b/src/plugins/ui_actions/public/types.ts
@@ -20,39 +20,6 @@
 import { Action } from './actions/action';
 import { Trigger } from './triggers/trigger';
 
-export { Action } from './actions';
-export { Trigger } from './triggers/trigger';
-
-export type ExecuteTriggerActions = <A>(triggerId: string, actionContext: A) => Promise<void>;
-
-export type GetActionsCompatibleWithTrigger = <C>(
-  triggerId: string,
-  context: C
-) => Promise<Action[]>;
-
-export interface UiActionsApi {
-  attachAction: (triggerId: string, actionId: string) => void;
-  detachAction: (triggerId: string, actionId: string) => void;
-  executeTriggerActions: ExecuteTriggerActions;
-  getTrigger: (id: string) => Trigger;
-  getTriggerActions: (id: string) => Action[];
-  getTriggerCompatibleActions: <C>(triggerId: string, context: C) => Promise<Array<Action<C>>>;
-  registerAction: (action: Action) => void;
-  registerTrigger: (trigger: Trigger) => void;
-}
-
-export interface UiActionsDependencies {
-  actions: ActionRegistry;
-  triggers: TriggerRegistry;
-}
-
-export interface UiActionsDependenciesInternal extends UiActionsDependencies {
-  api: Readonly<Partial<UiActionsApi>>;
-}
-
-export type UiActionsApiPure = {
-  [K in keyof UiActionsApi]: (deps: UiActionsDependenciesInternal) => UiActionsApi[K];
-};
-
 export type TriggerRegistry = Map<string, Trigger>;
 export type ActionRegistry = Map<string, Action>;
+export type TriggerToActionsRegistry = Map<string, string[]>;

From 85d737c401d492a715efcdf533efbd21d014d9c0 Mon Sep 17 00:00:00 2001
From: Vadim Dalecky <streamich@users.noreply.github.com>
Date: Mon, 17 Feb 2020 15:02:21 +0100
Subject: [PATCH 007/174] ExecutionContract (#57559)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat: 🎸 add ExecutionContract class

* test: 💍 add Execution and Executor tests

* feat: 🎸 add .execute() method to ExpressionsService

* refactor: 💡 replace ExpressionDataHandler by ExecutionContract

* fix: 🐛 fix TypeScript typecheck errors

* docs: ✏️ add JSDocs to ExecutionContract

* refactor: 💡 make .ast and .expresions both optional

* test: 💍 fix test

* test: 💍 fix a test

* test: 💍 fix interpreter functional tests
---
 .../common/execution/execution.test.ts        |  37 ++++-
 .../expressions/common/execution/execution.ts |  37 ++++-
 .../execution/execution_contract.test.ts      | 140 ++++++++++++++++++
 .../common/execution/execution_contract.ts    |  90 +++++++++++
 .../expressions/common/execution/index.ts     |   1 +
 .../executor/executor.execution.test.ts       |  59 ++++++++
 .../common/executor/executor.test.ts          |   4 +-
 .../expressions/common/executor/executor.ts   |  24 ++-
 .../common/service/expressions_services.ts    |  24 ++-
 .../expressions/public/execute.test.ts        | 100 -------------
 src/plugins/expressions/public/execute.ts     | 138 -----------------
 src/plugins/expressions/public/index.ts       |   2 +-
 src/plugins/expressions/public/loader.test.ts | 127 ++++------------
 src/plugins/expressions/public/loader.ts      |  42 +++---
 src/plugins/expressions/public/mocks.tsx      |   1 -
 src/plugins/expressions/public/plugin.test.ts |   9 ++
 src/plugins/expressions/public/plugin.ts      |   7 +-
 src/plugins/expressions/public/services.ts    |   5 +
 .../public/np_ready/app/components/main.tsx   |  11 +-
 .../public/np_ready/types.ts                  |   9 +-
 .../test_suites/run_pipeline/basic.ts         |   2 +-
 .../test_suites/run_pipeline/helpers.ts       |   6 +-
 22 files changed, 477 insertions(+), 398 deletions(-)
 create mode 100644 src/plugins/expressions/common/execution/execution_contract.test.ts
 create mode 100644 src/plugins/expressions/common/execution/execution_contract.ts
 create mode 100644 src/plugins/expressions/common/executor/executor.execution.test.ts
 delete mode 100644 src/plugins/expressions/public/execute.test.ts
 delete mode 100644 src/plugins/expressions/public/execute.ts

diff --git a/src/plugins/expressions/common/execution/execution.test.ts b/src/plugins/expressions/common/execution/execution.test.ts
index 3937bd309327d..eeb1ed80e8d0d 100644
--- a/src/plugins/expressions/common/execution/execution.test.ts
+++ b/src/plugins/expressions/common/execution/execution.test.ts
@@ -21,6 +21,7 @@ import { Execution } from './execution';
 import { parseExpression } from '../ast';
 import { createUnitTestExecutor } from '../test_helpers';
 import { ExpressionFunctionDefinition } from '../../public';
+import { ExecutionContract } from './execution_contract';
 
 const createExecution = (
   expression: string = 'foo bar=123',
@@ -48,7 +49,7 @@ const run = async (
 describe('Execution', () => {
   test('can instantiate', () => {
     const execution = createExecution('foo bar=123');
-    expect(execution.params.ast.chain[0].arguments.bar).toEqual([123]);
+    expect(execution.state.get().ast.chain[0].arguments.bar).toEqual([123]);
   });
 
   test('initial input is null at creation', () => {
@@ -127,6 +128,40 @@ describe('Execution', () => {
     });
   });
 
+  describe('.expression', () => {
+    test('uses expression passed in to constructor', () => {
+      const expression = 'add val="1"';
+      const executor = createUnitTestExecutor();
+      const execution = new Execution({
+        executor,
+        expression,
+      });
+      expect(execution.expression).toBe(expression);
+    });
+
+    test('generates expression from AST if not passed to constructor', () => {
+      const expression = 'add val="1"';
+      const executor = createUnitTestExecutor();
+      const execution = new Execution({
+        ast: parseExpression(expression),
+        executor,
+      });
+      expect(execution.expression).toBe(expression);
+    });
+  });
+
+  describe('.contract', () => {
+    test('is instance of ExecutionContract', () => {
+      const execution = createExecution('add val=1');
+      expect(execution.contract).toBeInstanceOf(ExecutionContract);
+    });
+
+    test('execution returns the same expression string', () => {
+      const execution = createExecution('add val=1');
+      expect(execution.expression).toBe(execution.contract.getExpression());
+    });
+  });
+
   describe('execution context', () => {
     test('context.variables is an object', async () => {
       const { result } = (await run('introspectContext key="variables"')) as any;
diff --git a/src/plugins/expressions/common/execution/execution.ts b/src/plugins/expressions/common/execution/execution.ts
index 7f4efafc13de8..2a272e187cffc 100644
--- a/src/plugins/expressions/common/execution/execution.ts
+++ b/src/plugins/expressions/common/execution/execution.ts
@@ -24,17 +24,25 @@ import { createError } from '../util';
 import { Defer } from '../../../kibana_utils/common';
 import { RequestAdapter, DataAdapter } from '../../../inspector/common';
 import { isExpressionValueError } from '../expression_types/specs/error';
-import { ExpressionAstExpression, ExpressionAstFunction, parse } from '../ast';
+import {
+  ExpressionAstExpression,
+  ExpressionAstFunction,
+  parse,
+  formatExpression,
+  parseExpression,
+} from '../ast';
 import { ExecutionContext, DefaultInspectorAdapters } from './types';
 import { getType } from '../expression_types';
 import { ArgumentType, ExpressionFunction } from '../expression_functions';
 import { getByAlias } from '../util/get_by_alias';
+import { ExecutionContract } from './execution_contract';
 
 export interface ExecutionParams<
   ExtraContext extends Record<string, unknown> = Record<string, unknown>
 > {
   executor: Executor<any>;
-  ast: ExpressionAstExpression;
+  ast?: ExpressionAstExpression;
+  expression?: string;
   context?: ExtraContext;
 }
 
@@ -85,6 +93,19 @@ export class Execution<
    */
   private readonly firstResultFuture = new Defer<Output>();
 
+  /**
+   * Contract is a public representation of `Execution` instances. Contract we
+   * can return to other plugins for their consumption.
+   */
+  public readonly contract: ExecutionContract<
+    ExtraContext,
+    Input,
+    Output,
+    InspectorAdapters
+  > = new ExecutionContract<ExtraContext, Input, Output, InspectorAdapters>(this);
+
+  public readonly expression: string;
+
   public get result(): Promise<unknown> {
     return this.firstResultFuture.promise;
   }
@@ -94,7 +115,17 @@ export class Execution<
   }
 
   constructor(public readonly params: ExecutionParams<ExtraContext>) {
-    const { executor, ast } = params;
+    const { executor } = params;
+
+    if (!params.ast && !params.expression) {
+      throw new TypeError('Execution params should contain at least .ast or .expression key.');
+    } else if (params.ast && params.expression) {
+      throw new TypeError('Execution params cannot contain both .ast and .expression key.');
+    }
+
+    this.expression = params.expression || formatExpression(params.ast!);
+    const ast = params.ast || parseExpression(this.expression);
+
     this.state = createExecutionContainer<Output>({
       ...executor.state.get(),
       state: 'not-started',
diff --git a/src/plugins/expressions/common/execution/execution_contract.test.ts b/src/plugins/expressions/common/execution/execution_contract.test.ts
new file mode 100644
index 0000000000000..c33f8a1a0f36e
--- /dev/null
+++ b/src/plugins/expressions/common/execution/execution_contract.test.ts
@@ -0,0 +1,140 @@
+/*
+ * 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 { Execution } from './execution';
+import { parseExpression } from '../ast';
+import { createUnitTestExecutor } from '../test_helpers';
+import { ExecutionContract } from './execution_contract';
+
+const createExecution = (
+  expression: string = 'foo bar=123',
+  context: Record<string, unknown> = {}
+) => {
+  const executor = createUnitTestExecutor();
+  const execution = new Execution({
+    executor,
+    ast: parseExpression(expression),
+    context,
+  });
+  return execution;
+};
+
+describe('ExecutionContract', () => {
+  test('can instantiate', () => {
+    const execution = createExecution('foo bar=123');
+    const contract = new ExecutionContract(execution);
+    expect(contract).toBeInstanceOf(ExecutionContract);
+  });
+
+  test('can get the AST of expression', () => {
+    const execution = createExecution('foo bar=123');
+    const contract = new ExecutionContract(execution);
+    expect(contract.getAst()).toMatchObject({
+      type: 'expression',
+      chain: expect.any(Array),
+    });
+  });
+
+  test('can get expression string', () => {
+    const execution = createExecution('foo bar=123');
+    const contract = new ExecutionContract(execution);
+    expect(contract.getExpression()).toBe('foo bar=123');
+  });
+
+  test('can cancel execution', () => {
+    const execution = createExecution('foo bar=123');
+    const spy = jest.spyOn(execution, 'cancel');
+    const contract = new ExecutionContract(execution);
+
+    expect(spy).toHaveBeenCalledTimes(0);
+    contract.cancel();
+    expect(spy).toHaveBeenCalledTimes(1);
+  });
+
+  test('can get inspector adapters', () => {
+    const execution = createExecution('foo bar=123');
+    const contract = new ExecutionContract(execution);
+    expect(contract.inspect()).toMatchObject({
+      data: expect.any(Object),
+      requests: expect.any(Object),
+    });
+  });
+
+  test('can get error result of the expression execution', async () => {
+    const execution = createExecution('foo bar=123');
+    const contract = new ExecutionContract(execution);
+    execution.start();
+
+    const result = await contract.getData();
+
+    expect(result).toMatchObject({
+      type: 'error',
+    });
+  });
+
+  test('can get result of the expression execution', async () => {
+    const execution = createExecution('var_set name="foo" value="bar" | var name="foo"');
+    const contract = new ExecutionContract(execution);
+    execution.start();
+
+    const result = await contract.getData();
+
+    expect(result).toBe('bar');
+  });
+
+  describe('isPending', () => {
+    test('is true if execution has not been started', async () => {
+      const execution = createExecution('var_set name="foo" value="bar" | var name="foo"');
+      const contract = new ExecutionContract(execution);
+      expect(contract.isPending).toBe(true);
+    });
+
+    test('is true when execution just started', async () => {
+      const execution = createExecution('var_set name="foo" value="bar" | var name="foo"');
+      const contract = new ExecutionContract(execution);
+
+      execution.start();
+
+      expect(contract.isPending).toBe(true);
+    });
+
+    test('is false when execution finished successfully', async () => {
+      const execution = createExecution('var_set name="foo" value="bar" | var name="foo"');
+      const contract = new ExecutionContract(execution);
+
+      execution.start();
+      await execution.result;
+
+      expect(contract.isPending).toBe(false);
+      expect(execution.state.get().state).toBe('result');
+    });
+
+    test('is false when execution finished with error', async () => {
+      const execution = createExecution('var_set name="foo" value="bar" | var name="foo"');
+      const contract = new ExecutionContract(execution);
+
+      execution.start();
+      await execution.result;
+      execution.state.get().state = 'error';
+
+      expect(contract.isPending).toBe(false);
+      expect(execution.state.get().state).toBe('error');
+    });
+  });
+});
diff --git a/src/plugins/expressions/common/execution/execution_contract.ts b/src/plugins/expressions/common/execution/execution_contract.ts
new file mode 100644
index 0000000000000..8c784352b9fdf
--- /dev/null
+++ b/src/plugins/expressions/common/execution/execution_contract.ts
@@ -0,0 +1,90 @@
+/*
+ * 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 { Execution } from './execution';
+
+/**
+ * `ExecutionContract` is a wrapper around `Execution` class. It provides the
+ * same functionality but does not expose Expressions plugin internals.
+ */
+export class ExecutionContract<
+  ExtraContext extends Record<string, unknown> = Record<string, unknown>,
+  Input = unknown,
+  Output = unknown,
+  InspectorAdapters = unknown
+> {
+  public get isPending(): boolean {
+    const state = this.execution.state.get().state;
+    const finished = state === 'error' || state === 'result';
+    return !finished;
+  }
+
+  constructor(
+    protected readonly execution: Execution<ExtraContext, Input, Output, InspectorAdapters>
+  ) {}
+
+  /**
+   * Cancel the execution of the expression. This will set abort signal
+   * (available in execution context) to aborted state, letting expression
+   * functions to stop their execution.
+   */
+  cancel = () => {
+    this.execution.cancel();
+  };
+
+  /**
+   * Returns the final output of expression, if any error happens still
+   * wraps that error into `ExpressionValueError` type and returns that.
+   * This function never throws.
+   */
+  getData = async () => {
+    try {
+      return await this.execution.result;
+    } catch (e) {
+      return {
+        type: 'error',
+        error: {
+          type: e.type,
+          message: e.message,
+          stack: e.stack,
+        },
+      };
+    }
+  };
+
+  /**
+   * Get string representation of the expression. Returns the original string
+   * if execution was started from a string. If execution was started from an
+   * AST this method returns a string generated from AST.
+   */
+  getExpression = () => {
+    return this.execution.expression;
+  };
+
+  /**
+   * Get AST used to execute the expression.
+   */
+  getAst = () => this.execution.state.get().ast;
+
+  /**
+   * Get Inspector adapters provided to all functions of expression through
+   * execution context.
+   */
+  inspect = () => this.execution.inspectorAdapters;
+}
diff --git a/src/plugins/expressions/common/execution/index.ts b/src/plugins/expressions/common/execution/index.ts
index 2452b0999d23e..fd5c0244438d7 100644
--- a/src/plugins/expressions/common/execution/index.ts
+++ b/src/plugins/expressions/common/execution/index.ts
@@ -20,3 +20,4 @@
 export * from './types';
 export * from './container';
 export * from './execution';
+export * from './execution_contract';
diff --git a/src/plugins/expressions/common/executor/executor.execution.test.ts b/src/plugins/expressions/common/executor/executor.execution.test.ts
new file mode 100644
index 0000000000000..eec7b5c907e29
--- /dev/null
+++ b/src/plugins/expressions/common/executor/executor.execution.test.ts
@@ -0,0 +1,59 @@
+/*
+ * 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 { Executor } from './executor';
+import { parseExpression } from '../ast';
+
+// eslint-disable-next-line
+const { __getArgs } = require('../execution/execution');
+
+jest.mock('../execution/execution', () => {
+  const mockedModule = {
+    args: undefined,
+    __getArgs: () => mockedModule.args,
+    Execution: function ExecutionMock(...args: any) {
+      mockedModule.args = args;
+    },
+  };
+
+  return mockedModule;
+});
+
+describe('Executor mocked execution tests', () => {
+  describe('createExecution()', () => {
+    describe('when execution is created from string', () => {
+      test('passes expression string to Execution', () => {
+        const executor = new Executor();
+        executor.createExecution('foo bar="baz"');
+
+        expect(__getArgs()[0].expression).toBe('foo bar="baz"');
+      });
+    });
+
+    describe('when execution is created from AST', () => {
+      test('does not pass in expression string', () => {
+        const executor = new Executor();
+        const ast = parseExpression('foo bar="baz"');
+        executor.createExecution(ast);
+
+        expect(__getArgs()[0].expression).toBe(undefined);
+      });
+    });
+  });
+});
diff --git a/src/plugins/expressions/common/executor/executor.test.ts b/src/plugins/expressions/common/executor/executor.test.ts
index 502728bb66403..4e43cedd18157 100644
--- a/src/plugins/expressions/common/executor/executor.test.ts
+++ b/src/plugins/expressions/common/executor/executor.test.ts
@@ -130,7 +130,7 @@ describe('Executor', () => {
         const execution = executor.createExecution('foo bar="baz"');
 
         expect(execution).toBeInstanceOf(Execution);
-        expect(execution.params.ast.chain[0].function).toBe('foo');
+        expect(execution.state.get().ast.chain[0].function).toBe('foo');
       });
 
       test('returns Execution object from AST', () => {
@@ -139,7 +139,7 @@ describe('Executor', () => {
         const execution = executor.createExecution(ast);
 
         expect(execution).toBeInstanceOf(Execution);
-        expect(execution.params.ast.chain[0].function).toBe('foo');
+        expect(execution.state.get().ast.chain[0].function).toBe('foo');
       });
 
       test('Execution inherits context from Executor', () => {
diff --git a/src/plugins/expressions/common/executor/executor.ts b/src/plugins/expressions/common/executor/executor.ts
index 5c27201b43fc0..24c1648a8cd0f 100644
--- a/src/plugins/expressions/common/executor/executor.ts
+++ b/src/plugins/expressions/common/executor/executor.ts
@@ -22,12 +22,12 @@
 import { ExecutorState, ExecutorContainer } from './container';
 import { createExecutorContainer } from './container';
 import { AnyExpressionFunctionDefinition, ExpressionFunction } from '../expression_functions';
-import { Execution } from '../execution/execution';
+import { Execution, ExecutionParams } from '../execution/execution';
 import { IRegistry } from '../types';
 import { ExpressionType } from '../expression_types/expression_type';
 import { AnyExpressionTypeDefinition } from '../expression_types/types';
 import { getType } from '../expression_types';
-import { ExpressionAstExpression, ExpressionAstNode, parseExpression } from '../ast';
+import { ExpressionAstExpression, ExpressionAstNode } from '../ast';
 import { typeSpecs } from '../expression_types/specs';
 import { functionSpecs } from '../expression_functions/specs';
 
@@ -186,19 +186,27 @@ export class Executor<Context extends Record<string, unknown> = Record<string, u
     return (await execution.result) as Output;
   }
 
-  public createExecution<ExtraContext extends Record<string, unknown> = Record<string, unknown>>(
+  public createExecution<
+    ExtraContext extends Record<string, unknown> = Record<string, unknown>,
+    Input = unknown,
+    Output = unknown
+  >(
     ast: string | ExpressionAstExpression,
     context: ExtraContext = {} as ExtraContext
-  ): Execution<Context & ExtraContext> {
-    if (typeof ast === 'string') ast = parseExpression(ast);
-    const execution = new Execution<Context & ExtraContext>({
-      ast,
+  ): Execution<Context & ExtraContext, Input, Output> {
+    const params: ExecutionParams<Context & ExtraContext> = {
       executor: this,
       context: {
         ...this.context,
         ...context,
       } as Context & ExtraContext,
-    });
+    };
+
+    if (typeof ast === 'string') params.expression = ast;
+    else params.ast = ast;
+
+    const execution = new Execution<Context & ExtraContext, Input, Output>(params);
+
     return execution;
   }
 }
diff --git a/src/plugins/expressions/common/service/expressions_services.ts b/src/plugins/expressions/common/service/expressions_services.ts
index 8543fbe0fced2..9663c05f0d7c2 100644
--- a/src/plugins/expressions/common/service/expressions_services.ts
+++ b/src/plugins/expressions/common/service/expressions_services.ts
@@ -20,6 +20,7 @@
 import { Executor } from '../executor';
 import { ExpressionRendererRegistry } from '../expression_renderers';
 import { ExpressionAstExpression } from '../ast';
+import { ExecutionContract } from '../execution/execution_contract';
 
 export type ExpressionsServiceSetup = ReturnType<ExpressionsService['setup']>;
 export type ExpressionsServiceStart = ReturnType<ExpressionsService['start']>;
@@ -117,6 +118,26 @@ export class ExpressionsService {
     context?: ExtraContext
   ): Promise<Output> => this.executor.run<Input, Output, ExtraContext>(ast, input, context);
 
+  /**
+   * Starts expression execution and immediately returns `ExecutionContract`
+   * instance that tracks the progress of the execution and can be used to
+   * interact with the execution.
+   */
+  public readonly execute = <
+    Input = unknown,
+    Output = unknown,
+    ExtraContext extends Record<string, unknown> = Record<string, unknown>
+  >(
+    ast: string | ExpressionAstExpression,
+    // This any is for legacy reasons.
+    input: Input = { type: 'null' } as any,
+    context?: ExtraContext
+  ): ExecutionContract<ExtraContext, Input, Output> => {
+    const execution = this.executor.createExecution<ExtraContext, Input, Output>(ast, context);
+    execution.start(input);
+    return execution.contract;
+  };
+
   public setup() {
     const { executor, renderers, registerFunction, run } = this;
 
@@ -144,7 +165,7 @@ export class ExpressionsService {
   }
 
   public start() {
-    const { executor, renderers, run } = this;
+    const { execute, executor, renderers, run } = this;
 
     const getFunction = executor.getFunction.bind(executor);
     const getFunctions = executor.getFunctions.bind(executor);
@@ -154,6 +175,7 @@ export class ExpressionsService {
     const getTypes = executor.getTypes.bind(executor);
 
     return {
+      execute,
       getFunction,
       getFunctions,
       getRenderer,
diff --git a/src/plugins/expressions/public/execute.test.ts b/src/plugins/expressions/public/execute.test.ts
deleted file mode 100644
index 2f2a303bad4c4..0000000000000
--- a/src/plugins/expressions/public/execute.test.ts
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { execute, ExpressionDataHandler } from './execute';
-import { ExpressionAstExpression, parseExpression } from '../common';
-
-jest.mock('./services', () => ({
-  getInterpreter: () => {
-    return {
-      interpretAst: async (expression: ExpressionAstExpression) => {
-        return {};
-      },
-    };
-  },
-  getNotifications: jest.fn(() => {
-    return {
-      toasts: {
-        addError: jest.fn(() => {}),
-      },
-    };
-  }),
-}));
-
-describe('execute helper function', () => {
-  it('returns ExpressionDataHandler instance', () => {
-    const response = execute('');
-    expect(response).toBeInstanceOf(ExpressionDataHandler);
-  });
-});
-
-describe('ExpressionDataHandler', () => {
-  const expressionString = '';
-
-  describe('constructor', () => {
-    it('accepts expression string', () => {
-      const expressionDataHandler = new ExpressionDataHandler(expressionString, {});
-      expect(expressionDataHandler.getExpression()).toEqual(expressionString);
-    });
-
-    it('accepts expression AST', () => {
-      const expressionAST = parseExpression(expressionString) as ExpressionAstExpression;
-      const expressionDataHandler = new ExpressionDataHandler(expressionAST, {});
-      expect(expressionDataHandler.getExpression()).toEqual(expressionString);
-      expect(expressionDataHandler.getAst()).toEqual(expressionAST);
-    });
-
-    it('allows passing in context', () => {
-      const expressionDataHandler = new ExpressionDataHandler(expressionString, {
-        context: { test: 'hello' },
-      });
-      expect(expressionDataHandler.getExpression()).toEqual(expressionString);
-    });
-
-    it('allows passing in search context', () => {
-      const expressionDataHandler = new ExpressionDataHandler(expressionString, {
-        searchContext: { filters: [] },
-      });
-      expect(expressionDataHandler.getExpression()).toEqual(expressionString);
-    });
-  });
-
-  describe('getData()', () => {
-    it('returns a promise', () => {
-      const expressionDataHandler = new ExpressionDataHandler(expressionString, {});
-      expect(expressionDataHandler.getData()).toBeInstanceOf(Promise);
-    });
-
-    it('promise resolves with data', async () => {
-      const expressionDataHandler = new ExpressionDataHandler(expressionString, {});
-      expect(await expressionDataHandler.getData()).toEqual({});
-    });
-  });
-
-  it('cancel() aborts request', () => {
-    const expressionDataHandler = new ExpressionDataHandler(expressionString, {});
-    expressionDataHandler.cancel();
-  });
-
-  it('inspect() returns correct inspector adapters', () => {
-    const expressionDataHandler = new ExpressionDataHandler(expressionString, {});
-    expect(expressionDataHandler.inspect()).toHaveProperty('requests');
-    expect(expressionDataHandler.inspect()).toHaveProperty('data');
-  });
-});
diff --git a/src/plugins/expressions/public/execute.ts b/src/plugins/expressions/public/execute.ts
deleted file mode 100644
index c07fb9ad0549c..0000000000000
--- a/src/plugins/expressions/public/execute.ts
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { DataAdapter, RequestAdapter, Adapters } from '../../inspector/public';
-import { getInterpreter } from './services';
-import { IExpressionLoaderParams } from './types';
-import {
-  ExpressionAstExpression,
-  parseExpression,
-  formatExpression,
-  ExpressionValue,
-} from '../common';
-
-/**
- * The search context describes a specific context (filters, time range and query)
- * that will be applied to the expression for execution. Not every expression will
- * be effected by that. You have to use special functions
- * that will pick up this search context and forward it to following functions that
- * understand it.
- */
-
-export class ExpressionDataHandler {
-  private abortController: AbortController;
-  private expression: string;
-  private ast: ExpressionAstExpression;
-
-  private inspectorAdapters: Adapters;
-  private promise: Promise<ExpressionValue>;
-
-  public isPending: boolean = true;
-  constructor(expression: string | ExpressionAstExpression, params: IExpressionLoaderParams) {
-    if (typeof expression === 'string') {
-      this.expression = expression;
-      this.ast = parseExpression(expression);
-    } else {
-      this.ast = expression;
-      this.expression = formatExpression(this.ast);
-    }
-
-    this.abortController = new AbortController();
-    this.inspectorAdapters = params.inspectorAdapters || this.getActiveInspectorAdapters();
-
-    const defaultInput = { type: 'null' };
-    const interpreter = getInterpreter();
-    this.promise = interpreter
-      .interpretAst<any, ExpressionValue>(this.ast, params.context || defaultInput, {
-        search: params.searchContext,
-        inspectorAdapters: this.inspectorAdapters,
-        abortSignal: this.abortController.signal,
-        variables: params.variables,
-      })
-      .then(
-        (v: ExpressionValue) => {
-          this.isPending = false;
-          return v;
-        },
-        () => {
-          this.isPending = false;
-        }
-      ) as Promise<ExpressionValue>;
-  }
-
-  cancel = () => {
-    this.abortController.abort();
-  };
-
-  getData = async () => {
-    try {
-      return await this.promise;
-    } catch (e) {
-      return {
-        type: 'error',
-        error: {
-          type: e.type,
-          message: e.message,
-          stack: e.stack,
-        },
-      };
-    }
-  };
-
-  getExpression = () => {
-    return this.expression;
-  };
-
-  getAst = () => {
-    return this.ast;
-  };
-
-  inspect = () => {
-    return this.inspectorAdapters;
-  };
-
-  /**
-   * Returns an object of all inspectors for this vis object.
-   * This must only be called after this.type has properly be initialized,
-   * since we need to read out data from the the vis type to check which
-   * inspectors are available.
-   */
-  private getActiveInspectorAdapters = (): Adapters => {
-    const adapters: Adapters = {};
-
-    // Add the requests inspector adapters if the vis type explicitly requested it via
-    // inspectorAdapters.requests: true in its definition or if it's using the courier
-    // request handler, since that will automatically log its requests.
-    adapters.requests = new RequestAdapter();
-
-    // Add the data inspector adapter if the vis type requested it or if the
-    // vis is using courier, since we know that courier supports logging
-    // its data.
-    adapters.data = new DataAdapter();
-
-    return adapters;
-  };
-}
-
-export function execute(
-  expression: string | ExpressionAstExpression,
-  params: IExpressionLoaderParams = {}
-): ExpressionDataHandler {
-  return new ExpressionDataHandler(expression, params);
-}
diff --git a/src/plugins/expressions/public/index.ts b/src/plugins/expressions/public/index.ts
index 5f64c11f4efe6..06dd951cd5410 100644
--- a/src/plugins/expressions/public/index.ts
+++ b/src/plugins/expressions/public/index.ts
@@ -37,7 +37,6 @@ export {
   ReactExpressionRendererProps,
   ReactExpressionRendererType,
 } from './react_expression_renderer';
-export { ExpressionDataHandler } from './execute';
 export { ExpressionRenderHandler } from './render';
 export {
   AnyExpressionFunctionDefinition,
@@ -48,6 +47,7 @@ export {
   DatatableColumnType,
   DatatableRow,
   Execution,
+  ExecutionContract,
   ExecutionContainer,
   ExecutionContext,
   ExecutionParams,
diff --git a/src/plugins/expressions/public/loader.test.ts b/src/plugins/expressions/public/loader.test.ts
index 480434244d6f5..e07a22a5e1d60 100644
--- a/src/plugins/expressions/public/loader.test.ts
+++ b/src/plugins/expressions/public/loader.test.ts
@@ -19,10 +19,12 @@
 
 import { first, skip, toArray } from 'rxjs/operators';
 import { loader, ExpressionLoader } from './loader';
-import { ExpressionDataHandler } from './execute';
 import { Observable } from 'rxjs';
 import { ExpressionAstExpression, parseExpression, IInterpreterRenderHandlers } from '../common';
 
+// eslint-disable-next-line
+const { __getLastExecution } = require('./services');
+
 const element: HTMLElement = null as any;
 
 jest.mock('./services', () => {
@@ -33,7 +35,13 @@ jest.mock('./services', () => {
       },
     },
   };
-  return {
+
+  // eslint-disable-next-line
+  const service = new (require('../common/service/expressions_services').ExpressionsService as any)();
+
+  const moduleMock = {
+    __execution: undefined,
+    __getLastExecution: () => moduleMock.__execution,
     getInterpreter: () => {
       return {
         interpretAst: async (expression: ExpressionAstExpression) => {
@@ -51,17 +59,19 @@ jest.mock('./services', () => {
         },
       };
     }),
+    getExpressionsService: () => service,
   };
-});
 
-jest.mock('./execute', () => {
-  const actual = jest.requireActual('./execute');
-  return {
-    ExpressionDataHandler: jest
-      .fn()
-      .mockImplementation((...args) => new actual.ExpressionDataHandler(...args)),
-    execute: jest.fn().mockReturnValue(actual.execute),
+  const execute = service.execute;
+  service.execute = (...args: any) => {
+    const execution = execute(...args);
+    jest.spyOn(execution, 'getData');
+    jest.spyOn(execution, 'cancel');
+    moduleMock.__execution = execution;
+    return execution;
   };
+
+  return moduleMock;
 });
 
 describe('execute helper function', () => {
@@ -97,9 +107,9 @@ describe('ExpressionLoader', () => {
   });
 
   it('emits on $data when data is available', async () => {
-    const expressionLoader = new ExpressionLoader(element, expressionString, {});
+    const expressionLoader = new ExpressionLoader(element, 'var foo', { variables: { foo: 123 } });
     const response = await expressionLoader.data$.pipe(first()).toPromise();
-    expect(response).toEqual({ type: 'render', as: 'test' });
+    expect(response).toBe(123);
   });
 
   it('emits on loading$ on initial load and on updates', async () => {
@@ -128,94 +138,13 @@ describe('ExpressionLoader', () => {
   });
 
   it('cancels the previous request when the expression is updated', () => {
-    const cancelMock = jest.fn();
-
-    (ExpressionDataHandler as jest.Mock).mockImplementationOnce(() => ({
-      getData: () => true,
-      cancel: cancelMock,
-      isPending: () => true,
-      inspect: () => {},
-    }));
-
-    const expressionLoader = new ExpressionLoader(element, expressionString, {});
-    expressionLoader.update('new', {});
-
-    expect(cancelMock).toHaveBeenCalledTimes(1);
-  });
-
-  it('does not send an observable message if a request was aborted', () => {
-    const cancelMock = jest.fn();
-
-    const getData = jest
-      .fn()
-      .mockResolvedValueOnce({
-        type: 'error',
-        error: {
-          name: 'AbortError',
-        },
-      })
-      .mockResolvedValueOnce({
-        type: 'real',
-      });
-
-    (ExpressionDataHandler as jest.Mock).mockImplementationOnce(() => ({
-      getData,
-      cancel: cancelMock,
-      isPending: () => true,
-      inspect: () => {},
-    }));
-
-    (ExpressionDataHandler as jest.Mock).mockImplementationOnce(() => ({
-      getData,
-      cancel: cancelMock,
-      isPending: () => true,
-      inspect: () => {},
-    }));
-
-    const expressionLoader = new ExpressionLoader(element, expressionString, {});
-
-    expect.assertions(2);
-    expressionLoader.data$.subscribe({
-      next(data) {
-        expect(data).toEqual({
-          type: 'real',
-        });
-      },
-      error() {
-        expect(false).toEqual('Should not be called');
-      },
-    });
-
-    expressionLoader.update('new expression', {});
-
-    expect(getData).toHaveBeenCalledTimes(2);
-  });
-
-  it('sends an observable error if the data fetching failed', () => {
-    const cancelMock = jest.fn();
-
-    const getData = jest.fn().mockResolvedValue('rejected');
-
-    (ExpressionDataHandler as jest.Mock).mockImplementationOnce(() => ({
-      getData,
-      cancel: cancelMock,
-      isPending: () => true,
-      inspect: () => {},
-    }));
-
-    const expressionLoader = new ExpressionLoader(element, expressionString, {});
-
-    expect.assertions(2);
-    expressionLoader.data$.subscribe({
-      next(data) {
-        expect(data).toEqual('Should not be called');
-      },
-      error(error) {
-        expect(error.message).toEqual('Could not fetch data');
-      },
-    });
+    const expressionLoader = new ExpressionLoader(element, 'var foo', {});
+    const execution = __getLastExecution();
+    jest.spyOn(execution, 'cancel');
 
-    expect(getData).toHaveBeenCalledTimes(1);
+    expect(execution.cancel).toHaveBeenCalledTimes(0);
+    expressionLoader.update('var bar', {});
+    expect(execution.cancel).toHaveBeenCalledTimes(1);
   });
 
   it('inspect() returns correct inspector adapters', () => {
diff --git a/src/plugins/expressions/public/loader.ts b/src/plugins/expressions/public/loader.ts
index 320a8469fe9e3..4600922e076fa 100644
--- a/src/plugins/expressions/public/loader.ts
+++ b/src/plugins/expressions/public/loader.ts
@@ -20,11 +20,11 @@
 import { BehaviorSubject, Observable, Subject } from 'rxjs';
 import { filter, map } from 'rxjs/operators';
 import { Adapters, InspectorSession } from '../../inspector/public';
-import { ExpressionDataHandler } from './execute';
 import { ExpressionRenderHandler } from './render';
 import { IExpressionLoaderParams } from './types';
 import { ExpressionAstExpression } from '../common';
-import { getInspector } from './services';
+import { getInspector, getExpressionsService } from './services';
+import { ExecutionContract } from '../common/execution/execution_contract';
 
 type Data = any;
 
@@ -35,7 +35,7 @@ export class ExpressionLoader {
   events$: ExpressionRenderHandler['events$'];
   loading$: Observable<void>;
 
-  private dataHandler: ExpressionDataHandler | undefined;
+  private execution: ExecutionContract | undefined;
   private renderHandler: ExpressionRenderHandler;
   private dataSubject: Subject<Data>;
   private loadingSubject: Subject<boolean>;
@@ -93,26 +93,26 @@ export class ExpressionLoader {
     this.dataSubject.complete();
     this.loadingSubject.complete();
     this.renderHandler.destroy();
-    if (this.dataHandler) {
-      this.dataHandler.cancel();
+    if (this.execution) {
+      this.execution.cancel();
     }
   }
 
   cancel() {
-    if (this.dataHandler) {
-      this.dataHandler.cancel();
+    if (this.execution) {
+      this.execution.cancel();
     }
   }
 
   getExpression(): string | undefined {
-    if (this.dataHandler) {
-      return this.dataHandler.getExpression();
+    if (this.execution) {
+      return this.execution.getExpression();
     }
   }
 
   getAst(): ExpressionAstExpression | undefined {
-    if (this.dataHandler) {
-      return this.dataHandler.getAst();
+    if (this.execution) {
+      return this.execution.getAst();
     }
   }
 
@@ -130,9 +130,7 @@ export class ExpressionLoader {
   }
 
   inspect(): Adapters | undefined {
-    if (this.dataHandler) {
-      return this.dataHandler.inspect();
-    }
+    return this.execution ? (this.execution.inspect() as Adapters) : undefined;
   }
 
   update(expression?: string | ExpressionAstExpression, params?: IExpressionLoaderParams): void {
@@ -150,15 +148,19 @@ export class ExpressionLoader {
     expression: string | ExpressionAstExpression,
     params: IExpressionLoaderParams
   ): Promise<void> => {
-    if (this.dataHandler && this.dataHandler.isPending) {
-      this.dataHandler.cancel();
+    if (this.execution && this.execution.isPending) {
+      this.execution.cancel();
     }
     this.setParams(params);
-    this.dataHandler = new ExpressionDataHandler(expression, params);
-    if (!params.inspectorAdapters) params.inspectorAdapters = this.dataHandler.inspect();
-    const prevDataHandler = this.dataHandler;
+    this.execution = getExpressionsService().execute(expression, params.context, {
+      search: params.searchContext,
+      variables: params.variables || {},
+      inspectorAdapters: params.inspectorAdapters,
+    });
+    if (!params.inspectorAdapters) params.inspectorAdapters = this.execution.inspect() as Adapters;
+    const prevDataHandler = this.execution;
     const data = await prevDataHandler.getData();
-    if (this.dataHandler !== prevDataHandler) {
+    if (this.execution !== prevDataHandler) {
       return;
     }
     this.dataSubject.next(data);
diff --git a/src/plugins/expressions/public/mocks.tsx b/src/plugins/expressions/public/mocks.tsx
index 70760ada83955..40ae698bc95eb 100644
--- a/src/plugins/expressions/public/mocks.tsx
+++ b/src/plugins/expressions/public/mocks.tsx
@@ -65,7 +65,6 @@ const createSetupContract = (): Setup => {
 const createStartContract = (): Start => {
   return {
     execute: jest.fn(),
-    ExpressionDataHandler: jest.fn(),
     ExpressionLoader: jest.fn(),
     ExpressionRenderHandler: jest.fn(),
     getFunction: jest.fn(),
diff --git a/src/plugins/expressions/public/plugin.test.ts b/src/plugins/expressions/public/plugin.test.ts
index 5437a7d21f338..fdfd583eac9de 100644
--- a/src/plugins/expressions/public/plugin.test.ts
+++ b/src/plugins/expressions/public/plugin.test.ts
@@ -65,6 +65,15 @@ describe('ExpressionsPublicPlugin', () => {
           }
         `);
       });
+
+      test('"kibana" function return value of type "kibana_context"', async () => {
+        const { doStart } = await expressionsPluginMock.createPlugin();
+        const start = await doStart();
+        const execution = start.execute('kibana');
+        const result = await execution.getData();
+
+        expect((result as any).type).toBe('kibana_context');
+      });
     });
   });
 });
diff --git a/src/plugins/expressions/public/plugin.ts b/src/plugins/expressions/public/plugin.ts
index 6799b1590f252..aac429b365c48 100644
--- a/src/plugins/expressions/public/plugin.ts
+++ b/src/plugins/expressions/public/plugin.ts
@@ -36,11 +36,11 @@ import {
   setInterpreter,
   setRenderersRegistry,
   setNotifications,
+  setExpressionsService,
 } from './services';
 import { kibanaContext as kibanaContextFunction } from './expression_functions/kibana_context';
 import { ReactExpressionRenderer } from './react_expression_renderer';
 import { ExpressionLoader, loader } from './loader';
-import { ExpressionDataHandler, execute } from './execute';
 import { render, ExpressionRenderHandler } from './render';
 
 export interface ExpressionsSetupDeps {
@@ -92,8 +92,6 @@ export interface ExpressionsSetup extends ExpressionsServiceSetup {
 }
 
 export interface ExpressionsStart extends ExpressionsServiceStart {
-  execute: typeof execute;
-  ExpressionDataHandler: typeof ExpressionDataHandler;
   ExpressionLoader: typeof ExpressionLoader;
   ExpressionRenderHandler: typeof ExpressionRenderHandler;
   loader: typeof loader;
@@ -118,6 +116,7 @@ export class ExpressionsPublicPlugin
     executor.registerFunction(kibanaContextFunction());
 
     setRenderersRegistry(renderers);
+    setExpressionsService(this.expressions);
 
     const expressionsSetup = expressions.setup();
 
@@ -180,8 +179,6 @@ export class ExpressionsPublicPlugin
 
     return {
       ...expressionsStart,
-      execute,
-      ExpressionDataHandler,
       ExpressionLoader,
       ExpressionRenderHandler,
       loader,
diff --git a/src/plugins/expressions/public/services.ts b/src/plugins/expressions/public/services.ts
index 75ec4826ea45a..4fdff9b151ac2 100644
--- a/src/plugins/expressions/public/services.ts
+++ b/src/plugins/expressions/public/services.ts
@@ -22,6 +22,7 @@ import { createKibanaUtilsCore, createGetterSetter } from '../../kibana_utils/pu
 import { ExpressionInterpreter } from './types';
 import { Start as IInspector } from '../../inspector/public';
 import { ExpressionsSetup } from './plugin';
+import { ExpressionsService } from '../common';
 
 export const { getCoreStart, setCoreStart, savedObjects } = createKibanaUtilsCore();
 
@@ -37,3 +38,7 @@ export const [getNotifications, setNotifications] = createGetterSetter<Notificat
 export const [getRenderersRegistry, setRenderersRegistry] = createGetterSetter<
   ExpressionsSetup['__LEGACY']['renderers']
 >('Renderers registry');
+
+export const [getExpressionsService, setExpressionsService] = createGetterSetter<
+  ExpressionsService
+>('ExpressionsService');
diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/app/components/main.tsx b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/app/components/main.tsx
index 41e466fddd11e..a50248a5b6fa3 100644
--- a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/app/components/main.tsx
+++ b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/app/components/main.tsx
@@ -22,7 +22,7 @@ import { EuiPage, EuiPageBody, EuiPageContent, EuiPageContentHeader } from '@ela
 import { first } from 'rxjs/operators';
 import { IInterpreterRenderHandlers, ExpressionValue } from 'src/plugins/expressions';
 import { RequestAdapter, DataAdapter } from '../../../../../../../../src/plugins/inspector';
-import { Adapters, ExpressionRenderHandler, ExpressionDataHandler } from '../../types';
+import { Adapters, ExpressionRenderHandler } from '../../types';
 import { getExpressions } from '../../services';
 
 declare global {
@@ -31,7 +31,7 @@ declare global {
       expressions: string,
       context?: ExpressionValue,
       initialContext?: ExpressionValue
-    ) => ReturnType<ExpressionDataHandler['getData']>;
+    ) => any;
     renderPipelineResponse: (context?: ExpressionValue) => Promise<any>;
   }
 }
@@ -61,12 +61,9 @@ class Main extends React.Component<{}, State> {
         data: new DataAdapter(),
       };
       return getExpressions()
-        .execute(expression, {
+        .execute(expression, context || { type: 'null' }, {
           inspectorAdapters: adapters,
-          context,
-          // TODO: naming / typing is confusing and doesn't match here
-          // searchContext is also a way to set initialContext and Context can't be set to SearchContext
-          searchContext: initialContext as any,
+          search: initialContext as any,
         })
         .getData();
     };
diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/types.ts b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/types.ts
index 6e0a93e4a3cb1..123baa1183c48 100644
--- a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/types.ts
+++ b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/types.ts
@@ -17,12 +17,7 @@
  * under the License.
  */
 
-import {
-  ExpressionsStart,
-  ExpressionRenderHandler,
-  ExpressionDataHandler,
-} from 'src/plugins/expressions/public';
-
+import { ExpressionsStart, ExpressionRenderHandler } from 'src/plugins/expressions/public';
 import { Adapters } from 'src/plugins/inspector/public';
 
-export { ExpressionsStart, ExpressionRenderHandler, ExpressionDataHandler, Adapters };
+export { ExpressionsStart, ExpressionRenderHandler, Adapters };
diff --git a/test/interpreter_functional/test_suites/run_pipeline/basic.ts b/test/interpreter_functional/test_suites/run_pipeline/basic.ts
index 77853b0bcd6a4..a2172dd2da1ba 100644
--- a/test/interpreter_functional/test_suites/run_pipeline/basic.ts
+++ b/test/interpreter_functional/test_suites/run_pipeline/basic.ts
@@ -22,7 +22,7 @@ import { ExpectExpression, expectExpressionProvider } from './helpers';
 import { FtrProviderContext } from '../../../functional/ftr_provider_context';
 
 // this file showcases how to use testing utilities defined in helpers.ts together with the kbn_tp_run_pipeline
-// test plugin to write autmated tests for interprete
+// test plugin to write automated tests for interpreter
 export default function({
   getService,
   updateBaselines,
diff --git a/test/interpreter_functional/test_suites/run_pipeline/helpers.ts b/test/interpreter_functional/test_suites/run_pipeline/helpers.ts
index 015c311c30aef..00693845bb266 100644
--- a/test/interpreter_functional/test_suites/run_pipeline/helpers.ts
+++ b/test/interpreter_functional/test_suites/run_pipeline/helpers.ts
@@ -20,10 +20,8 @@
 import expect from '@kbn/expect';
 import { ExpressionValue } from 'src/plugins/expressions';
 import { FtrProviderContext } from '../../../functional/ftr_provider_context';
-import { ExpressionDataHandler } from '../../plugins/kbn_tp_run_pipeline/public/np_ready/types';
 
-type UnWrapPromise<T> = T extends Promise<infer U> ? U : T;
-export type ExpressionResult = UnWrapPromise<ReturnType<ExpressionDataHandler['getData']>>;
+export type ExpressionResult = any;
 
 export type ExpectExpression = (
   name: string,
@@ -112,7 +110,7 @@ export function expectExpressionProvider({
             if (!_currentContext.type) _currentContext.type = 'null';
             return window
               .runPipeline(_expression, _currentContext, _initialContext)
-              .then(expressionResult => {
+              .then((expressionResult: any) => {
                 done(expressionResult);
                 return expressionResult;
               });

From d21d33d0b68bea3c45e49491fd4ee5671a00b3c7 Mon Sep 17 00:00:00 2001
From: Vadim Dalecky <streamich@users.noreply.github.com>
Date: Mon, 17 Feb 2020 15:25:17 +0100
Subject: [PATCH 008/174] =?UTF-8?q?fix:=20=F0=9F=90=9B=20don't=20cast=20to?=
 =?UTF-8?q?=20any=20(#57799)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

EUI 19.0 was merged into Kibana which supports React elements as context
menu items, so we don't need to cast to any here anymore.
---
 .../public/context_menu/build_eui_context_menu_panels.tsx    | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx
index 7b80a8ea830c0..3dce2c1f4c257 100644
--- a/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx
+++ b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx
@@ -101,10 +101,9 @@ function convertPanelActionToContextMenuItem<A>({
 }): EuiContextMenuPanelItemDescriptor {
   const menuPanelItem: EuiContextMenuPanelItemDescriptor = {
     name: action.MenuItem
-      ? // Cast to `any` because `name` typed to string.
-        (React.createElement(uiToReactComponent(action.MenuItem), {
+      ? React.createElement(uiToReactComponent(action.MenuItem), {
           context: actionContext,
-        }) as any)
+        })
       : action.getDisplayName(actionContext),
     icon: action.getIconType(actionContext),
     panel: _.get(action, 'childContextMenuPanel.id'),

From c31c7ee4c7b20eee566fd3d915620a0ce86416d1 Mon Sep 17 00:00:00 2001
From: James Gowdy <jgowdy@elastic.co>
Date: Mon, 17 Feb 2020 15:06:42 +0000
Subject: [PATCH 009/174] [ML] Adding get buckets endpoint wrapper (#57752)

* [ML] Adding results buckets endpoint wrapper

* removing unnecessary spread operator

* correcting timstamp format in api doc

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../ml/server/client/elasticsearch_ml.js      | 25 ++++++
 .../ml/server/routes/anomaly_detectors.ts     | 76 +++++++++++++++----
 .../ml/server/routes/data_frame_analytics.ts  | 20 ++---
 3 files changed, 98 insertions(+), 23 deletions(-)

diff --git a/x-pack/legacy/plugins/ml/server/client/elasticsearch_ml.js b/x-pack/legacy/plugins/ml/server/client/elasticsearch_ml.js
index cf13d329182ba..9317d3c6c3e07 100644
--- a/x-pack/legacy/plugins/ml/server/client/elasticsearch_ml.js
+++ b/x-pack/legacy/plugins/ml/server/client/elasticsearch_ml.js
@@ -450,6 +450,31 @@ export const elasticsearchJsPlugin = (Client, config, components) => {
     method: 'POST',
   });
 
+  ml.buckets = ca({
+    urls: [
+      {
+        fmt: '/_ml/anomaly_detectors/<%=jobId%>/results/buckets',
+        req: {
+          jobId: {
+            type: 'string',
+          },
+        },
+      },
+      {
+        fmt: '/_ml/anomaly_detectors/<%=jobId%>/results/buckets/<%=timestamp%>',
+        req: {
+          jobId: {
+            type: 'string',
+          },
+          timestamp: {
+            type: 'string',
+          },
+        },
+      },
+    ],
+    method: 'POST',
+  });
+
   ml.overallBuckets = ca({
     url: {
       fmt: '/_ml/anomaly_detectors/<%=jobId%>/results/overall_buckets',
diff --git a/x-pack/legacy/plugins/ml/server/routes/anomaly_detectors.ts b/x-pack/legacy/plugins/ml/server/routes/anomaly_detectors.ts
index 64af1f67bce29..927646e4f0acc 100644
--- a/x-pack/legacy/plugins/ml/server/routes/anomaly_detectors.ts
+++ b/x-pack/legacy/plugins/ml/server/routes/anomaly_detectors.ts
@@ -36,7 +36,7 @@ export function jobRoutes({ xpackMainPlugin, router }: RouteInitialization) {
       try {
         const results = await context.ml!.mlClient.callAsCurrentUser('ml.jobs');
         return response.ok({
-          body: { ...results },
+          body: results,
         });
       } catch (e) {
         return response.customError(wrapError(e));
@@ -67,7 +67,7 @@ export function jobRoutes({ xpackMainPlugin, router }: RouteInitialization) {
         const { jobId } = request.params;
         const results = await context.ml!.mlClient.callAsCurrentUser('ml.jobs', { jobId });
         return response.ok({
-          body: { ...results },
+          body: results,
         });
       } catch (e) {
         return response.customError(wrapError(e));
@@ -94,7 +94,7 @@ export function jobRoutes({ xpackMainPlugin, router }: RouteInitialization) {
       try {
         const results = await context.ml!.mlClient.callAsCurrentUser('ml.jobStats');
         return response.ok({
-          body: { ...results },
+          body: results,
         });
       } catch (e) {
         return response.customError(wrapError(e));
@@ -125,7 +125,7 @@ export function jobRoutes({ xpackMainPlugin, router }: RouteInitialization) {
         const { jobId } = request.params;
         const results = await context.ml!.mlClient.callAsCurrentUser('ml.jobStats', { jobId });
         return response.ok({
-          body: { ...results },
+          body: results,
         });
       } catch (e) {
         return response.customError(wrapError(e));
@@ -160,7 +160,7 @@ export function jobRoutes({ xpackMainPlugin, router }: RouteInitialization) {
           body: request.body,
         });
         return response.ok({
-          body: { ...results },
+          body: results,
         });
       } catch (e) {
         return response.customError(wrapError(e));
@@ -195,7 +195,7 @@ export function jobRoutes({ xpackMainPlugin, router }: RouteInitialization) {
           body: request.body,
         });
         return response.ok({
-          body: { ...results },
+          body: results,
         });
       } catch (e) {
         return response.customError(wrapError(e));
@@ -228,7 +228,7 @@ export function jobRoutes({ xpackMainPlugin, router }: RouteInitialization) {
           jobId,
         });
         return response.ok({
-          body: { ...results },
+          body: results,
         });
       } catch (e) {
         return response.customError(wrapError(e));
@@ -265,7 +265,7 @@ export function jobRoutes({ xpackMainPlugin, router }: RouteInitialization) {
         }
         const results = await context.ml!.mlClient.callAsCurrentUser('ml.closeJob', options);
         return response.ok({
-          body: { ...results },
+          body: results,
         });
       } catch (e) {
         return response.customError(wrapError(e));
@@ -302,7 +302,7 @@ export function jobRoutes({ xpackMainPlugin, router }: RouteInitialization) {
         }
         const results = await context.ml!.mlClient.callAsCurrentUser('ml.deleteJob', options);
         return response.ok({
-          body: { ...results },
+          body: results,
         });
       } catch (e) {
         return response.customError(wrapError(e));
@@ -332,7 +332,7 @@ export function jobRoutes({ xpackMainPlugin, router }: RouteInitialization) {
           body: request.body,
         });
         return response.ok({
-          body: { ...results },
+          body: results,
         });
       } catch (e) {
         return response.customError(wrapError(e));
@@ -368,7 +368,57 @@ export function jobRoutes({ xpackMainPlugin, router }: RouteInitialization) {
           duration,
         });
         return response.ok({
-          body: { ...results },
+          body: results,
+        });
+      } catch (e) {
+        return response.customError(wrapError(e));
+      }
+    })
+  );
+
+  /**
+   * @apiGroup AnomalyDetectors
+   *
+   * @api {post} /api/ml/anomaly_detectors/:jobId/results/buckets  Obtain bucket scores for the specified job ID
+   * @apiName GetOverallBuckets
+   * @apiDescription The get buckets API presents a chronological view of the records, grouped by bucket.
+   *
+   * @apiParam {String} jobId Job ID.
+   * @apiParam {String} timestamp.
+   *
+   * @apiSuccess {Number} count
+   * @apiSuccess {Object[]} buckets
+   */
+  router.post(
+    {
+      path: '/api/ml/anomaly_detectors/{jobId}/results/buckets/{timestamp?}',
+      validate: {
+        params: schema.object({
+          jobId: schema.string(),
+          timestamp: schema.maybe(schema.string()),
+        }),
+        body: schema.object({
+          anomaly_score: schema.maybe(schema.number()),
+          desc: schema.maybe(schema.boolean()),
+          end: schema.maybe(schema.string()),
+          exclude_interim: schema.maybe(schema.boolean()),
+          expand: schema.maybe(schema.boolean()),
+          'page.from': schema.maybe(schema.number()),
+          'page.size': schema.maybe(schema.number()),
+          sort: schema.maybe(schema.string()),
+          start: schema.maybe(schema.string()),
+        }),
+      },
+    },
+    licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
+      try {
+        const results = await context.ml!.mlClient.callAsCurrentUser('ml.buckets', {
+          jobId: request.params.jobId,
+          timestamp: request.params.timestamp,
+          ...request.body,
+        });
+        return response.ok({
+          body: results,
         });
       } catch (e) {
         return response.customError(wrapError(e));
@@ -413,7 +463,7 @@ export function jobRoutes({ xpackMainPlugin, router }: RouteInitialization) {
           end: request.body.end,
         });
         return response.ok({
-          body: { ...results },
+          body: results,
         });
       } catch (e) {
         return response.customError(wrapError(e));
@@ -449,7 +499,7 @@ export function jobRoutes({ xpackMainPlugin, router }: RouteInitialization) {
         };
         const results = await context.ml!.mlClient.callAsCurrentUser('ml.categories', options);
         return response.ok({
-          body: { ...results },
+          body: results,
         });
       } catch (e) {
         return response.customError(wrapError(e));
diff --git a/x-pack/legacy/plugins/ml/server/routes/data_frame_analytics.ts b/x-pack/legacy/plugins/ml/server/routes/data_frame_analytics.ts
index f134820adbb48..6541fa541a59f 100644
--- a/x-pack/legacy/plugins/ml/server/routes/data_frame_analytics.ts
+++ b/x-pack/legacy/plugins/ml/server/routes/data_frame_analytics.ts
@@ -40,7 +40,7 @@ export function dataFrameAnalyticsRoutes({ xpackMainPlugin, router }: RouteIniti
       try {
         const results = await context.ml!.mlClient.callAsCurrentUser('ml.getDataFrameAnalytics');
         return response.ok({
-          body: { ...results },
+          body: results,
         });
       } catch (e) {
         return response.customError(wrapError(e));
@@ -71,7 +71,7 @@ export function dataFrameAnalyticsRoutes({ xpackMainPlugin, router }: RouteIniti
           analyticsId,
         });
         return response.ok({
-          body: { ...results },
+          body: results,
         });
       } catch (e) {
         return response.customError(wrapError(e));
@@ -97,7 +97,7 @@ export function dataFrameAnalyticsRoutes({ xpackMainPlugin, router }: RouteIniti
           'ml.getDataFrameAnalyticsStats'
         );
         return response.ok({
-          body: { ...results },
+          body: results,
         });
       } catch (e) {
         return response.customError(wrapError(e));
@@ -131,7 +131,7 @@ export function dataFrameAnalyticsRoutes({ xpackMainPlugin, router }: RouteIniti
           }
         );
         return response.ok({
-          body: { ...results },
+          body: results,
         });
       } catch (e) {
         return response.customError(wrapError(e));
@@ -170,7 +170,7 @@ export function dataFrameAnalyticsRoutes({ xpackMainPlugin, router }: RouteIniti
           }
         );
         return response.ok({
-          body: { ...results },
+          body: results,
         });
       } catch (e) {
         return response.customError(wrapError(e));
@@ -201,7 +201,7 @@ export function dataFrameAnalyticsRoutes({ xpackMainPlugin, router }: RouteIniti
           }
         );
         return response.ok({
-          body: { ...results },
+          body: results,
         });
       } catch (e) {
         return response.customError(wrapError(e));
@@ -241,7 +241,7 @@ export function dataFrameAnalyticsRoutes({ xpackMainPlugin, router }: RouteIniti
           }
         );
         return response.ok({
-          body: { ...results },
+          body: results,
         });
       } catch (e) {
         return response.customError(wrapError(e));
@@ -277,7 +277,7 @@ export function dataFrameAnalyticsRoutes({ xpackMainPlugin, router }: RouteIniti
           }
         );
         return response.ok({
-          body: { ...results },
+          body: results,
         });
       } catch (e) {
         return response.customError(wrapError(e));
@@ -310,7 +310,7 @@ export function dataFrameAnalyticsRoutes({ xpackMainPlugin, router }: RouteIniti
           analyticsId,
         });
         return response.ok({
-          body: { ...results },
+          body: results,
         });
       } catch (e) {
         return response.customError(wrapError(e));
@@ -353,7 +353,7 @@ export function dataFrameAnalyticsRoutes({ xpackMainPlugin, router }: RouteIniti
           options
         );
         return response.ok({
-          body: { ...results },
+          body: results,
         });
       } catch (e) {
         return response.customError(wrapError(e));

From 3c51fb0314799365bfe641adc7423d34e81f0d3c Mon Sep 17 00:00:00 2001
From: James Gowdy <jgowdy@elastic.co>
Date: Tue, 18 Feb 2020 07:16:40 +0000
Subject: [PATCH 010/174] [ML] File data viz fix index pattern warning after
 index change (#57807)

---
 .../file_based/components/import_view/import_view.js   | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js
index bb95d3e420d2a..beb5918e277ae 100644
--- a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js
+++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js
@@ -325,9 +325,17 @@ export class ImportView extends Component {
 
   onIndexChange = e => {
     const name = e.target.value;
+    const { indexNames, indexPattern, indexPatternNames } = this.state;
+
     this.setState({
       index: name,
-      indexNameError: isIndexNameValid(name, this.state.indexNames),
+      indexNameError: isIndexNameValid(name, indexNames),
+      // if index pattern has been altered, check that it still matches the inputted index
+      ...(indexPattern === ''
+        ? {}
+        : {
+            indexPatternNameError: isIndexPatternNameValid(indexPattern, indexPatternNames, name),
+          }),
     });
   };
 

From 446fda62f20af07d15f9a39cceebeac9f406ffcd Mon Sep 17 00:00:00 2001
From: Victor Martinez <victormartinezrubio@gmail.com>
Date: Tue, 18 Feb 2020 08:25:14 +0000
Subject: [PATCH 011/174] [jenkins] Notify GH Checks for the apm-ui e2e
 pipeline (#52900)

---
 .ci/end2end.groovy                            | 27 +++++++++++++++++--
 .../plugins/apm/cypress/ci/kibana.dev.yml     |  3 +++
 2 files changed, 28 insertions(+), 2 deletions(-)

diff --git a/.ci/end2end.groovy b/.ci/end2end.groovy
index 5cf6efe324ac3..38fed4aca19dc 100644
--- a/.ci/end2end.groovy
+++ b/.ci/end2end.groovy
@@ -25,7 +25,7 @@ pipeline {
     durabilityHint('PERFORMANCE_OPTIMIZED')
   }
   triggers {
-    issueCommentTrigger('(?i).*jenkins\\W+run\\W+(?:the\\W+)?e2e(?:\\W+please)?.*')
+    issueCommentTrigger('(?i)(retest|.*jenkins\\W+run\\W+(?:the\\W+)?e2e?.*)')
   }
   parameters {
     booleanParam(name: 'FORCE', defaultValue: false, description: 'Whether to force the run.')
@@ -60,8 +60,14 @@ pipeline {
         }
       }
       steps {
+        notifyStatus('Starting services', 'PENDING')
         dir("${APM_ITS}"){
-          sh './scripts/compose.py start master --no-kibana --no-xpack-secure'
+          sh './scripts/compose.py start master --no-kibana'
+        }
+      }
+      post {
+        unsuccessful {
+          notifyStatus('Environmental issue', 'FAILURE')
         }
       }
     }
@@ -77,10 +83,16 @@ pipeline {
         JENKINS_NODE_COOKIE = 'dontKillMe'
       }
       steps {
+        notifyStatus('Preparing kibana', 'PENDING')
         dir("${BASE_DIR}"){
           sh script: "${CYPRESS_DIR}/ci/prepare-kibana.sh"
         }
       }
+      post {
+        unsuccessful {
+          notifyStatus('Kibana warm up failed', 'FAILURE')
+        }
+      }
     }
     stage('Smoke Tests'){
       options { skipDefaultCheckout() }
@@ -91,6 +103,7 @@ pipeline {
         }
       }
       steps{
+        notifyStatus('Running smoke tests', 'PENDING')
         dir("${BASE_DIR}"){
           sh '''
             jobs -l
@@ -112,6 +125,12 @@ pipeline {
             archiveArtifacts(allowEmptyArchive: false, artifacts: 'apm-its.log')
           }
         }
+        unsuccessful {
+          notifyStatus('Test failures', 'FAILURE')
+        }
+        success {
+          notifyStatus('Tests passed', 'SUCCESS')
+        }
       }
     }
   }
@@ -123,3 +142,7 @@ pipeline {
     }
   }
 }
+
+def notifyStatus(String description, String status) {
+  withGithubNotify.notify('end2end-for-apm-ui', description, status, getBlueoceanDisplayURL())
+}
diff --git a/x-pack/legacy/plugins/apm/cypress/ci/kibana.dev.yml b/x-pack/legacy/plugins/apm/cypress/ci/kibana.dev.yml
index 3082391f23a15..db57db9a1abe9 100644
--- a/x-pack/legacy/plugins/apm/cypress/ci/kibana.dev.yml
+++ b/x-pack/legacy/plugins/apm/cypress/ci/kibana.dev.yml
@@ -2,3 +2,6 @@
 # Disabled plugins
 ########################
 logging.verbose: true
+elasticsearch.username: "kibana_system_user"
+elasticsearch.password: "changeme"
+xpack.security.encryptionKey: "something_at_least_32_characters"

From 66e685b4a80e671e0ce840bc7e98b1c944ecdd61 Mon Sep 17 00:00:00 2001
From: MadameSheema <snootchie.boochies@gmail.com>
Date: Tue, 18 Feb 2020 10:03:43 +0100
Subject: [PATCH 012/174] refactors 'flyout-button' tests (#57572)

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../fields_browser/fields_browser.spec.ts     |  8 ++---
 .../smoke_tests/inspect/inspect.spec.ts       |  2 +-
 .../timeline/data_providers.spec.ts           |  3 +-
 .../timeline/flyout_button.spec.ts            | 30 +++++++------------
 .../plugins/siem/cypress/screens/siem_main.ts |  9 ++++++
 .../siem/cypress/screens/timeline/main.ts     |  5 ++++
 .../plugins/siem/cypress/tasks/siem_main.ts   | 20 +++++++++++++
 .../siem/cypress/tasks/timeline/main.ts       |  5 ----
 8 files changed, 50 insertions(+), 32 deletions(-)
 create mode 100644 x-pack/legacy/plugins/siem/cypress/screens/siem_main.ts
 create mode 100644 x-pack/legacy/plugins/siem/cypress/tasks/siem_main.ts

diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/fields_browser/fields_browser.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/fields_browser/fields_browser.spec.ts
index 2889d78891a06..6e8ef93a54016 100644
--- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/fields_browser/fields_browser.spec.ts
+++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/fields_browser/fields_browser.spec.ts
@@ -22,11 +22,9 @@ import {
   FIELDS_BROWSER_HEADER_HOST_GEO_CONTINENT_NAME_HEADER,
 } from '../../../screens/timeline/fields_browser';
 
-import {
-  openTimeline,
-  populateTimeline,
-  openTimelineFieldsBrowser,
-} from '../../../tasks/timeline/main';
+import { populateTimeline, openTimelineFieldsBrowser } from '../../../tasks/timeline/main';
+
+import { openTimeline } from '../../../tasks/siem_main';
 
 import {
   clearFieldsBrowser,
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/inspect/inspect.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/inspect/inspect.spec.ts
index e7411aba11af5..1555470f5eee7 100644
--- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/inspect/inspect.spec.ts
+++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/inspect/inspect.spec.ts
@@ -12,10 +12,10 @@ import {
 } from '../../../screens/inspect';
 import {
   executeTimelineKQL,
-  openTimeline,
   openTimelineSettings,
   openTimelineInspectButton,
 } from '../../../tasks/timeline/main';
+import { openTimeline } from '../../../tasks/siem_main';
 import { DEFAULT_TIMEOUT, loginAndWaitForPage } from '../../../tasks/login';
 import { closesModal, openStatsAndTables } from '../../../tasks/inspect';
 
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/data_providers.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/data_providers.spec.ts
index 3d251c1c6bcac..c3fedfb06939b 100644
--- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/data_providers.spec.ts
+++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/data_providers.spec.ts
@@ -13,7 +13,8 @@ import {
 } from '../../../tasks/hosts/all_hosts';
 import { HOSTS_NAMES } from '../../../screens/hosts/all_hosts';
 import { DEFAULT_TIMEOUT, loginAndWaitForPage } from '../../../tasks/login';
-import { openTimeline, createNewTimeline } from '../../../tasks/timeline/main';
+import { createNewTimeline } from '../../../tasks/timeline/main';
+import { openTimeline } from '../../../tasks/siem_main';
 import {
   TIMELINE_DATA_PROVIDERS_EMPTY,
   TIMELINE_DATA_PROVIDERS,
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/flyout_button.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/flyout_button.spec.ts
index 63fe56371a4cd..b7faaaac1c06c 100644
--- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/flyout_button.spec.ts
+++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/flyout_button.spec.ts
@@ -4,44 +4,34 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+import { HOSTS_PAGE } from '../../../urls/navigation';
+import { waitForAllHostsToBeLoaded, dragFirstHostToTimeline } from '../../../tasks/hosts/all_hosts';
+import { loginAndWaitForPage } from '../../../tasks/login';
+import { openTimelineIfClosed, openTimeline } from '../../../tasks/siem_main';
 import {
   TIMELINE_FLYOUT_BODY,
   TIMELINE_NOT_READY_TO_DROP_BUTTON,
-} from '../../lib/timeline/selectors';
-import { ALL_HOSTS_WIDGET_DRAGGABLE_HOSTS } from '../../lib/hosts/selectors';
-import { HOSTS_PAGE } from '../../lib/urls';
-import { waitForAllHostsWidget } from '../../lib/hosts/helpers';
-import { loginAndWaitForPage } from '../../lib/util/helpers';
-import { drag } from '../../lib/drag_n_drop/helpers';
-import { createNewTimeline, toggleTimelineVisibility } from '../../lib/timeline/helpers';
+} from '../../../screens/timeline/main';
+import { createNewTimeline } from '../../../tasks/timeline/main';
 
 describe('timeline flyout button', () => {
   before(() => {
     loginAndWaitForPage(HOSTS_PAGE);
+    waitForAllHostsToBeLoaded();
   });
 
   afterEach(() => {
-    cy.get('[data-test-subj="kibanaChrome"]').then($page => {
-      if ($page.find('[data-test-subj="flyoutOverlay"]').length === 1) {
-        toggleTimelineVisibility();
-      }
-    });
-
+    openTimelineIfClosed();
     createNewTimeline();
   });
 
   it('toggles open the timeline', () => {
-    toggleTimelineVisibility();
-
+    openTimeline();
     cy.get(TIMELINE_FLYOUT_BODY).should('have.css', 'visibility', 'visible');
   });
 
   it('sets the flyout button background to euiColorSuccess with a 10% alpha channel when the user starts dragging a host, but is not hovering over the flyout button', () => {
-    waitForAllHostsWidget();
-
-    cy.get(ALL_HOSTS_WIDGET_DRAGGABLE_HOSTS)
-      .first()
-      .then(host => drag(host));
+    dragFirstHostToTimeline();
 
     cy.get(TIMELINE_NOT_READY_TO_DROP_BUTTON).should(
       'have.css',
diff --git a/x-pack/legacy/plugins/siem/cypress/screens/siem_main.ts b/x-pack/legacy/plugins/siem/cypress/screens/siem_main.ts
new file mode 100644
index 0000000000000..d4eeeb036ee95
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/cypress/screens/siem_main.ts
@@ -0,0 +1,9 @@
+/*
+ * 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 MAIN_PAGE = '[data-test-subj="kibanaChrome"]';
+
+export const TIMELINE_TOGGLE_BUTTON = '[data-test-subj="flyoutOverlay"]';
diff --git a/x-pack/legacy/plugins/siem/cypress/screens/timeline/main.ts b/x-pack/legacy/plugins/siem/cypress/screens/timeline/main.ts
index 60c9c2ab44372..4c722ffa5f215 100644
--- a/x-pack/legacy/plugins/siem/cypress/screens/timeline/main.ts
+++ b/x-pack/legacy/plugins/siem/cypress/screens/timeline/main.ts
@@ -32,3 +32,8 @@ export const TIMELINE_DATA_PROVIDERS_EMPTY =
 
 export const TIMELINE_DROPPED_DATA_PROVIDERS =
   '[data-test-subj="dataProviders"] [data-test-subj="providerContainer"]';
+
+export const TIMELINE_FLYOUT_BODY = '[data-test-subj="eui-flyout-body"]';
+
+export const TIMELINE_NOT_READY_TO_DROP_BUTTON =
+  '[data-test-subj="flyout-button-not-ready-to-drop"]';
diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/siem_main.ts b/x-pack/legacy/plugins/siem/cypress/tasks/siem_main.ts
new file mode 100644
index 0000000000000..8501bb3d94e26
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/cypress/tasks/siem_main.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 { MAIN_PAGE, TIMELINE_TOGGLE_BUTTON } from '../screens/siem_main';
+import { DEFAULT_TIMEOUT } from '../tasks/login';
+
+export const openTimelineIfClosed = () => {
+  cy.get(MAIN_PAGE).then($page => {
+    if ($page.find(TIMELINE_TOGGLE_BUTTON).length === 1) {
+      openTimeline();
+    }
+  });
+};
+
+export const openTimeline = () => {
+  cy.get(TIMELINE_TOGGLE_BUTTON, { timeout: DEFAULT_TIMEOUT }).click();
+};
diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/timeline/main.ts b/x-pack/legacy/plugins/siem/cypress/tasks/timeline/main.ts
index 068b6dd9f8bd4..f347c072a3584 100644
--- a/x-pack/legacy/plugins/siem/cypress/tasks/timeline/main.ts
+++ b/x-pack/legacy/plugins/siem/cypress/tasks/timeline/main.ts
@@ -7,7 +7,6 @@
 import { DEFAULT_TIMEOUT } from '../../integration/lib/util/helpers';
 
 import {
-  TIMELINE_TOGGLE_BUTTON,
   SEARCH_OR_FILTER_CONTAINER,
   TIMELINE_FIELDS_BUTTON,
   SERVER_SIDE_EVENT_COUNT,
@@ -19,10 +18,6 @@ import {
 
 export const hostExistsQuery = 'host.name: *';
 
-export const openTimeline = () => {
-  cy.get(TIMELINE_TOGGLE_BUTTON, { timeout: DEFAULT_TIMEOUT }).click();
-};
-
 export const populateTimeline = () => {
   cy.get(`${SEARCH_OR_FILTER_CONTAINER} input`).type(`${hostExistsQuery} {enter}`);
   cy.get(SERVER_SIDE_EVENT_COUNT, { timeout: DEFAULT_TIMEOUT })

From 5b7734cd4d97b03b487b257c3c68ad5169b76b2f Mon Sep 17 00:00:00 2001
From: Maryia Lapata <mary.lopato@gmail.com>
Date: Tue, 18 Feb 2020 12:54:30 +0300
Subject: [PATCH 013/174] Apply sub url tracking utils to visualize and
 discover (#57307)

* Apply sub url tracking utils to visualize and discover

* Update query karma mock

* Remove unnecessary chrome legacy calls

* Fix typo

* Add setActiveUrl

* Add unit test for setActiveUrl

* Refactoring

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 src/legacy/core_plugins/kibana/index.js       |  2 +
 .../kibana/public/discover/plugin.ts          | 54 +++++++++++++-
 .../public/visualize/kibana_services.ts       |  3 +-
 .../kibana/public/visualize/legacy.ts         |  9 +--
 .../kibana/public/visualize/legacy_imports.ts |  7 --
 .../public/visualize/np_ready/application.ts  |  3 +-
 .../visualize/np_ready/editor/editor.js       |  7 +-
 .../np_ready/listing/visualize_listing.js     | 17 ++---
 .../kibana/public/visualize/plugin.ts         | 70 +++++++++++++++----
 .../new_platform/new_platform.karma_mock.js   | 50 +++++++++++--
 .../url/kbn_url_tracker.test.ts               |  6 ++
 .../state_management/url/kbn_url_tracker.ts   | 26 ++++---
 12 files changed, 189 insertions(+), 65 deletions(-)

diff --git a/src/legacy/core_plugins/kibana/index.js b/src/legacy/core_plugins/kibana/index.js
index ea81193c1dd0a..8e6bae0b588bc 100644
--- a/src/legacy/core_plugins/kibana/index.js
+++ b/src/legacy/core_plugins/kibana/index.js
@@ -77,6 +77,7 @@ export default function(kibana) {
           order: -1003,
           url: `${kbnBaseUrl}#/discover`,
           euiIconType: 'discoverApp',
+          disableSubUrlTracking: true,
           category: DEFAULT_APP_CATEGORIES.analyze,
         },
         {
@@ -87,6 +88,7 @@ export default function(kibana) {
           order: -1002,
           url: `${kbnBaseUrl}#/visualize`,
           euiIconType: 'visualizeApp',
+          disableSubUrlTracking: true,
           category: DEFAULT_APP_CATEGORIES.analyze,
         },
         {
diff --git a/src/legacy/core_plugins/kibana/public/discover/plugin.ts b/src/legacy/core_plugins/kibana/public/discover/plugin.ts
index 565382313e369..e8ded9d99f892 100644
--- a/src/legacy/core_plugins/kibana/public/discover/plugin.ts
+++ b/src/legacy/core_plugins/kibana/public/discover/plugin.ts
@@ -16,11 +16,17 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
+import { BehaviorSubject } from 'rxjs';
 import { i18n } from '@kbn/i18n';
 import { AppMountParameters, CoreSetup, CoreStart, Plugin } from 'kibana/public';
 import angular, { auto } from 'angular';
 import { UiActionsSetup, UiActionsStart } from 'src/plugins/ui_actions/public';
-import { DataPublicPluginStart } from 'src/plugins/data/public';
+import {
+  DataPublicPluginStart,
+  DataPublicPluginSetup,
+  getQueryStateContainer,
+} from '../../../../../plugins/data/public';
 import { registerFeature } from './np_ready/register_feature';
 import './kibana_services';
 import { IEmbeddableStart, IEmbeddableSetup } from '../../../../../plugins/embeddable/public';
@@ -30,7 +36,10 @@ import { NavigationPublicPluginStart as NavigationStart } from '../../../../../p
 import { ChartsPluginStart } from '../../../../../plugins/charts/public';
 import { buildServices } from './build_services';
 import { SharePluginStart } from '../../../../../plugins/share/public';
-import { KibanaLegacySetup } from '../../../../../plugins/kibana_legacy/public';
+import {
+  KibanaLegacySetup,
+  AngularRenderedAppUpdater,
+} from '../../../../../plugins/kibana_legacy/public';
 import { DocViewsRegistry } from './np_ready/doc_views/doc_views_registry';
 import { DocViewInput, DocViewInputFn } from './np_ready/doc_views/doc_views_types';
 import { DocViewTable } from './np_ready/components/table/table';
@@ -40,6 +49,7 @@ import {
   VisualizationsStart,
   VisualizationsSetup,
 } from '../../../visualizations/public/np_ready/public';
+import { createKbnUrlTracker } from '../../../../../plugins/kibana_utils/public';
 
 /**
  * These are the interfaces with your public contracts. You should export these
@@ -56,6 +66,7 @@ export interface DiscoverSetupPlugins {
   kibanaLegacy: KibanaLegacySetup;
   home: HomePublicPluginSetup;
   visualizations: VisualizationsSetup;
+  data: DataPublicPluginSetup;
 }
 export interface DiscoverStartPlugins {
   uiActions: UiActionsStart;
@@ -81,6 +92,9 @@ export class DiscoverPlugin implements Plugin<DiscoverSetup, DiscoverStart> {
   private docViewsRegistry: DocViewsRegistry | null = null;
   private embeddableInjector: auto.IInjectorService | null = null;
   private getEmbeddableInjector: (() => Promise<auto.IInjectorService>) | null = null;
+  private appStateUpdater = new BehaviorSubject<AngularRenderedAppUpdater>(() => ({}));
+  private stopUrlTracking: (() => void) | undefined = undefined;
+
   /**
    * why are those functions public? they are needed for some mocha tests
    * can be removed once all is Jest
@@ -89,6 +103,27 @@ export class DiscoverPlugin implements Plugin<DiscoverSetup, DiscoverStart> {
   public initializeServices?: () => Promise<{ core: CoreStart; plugins: DiscoverStartPlugins }>;
 
   setup(core: CoreSetup, plugins: DiscoverSetupPlugins): DiscoverSetup {
+    const { querySyncStateContainer, stop: stopQuerySyncStateContainer } = getQueryStateContainer(
+      plugins.data.query
+    );
+    const { appMounted, appUnMounted, stop: stopUrlTracker } = createKbnUrlTracker({
+      baseUrl: core.http.basePath.prepend('/app/kibana'),
+      defaultSubUrl: '#/discover',
+      storageKey: 'lastUrl:discover',
+      navLinkUpdater$: this.appStateUpdater,
+      toastNotifications: core.notifications.toasts,
+      stateParams: [
+        {
+          kbnUrlKey: '_g',
+          stateUpdate$: querySyncStateContainer.state$,
+        },
+      ],
+    });
+    this.stopUrlTracking = () => {
+      stopQuerySyncStateContainer();
+      stopUrlTracker();
+    };
+
     this.getEmbeddableInjector = this.getInjector.bind(this);
     this.docViewsRegistry = new DocViewsRegistry(this.getEmbeddableInjector);
     this.docViewsRegistry.addDocView({
@@ -108,6 +143,8 @@ export class DiscoverPlugin implements Plugin<DiscoverSetup, DiscoverStart> {
     plugins.kibanaLegacy.registerLegacyApp({
       id: 'discover',
       title: 'Discover',
+      updater$: this.appStateUpdater.asObservable(),
+      navLinkId: 'kibana:discover',
       order: -1004,
       euiIconType: 'discoverApp',
       mount: async (params: AppMountParameters) => {
@@ -117,11 +154,16 @@ export class DiscoverPlugin implements Plugin<DiscoverSetup, DiscoverStart> {
         if (!this.initializeInnerAngular) {
           throw Error('Discover plugin method initializeInnerAngular is undefined');
         }
+        appMounted();
         await this.initializeServices();
         await this.initializeInnerAngular();
 
         const { renderApp } = await import('./np_ready/application');
-        return renderApp(innerAngularName, params.element);
+        const unmount = await renderApp(innerAngularName, params.element);
+        return () => {
+          unmount();
+          appUnMounted();
+        };
       },
     });
     registerFeature(plugins.home);
@@ -160,6 +202,12 @@ export class DiscoverPlugin implements Plugin<DiscoverSetup, DiscoverStart> {
     this.registerEmbeddable(core, plugins);
   }
 
+  stop() {
+    if (this.stopUrlTracking) {
+      this.stopUrlTracking();
+    }
+  }
+
   /**
    * register embeddable with a slimmer embeddable version of inner angular
    */
diff --git a/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts b/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts
index 6082fb8428ac3..096877d5824c4 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts
+++ b/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts
@@ -35,7 +35,6 @@ import { DataPublicPluginStart, IndexPatternsContract } from '../../../../../plu
 import { VisualizationsStart } from '../../../visualizations/public';
 import { SavedVisualizations } from './np_ready/types';
 import { UsageCollectionSetup } from '../../../../../plugins/usage_collection/public';
-import { Chrome } from './legacy_imports';
 import { KibanaLegacyStart } from '../../../../../plugins/kibana_legacy/public';
 
 export interface VisualizeKibanaServices {
@@ -47,7 +46,6 @@ export interface VisualizeKibanaServices {
   embeddable: IEmbeddableStart;
   getBasePath: () => string;
   indexPatterns: IndexPatternsContract;
-  legacyChrome: Chrome;
   localStorage: Storage;
   navigation: NavigationStart;
   toastNotifications: ToastsStart;
@@ -61,6 +59,7 @@ export interface VisualizeKibanaServices {
   visualizations: VisualizationsStart;
   usageCollection?: UsageCollectionSetup;
   I18nContext: I18nStart['Context'];
+  setActiveUrl: (newUrl: string) => void;
 }
 
 let services: VisualizeKibanaServices | null = null;
diff --git a/src/legacy/core_plugins/kibana/public/visualize/legacy.ts b/src/legacy/core_plugins/kibana/public/visualize/legacy.ts
index bc2d700f6c6a1..fbbc7ab944daf 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/legacy.ts
+++ b/src/legacy/core_plugins/kibana/public/visualize/legacy.ts
@@ -18,19 +18,14 @@
  */
 
 import { PluginInitializerContext } from 'kibana/public';
-import { legacyChrome, npSetup, npStart } from './legacy_imports';
+import { npSetup, npStart } from 'ui/new_platform';
 import { start as visualizations } from '../../../visualizations/public/np_ready/public/legacy';
 import { plugin } from './index';
 
 const instance = plugin({
   env: npSetup.plugins.kibanaLegacy.env,
 } as PluginInitializerContext);
-instance.setup(npSetup.core, {
-  ...npSetup.plugins,
-  __LEGACY: {
-    legacyChrome,
-  },
-});
+instance.setup(npSetup.core, npSetup.plugins);
 instance.start(npStart.core, {
   ...npStart.plugins,
   visualizations,
diff --git a/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts b/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts
index ac9fc227406ff..92433799ba420 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts
+++ b/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts
@@ -24,11 +24,6 @@
  * directly where they are needed.
  */
 
-import chrome from 'ui/chrome';
-
-export const legacyChrome = chrome;
-export { Chrome } from 'ui/chrome';
-
 // @ts-ignore
 export { AppState, AppStateProvider } from 'ui/state_management/app_state';
 export { State } from 'ui/state_management/state';
@@ -39,8 +34,6 @@ export { StateManagementConfigProvider } from 'ui/state_management/config_provid
 export { stateMonitorFactory } from 'ui/state_management/state_monitor_factory';
 export { PersistedState } from 'ui/persisted_state';
 
-export { npSetup, npStart } from 'ui/new_platform';
-
 export { subscribeWithScope } from 'ui/utils/subscribe_with_scope';
 // @ts-ignore
 export { EventsProvider } from 'ui/events';
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts b/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts
index 3d5fd6605f56b..bd7b478f827a6 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts
+++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts
@@ -45,7 +45,7 @@ import { VisualizeKibanaServices } from '../kibana_services';
 
 let angularModuleInstance: IModule | null = null;
 
-export const renderApp = async (
+export const renderApp = (
   element: HTMLElement,
   appBasePath: string,
   deps: VisualizeKibanaServices
@@ -58,7 +58,6 @@ export const renderApp = async (
       { core: deps.core, env: deps.pluginInitializerContext.env },
       true
     );
-    // custom routing stuff
     initVisualizeApp(angularModuleInstance, deps);
   }
   const $injector = mountVisualizeApp(appBasePath, element);
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js
index 657104344662f..409d4b41fbe69 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js
+++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js
@@ -90,13 +90,13 @@ function VisualizeAppController(
       },
     },
     toastNotifications,
-    legacyChrome,
     chrome,
     getBasePath,
     core: { docLinks },
     savedQueryService,
     uiSettings,
     I18nContext,
+    setActiveUrl,
   } = getServices();
 
   const filterStateManager = new FilterStateManager(globalState, getAppState, filterManager);
@@ -580,10 +580,7 @@ function VisualizeAppController(
               });
               // Manually insert a new url so the back button will open the saved visualization.
               $window.history.pushState({}, '', savedVisualizationParsedUrl.getRootRelativePath());
-              // Since we aren't reloading the page, only inserting a new browser history item, we need to manually update
-              // the last url for this app, so directly clicking on the Visualize tab will also bring the user to the saved
-              // url, not the unsaved one.
-              legacyChrome.trackSubUrlForApp('kibana:visualize', savedVisualizationParsedUrl);
+              setActiveUrl(savedVisualizationParsedUrl.appPath);
 
               const lastDashboardAbsoluteUrl = chrome.navLinks.get('kibana:dashboard').url;
               const dashboardParsedUrl = absoluteToParsedUrl(
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/visualize_listing.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/visualize_listing.js
index cae1e40cd445a..c0cc499b598f0 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/visualize_listing.js
+++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/visualize_listing.js
@@ -36,7 +36,6 @@ export function VisualizeListingController($injector, $scope, createNewVis) {
   const {
     addBasePath,
     chrome,
-    legacyChrome,
     savedObjectsClient,
     savedVisualizations,
     data: {
@@ -100,17 +99,13 @@ export function VisualizeListingController($injector, $scope, createNewVis) {
       selectedItems.map(item => {
         return savedObjectsClient.delete(item.savedObjectType, item.id);
       })
-    )
-      .then(() => {
-        legacyChrome.untrackNavLinksForDeletedSavedObjects(selectedItems.map(item => item.id));
-      })
-      .catch(error => {
-        toastNotifications.addError(error, {
-          title: i18n.translate('kbn.visualize.visualizeListingDeleteErrorTitle', {
-            defaultMessage: 'Error deleting visualization',
-          }),
-        });
+    ).catch(error => {
+      toastNotifications.addError(error, {
+        title: i18n.translate('kbn.visualize.visualizeListingDeleteErrorTitle', {
+          defaultMessage: 'Error deleting visualization',
+        }),
       });
+    });
   };
 
   chrome.setBreadcrumbs([
diff --git a/src/legacy/core_plugins/kibana/public/visualize/plugin.ts b/src/legacy/core_plugins/kibana/public/visualize/plugin.ts
index 16715677d1e20..22804685db3cc 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/plugin.ts
+++ b/src/legacy/core_plugins/kibana/public/visualize/plugin.ts
@@ -17,6 +17,7 @@
  * under the License.
  */
 
+import { BehaviorSubject } from 'rxjs';
 import { i18n } from '@kbn/i18n';
 
 import {
@@ -28,12 +29,19 @@ import {
   SavedObjectsClientContract,
 } from 'kibana/public';
 
-import { Storage } from '../../../../../plugins/kibana_utils/public';
-import { DataPublicPluginStart } from '../../../../../plugins/data/public';
+import { Storage, createKbnUrlTracker } from '../../../../../plugins/kibana_utils/public';
+import {
+  DataPublicPluginStart,
+  DataPublicPluginSetup,
+  getQueryStateContainer,
+} from '../../../../../plugins/data/public';
 import { IEmbeddableStart } from '../../../../../plugins/embeddable/public';
 import { NavigationPublicPluginStart as NavigationStart } from '../../../../../plugins/navigation/public';
 import { SharePluginStart } from '../../../../../plugins/share/public';
-import { KibanaLegacySetup } from '../../../../../plugins/kibana_legacy/public';
+import {
+  KibanaLegacySetup,
+  AngularRenderedAppUpdater,
+} from '../../../../../plugins/kibana_legacy/public';
 import { VisualizationsStart } from '../../../visualizations/public';
 import { VisualizeConstants } from './np_ready/visualize_constants';
 import { setServices, VisualizeKibanaServices } from './kibana_services';
@@ -42,7 +50,6 @@ import {
   HomePublicPluginSetup,
 } from '../../../../../plugins/home/public';
 import { UsageCollectionSetup } from '../../../../../plugins/usage_collection/public';
-import { Chrome } from './legacy_imports';
 
 export interface VisualizePluginStartDependencies {
   data: DataPublicPluginStart;
@@ -53,12 +60,10 @@ export interface VisualizePluginStartDependencies {
 }
 
 export interface VisualizePluginSetupDependencies {
-  __LEGACY: {
-    legacyChrome: Chrome;
-  };
   home: HomePublicPluginSetup;
   kibanaLegacy: KibanaLegacySetup;
   usageCollection?: UsageCollectionSetup;
+  data: DataPublicPluginSetup;
 }
 
 export class VisualizePlugin implements Plugin {
@@ -70,46 +75,72 @@ export class VisualizePlugin implements Plugin {
     share: SharePluginStart;
     visualizations: VisualizationsStart;
   } | null = null;
+  private appStateUpdater = new BehaviorSubject<AngularRenderedAppUpdater>(() => ({}));
+  private stopUrlTracking: (() => void) | undefined = undefined;
 
   constructor(private initializerContext: PluginInitializerContext) {}
 
   public async setup(
     core: CoreSetup,
-    { home, kibanaLegacy, __LEGACY, usageCollection }: VisualizePluginSetupDependencies
+    { home, kibanaLegacy, usageCollection, data }: VisualizePluginSetupDependencies
   ) {
+    const { querySyncStateContainer, stop: stopQuerySyncStateContainer } = getQueryStateContainer(
+      data.query
+    );
+    const { appMounted, appUnMounted, stop: stopUrlTracker, setActiveUrl } = createKbnUrlTracker({
+      baseUrl: core.http.basePath.prepend('/app/kibana'),
+      defaultSubUrl: '#/visualize',
+      storageKey: 'lastUrl:visualize',
+      navLinkUpdater$: this.appStateUpdater,
+      toastNotifications: core.notifications.toasts,
+      stateParams: [
+        {
+          kbnUrlKey: '_g',
+          stateUpdate$: querySyncStateContainer.state$,
+        },
+      ],
+    });
+    this.stopUrlTracking = () => {
+      stopQuerySyncStateContainer();
+      stopUrlTracker();
+    };
+
     kibanaLegacy.registerLegacyApp({
       id: 'visualize',
       title: 'Visualize',
+      updater$: this.appStateUpdater.asObservable(),
+      navLinkId: 'kibana:visualize',
       mount: async (params: AppMountParameters) => {
         const [coreStart] = await core.getStartServices();
+
         if (this.startDependencies === null) {
           throw new Error('not started yet');
         }
 
+        appMounted();
         const {
           savedObjectsClient,
           embeddable,
           navigation,
           visualizations,
-          data,
+          data: dataStart,
           share,
         } = this.startDependencies;
 
         const deps: VisualizeKibanaServices = {
-          ...__LEGACY,
           pluginInitializerContext: this.initializerContext,
           addBasePath: coreStart.http.basePath.prepend,
           core: coreStart,
           chrome: coreStart.chrome,
-          data,
+          data: dataStart,
           embeddable,
           getBasePath: core.http.basePath.get,
-          indexPatterns: data.indexPatterns,
+          indexPatterns: dataStart.indexPatterns,
           localStorage: new Storage(localStorage),
           navigation,
           savedObjectsClient,
           savedVisualizations: visualizations.getSavedVisualizationsLoader(),
-          savedQueryService: data.query.savedQueries,
+          savedQueryService: dataStart.query.savedQueries,
           share,
           toastNotifications: coreStart.notifications.toasts,
           uiSettings: coreStart.uiSettings,
@@ -118,11 +149,16 @@ export class VisualizePlugin implements Plugin {
           visualizations,
           usageCollection,
           I18nContext: coreStart.i18n.Context,
+          setActiveUrl,
         };
         setServices(deps);
 
         const { renderApp } = await import('./np_ready/application');
-        return renderApp(params.element, params.appBasePath, deps);
+        const unmount = renderApp(params.element, params.appBasePath, deps);
+        return () => {
+          unmount();
+          appUnMounted();
+        };
       },
     });
 
@@ -153,4 +189,10 @@ export class VisualizePlugin implements Plugin {
       visualizations,
     };
   }
+
+  stop() {
+    if (this.stopUrlTracking) {
+      this.stopUrlTracking();
+    }
+  }
 }
diff --git a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js
index 4e52f6f6bafec..38b3434ef9c48 100644
--- a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js
+++ b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js
@@ -61,6 +61,10 @@ const mockCore = {
   },
 };
 
+let refreshInterval = undefined;
+let isTimeRangeSelectorEnabled = true;
+let isAutoRefreshSelectorEnabled = true;
+
 export const npSetup = {
   core: mockCore,
   plugins: {
@@ -101,7 +105,14 @@ export const npSetup = {
       },
       query: {
         filterManager: {
+          getFetches$: sinon.fake(),
+          getFilters: sinon.fake(),
+          getAppFilters: sinon.fake(),
           getGlobalFilters: sinon.fake(),
+          removeFilter: sinon.fake(),
+          addFilters: sinon.fake(),
+          setFilters: sinon.fake(),
+          removeAll: sinon.fake(),
           getUpdates$: mockObservable,
         },
         timefilter: {
@@ -110,6 +121,41 @@ export const npSetup = {
             getRefreshInterval: sinon.fake(),
             getTimeUpdate$: mockObservable,
             getRefreshIntervalUpdate$: mockObservable,
+            getFetch$: mockObservable,
+            getAutoRefreshFetch$: mockObservable,
+            getEnabledUpdated$: mockObservable,
+            getTimeUpdate$: mockObservable,
+            getRefreshIntervalUpdate$: mockObservable,
+            isTimeRangeSelectorEnabled: () => {
+              return isTimeRangeSelectorEnabled;
+            },
+            isAutoRefreshSelectorEnabled: () => {
+              return isAutoRefreshSelectorEnabled;
+            },
+            disableAutoRefreshSelector: () => {
+              isAutoRefreshSelectorEnabled = false;
+            },
+            enableAutoRefreshSelector: () => {
+              isAutoRefreshSelectorEnabled = true;
+            },
+            getRefreshInterval: () => {
+              return refreshInterval;
+            },
+            setRefreshInterval: interval => {
+              refreshInterval = interval;
+            },
+            enableTimeRangeSelector: () => {
+              isTimeRangeSelectorEnabled = true;
+            },
+            disableTimeRangeSelector: () => {
+              isTimeRangeSelectorEnabled = false;
+            },
+            getTime: sinon.fake(),
+            setTime: sinon.fake(),
+            getActiveBounds: sinon.fake(),
+            getBounds: sinon.fake(),
+            calculateBounds: sinon.fake(),
+            createFilter: sinon.fake(),
           },
           history: sinon.fake(),
         },
@@ -183,10 +229,6 @@ export const npSetup = {
   },
 };
 
-let refreshInterval = undefined;
-let isTimeRangeSelectorEnabled = true;
-let isAutoRefreshSelectorEnabled = true;
-
 export const npStart = {
   core: {
     chrome: {
diff --git a/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.test.ts b/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.test.ts
index 4b17d8517328b..4cf74d991ceb9 100644
--- a/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.test.ts
+++ b/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.test.ts
@@ -181,4 +181,10 @@ describe('kbnUrlTracker', () => {
       `"/app/test#/start?state1=(key1:abc)&state2=(key2:def)"`
     );
   });
+
+  test('set url to storage when setActiveUrl was called', () => {
+    createTracker();
+    urlTracker.setActiveUrl('/deep/path/4');
+    expect(storage.getItem('storageKey')).toEqual('#/deep/path/4');
+  });
 });
diff --git a/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.ts b/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.ts
index 6f3f64ea7b941..2edd135c184ec 100644
--- a/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.ts
+++ b/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.ts
@@ -36,6 +36,7 @@ export interface KbnUrlTracker {
    * Unregistering the url tracker. This won't reset the current state of the nav link
    */
   stop: () => void;
+  setActiveUrl: (newUrl: string) => void;
 }
 
 /**
@@ -130,20 +131,24 @@ export function createKbnUrlTracker({
     }
   }
 
+  function setActiveUrl(newUrl: string) {
+    const urlWithHashes = baseUrl + '#' + newUrl;
+    let urlWithStates = '';
+    try {
+      urlWithStates = unhashUrl(urlWithHashes);
+    } catch (e) {
+      toastNotifications.addDanger(e.message);
+    }
+
+    activeUrl = getActiveSubUrl(urlWithStates || urlWithHashes);
+    storageInstance.setItem(storageKey, activeUrl);
+  }
+
   function onMountApp() {
     unsubscribe();
     // track current hash when within app
     unsubscribeURLHistory = historyInstance.listen(location => {
-      const urlWithHashes = baseUrl + '#' + location.pathname + location.search;
-      let urlWithStates = '';
-      try {
-        urlWithStates = unhashUrl(urlWithHashes);
-      } catch (e) {
-        toastNotifications.addDanger(e.message);
-      }
-
-      activeUrl = getActiveSubUrl(urlWithStates || urlWithHashes);
-      storageInstance.setItem(storageKey, activeUrl);
+      setActiveUrl(location.pathname + location.search);
     });
   }
 
@@ -188,5 +193,6 @@ export function createKbnUrlTracker({
     stop() {
       unsubscribe();
     },
+    setActiveUrl,
   };
 }

From 8bc3fa40425778a8e056eb4d561ff28f3e870c22 Mon Sep 17 00:00:00 2001
From: Anton Dosov <anton.dosov@elastic.co>
Date: Tue, 18 Feb 2020 13:45:25 +0100
Subject: [PATCH 014/174] Bugfix: Navigation from unsaved dashboard to recently
 used fails (#57795)

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../np_ready/dashboard_state_manager.ts       | 20 ++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_state_manager.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_state_manager.ts
index fa5354a17b6d9..fe7beafcad18c 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_state_manager.ts
+++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_state_manager.ts
@@ -165,7 +165,7 @@ export class DashboardStateManager {
     // make sure url ('_a') matches initial state
     this.kbnUrlStateStorage.set(this.STATE_STORAGE_KEY, initialState, { replace: true });
 
-    // setup state syncing utils. state container will be synched with url into `this.STATE_STORAGE_KEY` query param
+    // setup state syncing utils. state container will be synced with url into `this.STATE_STORAGE_KEY` query param
     this.stateSyncRef = syncState<DashboardAppState>({
       storageKey: this.STATE_STORAGE_KEY,
       stateContainer: {
@@ -173,10 +173,20 @@ export class DashboardStateManager {
         set: (state: DashboardAppState | null) => {
           // sync state required state container to be able to handle null
           // overriding set() so it could handle null coming from url
-          this.stateContainer.set({
-            ...this.stateDefaults,
-            ...state,
-          });
+          if (state) {
+            this.stateContainer.set({
+              ...this.stateDefaults,
+              ...state,
+            });
+          } else {
+            // Do nothing in case when state from url is empty,
+            // this fixes: https://github.com/elastic/kibana/issues/57789
+            // There are not much cases when state in url could become empty:
+            // 1. User manually removed `_a` from the url
+            // 2. Browser is navigating away from the page and most likely there is no `_a` in the url.
+            //    In this case we don't want to do any state updates
+            //    and just allow $scope.$on('destroy') fire later and clean up everything
+          }
         },
       },
       stateStorage: this.kbnUrlStateStorage,

From 23306d80973878ecb22f1d3cb9d0a71b8e2ae6c1 Mon Sep 17 00:00:00 2001
From: Matthias Wilhelm <matthias.wilhelm@elastic.co>
Date: Tue, 18 Feb 2020 14:24:22 +0100
Subject: [PATCH 015/174] [Discover] Migrate context AppState / GlobalState to
 use new app state helpers  (#57078)

* Remove globalState, migrate to the new helpers

* Remove appState, migrate to the new helpers

* Add tests
---
 .../discover/np_ready/angular/context.js      |  73 +++--
 .../np_ready/angular/context/api/context.ts   |   2 +-
 .../np_ready/angular/context/query/actions.js |   4 +-
 .../np_ready/angular/context_state.test.ts    | 193 ++++++++++++
 .../np_ready/angular/context_state.ts         | 275 ++++++++++++++++++
 5 files changed, 506 insertions(+), 41 deletions(-)
 create mode 100644 src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_state.test.ts
 create mode 100644 src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_state.ts

diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context.js
index a370c66ae330b..038f783a0daf1 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context.js
+++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context.js
@@ -19,13 +19,11 @@
 
 import _ from 'lodash';
 import { i18n } from '@kbn/i18n';
-import { getAngularModule, getServices, subscribeWithScope } from '../../kibana_services';
-
+import { getAngularModule, getServices } from '../../kibana_services';
 import './context_app';
+import { getState } from './context_state';
 import contextAppRouteTemplate from './context.html';
 import { getRootBreadcrumbs } from '../helpers/breadcrumbs';
-import { FilterStateManager } from '../../../../../data/public';
-const { chrome } = getServices();
 
 const k7Breadcrumbs = $route => {
   const { indexPattern } = $route.current.locals;
@@ -68,53 +66,50 @@ getAngularModule().config($routeProvider => {
     });
 });
 
-function ContextAppRouteController(
-  $routeParams,
-  $scope,
-  AppState,
-  config,
-  $route,
-  getAppState,
-  globalState
-) {
+function ContextAppRouteController($routeParams, $scope, config, $route) {
   const filterManager = getServices().filterManager;
-  const filterStateManager = new FilterStateManager(globalState, getAppState, filterManager);
   const indexPattern = $route.current.locals.indexPattern.ip;
+  const {
+    startSync: startStateSync,
+    stopSync: stopStateSync,
+    appState,
+    getFilters,
+    setFilters,
+    setAppState,
+  } = getState({
+    defaultStepSize: config.get('context:defaultSize'),
+    timeFieldName: indexPattern.timeFieldName,
+    storeInSessionStorage: config.get('state:storeInSessionStorage'),
+  });
+  this.state = { ...appState.getState() };
+  this.anchorId = $routeParams.id;
+  this.indexPattern = indexPattern;
+  this.discoverUrl = getServices().chrome.navLinks.get('kibana:discover').url;
+  filterManager.setFilters(_.cloneDeep(getFilters()));
+  startStateSync();
 
-  this.state = new AppState(createDefaultAppState(config, indexPattern));
-  this.state.save(true);
-
+  // take care of parameter changes in UI
   $scope.$watchGroup(
     [
       'contextAppRoute.state.columns',
       'contextAppRoute.state.predecessorCount',
       'contextAppRoute.state.successorCount',
     ],
-    () => this.state.save(true)
+    newValues => {
+      const [columns, predecessorCount, successorCount] = newValues;
+      if (Array.isArray(columns) && predecessorCount >= 0 && successorCount >= 0) {
+        setAppState({ columns, predecessorCount, successorCount });
+      }
+    }
   );
-
-  const updateSubsciption = subscribeWithScope($scope, filterManager.getUpdates$(), {
-    next: () => {
-      this.filters = _.cloneDeep(filterManager.getFilters());
-    },
+  // take care of parameter filter changes
+  const filterObservable = filterManager.getUpdates$().subscribe(() => {
+    setFilters(filterManager);
+    $route.reload();
   });
 
   $scope.$on('$destroy', () => {
-    filterStateManager.destroy();
-    updateSubsciption.unsubscribe();
+    stopStateSync();
+    filterObservable.unsubscribe();
   });
-  this.anchorId = $routeParams.id;
-  this.indexPattern = indexPattern;
-  this.discoverUrl = chrome.navLinks.get('kibana:discover').url;
-  this.filters = _.cloneDeep(filterManager.getFilters());
-}
-
-function createDefaultAppState(config, indexPattern) {
-  return {
-    columns: ['_source'],
-    filters: [],
-    predecessorCount: parseInt(config.get('context:defaultSize'), 10),
-    sort: [indexPattern.timeFieldName, 'desc'],
-    successorCount: parseInt(config.get('context:defaultSize'), 10),
-  };
 }
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/context.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/context.ts
index a9c6918adbfde..b91ef5a6b79fb 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/context.ts
+++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/context.ts
@@ -67,7 +67,7 @@ function fetchContextProvider(indexPatterns: IndexPatternsContract) {
     size: number,
     filters: Filter[]
   ) {
-    if (typeof anchor !== 'object' || anchor === null) {
+    if (typeof anchor !== 'object' || anchor === null || !size) {
       return [];
     }
     const indexPattern = await indexPatterns.get(indexPatternId);
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query/actions.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query/actions.js
index 966ecffda7755..1cebb88cbda5a 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query/actions.js
+++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query/actions.js
@@ -88,9 +88,11 @@ export function QueryActionsProvider(Promise) {
 
   const fetchSurroundingRows = (type, state) => {
     const {
-      queryParameters: { indexPatternId, filters, sort, tieBreakerField },
+      queryParameters: { indexPatternId, sort, tieBreakerField },
       rows: { anchor },
     } = state;
+    const filters = getServices().filterManager.getFilters();
+
     const count =
       type === 'successors'
         ? state.queryParameters.successorCount
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_state.test.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_state.test.ts
new file mode 100644
index 0000000000000..1fa71ed11643a
--- /dev/null
+++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_state.test.ts
@@ -0,0 +1,193 @@
+/*
+ * 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 { getState } from './context_state';
+import { createBrowserHistory, History } from 'history';
+import { FilterManager, Filter } from '../../../../../../../plugins/data/public';
+import { coreMock } from '../../../../../../../core/public/mocks';
+const setupMock = coreMock.createSetup();
+
+describe('Test Discover Context State', () => {
+  let history: History;
+  let state: any;
+  const getCurrentUrl = () => history.createHref(history.location);
+  beforeEach(async () => {
+    history = createBrowserHistory();
+    history.push('/');
+    state = await getState({
+      defaultStepSize: '4',
+      timeFieldName: 'time',
+      history,
+    });
+    state.startSync();
+  });
+  afterEach(() => {
+    state.stopSync();
+  });
+  test('getState function default return', () => {
+    expect(state.appState.getState()).toMatchInlineSnapshot(`
+      Object {
+        "columns": Array [
+          "_source",
+        ],
+        "filters": Array [],
+        "predecessorCount": 4,
+        "sort": Array [
+          "time",
+          "desc",
+        ],
+        "successorCount": 4,
+      }
+    `);
+    expect(state.globalState.getState()).toMatchInlineSnapshot(`null`);
+    expect(state.startSync).toBeDefined();
+    expect(state.stopSync).toBeDefined();
+    expect(state.getFilters()).toStrictEqual([]);
+  });
+  test('getState -> setAppState syncing to url', async () => {
+    state.setAppState({ predecessorCount: 10 });
+    state.flushToUrl();
+    expect(getCurrentUrl()).toMatchInlineSnapshot(
+      `"/#?_a=(columns:!(_source),filters:!(),predecessorCount:10,sort:!(time,desc),successorCount:4)"`
+    );
+  });
+  test('getState -> url to appState syncing', async () => {
+    history.push(
+      '/#?_a=(columns:!(_source),predecessorCount:1,sort:!(time,desc),successorCount:1)'
+    );
+    expect(state.appState.getState()).toMatchInlineSnapshot(`
+      Object {
+        "columns": Array [
+          "_source",
+        ],
+        "predecessorCount": 1,
+        "sort": Array [
+          "time",
+          "desc",
+        ],
+        "successorCount": 1,
+      }
+    `);
+  });
+  test('getState -> url to appState syncing with return to a url without state', async () => {
+    history.push(
+      '/#?_a=(columns:!(_source),predecessorCount:1,sort:!(time,desc),successorCount:1)'
+    );
+    expect(state.appState.getState()).toMatchInlineSnapshot(`
+      Object {
+        "columns": Array [
+          "_source",
+        ],
+        "predecessorCount": 1,
+        "sort": Array [
+          "time",
+          "desc",
+        ],
+        "successorCount": 1,
+      }
+    `);
+    history.push('/');
+    expect(state.appState.getState()).toMatchInlineSnapshot(`
+      Object {
+        "columns": Array [
+          "_source",
+        ],
+        "predecessorCount": 1,
+        "sort": Array [
+          "time",
+          "desc",
+        ],
+        "successorCount": 1,
+      }
+    `);
+  });
+
+  test('getState -> filters', async () => {
+    const filterManager = new FilterManager(setupMock.uiSettings);
+    const filterGlobal = {
+      query: { match: { extension: { query: 'jpg', type: 'phrase' } } },
+      meta: { index: 'logstash-*', negate: false, disabled: false, alias: null },
+    } as Filter;
+    filterManager.setGlobalFilters([filterGlobal]);
+    const filterApp = {
+      query: { match: { extension: { query: 'png', type: 'phrase' } } },
+      meta: { index: 'logstash-*', negate: true, disabled: false, alias: null },
+    } as Filter;
+    filterManager.setAppFilters([filterApp]);
+    state.setFilters(filterManager);
+    expect(state.getFilters()).toMatchInlineSnapshot(`
+      Array [
+        Object {
+          "$state": Object {
+            "store": "globalState",
+          },
+          "meta": Object {
+            "alias": null,
+            "disabled": false,
+            "index": "logstash-*",
+            "key": "extension",
+            "negate": false,
+            "params": Object {
+              "query": "jpg",
+            },
+            "type": "phrase",
+            "value": [Function],
+          },
+          "query": Object {
+            "match": Object {
+              "extension": Object {
+                "query": "jpg",
+                "type": "phrase",
+              },
+            },
+          },
+        },
+        Object {
+          "$state": Object {
+            "store": "appState",
+          },
+          "meta": Object {
+            "alias": null,
+            "disabled": false,
+            "index": "logstash-*",
+            "key": "extension",
+            "negate": true,
+            "params": Object {
+              "query": "png",
+            },
+            "type": "phrase",
+            "value": [Function],
+          },
+          "query": Object {
+            "match": Object {
+              "extension": Object {
+                "query": "png",
+                "type": "phrase",
+              },
+            },
+          },
+        },
+      ]
+    `);
+    state.flushToUrl();
+    expect(getCurrentUrl()).toMatchInlineSnapshot(
+      `"/#?_g=(filters:!(('$state':(store:globalState),meta:(alias:!n,disabled:!f,index:'logstash-*',key:extension,negate:!f,params:(query:jpg),type:phrase),query:(match:(extension:(query:jpg,type:phrase))))))&_a=(columns:!(_source),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'logstash-*',key:extension,negate:!t,params:(query:png),type:phrase),query:(match:(extension:(query:png,type:phrase))))),predecessorCount:4,sort:!(time,desc),successorCount:4)"`
+    );
+  });
+});
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_state.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_state.ts
new file mode 100644
index 0000000000000..8fb6140d55e31
--- /dev/null
+++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_state.ts
@@ -0,0 +1,275 @@
+/*
+ * 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 _ from 'lodash';
+import { createBrowserHistory, History } from 'history';
+import {
+  createStateContainer,
+  createKbnUrlStateStorage,
+  syncStates,
+  BaseStateContainer,
+} from '../../../../../../../plugins/kibana_utils/public';
+import { esFilters, FilterManager, Filter } from '../../../../../../../plugins/data/public';
+
+interface AppState {
+  /**
+   * Columns displayed in the table, cannot be changed by UI, just in discover's main app
+   */
+  columns: string[];
+  /**
+   * Array of filters
+   */
+  filters: Filter[];
+  /**
+   * Number of records to be fetched before anchor records (newer records)
+   */
+  predecessorCount: number;
+  /**
+   * Sorting of the records to be fetched, assumed to be a legacy parameter
+   */
+  sort: string[];
+  /**
+   * Number of records to be fetched after the anchor records (older records)
+   */
+  successorCount: number;
+}
+
+interface GlobalState {
+  /**
+   * Array of filters
+   */
+  filters: Filter[];
+}
+
+interface GetStateParams {
+  /**
+   * Number of records to be fetched when 'Load' link/button is clicked
+   */
+  defaultStepSize: string;
+  /**
+   * The timefield used for sorting
+   */
+  timeFieldName: string;
+  /**
+   * Determins the use of long vs. short/hashed urls
+   */
+  storeInSessionStorage?: boolean;
+  /**
+   * Browser history used for testing
+   */
+  history?: History;
+}
+
+interface GetStateReturn {
+  /**
+   * Global state, the _g part of the URL
+   */
+  globalState: BaseStateContainer<GlobalState>;
+  /**
+   * App state, the _a part of the URL
+   */
+  appState: BaseStateContainer<AppState>;
+  /**
+   * Start sync between state and URL
+   */
+  startSync: () => void;
+  /**
+   * Stop sync between state and URL
+   */
+  stopSync: () => void;
+  /**
+   * Set app state to with a partial new app state
+   */
+  setAppState: (newState: Partial<AppState>) => void;
+  /**
+   * Get all filters, global and app state
+   */
+  getFilters: () => Filter[];
+  /**
+   * Set global state and app state filters by the given FilterManager instance
+   * @param filterManager
+   */
+  setFilters: (filterManager: FilterManager) => void;
+  /**
+   * sync state to URL, used for testing
+   */
+  flushToUrl: () => void;
+}
+const GLOBAL_STATE_URL_KEY = '_g';
+const APP_STATE_URL_KEY = '_a';
+
+/**
+ * Builds and returns appState and globalState containers
+ * provides helper functions to start/stop syncing with URL
+ */
+export function getState({
+  defaultStepSize,
+  timeFieldName,
+  storeInSessionStorage = false,
+  history,
+}: GetStateParams): GetStateReturn {
+  const stateStorage = createKbnUrlStateStorage({
+    useHash: storeInSessionStorage,
+    history: history ? history : createBrowserHistory(),
+  });
+
+  const globalStateInitial = stateStorage.get(GLOBAL_STATE_URL_KEY) as GlobalState;
+  const globalStateContainer = createStateContainer<GlobalState>(globalStateInitial);
+
+  const appStateFromUrl = stateStorage.get(APP_STATE_URL_KEY) as AppState;
+  const appStateInitial = createInitialAppState(defaultStepSize, timeFieldName, appStateFromUrl);
+  const appStateContainer = createStateContainer<AppState>(appStateInitial);
+
+  const { start, stop } = syncStates([
+    {
+      storageKey: GLOBAL_STATE_URL_KEY,
+      stateContainer: {
+        ...globalStateContainer,
+        ...{
+          set: (value: GlobalState | null) => {
+            if (value) {
+              globalStateContainer.set(value);
+            }
+          },
+        },
+      },
+      stateStorage,
+    },
+    {
+      storageKey: APP_STATE_URL_KEY,
+      stateContainer: {
+        ...appStateContainer,
+        ...{
+          set: (value: AppState | null) => {
+            if (value) {
+              appStateContainer.set(value);
+            }
+          },
+        },
+      },
+      stateStorage,
+    },
+  ]);
+
+  return {
+    globalState: globalStateContainer,
+    appState: appStateContainer,
+    startSync: start,
+    stopSync: stop,
+    setAppState: (newState: Partial<AppState>) => {
+      const oldState = appStateContainer.getState();
+      const mergedState = { ...oldState, ...newState };
+
+      if (!isEqualState(oldState, mergedState)) {
+        appStateContainer.set(mergedState);
+      }
+    },
+    getFilters: () => [
+      ...getFilters(globalStateContainer.getState()),
+      ...getFilters(appStateContainer.getState()),
+    ],
+    setFilters: (filterManager: FilterManager) => {
+      // global state filters
+      const globalFilters = filterManager.getGlobalFilters();
+      const globalFilterChanged = !isEqualFilters(
+        globalFilters,
+        getFilters(globalStateContainer.getState())
+      );
+      if (globalFilterChanged) {
+        globalStateContainer.set({ filters: globalFilters });
+      }
+      // app state filters
+      const appFilters = filterManager.getAppFilters();
+      const appFilterChanged = !isEqualFilters(
+        appFilters,
+        getFilters(appStateContainer.getState())
+      );
+      if (appFilterChanged) {
+        appStateContainer.set({ ...appStateContainer.getState(), ...{ filters: appFilters } });
+      }
+    },
+    // helper function just needed for testing
+    flushToUrl: () => stateStorage.flush(),
+  };
+}
+
+/**
+ * Helper function to compare 2 different filter states
+ */
+export function isEqualFilters(filtersA: Filter[], filtersB: Filter[]) {
+  if (!filtersA && !filtersB) {
+    return true;
+  } else if (!filtersA || !filtersB) {
+    return false;
+  }
+  return esFilters.compareFilters(filtersA, filtersB, esFilters.COMPARE_ALL_OPTIONS);
+}
+
+/**
+ * Helper function to compare 2 different states, is needed since comparing filters
+ * works differently, doesn't work with _.isEqual
+ */
+function isEqualState(stateA: AppState | GlobalState, stateB: AppState | GlobalState) {
+  if (!stateA && !stateB) {
+    return true;
+  } else if (!stateA || !stateB) {
+    return false;
+  }
+  const { filters: stateAFilters = [], ...stateAPartial } = stateA;
+  const { filters: stateBFilters = [], ...stateBPartial } = stateB;
+  return (
+    _.isEqual(stateAPartial, stateBPartial) &&
+    esFilters.compareFilters(stateAFilters, stateBFilters, esFilters.COMPARE_ALL_OPTIONS)
+  );
+}
+
+/**
+ * Helper function to return array of filter object of a given state
+ */
+function getFilters(state: AppState | GlobalState): Filter[] {
+  if (!state || !Array.isArray(state.filters)) {
+    return [];
+  }
+  return state.filters;
+}
+
+/**
+ * Helper function to return the initial app state, which is a merged object of url state and
+ * default state. The default size is the default number of successor/predecessor records to fetch
+ */
+function createInitialAppState(
+  defaultSize: string,
+  timeFieldName: string,
+  urlState: AppState
+): AppState {
+  const defaultState = {
+    columns: ['_source'],
+    filters: [],
+    predecessorCount: parseInt(defaultSize, 10),
+    sort: [timeFieldName, 'desc'],
+    successorCount: parseInt(defaultSize, 10),
+  };
+  if (typeof urlState !== 'object') {
+    return defaultState;
+  }
+
+  return {
+    ...defaultState,
+    ...urlState,
+  };
+}

From ad5daba2eaf6800a0c67d658f62877706033d797 Mon Sep 17 00:00:00 2001
From: patrykkopycinski <patryk.kopycinski@elastic.co>
Date: Tue, 18 Feb 2020 14:53:31 +0100
Subject: [PATCH 016/174] [SIEM] Replace AutoSizer with use-resize-observer
 (#56588)

---
 renovate.json5                                |   8 +
 .../auto_sizer/__examples__/index.stories.tsx |  27 -
 .../public/components/auto_sizer/index.tsx    | 182 ----
 .../components/charts/areachart.test.tsx      |   4 +-
 .../public/components/charts/areachart.tsx    |  36 +-
 .../components/charts/barchart.test.tsx       |   6 +-
 .../public/components/charts/barchart.tsx     |  39 +-
 .../events_viewer/events_viewer.test.tsx      |   5 +
 .../events_viewer/events_viewer.tsx           | 212 +++--
 .../components/events_viewer/index.test.tsx   |   5 +
 .../__snapshots__/timeline.test.tsx.snap      | 793 +++++++++++++++++-
 .../components/timeline/timeline.test.tsx     |   5 +
 .../public/components/timeline/timeline.tsx   | 192 ++---
 .../plugins/siem/public/pages/home/index.tsx  | 171 ++--
 .../pages/hosts/details/details_tabs.test.tsx |   5 +
 x-pack/package.json                           |   2 +
 yarn.lock                                     |  14 +
 17 files changed, 1144 insertions(+), 562 deletions(-)
 delete mode 100644 x-pack/legacy/plugins/siem/public/components/auto_sizer/__examples__/index.stories.tsx
 delete mode 100644 x-pack/legacy/plugins/siem/public/components/auto_sizer/index.tsx

diff --git a/renovate.json5 b/renovate.json5
index 642c4a98b5799..58a64a5d0f967 100644
--- a/renovate.json5
+++ b/renovate.json5
@@ -913,6 +913,14 @@
           '@types/tslib',
         ],
       },
+      {
+        groupSlug: 'use-resize-observer',
+        groupName: 'use-resize-observer related packages',
+        packageNames: [
+          'use-resize-observer',
+          '@types/use-resize-observer',
+        ],
+      },
       {
         groupSlug: 'uuid',
         groupName: 'uuid related packages',
diff --git a/x-pack/legacy/plugins/siem/public/components/auto_sizer/__examples__/index.stories.tsx b/x-pack/legacy/plugins/siem/public/components/auto_sizer/__examples__/index.stories.tsx
deleted file mode 100644
index 414cea0d3f40d..0000000000000
--- a/x-pack/legacy/plugins/siem/public/components/auto_sizer/__examples__/index.stories.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-import { storiesOf } from '@storybook/react';
-import React from 'react';
-import { AutoSizer } from '..';
-
-storiesOf('components/AutoSizer', module).add('example', () => (
-  <div>
-    <AutoSizer>
-      {({ measureRef, content }) => (
-        <div ref={measureRef} style={{ border: '1px solid tomato' }}>
-          <div>
-            {'width: '}
-            {content.width}
-          </div>
-          <div>
-            {'height: '}
-            {content.height}
-          </div>
-        </div>
-      )}
-    </AutoSizer>
-  </div>
-));
diff --git a/x-pack/legacy/plugins/siem/public/components/auto_sizer/index.tsx b/x-pack/legacy/plugins/siem/public/components/auto_sizer/index.tsx
deleted file mode 100644
index 8b3a85b28b8fe..0000000000000
--- a/x-pack/legacy/plugins/siem/public/components/auto_sizer/index.tsx
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import isEqual from 'lodash/fp/isEqual';
-import React from 'react';
-import ResizeObserver from 'resize-observer-polyfill';
-
-interface Measurement {
-  width?: number;
-  height?: number;
-}
-
-interface Measurements {
-  bounds: Measurement;
-  content: Measurement;
-  windowMeasurement: Measurement;
-}
-
-interface AutoSizerProps {
-  detectAnyWindowResize?: boolean;
-  bounds?: boolean;
-  content?: boolean;
-  onResize?: (size: Measurements) => void;
-  children: (
-    // eslint-disable-next-line @typescript-eslint/no-explicit-any
-    args: { measureRef: (instance: HTMLElement | null) => any } & Measurements
-  ) => React.ReactNode;
-}
-
-interface AutoSizerState {
-  boundsMeasurement: Measurement;
-  contentMeasurement: Measurement;
-  windowMeasurement: Measurement;
-}
-
-/** A hard-fork of the `infra` `AutoSizer` ಠ_ಠ */
-export class AutoSizer extends React.PureComponent<AutoSizerProps, AutoSizerState> {
-  public element: HTMLElement | null = null;
-  public resizeObserver: ResizeObserver | null = null;
-  public windowWidth: number = -1;
-
-  public readonly state = {
-    boundsMeasurement: {
-      height: void 0,
-      width: void 0,
-    },
-    contentMeasurement: {
-      height: void 0,
-      width: void 0,
-    },
-    windowMeasurement: {
-      height: void 0,
-      width: void 0,
-    },
-  };
-
-  constructor(props: AutoSizerProps) {
-    super(props);
-    if (this.props.detectAnyWindowResize) {
-      window.addEventListener('resize', this.updateMeasurement);
-    }
-    this.resizeObserver = new ResizeObserver(entries => {
-      entries.forEach(entry => {
-        if (entry.target === this.element) {
-          this.measure(entry);
-        }
-      });
-    });
-  }
-
-  public componentWillUnmount() {
-    if (this.resizeObserver) {
-      this.resizeObserver.disconnect();
-      this.resizeObserver = null;
-    }
-    if (this.props.detectAnyWindowResize) {
-      window.removeEventListener('resize', this.updateMeasurement);
-    }
-  }
-
-  public measure = (entry: ResizeObserverEntry | null) => {
-    if (!this.element) {
-      return;
-    }
-
-    const { content = true, bounds = false } = this.props;
-    const {
-      boundsMeasurement: previousBoundsMeasurement,
-      contentMeasurement: previousContentMeasurement,
-      windowMeasurement: previousWindowMeasurement,
-    } = this.state;
-
-    const boundsRect = bounds ? this.element.getBoundingClientRect() : null;
-    const boundsMeasurement = boundsRect
-      ? {
-          height: this.element.getBoundingClientRect().height,
-          width: this.element.getBoundingClientRect().width,
-        }
-      : previousBoundsMeasurement;
-    const windowMeasurement: Measurement = {
-      width: window.innerWidth,
-      height: window.innerHeight,
-    };
-
-    if (
-      this.props.detectAnyWindowResize &&
-      boundsMeasurement &&
-      boundsMeasurement.width &&
-      this.windowWidth !== -1 &&
-      this.windowWidth > window.innerWidth
-    ) {
-      const gap = this.windowWidth - window.innerWidth;
-      boundsMeasurement.width = boundsMeasurement.width - gap;
-    }
-    this.windowWidth = window.innerWidth;
-    const contentRect = content && entry ? entry.contentRect : null;
-    const contentMeasurement =
-      contentRect && entry
-        ? {
-            height: entry.contentRect.height,
-            width: entry.contentRect.width,
-          }
-        : previousContentMeasurement;
-
-    if (
-      isEqual(boundsMeasurement, previousBoundsMeasurement) &&
-      isEqual(contentMeasurement, previousContentMeasurement) &&
-      isEqual(windowMeasurement, previousWindowMeasurement)
-    ) {
-      return;
-    }
-
-    requestAnimationFrame(() => {
-      if (!this.resizeObserver) {
-        return;
-      }
-
-      this.setState({ boundsMeasurement, contentMeasurement, windowMeasurement });
-
-      if (this.props.onResize) {
-        this.props.onResize({
-          bounds: boundsMeasurement,
-          content: contentMeasurement,
-          windowMeasurement,
-        });
-      }
-    });
-  };
-
-  public render() {
-    const { children } = this.props;
-    const { boundsMeasurement, contentMeasurement, windowMeasurement } = this.state;
-
-    return children({
-      bounds: boundsMeasurement,
-      content: contentMeasurement,
-      windowMeasurement,
-      measureRef: this.storeRef,
-    });
-  }
-
-  private updateMeasurement = () => {
-    window.setTimeout(() => {
-      this.measure(null);
-    }, 0);
-  };
-
-  private storeRef = (element: HTMLElement | null) => {
-    if (this.element && this.resizeObserver) {
-      this.resizeObserver.unobserve(this.element);
-    }
-
-    if (element && this.resizeObserver) {
-      this.resizeObserver.observe(element);
-    }
-
-    this.element = element;
-  };
-}
diff --git a/x-pack/legacy/plugins/siem/public/components/charts/areachart.test.tsx b/x-pack/legacy/plugins/siem/public/components/charts/areachart.test.tsx
index 342d7d35f9cb7..27f0222b96b77 100644
--- a/x-pack/legacy/plugins/siem/public/components/charts/areachart.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/charts/areachart.test.tsx
@@ -331,7 +331,7 @@ describe('AreaChart', () => {
     });
 
     it(`should render area chart`, () => {
-      expect(shallowWrapper.find('AutoSizer')).toHaveLength(1);
+      expect(shallowWrapper.find('WrappedByAutoSizer')).toHaveLength(1);
       expect(shallowWrapper.find('ChartPlaceHolder')).toHaveLength(0);
     });
   });
@@ -344,7 +344,7 @@ describe('AreaChart', () => {
       });
 
       it(`should render a chart place holder`, () => {
-        expect(shallowWrapper.find('AutoSizer')).toHaveLength(0);
+        expect(shallowWrapper.find('WrappedByAutoSizer')).toHaveLength(0);
         expect(shallowWrapper.find('ChartPlaceHolder')).toHaveLength(1);
       });
     }
diff --git a/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx b/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx
index 57f78080abc60..fd05b80e41235 100644
--- a/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx
@@ -16,7 +16,8 @@ import {
   RecursivePartial,
 } from '@elastic/charts';
 import { getOr, get, isNull, isNumber } from 'lodash/fp';
-import { AutoSizer } from '../auto_sizer';
+import useResizeObserver from 'use-resize-observer';
+
 import { ChartPlaceHolder } from './chart_place_holder';
 import { useTimeZone } from '../../lib/kibana';
 import {
@@ -124,35 +125,24 @@ export const AreaChartBase = React.memo(AreaChartBaseComponent);
 
 AreaChartBase.displayName = 'AreaChartBase';
 
-export const AreaChartComponent = ({
-  areaChart,
-  configs,
-}: {
+interface AreaChartComponentProps {
   areaChart: ChartSeriesData[] | null | undefined;
   configs?: ChartSeriesConfigs | undefined;
-}) => {
+}
+
+export const AreaChartComponent: React.FC<AreaChartComponentProps> = ({ areaChart, configs }) => {
+  const { ref: measureRef, width, height } = useResizeObserver<HTMLDivElement>({});
   const customHeight = get('customHeight', configs);
   const customWidth = get('customWidth', configs);
+  const chartHeight = getChartHeight(customHeight, height);
+  const chartWidth = getChartWidth(customWidth, width);
 
   return checkIfAnyValidSeriesExist(areaChart) ? (
-    <AutoSizer detectAnyWindowResize={false} content>
-      {({ measureRef, content: { height, width } }) => (
-        <WrappedByAutoSizer ref={measureRef} height={getChartHeight(customHeight, height)}>
-          <AreaChartBase
-            data={areaChart}
-            height={getChartHeight(customHeight, height)}
-            width={getChartWidth(customWidth, width)}
-            configs={configs}
-          />
-        </WrappedByAutoSizer>
-      )}
-    </AutoSizer>
+    <WrappedByAutoSizer ref={measureRef} height={chartHeight}>
+      <AreaChartBase data={areaChart} height={chartHeight} width={chartWidth} configs={configs} />
+    </WrappedByAutoSizer>
   ) : (
-    <ChartPlaceHolder
-      height={getChartHeight(customHeight)}
-      width={getChartWidth(customWidth)}
-      data={areaChart}
-    />
+    <ChartPlaceHolder height={chartHeight} width={chartWidth} data={areaChart} />
   );
 };
 
diff --git a/x-pack/legacy/plugins/siem/public/components/charts/barchart.test.tsx b/x-pack/legacy/plugins/siem/public/components/charts/barchart.test.tsx
index d8e5079dd72a6..0b6635b04d380 100644
--- a/x-pack/legacy/plugins/siem/public/components/charts/barchart.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/charts/barchart.test.tsx
@@ -278,7 +278,7 @@ describe.each(chartDataSets)('BarChart with valid data [%o]', data => {
   });
 
   it(`should render chart`, () => {
-    expect(shallowWrapper.find('AutoSizer')).toHaveLength(1);
+    expect(shallowWrapper.find('WrappedByAutoSizer')).toHaveLength(1);
     expect(shallowWrapper.find('ChartPlaceHolder')).toHaveLength(0);
   });
 });
@@ -290,8 +290,8 @@ describe.each(chartHolderDataSets)('BarChart with invalid data [%o]', data => {
     shallowWrapper = shallow(<BarChartComponent configs={mockConfig} barChart={data} />);
   });
 
-  it(`should render chart holder`, () => {
-    expect(shallowWrapper.find('AutoSizer')).toHaveLength(0);
+  it(`should render a ChartPlaceHolder`, () => {
+    expect(shallowWrapper.find('WrappedByAutoSizer')).toHaveLength(0);
     expect(shallowWrapper.find('ChartPlaceHolder')).toHaveLength(1);
   });
 });
diff --git a/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx b/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx
index d9dd302dae724..1355926d343df 100644
--- a/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx
@@ -8,9 +8,9 @@ import React from 'react';
 import { Chart, BarSeries, Axis, Position, ScaleType, Settings } from '@elastic/charts';
 import { getOr, get, isNumber } from 'lodash/fp';
 import deepmerge from 'deepmerge';
+import useResizeObserver from 'use-resize-observer';
 
 import { useTimeZone } from '../../lib/kibana';
-import { AutoSizer } from '../auto_sizer';
 import { ChartPlaceHolder } from './chart_place_holder';
 import {
   chartDefaultSettings,
@@ -99,40 +99,25 @@ export const BarChartBase = React.memo(BarChartBaseComponent);
 
 BarChartBase.displayName = 'BarChartBase';
 
-export const BarChartComponent = ({
-  barChart,
-  configs,
-}: {
+interface BarChartComponentProps {
   barChart: ChartSeriesData[] | null | undefined;
   configs?: ChartSeriesConfigs | undefined;
-}) => {
+}
+
+export const BarChartComponent: React.FC<BarChartComponentProps> = ({ barChart, configs }) => {
+  const { ref: measureRef, width, height } = useResizeObserver<HTMLDivElement>({});
   const customHeight = get('customHeight', configs);
   const customWidth = get('customWidth', configs);
+  const chartHeight = getChartHeight(customHeight, height);
+  const chartWidth = getChartWidth(customWidth, width);
 
   return checkIfAnyValidSeriesExist(barChart) ? (
-    <AutoSizer detectAnyWindowResize={false} content>
-      {({ measureRef, content: { height, width } }) => (
-        <WrappedByAutoSizer ref={measureRef} height={getChartHeight(customHeight, height)}>
-          <BarChartBaseComponent
-            height={getChartHeight(customHeight, height)}
-            width={getChartWidth(customWidth, width)}
-            data={barChart}
-            configs={configs}
-          />
-        </WrappedByAutoSizer>
-      )}
-    </AutoSizer>
+    <WrappedByAutoSizer ref={measureRef} height={chartHeight}>
+      <BarChartBase height={chartHeight} width={chartHeight} data={barChart} configs={configs} />
+    </WrappedByAutoSizer>
   ) : (
-    <ChartPlaceHolder
-      height={getChartHeight(customHeight)}
-      width={getChartWidth(customWidth)}
-      data={barChart}
-    />
+    <ChartPlaceHolder height={chartHeight} width={chartWidth} data={barChart} />
   );
 };
 
-BarChartComponent.displayName = 'BarChartComponent';
-
 export const BarChart = React.memo(BarChartComponent);
-
-BarChart.displayName = 'BarChart';
diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.test.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.test.tsx
index 3cef3e98c2f0a..8c4228b597dbb 100644
--- a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.test.tsx
@@ -6,6 +6,7 @@
 
 import React from 'react';
 import { MockedProvider } from 'react-apollo/test-utils';
+import useResizeObserver from 'use-resize-observer';
 
 import { mockIndexPattern, TestProviders } from '../../mock';
 import { wait } from '../../lib/helpers';
@@ -27,6 +28,10 @@ mockUseFetchIndexPatterns.mockImplementation(() => [
   },
 ]);
 
+const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock;
+jest.mock('use-resize-observer');
+mockUseResizeObserver.mockImplementation(() => ({}));
+
 const from = 1566943856794;
 const to = 1566857456791;
 
diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx
index 14473605a7c88..cbce1f635310a 100644
--- a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx
@@ -9,13 +9,13 @@ import deepEqual from 'fast-deep-equal';
 import { getOr, isEmpty, isEqual, union } from 'lodash/fp';
 import React, { useMemo } from 'react';
 import styled from 'styled-components';
+import useResizeObserver from 'use-resize-observer';
 
 import { BrowserFields } from '../../containers/source';
 import { TimelineQuery } from '../../containers/timeline';
 import { Direction } from '../../graphql/types';
 import { useKibana } from '../../lib/kibana';
 import { KqlMode } from '../../store/timeline/model';
-import { AutoSizer } from '../auto_sizer';
 import { HeaderSection } from '../header_section';
 import { ColumnHeader } from '../timeline/body/column_headers/column_header';
 import { defaultHeaders } from '../timeline/body/column_headers/default_headers';
@@ -95,6 +95,7 @@ const EventsViewerComponent: React.FC<Props> = ({
   toggleColumn,
   utilityBar,
 }) => {
+  const { ref: measureRef, width = 0 } = useResizeObserver<HTMLDivElement>({});
   const columnsHeader = isEmpty(columns) ? defaultHeaders : columns;
   const kibana = useKibana();
   const combinedQueries = combineQueries({
@@ -120,115 +121,108 @@ const EventsViewerComponent: React.FC<Props> = ({
 
   return (
     <StyledEuiPanel data-test-subj="events-viewer-panel">
-      <AutoSizer detectAnyWindowResize={true} content>
-        {({ measureRef, content: { width = 0 } }) => (
-          <>
-            <WrappedByAutoSizer ref={measureRef}>
-              <div
-                data-test-subj="events-viewer-measured"
-                style={{ height: '0px', width: '100%' }}
-              />
-            </WrappedByAutoSizer>
-
-            {combinedQueries != null ? (
-              <TimelineQuery
-                fields={queryFields}
-                filterQuery={combinedQueries.filterQuery}
-                id={id}
-                indexPattern={indexPattern}
-                limit={itemsPerPage}
-                sortField={{
-                  sortFieldId: sort.columnId,
-                  direction: sort.sortDirection as Direction,
-                }}
-                sourceId="default"
-              >
-                {({
-                  events,
-                  getUpdatedAt,
-                  inspect,
-                  loading,
-                  loadMore,
-                  pageInfo,
-                  refetch,
-                  totalCount = 0,
-                }) => {
-                  const totalCountMinusDeleted =
-                    totalCount > 0 ? totalCount - deletedEventIds.length : 0;
-
-                  const subtitle = `${
-                    i18n.SHOWING
-                  }: ${totalCountMinusDeleted.toLocaleString()} ${timelineTypeContext.unit?.(
-                    totalCountMinusDeleted
-                  ) ?? i18n.UNIT(totalCountMinusDeleted)}`;
-
-                  // TODO: Reset eventDeletedIds/eventLoadingIds on refresh/loadmore (getUpdatedAt)
-                  return (
-                    <>
-                      <HeaderSection
+      <>
+        <WrappedByAutoSizer ref={measureRef}>
+          <div data-test-subj="events-viewer-measured" style={{ height: '0px', width: '100%' }} />
+        </WrappedByAutoSizer>
+
+        {combinedQueries != null ? (
+          <TimelineQuery
+            fields={queryFields}
+            filterQuery={combinedQueries.filterQuery}
+            id={id}
+            indexPattern={indexPattern}
+            limit={itemsPerPage}
+            sortField={{
+              sortFieldId: sort.columnId,
+              direction: sort.sortDirection as Direction,
+            }}
+            sourceId="default"
+          >
+            {({
+              events,
+              getUpdatedAt,
+              inspect,
+              loading,
+              loadMore,
+              pageInfo,
+              refetch,
+              totalCount = 0,
+            }) => {
+              const totalCountMinusDeleted =
+                totalCount > 0 ? totalCount - deletedEventIds.length : 0;
+
+              const subtitle = `${
+                i18n.SHOWING
+              }: ${totalCountMinusDeleted.toLocaleString()} ${timelineTypeContext.unit?.(
+                totalCountMinusDeleted
+              ) ?? i18n.UNIT(totalCountMinusDeleted)}`;
+
+              // TODO: Reset eventDeletedIds/eventLoadingIds on refresh/loadmore (getUpdatedAt)
+              return (
+                <>
+                  <HeaderSection
+                    id={id}
+                    subtitle={utilityBar ? undefined : subtitle}
+                    title={timelineTypeContext?.title ?? i18n.EVENTS}
+                  >
+                    {headerFilterGroup}
+                  </HeaderSection>
+
+                  {utilityBar?.(refetch, totalCountMinusDeleted)}
+
+                  <div
+                    data-test-subj={`events-container-loading-${loading}`}
+                    style={{ width: `${width}px` }}
+                  >
+                    <ManageTimelineContext
+                      loading={loading}
+                      width={width}
+                      type={timelineTypeContext}
+                    >
+                      <TimelineRefetch
                         id={id}
-                        subtitle={utilityBar ? undefined : subtitle}
-                        title={timelineTypeContext?.title ?? i18n.EVENTS}
-                      >
-                        {headerFilterGroup}
-                      </HeaderSection>
-
-                      {utilityBar?.(refetch, totalCountMinusDeleted)}
-
-                      <div
-                        data-test-subj={`events-container-loading-${loading}`}
-                        style={{ width: `${width}px` }}
-                      >
-                        <ManageTimelineContext
-                          loading={loading}
-                          width={width}
-                          type={timelineTypeContext}
-                        >
-                          <TimelineRefetch
-                            id={id}
-                            inputId="global"
-                            inspect={inspect}
-                            loading={loading}
-                            refetch={refetch}
-                          />
-
-                          <StatefulBody
-                            browserFields={browserFields}
-                            data={events.filter(e => !deletedEventIds.includes(e._id))}
-                            id={id}
-                            isEventViewer={true}
-                            height={height}
-                            sort={sort}
-                            toggleColumn={toggleColumn}
-                          />
-
-                          <Footer
-                            compact={isCompactFooter(width)}
-                            getUpdatedAt={getUpdatedAt}
-                            hasNextPage={getOr(false, 'hasNextPage', pageInfo)!}
-                            height={footerHeight}
-                            isEventViewer={true}
-                            isLive={isLive}
-                            isLoading={loading}
-                            itemsCount={events.length}
-                            itemsPerPage={itemsPerPage}
-                            itemsPerPageOptions={itemsPerPageOptions}
-                            onChangeItemsPerPage={onChangeItemsPerPage}
-                            onLoadMore={loadMore}
-                            nextCursor={getOr(null, 'endCursor.value', pageInfo)!}
-                            serverSideEventCount={totalCountMinusDeleted}
-                            tieBreaker={getOr(null, 'endCursor.tiebreaker', pageInfo)}
-                          />
-                        </ManageTimelineContext>
-                      </div>
-                    </>
-                  );
-                }}
-              </TimelineQuery>
-            ) : null}
-          </>
-        )}
-      </AutoSizer>
+                        inputId="global"
+                        inspect={inspect}
+                        loading={loading}
+                        refetch={refetch}
+                      />
+
+                      <StatefulBody
+                        browserFields={browserFields}
+                        data={events.filter(e => !deletedEventIds.includes(e._id))}
+                        id={id}
+                        isEventViewer={true}
+                        height={height}
+                        sort={sort}
+                        toggleColumn={toggleColumn}
+                      />
+
+                      <Footer
+                        compact={isCompactFooter(width)}
+                        getUpdatedAt={getUpdatedAt}
+                        hasNextPage={getOr(false, 'hasNextPage', pageInfo)!}
+                        height={footerHeight}
+                        isEventViewer={true}
+                        isLive={isLive}
+                        isLoading={loading}
+                        itemsCount={events.length}
+                        itemsPerPage={itemsPerPage}
+                        itemsPerPageOptions={itemsPerPageOptions}
+                        onChangeItemsPerPage={onChangeItemsPerPage}
+                        onLoadMore={loadMore}
+                        nextCursor={getOr(null, 'endCursor.value', pageInfo)!}
+                        serverSideEventCount={totalCountMinusDeleted}
+                        tieBreaker={getOr(null, 'endCursor.tiebreaker', pageInfo)}
+                      />
+                    </ManageTimelineContext>
+                  </div>
+                </>
+              );
+            }}
+          </TimelineQuery>
+        ) : null}
+      </>
     </StyledEuiPanel>
   );
 };
diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/index.test.tsx
index ec8d329f1dfe3..2bedd1cb89b41 100644
--- a/x-pack/legacy/plugins/siem/public/components/events_viewer/index.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/index.test.tsx
@@ -6,6 +6,7 @@
 
 import React from 'react';
 import { MockedProvider } from 'react-apollo/test-utils';
+import useResizeObserver from 'use-resize-observer';
 
 import { wait } from '../../lib/helpers';
 import { mockIndexPattern, TestProviders } from '../../mock';
@@ -26,6 +27,10 @@ mockUseFetchIndexPatterns.mockImplementation(() => [
   },
 ]);
 
+const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock;
+jest.mock('use-resize-observer');
+mockUseResizeObserver.mockImplementation(() => ({}));
+
 const from = 1566943856794;
 const to = 1566857456791;
 
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/__snapshots__/timeline.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/timeline/__snapshots__/timeline.test.tsx.snap
index 67d266d1cbf39..3fcd258b79147 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/__snapshots__/timeline.test.tsx.snap
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/__snapshots__/timeline.test.tsx.snap
@@ -1,10 +1,793 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`Timeline rendering renders correctly against snapshot 1`] = `
-<AutoSizer
-  content={true}
-  detectAnyWindowResize={true}
+<TimelineContainer
+  data-test-subj="timeline"
+  direction="column"
+  gutterSize="none"
+  justifyContent="flexStart"
 >
-  <Component />
-</AutoSizer>
+  <WrappedByAutoSizer>
+    <TimelineHeader
+      browserFields={
+        Object {
+          "agent": Object {
+            "fields": Object {
+              "agent.ephemeral_id": Object {
+                "aggregatable": true,
+                "category": "agent",
+                "description": "Ephemeral identifier of this agent (if one exists). This id normally changes across restarts, but \`agent.id\` does not.",
+                "example": "8a4f500f",
+                "format": "",
+                "indexes": Array [
+                  "auditbeat",
+                  "filebeat",
+                  "packetbeat",
+                ],
+                "name": "agent.ephemeral_id",
+                "searchable": true,
+                "type": "string",
+              },
+              "agent.hostname": Object {
+                "aggregatable": true,
+                "category": "agent",
+                "description": null,
+                "example": null,
+                "format": "",
+                "indexes": Array [
+                  "auditbeat",
+                  "filebeat",
+                  "packetbeat",
+                ],
+                "name": "agent.hostname",
+                "searchable": true,
+                "type": "string",
+              },
+              "agent.id": Object {
+                "aggregatable": true,
+                "category": "agent",
+                "description": "Unique identifier of this agent (if one exists). Example: For Beats this would be beat.id.",
+                "example": "8a4f500d",
+                "format": "",
+                "indexes": Array [
+                  "auditbeat",
+                  "filebeat",
+                  "packetbeat",
+                ],
+                "name": "agent.id",
+                "searchable": true,
+                "type": "string",
+              },
+              "agent.name": Object {
+                "aggregatable": true,
+                "category": "agent",
+                "description": "Name of the agent. This is a name that can be given to an agent. This can be helpful if for example two Filebeat instances are running on the same host but a human readable separation is needed on which Filebeat instance data is coming from. If no name is given, the name is often left empty.",
+                "example": "foo",
+                "format": "",
+                "indexes": Array [
+                  "auditbeat",
+                  "filebeat",
+                  "packetbeat",
+                ],
+                "name": "agent.name",
+                "searchable": true,
+                "type": "string",
+              },
+            },
+          },
+          "auditd": Object {
+            "fields": Object {
+              "auditd.data.a0": Object {
+                "aggregatable": true,
+                "category": "auditd",
+                "description": null,
+                "example": null,
+                "format": "",
+                "indexes": Array [
+                  "auditbeat",
+                ],
+                "name": "auditd.data.a0",
+                "searchable": true,
+                "type": "string",
+              },
+              "auditd.data.a1": Object {
+                "aggregatable": true,
+                "category": "auditd",
+                "description": null,
+                "example": null,
+                "format": "",
+                "indexes": Array [
+                  "auditbeat",
+                ],
+                "name": "auditd.data.a1",
+                "searchable": true,
+                "type": "string",
+              },
+              "auditd.data.a2": Object {
+                "aggregatable": true,
+                "category": "auditd",
+                "description": null,
+                "example": null,
+                "format": "",
+                "indexes": Array [
+                  "auditbeat",
+                ],
+                "name": "auditd.data.a2",
+                "searchable": true,
+                "type": "string",
+              },
+            },
+          },
+          "base": Object {
+            "fields": Object {
+              "@timestamp": Object {
+                "aggregatable": true,
+                "category": "base",
+                "description": "Date/time when the event originated. For log events this is the date/time when the event was generated, and not when it was read. Required field for all events.",
+                "example": "2016-05-23T08:05:34.853Z",
+                "format": "",
+                "indexes": Array [
+                  "auditbeat",
+                  "filebeat",
+                  "packetbeat",
+                ],
+                "name": "@timestamp",
+                "searchable": true,
+                "type": "date",
+              },
+            },
+          },
+          "client": Object {
+            "fields": Object {
+              "client.address": Object {
+                "aggregatable": true,
+                "category": "client",
+                "description": "Some event client addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket.  You should always store the raw address in the \`.address\` field. Then it should be duplicated to \`.ip\` or \`.domain\`, depending on which one it is.",
+                "example": null,
+                "format": "",
+                "indexes": Array [
+                  "auditbeat",
+                  "filebeat",
+                  "packetbeat",
+                ],
+                "name": "client.address",
+                "searchable": true,
+                "type": "string",
+              },
+              "client.bytes": Object {
+                "aggregatable": true,
+                "category": "client",
+                "description": "Bytes sent from the client to the server.",
+                "example": "184",
+                "format": "",
+                "indexes": Array [
+                  "auditbeat",
+                  "filebeat",
+                  "packetbeat",
+                ],
+                "name": "client.bytes",
+                "searchable": true,
+                "type": "number",
+              },
+              "client.domain": Object {
+                "aggregatable": true,
+                "category": "client",
+                "description": "Client domain.",
+                "example": null,
+                "format": "",
+                "indexes": Array [
+                  "auditbeat",
+                  "filebeat",
+                  "packetbeat",
+                ],
+                "name": "client.domain",
+                "searchable": true,
+                "type": "string",
+              },
+              "client.geo.country_iso_code": Object {
+                "aggregatable": true,
+                "category": "client",
+                "description": "Country ISO code.",
+                "example": "CA",
+                "format": "",
+                "indexes": Array [
+                  "auditbeat",
+                  "filebeat",
+                  "packetbeat",
+                ],
+                "name": "client.geo.country_iso_code",
+                "searchable": true,
+                "type": "string",
+              },
+            },
+          },
+          "cloud": Object {
+            "fields": Object {
+              "cloud.account.id": Object {
+                "aggregatable": true,
+                "category": "cloud",
+                "description": "The cloud account or organization id used to identify different entities in a multi-tenant environment. Examples: AWS account id, Google Cloud ORG Id, or other unique identifier.",
+                "example": "666777888999",
+                "format": "",
+                "indexes": Array [
+                  "auditbeat",
+                  "filebeat",
+                  "packetbeat",
+                ],
+                "name": "cloud.account.id",
+                "searchable": true,
+                "type": "string",
+              },
+              "cloud.availability_zone": Object {
+                "aggregatable": true,
+                "category": "cloud",
+                "description": "Availability zone in which this host is running.",
+                "example": "us-east-1c",
+                "format": "",
+                "indexes": Array [
+                  "auditbeat",
+                  "filebeat",
+                  "packetbeat",
+                ],
+                "name": "cloud.availability_zone",
+                "searchable": true,
+                "type": "string",
+              },
+            },
+          },
+          "container": Object {
+            "fields": Object {
+              "container.id": Object {
+                "aggregatable": true,
+                "category": "container",
+                "description": "Unique container id.",
+                "example": null,
+                "format": "",
+                "indexes": Array [
+                  "auditbeat",
+                  "filebeat",
+                  "packetbeat",
+                ],
+                "name": "container.id",
+                "searchable": true,
+                "type": "string",
+              },
+              "container.image.name": Object {
+                "aggregatable": true,
+                "category": "container",
+                "description": "Name of the image the container was built on.",
+                "example": null,
+                "format": "",
+                "indexes": Array [
+                  "auditbeat",
+                  "filebeat",
+                  "packetbeat",
+                ],
+                "name": "container.image.name",
+                "searchable": true,
+                "type": "string",
+              },
+              "container.image.tag": Object {
+                "aggregatable": true,
+                "category": "container",
+                "description": "Container image tag.",
+                "example": null,
+                "format": "",
+                "indexes": Array [
+                  "auditbeat",
+                  "filebeat",
+                  "packetbeat",
+                ],
+                "name": "container.image.tag",
+                "searchable": true,
+                "type": "string",
+              },
+            },
+          },
+          "destination": Object {
+            "fields": Object {
+              "destination.address": Object {
+                "aggregatable": true,
+                "category": "destination",
+                "description": "Some event destination addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket.  You should always store the raw address in the \`.address\` field. Then it should be duplicated to \`.ip\` or \`.domain\`, depending on which one it is.",
+                "example": null,
+                "format": "",
+                "indexes": Array [
+                  "auditbeat",
+                  "filebeat",
+                  "packetbeat",
+                ],
+                "name": "destination.address",
+                "searchable": true,
+                "type": "string",
+              },
+              "destination.bytes": Object {
+                "aggregatable": true,
+                "category": "destination",
+                "description": "Bytes sent from the destination to the source.",
+                "example": "184",
+                "format": "",
+                "indexes": Array [
+                  "auditbeat",
+                  "filebeat",
+                  "packetbeat",
+                ],
+                "name": "destination.bytes",
+                "searchable": true,
+                "type": "number",
+              },
+              "destination.domain": Object {
+                "aggregatable": true,
+                "category": "destination",
+                "description": "Destination domain.",
+                "example": null,
+                "format": "",
+                "indexes": Array [
+                  "auditbeat",
+                  "filebeat",
+                  "packetbeat",
+                ],
+                "name": "destination.domain",
+                "searchable": true,
+                "type": "string",
+              },
+              "destination.ip": Object {
+                "aggregatable": true,
+                "category": "destination",
+                "description": "IP address of the destination. Can be one or multiple IPv4 or IPv6 addresses.",
+                "example": "",
+                "format": "",
+                "indexes": Array [
+                  "auditbeat",
+                  "filebeat",
+                  "packetbeat",
+                ],
+                "name": "destination.ip",
+                "searchable": true,
+                "type": "ip",
+              },
+              "destination.port": Object {
+                "aggregatable": true,
+                "category": "destination",
+                "description": "Port of the destination.",
+                "example": "",
+                "format": "",
+                "indexes": Array [
+                  "auditbeat",
+                  "filebeat",
+                  "packetbeat",
+                ],
+                "name": "destination.port",
+                "searchable": true,
+                "type": "long",
+              },
+            },
+          },
+          "event": Object {
+            "fields": Object {
+              "event.end": Object {
+                "aggregatable": true,
+                "category": "event",
+                "description": "event.end contains the date when the event ended or when the activity was last observed.",
+                "example": null,
+                "format": "",
+                "indexes": Array [
+                  "apm-*-transaction*",
+                  "auditbeat-*",
+                  "endgame-*",
+                  "filebeat-*",
+                  "packetbeat-*",
+                  "winlogbeat-*",
+                ],
+                "name": "event.end",
+                "searchable": true,
+                "type": "date",
+              },
+            },
+          },
+          "source": Object {
+            "fields": Object {
+              "source.ip": Object {
+                "aggregatable": true,
+                "category": "source",
+                "description": "IP address of the source. Can be one or multiple IPv4 or IPv6 addresses.",
+                "example": "",
+                "format": "",
+                "indexes": Array [
+                  "auditbeat",
+                  "filebeat",
+                  "packetbeat",
+                ],
+                "name": "source.ip",
+                "searchable": true,
+                "type": "ip",
+              },
+              "source.port": Object {
+                "aggregatable": true,
+                "category": "source",
+                "description": "Port of the source.",
+                "example": "",
+                "format": "",
+                "indexes": Array [
+                  "auditbeat",
+                  "filebeat",
+                  "packetbeat",
+                ],
+                "name": "source.port",
+                "searchable": true,
+                "type": "long",
+              },
+            },
+          },
+        }
+      }
+      dataProviders={
+        Array [
+          Object {
+            "and": Array [],
+            "enabled": true,
+            "excluded": false,
+            "id": "id-Provider 1",
+            "kqlQuery": "",
+            "name": "Provider 1",
+            "queryMatch": Object {
+              "field": "name",
+              "operator": ":",
+              "value": "Provider 1",
+            },
+          },
+          Object {
+            "and": Array [],
+            "enabled": true,
+            "excluded": false,
+            "id": "id-Provider 2",
+            "kqlQuery": "",
+            "name": "Provider 2",
+            "queryMatch": Object {
+              "field": "name",
+              "operator": ":",
+              "value": "Provider 2",
+            },
+          },
+          Object {
+            "and": Array [],
+            "enabled": true,
+            "excluded": false,
+            "id": "id-Provider 3",
+            "kqlQuery": "",
+            "name": "Provider 3",
+            "queryMatch": Object {
+              "field": "name",
+              "operator": ":",
+              "value": "Provider 3",
+            },
+          },
+          Object {
+            "and": Array [],
+            "enabled": true,
+            "excluded": false,
+            "id": "id-Provider 4",
+            "kqlQuery": "",
+            "name": "Provider 4",
+            "queryMatch": Object {
+              "field": "name",
+              "operator": ":",
+              "value": "Provider 4",
+            },
+          },
+          Object {
+            "and": Array [],
+            "enabled": true,
+            "excluded": false,
+            "id": "id-Provider 5",
+            "kqlQuery": "",
+            "name": "Provider 5",
+            "queryMatch": Object {
+              "field": "name",
+              "operator": ":",
+              "value": "Provider 5",
+            },
+          },
+          Object {
+            "and": Array [],
+            "enabled": true,
+            "excluded": false,
+            "id": "id-Provider 6",
+            "kqlQuery": "",
+            "name": "Provider 6",
+            "queryMatch": Object {
+              "field": "name",
+              "operator": ":",
+              "value": "Provider 6",
+            },
+          },
+          Object {
+            "and": Array [],
+            "enabled": true,
+            "excluded": false,
+            "id": "id-Provider 7",
+            "kqlQuery": "",
+            "name": "Provider 7",
+            "queryMatch": Object {
+              "field": "name",
+              "operator": ":",
+              "value": "Provider 7",
+            },
+          },
+          Object {
+            "and": Array [],
+            "enabled": true,
+            "excluded": false,
+            "id": "id-Provider 8",
+            "kqlQuery": "",
+            "name": "Provider 8",
+            "queryMatch": Object {
+              "field": "name",
+              "operator": ":",
+              "value": "Provider 8",
+            },
+          },
+          Object {
+            "and": Array [],
+            "enabled": true,
+            "excluded": false,
+            "id": "id-Provider 9",
+            "kqlQuery": "",
+            "name": "Provider 9",
+            "queryMatch": Object {
+              "field": "name",
+              "operator": ":",
+              "value": "Provider 9",
+            },
+          },
+          Object {
+            "and": Array [],
+            "enabled": true,
+            "excluded": false,
+            "id": "id-Provider 10",
+            "kqlQuery": "",
+            "name": "Provider 10",
+            "queryMatch": Object {
+              "field": "name",
+              "operator": ":",
+              "value": "Provider 10",
+            },
+          },
+        ]
+      }
+      id="foo"
+      indexPattern={
+        Object {
+          "fields": Array [
+            Object {
+              "aggregatable": true,
+              "name": "@timestamp",
+              "searchable": true,
+              "type": "date",
+            },
+            Object {
+              "aggregatable": true,
+              "name": "@version",
+              "searchable": true,
+              "type": "string",
+            },
+            Object {
+              "aggregatable": true,
+              "name": "agent.ephemeral_id",
+              "searchable": true,
+              "type": "string",
+            },
+            Object {
+              "aggregatable": true,
+              "name": "agent.hostname",
+              "searchable": true,
+              "type": "string",
+            },
+            Object {
+              "aggregatable": true,
+              "name": "agent.id",
+              "searchable": true,
+              "type": "string",
+            },
+            Object {
+              "aggregatable": true,
+              "name": "agent.test1",
+              "searchable": true,
+              "type": "string",
+            },
+            Object {
+              "aggregatable": true,
+              "name": "agent.test2",
+              "searchable": true,
+              "type": "string",
+            },
+            Object {
+              "aggregatable": true,
+              "name": "agent.test3",
+              "searchable": true,
+              "type": "string",
+            },
+            Object {
+              "aggregatable": true,
+              "name": "agent.test4",
+              "searchable": true,
+              "type": "string",
+            },
+            Object {
+              "aggregatable": true,
+              "name": "agent.test5",
+              "searchable": true,
+              "type": "string",
+            },
+            Object {
+              "aggregatable": true,
+              "name": "agent.test6",
+              "searchable": true,
+              "type": "string",
+            },
+            Object {
+              "aggregatable": true,
+              "name": "agent.test7",
+              "searchable": true,
+              "type": "string",
+            },
+            Object {
+              "aggregatable": true,
+              "name": "agent.test8",
+              "searchable": true,
+              "type": "string",
+            },
+            Object {
+              "aggregatable": true,
+              "name": "host.name",
+              "searchable": true,
+              "type": "string",
+            },
+          ],
+          "title": "filebeat-*,auditbeat-*,packetbeat-*",
+        }
+      }
+      onChangeDataProviderKqlQuery={[MockFunction]}
+      onChangeDroppableAndProvider={[MockFunction]}
+      onDataProviderEdited={[MockFunction]}
+      onDataProviderRemoved={[MockFunction]}
+      onToggleDataProviderEnabled={[MockFunction]}
+      onToggleDataProviderExcluded={[MockFunction]}
+      show={true}
+      showCallOutUnauthorizedMsg={false}
+      sort={
+        Object {
+          "columnId": "@timestamp",
+          "sortDirection": "desc",
+        }
+      }
+    />
+  </WrappedByAutoSizer>
+  <Connect(Component)
+    id="foo"
+    indexPattern={
+      Object {
+        "fields": Array [
+          Object {
+            "aggregatable": true,
+            "name": "@timestamp",
+            "searchable": true,
+            "type": "date",
+          },
+          Object {
+            "aggregatable": true,
+            "name": "@version",
+            "searchable": true,
+            "type": "string",
+          },
+          Object {
+            "aggregatable": true,
+            "name": "agent.ephemeral_id",
+            "searchable": true,
+            "type": "string",
+          },
+          Object {
+            "aggregatable": true,
+            "name": "agent.hostname",
+            "searchable": true,
+            "type": "string",
+          },
+          Object {
+            "aggregatable": true,
+            "name": "agent.id",
+            "searchable": true,
+            "type": "string",
+          },
+          Object {
+            "aggregatable": true,
+            "name": "agent.test1",
+            "searchable": true,
+            "type": "string",
+          },
+          Object {
+            "aggregatable": true,
+            "name": "agent.test2",
+            "searchable": true,
+            "type": "string",
+          },
+          Object {
+            "aggregatable": true,
+            "name": "agent.test3",
+            "searchable": true,
+            "type": "string",
+          },
+          Object {
+            "aggregatable": true,
+            "name": "agent.test4",
+            "searchable": true,
+            "type": "string",
+          },
+          Object {
+            "aggregatable": true,
+            "name": "agent.test5",
+            "searchable": true,
+            "type": "string",
+          },
+          Object {
+            "aggregatable": true,
+            "name": "agent.test6",
+            "searchable": true,
+            "type": "string",
+          },
+          Object {
+            "aggregatable": true,
+            "name": "agent.test7",
+            "searchable": true,
+            "type": "string",
+          },
+          Object {
+            "aggregatable": true,
+            "name": "agent.test8",
+            "searchable": true,
+            "type": "string",
+          },
+          Object {
+            "aggregatable": true,
+            "name": "host.name",
+            "searchable": true,
+            "type": "string",
+          },
+        ],
+        "title": "filebeat-*,auditbeat-*,packetbeat-*",
+      }
+    }
+    inputId="timeline"
+  />
+  <Connect(Component)
+    eventType="raw"
+    fields={
+      Array [
+        "@timestamp",
+        "event.severity",
+        "event.category",
+        "event.action",
+        "host.name",
+        "source.ip",
+        "destination.ip",
+        "destination.bytes",
+        "user.name",
+        "_id",
+        "message",
+      ]
+    }
+    filterQuery="{\\"bool\\":{\\"must\\":[],\\"filter\\":[{\\"bool\\":{\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 1\\"}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 2\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 3\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 4\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 5\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 6\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 7\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 8\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 9\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 10\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"range\\":{\\"@timestamp\\":{\\"gte\\":1521830963132}}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"range\\":{\\"@timestamp\\":{\\"lte\\":1521862432253}}}],\\"minimum_should_match\\":1}}]}}]}}],\\"should\\":[],\\"must_not\\":[]}}"
+    id="foo"
+    indexToAdd={Array []}
+    limit={5}
+    sortField={
+      Object {
+        "direction": "desc",
+        "sortFieldId": "@timestamp",
+      }
+    }
+    sourceId="default"
+  >
+    <Component />
+  </Connect(Component)>
+</TimelineContainer>
 `;
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.test.tsx
index f7c0d0b475734..78899b7c5d628 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.test.tsx
@@ -7,6 +7,7 @@
 import { shallow } from 'enzyme';
 import React from 'react';
 import { MockedProvider } from 'react-apollo/test-utils';
+import useResizeObserver from 'use-resize-observer';
 
 import { timelineQuery } from '../../containers/timeline/index.gql_query';
 import { mockBrowserFields } from '../../containers/source/mock';
@@ -29,6 +30,10 @@ const testFlyoutHeight = 980;
 
 jest.mock('../../lib/kibana');
 
+const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock;
+jest.mock('use-resize-observer');
+mockUseResizeObserver.mockImplementation(() => ({}));
+
 describe('Timeline', () => {
   const sort: Sort = {
     columnId: '@timestamp',
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx
index 09457c8f0285a..4b7331ab14c7e 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx
@@ -8,13 +8,13 @@ import { EuiFlexGroup } from '@elastic/eui';
 import { getOr, isEmpty } from 'lodash/fp';
 import React from 'react';
 import styled from 'styled-components';
+import useResizeObserver from 'use-resize-observer';
 
 import { BrowserFields } from '../../containers/source';
 import { TimelineQuery } from '../../containers/timeline';
 import { Direction } from '../../graphql/types';
 import { useKibana } from '../../lib/kibana';
 import { KqlMode, EventType } from '../../store/timeline/model';
-import { AutoSizer } from '../auto_sizer';
 import { ColumnHeader } from './body/column_headers/column_header';
 import { defaultHeaders } from './body/column_headers/default_headers';
 import { Sort } from './body/sort';
@@ -88,7 +88,7 @@ interface Props {
 }
 
 /** The parent Timeline component */
-export const TimelineComponent = ({
+export const TimelineComponent: React.FC<Props> = ({
   browserFields,
   columns,
   dataProviders,
@@ -118,7 +118,10 @@ export const TimelineComponent = ({
   start,
   sort,
   toggleColumn,
-}: Props) => {
+}) => {
+  const { ref: measureRef, width = 0, height: timelineHeaderHeight = 0 } = useResizeObserver<
+    HTMLDivElement
+  >({});
   const kibana = useKibana();
   const combinedQueries = combineQueries({
     config: esQuery.getEsQueryConfig(kibana.services.uiSettings),
@@ -132,101 +135,98 @@ export const TimelineComponent = ({
     end,
   });
   const columnsHeader = isEmpty(columns) ? defaultHeaders : columns;
+
   return (
-    <AutoSizer detectAnyWindowResize={true} content>
-      {({ measureRef, content: { height: timelineHeaderHeight = 0, width = 0 } }) => (
-        <TimelineContainer
-          data-test-subj="timeline"
-          direction="column"
-          gutterSize="none"
-          justifyContent="flexStart"
+    <TimelineContainer
+      data-test-subj="timeline"
+      direction="column"
+      gutterSize="none"
+      justifyContent="flexStart"
+    >
+      <WrappedByAutoSizer ref={measureRef as React.RefObject<HTMLDivElement>}>
+        <TimelineHeader
+          browserFields={browserFields}
+          id={id}
+          indexPattern={indexPattern}
+          dataProviders={dataProviders}
+          onChangeDataProviderKqlQuery={onChangeDataProviderKqlQuery}
+          onChangeDroppableAndProvider={onChangeDroppableAndProvider}
+          onDataProviderEdited={onDataProviderEdited}
+          onDataProviderRemoved={onDataProviderRemoved}
+          onToggleDataProviderEnabled={onToggleDataProviderEnabled}
+          onToggleDataProviderExcluded={onToggleDataProviderExcluded}
+          show={show}
+          showCallOutUnauthorizedMsg={showCallOutUnauthorizedMsg}
+          sort={sort}
+        />
+      </WrappedByAutoSizer>
+      <TimelineKqlFetch id={id} indexPattern={indexPattern} inputId="timeline" />
+      {combinedQueries != null ? (
+        <TimelineQuery
+          eventType={eventType}
+          id={id}
+          indexToAdd={indexToAdd}
+          fields={columnsHeader.map(c => c.id)}
+          sourceId="default"
+          limit={itemsPerPage}
+          filterQuery={combinedQueries.filterQuery}
+          sortField={{
+            sortFieldId: sort.columnId,
+            direction: sort.sortDirection as Direction,
+          }}
         >
-          <WrappedByAutoSizer ref={measureRef}>
-            <TimelineHeader
-              browserFields={browserFields}
-              id={id}
-              indexPattern={indexPattern}
-              dataProviders={dataProviders}
-              onChangeDataProviderKqlQuery={onChangeDataProviderKqlQuery}
-              onChangeDroppableAndProvider={onChangeDroppableAndProvider}
-              onDataProviderEdited={onDataProviderEdited}
-              onDataProviderRemoved={onDataProviderRemoved}
-              onToggleDataProviderEnabled={onToggleDataProviderEnabled}
-              onToggleDataProviderExcluded={onToggleDataProviderExcluded}
-              show={show}
-              showCallOutUnauthorizedMsg={showCallOutUnauthorizedMsg}
-              sort={sort}
-            />
-          </WrappedByAutoSizer>
-          <TimelineKqlFetch id={id} indexPattern={indexPattern} inputId="timeline" />
-          {combinedQueries != null ? (
-            <TimelineQuery
-              eventType={eventType}
-              id={id}
-              indexToAdd={indexToAdd}
-              fields={columnsHeader.map(c => c.id)}
-              sourceId="default"
-              limit={itemsPerPage}
-              filterQuery={combinedQueries.filterQuery}
-              sortField={{
-                sortFieldId: sort.columnId,
-                direction: sort.sortDirection as Direction,
-              }}
-            >
-              {({
-                events,
-                inspect,
-                loading,
-                totalCount,
-                pageInfo,
-                loadMore,
-                getUpdatedAt,
-                refetch,
-              }) => (
-                <ManageTimelineContext loading={loading || loadingIndexName} width={width}>
-                  <TimelineRefetch
-                    id={id}
-                    inputId="timeline"
-                    inspect={inspect}
-                    loading={loading}
-                    refetch={refetch}
-                  />
-                  <StatefulBody
-                    browserFields={browserFields}
-                    data={events}
-                    id={id}
-                    height={calculateBodyHeight({
-                      flyoutHeight,
-                      flyoutHeaderHeight,
-                      timelineHeaderHeight,
-                      timelineFooterHeight: footerHeight,
-                    })}
-                    sort={sort}
-                    toggleColumn={toggleColumn}
-                  />
-                  <Footer
-                    serverSideEventCount={totalCount}
-                    hasNextPage={getOr(false, 'hasNextPage', pageInfo)!}
-                    height={footerHeight}
-                    isLive={isLive}
-                    isLoading={loading || loadingIndexName}
-                    itemsCount={events.length}
-                    itemsPerPage={itemsPerPage}
-                    itemsPerPageOptions={itemsPerPageOptions}
-                    onChangeItemsPerPage={onChangeItemsPerPage}
-                    onLoadMore={loadMore}
-                    nextCursor={getOr(null, 'endCursor.value', pageInfo)!}
-                    tieBreaker={getOr(null, 'endCursor.tiebreaker', pageInfo)}
-                    getUpdatedAt={getUpdatedAt}
-                    compact={isCompactFooter(width)}
-                  />
-                </ManageTimelineContext>
-              )}
-            </TimelineQuery>
-          ) : null}
-        </TimelineContainer>
-      )}
-    </AutoSizer>
+          {({
+            events,
+            inspect,
+            loading,
+            totalCount,
+            pageInfo,
+            loadMore,
+            getUpdatedAt,
+            refetch,
+          }) => (
+            <ManageTimelineContext loading={loading || loadingIndexName} width={width}>
+              <TimelineRefetch
+                id={id}
+                inputId="timeline"
+                inspect={inspect}
+                loading={loading}
+                refetch={refetch}
+              />
+              <StatefulBody
+                browserFields={browserFields}
+                data={events}
+                id={id}
+                height={calculateBodyHeight({
+                  flyoutHeight,
+                  flyoutHeaderHeight,
+                  timelineHeaderHeight,
+                  timelineFooterHeight: footerHeight,
+                })}
+                sort={sort}
+                toggleColumn={toggleColumn}
+              />
+              <Footer
+                serverSideEventCount={totalCount}
+                hasNextPage={getOr(false, 'hasNextPage', pageInfo)!}
+                height={footerHeight}
+                isLive={isLive}
+                isLoading={loading || loadingIndexName}
+                itemsCount={events.length}
+                itemsPerPage={itemsPerPage}
+                itemsPerPageOptions={itemsPerPageOptions}
+                onChangeItemsPerPage={onChangeItemsPerPage}
+                onLoadMore={loadMore}
+                nextCursor={getOr(null, 'endCursor.value', pageInfo)!}
+                tieBreaker={getOr(null, 'endCursor.tiebreaker', pageInfo)}
+                getUpdatedAt={getUpdatedAt}
+                compact={isCompactFooter(width)}
+              />
+            </ManageTimelineContext>
+          )}
+        </TimelineQuery>
+      ) : null}
+    </TimelineContainer>
   );
 };
 
diff --git a/x-pack/legacy/plugins/siem/public/pages/home/index.tsx b/x-pack/legacy/plugins/siem/public/pages/home/index.tsx
index 1dce26b7c5d3a..4608079167ed5 100644
--- a/x-pack/legacy/plugins/siem/public/pages/home/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/home/index.tsx
@@ -7,8 +7,8 @@
 import React from 'react';
 import { Redirect, Route, Switch } from 'react-router-dom';
 import styled from 'styled-components';
+import useResizeObserver from 'use-resize-observer';
 
-import { AutoSizer } from '../../components/auto_sizer';
 import { DragDropContextWrapper } from '../../components/drag_and_drop/drag_drop_context_wrapper';
 import { Flyout, flyoutHeaderHeight } from '../../components/flyout';
 import { HeaderGlobal } from '../../components/header_global';
@@ -61,96 +61,91 @@ const calculateFlyoutHeight = ({
   windowHeight: number;
 }): number => Math.max(0, windowHeight - globalHeaderSize);
 
-export const HomePage: React.FC = () => (
-  <AutoSizer detectAnyWindowResize={true} content>
-    {({ measureRef, windowMeasurement: { height: windowHeight = 0 } }) => (
-      <WrappedByAutoSizer data-test-subj="wrapped-by-auto-sizer" ref={measureRef}>
-        <HeaderGlobal />
+export const HomePage: React.FC = () => {
+  const { ref: measureRef, height: windowHeight = 0 } = useResizeObserver<HTMLDivElement>({});
+  const flyoutHeight = calculateFlyoutHeight({
+    globalHeaderSize: globalHeaderHeightPx,
+    windowHeight,
+  });
 
-        <Main data-test-subj="pageContainer">
-          <WithSource sourceId="default">
-            {({ browserFields, indexPattern, indicesExist }) => (
-              <DragDropContextWrapper browserFields={browserFields}>
-                <UseUrlState indexPattern={indexPattern} navTabs={navTabs} />
-                {indicesExistOrDataTemporarilyUnavailable(indicesExist) && (
-                  <>
-                    <AutoSaveWarningMsg />
-                    <Flyout
-                      flyoutHeight={calculateFlyoutHeight({
-                        globalHeaderSize: globalHeaderHeightPx,
-                        windowHeight,
-                      })}
-                      headerHeight={flyoutHeaderHeight}
-                      timelineId="timeline-1"
-                      usersViewing={usersViewing}
-                    >
-                      <StatefulTimeline
-                        flyoutHeaderHeight={flyoutHeaderHeight}
-                        flyoutHeight={calculateFlyoutHeight({
-                          globalHeaderSize: globalHeaderHeightPx,
-                          windowHeight,
-                        })}
-                        id="timeline-1"
-                      />
-                    </Flyout>
-                  </>
-                )}
+  return (
+    <WrappedByAutoSizer data-test-subj="wrapped-by-auto-sizer" ref={measureRef}>
+      <HeaderGlobal />
 
-                <Switch>
-                  <Redirect exact from="/" to={`/${SiemPageName.overview}`} />
-                  <Route
-                    path={`/:pageName(${SiemPageName.overview})`}
-                    render={() => <Overview />}
-                  />
-                  <Route
-                    path={`/:pageName(${SiemPageName.hosts})`}
-                    render={({ match }) => <HostsContainer url={match.url} />}
-                  />
-                  <Route
-                    path={`/:pageName(${SiemPageName.network})`}
-                    render={({ location, match }) => (
-                      <NetworkContainer location={location} url={match.url} />
-                    )}
-                  />
-                  <Route
-                    path={`/:pageName(${SiemPageName.detections})`}
-                    render={({ location, match }) => (
-                      <DetectionEngineContainer location={location} url={match.url} />
-                    )}
-                  />
-                  <Route
-                    path={`/:pageName(${SiemPageName.timelines})`}
-                    render={() => <Timelines />}
-                  />
-                  <Route path="/link-to" render={props => <LinkToPage {...props} />} />
-                  <Route
-                    path="/ml-hosts"
-                    render={({ location, match }) => (
-                      <MlHostConditionalContainer location={location} url={match.url} />
-                    )}
-                  />
-                  <Route
-                    path="/ml-network"
-                    render={({ location, match }) => (
-                      <MlNetworkConditionalContainer location={location} url={match.url} />
-                    )}
-                  />
-                  <Route path={`/:pageName(${SiemPageName.case})`}>
-                    <Case />
-                  </Route>
-                  <Route render={() => <NotFoundPage />} />
-                </Switch>
-              </DragDropContextWrapper>
-            )}
-          </WithSource>
-        </Main>
+      <Main data-test-subj="pageContainer">
+        <WithSource sourceId="default">
+          {({ browserFields, indexPattern, indicesExist }) => (
+            <DragDropContextWrapper browserFields={browserFields}>
+              <UseUrlState indexPattern={indexPattern} navTabs={navTabs} />
+              {indicesExistOrDataTemporarilyUnavailable(indicesExist) && (
+                <>
+                  <AutoSaveWarningMsg />
+                  <Flyout
+                    flyoutHeight={flyoutHeight}
+                    headerHeight={flyoutHeaderHeight}
+                    timelineId="timeline-1"
+                    usersViewing={usersViewing}
+                  >
+                    <StatefulTimeline
+                      flyoutHeaderHeight={flyoutHeaderHeight}
+                      flyoutHeight={flyoutHeight}
+                      id="timeline-1"
+                    />
+                  </Flyout>
+                </>
+              )}
 
-        <HelpMenu />
+              <Switch>
+                <Redirect exact from="/" to={`/${SiemPageName.overview}`} />
+                <Route path={`/:pageName(${SiemPageName.overview})`} render={() => <Overview />} />
+                <Route
+                  path={`/:pageName(${SiemPageName.hosts})`}
+                  render={({ match }) => <HostsContainer url={match.url} />}
+                />
+                <Route
+                  path={`/:pageName(${SiemPageName.network})`}
+                  render={({ location, match }) => (
+                    <NetworkContainer location={location} url={match.url} />
+                  )}
+                />
+                <Route
+                  path={`/:pageName(${SiemPageName.detections})`}
+                  render={({ location, match }) => (
+                    <DetectionEngineContainer location={location} url={match.url} />
+                  )}
+                />
+                <Route
+                  path={`/:pageName(${SiemPageName.timelines})`}
+                  render={() => <Timelines />}
+                />
+                <Route path="/link-to" render={props => <LinkToPage {...props} />} />
+                <Route
+                  path="/ml-hosts"
+                  render={({ location, match }) => (
+                    <MlHostConditionalContainer location={location} url={match.url} />
+                  )}
+                />
+                <Route
+                  path="/ml-network"
+                  render={({ location, match }) => (
+                    <MlNetworkConditionalContainer location={location} url={match.url} />
+                  )}
+                />
+                <Route path={`/:pageName(${SiemPageName.case})`}>
+                  <Case />
+                </Route>
+                <Route render={() => <NotFoundPage />} />
+              </Switch>
+            </DragDropContextWrapper>
+          )}
+        </WithSource>
+      </Main>
 
-        <SpyRoute />
-      </WrappedByAutoSizer>
-    )}
-  </AutoSizer>
-);
+      <HelpMenu />
+
+      <SpyRoute />
+    </WrappedByAutoSizer>
+  );
+};
 
 HomePage.displayName = 'HomePage';
diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.test.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.test.tsx
index 30955ed8ccb57..bdc0f79e96591 100644
--- a/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.test.tsx
@@ -7,6 +7,7 @@
 import React from 'react';
 import { IIndexPattern } from 'src/plugins/data/public';
 import { MemoryRouter } from 'react-router-dom';
+import useResizeObserver from 'use-resize-observer';
 
 import { mockIndexPattern } from '../../../mock/index_pattern';
 import { TestProviders } from '../../../mock/test_providers';
@@ -35,6 +36,10 @@ jest.mock('../../../components/query_bar', () => ({
   QueryBar: () => null,
 }));
 
+const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock;
+jest.mock('use-resize-observer');
+mockUseResizeObserver.mockImplementation(() => ({}));
+
 describe('body', () => {
   const scenariosMap = {
     authentications: 'AuthenticationsQueryTabBody',
diff --git a/x-pack/package.json b/x-pack/package.json
index 37791c255aee9..305aaa1d9457b 100644
--- a/x-pack/package.json
+++ b/x-pack/package.json
@@ -102,6 +102,7 @@
     "@types/supertest": "^2.0.5",
     "@types/tar-fs": "^1.16.1",
     "@types/tinycolor2": "^1.4.1",
+    "@types/use-resize-observer": "^6.0.0",
     "@types/uuid": "^3.4.4",
     "@types/xml-crypto": "^1.4.0",
     "@types/xml2js": "^0.4.5",
@@ -344,6 +345,7 @@
     "typescript-fsa-reducers": "^1.2.1",
     "ui-select": "0.19.8",
     "unstated": "^2.1.1",
+    "use-resize-observer": "^6.0.0",
     "uuid": "3.3.2",
     "venn.js": "0.2.20",
     "vscode-languageserver": "^5.2.1",
diff --git a/yarn.lock b/yarn.lock
index 27967aefa20c1..587dff16ffe58 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5337,6 +5337,13 @@
   resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e"
   integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==
 
+"@types/use-resize-observer@^6.0.0":
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/@types/use-resize-observer/-/use-resize-observer-6.0.0.tgz#d1b162b2733b22225908a7877ca7115c67f3752e"
+  integrity sha512-8RD06szR+wzHpfCBFbcTEQ0OCoogoSWCyyUmWyqc5qGG2fa1sOUdwNte5dwoJWG/sh5sBU0QVZ1+9zcQGLUSVg==
+  dependencies:
+    "@types/react" "*"
+
 "@types/uuid@^3.4.4":
   version "3.4.4"
   resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-3.4.4.tgz#7af69360fa65ef0decb41fd150bf4ca5c0cefdf5"
@@ -30379,6 +30386,13 @@ use-memo-one@^1.1.1:
   resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.1.tgz#39e6f08fe27e422a7d7b234b5f9056af313bd22c"
   integrity sha512-oFfsyun+bP7RX8X2AskHNTxu+R3QdE/RC5IefMbqptmACAA/gfol1KDD5KRzPsGMa62sWxGZw+Ui43u6x4ddoQ==
 
+use-resize-observer@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/use-resize-observer/-/use-resize-observer-6.0.0.tgz#8450ade735c386e8d93cdf983c26d34a9f478a54"
+  integrity sha512-Zfe1qsZVhzfgECs+L/ZcSukyVPFGOmWtC7xZI5Lpn4PR2hNgVvD1NmQ/GYBoCSV2pJskxflJWcDzpTAt84nhvw==
+  dependencies:
+    resize-observer-polyfill "^1.5.1"
+
 use@^2.0.0:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/use/-/use-2.0.2.tgz#ae28a0d72f93bf22422a18a2e379993112dec8e8"

From 6aec465208aaa11e0f5e3f1a0760f76fb5002198 Mon Sep 17 00:00:00 2001
From: Vadim Dalecky <streamich@users.noreply.github.com>
Date: Tue, 18 Feb 2020 14:56:20 +0100
Subject: [PATCH 017/174] Expression forks (#57491)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat: 🎸 add .fork() methods to ExpressionsService

* feat: 🎸 expose .fork() method in plugin contract
---
 .../expressions/common/executor/executor.ts   | 30 +++++++
 .../service/expressions_services.test.ts      | 79 +++++++++++++++++++
 .../common/service/expressions_services.ts    | 37 ++++++++-
 src/plugins/expressions/public/mocks.tsx      |  1 +
 src/plugins/expressions/public/plugin.test.ts |  8 ++
 5 files changed, 152 insertions(+), 3 deletions(-)

diff --git a/src/plugins/expressions/common/executor/executor.ts b/src/plugins/expressions/common/executor/executor.ts
index 24c1648a8cd0f..af3662d13de4e 100644
--- a/src/plugins/expressions/common/executor/executor.ts
+++ b/src/plugins/expressions/common/executor/executor.ts
@@ -209,4 +209,34 @@ export class Executor<Context extends Record<string, unknown> = Record<string, u
 
     return execution;
   }
+
+  public fork(): Executor<Context> {
+    const initialState = this.state.get();
+    const fork = new Executor<Context>(initialState);
+
+    /**
+     * Synchronize registry state - make any new types, functions and context
+     * also available in the forked instance of `Executor`.
+     */
+    this.state.state$.subscribe(({ types, functions, context }) => {
+      const state = fork.state.get();
+      fork.state.set({
+        ...state,
+        types: {
+          ...types,
+          ...state.types,
+        },
+        functions: {
+          ...functions,
+          ...state.functions,
+        },
+        context: {
+          ...context,
+          ...state.context,
+        },
+      });
+    });
+
+    return fork;
+  }
 }
diff --git a/src/plugins/expressions/common/service/expressions_services.test.ts b/src/plugins/expressions/common/service/expressions_services.test.ts
index c9687192481c6..6028e4a4cd1df 100644
--- a/src/plugins/expressions/common/service/expressions_services.test.ts
+++ b/src/plugins/expressions/common/service/expressions_services.test.ts
@@ -51,4 +51,83 @@ describe('ExpressionsService', () => {
 
     expect(typeof expressions.setup().getFunctions().var_set).toBe('object');
   });
+
+  describe('.fork()', () => {
+    test('returns a new ExpressionsService instance', () => {
+      const service = new ExpressionsService();
+      const fork = service.fork();
+
+      expect(fork).not.toBe(service);
+      expect(fork).toBeInstanceOf(ExpressionsService);
+    });
+
+    test('fork keeps all types of the origin service', () => {
+      const service = new ExpressionsService();
+      const fork = service.fork();
+
+      expect(fork.executor.state.get().types).toEqual(service.executor.state.get().types);
+    });
+
+    test('fork keeps all functions of the origin service', () => {
+      const service = new ExpressionsService();
+      const fork = service.fork();
+
+      expect(fork.executor.state.get().functions).toEqual(service.executor.state.get().functions);
+    });
+
+    test('fork keeps context of the origin service', () => {
+      const service = new ExpressionsService();
+      const fork = service.fork();
+
+      expect(fork.executor.state.get().context).toEqual(service.executor.state.get().context);
+    });
+
+    test('newly registered functions in origin are also available in fork', () => {
+      const service = new ExpressionsService();
+      const fork = service.fork();
+
+      service.registerFunction({
+        name: '__test__',
+        args: {},
+        help: '',
+        fn: () => {},
+      });
+
+      expect(fork.executor.state.get().functions).toEqual(service.executor.state.get().functions);
+    });
+
+    test('newly registered functions in fork are NOT available in origin', () => {
+      const service = new ExpressionsService();
+      const fork = service.fork();
+
+      fork.registerFunction({
+        name: '__test__',
+        args: {},
+        help: '',
+        fn: () => {},
+      });
+
+      expect(Object.values(fork.executor.state.get().functions)).toHaveLength(
+        Object.values(service.executor.state.get().functions).length + 1
+      );
+    });
+
+    test('fork can execute an expression with newly registered function', async () => {
+      const service = new ExpressionsService();
+      const fork = service.fork();
+
+      service.registerFunction({
+        name: '__test__',
+        args: {},
+        help: '',
+        fn: () => {
+          return '123';
+        },
+      });
+
+      const result = await fork.run('__test__', null);
+
+      expect(result).toBe('123');
+    });
+  });
 });
diff --git a/src/plugins/expressions/common/service/expressions_services.ts b/src/plugins/expressions/common/service/expressions_services.ts
index 9663c05f0d7c2..94019aa62841e 100644
--- a/src/plugins/expressions/common/service/expressions_services.ts
+++ b/src/plugins/expressions/common/service/expressions_services.ts
@@ -25,6 +25,11 @@ import { ExecutionContract } from '../execution/execution_contract';
 export type ExpressionsServiceSetup = ReturnType<ExpressionsService['setup']>;
 export type ExpressionsServiceStart = ReturnType<ExpressionsService['start']>;
 
+export interface ExpressionServiceParams {
+  executor?: Executor;
+  renderers?: ExpressionRendererRegistry;
+}
+
 /**
  * `ExpressionsService` class is used for multiple purposes:
  *
@@ -45,8 +50,16 @@ export type ExpressionsServiceStart = ReturnType<ExpressionsService['start']>;
  *    so that JSDoc appears in developers IDE when they use those `plugins.expressions.registerFunction(`.
  */
 export class ExpressionsService {
-  public readonly executor = Executor.createWithDefaults();
-  public readonly renderers = new ExpressionRendererRegistry();
+  public readonly executor: Executor;
+  public readonly renderers: ExpressionRendererRegistry;
+
+  constructor({
+    executor = Executor.createWithDefaults(),
+    renderers = new ExpressionRendererRegistry(),
+  }: ExpressionServiceParams = {}) {
+    this.executor = executor;
+    this.renderers = renderers;
+  }
 
   /**
    * Register an expression function, which will be possible to execute as
@@ -118,6 +131,23 @@ export class ExpressionsService {
     context?: ExtraContext
   ): Promise<Output> => this.executor.run<Input, Output, ExtraContext>(ast, input, context);
 
+  /**
+   * Create a new instance of `ExpressionsService`. The new instance inherits
+   * all state of the original `ExpressionsService`, including all expression
+   * types, expression functions and context. Also, all new types and functions
+   * registered in the original services AFTER the forking event will be
+   * available in the forked instance. However, all new types and functions
+   * registered in the forked instances will NOT be available to the original
+   * service.
+   */
+  public readonly fork = (): ExpressionsService => {
+    const executor = this.executor.fork();
+    const renderers = this.renderers;
+    const fork = new ExpressionsService({ executor, renderers });
+
+    return fork;
+  };
+
   /**
    * Starts expression execution and immediately returns `ExecutionContract`
    * instance that tracks the progress of the execution and can be used to
@@ -139,7 +169,7 @@ export class ExpressionsService {
   };
 
   public setup() {
-    const { executor, renderers, registerFunction, run } = this;
+    const { executor, renderers, registerFunction, run, fork } = this;
 
     const getFunction = executor.getFunction.bind(executor);
     const getFunctions = executor.getFunctions.bind(executor);
@@ -151,6 +181,7 @@ export class ExpressionsService {
     const registerType = executor.registerType.bind(executor);
 
     return {
+      fork,
       getFunction,
       getFunctions,
       getRenderer,
diff --git a/src/plugins/expressions/public/mocks.tsx b/src/plugins/expressions/public/mocks.tsx
index 40ae698bc95eb..eabc4034e7430 100644
--- a/src/plugins/expressions/public/mocks.tsx
+++ b/src/plugins/expressions/public/mocks.tsx
@@ -31,6 +31,7 @@ export type Start = jest.Mocked<ExpressionsStart>;
 
 const createSetupContract = (): Setup => {
   const setupContract: Setup = {
+    fork: jest.fn(),
     getFunction: jest.fn(),
     getFunctions: jest.fn(),
     getRenderer: jest.fn(),
diff --git a/src/plugins/expressions/public/plugin.test.ts b/src/plugins/expressions/public/plugin.test.ts
index fdfd583eac9de..ac9a2f508e2db 100644
--- a/src/plugins/expressions/public/plugin.test.ts
+++ b/src/plugins/expressions/public/plugin.test.ts
@@ -19,6 +19,7 @@
 
 import { expressionsPluginMock } from './mocks';
 import { add } from '../common/test_helpers/expression_functions/add';
+import { ExpressionsService } from '../common';
 
 describe('ExpressionsPublicPlugin', () => {
   test('can instantiate from mocks', async () => {
@@ -27,6 +28,13 @@ describe('ExpressionsPublicPlugin', () => {
   });
 
   describe('setup contract', () => {
+    test('.fork() method returns ExpressionsService', async () => {
+      const { setup } = await expressionsPluginMock.createPlugin();
+      const fork = setup.fork();
+
+      expect(fork).toBeInstanceOf(ExpressionsService);
+    });
+
     describe('.registerFunction()', () => {
       test('can register a function', async () => {
         const { setup } = await expressionsPluginMock.createPlugin();

From c8f9fca8da923ca87e505fdb068feb24e60c6a6d Mon Sep 17 00:00:00 2001
From: MadameSheema <snootchie.boochies@gmail.com>
Date: Tue, 18 Feb 2020 15:37:21 +0100
Subject: [PATCH 018/174] 'search or filter' test refactor (#57826)

---
 .../timeline/search_or_filter.spec.ts         | 23 +++++++++----------
 1 file changed, 11 insertions(+), 12 deletions(-)

diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/search_or_filter.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/search_or_filter.spec.ts
index 9f21b4e3d53a1..28cc4a6e8827d 100644
--- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/search_or_filter.spec.ts
+++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/search_or_filter.spec.ts
@@ -4,14 +4,11 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import {
-  assertAtLeastOneEventMatchesSearch,
-  executeKQL,
-  hostExistsQuery,
-  toggleTimelineVisibility,
-} from '../../lib/timeline/helpers';
-import { HOSTS_PAGE } from '../../lib/urls';
-import { loginAndWaitForPage } from '../../lib/util/helpers';
+import { SERVER_SIDE_EVENT_COUNT } from '../../../screens/timeline/main';
+import { HOSTS_PAGE } from '../../../urls/navigation';
+import { loginAndWaitForPage, DEFAULT_TIMEOUT } from '../../../tasks/login';
+import { openTimeline } from '../../../tasks/siem_main';
+import { executeTimelineKQL } from '../../../tasks/timeline/main';
 
 describe('timeline search or filter KQL bar', () => {
   beforeEach(() => {
@@ -19,10 +16,12 @@ describe('timeline search or filter KQL bar', () => {
   });
 
   it('executes a KQL query', () => {
-    toggleTimelineVisibility();
+    const hostExistsQuery = 'host.name: *';
+    openTimeline();
+    executeTimelineKQL(hostExistsQuery);
 
-    executeKQL(hostExistsQuery);
-
-    assertAtLeastOneEventMatchesSearch();
+    cy.get(SERVER_SIDE_EVENT_COUNT, { timeout: DEFAULT_TIMEOUT })
+      .invoke('text')
+      .should('be.above', 0);
   });
 });

From 055c61110f8bbbdaea46ba5f28687d6692ccb97e Mon Sep 17 00:00:00 2001
From: Pierre Gayvallet <pierre.gayvallet@elastic.co>
Date: Tue, 18 Feb 2020 15:40:21 +0100
Subject: [PATCH 019/174] migrate savedObjects routes to core (#56734)

* migrate `get` route

* migrate `create` route

* migrate `delete` route

* migrate `find` route

* migrate `update` route

* migrate `bulk_get` route

* migrate `bulk_create` route

* remove route-related mixin tests

* migrate `bulk_update` route

* fix expectTypeRequired assertion

* migrate `log_legacy_imports` route

* migrate `export` route

* fix karma tests

* array is better than object in some situations.

* remove prototype pollution tests

* adapt ftr assertions

* adapt ftr assertions

* adapt yet more ftr assertions

* migrate `import` route

* fix test tests

* fix getSortedObjectsForExport usages

* fix snapshots

* fix so ui exports usages due to merge

* create router with prefix

* creates `savedObjects` namespace config in addition to `migrations`

* migrate `resolve_import_errors` route

* remove old types file

* fix FTR assertion

* remove types parameter from copy_to_space

* move route tests to integration_tests

* use byteSize instead of number

* fix unit tests

* add has_reference query parameter

Co-authored-by: Mikhail Shustov <restrry@gmail.com>
---
 src/core/server/http/test_utils.ts            |  11 +-
 src/core/server/legacy/legacy_service.mock.ts |   1 +
 .../legacy/plugins/get_nav_links.test.ts      |   1 +
 src/core/server/mocks.ts                      |  23 +-
 .../get_sorted_objects_for_export.test.ts     |   3 -
 .../export/get_sorted_objects_for_export.ts   |   3 +
 src/core/server/saved_objects/index.ts        |   2 +-
 .../migrations/kibana/kibana_migrator.ts      |   6 +-
 .../saved_objects/routes/bulk_create.ts       |  57 ++++
 .../server/saved_objects/routes/bulk_get.ts}  |  35 +-
 .../saved_objects/routes/bulk_update.ts       |  52 +++
 .../server/saved_objects/routes/create.ts     |  60 ++++
 .../server/saved_objects/routes/delete.ts}    |  36 +-
 .../server/saved_objects/routes/export.ts     |  97 ++++++
 src/core/server/saved_objects/routes/find.ts  |  70 ++++
 .../server/saved_objects/routes/get.ts}       |  22 +-
 .../server/saved_objects/routes/import.ts     |  79 +++++
 src/core/server/saved_objects/routes/index.ts |  61 ++++
 .../integration_tests}/bulk_create.test.ts    | 103 +++---
 .../integration_tests}/bulk_get.test.ts       |  85 +++--
 .../integration_tests}/bulk_update.test.ts    | 106 +++---
 .../routes/integration_tests/create.test.ts   | 120 +++++++
 .../routes/integration_tests/delete.test.ts   |  63 ++++
 .../routes/integration_tests/export.test.ts   | 110 ++++++
 .../routes/integration_tests}/find.test.ts    | 174 +++++-----
 .../routes/integration_tests/get.test.ts      |  92 +++++
 .../routes/integration_tests/import.test.ts   | 320 +++++++++++++++++
 .../log_legacy_import.test.ts                 |  61 ++++
 .../resolve_import_errors.test.ts             | 283 ++++++++-------
 .../routes/integration_tests/test_utils.ts}   |  50 ++-
 .../routes/integration_tests}/update.test.ts  |  82 ++---
 .../saved_objects/routes/log_legacy_import.ts |  26 +-
 .../routes/resolve_import_errors.ts           |  89 +++++
 .../server/saved_objects/routes/update.ts     |  56 +++
 .../saved_objects/routes/utils.test.ts}       |   4 +-
 .../server/saved_objects/routes/utils.ts}     |   7 +-
 .../saved_objects/saved_objects_config.ts     |  30 +-
 .../saved_objects_service.test.ts             |  41 ++-
 .../saved_objects/saved_objects_service.ts    |  59 +++-
 src/core/server/saved_objects/types.ts        |   2 +
 src/core/server/saved_objects/utils.test.ts   |   5 +
 src/core/server/server.ts                     |   4 +-
 .../core_plugins/kibana/migrations/types.ts   |   3 +-
 .../saved_objects/routes/bulk_create.ts       |  81 -----
 .../server/saved_objects/routes/bulk_get.ts   |  56 ---
 .../saved_objects/routes/bulk_update.ts       |  61 ----
 .../saved_objects/routes/create.test.ts       | 132 -------
 .../server/saved_objects/routes/create.ts     |  86 -----
 .../saved_objects/routes/delete.test.ts       |  71 ----
 .../server/saved_objects/routes/delete.ts     |  55 ---
 .../saved_objects/routes/export.test.ts       | 186 ----------
 .../server/saved_objects/routes/export.ts     | 109 ------
 .../server/saved_objects/routes/find.ts       | 105 ------
 .../server/saved_objects/routes/get.test.ts   |  95 ------
 src/legacy/server/saved_objects/routes/get.ts |  55 ---
 .../saved_objects/routes/import.test.ts       | 321 ------------------
 .../server/saved_objects/routes/import.ts     |  91 -----
 .../routes/resolve_import_errors.ts           | 112 ------
 .../server/saved_objects/routes/update.ts     |  75 ----
 .../saved_objects/saved_objects_mixin.js      |  46 ---
 .../saved_objects/saved_objects_mixin.test.js |  76 -----
 test/api_integration/apis/general/index.js    |   1 -
 .../apis/general/prototype_pollution.ts       |  57 ----
 .../apis/saved_objects/export.js              |  15 +-
 .../apis/saved_objects/find.js                |   7 +-
 .../saved_objects/resolve_import_errors.js    |   3 +-
 .../lib/copy_to_spaces/copy_to_spaces.test.ts |  40 +--
 .../lib/copy_to_spaces/copy_to_spaces.ts      |   1 -
 .../resolve_copy_conflicts.test.ts            |  40 +--
 .../copy_to_spaces/resolve_copy_conflicts.ts  |   1 -
 .../common/suites/export.ts                   |   9 +-
 .../common/suites/find.ts                     |   6 +-
 .../spaces_only/apis/bulk_create.ts           |   7 +-
 .../spaces_only/apis/create.ts                |   6 +-
 74 files changed, 2091 insertions(+), 2509 deletions(-)
 create mode 100644 src/core/server/saved_objects/routes/bulk_create.ts
 rename src/{legacy/server/saved_objects/routes/index.ts => core/server/saved_objects/routes/bulk_get.ts} (55%)
 create mode 100644 src/core/server/saved_objects/routes/bulk_update.ts
 create mode 100644 src/core/server/saved_objects/routes/create.ts
 rename src/{legacy/server/saved_objects/routes/types.ts => core/server/saved_objects/routes/delete.ts} (58%)
 create mode 100644 src/core/server/saved_objects/routes/export.ts
 create mode 100644 src/core/server/saved_objects/routes/find.ts
 rename src/{legacy/server/saved_objects/lib/index.ts => core/server/saved_objects/routes/get.ts} (58%)
 create mode 100644 src/core/server/saved_objects/routes/import.ts
 create mode 100644 src/core/server/saved_objects/routes/index.ts
 rename src/{legacy/server/saved_objects/routes => core/server/saved_objects/routes/integration_tests}/bulk_create.test.ts (55%)
 rename src/{legacy/server/saved_objects/routes => core/server/saved_objects/routes/integration_tests}/bulk_get.test.ts (53%)
 rename src/{legacy/server/saved_objects/routes => core/server/saved_objects/routes/integration_tests}/bulk_update.test.ts (65%)
 create mode 100644 src/core/server/saved_objects/routes/integration_tests/create.test.ts
 create mode 100644 src/core/server/saved_objects/routes/integration_tests/delete.test.ts
 create mode 100644 src/core/server/saved_objects/routes/integration_tests/export.test.ts
 rename src/{legacy/server/saved_objects/routes => core/server/saved_objects/routes/integration_tests}/find.test.ts (55%)
 create mode 100644 src/core/server/saved_objects/routes/integration_tests/get.test.ts
 create mode 100644 src/core/server/saved_objects/routes/integration_tests/import.test.ts
 create mode 100644 src/core/server/saved_objects/routes/integration_tests/log_legacy_import.test.ts
 rename src/{legacy/server/saved_objects/routes => core/server/saved_objects/routes/integration_tests}/resolve_import_errors.test.ts (51%)
 rename src/{legacy/server/saved_objects/routes/_mock_server.ts => core/server/saved_objects/routes/integration_tests/test_utils.ts} (51%)
 rename src/{legacy/server/saved_objects/routes => core/server/saved_objects/routes/integration_tests}/update.test.ts (56%)
 rename src/{legacy => core}/server/saved_objects/routes/log_legacy_import.ts (65%)
 create mode 100644 src/core/server/saved_objects/routes/resolve_import_errors.ts
 create mode 100644 src/core/server/saved_objects/routes/update.ts
 rename src/{legacy/server/saved_objects/lib/create_saved_objects_stream_from_ndjson.test.ts => core/server/saved_objects/routes/utils.test.ts} (96%)
 rename src/{legacy/server/saved_objects/lib/create_saved_objects_stream_from_ndjson.ts => core/server/saved_objects/routes/utils.ts} (92%)
 delete mode 100644 src/legacy/server/saved_objects/routes/bulk_create.ts
 delete mode 100644 src/legacy/server/saved_objects/routes/bulk_get.ts
 delete mode 100644 src/legacy/server/saved_objects/routes/bulk_update.ts
 delete mode 100644 src/legacy/server/saved_objects/routes/create.test.ts
 delete mode 100644 src/legacy/server/saved_objects/routes/create.ts
 delete mode 100644 src/legacy/server/saved_objects/routes/delete.test.ts
 delete mode 100644 src/legacy/server/saved_objects/routes/delete.ts
 delete mode 100644 src/legacy/server/saved_objects/routes/export.test.ts
 delete mode 100644 src/legacy/server/saved_objects/routes/export.ts
 delete mode 100644 src/legacy/server/saved_objects/routes/find.ts
 delete mode 100644 src/legacy/server/saved_objects/routes/get.test.ts
 delete mode 100644 src/legacy/server/saved_objects/routes/get.ts
 delete mode 100644 src/legacy/server/saved_objects/routes/import.test.ts
 delete mode 100644 src/legacy/server/saved_objects/routes/import.ts
 delete mode 100644 src/legacy/server/saved_objects/routes/resolve_import_errors.ts
 delete mode 100644 src/legacy/server/saved_objects/routes/update.ts
 delete mode 100644 test/api_integration/apis/general/prototype_pollution.ts

diff --git a/src/core/server/http/test_utils.ts b/src/core/server/http/test_utils.ts
index ffdc04d156ca0..49c4d690c6876 100644
--- a/src/core/server/http/test_utils.ts
+++ b/src/core/server/http/test_utils.ts
@@ -55,13 +55,14 @@ const defaultContext: CoreContext = {
   configService,
 };
 
+export const createCoreContext = (overrides: Partial<CoreContext> = {}): CoreContext => ({
+  ...defaultContext,
+  ...overrides,
+});
+
 /**
  * Creates a concrete HttpServer with a mocked context.
  */
 export const createHttpServer = (overrides: Partial<CoreContext> = {}): HttpService => {
-  const context = {
-    ...defaultContext,
-    ...overrides,
-  };
-  return new HttpService(context);
+  return new HttpService(createCoreContext(overrides));
 };
diff --git a/src/core/server/legacy/legacy_service.mock.ts b/src/core/server/legacy/legacy_service.mock.ts
index 44405dc391d8e..26ec52185a5d8 100644
--- a/src/core/server/legacy/legacy_service.mock.ts
+++ b/src/core/server/legacy/legacy_service.mock.ts
@@ -29,6 +29,7 @@ const createDiscoverPluginsMock = (): LegacyServiceDiscoverPlugins => ({
     savedObjectMappings: [],
     savedObjectMigrations: {},
     savedObjectValidations: {},
+    savedObjectsManagement: {},
   },
   navLinks: [],
   pluginExtendedConfig: {
diff --git a/src/core/server/legacy/plugins/get_nav_links.test.ts b/src/core/server/legacy/plugins/get_nav_links.test.ts
index dcb19020f769e..44d080ec37a25 100644
--- a/src/core/server/legacy/plugins/get_nav_links.test.ts
+++ b/src/core/server/legacy/plugins/get_nav_links.test.ts
@@ -35,6 +35,7 @@ const createLegacyExports = ({
   savedObjectSchemas: {},
   savedObjectMigrations: {},
   savedObjectValidations: {},
+  savedObjectsManagement: {},
 });
 
 const createPluginSpecs = (...ids: string[]): LegacyPluginSpec[] =>
diff --git a/src/core/server/mocks.ts b/src/core/server/mocks.ts
index 9a7868d568ea0..d6554babab53e 100644
--- a/src/core/server/mocks.ts
+++ b/src/core/server/mocks.ts
@@ -25,6 +25,7 @@ import { elasticsearchServiceMock } from './elasticsearch/elasticsearch_service.
 import { httpServiceMock } from './http/http_service.mock';
 import { contextServiceMock } from './context/context_service.mock';
 import { savedObjectsServiceMock } from './saved_objects/saved_objects_service.mock';
+import { savedObjectsClientMock } from './saved_objects/service/saved_objects_client.mock';
 import { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock';
 import { SharedGlobalConfig } from './plugins';
 import { InternalCoreSetup, InternalCoreStart } from './internal_types';
@@ -36,7 +37,6 @@ export { configServiceMock } from './config/config_service.mock';
 export { elasticsearchServiceMock } from './elasticsearch/elasticsearch_service.mock';
 export { httpServiceMock } from './http/http_service.mock';
 export { loggingServiceMock } from './logging/logging_service.mock';
-export { savedObjectsClientMock } from './saved_objects/service/saved_objects_client.mock';
 export { savedObjectsRepositoryMock } from './saved_objects/service/lib/repository.mock';
 export { typeRegistryMock as savedObjectsTypeRegistryMock } from './saved_objects/saved_objects_type_registry.mock';
 export { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock';
@@ -168,10 +168,31 @@ function createInternalCoreStartMock() {
   return startDeps;
 }
 
+function createCoreRequestHandlerContextMock() {
+  return {
+    rendering: {
+      render: jest.fn(),
+    },
+    savedObjects: {
+      client: savedObjectsClientMock.create(),
+    },
+    elasticsearch: {
+      adminClient: elasticsearchServiceMock.createScopedClusterClient(),
+      dataClient: elasticsearchServiceMock.createScopedClusterClient(),
+    },
+    uiSettings: {
+      client: uiSettingsServiceMock.createClient(),
+    },
+  };
+}
+
 export const coreMock = {
   createSetup: createCoreSetupMock,
   createStart: createCoreStartMock,
   createInternalSetup: createInternalCoreSetupMock,
   createInternalStart: createInternalCoreStartMock,
   createPluginInitializerContext: pluginInitializerContextMock,
+  createRequestHandlerContext: createCoreRequestHandlerContextMock,
 };
+
+export { savedObjectsClientMock };
diff --git a/src/core/server/saved_objects/export/get_sorted_objects_for_export.test.ts b/src/core/server/saved_objects/export/get_sorted_objects_for_export.test.ts
index fafa04447ddfe..1088478add137 100644
--- a/src/core/server/saved_objects/export/get_sorted_objects_for_export.test.ts
+++ b/src/core/server/saved_objects/export/get_sorted_objects_for_export.test.ts
@@ -492,7 +492,6 @@ describe('getSortedObjectsForExport()', () => {
     const exportStream = await getSortedObjectsForExport({
       exportSizeLimit: 10000,
       savedObjectsClient,
-      types: ['index-pattern', 'search'],
       objects: [
         {
           type: 'index-pattern',
@@ -591,7 +590,6 @@ describe('getSortedObjectsForExport()', () => {
     const exportStream = await getSortedObjectsForExport({
       exportSizeLimit: 10000,
       savedObjectsClient,
-      types: ['index-pattern', 'search'],
       objects: [
         {
           type: 'search',
@@ -672,7 +670,6 @@ describe('getSortedObjectsForExport()', () => {
     const exportOpts = {
       exportSizeLimit: 1,
       savedObjectsClient,
-      types: ['index-pattern', 'search'],
       objects: [
         {
           type: 'index-pattern',
diff --git a/src/core/server/saved_objects/export/get_sorted_objects_for_export.ts b/src/core/server/saved_objects/export/get_sorted_objects_for_export.ts
index a4dfacfd9e34f..4b4cf1146aca0 100644
--- a/src/core/server/saved_objects/export/get_sorted_objects_for_export.ts
+++ b/src/core/server/saved_objects/export/get_sorted_objects_for_export.ts
@@ -84,6 +84,9 @@ async function fetchObjectsToExport({
   savedObjectsClient: SavedObjectsClientContract;
   namespace?: string;
 }) {
+  if ((types?.length ?? 0) > 0 && (objects?.length ?? 0) > 0) {
+    throw Boom.badRequest(`Can't specify both "types" and "objects" properties when exporting`);
+  }
   if (objects && objects.length > 0) {
     if (objects.length > exportSizeLimit) {
       throw Boom.badRequest(`Can't export more than ${exportSizeLimit} objects`);
diff --git a/src/core/server/saved_objects/index.ts b/src/core/server/saved_objects/index.ts
index 529ee9599f178..5be4458bdf2af 100644
--- a/src/core/server/saved_objects/index.ts
+++ b/src/core/server/saved_objects/index.ts
@@ -68,5 +68,5 @@ export { SavedObjectMigrationMap, SavedObjectMigrationFn } from './migrations';
 
 export { SavedObjectsType } from './types';
 
-export { config } from './saved_objects_config';
+export { savedObjectsConfig, savedObjectsMigrationConfig } from './saved_objects_config';
 export { SavedObjectTypeRegistry, ISavedObjectTypeRegistry } from './saved_objects_type_registry';
diff --git a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts
index 9b4fe10a35100..494f834717def 100644
--- a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts
+++ b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts
@@ -30,14 +30,14 @@ import { docValidator, PropertyValidators } from '../../validation';
 import { buildActiveMappings, CallCluster, IndexMigrator } from '../core';
 import { DocumentMigrator, VersionedTransformer } from '../core/document_migrator';
 import { createIndexMap } from '../core/build_index_map';
-import { SavedObjectsConfigType } from '../../saved_objects_config';
+import { SavedObjectsMigrationConfigType } from '../../saved_objects_config';
 import { ISavedObjectTypeRegistry } from '../../saved_objects_type_registry';
 import { SavedObjectsType } from '../../types';
 
 export interface KibanaMigratorOptions {
   callCluster: CallCluster;
   typeRegistry: ISavedObjectTypeRegistry;
-  savedObjectsConfig: SavedObjectsConfigType;
+  savedObjectsConfig: SavedObjectsMigrationConfigType;
   kibanaConfig: KibanaConfigType;
   kibanaVersion: string;
   logger: Logger;
@@ -51,7 +51,7 @@ export type IKibanaMigrator = Pick<KibanaMigrator, keyof KibanaMigrator>;
  */
 export class KibanaMigrator {
   private readonly callCluster: CallCluster;
-  private readonly savedObjectsConfig: SavedObjectsConfigType;
+  private readonly savedObjectsConfig: SavedObjectsMigrationConfigType;
   private readonly documentMigrator: VersionedTransformer;
   private readonly kibanaConfig: KibanaConfigType;
   private readonly log: Logger;
diff --git a/src/core/server/saved_objects/routes/bulk_create.ts b/src/core/server/saved_objects/routes/bulk_create.ts
new file mode 100644
index 0000000000000..af1a7bd2af9b7
--- /dev/null
+++ b/src/core/server/saved_objects/routes/bulk_create.ts
@@ -0,0 +1,57 @@
+/*
+ * 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 { schema } from '@kbn/config-schema';
+import { IRouter } from '../../http';
+
+export const registerBulkCreateRoute = (router: IRouter) => {
+  router.post(
+    {
+      path: '/_bulk_create',
+      validate: {
+        query: schema.object({
+          overwrite: schema.boolean({ defaultValue: false }),
+        }),
+        body: schema.arrayOf(
+          schema.object({
+            type: schema.string(),
+            id: schema.maybe(schema.string()),
+            attributes: schema.recordOf(schema.string(), schema.any()),
+            version: schema.maybe(schema.string()),
+            migrationVersion: schema.maybe(schema.recordOf(schema.string(), schema.string())),
+            references: schema.maybe(
+              schema.arrayOf(
+                schema.object({
+                  name: schema.string(),
+                  type: schema.string(),
+                  id: schema.string(),
+                })
+              )
+            ),
+          })
+        ),
+      },
+    },
+    router.handleLegacyErrors(async (context, req, res) => {
+      const { overwrite } = req.query;
+      const result = await context.core.savedObjects.client.bulkCreate(req.body, { overwrite });
+      return res.ok({ body: result });
+    })
+  );
+};
diff --git a/src/legacy/server/saved_objects/routes/index.ts b/src/core/server/saved_objects/routes/bulk_get.ts
similarity index 55%
rename from src/legacy/server/saved_objects/routes/index.ts
rename to src/core/server/saved_objects/routes/bulk_get.ts
index 0afcfba308546..067388dcf9220 100644
--- a/src/legacy/server/saved_objects/routes/index.ts
+++ b/src/core/server/saved_objects/routes/bulk_get.ts
@@ -17,15 +17,26 @@
  * under the License.
  */
 
-export { createBulkCreateRoute } from './bulk_create';
-export { createBulkGetRoute } from './bulk_get';
-export { createCreateRoute } from './create';
-export { createDeleteRoute } from './delete';
-export { createFindRoute } from './find';
-export { createGetRoute } from './get';
-export { createImportRoute } from './import';
-export { createLogLegacyImportRoute } from './log_legacy_import';
-export { createResolveImportErrorsRoute } from './resolve_import_errors';
-export { createUpdateRoute } from './update';
-export { createBulkUpdateRoute } from './bulk_update';
-export { createExportRoute } from './export';
+import { schema } from '@kbn/config-schema';
+import { IRouter } from '../../http';
+
+export const registerBulkGetRoute = (router: IRouter) => {
+  router.post(
+    {
+      path: '/_bulk_get',
+      validate: {
+        body: schema.arrayOf(
+          schema.object({
+            type: schema.string(),
+            id: schema.string(),
+            fields: schema.maybe(schema.arrayOf(schema.string())),
+          })
+        ),
+      },
+    },
+    router.handleLegacyErrors(async (context, req, res) => {
+      const result = await context.core.savedObjects.client.bulkGet(req.body);
+      return res.ok({ body: result });
+    })
+  );
+};
diff --git a/src/core/server/saved_objects/routes/bulk_update.ts b/src/core/server/saved_objects/routes/bulk_update.ts
new file mode 100644
index 0000000000000..c112833b29f3f
--- /dev/null
+++ b/src/core/server/saved_objects/routes/bulk_update.ts
@@ -0,0 +1,52 @@
+/*
+ * 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 { schema } from '@kbn/config-schema';
+import { IRouter } from '../../http';
+
+export const registerBulkUpdateRoute = (router: IRouter) => {
+  router.put(
+    {
+      path: '/_bulk_update',
+      validate: {
+        body: schema.arrayOf(
+          schema.object({
+            type: schema.string(),
+            id: schema.string(),
+            attributes: schema.recordOf(schema.string(), schema.any()),
+            version: schema.maybe(schema.string()),
+            references: schema.maybe(
+              schema.arrayOf(
+                schema.object({
+                  name: schema.string(),
+                  type: schema.string(),
+                  id: schema.string(),
+                })
+              )
+            ),
+          })
+        ),
+      },
+    },
+    router.handleLegacyErrors(async (context, req, res) => {
+      const savedObject = await context.core.savedObjects.client.bulkUpdate(req.body);
+      return res.ok({ body: savedObject });
+    })
+  );
+};
diff --git a/src/core/server/saved_objects/routes/create.ts b/src/core/server/saved_objects/routes/create.ts
new file mode 100644
index 0000000000000..6cf906a3b2895
--- /dev/null
+++ b/src/core/server/saved_objects/routes/create.ts
@@ -0,0 +1,60 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { schema } from '@kbn/config-schema';
+import { IRouter } from '../../http';
+
+export const registerCreateRoute = (router: IRouter) => {
+  router.post(
+    {
+      path: '/{type}/{id?}',
+      validate: {
+        params: schema.object({
+          type: schema.string(),
+          id: schema.maybe(schema.string()),
+        }),
+        query: schema.object({
+          overwrite: schema.boolean({ defaultValue: false }),
+        }),
+        body: schema.object({
+          attributes: schema.recordOf(schema.string(), schema.any()),
+          migrationVersion: schema.maybe(schema.recordOf(schema.string(), schema.string())),
+          references: schema.maybe(
+            schema.arrayOf(
+              schema.object({
+                name: schema.string(),
+                type: schema.string(),
+                id: schema.string(),
+              })
+            )
+          ),
+        }),
+      },
+    },
+    router.handleLegacyErrors(async (context, req, res) => {
+      const { type, id } = req.params;
+      const { overwrite } = req.query;
+      const { attributes, migrationVersion, references } = req.body;
+
+      const options = { id, overwrite, migrationVersion, references };
+      const result = await context.core.savedObjects.client.create(type, attributes, options);
+      return res.ok({ body: result });
+    })
+  );
+};
diff --git a/src/legacy/server/saved_objects/routes/types.ts b/src/core/server/saved_objects/routes/delete.ts
similarity index 58%
rename from src/legacy/server/saved_objects/routes/types.ts
rename to src/core/server/saved_objects/routes/delete.ts
index b3f294b66499b..d119455336212 100644
--- a/src/legacy/server/saved_objects/routes/types.ts
+++ b/src/core/server/saved_objects/routes/delete.ts
@@ -17,20 +17,24 @@
  * under the License.
  */
 
-import Hapi from 'hapi';
-import { SavedObjectsClientContract } from 'src/core/server';
+import { schema } from '@kbn/config-schema';
+import { IRouter } from '../../http';
 
-export interface SavedObjectReference {
-  name: string;
-  type: string;
-  id: string;
-}
-
-export interface Prerequisites {
-  getSavedObjectsClient: {
-    assign: string;
-    method: (req: Hapi.Request) => SavedObjectsClientContract;
-  };
-}
-
-export type WithoutQueryAndParams<T> = Pick<T, Exclude<keyof T, 'query' | 'params'>>;
+export const registerDeleteRoute = (router: IRouter) => {
+  router.delete(
+    {
+      path: '/{type}/{id}',
+      validate: {
+        params: schema.object({
+          type: schema.string(),
+          id: schema.string(),
+        }),
+      },
+    },
+    router.handleLegacyErrors(async (context, req, res) => {
+      const { type, id } = req.params;
+      const result = await context.core.savedObjects.client.delete(type, id);
+      return res.ok({ body: result });
+    })
+  );
+};
diff --git a/src/core/server/saved_objects/routes/export.ts b/src/core/server/saved_objects/routes/export.ts
new file mode 100644
index 0000000000000..ab287332d8a65
--- /dev/null
+++ b/src/core/server/saved_objects/routes/export.ts
@@ -0,0 +1,97 @@
+/*
+ * 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 { schema } from '@kbn/config-schema';
+import stringify from 'json-stable-stringify';
+import {
+  createPromiseFromStreams,
+  createMapStream,
+  createConcatStream,
+} from '../../../../legacy/utils/streams';
+import { IRouter } from '../../http';
+import { SavedObjectConfig } from '../saved_objects_config';
+import { getSortedObjectsForExport } from '../export';
+
+export const registerExportRoute = (
+  router: IRouter,
+  config: SavedObjectConfig,
+  supportedTypes: string[]
+) => {
+  const { maxImportExportSize } = config;
+
+  const typeSchema = schema.string({
+    validate: (type: string) => {
+      if (!supportedTypes.includes(type)) {
+        return `${type} is not exportable`;
+      }
+    },
+  });
+
+  router.post(
+    {
+      path: '/_export',
+      validate: {
+        body: schema.object({
+          type: schema.maybe(schema.oneOf([typeSchema, schema.arrayOf(typeSchema)])),
+          objects: schema.maybe(
+            schema.arrayOf(
+              schema.object({
+                type: typeSchema,
+                id: schema.string(),
+              }),
+              { maxSize: maxImportExportSize }
+            )
+          ),
+          search: schema.maybe(schema.string()),
+          includeReferencesDeep: schema.boolean({ defaultValue: false }),
+          excludeExportDetails: schema.boolean({ defaultValue: false }),
+        }),
+      },
+    },
+    router.handleLegacyErrors(async (context, req, res) => {
+      const savedObjectsClient = context.core.savedObjects.client;
+      const { type, objects, search, excludeExportDetails, includeReferencesDeep } = req.body;
+      const exportStream = await getSortedObjectsForExport({
+        savedObjectsClient,
+        types: typeof type === 'string' ? [type] : type,
+        search,
+        objects,
+        exportSizeLimit: maxImportExportSize,
+        includeReferencesDeep,
+        excludeExportDetails,
+      });
+
+      const docsToExport: string[] = await createPromiseFromStreams([
+        exportStream,
+        createMapStream((obj: unknown) => {
+          return stringify(obj);
+        }),
+        createConcatStream([]),
+      ]);
+
+      return res.ok({
+        body: docsToExport.join('\n'),
+        headers: {
+          'Content-Disposition': `attachment; filename="export.ndjson"`,
+          'Content-Type': 'application/ndjson',
+        },
+      });
+    })
+  );
+};
diff --git a/src/core/server/saved_objects/routes/find.ts b/src/core/server/saved_objects/routes/find.ts
new file mode 100644
index 0000000000000..5c1c2c9a9ab87
--- /dev/null
+++ b/src/core/server/saved_objects/routes/find.ts
@@ -0,0 +1,70 @@
+/*
+ * 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 { schema } from '@kbn/config-schema';
+import { IRouter } from '../../http';
+
+export const registerFindRoute = (router: IRouter) => {
+  router.get(
+    {
+      path: '/_find',
+      validate: {
+        query: schema.object({
+          per_page: schema.number({ min: 0, defaultValue: 20 }),
+          page: schema.number({ min: 0, defaultValue: 1 }),
+          type: schema.oneOf([schema.string(), schema.arrayOf(schema.string())]),
+          search: schema.maybe(schema.string()),
+          default_search_operator: schema.oneOf([schema.literal('OR'), schema.literal('AND')], {
+            defaultValue: 'OR',
+          }),
+          search_fields: schema.maybe(
+            schema.oneOf([schema.string(), schema.arrayOf(schema.string())])
+          ),
+          sort_field: schema.maybe(schema.string()),
+          has_reference: schema.maybe(
+            schema.object({
+              type: schema.string(),
+              id: schema.string(),
+            })
+          ),
+          fields: schema.maybe(schema.oneOf([schema.string(), schema.arrayOf(schema.string())])),
+          filter: schema.maybe(schema.string()),
+        }),
+      },
+    },
+    router.handleLegacyErrors(async (context, req, res) => {
+      const query = req.query;
+      const result = await context.core.savedObjects.client.find({
+        perPage: query.per_page,
+        page: query.page,
+        type: Array.isArray(query.type) ? query.type : [query.type],
+        search: query.search,
+        defaultSearchOperator: query.default_search_operator,
+        searchFields:
+          typeof query.search_fields === 'string' ? [query.search_fields] : query.search_fields,
+        sortField: query.sort_field,
+        hasReference: query.has_reference,
+        fields: typeof query.fields === 'string' ? [query.fields] : query.fields,
+        filter: query.filter,
+      });
+
+      return res.ok({ body: result });
+    })
+  );
+};
diff --git a/src/legacy/server/saved_objects/lib/index.ts b/src/core/server/saved_objects/routes/get.ts
similarity index 58%
rename from src/legacy/server/saved_objects/lib/index.ts
rename to src/core/server/saved_objects/routes/get.ts
index 1255ef67a03c2..f1b974c70b1a9 100644
--- a/src/legacy/server/saved_objects/lib/index.ts
+++ b/src/core/server/saved_objects/routes/get.ts
@@ -17,4 +17,24 @@
  * under the License.
  */
 
-export { createSavedObjectsStreamFromNdJson } from './create_saved_objects_stream_from_ndjson';
+import { schema } from '@kbn/config-schema';
+import { IRouter } from '../../http';
+
+export const registerGetRoute = (router: IRouter) => {
+  router.get(
+    {
+      path: '/{type}/{id}',
+      validate: {
+        params: schema.object({
+          type: schema.string(),
+          id: schema.string(),
+        }),
+      },
+    },
+    router.handleLegacyErrors(async (context, req, res) => {
+      const { type, id } = req.params;
+      const savedObject = await context.core.savedObjects.client.get(type, id);
+      return res.ok({ body: savedObject });
+    })
+  );
+};
diff --git a/src/core/server/saved_objects/routes/import.ts b/src/core/server/saved_objects/routes/import.ts
new file mode 100644
index 0000000000000..e3f249dca05f7
--- /dev/null
+++ b/src/core/server/saved_objects/routes/import.ts
@@ -0,0 +1,79 @@
+/*
+ * 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 { Readable } from 'stream';
+import { extname } from 'path';
+import { schema } from '@kbn/config-schema';
+import { IRouter } from '../../http';
+import { importSavedObjects } from '../import';
+import { SavedObjectConfig } from '../saved_objects_config';
+import { createSavedObjectsStreamFromNdJson } from './utils';
+
+interface FileStream extends Readable {
+  hapi: {
+    filename: string;
+  };
+}
+
+export const registerImportRoute = (
+  router: IRouter,
+  config: SavedObjectConfig,
+  supportedTypes: string[]
+) => {
+  const { maxImportExportSize, maxImportPayloadBytes } = config;
+
+  router.post(
+    {
+      path: '/_import',
+      options: {
+        body: {
+          maxBytes: maxImportPayloadBytes,
+          output: 'stream',
+          accepts: 'multipart/form-data',
+        },
+      },
+      validate: {
+        query: schema.object({
+          overwrite: schema.boolean({ defaultValue: false }),
+        }),
+        body: schema.object({
+          file: schema.stream(),
+        }),
+      },
+    },
+    router.handleLegacyErrors(async (context, req, res) => {
+      const { overwrite } = req.query;
+      const file = req.body.file as FileStream;
+      const fileExtension = extname(file.hapi.filename).toLowerCase();
+      if (fileExtension !== '.ndjson') {
+        return res.badRequest({ body: `Invalid file extension ${fileExtension}` });
+      }
+
+      const result = await importSavedObjects({
+        supportedTypes,
+        savedObjectsClient: context.core.savedObjects.client,
+        readStream: createSavedObjectsStreamFromNdJson(file),
+        objectLimit: maxImportExportSize,
+        overwrite,
+      });
+
+      return res.ok({ body: result });
+    })
+  );
+};
diff --git a/src/core/server/saved_objects/routes/index.ts b/src/core/server/saved_objects/routes/index.ts
new file mode 100644
index 0000000000000..f2f57798dd5f0
--- /dev/null
+++ b/src/core/server/saved_objects/routes/index.ts
@@ -0,0 +1,61 @@
+/*
+ * 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 { InternalHttpServiceSetup } from '../../http';
+import { Logger } from '../../logging';
+import { SavedObjectConfig } from '../saved_objects_config';
+import { registerGetRoute } from './get';
+import { registerCreateRoute } from './create';
+import { registerDeleteRoute } from './delete';
+import { registerFindRoute } from './find';
+import { registerUpdateRoute } from './update';
+import { registerBulkGetRoute } from './bulk_get';
+import { registerBulkCreateRoute } from './bulk_create';
+import { registerBulkUpdateRoute } from './bulk_update';
+import { registerLogLegacyImportRoute } from './log_legacy_import';
+import { registerExportRoute } from './export';
+import { registerImportRoute } from './import';
+import { registerResolveImportErrorsRoute } from './resolve_import_errors';
+
+export function registerRoutes({
+  http,
+  logger,
+  config,
+  importableExportableTypes,
+}: {
+  http: InternalHttpServiceSetup;
+  logger: Logger;
+  config: SavedObjectConfig;
+  importableExportableTypes: string[];
+}) {
+  const router = http.createRouter('/api/saved_objects/');
+
+  registerGetRoute(router);
+  registerCreateRoute(router);
+  registerDeleteRoute(router);
+  registerFindRoute(router);
+  registerUpdateRoute(router);
+  registerBulkGetRoute(router);
+  registerBulkCreateRoute(router);
+  registerBulkUpdateRoute(router);
+  registerLogLegacyImportRoute(router, logger);
+  registerExportRoute(router, config, importableExportableTypes);
+  registerImportRoute(router, config, importableExportableTypes);
+  registerResolveImportErrorsRoute(router, config, importableExportableTypes);
+}
diff --git a/src/legacy/server/saved_objects/routes/bulk_create.test.ts b/src/core/server/saved_objects/routes/integration_tests/bulk_create.test.ts
similarity index 55%
rename from src/legacy/server/saved_objects/routes/bulk_create.test.ts
rename to src/core/server/saved_objects/routes/integration_tests/bulk_create.test.ts
index b49554995aab6..5b52665b6268e 100644
--- a/src/legacy/server/saved_objects/routes/bulk_create.test.ts
+++ b/src/core/server/saved_objects/routes/integration_tests/bulk_create.test.ts
@@ -17,52 +17,36 @@
  * under the License.
  */
 
-import Hapi from 'hapi';
-import { createMockServer } from './_mock_server';
-import { createBulkCreateRoute } from './bulk_create';
-// Disable lint errors for imports from src/core/* until SavedObjects migration is complete
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { savedObjectsClientMock } from '../../../../core/server/saved_objects/service/saved_objects_client.mock';
+import supertest from 'supertest';
+import { UnwrapPromise } from '@kbn/utility-types';
+import { registerBulkCreateRoute } from '../bulk_create';
+import { savedObjectsClientMock } from '../../../../../core/server/mocks';
+import { setupServer } from './test_utils';
+
+type setupServerReturn = UnwrapPromise<ReturnType<typeof setupServer>>;
 
 describe('POST /api/saved_objects/_bulk_create', () => {
-  let server: Hapi.Server;
-  const savedObjectsClient = savedObjectsClientMock.create();
+  let server: setupServerReturn['server'];
+  let httpSetup: setupServerReturn['httpSetup'];
+  let handlerContext: setupServerReturn['handlerContext'];
+  let savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
 
-  beforeEach(() => {
-    savedObjectsClient.bulkCreate.mockImplementation(() => Promise.resolve('' as any));
-    server = createMockServer();
+  beforeEach(async () => {
+    ({ server, httpSetup, handlerContext } = await setupServer());
+    savedObjectsClient = handlerContext.savedObjects.client;
+    savedObjectsClient.bulkCreate.mockResolvedValue({ saved_objects: [] });
 
-    const prereqs = {
-      getSavedObjectsClient: {
-        assign: 'savedObjectsClient',
-        method() {
-          return savedObjectsClient;
-        },
-      },
-    };
+    const router = httpSetup.createRouter('/api/saved_objects/');
+    registerBulkCreateRoute(router);
 
-    server.route(createBulkCreateRoute(prereqs));
+    await server.start();
   });
 
-  afterEach(() => {
-    savedObjectsClient.bulkCreate.mockReset();
+  afterEach(async () => {
+    await server.stop();
   });
 
   it('formats successful response', async () => {
-    const request = {
-      method: 'POST',
-      url: '/api/saved_objects/_bulk_create',
-      payload: [
-        {
-          id: 'abc123',
-          type: 'index-pattern',
-          attributes: {
-            title: 'my_title',
-          },
-        },
-      ],
-    };
-
     const clientResponse = {
       saved_objects: [
         {
@@ -75,14 +59,22 @@ describe('POST /api/saved_objects/_bulk_create', () => {
         },
       ],
     };
+    savedObjectsClient.bulkCreate.mockResolvedValue(clientResponse);
 
-    savedObjectsClient.bulkCreate.mockImplementation(() => Promise.resolve(clientResponse));
-
-    const { payload, statusCode } = await server.inject(request);
-    const response = JSON.parse(payload);
+    const result = await supertest(httpSetup.server.listener)
+      .post('/api/saved_objects/_bulk_create')
+      .send([
+        {
+          id: 'abc123',
+          type: 'index-pattern',
+          attributes: {
+            title: 'my_title',
+          },
+        },
+      ])
+      .expect(200);
 
-    expect(statusCode).toBe(200);
-    expect(response).toEqual(clientResponse);
+    expect(result.body).toEqual(clientResponse);
   });
 
   it('calls upon savedObjectClient.bulkCreate', async () => {
@@ -105,24 +97,20 @@ describe('POST /api/saved_objects/_bulk_create', () => {
       },
     ];
 
-    const request = {
-      method: 'POST',
-      url: '/api/saved_objects/_bulk_create',
-      payload: docs,
-    };
-
-    await server.inject(request);
-    expect(savedObjectsClient.bulkCreate).toHaveBeenCalled();
+    await supertest(httpSetup.server.listener)
+      .post('/api/saved_objects/_bulk_create')
+      .send(docs)
+      .expect(200);
 
+    expect(savedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1);
     const args = savedObjectsClient.bulkCreate.mock.calls[0];
     expect(args[0]).toEqual(docs);
   });
 
   it('passes along the overwrite option', async () => {
-    await server.inject({
-      method: 'POST',
-      url: '/api/saved_objects/_bulk_create?overwrite=true',
-      payload: [
+    await supertest(httpSetup.server.listener)
+      .post('/api/saved_objects/_bulk_create?overwrite=true')
+      .send([
         {
           id: 'abc1234',
           type: 'index-pattern',
@@ -131,11 +119,10 @@ describe('POST /api/saved_objects/_bulk_create', () => {
           },
           references: [],
         },
-      ],
-    });
-
-    expect(savedObjectsClient.bulkCreate).toHaveBeenCalled();
+      ])
+      .expect(200);
 
+    expect(savedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1);
     const args = savedObjectsClient.bulkCreate.mock.calls[0];
     expect(args[1]).toEqual({ overwrite: true });
   });
diff --git a/src/legacy/server/saved_objects/routes/bulk_get.test.ts b/src/core/server/saved_objects/routes/integration_tests/bulk_get.test.ts
similarity index 53%
rename from src/legacy/server/saved_objects/routes/bulk_get.test.ts
rename to src/core/server/saved_objects/routes/integration_tests/bulk_get.test.ts
index e154649e2cf04..845bae47b41f2 100644
--- a/src/legacy/server/saved_objects/routes/bulk_get.test.ts
+++ b/src/core/server/saved_objects/routes/integration_tests/bulk_get.test.ts
@@ -17,50 +17,38 @@
  * under the License.
  */
 
-import Hapi from 'hapi';
-import { createMockServer } from './_mock_server';
-import { createBulkGetRoute } from './bulk_get';
-import { savedObjectsClientMock } from '../../../../core/server/mocks';
+import supertest from 'supertest';
+import { UnwrapPromise } from '@kbn/utility-types';
+import { registerBulkGetRoute } from '../bulk_get';
+import { savedObjectsClientMock } from '../../../../../core/server/mocks';
+import { setupServer } from './test_utils';
+
+type setupServerReturn = UnwrapPromise<ReturnType<typeof setupServer>>;
 
 describe('POST /api/saved_objects/_bulk_get', () => {
-  let server: Hapi.Server;
-  const savedObjectsClient = savedObjectsClientMock.create();
+  let server: setupServerReturn['server'];
+  let httpSetup: setupServerReturn['httpSetup'];
+  let handlerContext: setupServerReturn['handlerContext'];
+  let savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
 
-  beforeEach(() => {
-    savedObjectsClient.bulkGet.mockImplementation(() =>
-      Promise.resolve({
-        saved_objects: [],
-      })
-    );
-    server = createMockServer();
-    const prereqs = {
-      getSavedObjectsClient: {
-        assign: 'savedObjectsClient',
-        method() {
-          return savedObjectsClient;
-        },
-      },
-    };
+  beforeEach(async () => {
+    ({ server, httpSetup, handlerContext } = await setupServer());
+    savedObjectsClient = handlerContext.savedObjects.client;
+
+    savedObjectsClient.bulkGet.mockResolvedValue({
+      saved_objects: [],
+    });
+    const router = httpSetup.createRouter('/api/saved_objects/');
+    registerBulkGetRoute(router);
 
-    server.route(createBulkGetRoute(prereqs));
+    await server.start();
   });
 
-  afterEach(() => {
-    savedObjectsClient.bulkGet.mockReset();
+  afterEach(async () => {
+    await server.stop();
   });
 
   it('formats successful response', async () => {
-    const request = {
-      method: 'POST',
-      url: '/api/saved_objects/_bulk_get',
-      payload: [
-        {
-          id: 'abc123',
-          type: 'index-pattern',
-        },
-      ],
-    };
-
     const clientResponse = {
       saved_objects: [
         {
@@ -73,14 +61,19 @@ describe('POST /api/saved_objects/_bulk_get', () => {
         },
       ],
     };
-
     savedObjectsClient.bulkGet.mockImplementation(() => Promise.resolve(clientResponse));
 
-    const { payload, statusCode } = await server.inject(request);
-    const response = JSON.parse(payload);
+    const result = await supertest(httpSetup.server.listener)
+      .post('/api/saved_objects/_bulk_get')
+      .send([
+        {
+          id: 'abc123',
+          type: 'index-pattern',
+        },
+      ])
+      .expect(200);
 
-    expect(statusCode).toBe(200);
-    expect(response).toEqual(clientResponse);
+    expect(result.body).toEqual(clientResponse);
   });
 
   it('calls upon savedObjectClient.bulkGet', async () => {
@@ -91,14 +84,12 @@ describe('POST /api/saved_objects/_bulk_get', () => {
       },
     ];
 
-    const request = {
-      method: 'POST',
-      url: '/api/saved_objects/_bulk_get',
-      payload: docs,
-    };
-
-    await server.inject(request);
+    await supertest(httpSetup.server.listener)
+      .post('/api/saved_objects/_bulk_get')
+      .send(docs)
+      .expect(200);
 
+    expect(savedObjectsClient.bulkGet).toHaveBeenCalledTimes(1);
     expect(savedObjectsClient.bulkGet).toHaveBeenCalledWith(docs);
   });
 });
diff --git a/src/legacy/server/saved_objects/routes/bulk_update.test.ts b/src/core/server/saved_objects/routes/integration_tests/bulk_update.test.ts
similarity index 65%
rename from src/legacy/server/saved_objects/routes/bulk_update.test.ts
rename to src/core/server/saved_objects/routes/integration_tests/bulk_update.test.ts
index dc21ab08035ce..6356fc787a8d8 100644
--- a/src/legacy/server/saved_objects/routes/bulk_update.test.ts
+++ b/src/core/server/saved_objects/routes/integration_tests/bulk_update.test.ts
@@ -17,56 +17,35 @@
  * under the License.
  */
 
-import Hapi from 'hapi';
-import { createMockServer } from './_mock_server';
-import { createBulkUpdateRoute } from './bulk_update';
-import { savedObjectsClientMock } from '../../../../core/server/mocks';
+import supertest from 'supertest';
+import { UnwrapPromise } from '@kbn/utility-types';
+import { registerBulkUpdateRoute } from '../bulk_update';
+import { savedObjectsClientMock } from '../../../../../core/server/mocks';
+import { setupServer } from './test_utils';
+
+type setupServerReturn = UnwrapPromise<ReturnType<typeof setupServer>>;
 
 describe('PUT /api/saved_objects/_bulk_update', () => {
-  let server: Hapi.Server;
-  const savedObjectsClient = savedObjectsClientMock.create();
+  let server: setupServerReturn['server'];
+  let httpSetup: setupServerReturn['httpSetup'];
+  let handlerContext: setupServerReturn['handlerContext'];
+  let savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
 
-  beforeEach(() => {
-    server = createMockServer();
+  beforeEach(async () => {
+    ({ server, httpSetup, handlerContext } = await setupServer());
+    savedObjectsClient = handlerContext.savedObjects.client;
 
-    const prereqs = {
-      getSavedObjectsClient: {
-        assign: 'savedObjectsClient',
-        method() {
-          return savedObjectsClient;
-        },
-      },
-    };
+    const router = httpSetup.createRouter('/api/saved_objects/');
+    registerBulkUpdateRoute(router);
 
-    server.route(createBulkUpdateRoute(prereqs));
+    await server.start();
   });
 
-  afterEach(() => {
-    savedObjectsClient.bulkUpdate.mockReset();
+  afterEach(async () => {
+    await server.stop();
   });
 
   it('formats successful response', async () => {
-    const request = {
-      method: 'PUT',
-      url: '/api/saved_objects/_bulk_update',
-      payload: [
-        {
-          type: 'visualization',
-          id: 'dd7caf20-9efd-11e7-acb3-3dab96693fab',
-          attributes: {
-            title: 'An existing visualization',
-          },
-        },
-        {
-          type: 'dashboard',
-          id: 'be3733a0-9efe-11e7-acb3-3dab96693fab',
-          attributes: {
-            title: 'An existing dashboard',
-          },
-        },
-      ],
-    };
-
     const time = Date.now().toLocaleString();
     const clientResponse = [
       {
@@ -90,23 +69,37 @@ describe('PUT /api/saved_objects/_bulk_update', () => {
         },
       },
     ];
+    savedObjectsClient.bulkUpdate.mockResolvedValue({ saved_objects: clientResponse });
 
-    savedObjectsClient.bulkUpdate.mockImplementation(() =>
-      Promise.resolve({ saved_objects: clientResponse })
-    );
-
-    const { payload, statusCode } = await server.inject(request);
-    const response = JSON.parse(payload);
+    const result = await supertest(httpSetup.server.listener)
+      .put('/api/saved_objects/_bulk_update')
+      .send([
+        {
+          type: 'visualization',
+          id: 'dd7caf20-9efd-11e7-acb3-3dab96693fab',
+          attributes: {
+            title: 'An existing visualization',
+          },
+        },
+        {
+          type: 'dashboard',
+          id: 'be3733a0-9efe-11e7-acb3-3dab96693fab',
+          attributes: {
+            title: 'An existing dashboard',
+          },
+        },
+      ])
+      .expect(200);
 
-    expect(statusCode).toBe(200);
-    expect(response).toEqual({ saved_objects: clientResponse });
+    expect(result.body).toEqual({ saved_objects: clientResponse });
   });
 
   it('calls upon savedObjectClient.bulkUpdate', async () => {
-    const request = {
-      method: 'PUT',
-      url: '/api/saved_objects/_bulk_update',
-      payload: [
+    savedObjectsClient.bulkUpdate.mockResolvedValue({ saved_objects: [] });
+
+    await supertest(httpSetup.server.listener)
+      .put('/api/saved_objects/_bulk_update')
+      .send([
         {
           type: 'visualization',
           id: 'dd7caf20-9efd-11e7-acb3-3dab96693fab',
@@ -121,13 +114,10 @@ describe('PUT /api/saved_objects/_bulk_update', () => {
             title: 'An existing dashboard',
           },
         },
-      ],
-    };
-
-    savedObjectsClient.bulkUpdate.mockImplementation(() => Promise.resolve({ saved_objects: [] }));
-
-    await server.inject(request);
+      ])
+      .expect(200);
 
+    expect(savedObjectsClient.bulkUpdate).toHaveBeenCalledTimes(1);
     expect(savedObjectsClient.bulkUpdate).toHaveBeenCalledWith([
       {
         type: 'visualization',
diff --git a/src/core/server/saved_objects/routes/integration_tests/create.test.ts b/src/core/server/saved_objects/routes/integration_tests/create.test.ts
new file mode 100644
index 0000000000000..5a53a30209281
--- /dev/null
+++ b/src/core/server/saved_objects/routes/integration_tests/create.test.ts
@@ -0,0 +1,120 @@
+/*
+ * 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 supertest from 'supertest';
+import { UnwrapPromise } from '@kbn/utility-types';
+import { registerCreateRoute } from '../create';
+import { savedObjectsClientMock } from '../../service/saved_objects_client.mock';
+import { setupServer } from './test_utils';
+
+type setupServerReturn = UnwrapPromise<ReturnType<typeof setupServer>>;
+
+describe('POST /api/saved_objects/{type}', () => {
+  let server: setupServerReturn['server'];
+  let httpSetup: setupServerReturn['httpSetup'];
+  let handlerContext: setupServerReturn['handlerContext'];
+  let savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
+
+  const clientResponse = {
+    id: 'logstash-*',
+    type: 'index-pattern',
+    title: 'logstash-*',
+    version: 'foo',
+    references: [],
+    attributes: {},
+  };
+
+  beforeEach(async () => {
+    ({ server, httpSetup, handlerContext } = await setupServer());
+    savedObjectsClient = handlerContext.savedObjects.client;
+    savedObjectsClient.create.mockImplementation(() => Promise.resolve(clientResponse));
+
+    const router = httpSetup.createRouter('/api/saved_objects/');
+    registerCreateRoute(router);
+
+    await server.start();
+  });
+
+  afterEach(async () => {
+    await server.stop();
+  });
+
+  it('formats successful response', async () => {
+    const result = await supertest(httpSetup.server.listener)
+      .post('/api/saved_objects/index-pattern')
+      .send({
+        attributes: {
+          title: 'Testing',
+        },
+      })
+      .expect(200);
+
+    expect(result.body).toEqual(clientResponse);
+  });
+
+  it('requires attributes', async () => {
+    const result = await supertest(httpSetup.server.listener)
+      .post('/api/saved_objects/index-pattern')
+      .send({})
+      .expect(400);
+
+    // expect(response.validation.keys).toContain('attributes');
+    expect(result.body.message).toMatchInlineSnapshot(
+      `"[request body.attributes]: expected value of type [object] but got [undefined]"`
+    );
+  });
+
+  it('calls upon savedObjectClient.create', async () => {
+    await supertest(httpSetup.server.listener)
+      .post('/api/saved_objects/index-pattern')
+      .send({
+        attributes: {
+          title: 'Testing',
+        },
+      })
+      .expect(200);
+
+    expect(savedObjectsClient.create).toHaveBeenCalledTimes(1);
+    expect(savedObjectsClient.create).toHaveBeenCalledWith(
+      'index-pattern',
+      { title: 'Testing' },
+      { overwrite: false, id: undefined, migrationVersion: undefined }
+    );
+  });
+
+  it('can specify an id', async () => {
+    await supertest(httpSetup.server.listener)
+      .post('/api/saved_objects/index-pattern/logstash-*')
+      .send({
+        attributes: {
+          title: 'Testing',
+        },
+      })
+      .expect(200);
+
+    expect(savedObjectsClient.create).toHaveBeenCalledTimes(1);
+
+    const args = savedObjectsClient.create.mock.calls[0];
+    expect(args).toEqual([
+      'index-pattern',
+      { title: 'Testing' },
+      { overwrite: false, id: 'logstash-*' },
+    ]);
+  });
+});
diff --git a/src/core/server/saved_objects/routes/integration_tests/delete.test.ts b/src/core/server/saved_objects/routes/integration_tests/delete.test.ts
new file mode 100644
index 0000000000000..d4ce4d421dde1
--- /dev/null
+++ b/src/core/server/saved_objects/routes/integration_tests/delete.test.ts
@@ -0,0 +1,63 @@
+/*
+ * 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 supertest from 'supertest';
+import { UnwrapPromise } from '@kbn/utility-types';
+import { registerDeleteRoute } from '../delete';
+import { savedObjectsClientMock } from '../../../../../core/server/mocks';
+import { setupServer } from './test_utils';
+
+type setupServerReturn = UnwrapPromise<ReturnType<typeof setupServer>>;
+
+describe('DELETE /api/saved_objects/{type}/{id}', () => {
+  let server: setupServerReturn['server'];
+  let httpSetup: setupServerReturn['httpSetup'];
+  let handlerContext: setupServerReturn['handlerContext'];
+  let savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
+
+  beforeEach(async () => {
+    ({ server, httpSetup, handlerContext } = await setupServer());
+    savedObjectsClient = handlerContext.savedObjects.client;
+
+    const router = httpSetup.createRouter('/api/saved_objects/');
+    registerDeleteRoute(router);
+
+    await server.start();
+  });
+
+  afterEach(async () => {
+    await server.stop();
+  });
+
+  it('formats successful response', async () => {
+    const result = await supertest(httpSetup.server.listener)
+      .delete('/api/saved_objects/index-pattern/logstash-*')
+      .expect(200);
+
+    expect(result.body).toEqual({});
+  });
+
+  it('calls upon savedObjectClient.delete', async () => {
+    await supertest(httpSetup.server.listener)
+      .delete('/api/saved_objects/index-pattern/logstash-*')
+      .expect(200);
+
+    expect(savedObjectsClient.delete).toHaveBeenCalledWith('index-pattern', 'logstash-*');
+  });
+});
diff --git a/src/core/server/saved_objects/routes/integration_tests/export.test.ts b/src/core/server/saved_objects/routes/integration_tests/export.test.ts
new file mode 100644
index 0000000000000..b52a8957176cc
--- /dev/null
+++ b/src/core/server/saved_objects/routes/integration_tests/export.test.ts
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+
+jest.mock('../../export', () => ({
+  getSortedObjectsForExport: jest.fn(),
+}));
+
+import * as exportMock from '../../export';
+import { createListStream } from '../../../../../legacy/utils/streams';
+import supertest from 'supertest';
+import { UnwrapPromise } from '@kbn/utility-types';
+import { SavedObjectConfig } from '../../saved_objects_config';
+import { registerExportRoute } from '../export';
+import { setupServer } from './test_utils';
+
+type setupServerReturn = UnwrapPromise<ReturnType<typeof setupServer>>;
+const getSortedObjectsForExport = exportMock.getSortedObjectsForExport as jest.Mock;
+const allowedTypes = ['index-pattern', 'search'];
+const config = {
+  maxImportPayloadBytes: 10485760,
+  maxImportExportSize: 10000,
+} as SavedObjectConfig;
+
+describe('POST /api/saved_objects/_export', () => {
+  let server: setupServerReturn['server'];
+  let httpSetup: setupServerReturn['httpSetup'];
+
+  beforeEach(async () => {
+    ({ server, httpSetup } = await setupServer());
+
+    const router = httpSetup.createRouter('/api/saved_objects/');
+    registerExportRoute(router, config, allowedTypes);
+
+    await server.start();
+  });
+
+  afterEach(async () => {
+    jest.clearAllMocks();
+    await server.stop();
+  });
+
+  it('formats successful response', async () => {
+    const sortedObjects = [
+      {
+        id: '1',
+        type: 'index-pattern',
+        attributes: {},
+        references: [],
+      },
+      {
+        id: '2',
+        type: 'search',
+        attributes: {},
+        references: [
+          {
+            name: 'ref_0',
+            type: 'index-pattern',
+            id: '1',
+          },
+        ],
+      },
+    ];
+    getSortedObjectsForExport.mockResolvedValueOnce(createListStream(sortedObjects));
+
+    const result = await supertest(httpSetup.server.listener)
+      .post('/api/saved_objects/_export')
+      .send({
+        type: 'search',
+        search: 'my search string',
+        includeReferencesDeep: true,
+      });
+
+    expect(result.status).toBe(200);
+    expect(result.header).toEqual(
+      expect.objectContaining({
+        'content-disposition': 'attachment; filename="export.ndjson"',
+        'content-type': 'application/ndjson',
+      })
+    );
+
+    const objects = (result.text as string).split('\n').map(row => JSON.parse(row));
+    expect(objects).toEqual(sortedObjects);
+    expect(getSortedObjectsForExport.mock.calls[0][0]).toEqual(
+      expect.objectContaining({
+        excludeExportDetails: false,
+        exportSizeLimit: 10000,
+        includeReferencesDeep: true,
+        objects: undefined,
+        search: 'my search string',
+        types: ['search'],
+      })
+    );
+  });
+});
diff --git a/src/legacy/server/saved_objects/routes/find.test.ts b/src/core/server/saved_objects/routes/integration_tests/find.test.ts
similarity index 55%
rename from src/legacy/server/saved_objects/routes/find.test.ts
rename to src/core/server/saved_objects/routes/integration_tests/find.test.ts
index 4bf5f57fec199..907bf44c7748f 100644
--- a/src/legacy/server/saved_objects/routes/find.test.ts
+++ b/src/core/server/saved_objects/routes/integration_tests/find.test.ts
@@ -17,14 +17,21 @@
  * under the License.
  */
 
-import Hapi from 'hapi';
-import { createMockServer } from './_mock_server';
-import { createFindRoute } from './find';
-import { savedObjectsClientMock } from '../../../../core/server/mocks';
+import supertest from 'supertest';
+import querystring from 'querystring';
+
+import { UnwrapPromise } from '@kbn/utility-types';
+import { registerFindRoute } from '../find';
+import { savedObjectsClientMock } from '../../../../../core/server/mocks';
+import { setupServer } from './test_utils';
+
+type setupServerReturn = UnwrapPromise<ReturnType<typeof setupServer>>;
 
 describe('GET /api/saved_objects/_find', () => {
-  let server: Hapi.Server;
-  const savedObjectsClient = savedObjectsClientMock.create();
+  let server: setupServerReturn['server'];
+  let httpSetup: setupServerReturn['httpSetup'];
+  let handlerContext: setupServerReturn['handlerContext'];
+  let savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
 
   const clientResponse = {
     total: 0,
@@ -32,48 +39,34 @@ describe('GET /api/saved_objects/_find', () => {
     per_page: 0,
     page: 0,
   };
-  beforeEach(() => {
-    savedObjectsClient.find.mockImplementation(() => Promise.resolve(clientResponse));
-    server = createMockServer();
-
-    const prereqs = {
-      getSavedObjectsClient: {
-        assign: 'savedObjectsClient',
-        method() {
-          return savedObjectsClient;
-        },
-      },
-    };
 
-    server.route(createFindRoute(prereqs));
+  beforeEach(async () => {
+    ({ server, httpSetup, handlerContext } = await setupServer());
+    savedObjectsClient = handlerContext.savedObjects.client;
+
+    savedObjectsClient.find.mockResolvedValue(clientResponse);
+
+    const router = httpSetup.createRouter('/api/saved_objects/');
+    registerFindRoute(router);
+
+    await server.start();
   });
 
-  afterEach(() => {
-    savedObjectsClient.find.mockReset();
+  afterEach(async () => {
+    await server.stop();
   });
 
   it('returns with status 400 when type is missing', async () => {
-    const request = {
-      method: 'GET',
-      url: '/api/saved_objects/_find',
-    };
+    const result = await supertest(httpSetup.server.listener)
+      .get('/api/saved_objects/_find')
+      .expect(400);
 
-    const { payload, statusCode } = await server.inject(request);
-
-    expect(statusCode).toEqual(400);
-    expect(JSON.parse(payload)).toMatchObject({
-      statusCode: 400,
-      error: 'Bad Request',
-      message: 'child "type" fails because ["type" is required]',
-    });
+    expect(result.body.message).toContain(
+      '[request query.type]: expected at least one defined value'
+    );
   });
 
   it('formats successful response', async () => {
-    const request = {
-      method: 'GET',
-      url: '/api/saved_objects/_find?type=index-pattern',
-    };
-
     const findResponse = {
       total: 2,
       per_page: 2,
@@ -99,23 +92,19 @@ describe('GET /api/saved_objects/_find', () => {
         },
       ],
     };
+    savedObjectsClient.find.mockResolvedValue(findResponse);
 
-    savedObjectsClient.find.mockImplementation(() => Promise.resolve(findResponse));
-
-    const { payload, statusCode } = await server.inject(request);
-    const response = JSON.parse(payload);
+    const result = await supertest(httpSetup.server.listener)
+      .get('/api/saved_objects/_find?type=index-pattern')
+      .expect(200);
 
-    expect(statusCode).toBe(200);
-    expect(response).toEqual(findResponse);
+    expect(result.body).toEqual(findResponse);
   });
 
   it('calls upon savedObjectClient.find with defaults', async () => {
-    const request = {
-      method: 'GET',
-      url: '/api/saved_objects/_find?type=foo&type=bar',
-    };
-
-    await server.inject(request);
+    await supertest(httpSetup.server.listener)
+      .get('/api/saved_objects/_find?type=foo&type=bar')
+      .expect(200);
 
     expect(savedObjectsClient.find).toHaveBeenCalledTimes(1);
 
@@ -129,12 +118,9 @@ describe('GET /api/saved_objects/_find', () => {
   });
 
   it('accepts the query parameter page/per_page', async () => {
-    const request = {
-      method: 'GET',
-      url: '/api/saved_objects/_find?type=foo&per_page=10&page=50',
-    };
-
-    await server.inject(request);
+    await supertest(httpSetup.server.listener)
+      .get('/api/saved_objects/_find?type=foo&per_page=10&page=50')
+      .expect(200);
 
     expect(savedObjectsClient.find).toHaveBeenCalledTimes(1);
 
@@ -142,13 +128,41 @@ describe('GET /api/saved_objects/_find', () => {
     expect(options).toEqual({ perPage: 10, page: 50, type: ['foo'], defaultSearchOperator: 'OR' });
   });
 
-  it('accepts the query parameter search_fields', async () => {
-    const request = {
-      method: 'GET',
-      url: '/api/saved_objects/_find?type=foo&search_fields=title',
-    };
+  it('accepts the optional query parameter has_reference', async () => {
+    await supertest(httpSetup.server.listener)
+      .get('/api/saved_objects/_find?type=foo')
+      .expect(200);
+
+    expect(savedObjectsClient.find).toHaveBeenCalledTimes(1);
+
+    const options = savedObjectsClient.find.mock.calls[0][0];
+    expect(options.hasReference).toBe(undefined);
+  });
+
+  it('accepts the query parameter has_reference', async () => {
+    const references = querystring.escape(
+      JSON.stringify({
+        id: '1',
+        type: 'reference',
+      })
+    );
+    await supertest(httpSetup.server.listener)
+      .get(`/api/saved_objects/_find?type=foo&has_reference=${references}`)
+      .expect(200);
+
+    expect(savedObjectsClient.find).toHaveBeenCalledTimes(1);
+
+    const options = savedObjectsClient.find.mock.calls[0][0];
+    expect(options.hasReference).toEqual({
+      id: '1',
+      type: 'reference',
+    });
+  });
 
-    await server.inject(request);
+  it('accepts the query parameter search_fields', async () => {
+    await supertest(httpSetup.server.listener)
+      .get('/api/saved_objects/_find?type=foo&search_fields=title')
+      .expect(200);
 
     expect(savedObjectsClient.find).toHaveBeenCalledTimes(1);
 
@@ -163,12 +177,9 @@ describe('GET /api/saved_objects/_find', () => {
   });
 
   it('accepts the query parameter fields as a string', async () => {
-    const request = {
-      method: 'GET',
-      url: '/api/saved_objects/_find?type=foo&fields=title',
-    };
-
-    await server.inject(request);
+    await supertest(httpSetup.server.listener)
+      .get('/api/saved_objects/_find?type=foo&fields=title')
+      .expect(200);
 
     expect(savedObjectsClient.find).toHaveBeenCalledTimes(1);
 
@@ -183,12 +194,9 @@ describe('GET /api/saved_objects/_find', () => {
   });
 
   it('accepts the query parameter fields as an array', async () => {
-    const request = {
-      method: 'GET',
-      url: '/api/saved_objects/_find?type=foo&fields=title&fields=description',
-    };
-
-    await server.inject(request);
+    await supertest(httpSetup.server.listener)
+      .get('/api/saved_objects/_find?type=foo&fields=title&fields=description')
+      .expect(200);
 
     expect(savedObjectsClient.find).toHaveBeenCalledTimes(1);
 
@@ -203,12 +211,9 @@ describe('GET /api/saved_objects/_find', () => {
   });
 
   it('accepts the query parameter type as a string', async () => {
-    const request = {
-      method: 'GET',
-      url: '/api/saved_objects/_find?type=index-pattern',
-    };
-
-    await server.inject(request);
+    await supertest(httpSetup.server.listener)
+      .get('/api/saved_objects/_find?type=index-pattern')
+      .expect(200);
 
     expect(savedObjectsClient.find).toHaveBeenCalledTimes(1);
 
@@ -222,12 +227,9 @@ describe('GET /api/saved_objects/_find', () => {
   });
 
   it('accepts the query parameter type as an array', async () => {
-    const request = {
-      method: 'GET',
-      url: '/api/saved_objects/_find?type=index-pattern&type=visualization',
-    };
-
-    await server.inject(request);
+    await supertest(httpSetup.server.listener)
+      .get('/api/saved_objects/_find?type=index-pattern&type=visualization')
+      .expect(200);
 
     expect(savedObjectsClient.find).toHaveBeenCalledTimes(1);
 
diff --git a/src/core/server/saved_objects/routes/integration_tests/get.test.ts b/src/core/server/saved_objects/routes/integration_tests/get.test.ts
new file mode 100644
index 0000000000000..1e3405d7a318f
--- /dev/null
+++ b/src/core/server/saved_objects/routes/integration_tests/get.test.ts
@@ -0,0 +1,92 @@
+/*
+ * 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 supertest from 'supertest';
+import { registerGetRoute } from '../get';
+import { ContextService } from '../../../context';
+import { savedObjectsClientMock } from '../../service/saved_objects_client.mock';
+import { HttpService, InternalHttpServiceSetup } from '../../../http';
+import { createHttpServer, createCoreContext } from '../../../http/test_utils';
+import { coreMock } from '../../../mocks';
+
+const coreId = Symbol('core');
+
+describe('GET /api/saved_objects/{type}/{id}', () => {
+  let server: HttpService;
+  let httpSetup: InternalHttpServiceSetup;
+  let handlerContext: ReturnType<typeof coreMock.createRequestHandlerContext>;
+  let savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
+
+  beforeEach(async () => {
+    const coreContext = createCoreContext({ coreId });
+    server = createHttpServer(coreContext);
+
+    const contextService = new ContextService(coreContext);
+    httpSetup = await server.setup({
+      context: contextService.setup({ pluginDependencies: new Map() }),
+    });
+
+    handlerContext = coreMock.createRequestHandlerContext();
+    savedObjectsClient = handlerContext.savedObjects.client;
+
+    httpSetup.registerRouteHandlerContext(coreId, 'core', async (ctx, req, res) => {
+      return handlerContext;
+    });
+
+    const router = httpSetup.createRouter('/api/saved_objects/');
+    registerGetRoute(router);
+
+    await server.start();
+  });
+
+  afterEach(async () => {
+    await server.stop();
+  });
+
+  it('formats successful response', async () => {
+    const clientResponse = {
+      id: 'logstash-*',
+      title: 'logstash-*',
+      type: 'logstash-type',
+      attributes: {},
+      timeFieldName: '@timestamp',
+      notExpandable: true,
+      references: [],
+    };
+
+    savedObjectsClient.get.mockResolvedValue(clientResponse);
+
+    const result = await supertest(httpSetup.server.listener)
+      .get('/api/saved_objects/index-pattern/logstash-*')
+      .expect(200);
+
+    expect(result.body).toEqual(clientResponse);
+  });
+
+  it('calls upon savedObjectClient.get', async () => {
+    await supertest(httpSetup.server.listener)
+      .get('/api/saved_objects/index-pattern/logstash-*')
+      .expect(200);
+
+    expect(savedObjectsClient.get).toHaveBeenCalled();
+
+    const args = savedObjectsClient.get.mock.calls[0];
+    expect(args).toEqual(['index-pattern', 'logstash-*']);
+  });
+});
diff --git a/src/core/server/saved_objects/routes/integration_tests/import.test.ts b/src/core/server/saved_objects/routes/integration_tests/import.test.ts
new file mode 100644
index 0000000000000..2c8d568b750c6
--- /dev/null
+++ b/src/core/server/saved_objects/routes/integration_tests/import.test.ts
@@ -0,0 +1,320 @@
+/*
+ * 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 supertest from 'supertest';
+import { UnwrapPromise } from '@kbn/utility-types';
+import { registerImportRoute } from '../import';
+import { savedObjectsClientMock } from '../../../../../core/server/mocks';
+import { SavedObjectConfig } from '../../saved_objects_config';
+import { setupServer } from './test_utils';
+
+type setupServerReturn = UnwrapPromise<ReturnType<typeof setupServer>>;
+
+const allowedTypes = ['index-pattern', 'visualization', 'dashboard'];
+const config = {
+  maxImportPayloadBytes: 10485760,
+  maxImportExportSize: 10000,
+} as SavedObjectConfig;
+
+describe('POST /api/saved_objects/_import', () => {
+  let server: setupServerReturn['server'];
+  let httpSetup: setupServerReturn['httpSetup'];
+  let handlerContext: setupServerReturn['handlerContext'];
+  let savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
+
+  const emptyResponse = {
+    saved_objects: [],
+    total: 0,
+    per_page: 0,
+    page: 0,
+  };
+
+  beforeEach(async () => {
+    ({ server, httpSetup, handlerContext } = await setupServer());
+    savedObjectsClient = handlerContext.savedObjects.client;
+
+    savedObjectsClient.find.mockResolvedValue(emptyResponse);
+
+    const router = httpSetup.createRouter('/api/saved_objects/');
+    registerImportRoute(router, config, allowedTypes);
+
+    await server.start();
+  });
+
+  afterEach(async () => {
+    await server.stop();
+  });
+
+  it('formats successful response', async () => {
+    const result = await supertest(httpSetup.server.listener)
+      .post('/api/saved_objects/_import')
+      .set('content-Type', 'multipart/form-data; boundary=BOUNDARY')
+      .send(
+        [
+          '--BOUNDARY',
+          'Content-Disposition: form-data; name="file"; filename="export.ndjson"',
+          'Content-Type: application/ndjson',
+          '',
+          '',
+          '--BOUNDARY--',
+        ].join('\r\n')
+      )
+      .expect(200);
+
+    expect(result.body).toEqual({
+      success: true,
+      successCount: 0,
+    });
+    expect(savedObjectsClient.bulkCreate).toHaveBeenCalledTimes(0);
+  });
+
+  it('defaults migrationVersion to empty object', async () => {
+    savedObjectsClient.bulkCreate.mockResolvedValueOnce({
+      saved_objects: [
+        {
+          type: 'index-pattern',
+          id: 'my-pattern',
+          attributes: {
+            title: 'my-pattern-*',
+          },
+          references: [],
+        },
+      ],
+    });
+
+    const result = await supertest(httpSetup.server.listener)
+      .post('/api/saved_objects/_import')
+      .set('content-Type', 'multipart/form-data; boundary=EXAMPLE')
+      .send(
+        [
+          '--EXAMPLE',
+          'Content-Disposition: form-data; name="file"; filename="export.ndjson"',
+          'Content-Type: application/ndjson',
+          '',
+          '{"type":"index-pattern","id":"my-pattern","attributes":{"title":"my-pattern-*"}}',
+          '--EXAMPLE--',
+        ].join('\r\n')
+      )
+      .expect(200);
+
+    expect(result.body).toEqual({
+      success: true,
+      successCount: 1,
+    });
+    expect(savedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1);
+    const firstBulkCreateCallArray = savedObjectsClient.bulkCreate.mock.calls[0][0];
+    expect(firstBulkCreateCallArray).toHaveLength(1);
+    expect(firstBulkCreateCallArray[0].migrationVersion).toEqual({});
+  });
+
+  it('imports an index pattern and dashboard, ignoring empty lines in the file', async () => {
+    // NOTE: changes to this scenario should be reflected in the docs
+
+    savedObjectsClient.bulkCreate.mockResolvedValueOnce({
+      saved_objects: [
+        {
+          type: 'index-pattern',
+          id: 'my-pattern',
+          attributes: {
+            title: 'my-pattern-*',
+          },
+          references: [],
+        },
+        {
+          type: 'dashboard',
+          id: 'my-dashboard',
+          attributes: {
+            title: 'Look at my dashboard',
+          },
+          references: [],
+        },
+      ],
+    });
+
+    const result = await supertest(httpSetup.server.listener)
+      .post('/api/saved_objects/_import')
+      .set('content-Type', 'multipart/form-data; boundary=EXAMPLE')
+      .send(
+        [
+          '--EXAMPLE',
+          'Content-Disposition: form-data; name="file"; filename="export.ndjson"',
+          'Content-Type: application/ndjson',
+          '',
+          '{"type":"index-pattern","id":"my-pattern","attributes":{"title":"my-pattern-*"}}',
+          '',
+          '',
+          '',
+          '{"type":"dashboard","id":"my-dashboard","attributes":{"title":"Look at my dashboard"}}',
+          '--EXAMPLE--',
+        ].join('\r\n')
+      )
+      .expect(200);
+
+    expect(result.body).toEqual({
+      success: true,
+      successCount: 2,
+    });
+  });
+
+  it('imports an index pattern and dashboard but has a conflict on the index pattern', async () => {
+    // NOTE: changes to this scenario should be reflected in the docs
+
+    savedObjectsClient.bulkCreate.mockResolvedValueOnce({
+      saved_objects: [
+        {
+          type: 'index-pattern',
+          id: 'my-pattern',
+          attributes: {},
+          references: [],
+          error: {
+            statusCode: 409,
+            message: 'version conflict, document already exists',
+          },
+        },
+        {
+          type: 'dashboard',
+          id: 'my-dashboard',
+          attributes: {
+            title: 'Look at my dashboard',
+          },
+          references: [],
+        },
+      ],
+    });
+
+    const result = await supertest(httpSetup.server.listener)
+      .post('/api/saved_objects/_import')
+      .set('content-Type', 'multipart/form-data; boundary=EXAMPLE')
+      .send(
+        [
+          '--EXAMPLE',
+          'Content-Disposition: form-data; name="file"; filename="export.ndjson"',
+          'Content-Type: application/ndjson',
+          '',
+          '{"type":"index-pattern","id":"my-pattern","attributes":{"title":"my-pattern-*"}}',
+          '{"type":"dashboard","id":"my-dashboard","attributes":{"title":"Look at my dashboard"}}',
+          '--EXAMPLE--',
+        ].join('\r\n')
+      )
+      .expect(200);
+
+    expect(result.body).toEqual({
+      success: false,
+      successCount: 1,
+      errors: [
+        {
+          id: 'my-pattern',
+          type: 'index-pattern',
+          title: 'my-pattern-*',
+          error: {
+            type: 'conflict',
+          },
+        },
+      ],
+    });
+  });
+
+  it('imports a visualization with missing references', async () => {
+    // NOTE: changes to this scenario should be reflected in the docs
+
+    savedObjectsClient.bulkGet.mockResolvedValueOnce({
+      saved_objects: [
+        {
+          id: 'my-pattern-*',
+          type: 'index-pattern',
+          error: {
+            statusCode: 404,
+            message: 'Not found',
+          },
+          references: [],
+          attributes: {},
+        },
+      ],
+    });
+
+    const result = await supertest(httpSetup.server.listener)
+      .post('/api/saved_objects/_import')
+      .set('content-Type', 'multipart/form-data; boundary=EXAMPLE')
+      .send(
+        [
+          '--EXAMPLE',
+          'Content-Disposition: form-data; name="file"; filename="export.ndjson"',
+          'Content-Type: application/ndjson',
+          '',
+          '{"type":"visualization","id":"my-vis","attributes":{"title":"my-vis"},"references":[{"name":"ref_0","type":"index-pattern","id":"my-pattern-*"}]}',
+          '{"type":"dashboard","id":"my-dashboard","attributes":{"title":"Look at my dashboard"},"references":[{"name":"ref_0","type":"visualization","id":"my-vis"}]}',
+          '--EXAMPLE--',
+        ].join('\r\n')
+      )
+      .expect(200);
+
+    expect(result.body).toEqual({
+      success: false,
+      successCount: 0,
+      errors: [
+        {
+          id: 'my-vis',
+          type: 'visualization',
+          title: 'my-vis',
+          error: {
+            type: 'missing_references',
+            references: [
+              {
+                type: 'index-pattern',
+                id: 'my-pattern-*',
+              },
+            ],
+            blocking: [
+              {
+                type: 'dashboard',
+                id: 'my-dashboard',
+              },
+            ],
+          },
+        },
+      ],
+    });
+    expect(savedObjectsClient.bulkGet).toMatchInlineSnapshot(`
+[MockFunction] {
+  "calls": Array [
+    Array [
+      Array [
+        Object {
+          "fields": Array [
+            "id",
+          ],
+          "id": "my-pattern-*",
+          "type": "index-pattern",
+        },
+      ],
+      Object {
+        "namespace": undefined,
+      },
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`);
+  });
+});
diff --git a/src/core/server/saved_objects/routes/integration_tests/log_legacy_import.test.ts b/src/core/server/saved_objects/routes/integration_tests/log_legacy_import.test.ts
new file mode 100644
index 0000000000000..4bbe3271e0232
--- /dev/null
+++ b/src/core/server/saved_objects/routes/integration_tests/log_legacy_import.test.ts
@@ -0,0 +1,61 @@
+/*
+ * 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 supertest from 'supertest';
+import { UnwrapPromise } from '@kbn/utility-types';
+import { registerLogLegacyImportRoute } from '../log_legacy_import';
+import { loggingServiceMock } from '../../../logging/logging_service.mock';
+import { setupServer } from './test_utils';
+
+type setupServerReturn = UnwrapPromise<ReturnType<typeof setupServer>>;
+
+describe('POST /api/saved_objects/_log_legacy_import', () => {
+  let server: setupServerReturn['server'];
+  let httpSetup: setupServerReturn['httpSetup'];
+  let logger: ReturnType<typeof loggingServiceMock.createLogger>;
+
+  beforeEach(async () => {
+    ({ server, httpSetup } = await setupServer());
+    logger = loggingServiceMock.createLogger();
+
+    const router = httpSetup.createRouter('/api/saved_objects/');
+    registerLogLegacyImportRoute(router, logger);
+
+    await server.start();
+  });
+
+  afterEach(async () => {
+    await server.stop();
+  });
+
+  it('logs a warning when called', async () => {
+    const result = await supertest(httpSetup.server.listener)
+      .post('/api/saved_objects/_log_legacy_import')
+      .expect(200);
+
+    expect(result.body).toEqual({ success: true });
+    expect(loggingServiceMock.collect(logger).warn).toMatchInlineSnapshot(`
+      Array [
+        Array [
+          "Importing saved objects from a .json file has been deprecated",
+        ],
+      ]
+    `);
+  });
+});
diff --git a/src/legacy/server/saved_objects/routes/resolve_import_errors.test.ts b/src/core/server/saved_objects/routes/integration_tests/resolve_import_errors.test.ts
similarity index 51%
rename from src/legacy/server/saved_objects/routes/resolve_import_errors.test.ts
rename to src/core/server/saved_objects/routes/integration_tests/resolve_import_errors.test.ts
index 44fa46bccfce5..c2974395217f8 100644
--- a/src/legacy/server/saved_objects/routes/resolve_import_errors.test.ts
+++ b/src/core/server/saved_objects/routes/integration_tests/resolve_import_errors.test.ts
@@ -17,84 +17,66 @@
  * under the License.
  */
 
-import Hapi from 'hapi';
-import { createMockServer } from './_mock_server';
-import { createResolveImportErrorsRoute } from './resolve_import_errors';
-import { savedObjectsClientMock } from '../../../../core/server/mocks';
+import supertest from 'supertest';
+import { UnwrapPromise } from '@kbn/utility-types';
+import { registerResolveImportErrorsRoute } from '../resolve_import_errors';
+import { savedObjectsClientMock } from '../../../../../core/server/mocks';
+import { setupServer } from './test_utils';
+import { SavedObjectConfig } from '../../saved_objects_config';
+
+type setupServerReturn = UnwrapPromise<ReturnType<typeof setupServer>>;
+
+const allowedTypes = ['index-pattern', 'visualization', 'dashboard'];
+const config = {
+  maxImportPayloadBytes: 10485760,
+  maxImportExportSize: 10000,
+} as SavedObjectConfig;
 
 describe('POST /api/saved_objects/_resolve_import_errors', () => {
-  let server: Hapi.Server;
-  const savedObjectsClient = savedObjectsClientMock.create();
+  let server: setupServerReturn['server'];
+  let httpSetup: setupServerReturn['httpSetup'];
+  let handlerContext: setupServerReturn['handlerContext'];
+  let savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
 
-  beforeEach(() => {
-    server = createMockServer();
-    jest.resetAllMocks();
+  beforeEach(async () => {
+    ({ server, httpSetup, handlerContext } = await setupServer());
+    savedObjectsClient = handlerContext.savedObjects.client;
 
-    const prereqs = {
-      getSavedObjectsClient: {
-        assign: 'savedObjectsClient',
-        method() {
-          return savedObjectsClient;
-        },
-      },
-    };
+    const router = httpSetup.createRouter('/api/saved_objects/');
+    registerResolveImportErrorsRoute(router, config, allowedTypes);
 
-    server.route(
-      createResolveImportErrorsRoute(prereqs, server, [
-        'index-pattern',
-        'visualization',
-        'dashboard',
-      ])
-    );
+    await server.start();
   });
 
-  test('formats successful response', async () => {
-    const request = {
-      method: 'POST',
-      url: '/api/saved_objects/_resolve_import_errors',
-      payload: [
-        '--BOUNDARY',
-        'Content-Disposition: form-data; name="file"; filename="export.ndjson"',
-        'Content-Type: application/ndjson',
-        '',
-        '',
-        '--BOUNDARY',
-        'Content-Disposition: form-data; name="retries"',
-        '',
-        '[]',
-        '--BOUNDARY--',
-      ].join('\r\n'),
-      headers: {
-        'content-Type': 'multipart/form-data; boundary=BOUNDARY',
-      },
-    };
-    const { payload, statusCode } = await server.inject(request);
-    const response = JSON.parse(payload);
-    expect(statusCode).toBe(200);
-    expect(response).toEqual({ success: true, successCount: 0 });
+  afterEach(async () => {
+    await server.stop();
+  });
+
+  it('formats successful response', async () => {
+    const result = await supertest(httpSetup.server.listener)
+      .post('/api/saved_objects/_resolve_import_errors')
+      .set('content-Type', 'multipart/form-data; boundary=BOUNDARY')
+      .send(
+        [
+          '--BOUNDARY',
+          'Content-Disposition: form-data; name="file"; filename="export.ndjson"',
+          'Content-Type: application/ndjson',
+          '',
+          '',
+          '--BOUNDARY',
+          'Content-Disposition: form-data; name="retries"',
+          '',
+          '[]',
+          '--BOUNDARY--',
+        ].join('\r\n')
+      )
+      .expect(200);
+
+    expect(result.body).toEqual({ success: true, successCount: 0 });
     expect(savedObjectsClient.bulkCreate).toHaveBeenCalledTimes(0);
   });
 
-  test('defaults migrationVersion to empty object', async () => {
-    const request = {
-      method: 'POST',
-      url: '/api/saved_objects/_resolve_import_errors',
-      payload: [
-        '--EXAMPLE',
-        'Content-Disposition: form-data; name="file"; filename="export.ndjson"',
-        'Content-Type: application/ndjson',
-        '',
-        '{"type":"dashboard","id":"my-dashboard","attributes":{"title":"Look at my dashboard"}}',
-        '--EXAMPLE',
-        'Content-Disposition: form-data; name="retries"',
-        '',
-        '[{"type":"dashboard","id":"my-dashboard"}]',
-        '--EXAMPLE--',
-      ].join('\r\n'),
-      headers: {
-        'content-Type': 'multipart/form-data; boundary=EXAMPLE',
-      },
-    };
+  it('defaults migrationVersion to empty object', async () => {
     savedObjectsClient.bulkCreate.mockResolvedValueOnce({
       saved_objects: [
         {
@@ -107,37 +89,35 @@ describe('POST /api/saved_objects/_resolve_import_errors', () => {
         },
       ],
     });
-    const { payload, statusCode } = await server.inject(request);
-    const response = JSON.parse(payload);
-    expect(statusCode).toBe(200);
-    expect(response).toEqual({ success: true, successCount: 1 });
+
+    const result = await supertest(httpSetup.server.listener)
+      .post('/api/saved_objects/_resolve_import_errors')
+      .set('content-Type', 'multipart/form-data; boundary=EXAMPLE')
+      .send(
+        [
+          '--EXAMPLE',
+          'Content-Disposition: form-data; name="file"; filename="export.ndjson"',
+          'Content-Type: application/ndjson',
+          '',
+          '{"type":"dashboard","id":"my-dashboard","attributes":{"title":"Look at my dashboard"}}',
+          '--EXAMPLE',
+          'Content-Disposition: form-data; name="retries"',
+          '',
+          '[{"type":"dashboard","id":"my-dashboard"}]',
+          '--EXAMPLE--',
+        ].join('\r\n')
+      )
+      .expect(200);
+
+    expect(result.body).toEqual({ success: true, successCount: 1 });
     expect(savedObjectsClient.bulkCreate.mock.calls).toHaveLength(1);
     const firstBulkCreateCallArray = savedObjectsClient.bulkCreate.mock.calls[0][0];
     expect(firstBulkCreateCallArray).toHaveLength(1);
     expect(firstBulkCreateCallArray[0].migrationVersion).toEqual({});
   });
 
-  test('retries importing a dashboard', async () => {
+  it('retries importing a dashboard', async () => {
     // NOTE: changes to this scenario should be reflected in the docs
-    const request = {
-      method: 'POST',
-      url: '/api/saved_objects/_resolve_import_errors',
-      payload: [
-        '--EXAMPLE',
-        'Content-Disposition: form-data; name="file"; filename="export.ndjson"',
-        'Content-Type: application/ndjson',
-        '',
-        '{"type":"dashboard","id":"my-dashboard","attributes":{"title":"Look at my dashboard"}}',
-        '--EXAMPLE',
-        'Content-Disposition: form-data; name="retries"',
-        '',
-        '[{"type":"dashboard","id":"my-dashboard"}]',
-        '--EXAMPLE--',
-      ].join('\r\n'),
-      headers: {
-        'content-Type': 'multipart/form-data; boundary=EXAMPLE',
-      },
-    };
     savedObjectsClient.bulkCreate.mockResolvedValueOnce({
       saved_objects: [
         {
@@ -150,10 +130,27 @@ describe('POST /api/saved_objects/_resolve_import_errors', () => {
         },
       ],
     });
-    const { payload, statusCode } = await server.inject(request);
-    const response = JSON.parse(payload);
-    expect(statusCode).toBe(200);
-    expect(response).toEqual({ success: true, successCount: 1 });
+
+    const result = await supertest(httpSetup.server.listener)
+      .post('/api/saved_objects/_resolve_import_errors')
+      .set('content-Type', 'multipart/form-data; boundary=EXAMPLE')
+      .send(
+        [
+          '--EXAMPLE',
+          'Content-Disposition: form-data; name="file"; filename="export.ndjson"',
+          'Content-Type: application/ndjson',
+          '',
+          '{"type":"dashboard","id":"my-dashboard","attributes":{"title":"Look at my dashboard"}}',
+          '--EXAMPLE',
+          'Content-Disposition: form-data; name="retries"',
+          '',
+          '[{"type":"dashboard","id":"my-dashboard"}]',
+          '--EXAMPLE--',
+        ].join('\r\n')
+      )
+      .expect(200);
+
+    expect(result.body).toEqual({ success: true, successCount: 1 });
     expect(savedObjectsClient.bulkCreate).toMatchInlineSnapshot(`
       [MockFunction] {
         "calls": Array [
@@ -183,28 +180,8 @@ describe('POST /api/saved_objects/_resolve_import_errors', () => {
     `);
   });
 
-  test('resolves conflicts for dashboard', async () => {
+  it('resolves conflicts for dashboard', async () => {
     // NOTE: changes to this scenario should be reflected in the docs
-    const request = {
-      method: 'POST',
-      url: '/api/saved_objects/_resolve_import_errors',
-      payload: [
-        '--EXAMPLE',
-        'Content-Disposition: form-data; name="file"; filename="export.ndjson"',
-        'Content-Type: application/ndjson',
-        '',
-        '{"type":"index-pattern","id":"my-pattern","attributes":{"title":"my-pattern-*"}}',
-        '{"type":"dashboard","id":"my-dashboard","attributes":{"title":"Look at my dashboard"}}',
-        '--EXAMPLE',
-        'Content-Disposition: form-data; name="retries"',
-        '',
-        '[{"type":"dashboard","id":"my-dashboard","overwrite":true}]',
-        '--EXAMPLE--',
-      ].join('\r\n'),
-      headers: {
-        'content-Type': 'multipart/form-data; boundary=EXAMPLE',
-      },
-    };
     savedObjectsClient.bulkCreate.mockResolvedValueOnce({
       saved_objects: [
         {
@@ -217,10 +194,28 @@ describe('POST /api/saved_objects/_resolve_import_errors', () => {
         },
       ],
     });
-    const { payload, statusCode } = await server.inject(request);
-    const response = JSON.parse(payload);
-    expect(statusCode).toBe(200);
-    expect(response).toEqual({ success: true, successCount: 1 });
+
+    const result = await supertest(httpSetup.server.listener)
+      .post('/api/saved_objects/_resolve_import_errors')
+      .set('content-Type', 'multipart/form-data; boundary=EXAMPLE')
+      .send(
+        [
+          '--EXAMPLE',
+          'Content-Disposition: form-data; name="file"; filename="export.ndjson"',
+          'Content-Type: application/ndjson',
+          '',
+          '{"type":"index-pattern","id":"my-pattern","attributes":{"title":"my-pattern-*"}}',
+          '{"type":"dashboard","id":"my-dashboard","attributes":{"title":"Look at my dashboard"}}',
+          '--EXAMPLE',
+          'Content-Disposition: form-data; name="retries"',
+          '',
+          '[{"type":"dashboard","id":"my-dashboard","overwrite":true}]',
+          '--EXAMPLE--',
+        ].join('\r\n')
+      )
+      .expect(200);
+
+    expect(result.body).toEqual({ success: true, successCount: 1 });
     expect(savedObjectsClient.bulkCreate).toMatchInlineSnapshot(`
       [MockFunction] {
         "calls": Array [
@@ -251,27 +246,8 @@ describe('POST /api/saved_objects/_resolve_import_errors', () => {
     `);
   });
 
-  test('resolves conflicts by replacing the visualization references', async () => {
+  it('resolves conflicts by replacing the visualization references', async () => {
     // NOTE: changes to this scenario should be reflected in the docs
-    const request = {
-      method: 'POST',
-      url: '/api/saved_objects/_resolve_import_errors',
-      payload: [
-        '--EXAMPLE',
-        'Content-Disposition: form-data; name="file"; filename="export.ndjson"',
-        'Content-Type: application/ndjson',
-        '',
-        '{"type":"visualization","id":"my-vis","attributes":{"title":"Look at my visualization"},"references":[{"name":"ref_0","type":"index-pattern","id":"missing"}]}',
-        '--EXAMPLE',
-        'Content-Disposition: form-data; name="retries"',
-        '',
-        '[{"type":"visualization","id":"my-vis","replaceReferences":[{"type":"index-pattern","from":"missing","to":"existing"}]}]',
-        '--EXAMPLE--',
-      ].join('\r\n'),
-      headers: {
-        'content-Type': 'multipart/form-data; boundary=EXAMPLE',
-      },
-    };
     savedObjectsClient.bulkCreate.mockResolvedValueOnce({
       saved_objects: [
         {
@@ -300,10 +276,27 @@ describe('POST /api/saved_objects/_resolve_import_errors', () => {
         },
       ],
     });
-    const { payload, statusCode } = await server.inject(request);
-    const response = JSON.parse(payload);
-    expect(statusCode).toBe(200);
-    expect(response).toEqual({ success: true, successCount: 1 });
+
+    const result = await supertest(httpSetup.server.listener)
+      .post('/api/saved_objects/_resolve_import_errors')
+      .set('content-Type', 'multipart/form-data; boundary=EXAMPLE')
+      .send(
+        [
+          '--EXAMPLE',
+          'Content-Disposition: form-data; name="file"; filename="export.ndjson"',
+          'Content-Type: application/ndjson',
+          '',
+          '{"type":"visualization","id":"my-vis","attributes":{"title":"Look at my visualization"},"references":[{"name":"ref_0","type":"index-pattern","id":"missing"}]}',
+          '--EXAMPLE',
+          'Content-Disposition: form-data; name="retries"',
+          '',
+          '[{"type":"visualization","id":"my-vis","replaceReferences":[{"type":"index-pattern","from":"missing","to":"existing"}]}]',
+          '--EXAMPLE--',
+        ].join('\r\n')
+      )
+      .expect(200);
+
+    expect(result.body).toEqual({ success: true, successCount: 1 });
     expect(savedObjectsClient.bulkCreate).toMatchInlineSnapshot(`
       [MockFunction] {
         "calls": Array [
diff --git a/src/legacy/server/saved_objects/routes/_mock_server.ts b/src/core/server/saved_objects/routes/integration_tests/test_utils.ts
similarity index 51%
rename from src/legacy/server/saved_objects/routes/_mock_server.ts
rename to src/core/server/saved_objects/routes/integration_tests/test_utils.ts
index 10b8c1aa07959..093b36a413214 100644
--- a/src/legacy/server/saved_objects/routes/_mock_server.ts
+++ b/src/core/server/saved_objects/routes/integration_tests/test_utils.ts
@@ -17,35 +17,29 @@
  * under the License.
  */
 
-import Hapi from 'hapi';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { defaultValidationErrorHandler } from '../../../../core/server/http/http_tools';
+import { ContextService } from '../../../context';
+import { createHttpServer, createCoreContext } from '../../../http/test_utils';
+import { coreMock } from '../../../mocks';
 
-const defaultConfig = {
-  'kibana.index': '.kibana',
-  'savedObjects.maxImportExportSize': 10000,
-  'savedObjects.maxImportPayloadBytes': 52428800,
-};
+const coreId = Symbol('core');
+
+export const setupServer = async () => {
+  const coreContext = createCoreContext({ coreId });
+  const contextService = new ContextService(coreContext);
 
-export function createMockServer(config: { [key: string]: any } = defaultConfig) {
-  const server = new Hapi.Server({
-    port: 0,
-    routes: {
-      validate: {
-        failAction: defaultValidationErrorHandler,
-      },
-    },
+  const server = createHttpServer(coreContext);
+  const httpSetup = await server.setup({
+    context: contextService.setup({ pluginDependencies: new Map() }),
   });
-  server.config = () => {
-    return {
-      get(key: string) {
-        return config[key];
-      },
-      has(key: string) {
-        return config.hasOwnProperty(key);
-      },
-    };
-  };
+  const handlerContext = coreMock.createRequestHandlerContext();
 
-  return server;
-}
+  httpSetup.registerRouteHandlerContext(coreId, 'core', async (ctx, req, res) => {
+    return handlerContext;
+  });
+
+  return {
+    server,
+    httpSetup,
+    handlerContext,
+  };
+};
diff --git a/src/legacy/server/saved_objects/routes/update.test.ts b/src/core/server/saved_objects/routes/integration_tests/update.test.ts
similarity index 56%
rename from src/legacy/server/saved_objects/routes/update.test.ts
rename to src/core/server/saved_objects/routes/integration_tests/update.test.ts
index aaeaff489d30a..b0c3d68090db6 100644
--- a/src/legacy/server/saved_objects/routes/update.test.ts
+++ b/src/core/server/saved_objects/routes/integration_tests/update.test.ts
@@ -17,16 +17,21 @@
  * under the License.
  */
 
-import Hapi from 'hapi';
-import { createMockServer } from './_mock_server';
-import { createUpdateRoute } from './update';
-import { savedObjectsClientMock } from '../../../../core/server/mocks';
+import supertest from 'supertest';
+import { UnwrapPromise } from '@kbn/utility-types';
+import { registerUpdateRoute } from '../update';
+import { savedObjectsClientMock } from '../../../../../core/server/mocks';
+import { setupServer } from './test_utils';
+
+type setupServerReturn = UnwrapPromise<ReturnType<typeof setupServer>>;
 
 describe('PUT /api/saved_objects/{type}/{id?}', () => {
-  let server: Hapi.Server;
-  const savedObjectsClient = savedObjectsClientMock.create();
+  let server: setupServerReturn['server'];
+  let httpSetup: setupServerReturn['httpSetup'];
+  let handlerContext: setupServerReturn['handlerContext'];
+  let savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
 
-  beforeEach(() => {
+  beforeEach(async () => {
     const clientResponse = {
       id: 'logstash-*',
       title: 'logstash-*',
@@ -36,37 +41,22 @@ describe('PUT /api/saved_objects/{type}/{id?}', () => {
       notExpandable: true,
       references: [],
     };
-    savedObjectsClient.update.mockImplementation(() => Promise.resolve(clientResponse));
-    server = createMockServer();
 
-    const prereqs = {
-      getSavedObjectsClient: {
-        assign: 'savedObjectsClient',
-        method() {
-          return savedObjectsClient;
-        },
-      },
-    };
+    ({ server, httpSetup, handlerContext } = await setupServer());
+    savedObjectsClient = handlerContext.savedObjects.client;
+    savedObjectsClient.update.mockResolvedValue(clientResponse);
 
-    server.route(createUpdateRoute(prereqs));
+    const router = httpSetup.createRouter('/api/saved_objects/');
+    registerUpdateRoute(router);
+
+    await server.start();
   });
 
-  afterEach(() => {
-    savedObjectsClient.update.mockReset();
+  afterEach(async () => {
+    await server.stop();
   });
 
   it('formats successful response', async () => {
-    const request = {
-      method: 'PUT',
-      url: '/api/saved_objects/index-pattern/logstash-*',
-      payload: {
-        attributes: {
-          title: 'Testing',
-        },
-        references: [],
-      },
-    };
-
     const clientResponse = {
       id: 'logstash-*',
       title: 'logstash-*',
@@ -76,27 +66,29 @@ describe('PUT /api/saved_objects/{type}/{id?}', () => {
       attributes: {},
       references: [],
     };
+    savedObjectsClient.update.mockResolvedValue(clientResponse);
 
-    savedObjectsClient.update.mockImplementation(() => Promise.resolve(clientResponse));
-
-    const { payload, statusCode } = await server.inject(request);
-    const response = JSON.parse(payload);
+    const result = await supertest(httpSetup.server.listener)
+      .put('/api/saved_objects/index-pattern/logstash-*')
+      .send({
+        attributes: {
+          title: 'Testing',
+        },
+        references: [],
+      })
+      .expect(200);
 
-    expect(statusCode).toBe(200);
-    expect(response).toEqual(clientResponse);
+    expect(result.body).toEqual(clientResponse);
   });
 
   it('calls upon savedObjectClient.update', async () => {
-    const request = {
-      method: 'PUT',
-      url: '/api/saved_objects/index-pattern/logstash-*',
-      payload: {
+    await supertest(httpSetup.server.listener)
+      .put('/api/saved_objects/index-pattern/logstash-*')
+      .send({
         attributes: { title: 'Testing' },
         version: 'foo',
-      },
-    };
-
-    await server.inject(request);
+      })
+      .expect(200);
 
     expect(savedObjectsClient.update).toHaveBeenCalledWith(
       'index-pattern',
diff --git a/src/legacy/server/saved_objects/routes/log_legacy_import.ts b/src/core/server/saved_objects/routes/log_legacy_import.ts
similarity index 65%
rename from src/legacy/server/saved_objects/routes/log_legacy_import.ts
rename to src/core/server/saved_objects/routes/log_legacy_import.ts
index 038c03d30e030..459b38abb9874 100644
--- a/src/legacy/server/saved_objects/routes/log_legacy_import.ts
+++ b/src/core/server/saved_objects/routes/log_legacy_import.ts
@@ -17,18 +17,18 @@
  * under the License.
  */
 
-import Hapi from 'hapi';
+import { IRouter } from '../../http';
+import { Logger } from '../../logging';
 
-export const createLogLegacyImportRoute = () => ({
-  path: '/api/saved_objects/_log_legacy_import',
-  method: 'POST',
-  options: {
-    handler(request: Hapi.Request) {
-      request.server.log(
-        ['warning'],
-        'Importing saved objects from a .json file has been deprecated'
-      );
-      return { success: true };
+export const registerLogLegacyImportRoute = (router: IRouter, logger: Logger) => {
+  router.post(
+    {
+      path: '/_log_legacy_import',
+      validate: false,
     },
-  },
-});
+    async (context, req, res) => {
+      logger.warn('Importing saved objects from a .json file has been deprecated');
+      return res.ok({ body: { success: true } });
+    }
+  );
+};
diff --git a/src/core/server/saved_objects/routes/resolve_import_errors.ts b/src/core/server/saved_objects/routes/resolve_import_errors.ts
new file mode 100644
index 0000000000000..efa7add7951b0
--- /dev/null
+++ b/src/core/server/saved_objects/routes/resolve_import_errors.ts
@@ -0,0 +1,89 @@
+/*
+ * 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 { extname } from 'path';
+import { Readable } from 'stream';
+import { schema } from '@kbn/config-schema';
+import { IRouter } from '../../http';
+import { resolveImportErrors } from '../import';
+import { SavedObjectConfig } from '../saved_objects_config';
+import { createSavedObjectsStreamFromNdJson } from './utils';
+
+interface FileStream extends Readable {
+  hapi: {
+    filename: string;
+  };
+}
+
+export const registerResolveImportErrorsRoute = (
+  router: IRouter,
+  config: SavedObjectConfig,
+  supportedTypes: string[]
+) => {
+  const { maxImportExportSize, maxImportPayloadBytes } = config;
+
+  router.post(
+    {
+      path: '/_resolve_import_errors',
+      options: {
+        body: {
+          maxBytes: maxImportPayloadBytes,
+          output: 'stream',
+          accepts: 'multipart/form-data',
+        },
+      },
+      validate: {
+        body: schema.object({
+          file: schema.stream(),
+          retries: schema.arrayOf(
+            schema.object({
+              type: schema.string(),
+              id: schema.string(),
+              overwrite: schema.boolean({ defaultValue: false }),
+              replaceReferences: schema.arrayOf(
+                schema.object({
+                  type: schema.string(),
+                  from: schema.string(),
+                  to: schema.string(),
+                }),
+                { defaultValue: [] }
+              ),
+            })
+          ),
+        }),
+      },
+    },
+    router.handleLegacyErrors(async (context, req, res) => {
+      const file = req.body.file as FileStream;
+      const fileExtension = extname(file.hapi.filename).toLowerCase();
+      if (fileExtension !== '.ndjson') {
+        return res.badRequest({ body: `Invalid file extension ${fileExtension}` });
+      }
+      const result = await resolveImportErrors({
+        supportedTypes,
+        savedObjectsClient: context.core.savedObjects.client,
+        readStream: createSavedObjectsStreamFromNdJson(file),
+        retries: req.body.retries,
+        objectLimit: maxImportExportSize,
+      });
+
+      return res.ok({ body: result });
+    })
+  );
+};
diff --git a/src/core/server/saved_objects/routes/update.ts b/src/core/server/saved_objects/routes/update.ts
new file mode 100644
index 0000000000000..c0d94d362e648
--- /dev/null
+++ b/src/core/server/saved_objects/routes/update.ts
@@ -0,0 +1,56 @@
+/*
+ * 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 { schema } from '@kbn/config-schema';
+import { IRouter } from '../../http';
+
+export const registerUpdateRoute = (router: IRouter) => {
+  router.put(
+    {
+      path: '/{type}/{id}',
+      validate: {
+        params: schema.object({
+          type: schema.string(),
+          id: schema.string(),
+        }),
+        body: schema.object({
+          attributes: schema.recordOf(schema.string(), schema.any()),
+          version: schema.maybe(schema.string()),
+          references: schema.maybe(
+            schema.arrayOf(
+              schema.object({
+                name: schema.string(),
+                type: schema.string(),
+                id: schema.string(),
+              })
+            )
+          ),
+        }),
+      },
+    },
+    router.handleLegacyErrors(async (context, req, res) => {
+      const { type, id } = req.params;
+      const { attributes, version, references } = req.body;
+      const options = { version, references };
+
+      const result = await context.core.savedObjects.client.update(type, id, attributes, options);
+      return res.ok({ body: result });
+    })
+  );
+};
diff --git a/src/legacy/server/saved_objects/lib/create_saved_objects_stream_from_ndjson.test.ts b/src/core/server/saved_objects/routes/utils.test.ts
similarity index 96%
rename from src/legacy/server/saved_objects/lib/create_saved_objects_stream_from_ndjson.test.ts
rename to src/core/server/saved_objects/routes/utils.test.ts
index 342063fefaec6..83dceda2e1398 100644
--- a/src/legacy/server/saved_objects/lib/create_saved_objects_stream_from_ndjson.test.ts
+++ b/src/core/server/saved_objects/routes/utils.test.ts
@@ -17,9 +17,9 @@
  * under the License.
  */
 
-import { createSavedObjectsStreamFromNdJson } from './create_saved_objects_stream_from_ndjson';
+import { createSavedObjectsStreamFromNdJson } from './utils';
 import { Readable } from 'stream';
-import { createPromiseFromStreams, createConcatStream } from '../../../utils/streams';
+import { createPromiseFromStreams, createConcatStream } from '../../../../legacy/utils/streams';
 
 async function readStreamToCompletion(stream: Readable) {
   return createPromiseFromStreams([stream, createConcatStream([])]);
diff --git a/src/legacy/server/saved_objects/lib/create_saved_objects_stream_from_ndjson.ts b/src/core/server/saved_objects/routes/utils.ts
similarity index 92%
rename from src/legacy/server/saved_objects/lib/create_saved_objects_stream_from_ndjson.ts
rename to src/core/server/saved_objects/routes/utils.ts
index b96514054db56..5536391341da3 100644
--- a/src/legacy/server/saved_objects/lib/create_saved_objects_stream_from_ndjson.ts
+++ b/src/core/server/saved_objects/routes/utils.ts
@@ -16,9 +16,14 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 import { Readable } from 'stream';
 import { SavedObject, SavedObjectsExportResultDetails } from 'src/core/server';
-import { createSplitStream, createMapStream, createFilterStream } from '../../../utils/streams';
+import {
+  createSplitStream,
+  createMapStream,
+  createFilterStream,
+} from '../../../../legacy/utils/streams';
 
 export function createSavedObjectsStreamFromNdJson(ndJsonStream: Readable) {
   return ndJsonStream
diff --git a/src/core/server/saved_objects/saved_objects_config.ts b/src/core/server/saved_objects/saved_objects_config.ts
index 7217cde55d061..cac04003f29b2 100644
--- a/src/core/server/saved_objects/saved_objects_config.ts
+++ b/src/core/server/saved_objects/saved_objects_config.ts
@@ -19,9 +19,9 @@
 
 import { schema, TypeOf } from '@kbn/config-schema';
 
-export type SavedObjectsConfigType = TypeOf<typeof config.schema>;
+export type SavedObjectsMigrationConfigType = TypeOf<typeof savedObjectsMigrationConfig.schema>;
 
-export const config = {
+export const savedObjectsMigrationConfig = {
   path: 'migrations',
   schema: schema.object({
     batchSize: schema.number({ defaultValue: 100 }),
@@ -30,3 +30,29 @@ export const config = {
     skip: schema.boolean({ defaultValue: false }),
   }),
 };
+
+export type SavedObjectsConfigType = TypeOf<typeof savedObjectsConfig.schema>;
+
+export const savedObjectsConfig = {
+  path: 'savedObjects',
+  schema: schema.object({
+    maxImportPayloadBytes: schema.byteSize({ defaultValue: 10485760 }),
+    maxImportExportSize: schema.byteSize({ defaultValue: 10000 }),
+  }),
+};
+
+export class SavedObjectConfig {
+  public maxImportPayloadBytes: number;
+  public maxImportExportSize: number;
+
+  public migration: SavedObjectsMigrationConfigType;
+
+  constructor(
+    rawConfig: SavedObjectsConfigType,
+    rawMigrationConfig: SavedObjectsMigrationConfigType
+  ) {
+    this.maxImportPayloadBytes = rawConfig.maxImportPayloadBytes.getValueInBytes();
+    this.maxImportExportSize = rawConfig.maxImportExportSize.getValueInBytes();
+    this.migration = rawMigrationConfig;
+  }
+}
diff --git a/src/core/server/saved_objects/saved_objects_service.test.ts b/src/core/server/saved_objects/saved_objects_service.test.ts
index 07e7d8db8b25c..0c7bedecf39f5 100644
--- a/src/core/server/saved_objects/saved_objects_service.test.ts
+++ b/src/core/server/saved_objects/saved_objects_service.test.ts
@@ -24,6 +24,7 @@ import {
   typeRegistryInstanceMock,
 } from './saved_objects_service.test.mocks';
 
+import { ByteSizeValue } from '@kbn/config-schema';
 import { SavedObjectsService } from './saved_objects_service';
 import { mockCoreContext } from '../core_context.mock';
 import * as legacyElasticsearch from 'elasticsearch';
@@ -31,14 +32,33 @@ import { Env } from '../config';
 import { configServiceMock } from '../mocks';
 import { elasticsearchServiceMock } from '../elasticsearch/elasticsearch_service.mock';
 import { legacyServiceMock } from '../legacy/legacy_service.mock';
+import { httpServiceMock } from '../http/http_service.mock';
 import { SavedObjectsClientFactoryProvider } from './service/lib';
 import { BehaviorSubject } from 'rxjs';
 import { NodesVersionCompatibility } from '../elasticsearch/version_check/ensure_es_version';
 
 describe('SavedObjectsService', () => {
+  const createCoreContext = ({
+    skipMigration = true,
+    env,
+  }: { skipMigration?: boolean; env?: Env } = {}) => {
+    const configService = configServiceMock.create({ atPath: { skip: true } });
+    configService.atPath.mockImplementation(path => {
+      if (path === 'migrations') {
+        return new BehaviorSubject({ skip: skipMigration });
+      }
+      return new BehaviorSubject({
+        maxImportPayloadBytes: new ByteSizeValue(0),
+        maxImportExportSize: new ByteSizeValue(0),
+      });
+    });
+    return mockCoreContext.create({ configService, env });
+  };
+
   const createSetupDeps = () => {
     const elasticsearchMock = elasticsearchServiceMock.createInternalSetup();
     return {
+      http: httpServiceMock.createSetupContract(),
       elasticsearch: elasticsearchMock,
       legacyPlugins: legacyServiceMock.createDiscoverPlugins(),
     };
@@ -51,7 +71,7 @@ describe('SavedObjectsService', () => {
   describe('#setup()', () => {
     describe('#setClientFactoryProvider', () => {
       it('registers the factory to the clientProvider', async () => {
-        const coreContext = mockCoreContext.create();
+        const coreContext = createCoreContext();
         const soService = new SavedObjectsService(coreContext);
         const setup = await soService.setup(createSetupDeps());
 
@@ -65,7 +85,7 @@ describe('SavedObjectsService', () => {
         expect(clientProviderInstanceMock.setClientFactory).toHaveBeenCalledWith(factory);
       });
       it('throws if a factory is already registered', async () => {
-        const coreContext = mockCoreContext.create();
+        const coreContext = createCoreContext();
         const soService = new SavedObjectsService(coreContext);
         const setup = await soService.setup(createSetupDeps());
 
@@ -84,7 +104,7 @@ describe('SavedObjectsService', () => {
 
     describe('#addClientWrapper', () => {
       it('registers the wrapper to the clientProvider', async () => {
-        const coreContext = mockCoreContext.create();
+        const coreContext = createCoreContext();
         const soService = new SavedObjectsService(coreContext);
         const setup = await soService.setup(createSetupDeps());
 
@@ -112,7 +132,7 @@ describe('SavedObjectsService', () => {
 
     describe('registerType', () => {
       it('registers the type to the internal typeRegistry', async () => {
-        const coreContext = mockCoreContext.create();
+        const coreContext = createCoreContext();
         const soService = new SavedObjectsService(coreContext);
         const setup = await soService.setup(createSetupDeps());
 
@@ -132,7 +152,7 @@ describe('SavedObjectsService', () => {
 
   describe('#start()', () => {
     it('creates a KibanaMigrator which retries NoConnections errors from callAsInternalUser', async () => {
-      const coreContext = mockCoreContext.create();
+      const coreContext = createCoreContext();
 
       const soService = new SavedObjectsService(coreContext);
       const coreSetup = createSetupDeps();
@@ -153,7 +173,7 @@ describe('SavedObjectsService', () => {
     });
 
     it('skips KibanaMigrator migrations when --optimize=true', async () => {
-      const coreContext = mockCoreContext.create({
+      const coreContext = createCoreContext({
         env: ({ cliArgs: { optimize: true }, packageInfo: { version: 'x.x.x' } } as unknown) as Env,
       });
       const soService = new SavedObjectsService(coreContext);
@@ -164,8 +184,7 @@ describe('SavedObjectsService', () => {
     });
 
     it('skips KibanaMigrator migrations when migrations.skip=true', async () => {
-      const configService = configServiceMock.create({ atPath: { skip: true } });
-      const coreContext = mockCoreContext.create({ configService });
+      const coreContext = createCoreContext({ skipMigration: true });
       const soService = new SavedObjectsService(coreContext);
       await soService.setup(createSetupDeps());
       await soService.start({});
@@ -174,8 +193,7 @@ describe('SavedObjectsService', () => {
 
     it('waits for all es nodes to be compatible before running migrations', async done => {
       expect.assertions(2);
-      const configService = configServiceMock.create({ atPath: { skip: false } });
-      const coreContext = mockCoreContext.create({ configService });
+      const coreContext = createCoreContext({ skipMigration: false });
       const soService = new SavedObjectsService(coreContext);
       const setupDeps = createSetupDeps();
       // Create an new subject so that we can control when isCompatible=true
@@ -204,8 +222,7 @@ describe('SavedObjectsService', () => {
     });
 
     it('resolves with KibanaMigrator after waiting for migrations to complete', async () => {
-      const configService = configServiceMock.create({ atPath: { skip: false } });
-      const coreContext = mockCoreContext.create({ configService });
+      const coreContext = createCoreContext({ skipMigration: false });
       const soService = new SavedObjectsService(coreContext);
       await soService.setup(createSetupDeps());
       expect(migratorInstanceMock.runMigrations).toHaveBeenCalledTimes(0);
diff --git a/src/core/server/saved_objects/saved_objects_service.ts b/src/core/server/saved_objects/saved_objects_service.ts
index 2f07dbe51a09e..ece00539536e1 100644
--- a/src/core/server/saved_objects/saved_objects_service.ts
+++ b/src/core/server/saved_objects/saved_objects_service.ts
@@ -31,9 +31,13 @@ import { LegacyServiceDiscoverPlugins } from '../legacy';
 import { InternalElasticsearchServiceSetup, APICaller } from '../elasticsearch';
 import { KibanaConfigType } from '../kibana_config';
 import { migrationsRetryCallCluster } from '../elasticsearch/retry_call_cluster';
-import { SavedObjectsConfigType } from './saved_objects_config';
-import { KibanaRequest } from '../http';
-import { SavedObjectsClientContract, SavedObjectsType } from './types';
+import {
+  SavedObjectsConfigType,
+  SavedObjectsMigrationConfigType,
+  SavedObjectConfig,
+} from './saved_objects_config';
+import { InternalHttpServiceSetup, KibanaRequest } from '../http';
+import { SavedObjectsClientContract, SavedObjectsType, SavedObjectsLegacyUiExports } from './types';
 import { ISavedObjectsRepository, SavedObjectsRepository } from './service/lib/repository';
 import {
   SavedObjectsClientFactoryProvider,
@@ -43,6 +47,7 @@ import { Logger } from '../logging';
 import { convertLegacyTypes } from './utils';
 import { SavedObjectTypeRegistry, ISavedObjectTypeRegistry } from './saved_objects_type_registry';
 import { PropertyValidators } from './validation';
+import { registerRoutes } from './routes';
 import { SavedObjectsSerializer } from './serialization';
 
 /**
@@ -198,6 +203,7 @@ export interface SavedObjectsRepositoryFactory {
 export interface SavedObjectsSetupDeps {
   legacyPlugins: LegacyServiceDiscoverPlugins;
   elasticsearch: InternalElasticsearchServiceSetup;
+  http: InternalHttpServiceSetup;
 }
 
 interface WrappedClientFactoryWrapper {
@@ -215,6 +221,7 @@ export class SavedObjectsService
   private logger: Logger;
 
   private setupDeps?: SavedObjectsSetupDeps;
+  private config?: SavedObjectConfig;
   private clientFactoryProvider?: SavedObjectsClientFactoryProvider;
   private clientFactoryWrappers: WrappedClientFactoryWrapper[] = [];
 
@@ -237,6 +244,27 @@ export class SavedObjectsService
     legacyTypes.forEach(type => this.typeRegistry.registerType(type));
     this.validations = setupDeps.legacyPlugins.uiExports.savedObjectValidations || {};
 
+    const importableExportableTypes = getImportableAndExportableTypes(
+      setupDeps.legacyPlugins.uiExports
+    );
+
+    const savedObjectsConfig = await this.coreContext.configService
+      .atPath<SavedObjectsConfigType>('savedObjects')
+      .pipe(first())
+      .toPromise();
+    const savedObjectsMigrationConfig = await this.coreContext.configService
+      .atPath<SavedObjectsMigrationConfigType>('migrations')
+      .pipe(first())
+      .toPromise();
+    this.config = new SavedObjectConfig(savedObjectsConfig, savedObjectsMigrationConfig);
+
+    registerRoutes({
+      http: setupDeps.http,
+      logger: this.logger,
+      config: this.config,
+      importableExportableTypes,
+    });
+
     return {
       setClientFactoryProvider: provider => {
         if (this.clientFactoryProvider) {
@@ -261,7 +289,7 @@ export class SavedObjectsService
     core: SavedObjectsStartDeps,
     migrationsRetryDelay?: number
   ): Promise<InternalSavedObjectsServiceStart> {
-    if (!this.setupDeps) {
+    if (!this.setupDeps || !this.config) {
       throw new Error('#setup() needs to be run first');
     }
 
@@ -271,12 +299,8 @@ export class SavedObjectsService
       .atPath<KibanaConfigType>('kibana')
       .pipe(first())
       .toPromise();
-    const savedObjectsConfig = await this.coreContext.configService
-      .atPath<SavedObjectsConfigType>('migrations')
-      .pipe(first())
-      .toPromise();
     const adminClient = this.setupDeps!.elasticsearch.adminClient;
-    const migrator = this.createMigrator(kibanaConfig, savedObjectsConfig, migrationsRetryDelay);
+    const migrator = this.createMigrator(kibanaConfig, this.config.migration, migrationsRetryDelay);
 
     /**
      * Note: We want to ensure that migrations have completed before
@@ -289,7 +313,7 @@ export class SavedObjectsService
      * So, when the `migrations.skip` is true, we skip migrations altogether.
      */
     const cliArgs = this.coreContext.env.cliArgs;
-    const skipMigrations = cliArgs.optimize || savedObjectsConfig.skip;
+    const skipMigrations = cliArgs.optimize || this.config.migration.skip;
 
     if (skipMigrations) {
       this.logger.warn(
@@ -354,7 +378,7 @@ export class SavedObjectsService
 
   private createMigrator(
     kibanaConfig: KibanaConfigType,
-    savedObjectsConfig: SavedObjectsConfigType,
+    savedObjectsConfig: SavedObjectsMigrationConfigType,
     migrationsRetryDelay?: number
   ): KibanaMigrator {
     const adminClient = this.setupDeps!.elasticsearch.adminClient;
@@ -374,3 +398,16 @@ export class SavedObjectsService
     });
   }
 }
+
+function getImportableAndExportableTypes({
+  savedObjectMappings = [],
+  savedObjectsManagement = {},
+}: SavedObjectsLegacyUiExports) {
+  const visibleTypes = savedObjectMappings.reduce(
+    (types, mapping) => [...types, ...Object.keys(mapping.properties)],
+    [] as string[]
+  );
+  return visibleTypes.filter(
+    type => savedObjectsManagement[type]?.isImportableAndExportable === true ?? false
+  );
+}
diff --git a/src/core/server/saved_objects/types.ts b/src/core/server/saved_objects/types.ts
index 980ba005e0eeb..a4fde1765b7d3 100644
--- a/src/core/server/saved_objects/types.ts
+++ b/src/core/server/saved_objects/types.ts
@@ -21,6 +21,7 @@ import { SavedObjectsClient } from './service/saved_objects_client';
 import { SavedObjectsTypeMappingDefinition, SavedObjectsTypeMappingDefinitions } from './mappings';
 import { SavedObjectMigrationMap } from './migrations';
 import { PropertyValidators } from './validation';
+import { SavedObjectsManagementDefinition } from './management';
 
 export {
   SavedObjectsImportResponse,
@@ -256,6 +257,7 @@ export interface SavedObjectsLegacyUiExports {
   savedObjectMigrations: SavedObjectsLegacyMigrationDefinitions;
   savedObjectSchemas: SavedObjectsLegacySchemaDefinitions;
   savedObjectValidations: PropertyValidators;
+  savedObjectsManagement: SavedObjectsManagementDefinition;
 }
 
 /**
diff --git a/src/core/server/saved_objects/utils.test.ts b/src/core/server/saved_objects/utils.test.ts
index d1c15517e94a6..1e2b9f6a0f694 100644
--- a/src/core/server/saved_objects/utils.test.ts
+++ b/src/core/server/saved_objects/utils.test.ts
@@ -61,6 +61,7 @@ describe('convertLegacyTypes', () => {
       savedObjectMigrations: {},
       savedObjectSchemas: {},
       savedObjectValidations: {},
+      savedObjectsManagement: {},
     };
 
     const converted = convertLegacyTypes(uiExports, legacyConfig);
@@ -100,6 +101,7 @@ describe('convertLegacyTypes', () => {
         },
       },
       savedObjectValidations: {},
+      savedObjectsManagement: {},
     };
 
     const converted = convertLegacyTypes(uiExports, legacyConfig);
@@ -134,6 +136,7 @@ describe('convertLegacyTypes', () => {
         },
       },
       savedObjectValidations: {},
+      savedObjectsManagement: {},
     };
 
     const converted = convertLegacyTypes(uiExports, legacyConfig);
@@ -182,6 +185,7 @@ describe('convertLegacyTypes', () => {
       },
       savedObjectSchemas: {},
       savedObjectValidations: {},
+      savedObjectsManagement: {},
     };
 
     const converted = convertLegacyTypes(uiExports, legacyConfig);
@@ -244,6 +248,7 @@ describe('convertLegacyTypes', () => {
         },
       },
       savedObjectValidations: {},
+      savedObjectsManagement: {},
     };
 
     const converted = convertLegacyTypes(uiExports, legacyConfig);
diff --git a/src/core/server/server.ts b/src/core/server/server.ts
index 96adb3bbcd210..db2493b38d6e0 100644
--- a/src/core/server/server.ts
+++ b/src/core/server/server.ts
@@ -42,7 +42,7 @@ import { config as loggingConfig } from './logging';
 import { config as devConfig } from './dev';
 import { config as pathConfig } from './path';
 import { config as kibanaConfig } from './kibana_config';
-import { config as savedObjectsConfig } from './saved_objects';
+import { savedObjectsConfig, savedObjectsMigrationConfig } from './saved_objects';
 import { config as uiSettingsConfig } from './ui_settings';
 import { mapToObject } from '../utils';
 import { ContextService } from './context';
@@ -132,6 +132,7 @@ export class Server {
     });
 
     const savedObjectsSetup = await this.savedObjects.setup({
+      http: httpSetup,
       elasticsearch: elasticsearchServiceSetup,
       legacyPlugins,
     });
@@ -257,6 +258,7 @@ export class Server {
       [devConfig.path, devConfig.schema],
       [kibanaConfig.path, kibanaConfig.schema],
       [savedObjectsConfig.path, savedObjectsConfig.schema],
+      [savedObjectsMigrationConfig.path, savedObjectsMigrationConfig.schema],
       [uiSettingsConfig.path, uiSettingsConfig.schema],
     ];
 
diff --git a/src/legacy/core_plugins/kibana/migrations/types.ts b/src/legacy/core_plugins/kibana/migrations/types.ts
index 144151ed80d43..839f753670b20 100644
--- a/src/legacy/core_plugins/kibana/migrations/types.ts
+++ b/src/legacy/core_plugins/kibana/migrations/types.ts
@@ -17,7 +17,8 @@
  * under the License.
  */
 
-import { SavedObjectReference } from '../../../../legacy/server/saved_objects/routes/types';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { SavedObjectReference } from '../../../../core/server';
 
 export interface SavedObjectAttributes {
   kibanaSavedObjectMeta: {
diff --git a/src/legacy/server/saved_objects/routes/bulk_create.ts b/src/legacy/server/saved_objects/routes/bulk_create.ts
deleted file mode 100644
index b185650494f94..0000000000000
--- a/src/legacy/server/saved_objects/routes/bulk_create.ts
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import Hapi from 'hapi';
-import Joi from 'joi';
-import { SavedObjectAttributes, SavedObjectsClientContract } from 'src/core/server';
-import { Prerequisites, SavedObjectReference, WithoutQueryAndParams } from './types';
-
-interface SavedObject {
-  type: string;
-  id?: string;
-  attributes: SavedObjectAttributes;
-  version?: string;
-  migrationVersion?: { [key: string]: string };
-  references: SavedObjectReference[];
-}
-
-interface BulkCreateRequest extends WithoutQueryAndParams<Hapi.Request> {
-  pre: {
-    savedObjectsClient: SavedObjectsClientContract;
-  };
-  query: {
-    overwrite: boolean;
-  };
-  payload: SavedObject[];
-}
-
-export const createBulkCreateRoute = (prereqs: Prerequisites) => ({
-  path: '/api/saved_objects/_bulk_create',
-  method: 'POST',
-  config: {
-    pre: [prereqs.getSavedObjectsClient],
-    validate: {
-      query: Joi.object()
-        .keys({
-          overwrite: Joi.boolean().default(false),
-        })
-        .default(),
-      payload: Joi.array().items(
-        Joi.object({
-          type: Joi.string().required(),
-          id: Joi.string(),
-          attributes: Joi.object().required(),
-          version: Joi.string(),
-          migrationVersion: Joi.object().optional(),
-          references: Joi.array()
-            .items(
-              Joi.object().keys({
-                name: Joi.string().required(),
-                type: Joi.string().required(),
-                id: Joi.string().required(),
-              })
-            )
-            .default([]),
-        }).required()
-      ),
-    },
-    handler(request: BulkCreateRequest) {
-      const { overwrite } = request.query;
-      const { savedObjectsClient } = request.pre;
-
-      return savedObjectsClient.bulkCreate(request.payload, { overwrite });
-    },
-  },
-});
diff --git a/src/legacy/server/saved_objects/routes/bulk_get.ts b/src/legacy/server/saved_objects/routes/bulk_get.ts
deleted file mode 100644
index e9eca8e557982..0000000000000
--- a/src/legacy/server/saved_objects/routes/bulk_get.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import Hapi from 'hapi';
-import Joi from 'joi';
-import { SavedObjectsClientContract } from 'src/core/server';
-import { Prerequisites } from './types';
-
-interface BulkGetRequest extends Hapi.Request {
-  pre: {
-    savedObjectsClient: SavedObjectsClientContract;
-  };
-  payload: Array<{
-    type: string;
-    id: string;
-    fields?: string[];
-  }>;
-}
-
-export const createBulkGetRoute = (prereqs: Prerequisites) => ({
-  path: '/api/saved_objects/_bulk_get',
-  method: 'POST',
-  config: {
-    pre: [prereqs.getSavedObjectsClient],
-    validate: {
-      payload: Joi.array().items(
-        Joi.object({
-          type: Joi.string().required(),
-          id: Joi.string().required(),
-          fields: Joi.array().items(Joi.string()),
-        }).required()
-      ),
-    },
-    handler(request: BulkGetRequest) {
-      const { savedObjectsClient } = request.pre;
-
-      return savedObjectsClient.bulkGet(request.payload);
-    },
-  },
-});
diff --git a/src/legacy/server/saved_objects/routes/bulk_update.ts b/src/legacy/server/saved_objects/routes/bulk_update.ts
deleted file mode 100644
index a77b0c059447f..0000000000000
--- a/src/legacy/server/saved_objects/routes/bulk_update.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import Hapi from 'hapi';
-import Joi from 'joi';
-import { SavedObjectsClient, SavedObjectsBulkUpdateObject } from 'src/core/server';
-import { Prerequisites } from './types';
-
-interface BulkUpdateRequest extends Hapi.Request {
-  pre: {
-    savedObjectsClient: SavedObjectsClient;
-  };
-  payload: SavedObjectsBulkUpdateObject[];
-}
-
-export const createBulkUpdateRoute = (prereqs: Prerequisites) => {
-  return {
-    path: '/api/saved_objects/_bulk_update',
-    method: 'PUT',
-    config: {
-      pre: [prereqs.getSavedObjectsClient],
-      validate: {
-        payload: Joi.array().items(
-          Joi.object({
-            type: Joi.string().required(),
-            id: Joi.string().required(),
-            attributes: Joi.object().required(),
-            version: Joi.string(),
-            references: Joi.array().items(
-              Joi.object().keys({
-                name: Joi.string().required(),
-                type: Joi.string().required(),
-                id: Joi.string().required(),
-              })
-            ),
-          })
-        ),
-      },
-      handler(request: BulkUpdateRequest) {
-        const { savedObjectsClient } = request.pre;
-        return savedObjectsClient.bulkUpdate(request.payload);
-      },
-    },
-  };
-};
diff --git a/src/legacy/server/saved_objects/routes/create.test.ts b/src/legacy/server/saved_objects/routes/create.test.ts
deleted file mode 100644
index 4f096a9ee5c93..0000000000000
--- a/src/legacy/server/saved_objects/routes/create.test.ts
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import Hapi from 'hapi';
-import { createMockServer } from './_mock_server';
-import { createCreateRoute } from './create';
-import { savedObjectsClientMock } from '../../../../core/server/mocks';
-
-describe('POST /api/saved_objects/{type}', () => {
-  let server: Hapi.Server;
-  const clientResponse = {
-    id: 'logstash-*',
-    type: 'index-pattern',
-    title: 'logstash-*',
-    version: 'foo',
-    references: [],
-    attributes: {},
-  };
-  const savedObjectsClient = savedObjectsClientMock.create();
-
-  beforeEach(() => {
-    savedObjectsClient.create.mockImplementation(() => Promise.resolve(clientResponse));
-    server = createMockServer();
-
-    const prereqs = {
-      getSavedObjectsClient: {
-        assign: 'savedObjectsClient',
-        method() {
-          return savedObjectsClient;
-        },
-      },
-    };
-
-    server.route(createCreateRoute(prereqs));
-  });
-
-  afterEach(() => {
-    savedObjectsClient.create.mockReset();
-  });
-
-  it('formats successful response', async () => {
-    const request = {
-      method: 'POST',
-      url: '/api/saved_objects/index-pattern',
-      payload: {
-        attributes: {
-          title: 'Testing',
-        },
-      },
-    };
-
-    const { payload, statusCode } = await server.inject(request);
-    const response = JSON.parse(payload);
-
-    expect(statusCode).toBe(200);
-    expect(response).toEqual(clientResponse);
-  });
-
-  it('requires attributes', async () => {
-    const request = {
-      method: 'POST',
-      url: '/api/saved_objects/index-pattern',
-      payload: {},
-    };
-
-    const { statusCode, payload } = await server.inject(request);
-    const response = JSON.parse(payload);
-
-    expect(response.validation.keys).toContain('attributes');
-    expect(response.message).toMatch(/is required/);
-    expect(response.statusCode).toBe(400);
-    expect(statusCode).toBe(400);
-  });
-
-  it('calls upon savedObjectClient.create', async () => {
-    const request = {
-      method: 'POST',
-      url: '/api/saved_objects/index-pattern',
-      payload: {
-        attributes: {
-          title: 'Testing',
-        },
-      },
-    };
-
-    await server.inject(request);
-    expect(savedObjectsClient.create).toHaveBeenCalled();
-
-    expect(savedObjectsClient.create).toHaveBeenCalledWith(
-      'index-pattern',
-      { title: 'Testing' },
-      { overwrite: false, id: undefined, migrationVersion: undefined, references: [] }
-    );
-  });
-
-  it('can specify an id', async () => {
-    const request = {
-      method: 'POST',
-      url: '/api/saved_objects/index-pattern/logstash-*',
-      payload: {
-        attributes: {
-          title: 'Testing',
-        },
-      },
-    };
-
-    await server.inject(request);
-    expect(savedObjectsClient.create).toHaveBeenCalled();
-
-    const args = savedObjectsClient.create.mock.calls[0];
-    const options = { overwrite: false, id: 'logstash-*', references: [] };
-    const attributes = { title: 'Testing' };
-
-    expect(args).toEqual(['index-pattern', attributes, options]);
-  });
-});
diff --git a/src/legacy/server/saved_objects/routes/create.ts b/src/legacy/server/saved_objects/routes/create.ts
deleted file mode 100644
index a3f4a926972ca..0000000000000
--- a/src/legacy/server/saved_objects/routes/create.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import Hapi from 'hapi';
-import Joi from 'joi';
-import { SavedObjectAttributes, SavedObjectsClient } from 'src/core/server';
-import { Prerequisites, SavedObjectReference, WithoutQueryAndParams } from './types';
-
-interface CreateRequest extends WithoutQueryAndParams<Hapi.Request> {
-  pre: {
-    savedObjectsClient: SavedObjectsClient;
-  };
-  query: {
-    overwrite: boolean;
-  };
-  params: {
-    type: string;
-    id?: string;
-  };
-  payload: {
-    attributes: SavedObjectAttributes;
-    migrationVersion?: { [key: string]: string };
-    references: SavedObjectReference[];
-  };
-}
-
-export const createCreateRoute = (prereqs: Prerequisites) => {
-  return {
-    path: '/api/saved_objects/{type}/{id?}',
-    method: 'POST',
-    options: {
-      pre: [prereqs.getSavedObjectsClient],
-      validate: {
-        query: Joi.object()
-          .keys({
-            overwrite: Joi.boolean().default(false),
-          })
-          .default(),
-        params: Joi.object()
-          .keys({
-            type: Joi.string().required(),
-            id: Joi.string(),
-          })
-          .required(),
-        payload: Joi.object({
-          attributes: Joi.object().required(),
-          migrationVersion: Joi.object().optional(),
-          references: Joi.array()
-            .items(
-              Joi.object().keys({
-                name: Joi.string().required(),
-                type: Joi.string().required(),
-                id: Joi.string().required(),
-              })
-            )
-            .default([]),
-        }).required(),
-      },
-      handler(request: CreateRequest) {
-        const { savedObjectsClient } = request.pre;
-        const { type, id } = request.params;
-        const { overwrite } = request.query;
-        const { migrationVersion, references } = request.payload;
-        const options = { id, overwrite, migrationVersion, references };
-
-        return savedObjectsClient.create(type, request.payload.attributes, options);
-      },
-    },
-  };
-};
diff --git a/src/legacy/server/saved_objects/routes/delete.test.ts b/src/legacy/server/saved_objects/routes/delete.test.ts
deleted file mode 100644
index f3e5e83771471..0000000000000
--- a/src/legacy/server/saved_objects/routes/delete.test.ts
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import Hapi from 'hapi';
-import { createMockServer } from './_mock_server';
-import { createDeleteRoute } from './delete';
-import { savedObjectsClientMock } from '../../../../core/server/mocks';
-
-describe('DELETE /api/saved_objects/{type}/{id}', () => {
-  let server: Hapi.Server;
-  const savedObjectsClient = savedObjectsClientMock.create();
-
-  beforeEach(() => {
-    savedObjectsClient.delete.mockImplementation(() => Promise.resolve('{}'));
-    server = createMockServer();
-
-    const prereqs = {
-      getSavedObjectsClient: {
-        assign: 'savedObjectsClient',
-        method() {
-          return savedObjectsClient;
-        },
-      },
-    };
-
-    server.route(createDeleteRoute(prereqs));
-  });
-
-  afterEach(() => {
-    savedObjectsClient.delete.mockReset();
-  });
-
-  it('formats successful response', async () => {
-    const request = {
-      method: 'DELETE',
-      url: '/api/saved_objects/index-pattern/logstash-*',
-    };
-
-    const { payload, statusCode } = await server.inject(request);
-    const response = JSON.parse(payload);
-
-    expect(statusCode).toBe(200);
-    expect(response).toEqual({});
-  });
-
-  it('calls upon savedObjectClient.delete', async () => {
-    const request = {
-      method: 'DELETE',
-      url: '/api/saved_objects/index-pattern/logstash-*',
-    };
-
-    await server.inject(request);
-    expect(savedObjectsClient.delete).toHaveBeenCalledWith('index-pattern', 'logstash-*');
-  });
-});
diff --git a/src/legacy/server/saved_objects/routes/delete.ts b/src/legacy/server/saved_objects/routes/delete.ts
deleted file mode 100644
index a718f26bc2014..0000000000000
--- a/src/legacy/server/saved_objects/routes/delete.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import Hapi from 'hapi';
-import Joi from 'joi';
-import { SavedObjectsClientContract } from 'src/core/server';
-import { Prerequisites } from './types';
-
-interface DeleteRequest extends Hapi.Request {
-  pre: {
-    savedObjectsClient: SavedObjectsClientContract;
-  };
-  params: {
-    type: string;
-    id: string;
-  };
-}
-
-export const createDeleteRoute = (prereqs: Prerequisites) => ({
-  path: '/api/saved_objects/{type}/{id}',
-  method: 'DELETE',
-  config: {
-    pre: [prereqs.getSavedObjectsClient],
-    validate: {
-      params: Joi.object()
-        .keys({
-          type: Joi.string().required(),
-          id: Joi.string().required(),
-        })
-        .required(),
-    },
-    handler(request: DeleteRequest) {
-      const { savedObjectsClient } = request.pre;
-      const { type, id } = request.params;
-
-      return savedObjectsClient.delete(type, id);
-    },
-  },
-});
diff --git a/src/legacy/server/saved_objects/routes/export.test.ts b/src/legacy/server/saved_objects/routes/export.test.ts
deleted file mode 100644
index 93ca3a419e6df..0000000000000
--- a/src/legacy/server/saved_objects/routes/export.test.ts
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-jest.mock('../../../../core/server/saved_objects/export', () => ({
-  getSortedObjectsForExport: jest.fn(),
-}));
-
-import Hapi from 'hapi';
-// Disable lint errors for imports from src/core/server/saved_objects until SavedObjects migration is complete
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import * as exportMock from '../../../../core/server/saved_objects/export';
-import { createMockServer } from './_mock_server';
-import { createExportRoute } from './export';
-import { createListStream } from '../../../utils/streams';
-import { savedObjectsClientMock } from '../../../../core/server/mocks';
-
-const getSortedObjectsForExport = exportMock.getSortedObjectsForExport as jest.Mock;
-
-describe('POST /api/saved_objects/_export', () => {
-  let server: Hapi.Server;
-  const savedObjectsClient = {
-    ...savedObjectsClientMock.create(),
-    errors: {} as any,
-  };
-
-  beforeEach(() => {
-    server = createMockServer();
-    const prereqs = {
-      getSavedObjectsClient: {
-        assign: 'savedObjectsClient',
-        method() {
-          return savedObjectsClient;
-        },
-      },
-    };
-
-    server.route(createExportRoute(prereqs, server, ['index-pattern', 'search']));
-  });
-
-  afterEach(() => {
-    jest.resetAllMocks();
-  });
-
-  test('does not allow both "search" and "objects" to be specified', async () => {
-    const request = {
-      method: 'POST',
-      url: '/api/saved_objects/_export',
-      payload: {
-        search: 'search',
-        objects: [{ type: 'search', id: 'bar' }],
-        includeReferencesDeep: true,
-      },
-    };
-
-    const { payload, statusCode } = await server.inject(request);
-
-    expect(statusCode).toEqual(400);
-    expect(JSON.parse(payload)).toMatchInlineSnapshot(`
-      Object {
-        "error": "Bad Request",
-        "message": "\\"search\\" must not exist simultaneously with [objects]",
-        "statusCode": 400,
-        "validation": Object {
-          "keys": Array [
-            "value",
-          ],
-          "source": "payload",
-        },
-      }
-    `);
-  });
-
-  test('formats successful response', async () => {
-    const request = {
-      method: 'POST',
-      url: '/api/saved_objects/_export',
-      payload: {
-        type: 'search',
-        search: 'my search string',
-        includeReferencesDeep: true,
-      },
-    };
-    getSortedObjectsForExport.mockResolvedValueOnce(
-      createListStream([
-        {
-          id: '1',
-          type: 'index-pattern',
-          attributes: {},
-          references: [],
-        },
-        {
-          id: '2',
-          type: 'search',
-          attributes: {},
-          references: [
-            {
-              name: 'ref_0',
-              type: 'index-pattern',
-              id: '1',
-            },
-          ],
-        },
-      ])
-    );
-
-    const { payload, statusCode, headers } = await server.inject(request);
-    const objects = payload.split('\n').map(row => JSON.parse(row));
-
-    expect(statusCode).toBe(200);
-    expect(headers).toHaveProperty('content-disposition', 'attachment; filename="export.ndjson"');
-    expect(headers).toHaveProperty('content-type', 'application/ndjson');
-    expect(objects).toMatchInlineSnapshot(`
-      Array [
-        Object {
-          "attributes": Object {},
-          "id": "1",
-          "references": Array [],
-          "type": "index-pattern",
-        },
-        Object {
-          "attributes": Object {},
-          "id": "2",
-          "references": Array [
-            Object {
-              "id": "1",
-              "name": "ref_0",
-              "type": "index-pattern",
-            },
-          ],
-          "type": "search",
-        },
-      ]
-    `);
-    expect(getSortedObjectsForExport).toMatchInlineSnapshot(`
-      [MockFunction] {
-        "calls": Array [
-          Array [
-            Object {
-              "excludeExportDetails": false,
-              "exportSizeLimit": 10000,
-              "includeReferencesDeep": true,
-              "objects": undefined,
-              "savedObjectsClient": Object {
-                "bulkCreate": [MockFunction],
-                "bulkGet": [MockFunction],
-                "bulkUpdate": [MockFunction],
-                "create": [MockFunction],
-                "delete": [MockFunction],
-                "errors": Object {},
-                "find": [MockFunction],
-                "get": [MockFunction],
-                "update": [MockFunction],
-              },
-              "search": "my search string",
-              "types": Array [
-                "search",
-              ],
-            },
-          ],
-        ],
-        "results": Array [
-          Object {
-            "type": "return",
-            "value": Promise {},
-          },
-        ],
-      }
-    `);
-  });
-});
diff --git a/src/legacy/server/saved_objects/routes/export.ts b/src/legacy/server/saved_objects/routes/export.ts
deleted file mode 100644
index ce4aed4b78c2a..0000000000000
--- a/src/legacy/server/saved_objects/routes/export.ts
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import Hapi from 'hapi';
-import Joi from 'joi';
-import stringify from 'json-stable-stringify';
-import { SavedObjectsClientContract } from 'src/core/server';
-import {
-  createPromiseFromStreams,
-  createMapStream,
-  createConcatStream,
-} from '../../../utils/streams';
-// Disable lint errors for imports from src/core/server/saved_objects until SavedObjects migration is complete
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { getSortedObjectsForExport } from '../../../../core/server/saved_objects';
-import { Prerequisites } from './types';
-
-interface ExportRequest extends Hapi.Request {
-  pre: {
-    savedObjectsClient: SavedObjectsClientContract;
-  };
-  payload: {
-    type?: string[];
-    objects?: Array<{
-      type: string;
-      id: string;
-    }>;
-    search?: string;
-    includeReferencesDeep: boolean;
-    excludeExportDetails: boolean;
-  };
-}
-
-export const createExportRoute = (
-  prereqs: Prerequisites,
-  server: Hapi.Server,
-  supportedTypes: string[]
-) => ({
-  path: '/api/saved_objects/_export',
-  method: 'POST',
-  config: {
-    pre: [prereqs.getSavedObjectsClient],
-    validate: {
-      payload: Joi.object()
-        .keys({
-          type: Joi.array()
-            .items(Joi.string().valid(supportedTypes.sort()))
-            .single()
-            .optional(),
-          objects: Joi.array()
-            .items({
-              type: Joi.string()
-                .valid(supportedTypes.sort())
-                .required(),
-              id: Joi.string().required(),
-            })
-            .max(server.config().get('savedObjects.maxImportExportSize'))
-            .optional(),
-          search: Joi.string().optional(),
-          includeReferencesDeep: Joi.boolean().default(false),
-          excludeExportDetails: Joi.boolean().default(false),
-        })
-        .xor('type', 'objects')
-        .nand('search', 'objects')
-        .default(),
-    },
-    async handler(request: ExportRequest, h: Hapi.ResponseToolkit) {
-      const { savedObjectsClient } = request.pre;
-      const exportStream = await getSortedObjectsForExport({
-        savedObjectsClient,
-        types: request.payload.type,
-        search: request.payload.search,
-        objects: request.payload.objects,
-        exportSizeLimit: server.config().get('savedObjects.maxImportExportSize'),
-        includeReferencesDeep: request.payload.includeReferencesDeep,
-        excludeExportDetails: request.payload.excludeExportDetails,
-      });
-
-      const docsToExport: string[] = await createPromiseFromStreams([
-        exportStream,
-        createMapStream((obj: unknown) => {
-          return stringify(obj);
-        }),
-        createConcatStream([]),
-      ]);
-
-      return h
-        .response(docsToExport.join('\n'))
-        .header('Content-Disposition', `attachment; filename="export.ndjson"`)
-        .header('Content-Type', 'application/ndjson');
-    },
-  },
-});
diff --git a/src/legacy/server/saved_objects/routes/find.ts b/src/legacy/server/saved_objects/routes/find.ts
deleted file mode 100644
index f8cb8c50d9684..0000000000000
--- a/src/legacy/server/saved_objects/routes/find.ts
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import Hapi from 'hapi';
-import Joi from 'joi';
-import { SavedObjectsClientContract } from 'src/core/server';
-import { Prerequisites, WithoutQueryAndParams } from './types';
-
-interface FindRequest extends WithoutQueryAndParams<Hapi.Request> {
-  pre: {
-    savedObjectsClient: SavedObjectsClientContract;
-  };
-  query: {
-    per_page: number;
-    page: number;
-    type: string[];
-    search?: string;
-    default_search_operator: 'AND' | 'OR';
-    search_fields?: string[];
-    sort_field?: string;
-    has_reference?: {
-      type: string;
-      id: string;
-    };
-    fields?: string[];
-    filter?: string;
-  };
-}
-
-export const createFindRoute = (prereqs: Prerequisites) => ({
-  path: '/api/saved_objects/_find',
-  method: 'GET',
-  config: {
-    pre: [prereqs.getSavedObjectsClient],
-    validate: {
-      query: Joi.object()
-        .keys({
-          per_page: Joi.number()
-            .min(0)
-            .default(20),
-          page: Joi.number()
-            .min(0)
-            .default(1),
-          type: Joi.array()
-            .items(Joi.string())
-            .single()
-            .required(),
-          search: Joi.string()
-            .allow('')
-            .optional(),
-          default_search_operator: Joi.string()
-            .valid('OR', 'AND')
-            .default('OR'),
-          search_fields: Joi.array()
-            .items(Joi.string())
-            .single(),
-          sort_field: Joi.string(),
-          has_reference: Joi.object()
-            .keys({
-              type: Joi.string().required(),
-              id: Joi.string().required(),
-            })
-            .optional(),
-          fields: Joi.array()
-            .items(Joi.string())
-            .single(),
-          filter: Joi.string()
-            .allow('')
-            .optional(),
-        })
-        .default(),
-    },
-    handler(request: FindRequest) {
-      const query = request.query;
-      return request.pre.savedObjectsClient.find({
-        perPage: query.per_page,
-        page: query.page,
-        type: query.type,
-        search: query.search,
-        defaultSearchOperator: query.default_search_operator,
-        searchFields: query.search_fields,
-        sortField: query.sort_field,
-        hasReference: query.has_reference,
-        fields: query.fields,
-        filter: query.filter,
-      });
-    },
-  },
-});
diff --git a/src/legacy/server/saved_objects/routes/get.test.ts b/src/legacy/server/saved_objects/routes/get.test.ts
deleted file mode 100644
index 7ede2a8b4d7b4..0000000000000
--- a/src/legacy/server/saved_objects/routes/get.test.ts
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import Hapi from 'hapi';
-import { createMockServer } from './_mock_server';
-import { createGetRoute } from './get';
-import { savedObjectsClientMock } from '../../../../core/server/mocks';
-
-describe('GET /api/saved_objects/{type}/{id}', () => {
-  let server: Hapi.Server;
-  const savedObjectsClient = savedObjectsClientMock.create();
-
-  beforeEach(() => {
-    savedObjectsClient.get.mockImplementation(() =>
-      Promise.resolve({
-        id: 'logstash-*',
-        title: 'logstash-*',
-        type: 'logstash-type',
-        attributes: {},
-        timeFieldName: '@timestamp',
-        notExpandable: true,
-        references: [],
-      })
-    );
-    server = createMockServer();
-
-    const prereqs = {
-      getSavedObjectsClient: {
-        assign: 'savedObjectsClient',
-        method() {
-          return savedObjectsClient;
-        },
-      },
-    };
-
-    server.route(createGetRoute(prereqs));
-  });
-
-  afterEach(() => {
-    savedObjectsClient.get.mockReset();
-  });
-
-  it('formats successful response', async () => {
-    const request = {
-      method: 'GET',
-      url: '/api/saved_objects/index-pattern/logstash-*',
-    };
-    const clientResponse = {
-      id: 'logstash-*',
-      title: 'logstash-*',
-      type: 'logstash-type',
-      attributes: {},
-      timeFieldName: '@timestamp',
-      notExpandable: true,
-      references: [],
-    };
-
-    savedObjectsClient.get.mockImplementation(() => Promise.resolve(clientResponse));
-
-    const { payload, statusCode } = await server.inject(request);
-    const response = JSON.parse(payload);
-
-    expect(statusCode).toBe(200);
-    expect(response).toEqual(clientResponse);
-  });
-
-  it('calls upon savedObjectClient.get', async () => {
-    const request = {
-      method: 'GET',
-      url: '/api/saved_objects/index-pattern/logstash-*',
-    };
-
-    await server.inject(request);
-    expect(savedObjectsClient.get).toHaveBeenCalled();
-
-    const args = savedObjectsClient.get.mock.calls[0];
-    expect(args).toEqual(['index-pattern', 'logstash-*']);
-  });
-});
diff --git a/src/legacy/server/saved_objects/routes/get.ts b/src/legacy/server/saved_objects/routes/get.ts
deleted file mode 100644
index 4dbb06d53425a..0000000000000
--- a/src/legacy/server/saved_objects/routes/get.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import Hapi from 'hapi';
-import Joi from 'joi';
-import { SavedObjectsClientContract } from 'src/core/server';
-import { Prerequisites } from './types';
-
-interface GetRequest extends Hapi.Request {
-  pre: {
-    savedObjectsClient: SavedObjectsClientContract;
-  };
-  params: {
-    type: string;
-    id: string;
-  };
-}
-
-export const createGetRoute = (prereqs: Prerequisites) => ({
-  path: '/api/saved_objects/{type}/{id}',
-  method: 'GET',
-  config: {
-    pre: [prereqs.getSavedObjectsClient],
-    validate: {
-      params: Joi.object()
-        .keys({
-          type: Joi.string().required(),
-          id: Joi.string().required(),
-        })
-        .required(),
-    },
-    handler(request: GetRequest) {
-      const { savedObjectsClient } = request.pre;
-      const { type, id } = request.params;
-
-      return savedObjectsClient.get(type, id);
-    },
-  },
-});
diff --git a/src/legacy/server/saved_objects/routes/import.test.ts b/src/legacy/server/saved_objects/routes/import.test.ts
deleted file mode 100644
index 2b8d9d7523507..0000000000000
--- a/src/legacy/server/saved_objects/routes/import.test.ts
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import Hapi from 'hapi';
-import { createMockServer } from './_mock_server';
-import { createImportRoute } from './import';
-import { savedObjectsClientMock } from '../../../../core/server/mocks';
-
-describe('POST /api/saved_objects/_import', () => {
-  let server: Hapi.Server;
-  const savedObjectsClient = savedObjectsClientMock.create();
-  const emptyResponse = {
-    saved_objects: [],
-    total: 0,
-    per_page: 0,
-    page: 0,
-  };
-
-  beforeEach(() => {
-    server = createMockServer();
-    jest.resetAllMocks();
-
-    const prereqs = {
-      getSavedObjectsClient: {
-        assign: 'savedObjectsClient',
-        method() {
-          return savedObjectsClient;
-        },
-      },
-    };
-
-    server.route(
-      createImportRoute(prereqs, server, ['index-pattern', 'visualization', 'dashboard'])
-    );
-  });
-
-  test('formats successful response', async () => {
-    const request = {
-      method: 'POST',
-      url: '/api/saved_objects/_import',
-      payload: [
-        '--BOUNDARY',
-        'Content-Disposition: form-data; name="file"; filename="export.ndjson"',
-        'Content-Type: application/ndjson',
-        '',
-        '',
-        '--BOUNDARY--',
-      ].join('\r\n'),
-      headers: {
-        'content-Type': 'multipart/form-data; boundary=BOUNDARY',
-      },
-    };
-    savedObjectsClient.find.mockResolvedValueOnce(emptyResponse);
-    const { payload, statusCode } = await server.inject(request);
-    const response = JSON.parse(payload);
-    expect(statusCode).toBe(200);
-    expect(response).toEqual({
-      success: true,
-      successCount: 0,
-    });
-    expect(savedObjectsClient.bulkCreate).toHaveBeenCalledTimes(0);
-  });
-
-  test('defaults migrationVersion to empty object', async () => {
-    const request = {
-      method: 'POST',
-      url: '/api/saved_objects/_import',
-      payload: [
-        '--EXAMPLE',
-        'Content-Disposition: form-data; name="file"; filename="export.ndjson"',
-        'Content-Type: application/ndjson',
-        '',
-        '{"type":"index-pattern","id":"my-pattern","attributes":{"title":"my-pattern-*"}}',
-        '--EXAMPLE--',
-      ].join('\r\n'),
-      headers: {
-        'content-Type': 'multipart/form-data; boundary=EXAMPLE',
-      },
-    };
-    savedObjectsClient.find.mockResolvedValueOnce(emptyResponse);
-    savedObjectsClient.bulkCreate.mockResolvedValueOnce({
-      saved_objects: [
-        {
-          type: 'index-pattern',
-          id: 'my-pattern',
-          attributes: {
-            title: 'my-pattern-*',
-          },
-          references: [],
-        },
-      ],
-    });
-    const { payload, statusCode } = await server.inject(request);
-    const response = JSON.parse(payload);
-    expect(statusCode).toBe(200);
-    expect(response).toEqual({
-      success: true,
-      successCount: 1,
-    });
-    expect(savedObjectsClient.bulkCreate.mock.calls).toHaveLength(1);
-    const firstBulkCreateCallArray = savedObjectsClient.bulkCreate.mock.calls[0][0];
-    expect(firstBulkCreateCallArray).toHaveLength(1);
-    expect(firstBulkCreateCallArray[0].migrationVersion).toEqual({});
-  });
-
-  test('imports an index pattern and dashboard, ignoring empty lines in the file', async () => {
-    // NOTE: changes to this scenario should be reflected in the docs
-    const request = {
-      method: 'POST',
-      url: '/api/saved_objects/_import',
-      payload: [
-        '--EXAMPLE',
-        'Content-Disposition: form-data; name="file"; filename="export.ndjson"',
-        'Content-Type: application/ndjson',
-        '',
-        '{"type":"index-pattern","id":"my-pattern","attributes":{"title":"my-pattern-*"}}',
-        '',
-        '',
-        '',
-        '{"type":"dashboard","id":"my-dashboard","attributes":{"title":"Look at my dashboard"}}',
-        '--EXAMPLE--',
-      ].join('\r\n'),
-      headers: {
-        'content-Type': 'multipart/form-data; boundary=EXAMPLE',
-      },
-    };
-    savedObjectsClient.find.mockResolvedValueOnce(emptyResponse);
-    savedObjectsClient.bulkCreate.mockResolvedValueOnce({
-      saved_objects: [
-        {
-          type: 'index-pattern',
-          id: 'my-pattern',
-          attributes: {
-            title: 'my-pattern-*',
-          },
-          references: [],
-        },
-        {
-          type: 'dashboard',
-          id: 'my-dashboard',
-          attributes: {
-            title: 'Look at my dashboard',
-          },
-          references: [],
-        },
-      ],
-    });
-    const { payload, statusCode } = await server.inject(request);
-    const response = JSON.parse(payload);
-    expect(statusCode).toBe(200);
-    expect(response).toEqual({
-      success: true,
-      successCount: 2,
-    });
-  });
-
-  test('imports an index pattern and dashboard but has a conflict on the index pattern', async () => {
-    // NOTE: changes to this scenario should be reflected in the docs
-    const request = {
-      method: 'POST',
-      url: '/api/saved_objects/_import',
-      payload: [
-        '--EXAMPLE',
-        'Content-Disposition: form-data; name="file"; filename="export.ndjson"',
-        'Content-Type: application/ndjson',
-        '',
-        '{"type":"index-pattern","id":"my-pattern","attributes":{"title":"my-pattern-*"}}',
-        '{"type":"dashboard","id":"my-dashboard","attributes":{"title":"Look at my dashboard"}}',
-        '--EXAMPLE--',
-      ].join('\r\n'),
-      headers: {
-        'content-Type': 'multipart/form-data; boundary=EXAMPLE',
-      },
-    };
-    savedObjectsClient.find.mockResolvedValueOnce(emptyResponse);
-    savedObjectsClient.bulkCreate.mockResolvedValueOnce({
-      saved_objects: [
-        {
-          type: 'index-pattern',
-          id: 'my-pattern',
-          attributes: {},
-          references: [],
-          error: {
-            statusCode: 409,
-            message: 'version conflict, document already exists',
-          },
-        },
-        {
-          type: 'dashboard',
-          id: 'my-dashboard',
-          attributes: {
-            title: 'Look at my dashboard',
-          },
-          references: [],
-        },
-      ],
-    });
-    const { payload, statusCode } = await server.inject(request);
-    const response = JSON.parse(payload);
-    expect(statusCode).toBe(200);
-    expect(response).toEqual({
-      success: false,
-      successCount: 1,
-      errors: [
-        {
-          id: 'my-pattern',
-          type: 'index-pattern',
-          title: 'my-pattern-*',
-          error: {
-            type: 'conflict',
-          },
-        },
-      ],
-    });
-  });
-
-  test('imports a visualization with missing references', async () => {
-    // NOTE: changes to this scenario should be reflected in the docs
-    const request = {
-      method: 'POST',
-      url: '/api/saved_objects/_import',
-      payload: [
-        '--EXAMPLE',
-        'Content-Disposition: form-data; name="file"; filename="export.ndjson"',
-        'Content-Type: application/ndjson',
-        '',
-        '{"type":"visualization","id":"my-vis","attributes":{"title":"my-vis"},"references":[{"name":"ref_0","type":"index-pattern","id":"my-pattern-*"}]}',
-        '{"type":"dashboard","id":"my-dashboard","attributes":{"title":"Look at my dashboard"},"references":[{"name":"ref_0","type":"visualization","id":"my-vis"}]}',
-        '--EXAMPLE--',
-      ].join('\r\n'),
-      headers: {
-        'content-Type': 'multipart/form-data; boundary=EXAMPLE',
-      },
-    };
-    savedObjectsClient.bulkGet.mockResolvedValueOnce({
-      saved_objects: [
-        {
-          id: 'my-pattern-*',
-          type: 'index-pattern',
-          error: {
-            statusCode: 404,
-            message: 'Not found',
-          },
-          references: [],
-          attributes: {},
-        },
-      ],
-    });
-    const { payload, statusCode } = await server.inject(request);
-    const response = JSON.parse(payload);
-    expect(statusCode).toBe(200);
-    expect(response).toEqual({
-      success: false,
-      successCount: 0,
-      errors: [
-        {
-          id: 'my-vis',
-          type: 'visualization',
-          title: 'my-vis',
-          error: {
-            type: 'missing_references',
-            references: [
-              {
-                type: 'index-pattern',
-                id: 'my-pattern-*',
-              },
-            ],
-            blocking: [
-              {
-                type: 'dashboard',
-                id: 'my-dashboard',
-              },
-            ],
-          },
-        },
-      ],
-    });
-    expect(savedObjectsClient.bulkGet).toMatchInlineSnapshot(`
-[MockFunction] {
-  "calls": Array [
-    Array [
-      Array [
-        Object {
-          "fields": Array [
-            "id",
-          ],
-          "id": "my-pattern-*",
-          "type": "index-pattern",
-        },
-      ],
-      Object {
-        "namespace": undefined,
-      },
-    ],
-  ],
-  "results": Array [
-    Object {
-      "type": "return",
-      "value": Promise {},
-    },
-  ],
-}
-`);
-  });
-});
diff --git a/src/legacy/server/saved_objects/routes/import.ts b/src/legacy/server/saved_objects/routes/import.ts
deleted file mode 100644
index 53fa3ea7d5d00..0000000000000
--- a/src/legacy/server/saved_objects/routes/import.ts
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import Boom from 'boom';
-import Hapi from 'hapi';
-import Joi from 'joi';
-import { extname } from 'path';
-import { Readable } from 'stream';
-import { SavedObjectsClientContract } from 'src/core/server';
-// Disable lint errors for imports from src/core/server/saved_objects until SavedObjects migration is complete
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { importSavedObjects } from '../../../../core/server/saved_objects/import';
-import { Prerequisites, WithoutQueryAndParams } from './types';
-import { createSavedObjectsStreamFromNdJson } from '../lib';
-
-interface HapiReadableStream extends Readable {
-  hapi: {
-    filename: string;
-  };
-}
-
-interface ImportRequest extends WithoutQueryAndParams<Hapi.Request> {
-  pre: {
-    savedObjectsClient: SavedObjectsClientContract;
-  };
-  query: {
-    overwrite: boolean;
-  };
-  payload: {
-    file: HapiReadableStream;
-  };
-}
-
-export const createImportRoute = (
-  prereqs: Prerequisites,
-  server: Hapi.Server,
-  supportedTypes: string[]
-) => ({
-  path: '/api/saved_objects/_import',
-  method: 'POST',
-  config: {
-    pre: [prereqs.getSavedObjectsClient],
-    payload: {
-      maxBytes: server.config().get('savedObjects.maxImportPayloadBytes'),
-      output: 'stream',
-      allow: 'multipart/form-data',
-    },
-    validate: {
-      query: Joi.object()
-        .keys({
-          overwrite: Joi.boolean().default(false),
-        })
-        .default(),
-      payload: Joi.object({
-        file: Joi.object().required(),
-      }).default(),
-    },
-  },
-  async handler(request: ImportRequest, h: Hapi.ResponseToolkit) {
-    const { savedObjectsClient } = request.pre;
-    const { filename } = request.payload.file.hapi;
-    const fileExtension = extname(filename).toLowerCase();
-    if (fileExtension !== '.ndjson') {
-      return Boom.badRequest(`Invalid file extension ${fileExtension}`);
-    }
-
-    return await importSavedObjects({
-      supportedTypes,
-      savedObjectsClient,
-      readStream: createSavedObjectsStreamFromNdJson(request.payload.file),
-      objectLimit: request.server.config().get('savedObjects.maxImportExportSize'),
-      overwrite: request.query.overwrite,
-    });
-  },
-});
diff --git a/src/legacy/server/saved_objects/routes/resolve_import_errors.ts b/src/legacy/server/saved_objects/routes/resolve_import_errors.ts
deleted file mode 100644
index 15d02f525c2cf..0000000000000
--- a/src/legacy/server/saved_objects/routes/resolve_import_errors.ts
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import Boom from 'boom';
-import Hapi from 'hapi';
-import Joi from 'joi';
-import { extname } from 'path';
-import { Readable } from 'stream';
-import { SavedObjectsClientContract } from 'src/core/server';
-// Disable lint errors for imports from src/core/server/saved_objects until SavedObjects migration is complete
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { resolveImportErrors } from '../../../../core/server/saved_objects/import';
-import { Prerequisites } from './types';
-import { createSavedObjectsStreamFromNdJson } from '../lib';
-
-interface HapiReadableStream extends Readable {
-  hapi: {
-    filename: string;
-  };
-}
-
-interface ImportRequest extends Hapi.Request {
-  pre: {
-    savedObjectsClient: SavedObjectsClientContract;
-  };
-  payload: {
-    file: HapiReadableStream;
-    retries: Array<{
-      type: string;
-      id: string;
-      overwrite: boolean;
-      replaceReferences: Array<{
-        type: string;
-        from: string;
-        to: string;
-      }>;
-    }>;
-  };
-}
-
-export const createResolveImportErrorsRoute = (
-  prereqs: Prerequisites,
-  server: Hapi.Server,
-  supportedTypes: string[]
-) => ({
-  path: '/api/saved_objects/_resolve_import_errors',
-  method: 'POST',
-  config: {
-    pre: [prereqs.getSavedObjectsClient],
-    payload: {
-      maxBytes: server.config().get('savedObjects.maxImportPayloadBytes'),
-      output: 'stream',
-      allow: 'multipart/form-data',
-    },
-    validate: {
-      payload: Joi.object({
-        file: Joi.object().required(),
-        retries: Joi.array()
-          .items(
-            Joi.object({
-              type: Joi.string().required(),
-              id: Joi.string().required(),
-              overwrite: Joi.boolean().default(false),
-              replaceReferences: Joi.array()
-                .items(
-                  Joi.object({
-                    type: Joi.string().required(),
-                    from: Joi.string().required(),
-                    to: Joi.string().required(),
-                  })
-                )
-                .default([]),
-            })
-          )
-          .required(),
-      }).default(),
-    },
-  },
-  async handler(request: ImportRequest) {
-    const { savedObjectsClient } = request.pre;
-    const { filename } = request.payload.file.hapi;
-    const fileExtension = extname(filename).toLowerCase();
-
-    if (fileExtension !== '.ndjson') {
-      return Boom.badRequest(`Invalid file extension ${fileExtension}`);
-    }
-
-    return await resolveImportErrors({
-      supportedTypes,
-      savedObjectsClient,
-      readStream: createSavedObjectsStreamFromNdJson(request.payload.file),
-      retries: request.payload.retries,
-      objectLimit: request.server.config().get('savedObjects.maxImportExportSize'),
-    });
-  },
-});
diff --git a/src/legacy/server/saved_objects/routes/update.ts b/src/legacy/server/saved_objects/routes/update.ts
deleted file mode 100644
index dadd1e412c8cd..0000000000000
--- a/src/legacy/server/saved_objects/routes/update.ts
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import Hapi from 'hapi';
-import Joi from 'joi';
-import { SavedObjectAttributes, SavedObjectsClient } from 'src/core/server';
-import { Prerequisites, SavedObjectReference } from './types';
-
-interface UpdateRequest extends Hapi.Request {
-  pre: {
-    savedObjectsClient: SavedObjectsClient;
-  };
-  params: {
-    type: string;
-    id: string;
-  };
-  payload: {
-    attributes: SavedObjectAttributes;
-    version?: string;
-    references: SavedObjectReference[];
-  };
-}
-
-export const createUpdateRoute = (prereqs: Prerequisites) => {
-  return {
-    path: '/api/saved_objects/{type}/{id}',
-    method: 'PUT',
-    config: {
-      pre: [prereqs.getSavedObjectsClient],
-      validate: {
-        params: Joi.object()
-          .keys({
-            type: Joi.string().required(),
-            id: Joi.string().required(),
-          })
-          .required(),
-        payload: Joi.object({
-          attributes: Joi.object().required(),
-          version: Joi.string(),
-          references: Joi.array().items(
-            Joi.object().keys({
-              name: Joi.string().required(),
-              type: Joi.string().required(),
-              id: Joi.string().required(),
-            })
-          ),
-        }).required(),
-      },
-      handler(request: UpdateRequest) {
-        const { savedObjectsClient } = request.pre;
-        const { type, id } = request.params;
-        const { attributes, version, references } = request.payload;
-        const options = { version, references };
-
-        return savedObjectsClient.update(type, id, attributes, options);
-      },
-    },
-  };
-};
diff --git a/src/legacy/server/saved_objects/saved_objects_mixin.js b/src/legacy/server/saved_objects/saved_objects_mixin.js
index 1d2f2d0f23e17..c7286f77ceccd 100644
--- a/src/legacy/server/saved_objects/saved_objects_mixin.js
+++ b/src/legacy/server/saved_objects/saved_objects_mixin.js
@@ -31,30 +31,6 @@ import { getRootPropertiesObjects } from '../../../core/server/saved_objects/map
 import { convertTypesToLegacySchema } from '../../../core/server/saved_objects/utils';
 import { SavedObjectsManagement } from '../../../core/server/saved_objects/management';
 
-import {
-  createBulkCreateRoute,
-  createBulkGetRoute,
-  createCreateRoute,
-  createDeleteRoute,
-  createFindRoute,
-  createGetRoute,
-  createUpdateRoute,
-  createBulkUpdateRoute,
-  createExportRoute,
-  createImportRoute,
-  createResolveImportErrorsRoute,
-  createLogLegacyImportRoute,
-} from './routes';
-
-function getImportableAndExportableTypes({ kbnServer, visibleTypes }) {
-  const { savedObjectsManagement = {} } = kbnServer.uiExports;
-  return visibleTypes.filter(
-    type =>
-      savedObjectsManagement[type] &&
-      savedObjectsManagement[type].isImportableAndExportable === true
-  );
-}
-
 export function savedObjectsMixin(kbnServer, server) {
   const migrator = kbnServer.newPlatform.__internals.kibanaMigrator;
   const typeRegistry = kbnServer.newPlatform.__internals.typeRegistry;
@@ -62,7 +38,6 @@ export function savedObjectsMixin(kbnServer, server) {
   const allTypes = Object.keys(getRootPropertiesObjects(mappings));
   const schema = new SavedObjectsSchema(convertTypesToLegacySchema(typeRegistry.getAllTypes()));
   const visibleTypes = allTypes.filter(type => !schema.isHiddenType(type));
-  const importableAndExportableTypes = getImportableAndExportableTypes({ kbnServer, visibleTypes });
 
   server.decorate('server', 'kibanaMigrator', migrator);
   server.decorate(
@@ -79,27 +54,6 @@ export function savedObjectsMixin(kbnServer, server) {
     return;
   }
 
-  const prereqs = {
-    getSavedObjectsClient: {
-      assign: 'savedObjectsClient',
-      method(req) {
-        return req.getSavedObjectsClient();
-      },
-    },
-  };
-  server.route(createBulkCreateRoute(prereqs));
-  server.route(createBulkGetRoute(prereqs));
-  server.route(createBulkUpdateRoute(prereqs));
-  server.route(createCreateRoute(prereqs));
-  server.route(createDeleteRoute(prereqs));
-  server.route(createFindRoute(prereqs));
-  server.route(createGetRoute(prereqs));
-  server.route(createUpdateRoute(prereqs));
-  server.route(createExportRoute(prereqs, server, importableAndExportableTypes));
-  server.route(createImportRoute(prereqs, server, importableAndExportableTypes));
-  server.route(createResolveImportErrorsRoute(prereqs, server, importableAndExportableTypes));
-  server.route(createLogLegacyImportRoute());
-
   const serializer = kbnServer.newPlatform.start.core.savedObjects.createSerializer();
 
   const createRepository = (callCluster, extraTypes = []) => {
diff --git a/src/legacy/server/saved_objects/saved_objects_mixin.test.js b/src/legacy/server/saved_objects/saved_objects_mixin.test.js
index 9ca0374b959f6..99d2041448426 100644
--- a/src/legacy/server/saved_objects/saved_objects_mixin.test.js
+++ b/src/legacy/server/saved_objects/saved_objects_mixin.test.js
@@ -185,82 +185,6 @@ describe('Saved Objects Mixin', () => {
     });
   });
 
-  describe('Routes', () => {
-    it('should create 12 routes', () => {
-      savedObjectsMixin(mockKbnServer, mockServer);
-      expect(mockServer.route).toHaveBeenCalledTimes(12);
-    });
-    it('should add POST /api/saved_objects/_bulk_create', () => {
-      savedObjectsMixin(mockKbnServer, mockServer);
-      expect(mockServer.route).toHaveBeenCalledWith(
-        expect.objectContaining({ path: '/api/saved_objects/_bulk_create', method: 'POST' })
-      );
-    });
-    it('should add POST /api/saved_objects/_bulk_get', () => {
-      savedObjectsMixin(mockKbnServer, mockServer);
-      expect(mockServer.route).toHaveBeenCalledWith(
-        expect.objectContaining({ path: '/api/saved_objects/_bulk_get', method: 'POST' })
-      );
-    });
-    it('should add POST /api/saved_objects/{type}/{id?}', () => {
-      savedObjectsMixin(mockKbnServer, mockServer);
-      expect(mockServer.route).toHaveBeenCalledWith(
-        expect.objectContaining({ path: '/api/saved_objects/{type}/{id?}', method: 'POST' })
-      );
-    });
-    it('should add DELETE /api/saved_objects/{type}/{id}', () => {
-      savedObjectsMixin(mockKbnServer, mockServer);
-      expect(mockServer.route).toHaveBeenCalledWith(
-        expect.objectContaining({ path: '/api/saved_objects/{type}/{id}', method: 'DELETE' })
-      );
-    });
-    it('should add GET /api/saved_objects/_find', () => {
-      savedObjectsMixin(mockKbnServer, mockServer);
-      expect(mockServer.route).toHaveBeenCalledWith(
-        expect.objectContaining({ path: '/api/saved_objects/_find', method: 'GET' })
-      );
-    });
-    it('should add GET /api/saved_objects/{type}/{id}', () => {
-      savedObjectsMixin(mockKbnServer, mockServer);
-      expect(mockServer.route).toHaveBeenCalledWith(
-        expect.objectContaining({ path: '/api/saved_objects/{type}/{id}', method: 'GET' })
-      );
-    });
-    it('should add PUT /api/saved_objects/{type}/{id}', () => {
-      savedObjectsMixin(mockKbnServer, mockServer);
-      expect(mockServer.route).toHaveBeenCalledWith(
-        expect.objectContaining({ path: '/api/saved_objects/{type}/{id}', method: 'PUT' })
-      );
-    });
-    it('should add GET /api/saved_objects/_export', () => {
-      savedObjectsMixin(mockKbnServer, mockServer);
-      expect(mockServer.route).toHaveBeenCalledWith(
-        expect.objectContaining({ path: '/api/saved_objects/_export', method: 'POST' })
-      );
-    });
-    it('should add POST /api/saved_objects/_import', () => {
-      savedObjectsMixin(mockKbnServer, mockServer);
-      expect(mockServer.route).toHaveBeenCalledWith(
-        expect.objectContaining({ path: '/api/saved_objects/_import', method: 'POST' })
-      );
-    });
-    it('should add POST /api/saved_objects/_resolve_import_errors', () => {
-      savedObjectsMixin(mockKbnServer, mockServer);
-      expect(mockServer.route).toHaveBeenCalledWith(
-        expect.objectContaining({
-          path: '/api/saved_objects/_resolve_import_errors',
-          method: 'POST',
-        })
-      );
-    });
-    it('should add POST /api/saved_objects/_log_legacy_import', () => {
-      savedObjectsMixin(mockKbnServer, mockServer);
-      expect(mockServer.route).toHaveBeenCalledWith(
-        expect.objectContaining({ path: '/api/saved_objects/_log_legacy_import', method: 'POST' })
-      );
-    });
-  });
-
   describe('Saved object service', () => {
     let service;
 
diff --git a/test/api_integration/apis/general/index.js b/test/api_integration/apis/general/index.js
index dca47dddbd4c8..2420297fb1af9 100644
--- a/test/api_integration/apis/general/index.js
+++ b/test/api_integration/apis/general/index.js
@@ -21,6 +21,5 @@ export default function({ loadTestFile }) {
   describe('general', () => {
     loadTestFile(require.resolve('./cookies'));
     loadTestFile(require.resolve('./csp'));
-    loadTestFile(require.resolve('./prototype_pollution'));
   });
 }
diff --git a/test/api_integration/apis/general/prototype_pollution.ts b/test/api_integration/apis/general/prototype_pollution.ts
deleted file mode 100644
index 3e74480ebe9eb..0000000000000
--- a/test/api_integration/apis/general/prototype_pollution.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { FtrProviderContext } from 'test/api_integration/ftr_provider_context';
-
-// eslint-disable-next-line import/no-default-export
-export default function({ getService }: FtrProviderContext) {
-  const supertest = getService('supertest');
-
-  describe('prototype pollution smoke test', () => {
-    it('prevents payloads with the "constructor.prototype" pollution vector from being accepted', async () => {
-      await supertest
-        .post('/api/saved_objects/_log_legacy_import')
-        .send([
-          {
-            constructor: {
-              prototype: 'foo',
-            },
-          },
-        ])
-        .expect(400, {
-          statusCode: 400,
-          error: 'Bad Request',
-          message: "'constructor.prototype' is an invalid key",
-          validation: { source: 'payload', keys: [] },
-        });
-    });
-
-    it('prevents payloads with the "__proto__" pollution vector from being accepted', async () => {
-      await supertest
-        .post('/api/saved_objects/_log_legacy_import')
-        .send(JSON.parse(`{"foo": { "__proto__": {} } }`))
-        .expect(400, {
-          statusCode: 400,
-          error: 'Bad Request',
-          message: "'__proto__' is an invalid key",
-          validation: { source: 'payload', keys: [] },
-        });
-    });
-  });
-}
diff --git a/test/api_integration/apis/saved_objects/export.js b/test/api_integration/apis/saved_objects/export.js
index 6648ac7b90622..ace65f190dec2 100644
--- a/test/api_integration/apis/saved_objects/export.js
+++ b/test/api_integration/apis/saved_objects/export.js
@@ -192,12 +192,9 @@ export default function({ getService }) {
                 statusCode: 400,
                 error: 'Bad Request',
                 message:
-                  'child "type" fails because ["type" at position 0 fails because ' +
-                  '["0" must be one of [config, dashboard, index-pattern, query, search, url, visualization]]]',
-                validation: {
-                  source: 'payload',
-                  keys: ['type.0'],
-                },
+                  '[request body.type]: types that failed validation:\n' +
+                  '- [request body.type.0]: expected value of type [string] but got [Array]\n' +
+                  '- [request body.type.1.0]: wigwags is not exportable',
               });
             });
         });
@@ -215,8 +212,7 @@ export default function({ getService }) {
               expect(resp.body).to.eql({
                 statusCode: 400,
                 error: 'Bad Request',
-                message: '"value" must be an object',
-                validation: { source: 'payload', keys: ['value'] },
+                message: '[request body]: expected a plain object value, but found [null] instead.',
               });
             });
         });
@@ -421,8 +417,7 @@ export default function({ getService }) {
               expect(resp.body).to.eql({
                 statusCode: 400,
                 error: 'Bad Request',
-                message: '"value" contains a conflict between exclusive peers [type, objects]',
-                validation: { source: 'payload', keys: ['value'] },
+                message: `Can't specify both "types" and "objects" properties when exporting`,
               });
             });
         });
diff --git a/test/api_integration/apis/saved_objects/find.js b/test/api_integration/apis/saved_objects/find.js
index 43c32156b6ada..54a19602fd414 100644
--- a/test/api_integration/apis/saved_objects/find.js
+++ b/test/api_integration/apis/saved_objects/find.js
@@ -230,12 +230,9 @@ export default function({ getService }) {
             .then(resp => {
               expect(resp.body).to.eql({
                 error: 'Bad Request',
-                message: 'child "type" fails because ["type" is required]',
+                message:
+                  '[request query.type]: expected at least one defined value but got [undefined]',
                 statusCode: 400,
-                validation: {
-                  keys: ['type'],
-                  source: 'query',
-                },
               });
             }));
       });
diff --git a/test/api_integration/apis/saved_objects/resolve_import_errors.js b/test/api_integration/apis/saved_objects/resolve_import_errors.js
index 0375ad86b8f4a..60d2f42d51d13 100644
--- a/test/api_integration/apis/saved_objects/resolve_import_errors.js
+++ b/test/api_integration/apis/saved_objects/resolve_import_errors.js
@@ -85,8 +85,7 @@ export default function({ getService }) {
             expect(resp.body).to.eql({
               statusCode: 400,
               error: 'Bad Request',
-              message: 'child "file" fails because ["file" is required]',
-              validation: { source: 'payload', keys: ['file'] },
+              message: '[request body.file]: expected value of type [Stream] but got [undefined]',
             });
           });
       });
diff --git a/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.test.ts b/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.test.ts
index c9ac114f3808a..9ef229159a885 100644
--- a/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.test.ts
+++ b/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.test.ts
@@ -135,28 +135,24 @@ describe('copySavedObjectsToSpaces', () => {
 
     expect((savedObjectsService.importExport.getSortedObjectsForExport as jest.Mock).mock.calls)
       .toMatchInlineSnapshot(`
-                  Array [
-                    Array [
-                      Object {
-                        "excludeExportDetails": true,
-                        "exportSizeLimit": 1000,
-                        "includeReferencesDeep": true,
-                        "namespace": "sourceSpace",
-                        "objects": Array [
-                          Object {
-                            "id": "my-dashboard",
-                            "type": "dashboard",
-                          },
-                        ],
-                        "savedObjectsClient": null,
-                        "types": Array [
-                          "dashboard",
-                          "visualization",
-                        ],
-                      },
-                    ],
-                  ]
-            `);
+      Array [
+        Array [
+          Object {
+            "excludeExportDetails": true,
+            "exportSizeLimit": 1000,
+            "includeReferencesDeep": true,
+            "namespace": "sourceSpace",
+            "objects": Array [
+              Object {
+                "id": "my-dashboard",
+                "type": "dashboard",
+              },
+            ],
+            "savedObjectsClient": null,
+          },
+        ],
+      ]
+    `);
 
     expect((savedObjectsService.importExport.importSavedObjects as jest.Mock).mock.calls)
       .toMatchInlineSnapshot(`
diff --git a/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.ts b/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.ts
index 76c3037e672ad..04b09b5e05b83 100644
--- a/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.ts
+++ b/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.ts
@@ -39,7 +39,6 @@ export function copySavedObjectsToSpacesFactory(
       excludeExportDetails: true,
       objects: options.objects,
       savedObjectsClient,
-      types: eligibleTypes,
       exportSizeLimit: importExport.objectLimit,
     });
 
diff --git a/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.test.ts b/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.test.ts
index a1db5032ba73b..25ed4dee6d4d0 100644
--- a/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.test.ts
+++ b/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.test.ts
@@ -155,28 +155,24 @@ describe('resolveCopySavedObjectsToSpacesConflicts', () => {
 
     expect((savedObjectsService.importExport.getSortedObjectsForExport as jest.Mock).mock.calls)
       .toMatchInlineSnapshot(`
-            Array [
-              Array [
-                Object {
-                  "excludeExportDetails": true,
-                  "exportSizeLimit": 1000,
-                  "includeReferencesDeep": true,
-                  "namespace": "sourceSpace",
-                  "objects": Array [
-                    Object {
-                      "id": "my-dashboard",
-                      "type": "dashboard",
-                    },
-                  ],
-                  "savedObjectsClient": null,
-                  "types": Array [
-                    "dashboard",
-                    "visualization",
-                  ],
-                },
-              ],
-            ]
-        `);
+      Array [
+        Array [
+          Object {
+            "excludeExportDetails": true,
+            "exportSizeLimit": 1000,
+            "includeReferencesDeep": true,
+            "namespace": "sourceSpace",
+            "objects": Array [
+              Object {
+                "id": "my-dashboard",
+                "type": "dashboard",
+              },
+            ],
+            "savedObjectsClient": null,
+          },
+        ],
+      ]
+    `);
 
     expect((savedObjectsService.importExport.resolveImportErrors as jest.Mock).mock.calls)
       .toMatchInlineSnapshot(`
diff --git a/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.ts b/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.ts
index 22ceeb9dd4dfa..1ec642c158774 100644
--- a/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.ts
+++ b/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.ts
@@ -34,7 +34,6 @@ export function resolveCopySavedObjectsToSpacesConflictsFactory(
       excludeExportDetails: true,
       objects: options.objects,
       savedObjectsClient,
-      types: eligibleTypes,
       exportSizeLimit: importExport.objectLimit,
     });
     return readStreamToCompletion<SavedObject>(objectStream);
diff --git a/x-pack/test/saved_object_api_integration/common/suites/export.ts b/x-pack/test/saved_object_api_integration/common/suites/export.ts
index 4a56d18342dc9..caf149a23cdbf 100644
--- a/x-pack/test/saved_object_api_integration/common/suites/export.ts
+++ b/x-pack/test/saved_object_api_integration/common/suites/export.ts
@@ -51,8 +51,7 @@ export function exportTestSuiteFactory(esArchiver: any, supertest: SuperTest<any
     expect(resp.body).to.eql({
       statusCode: 400,
       error: 'Bad Request',
-      message: '"value" must be an object',
-      validation: { source: 'payload', keys: ['value'] },
+      message: '[request body]: expected a plain object value, but found [null] instead.',
     });
   };
 
@@ -60,11 +59,7 @@ export function exportTestSuiteFactory(esArchiver: any, supertest: SuperTest<any
     expect(resp.body).to.eql({
       statusCode: 400,
       error: 'Bad Request',
-      message: `child \"objects\" fails because [\"objects\" at position 0 fails because [child \"type\" fails because [\"type\" must be one of [canvas-element, canvas-workpad, config, dashboard, globaltype, index-pattern, lens, map, query, search, url, visualization]]]]`,
-      validation: {
-        source: 'payload',
-        keys: ['objects.0.type'],
-      },
+      message: `[request body.objects.0.type]: hiddentype is not exportable`,
     });
   };
 
diff --git a/x-pack/test/saved_object_api_integration/common/suites/find.ts b/x-pack/test/saved_object_api_integration/common/suites/find.ts
index f270fc8f4db05..5479960634ccb 100644
--- a/x-pack/test/saved_object_api_integration/common/suites/find.ts
+++ b/x-pack/test/saved_object_api_integration/common/suites/find.ts
@@ -89,12 +89,8 @@ export function findTestSuiteFactory(esArchiver: any, supertest: SuperTest<any>)
   const expectTypeRequired = (resp: { [key: string]: any }) => {
     expect(resp.body).to.eql({
       error: 'Bad Request',
-      message: 'child "type" fails because ["type" is required]',
+      message: '[request query.type]: expected at least one defined value but got [undefined]',
       statusCode: 400,
-      validation: {
-        keys: ['type'],
-        source: 'query',
-      },
     });
   };
 
diff --git a/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_create.ts b/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_create.ts
index d89e3b4f8605b..690e358b744d5 100644
--- a/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_create.ts
+++ b/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_create.ts
@@ -12,13 +12,8 @@ import { bulkCreateTestSuiteFactory } from '../../common/suites/bulk_create';
 const expectNamespaceSpecifiedBadRequest = (resp: { [key: string]: any }) => {
   expect(resp.body).to.eql({
     error: 'Bad Request',
-    message:
-      '"value" at position 0 fails because ["namespace" is not allowed]. "value" does not contain 1 required value(s)',
+    message: '[request body.0.namespace]: definition for this key is missing',
     statusCode: 400,
-    validation: {
-      keys: ['0.namespace', 'value'],
-      source: 'payload',
-    },
   });
 };
 
diff --git a/x-pack/test/saved_object_api_integration/spaces_only/apis/create.ts b/x-pack/test/saved_object_api_integration/spaces_only/apis/create.ts
index cf34f7505cdb2..3bd4019649363 100644
--- a/x-pack/test/saved_object_api_integration/spaces_only/apis/create.ts
+++ b/x-pack/test/saved_object_api_integration/spaces_only/apis/create.ts
@@ -12,12 +12,8 @@ import { createTestSuiteFactory } from '../../common/suites/create';
 const expectNamespaceSpecifiedBadRequest = (resp: { [key: string]: any }) => {
   expect(resp.body).to.eql({
     error: 'Bad Request',
-    message: '"namespace" is not allowed',
+    message: '[request body.namespace]: definition for this key is missing',
     statusCode: 400,
-    validation: {
-      keys: ['namespace'],
-      source: 'payload',
-    },
   });
 };
 

From c06649917e14494163002fc860f155936e6a462a Mon Sep 17 00:00:00 2001
From: Daniil Suleiman <31325372+sulemanof@users.noreply.github.com>
Date: Tue, 18 Feb 2020 17:49:16 +0300
Subject: [PATCH 020/174] Get features inside the rendering phase (#57829)

---
 .../core_plugins/kibana/public/home/kibana_services.ts     | 4 ++--
 .../kibana/public/home/np_ready/application.tsx            | 6 +++++-
 src/legacy/core_plugins/kibana/public/home/plugin.ts       | 7 +++----
 3 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/src/legacy/core_plugins/kibana/public/home/kibana_services.ts b/src/legacy/core_plugins/kibana/public/home/kibana_services.ts
index 6cb1531be6b5b..4b09997fc6244 100644
--- a/src/legacy/core_plugins/kibana/public/home/kibana_services.ts
+++ b/src/legacy/core_plugins/kibana/public/home/kibana_services.ts
@@ -31,7 +31,7 @@ import { TelemetryPluginStart } from '../../../../../plugins/telemetry/public';
 import {
   Environment,
   HomePublicPluginSetup,
-  FeatureCatalogueEntry,
+  HomePublicPluginStart,
 } from '../../../../../plugins/home/public';
 import { KibanaLegacySetup } from '../../../../../plugins/kibana_legacy/public';
 
@@ -43,7 +43,7 @@ export interface HomeKibanaServices {
   uiSettings: IUiSettingsClient;
   config: KibanaLegacySetup['config'];
   homeConfig: HomePublicPluginSetup['config'];
-  directories: readonly FeatureCatalogueEntry[];
+  featureCatalogue: HomePublicPluginStart['featureCatalogue'];
   http: HttpStart;
   savedObjectsClient: SavedObjectsClientContract;
   toastNotifications: NotificationsSetup['toasts'];
diff --git a/src/legacy/core_plugins/kibana/public/home/np_ready/application.tsx b/src/legacy/core_plugins/kibana/public/home/np_ready/application.tsx
index 2149885f3ee11..578d1f0a766a5 100644
--- a/src/legacy/core_plugins/kibana/public/home/np_ready/application.tsx
+++ b/src/legacy/core_plugins/kibana/public/home/np_ready/application.tsx
@@ -26,7 +26,11 @@ import { getServices } from '../kibana_services';
 
 export const renderApp = async (element: HTMLElement) => {
   const homeTitle = i18n.translate('kbn.home.breadcrumbs.homeTitle', { defaultMessage: 'Home' });
-  const { directories, chrome } = getServices();
+  const { featureCatalogue, chrome } = getServices();
+
+  // all the directories could be get in "start" phase of plugin after all of the legacy plugins will be moved to a NP
+  const directories = featureCatalogue.get();
+
   chrome.setBreadcrumbs([{ text: homeTitle }]);
 
   render(<HomeApp directories={directories} />, element);
diff --git a/src/legacy/core_plugins/kibana/public/home/plugin.ts b/src/legacy/core_plugins/kibana/public/home/plugin.ts
index 75e7cc2e453be..1f4b5fe7cacef 100644
--- a/src/legacy/core_plugins/kibana/public/home/plugin.ts
+++ b/src/legacy/core_plugins/kibana/public/home/plugin.ts
@@ -34,7 +34,6 @@ import {
   Environment,
   HomePublicPluginStart,
   HomePublicPluginSetup,
-  FeatureCatalogueEntry,
 } from '../../../../../plugins/home/public';
 
 export interface HomePluginStartDependencies {
@@ -53,7 +52,7 @@ export class HomePlugin implements Plugin {
   private dataStart: DataPublicPluginStart | null = null;
   private savedObjectsClient: any = null;
   private environment: Environment | null = null;
-  private directories: readonly FeatureCatalogueEntry[] | null = null;
+  private featureCatalogue: HomePublicPluginStart['featureCatalogue'] | null = null;
   private telemetry?: TelemetryPluginStart;
 
   constructor(private initializerContext: PluginInitializerContext) {}
@@ -83,7 +82,7 @@ export class HomePlugin implements Plugin {
           environment: this.environment!,
           config: kibanaLegacy.config,
           homeConfig: home.config,
-          directories: this.directories!,
+          featureCatalogue: this.featureCatalogue!,
         });
         const { renderApp } = await import('./np_ready/application');
         return await renderApp(params.element);
@@ -93,7 +92,7 @@ export class HomePlugin implements Plugin {
 
   start(core: CoreStart, { data, home, telemetry }: HomePluginStartDependencies) {
     this.environment = home.environment.get();
-    this.directories = home.featureCatalogue.get();
+    this.featureCatalogue = home.featureCatalogue;
     this.dataStart = data;
     this.telemetry = telemetry;
     this.savedObjectsClient = core.savedObjects.client;

From 60f7081e0814d04f34ca8f27749c16737c8f6d8d Mon Sep 17 00:00:00 2001
From: Walter Rafelsberger <walter@elastic.co>
Date: Tue, 18 Feb 2020 16:03:31 +0100
Subject: [PATCH 021/174] [ML] Transform: Fix transform wizard query
 autocomplete. (#57833)

Fixes a regression introduced by #56358. We're reusing the KqlFilterBar from the ML plugin in the transform plugin and missed updating the dependency management. Note this PR is only about fixing that regression. Future work on NP migration should take care of cleaning up the dependency management in the transforms plugin in general.
---
 .../transform/public/app/app_dependencies.tsx | 24 ++++++++++++++++++-
 .../legacy/plugins/transform/public/plugin.ts |  7 +++---
 .../transform/public/shared_imports.ts        |  3 +++
 .../legacy/plugins/transform/public/shim.ts   |  1 +
 4 files changed, 31 insertions(+), 4 deletions(-)

diff --git a/x-pack/legacy/plugins/transform/public/app/app_dependencies.tsx b/x-pack/legacy/plugins/transform/public/app/app_dependencies.tsx
index 28a54e67bf838..282d1380b396b 100644
--- a/x-pack/legacy/plugins/transform/public/app/app_dependencies.tsx
+++ b/x-pack/legacy/plugins/transform/public/app/app_dependencies.tsx
@@ -7,13 +7,35 @@
 import React, { createContext, useContext, ReactNode } from 'react';
 import { HashRouter } from 'react-router-dom';
 
+import chrome from 'ui/chrome';
+import { metadata } from 'ui/metadata';
+
 import { API_BASE_PATH } from '../../common/constants';
-import { AuthorizationProvider } from './lib/authorization';
+
+import { setDependencyCache } from '../shared_imports';
 import { AppDependencies } from '../shim';
 
+import { AuthorizationProvider } from './lib/authorization';
+
+const legacyBasePath = {
+  prepend: chrome.addBasePath,
+  get: chrome.getBasePath,
+  remove: () => {},
+};
+const legacyDocLinks = {
+  ELASTIC_WEBSITE_URL: 'https://www.elastic.co/',
+  DOC_LINK_VERSION: metadata.branch,
+};
+
 let DependenciesContext: React.Context<AppDependencies>;
 
 const setAppDependencies = (deps: AppDependencies) => {
+  setDependencyCache({
+    autocomplete: deps.plugins.data.autocomplete,
+    docLinks: legacyDocLinks as any,
+    basePath: legacyBasePath as any,
+    XSRF: chrome.getXsrfToken(),
+  });
   DependenciesContext = createContext<AppDependencies>(deps);
   return DependenciesContext.Provider;
 };
diff --git a/x-pack/legacy/plugins/transform/public/plugin.ts b/x-pack/legacy/plugins/transform/public/plugin.ts
index 3b02d07b8c150..2e29ecfc19510 100644
--- a/x-pack/legacy/plugins/transform/public/plugin.ts
+++ b/x-pack/legacy/plugins/transform/public/plugin.ts
@@ -35,12 +35,13 @@ export class Plugin {
       savedObjects,
       overlays,
     } = core;
-    const { management, savedSearches: coreSavedSearches, uiMetric } = plugins;
+    const { data, management, savedSearches: coreSavedSearches, uiMetric } = plugins;
 
     // AppCore/AppPlugins to be passed on as React context
-    const AppDependencies = {
+    const appDependencies = {
       core: { chrome, http, i18n: core.i18n, uiSettings, savedObjects, overlays },
       plugins: {
+        data,
         management: { sections: management.sections },
         savedSearches: coreSavedSearches,
       },
@@ -113,7 +114,7 @@ export class Plugin {
           unmountReactApp();
           const elem = document.getElementById(REACT_ROOT_ID);
           if (elem) {
-            renderReact(elem, AppDependencies);
+            renderReact(elem, appDependencies);
           }
         });
       },
diff --git a/x-pack/legacy/plugins/transform/public/shared_imports.ts b/x-pack/legacy/plugins/transform/public/shared_imports.ts
index fe9fe8b8e695b..74e0c9a3878db 100644
--- a/x-pack/legacy/plugins/transform/public/shared_imports.ts
+++ b/x-pack/legacy/plugins/transform/public/shared_imports.ts
@@ -32,5 +32,8 @@ export {
   SORT_DIRECTION,
 } from '../../ml/public/application/components/ml_in_memory_table';
 
+// Needs to be imported because we're reusing KqlFilterBar which depends on it.
+export { setDependencyCache } from '../../ml/public/application/util/dependency_cache';
+
 // @ts-ignore: could not find declaration file for module
 export { KqlFilterBar } from '../../ml/public/application/components/kql_filter_bar';
diff --git a/x-pack/legacy/plugins/transform/public/shim.ts b/x-pack/legacy/plugins/transform/public/shim.ts
index 758cc90210579..38bb072ff9eb7 100644
--- a/x-pack/legacy/plugins/transform/public/shim.ts
+++ b/x-pack/legacy/plugins/transform/public/shim.ts
@@ -26,6 +26,7 @@ export type AppCore = Pick<
 >;
 
 export interface AppPlugins {
+  data: DataPublicPluginStart;
   management: {
     sections: typeof management;
   };

From 326a162b4241efd9f4a6a6795346e61525e37257 Mon Sep 17 00:00:00 2001
From: Shahzad <shahzad.muhammad@elastic.co>
Date: Tue, 18 Feb 2020 16:54:29 +0100
Subject: [PATCH 022/174] [Uptime] Fix/filter group autocomplete (#57686)

* added autocomplete

* snaps

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../__tests__/__snapshots__/filter_popover.test.tsx.snap      | 4 ++--
 .../components/functional/filter_group/filter_popover.tsx     | 1 +
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/__tests__/__snapshots__/filter_popover.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/__tests__/__snapshots__/filter_popover.test.tsx.snap
index af1ed12e5e811..06b516e9ae23b 100644
--- a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/__tests__/__snapshots__/filter_popover.test.tsx.snap
+++ b/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/__tests__/__snapshots__/filter_popover.test.tsx.snap
@@ -28,7 +28,7 @@ exports[`FilterPopover component does not show item list when loading 1`] = `
       compressed={false}
       disabled={false}
       fullWidth={false}
-      incremental={false}
+      incremental={true}
       isClearable={true}
       isLoading={false}
       onSearch={[Function]}
@@ -66,7 +66,7 @@ exports[`FilterPopover component renders without errors for valid props 1`] = `
       compressed={false}
       disabled={false}
       fullWidth={false}
-      incremental={false}
+      incremental={true}
       isClearable={true}
       isLoading={false}
       onSearch={[Function]}
diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/filter_popover.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/filter_popover.tsx
index f96fef609fe76..ac65063ee897d 100644
--- a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/filter_popover.tsx
+++ b/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/filter_popover.tsx
@@ -77,6 +77,7 @@ export const FilterPopover = ({
     >
       <EuiPopoverTitle>
         <EuiFieldSearch
+          incremental={true}
           disabled={items.length === 0}
           onSearch={query => setSearchQuery(query)}
           placeholder={

From c5ab9828b4daec0926f62bdf2b8227d19a591c8c Mon Sep 17 00:00:00 2001
From: Nathan Reese <reese.nathan@gmail.com>
Date: Tue, 18 Feb 2020 09:24:41 -0700
Subject: [PATCH 023/174] [Maps] only request field in docvalue_fields when the
 field supports doc values (#57372)

* [Maps] only request field in docvalue_fields when the field supports doc values

* do not include scripted fields in sourceOnlyFields

* review feedback

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../es_search_source/es_search_source.js      | 124 ++++++++----------
 .../maps/documents_source/docvalue_fields.js  |   2 -
 2 files changed, 52 insertions(+), 74 deletions(-)

diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js
index 93ef40162a584..288dd117da137 100644
--- a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js
+++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js
@@ -54,6 +54,36 @@ function addFieldToDSL(dsl, field) {
       };
 }
 
+function getDocValueAndSourceFields(indexPattern, fieldNames) {
+  const docValueFields = [];
+  const sourceOnlyFields = [];
+  const scriptFields = {};
+  fieldNames.forEach(fieldName => {
+    const field = getField(indexPattern, fieldName);
+    if (field.scripted) {
+      scriptFields[field.name] = {
+        script: {
+          source: field.script,
+          lang: field.lang,
+        },
+      };
+    } else if (field.readFromDocValues) {
+      const docValueField =
+        field.type === 'date'
+          ? {
+              field: fieldName,
+              format: 'epoch_millis',
+            }
+          : fieldName;
+      docValueFields.push(docValueField);
+    } else {
+      sourceOnlyFields.push(fieldName);
+    }
+  });
+
+  return { docValueFields, sourceOnlyFields, scriptFields };
+}
+
 export class ESSearchSource extends AbstractESSource {
   static type = ES_SEARCH;
   static title = i18n.translate('xpack.maps.source.esSearchTitle', {
@@ -244,63 +274,29 @@ export class ESSearchSource extends AbstractESSource {
     ];
   }
 
-  async _excludeDateFields(fieldNames) {
-    const dateFieldNames = (await this.getDateFields()).map(field => field.getName());
-    return fieldNames.filter(field => {
-      return !dateFieldNames.includes(field);
-    });
-  }
-
-  // Returns docvalue_fields array for the union of indexPattern's dateFields and request's field names.
-  async _getDateDocvalueFields(searchFields) {
-    const dateFieldNames = (await this.getDateFields()).map(field => field.getName());
-    return searchFields
-      .filter(fieldName => {
-        return dateFieldNames.includes(fieldName);
-      })
-      .map(fieldName => {
-        return {
-          field: fieldName,
-          format: 'epoch_millis',
-        };
-      });
-  }
-
   async _getTopHits(layerName, searchFilters, registerCancelCallback) {
     const { topHitsSplitField: topHitsSplitFieldName, topHitsSize } = this._descriptor;
 
     const indexPattern = await this.getIndexPattern();
-    const geoField = await this._getGeoField();
-
-    const scriptFields = {};
-    searchFilters.fieldNames.forEach(fieldName => {
-      const field = indexPattern.fields.getByName(fieldName);
-      if (field && field.scripted) {
-        scriptFields[field.name] = {
-          script: {
-            source: field.script,
-            lang: field.lang,
-          },
-        };
-      }
-    });
 
+    const { docValueFields, sourceOnlyFields, scriptFields } = getDocValueAndSourceFields(
+      indexPattern,
+      searchFilters.fieldNames
+    );
     const topHits = {
       size: topHitsSize,
       script_fields: scriptFields,
-      docvalue_fields: await this._getDateDocvalueFields(searchFilters.fieldNames),
+      docvalue_fields: docValueFields,
     };
-    const nonDateFieldNames = await this._excludeDateFields(searchFilters.fieldNames);
 
     if (this._hasSort()) {
       topHits.sort = this._buildEsSort();
     }
-    if (geoField.type === ES_GEO_FIELD_TYPE.GEO_POINT) {
+    if (sourceOnlyFields.length === 0) {
       topHits._source = false;
-      topHits.docvalue_fields.push(...nonDateFieldNames);
     } else {
       topHits._source = {
-        includes: nonDateFieldNames,
+        includes: sourceOnlyFields,
       };
     }
 
@@ -364,41 +360,25 @@ export class ESSearchSource extends AbstractESSource {
   // searchFilters.fieldNames contains geo field and any fields needed for styling features
   // Performs Elasticsearch search request being careful to pull back only required fields to minimize response size
   async _getSearchHits(layerName, searchFilters, maxResultWindow, registerCancelCallback) {
-    const initialSearchContext = {
-      docvalue_fields: await this._getDateDocvalueFields(searchFilters.fieldNames),
-    };
-    const geoField = await this._getGeoField();
+    const indexPattern = await this.getIndexPattern();
 
-    let searchSource;
-    if (geoField.type === ES_GEO_FIELD_TYPE.GEO_POINT) {
-      // Request geo_point and style fields in docvalue_fields insted of _source
-      // 1) Returns geo_point in a consistent format regardless of how geo_point is stored in source
-      // 2) Setting _source to false so we avoid pulling back unneeded fields.
-      initialSearchContext.docvalue_fields.push(
-        ...(await this._excludeDateFields(searchFilters.fieldNames))
-      );
-      searchSource = await this._makeSearchSource(
-        searchFilters,
-        maxResultWindow,
-        initialSearchContext
-      );
+    const { docValueFields, sourceOnlyFields } = getDocValueAndSourceFields(
+      indexPattern,
+      searchFilters.fieldNames
+    );
+
+    const initialSearchContext = { docvalue_fields: docValueFields }; // Request fields in docvalue_fields insted of _source
+    const searchSource = await this._makeSearchSource(
+      searchFilters,
+      maxResultWindow,
+      initialSearchContext
+    );
+    searchSource.setField('fields', searchFilters.fieldNames); // Setting "fields" filters out unused scripted fields
+    if (sourceOnlyFields.length === 0) {
       searchSource.setField('source', false); // do not need anything from _source
-      searchSource.setField('fields', searchFilters.fieldNames); // Setting "fields" filters out unused scripted fields
     } else {
-      // geo_shape fields do not support docvalue_fields yet, so still have to be pulled from _source
-      searchSource = await this._makeSearchSource(
-        searchFilters,
-        maxResultWindow,
-        initialSearchContext
-      );
-      // Setting "fields" instead of "source: { includes: []}"
-      // because SearchSource automatically adds the following by default
-      // 1) all scripted fields
-      // 2) docvalue_fields value is added for each date field in an index - see getComputedFields
-      // By setting "fields", SearchSource removes all of defaults
-      searchSource.setField('fields', searchFilters.fieldNames);
+      searchSource.setField('source', sourceOnlyFields);
     }
-
     if (this._hasSort()) {
       searchSource.setField('sort', this._buildEsSort());
     }
diff --git a/x-pack/test/functional/apps/maps/documents_source/docvalue_fields.js b/x-pack/test/functional/apps/maps/documents_source/docvalue_fields.js
index 48f225a467d8f..fdacd89722d3c 100644
--- a/x-pack/test/functional/apps/maps/documents_source/docvalue_fields.js
+++ b/x-pack/test/functional/apps/maps/documents_source/docvalue_fields.js
@@ -10,7 +10,6 @@ export default function({ getPageObjects, getService }) {
   const PageObjects = getPageObjects(['maps']);
   const inspector = getService('inspector');
   const testSubjects = getService('testSubjects');
-  const log = getService('log');
 
   describe('docvalue_fields', () => {
     before(async () => {
@@ -22,7 +21,6 @@ export default function({ getPageObjects, getService }) {
       await inspector.openInspectorRequestsView();
       await testSubjects.click('inspectorRequestDetailResponse');
       const responseBody = await testSubjects.getVisibleText('inspectorResponseBody');
-      log.info(responseBody);
       await inspector.close();
       return JSON.parse(responseBody);
     }

From 6685bd000c0652c9939dd558684018a7a0d051e8 Mon Sep 17 00:00:00 2001
From: Melissa Alvarez <melissa.alvarez@elastic.co>
Date: Tue, 18 Feb 2020 11:25:33 -0500
Subject: [PATCH 024/174] [ML] New Platform server shim: update job validation
 routes  (#57644)

* wip: convert jobValidation routes to NP

* fix typo in np migration docs

* replace legacy es plugin with callAsInternalUser from context

* add schema info for job validation routes

* update types and schema for job + cardinality validation

* update bucketSpanEstimator test

* update job validation schema
---
 src/core/MIGRATION.md                         |   4 +-
 .../__tests__/bucket_span_estimator.js        |  38 +---
 .../bucket_span_estimator.d.ts                |  14 ++
 .../bucket_span_estimator.js                  |  16 +-
 .../{index.js => index.ts}                    |   0
 .../polled_data_checker.js                    |   4 +-
 .../single_series_checker.js                  |   4 +-
 .../calculate_model_memory_limit.d.ts         |  20 ++
 .../calculate_model_memory_limit.js           |   6 +-
 .../{index.js => index.ts}                    |   0
 .../models/data_visualizer/data_visualizer.ts |   7 +-
 .../job_validation/{index.js => index.ts}     |   0
 .../models/job_validation/job_validation.d.ts |  19 ++
 .../models/job_validation/job_validation.js   |   4 +-
 .../job_validation/validate_bucket_span.js    |   4 +-
 .../job_validation/validate_cardinality.d.ts  |  17 ++
 .../new_platform/anomaly_detectors_schema.ts  |  24 ++-
 .../new_platform/job_validation_schema.ts     |  59 ++++++
 .../plugins/ml/server/new_platform/plugin.ts  |   7 -
 .../ml/server/routes/data_visualizer.ts       |   4 +-
 .../ml/server/routes/job_validation.js        | 126 ------------
 .../ml/server/routes/job_validation.ts        | 194 ++++++++++++++++++
 22 files changed, 375 insertions(+), 196 deletions(-)
 create mode 100644 x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/bucket_span_estimator.d.ts
 rename x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/{index.js => index.ts} (100%)
 create mode 100644 x-pack/legacy/plugins/ml/server/models/calculate_model_memory_limit/calculate_model_memory_limit.d.ts
 rename x-pack/legacy/plugins/ml/server/models/calculate_model_memory_limit/{index.js => index.ts} (100%)
 rename x-pack/legacy/plugins/ml/server/models/job_validation/{index.js => index.ts} (100%)
 create mode 100644 x-pack/legacy/plugins/ml/server/models/job_validation/job_validation.d.ts
 create mode 100644 x-pack/legacy/plugins/ml/server/models/job_validation/validate_cardinality.d.ts
 create mode 100644 x-pack/legacy/plugins/ml/server/new_platform/job_validation_schema.ts
 delete mode 100644 x-pack/legacy/plugins/ml/server/routes/job_validation.js
 create mode 100644 x-pack/legacy/plugins/ml/server/routes/job_validation.ts

diff --git a/src/core/MIGRATION.md b/src/core/MIGRATION.md
index fa0edd8faadd7..9e57fc4c36876 100644
--- a/src/core/MIGRATION.md
+++ b/src/core/MIGRATION.md
@@ -1197,8 +1197,8 @@ In server code, `core` can be accessed from either `server.newPlatform` or `kbnS
 | `server.route`                                                                | [`core.http.createRouter`](/docs/development/core/server/kibana-plugin-server.httpservicesetup.createrouter.md)                                                                                                                                                                                             | [Examples](./MIGRATION_EXAMPLES.md#route-registration)                      |
 | `server.renderApp()` / `server.renderAppWithDefaultConfig()`                  | [`context.rendering.render()`](/docs/development/core/server/kibana-plugin-server.iscopedrenderingclient.render.md)                                                                                                                                                                                         | [Examples](./MIGRATION_EXAMPLES.md#render-html-content)                     |
 | `request.getBasePath()`                                                       | [`core.http.basePath.get`](/docs/development/core/server/kibana-plugin-server.httpservicesetup.basepath.md)                                                                                                                                                                                                 |                                                                             |
-| `server.plugins.elasticsearch.getCluster('data')`                             | [`context.elasticsearch.dataClient`](/docs/development/core/server/kibana-plugin-server.iscopedclusterclient.md)                                                                                                                                                                                            |                                                                             |
-| `server.plugins.elasticsearch.getCluster('admin')`                            | [`context.elasticsearch.adminClient`](/docs/development/core/server/kibana-plugin-server.iscopedclusterclient.md)                                                                                                                                                                                           |                                                                             |
+| `server.plugins.elasticsearch.getCluster('data')`                             | [`context.core.elasticsearch.dataClient`](/docs/development/core/server/kibana-plugin-server.iscopedclusterclient.md)                                                                                                                                                                                            |                                                                             |
+| `server.plugins.elasticsearch.getCluster('admin')`                            | [`context.core.elasticsearch.adminClient`](/docs/development/core/server/kibana-plugin-server.iscopedclusterclient.md)                                                                                                                                                                                           |                                                                             |
 | `server.plugins.elasticsearch.createCluster(...)`                             | [`core.elasticsearch.createClient`](/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.createclient.md)                                                                                                                                                                           |                                                                             |
 | `server.savedObjects.setScopedSavedObjectsClientFactory`                      | [`core.savedObjects.setClientFactory`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.setclientfactory.md)                                                                                                                                                                     |                                                                             |
 | `server.savedObjects.addScopedSavedObjectsClientWrapperFactory`               | [`core.savedObjects.addClientWrapper`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.addclientwrapper.md)                                                                                                                                                                     |                                                                             |
diff --git a/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/__tests__/bucket_span_estimator.js b/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/__tests__/bucket_span_estimator.js
index c003d3117132f..a0dacc38e5835 100644
--- a/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/__tests__/bucket_span_estimator.js
+++ b/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/__tests__/bucket_span_estimator.js
@@ -4,7 +4,6 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import sinon from 'sinon';
 import expect from '@kbn/expect';
 import { estimateBucketSpanFactory } from '../bucket_span_estimator';
 
@@ -32,9 +31,11 @@ const callWithRequest = method => {
   });
 };
 
-// mock callWithInternalUserFactory
-// we replace the return value of the factory with the above mocked callWithRequest
-import * as mockModule from '../../../client/call_with_internal_user_factory';
+const callWithInternalUser = () => {
+  return new Promise(resolve => {
+    resolve({});
+  });
+};
 
 // mock xpack_main plugin
 function mockXpackMainPluginFactory(isEnabled = false, licenseType = 'platinum') {
@@ -51,7 +52,6 @@ function mockXpackMainPluginFactory(isEnabled = false, licenseType = 'platinum')
   };
 }
 
-const mockElasticsearchPlugin = {};
 // mock configuration to be passed to the estimator
 const formConfig = {
   aggTypes: ['count'],
@@ -67,20 +67,9 @@ const formConfig = {
 };
 
 describe('ML - BucketSpanEstimator', () => {
-  let mockCallWithInternalUserFactory;
-
-  beforeEach(() => {
-    mockCallWithInternalUserFactory = sinon.mock(mockModule);
-    mockCallWithInternalUserFactory
-      .expects('callWithInternalUserFactory')
-      .once()
-      .returns(callWithRequest);
-  });
-
   it('call factory', () => {
     expect(function() {
-      estimateBucketSpanFactory(callWithRequest);
-      mockCallWithInternalUserFactory.verify();
+      estimateBucketSpanFactory(callWithRequest, callWithInternalUser);
     }).to.not.throwError('Not initialized.');
   });
 
@@ -88,13 +77,13 @@ describe('ML - BucketSpanEstimator', () => {
     expect(function() {
       const estimateBucketSpan = estimateBucketSpanFactory(
         callWithRequest,
-        mockElasticsearchPlugin,
+        callWithInternalUser,
         mockXpackMainPluginFactory()
       );
 
       estimateBucketSpan(formConfig).catch(catchData => {
         expect(catchData).to.be('Unable to retrieve cluster setting search.max_buckets');
-        mockCallWithInternalUserFactory.verify();
+
         done();
       });
     }).to.not.throwError('Not initialized.');
@@ -104,12 +93,12 @@ describe('ML - BucketSpanEstimator', () => {
     expect(function() {
       const estimateBucketSpan = estimateBucketSpanFactory(
         callWithRequest,
-        mockElasticsearchPlugin,
+        callWithInternalUser,
         mockXpackMainPluginFactory(true)
       );
       estimateBucketSpan(formConfig).catch(catchData => {
         expect(catchData).to.be('Unable to retrieve cluster setting search.max_buckets');
-        mockCallWithInternalUserFactory.verify();
+
         done();
       });
     }).to.not.throwError('Not initialized.');
@@ -119,19 +108,14 @@ describe('ML - BucketSpanEstimator', () => {
     expect(function() {
       const estimateBucketSpan = estimateBucketSpanFactory(
         callWithRequest,
-        mockElasticsearchPlugin,
+        callWithInternalUser,
         mockXpackMainPluginFactory(true)
       );
 
       estimateBucketSpan(formConfig).catch(catchData => {
         expect(catchData).to.be('Insufficient permissions to call bucket span estimation.');
-        mockCallWithInternalUserFactory.verify();
         done();
       });
     }).to.not.throwError('Not initialized.');
   });
-
-  afterEach(() => {
-    mockCallWithInternalUserFactory.restore();
-  });
 });
diff --git a/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/bucket_span_estimator.d.ts b/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/bucket_span_estimator.d.ts
new file mode 100644
index 0000000000000..ea986feab4e99
--- /dev/null
+++ b/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/bucket_span_estimator.d.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 { APICaller } from 'src/core/server';
+import { BucketSpanEstimatorData } from '../../../public/application/services/ml_api_service';
+
+export function estimateBucketSpanFactory(
+  callAsCurrentUser: APICaller,
+  callAsInternalUser: APICaller,
+  xpackMainPlugin: any
+): (config: BucketSpanEstimatorData) => Promise<any>;
diff --git a/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/bucket_span_estimator.js b/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/bucket_span_estimator.js
index c0edcb5eeb537..aec677dd57d61 100644
--- a/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/bucket_span_estimator.js
+++ b/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/bucket_span_estimator.js
@@ -12,13 +12,11 @@ import { INTERVALS } from './intervals';
 import { singleSeriesCheckerFactory } from './single_series_checker';
 import { polledDataCheckerFactory } from './polled_data_checker';
 
-import { callWithInternalUserFactory } from '../../client/call_with_internal_user_factory';
 import { isSecurityDisabled } from '../../lib/security_utils';
 
-export function estimateBucketSpanFactory(callWithRequest, elasticsearchPlugin, xpackMainPlugin) {
-  const callWithInternalUser = callWithInternalUserFactory(elasticsearchPlugin);
-  const PolledDataChecker = polledDataCheckerFactory(callWithRequest);
-  const SingleSeriesChecker = singleSeriesCheckerFactory(callWithRequest);
+export function estimateBucketSpanFactory(callAsCurrentUser, callAsInternalUser, xpackMainPlugin) {
+  const PolledDataChecker = polledDataCheckerFactory(callAsCurrentUser);
+  const SingleSeriesChecker = singleSeriesCheckerFactory(callAsCurrentUser);
 
   class BucketSpanEstimator {
     constructor(
@@ -245,7 +243,7 @@ export function estimateBucketSpanFactory(callWithRequest, elasticsearchPlugin,
 
   const getFieldCardinality = function(index, field) {
     return new Promise((resolve, reject) => {
-      callWithRequest('search', {
+      callAsCurrentUser('search', {
         index,
         size: 0,
         body: {
@@ -277,7 +275,7 @@ export function estimateBucketSpanFactory(callWithRequest, elasticsearchPlugin,
       getFieldCardinality(index, field)
         .then(value => {
           const numPartitions = Math.floor(value / NUM_PARTITIONS) || 1;
-          callWithRequest('search', {
+          callAsCurrentUser('search', {
             index,
             size: 0,
             body: {
@@ -337,7 +335,7 @@ export function estimateBucketSpanFactory(callWithRequest, elasticsearchPlugin,
       function getBucketSpanEstimation() {
         // fetch the `search.max_buckets` cluster setting so we're able to
         // adjust aggregations to not exceed that limit.
-        callWithInternalUser('cluster.getSettings', {
+        callAsInternalUser('cluster.getSettings', {
           flatSettings: true,
           includeDefaults: true,
           filterPath: '*.*max_buckets',
@@ -402,7 +400,7 @@ export function estimateBucketSpanFactory(callWithRequest, elasticsearchPlugin,
             'cluster:monitor/xpack/ml/datafeeds/stats/get',
           ],
         };
-        callWithRequest('ml.privilegeCheck', { body })
+        callAsCurrentUser('ml.privilegeCheck', { body })
           .then(resp => {
             if (
               resp.cluster['cluster:monitor/xpack/ml/job/get'] &&
diff --git a/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/index.js b/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/index.js
rename to x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/index.ts
diff --git a/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/polled_data_checker.js b/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/polled_data_checker.js
index 8b462989866e4..674875f8d5d16 100644
--- a/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/polled_data_checker.js
+++ b/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/polled_data_checker.js
@@ -12,7 +12,7 @@
 
 import _ from 'lodash';
 
-export function polledDataCheckerFactory(callWithRequest) {
+export function polledDataCheckerFactory(callAsCurrentUser) {
   class PolledDataChecker {
     constructor(index, timeField, duration, query) {
       this.index = index;
@@ -68,7 +68,7 @@ export function polledDataCheckerFactory(callWithRequest) {
     performSearch(intervalMs) {
       const body = this.createSearch(intervalMs);
 
-      return callWithRequest('search', {
+      return callAsCurrentUser('search', {
         index: this.index,
         size: 0,
         body,
diff --git a/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/single_series_checker.js b/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/single_series_checker.js
index 0994d432802e3..71e692d089b49 100644
--- a/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/single_series_checker.js
+++ b/x-pack/legacy/plugins/ml/server/models/bucket_span_estimator/single_series_checker.js
@@ -13,7 +13,7 @@
 import { mlLog } from '../../client/log';
 import { INTERVALS, LONG_INTERVALS } from './intervals';
 
-export function singleSeriesCheckerFactory(callWithRequest) {
+export function singleSeriesCheckerFactory(callAsCurrentUser) {
   const REF_DATA_INTERVAL = { name: '1h', ms: 3600000 };
 
   class SingleSeriesChecker {
@@ -187,7 +187,7 @@ export function singleSeriesCheckerFactory(callWithRequest) {
     performSearch(intervalMs) {
       const body = this.createSearch(intervalMs);
 
-      return callWithRequest('search', {
+      return callAsCurrentUser('search', {
         index: this.index,
         size: 0,
         body,
diff --git a/x-pack/legacy/plugins/ml/server/models/calculate_model_memory_limit/calculate_model_memory_limit.d.ts b/x-pack/legacy/plugins/ml/server/models/calculate_model_memory_limit/calculate_model_memory_limit.d.ts
new file mode 100644
index 0000000000000..87c9a8c978274
--- /dev/null
+++ b/x-pack/legacy/plugins/ml/server/models/calculate_model_memory_limit/calculate_model_memory_limit.d.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 { APICaller } from 'src/core/server';
+
+export function calculateModelMemoryLimitProvider(
+  callAsCurrentUser: APICaller
+): (
+  indexPattern: string,
+  splitFieldName: string,
+  query: any,
+  fieldNames: any,
+  influencerNames: any, // string[] ?
+  timeFieldName: string,
+  earliestMs: number,
+  latestMs: number
+) => Promise<any>;
diff --git a/x-pack/legacy/plugins/ml/server/models/calculate_model_memory_limit/calculate_model_memory_limit.js b/x-pack/legacy/plugins/ml/server/models/calculate_model_memory_limit/calculate_model_memory_limit.js
index ace6c63257c3a..8a06895762dc2 100644
--- a/x-pack/legacy/plugins/ml/server/models/calculate_model_memory_limit/calculate_model_memory_limit.js
+++ b/x-pack/legacy/plugins/ml/server/models/calculate_model_memory_limit/calculate_model_memory_limit.js
@@ -10,8 +10,8 @@
 import numeral from '@elastic/numeral';
 import { fieldsServiceProvider } from '../fields_service';
 
-export function calculateModelMemoryLimitProvider(callWithRequest) {
-  const fieldsService = fieldsServiceProvider(callWithRequest);
+export function calculateModelMemoryLimitProvider(callAsCurrentUser) {
+  const fieldsService = fieldsServiceProvider(callAsCurrentUser);
 
   return function calculateModelMemoryLimit(
     indexPattern,
@@ -26,7 +26,7 @@ export function calculateModelMemoryLimitProvider(callWithRequest) {
   ) {
     return new Promise((response, reject) => {
       const limits = {};
-      callWithRequest('ml.info')
+      callAsCurrentUser('ml.info')
         .then(resp => {
           if (resp.limits !== undefined && resp.limits.max_model_memory_limit !== undefined) {
             limits.max_model_memory_limit = resp.limits.max_model_memory_limit;
diff --git a/x-pack/legacy/plugins/ml/server/models/calculate_model_memory_limit/index.js b/x-pack/legacy/plugins/ml/server/models/calculate_model_memory_limit/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/ml/server/models/calculate_model_memory_limit/index.js
rename to x-pack/legacy/plugins/ml/server/models/calculate_model_memory_limit/index.ts
diff --git a/x-pack/legacy/plugins/ml/server/models/data_visualizer/data_visualizer.ts b/x-pack/legacy/plugins/ml/server/models/data_visualizer/data_visualizer.ts
index 6186a61c5075f..b0a61b1232dc0 100644
--- a/x-pack/legacy/plugins/ml/server/models/data_visualizer/data_visualizer.ts
+++ b/x-pack/legacy/plugins/ml/server/models/data_visualizer/data_visualizer.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { CallAPIOptions, RequestHandlerContext } from 'kibana/server';
+import { CallAPIOptions, IScopedClusterClient } from 'src/core/server';
 import _ from 'lodash';
 import { ML_JOB_FIELD_TYPES } from '../../../common/constants/field_types';
 import { getSafeAggregationName } from '../../../common/util/job_utils';
@@ -113,9 +113,8 @@ export class DataVisualizer {
     options?: CallAPIOptions
   ) => Promise<any>;
 
-  constructor(client: RequestHandlerContext | (() => any)) {
-    this.callAsCurrentUser =
-      typeof client === 'object' ? client.ml!.mlClient.callAsCurrentUser : client;
+  constructor(callAsCurrentUser: IScopedClusterClient['callAsCurrentUser']) {
+    this.callAsCurrentUser = callAsCurrentUser;
   }
 
   // Obtains overall stats on the fields in the supplied index pattern, returning an object
diff --git a/x-pack/legacy/plugins/ml/server/models/job_validation/index.js b/x-pack/legacy/plugins/ml/server/models/job_validation/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/ml/server/models/job_validation/index.js
rename to x-pack/legacy/plugins/ml/server/models/job_validation/index.ts
diff --git a/x-pack/legacy/plugins/ml/server/models/job_validation/job_validation.d.ts b/x-pack/legacy/plugins/ml/server/models/job_validation/job_validation.d.ts
new file mode 100644
index 0000000000000..4580602b0af23
--- /dev/null
+++ b/x-pack/legacy/plugins/ml/server/models/job_validation/job_validation.d.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 { APICaller } from 'src/core/server';
+import { TypeOf } from '@kbn/config-schema';
+import { validateJobSchema } from '../../new_platform/job_validation_schema';
+
+type ValidateJobPayload = TypeOf<typeof validateJobSchema>;
+
+export function validateJob(
+  callAsCurrentUser: APICaller,
+  payload: ValidateJobPayload,
+  kbnVersion: string,
+  callAsInternalUser: APICaller,
+  xpackMainPlugin: any
+): string[];
diff --git a/x-pack/legacy/plugins/ml/server/models/job_validation/job_validation.js b/x-pack/legacy/plugins/ml/server/models/job_validation/job_validation.js
index 07194b6aa226d..ab1fbb39ee706 100644
--- a/x-pack/legacy/plugins/ml/server/models/job_validation/job_validation.js
+++ b/x-pack/legacy/plugins/ml/server/models/job_validation/job_validation.js
@@ -23,7 +23,7 @@ export async function validateJob(
   callWithRequest,
   payload,
   kbnVersion = 'current',
-  elasticsearchPlugin,
+  callAsInternalUser,
   xpackMainPlugin
 ) {
   const messages = getMessages();
@@ -111,7 +111,7 @@ export async function validateJob(
           callWithRequest,
           job,
           duration,
-          elasticsearchPlugin,
+          callAsInternalUser,
           xpackMainPlugin
         ))
       );
diff --git a/x-pack/legacy/plugins/ml/server/models/job_validation/validate_bucket_span.js b/x-pack/legacy/plugins/ml/server/models/job_validation/validate_bucket_span.js
index ea069bffaef2a..2914f086c1a83 100644
--- a/x-pack/legacy/plugins/ml/server/models/job_validation/validate_bucket_span.js
+++ b/x-pack/legacy/plugins/ml/server/models/job_validation/validate_bucket_span.js
@@ -50,7 +50,7 @@ export async function validateBucketSpan(
   callWithRequest,
   job,
   duration,
-  elasticsearchPlugin,
+  callAsInternalUser,
   xpackMainPlugin
 ) {
   validateJobObject(job);
@@ -123,7 +123,7 @@ export async function validateBucketSpan(
       return new Promise(resolve => {
         estimateBucketSpanFactory(
           callWithRequest,
-          elasticsearchPlugin,
+          callAsInternalUser,
           xpackMainPlugin
         )(data)
           .then(resolve)
diff --git a/x-pack/legacy/plugins/ml/server/models/job_validation/validate_cardinality.d.ts b/x-pack/legacy/plugins/ml/server/models/job_validation/validate_cardinality.d.ts
new file mode 100644
index 0000000000000..dc10905533788
--- /dev/null
+++ b/x-pack/legacy/plugins/ml/server/models/job_validation/validate_cardinality.d.ts
@@ -0,0 +1,17 @@
+/*
+ * 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 { APICaller } from 'src/core/server';
+import { Job, Datafeed } from '../../../public/application/jobs/new_job/common/job_creator/configs';
+
+interface ValidateCardinalityConfig extends Job {
+  datafeed_config?: Datafeed;
+}
+
+export function validateCardinality(
+  callAsCurrentUser: APICaller,
+  job: ValidateCardinalityConfig
+): any[];
diff --git a/x-pack/legacy/plugins/ml/server/new_platform/anomaly_detectors_schema.ts b/x-pack/legacy/plugins/ml/server/new_platform/anomaly_detectors_schema.ts
index d728fbf312d76..a46ccd8664a62 100644
--- a/x-pack/legacy/plugins/ml/server/new_platform/anomaly_detectors_schema.ts
+++ b/x-pack/legacy/plugins/ml/server/new_platform/anomaly_detectors_schema.ts
@@ -54,10 +54,12 @@ export const anomalyDetectionUpdateJobSchema = {
     )
   ),
   custom_settings: schema.maybe(customSettingsSchema),
-  analysis_limits: schema.object({
-    categorization_examples_limit: schema.maybe(schema.number()),
-    model_memory_limit: schema.maybe(schema.string()),
-  }),
+  analysis_limits: schema.maybe(
+    schema.object({
+      categorization_examples_limit: schema.maybe(schema.number()),
+      model_memory_limit: schema.maybe(schema.string()),
+    })
+  ),
   groups: schema.maybe(schema.arrayOf(schema.maybe(schema.string()))),
 };
 
@@ -69,15 +71,19 @@ export const anomalyDetectionJobSchema = {
     influencers: schema.arrayOf(schema.maybe(schema.string())),
     categorization_field_name: schema.maybe(schema.string()),
   }),
-  analysis_limits: schema.object({
-    categorization_examples_limit: schema.maybe(schema.number()),
-    model_memory_limit: schema.maybe(schema.string()),
-  }),
+  analysis_limits: schema.maybe(
+    schema.object({
+      categorization_examples_limit: schema.maybe(schema.number()),
+      model_memory_limit: schema.maybe(schema.string()),
+    })
+  ),
+  background_persist_interval: schema.maybe(schema.string()),
   create_time: schema.maybe(schema.number()),
   custom_settings: schema.maybe(customSettingsSchema),
   allow_lazy_open: schema.maybe(schema.any()),
   data_counts: schema.maybe(schema.any()),
   data_description: schema.object({
+    format: schema.maybe(schema.string()),
     time_field: schema.string(),
     time_format: schema.maybe(schema.string()),
   }),
@@ -94,6 +100,8 @@ export const anomalyDetectionJobSchema = {
   model_snapshot_id: schema.maybe(schema.string()),
   model_snapshot_min_version: schema.maybe(schema.string()),
   model_snapshot_retention_days: schema.maybe(schema.number()),
+  renormalization_window_days: schema.maybe(schema.number()),
   results_index_name: schema.maybe(schema.string()),
+  results_retention_days: schema.maybe(schema.number()),
   state: schema.maybe(schema.string()),
 };
diff --git a/x-pack/legacy/plugins/ml/server/new_platform/job_validation_schema.ts b/x-pack/legacy/plugins/ml/server/new_platform/job_validation_schema.ts
new file mode 100644
index 0000000000000..1cc6e8a97ffc0
--- /dev/null
+++ b/x-pack/legacy/plugins/ml/server/new_platform/job_validation_schema.ts
@@ -0,0 +1,59 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { schema } from '@kbn/config-schema';
+import { anomalyDetectionJobSchema } from './anomaly_detectors_schema';
+
+export const estimateBucketSpanSchema = schema.object({
+  aggTypes: schema.arrayOf(schema.nullable(schema.string())),
+  duration: schema.object({ start: schema.number(), end: schema.number() }),
+  fields: schema.arrayOf(schema.nullable(schema.string())),
+  index: schema.string(),
+  query: schema.any(),
+  splitField: schema.maybe(schema.string()),
+  timeField: schema.maybe(schema.string()),
+});
+
+export const modelMemoryLimitSchema = schema.object({
+  indexPattern: schema.string(),
+  splitFieldName: schema.string(),
+  query: schema.any(),
+  fieldNames: schema.arrayOf(schema.string()),
+  influencerNames: schema.arrayOf(schema.maybe(schema.string())),
+  timeFieldName: schema.string(),
+  earliestMs: schema.number(),
+  latestMs: schema.number(),
+});
+
+export const validateJobSchema = schema.object({
+  duration: schema.object({
+    start: schema.maybe(schema.number()),
+    end: schema.maybe(schema.number()),
+  }),
+  fields: schema.maybe(schema.any()),
+  job: schema.object(anomalyDetectionJobSchema),
+});
+
+const datafeedConfigSchema = schema.object({
+  datafeed_id: schema.string(),
+  aggregations: schema.maybe(schema.any()),
+  aggs: schema.maybe(schema.any()),
+  chunking_config: schema.maybe(schema.any()),
+  frequency: schema.maybe(schema.string()),
+  indices: schema.arrayOf(schema.string()),
+  indexes: schema.maybe(schema.arrayOf(schema.string())),
+  job_id: schema.string(),
+  query: schema.any(),
+  query_delay: schema.maybe(schema.string()),
+  script_fields: schema.maybe(schema.any()),
+  scroll_size: schema.maybe(schema.number()),
+  delayed_data_check_config: schema.maybe(schema.any()),
+});
+
+export const validateCardinalitySchema = {
+  ...anomalyDetectionJobSchema,
+  datafeed_config: datafeedConfigSchema,
+};
diff --git a/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts b/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts
index 068bfc40f53e1..69b08bfeda13d 100644
--- a/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts
+++ b/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts
@@ -25,15 +25,12 @@ import { checkLicense } from '../lib/check_license';
 // @ts-ignore: could not find declaration file for module
 import { mirrorPluginStatus } from '../../../../server/lib/mirror_plugin_status';
 import { LICENSE_TYPE } from '../../common/constants/license';
-// @ts-ignore: could not find declaration file for module
 import { annotationRoutes } from '../routes/annotations';
-// @ts-ignore: could not find declaration file for module
 import { jobRoutes } from '../routes/anomaly_detectors';
 // @ts-ignore: could not find declaration file for module
 import { dataFeedRoutes } from '../routes/datafeeds';
 // @ts-ignore: could not find declaration file for module
 import { indicesRoutes } from '../routes/indices';
-// @ts-ignore: could not find declaration file for module
 import { jobValidationRoutes } from '../routes/job_validation';
 import { makeMlUsageCollector } from '../lib/ml_telemetry';
 // @ts-ignore: could not find declaration file for module
@@ -41,17 +38,13 @@ import { notificationRoutes } from '../routes/notification_settings';
 // @ts-ignore: could not find declaration file for module
 import { systemRoutes } from '../routes/system';
 import { dataFrameAnalyticsRoutes } from '../routes/data_frame_analytics';
-// @ts-ignore: could not find declaration file for module
 import { dataRecognizer } from '../routes/modules';
-// @ts-ignore: could not find declaration file for module
 import { dataVisualizerRoutes } from '../routes/data_visualizer';
 import { calendars } from '../routes/calendars';
 // @ts-ignore: could not find declaration file for module
 import { fieldsService } from '../routes/fields_service';
 import { filtersRoutes } from '../routes/filters';
-// @ts-ignore: could not find declaration file for module
 import { resultsServiceRoutes } from '../routes/results_service';
-// @ts-ignore: could not find declaration file for module
 import { jobServiceRoutes } from '../routes/job_service';
 // @ts-ignore: could not find declaration file for module
 import { jobAuditMessagesRoutes } from '../routes/job_audit_messages';
diff --git a/x-pack/legacy/plugins/ml/server/routes/data_visualizer.ts b/x-pack/legacy/plugins/ml/server/routes/data_visualizer.ts
index 235fc26d78441..df7e4b7010877 100644
--- a/x-pack/legacy/plugins/ml/server/routes/data_visualizer.ts
+++ b/x-pack/legacy/plugins/ml/server/routes/data_visualizer.ts
@@ -26,7 +26,7 @@ function getOverallStats(
   earliestMs: number,
   latestMs: number
 ) {
-  const dv = new DataVisualizer(context);
+  const dv = new DataVisualizer(context.ml!.mlClient.callAsCurrentUser);
   return dv.getOverallStats(
     indexPatternTitle,
     query,
@@ -51,7 +51,7 @@ function getStatsForFields(
   interval: number,
   maxExamples: number
 ) {
-  const dv = new DataVisualizer(context);
+  const dv = new DataVisualizer(context.ml!.mlClient.callAsCurrentUser);
   return dv.getStatsForFields(
     indexPatternTitle,
     query,
diff --git a/x-pack/legacy/plugins/ml/server/routes/job_validation.js b/x-pack/legacy/plugins/ml/server/routes/job_validation.js
deleted file mode 100644
index 7ea3d6ebf1557..0000000000000
--- a/x-pack/legacy/plugins/ml/server/routes/job_validation.js
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import Boom from 'boom';
-
-import { callWithRequestFactory } from '../client/call_with_request_factory';
-import { wrapError } from '../client/errors';
-import { estimateBucketSpanFactory } from '../models/bucket_span_estimator';
-import { calculateModelMemoryLimitProvider } from '../models/calculate_model_memory_limit';
-import { validateJob, validateCardinality } from '../models/job_validation';
-
-export function jobValidationRoutes({
-  commonRouteConfig,
-  config,
-  elasticsearchPlugin,
-  route,
-  xpackMainPlugin,
-}) {
-  function calculateModelMemoryLimit(callWithRequest, payload) {
-    const {
-      indexPattern,
-      splitFieldName,
-      query,
-      fieldNames,
-      influencerNames,
-      timeFieldName,
-      earliestMs,
-      latestMs,
-    } = payload;
-
-    return calculateModelMemoryLimitProvider(callWithRequest)(
-      indexPattern,
-      splitFieldName,
-      query,
-      fieldNames,
-      influencerNames,
-      timeFieldName,
-      earliestMs,
-      latestMs
-    );
-  }
-
-  route({
-    method: 'POST',
-    path: '/api/ml/validate/estimate_bucket_span',
-    handler(request) {
-      const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
-      try {
-        return (
-          estimateBucketSpanFactory(
-            callWithRequest,
-            elasticsearchPlugin,
-            xpackMainPlugin
-          )(request.payload)
-            // this catch gets triggered when the estimation code runs without error
-            // but isn't able to come up with a bucket span estimation.
-            // this doesn't return a HTTP error but an object with an error message
-            // which the client is then handling. triggering a HTTP error would be
-            // too severe for this case.
-            .catch(resp => ({
-              error: true,
-              message: resp,
-            }))
-        );
-        // this catch gets triggered when an actual error gets thrown when running
-        // the estimation code, for example when the request payload is malformed
-      } catch (error) {
-        throw Boom.badRequest(error);
-      }
-    },
-    config: {
-      ...commonRouteConfig,
-    },
-  });
-
-  route({
-    method: 'POST',
-    path: '/api/ml/validate/calculate_model_memory_limit',
-    handler(request) {
-      const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
-      return calculateModelMemoryLimit(callWithRequest, request.payload).catch(resp =>
-        wrapError(resp)
-      );
-    },
-    config: {
-      ...commonRouteConfig,
-    },
-  });
-
-  route({
-    method: 'POST',
-    path: '/api/ml/validate/cardinality',
-    handler(request, reply) {
-      const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
-      return validateCardinality(callWithRequest, request.payload)
-        .then(reply)
-        .catch(resp => wrapError(resp));
-    },
-    config: {
-      ...commonRouteConfig,
-    },
-  });
-
-  route({
-    method: 'POST',
-    path: '/api/ml/validate/job',
-    handler(request) {
-      const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
-      // pkg.branch corresponds to the version used in documentation links.
-      const version = config.get('pkg.branch');
-      return validateJob(
-        callWithRequest,
-        request.payload,
-        version,
-        elasticsearchPlugin,
-        xpackMainPlugin
-      ).catch(resp => wrapError(resp));
-    },
-    config: {
-      ...commonRouteConfig,
-    },
-  });
-}
diff --git a/x-pack/legacy/plugins/ml/server/routes/job_validation.ts b/x-pack/legacy/plugins/ml/server/routes/job_validation.ts
new file mode 100644
index 0000000000000..64c9ccd27720a
--- /dev/null
+++ b/x-pack/legacy/plugins/ml/server/routes/job_validation.ts
@@ -0,0 +1,194 @@
+/*
+ * 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 Boom from 'boom';
+import { RequestHandlerContext } from 'src/core/server';
+import { schema, TypeOf } from '@kbn/config-schema';
+import { licensePreRoutingFactory } from '../new_platform/licence_check_pre_routing_factory';
+import { wrapError } from '../client/error_wrapper';
+import { RouteInitialization } from '../new_platform/plugin';
+import {
+  estimateBucketSpanSchema,
+  modelMemoryLimitSchema,
+  validateCardinalitySchema,
+  validateJobSchema,
+} from '../new_platform/job_validation_schema';
+import { estimateBucketSpanFactory } from '../models/bucket_span_estimator';
+import { calculateModelMemoryLimitProvider } from '../models/calculate_model_memory_limit';
+import { validateJob, validateCardinality } from '../models/job_validation';
+
+type CalculateModelMemoryLimitPayload = TypeOf<typeof modelMemoryLimitSchema>;
+
+/**
+ * Routes for job validation
+ */
+export function jobValidationRoutes({ config, xpackMainPlugin, router }: RouteInitialization) {
+  function calculateModelMemoryLimit(
+    context: RequestHandlerContext,
+    payload: CalculateModelMemoryLimitPayload
+  ) {
+    const {
+      indexPattern,
+      splitFieldName,
+      query,
+      fieldNames,
+      influencerNames,
+      timeFieldName,
+      earliestMs,
+      latestMs,
+    } = payload;
+
+    return calculateModelMemoryLimitProvider(context.ml!.mlClient.callAsCurrentUser)(
+      indexPattern,
+      splitFieldName,
+      query,
+      fieldNames,
+      influencerNames,
+      timeFieldName,
+      earliestMs,
+      latestMs
+    );
+  }
+
+  /**
+   * @apiGroup JobValidation
+   *
+   * @api {post} /api/ml/validate/estimate_bucket_span Estimate bucket span
+   * @apiName EstimateBucketSpan
+   * @apiDescription  Estimates minimum viable bucket span based on the characteristics of a pre-viewed subset of the data
+   */
+  router.post(
+    {
+      path: '/api/ml/validate/estimate_bucket_span',
+      validate: {
+        body: estimateBucketSpanSchema,
+      },
+    },
+    licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
+      try {
+        let errorResp;
+        const resp = await estimateBucketSpanFactory(
+          context.ml!.mlClient.callAsCurrentUser,
+          context.core.elasticsearch.adminClient.callAsInternalUser,
+          xpackMainPlugin
+        )(request.body)
+          // this catch gets triggered when the estimation code runs without error
+          // but isn't able to come up with a bucket span estimation.
+          // this doesn't return a HTTP error but an object with an error message
+          // which the client is then handling. triggering a HTTP error would be
+          // too severe for this case.
+          .catch((error: any) => {
+            errorResp = {
+              error: true,
+              message: error,
+            };
+          });
+
+        return response.ok({
+          body: errorResp !== undefined ? errorResp : resp,
+        });
+      } catch (e) {
+        // this catch gets triggered when an actual error gets thrown when running
+        // the estimation code, for example when the request payload is malformed
+        throw Boom.badRequest(e);
+      }
+    })
+  );
+
+  /**
+   * @apiGroup JobValidation
+   *
+   * @api {post} /api/ml/validate/calculate_model_memory_limit Calculates model memory limit
+   * @apiName CalculateModelMemoryLimit
+   * @apiDescription Calculates the model memory limit
+   *
+   * @apiSuccess {String} modelMemoryLimit
+   */
+  router.post(
+    {
+      path: '/api/ml/validate/calculate_model_memory_limit',
+      validate: {
+        body: modelMemoryLimitSchema,
+      },
+    },
+    licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
+      try {
+        const resp = await calculateModelMemoryLimit(context, request.body);
+
+        return response.ok({
+          body: resp,
+        });
+      } catch (e) {
+        return response.customError(wrapError(e));
+      }
+    })
+  );
+
+  /**
+   * @apiGroup JobValidation
+   *
+   * @api {post} /api/ml/validate/cardinality Validate cardinality
+   * @apiName ValidateCardinality
+   * @apiDescription Validates cardinality for the given job configuration
+   */
+  router.post(
+    {
+      path: '/api/ml/validate/cardinality',
+      validate: {
+        body: schema.object(validateCardinalitySchema),
+      },
+    },
+    licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
+      try {
+        const resp = await validateCardinality(
+          context.ml!.mlClient.callAsCurrentUser,
+          request.body
+        );
+
+        return response.ok({
+          body: resp,
+        });
+      } catch (e) {
+        return response.customError(wrapError(e));
+      }
+    })
+  );
+
+  /**
+   * @apiGroup JobValidation
+   *
+   * @api {post} /api/ml/validate/job Validates job
+   * @apiName ValidateJob
+   * @apiDescription Validates the given job configuration
+   */
+  router.post(
+    {
+      path: '/api/ml/validate/job',
+      validate: {
+        body: validateJobSchema,
+      },
+    },
+    licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
+      try {
+        // pkg.branch corresponds to the version used in documentation links.
+        const version = config.get('pkg.branch');
+        const resp = await validateJob(
+          context.ml!.mlClient.callAsCurrentUser,
+          request.body,
+          version,
+          context.core.elasticsearch.adminClient.callAsInternalUser,
+          xpackMainPlugin
+        );
+
+        return response.ok({
+          body: resp,
+        });
+      } catch (e) {
+        return response.customError(wrapError(e));
+      }
+    })
+  );
+}

From 6fe508448be85ed8bef18f0d3e5d226778c4a65d Mon Sep 17 00:00:00 2001
From: Kaarina Tungseth <kaarina.tungseth@elastic.co>
Date: Tue, 18 Feb 2020 10:31:51 -0600
Subject: [PATCH 025/174] [DOCS] Fixed typo in dashboard import API (#57862)

---
 docs/api/dashboard/import-dashboard.asciidoc | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/docs/api/dashboard/import-dashboard.asciidoc b/docs/api/dashboard/import-dashboard.asciidoc
index 0c6ea2bcf5933..14817719ec7ee 100644
--- a/docs/api/dashboard/import-dashboard.asciidoc
+++ b/docs/api/dashboard/import-dashboard.asciidoc
@@ -16,7 +16,7 @@ experimental[] Import dashboards and corresponding saved objects.
 
 `force`::
   (Optional, boolean) Overwrite any existing objects on ID conflict.
-  
+
 `exclude`::
   (Optional, array) Saved object types that you want to exclude from the import.
 
@@ -28,14 +28,14 @@ Use the complete response body from the <<dashboard-api-export, Export dashboard
 [[dashboard-api-import-response-body]]
 ==== Response body
 
-`objects`:: 
-  (array) A top level property that includes the saved objects. 
+`objects`::
+  (array) A top level property that includes the saved objects.
 
 [[dashboard-api-import-codes]]
 ==== Response code
 
 `200`::
-  Indicates a successful call, even if there are errors importing individual saved objects. If there ar errors, the error information is returned in the response body on an object-by-object basis.
+  Indicates a successful call, even if there are errors importing individual saved objects. If there are errors, the error information is returned in the response body on an object-by-object basis.
 
 [[dashboard-api-import-example]]
 ==== Example

From 7c6e6bf08a0ffd19990475b596aede7714729145 Mon Sep 17 00:00:00 2001
From: Frank Hassanabad <frank.hassanabad@elastic.co>
Date: Tue, 18 Feb 2020 09:45:18 -0700
Subject: [PATCH 026/174] [SIEM][Detection Engine] Removes internal tags when
 copying signals from rules

## Summary

Removes internal tags when copying tags from rules to the signals

Before where you can see internal tags going in:
<img width="1565" alt="Screen Shot 2020-02-14 at 5 17 36 PM" src="https://user-images.githubusercontent.com/1151048/74578011-6d1f4d00-4f4f-11ea-9057-e89896944af8.png">

After where you can see internal tags not being pushed in:
<img width="1555" alt="Screen Shot 2020-02-14 at 5 22 52 PM" src="https://user-images.githubusercontent.com/1151048/74578023-81fbe080-4f4f-11ea-8f37-86f7e5fe29f5.png">

Notice that these two internal tags should no longer be there:
```
__internal_immutable:false
__internal_rule_id:3b1bcf8a-7826-43b8-833b-6485aa700c41
```

### Checklist

Delete any items that are not applicable to this PR.

~~- [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/master/packages/kbn-i18n/README.md)~~

~~- [ ] [Documentation](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#writing-documentation) was added for features that require explanation or tutorials~~

- [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

~~- [ ] This was checked for [keyboard-only and screenreader accessibility](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility#Accessibility_testing_checklist)~~

~~- [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)~~

~~- [ ] This was checked for cross-browser compatibility, [including a check against IE11](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility)~~

### For maintainers

~~- [ ] This was checked for breaking API changes and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process)~~
---
 .../signals/build_signal.test.ts              | 160 +++++++++++++++++-
 .../detection_engine/signals/build_signal.ts  |  18 +-
 .../tests/create_rules_bulk.ts                |   1 -
 3 files changed, 176 insertions(+), 3 deletions(-)

diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_signal.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_signal.test.ts
index dcd36ab811e6a..93e9c7f6e0d50 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_signal.test.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_signal.test.ts
@@ -5,8 +5,14 @@
  */
 
 import { sampleDocNoSortId, sampleRule } from './__mocks__/es_results';
-import { buildSignal, buildAncestor, buildAncestorsSignal } from './build_signal';
+import {
+  buildSignal,
+  buildAncestor,
+  buildAncestorsSignal,
+  removeInternalTagsFromRule,
+} from './build_signal';
 import { Signal, Ancestor } from './types';
+import { INTERNAL_RULE_ID_KEY, INTERNAL_IMMUTABLE_KEY } from '../../../../common/constants';
 
 describe('buildSignal', () => {
   beforeEach(() => {
@@ -132,6 +138,77 @@ describe('buildSignal', () => {
     expect(signal).toEqual(expected);
   });
 
+  test('it builds a signal as expected with original_event if is present and without internal tags in them', () => {
+    const doc = sampleDocNoSortId('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71');
+    doc._source.event = {
+      action: 'socket_opened',
+      dataset: 'socket',
+      kind: 'event',
+      module: 'system',
+    };
+    const rule = sampleRule();
+    rule.tags = [
+      'some fake tag 1',
+      'some fake tag 2',
+      `${INTERNAL_RULE_ID_KEY}:rule-1`,
+      `${INTERNAL_IMMUTABLE_KEY}:true`,
+    ];
+    const signal = buildSignal(doc, rule);
+    const expected: Signal = {
+      parent: {
+        rule: '04128c15-0d1b-4716-a4c5-46997ac7f3bd',
+        id: 'd5e8eb51-a6a0-456d-8a15-4b79bfec3d71',
+        type: 'event',
+        index: 'myFakeSignalIndex',
+        depth: 1,
+      },
+      ancestors: [
+        {
+          rule: '04128c15-0d1b-4716-a4c5-46997ac7f3bd',
+          id: 'd5e8eb51-a6a0-456d-8a15-4b79bfec3d71',
+          type: 'event',
+          index: 'myFakeSignalIndex',
+          depth: 1,
+        },
+      ],
+      original_time: 'someTimeStamp',
+      original_event: {
+        action: 'socket_opened',
+        dataset: 'socket',
+        kind: 'event',
+        module: 'system',
+      },
+      status: 'open',
+      rule: {
+        created_by: 'elastic',
+        description: 'Detecting root and admin users',
+        enabled: true,
+        false_positives: [],
+        from: 'now-6m',
+        id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd',
+        immutable: false,
+        index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'],
+        interval: '5m',
+        risk_score: 50,
+        rule_id: 'rule-1',
+        language: 'kuery',
+        max_signals: 100,
+        name: 'Detect Root/Admin Users',
+        output_index: '.siem-signals',
+        query: 'user.name: root or user.name: admin',
+        references: ['http://www.example.com', 'https://ww.example.com'],
+        severity: 'high',
+        updated_by: 'elastic',
+        tags: ['some fake tag 1', 'some fake tag 2'],
+        to: 'now',
+        type: 'query',
+        updated_at: signal.rule.updated_at,
+        created_at: signal.rule.created_at,
+      },
+    };
+    expect(signal).toEqual(expected);
+  });
+
   test('it builds a ancestor correctly if the parent does not exist', () => {
     const doc = sampleDocNoSortId('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71');
     doc._source.event = {
@@ -190,6 +267,51 @@ describe('buildSignal', () => {
     expect(signal).toEqual(expected);
   });
 
+  test('it builds a ancestor correctly if the parent does exist without internal tags in them', () => {
+    const doc = sampleDocNoSortId('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71');
+    doc._source.event = {
+      action: 'socket_opened',
+      dataset: 'socket',
+      kind: 'event',
+      module: 'system',
+    };
+    doc._source.signal = {
+      parent: {
+        rule: '98c0bf9e-4d38-46f4-9a6a-8a820426256b',
+        id: '730ddf9e-5a00-4f85-9ddf-5878ca511a87',
+        type: 'event',
+        index: 'myFakeSignalIndex',
+        depth: 1,
+      },
+      ancestors: [
+        {
+          rule: '98c0bf9e-4d38-46f4-9a6a-8a820426256b',
+          id: '730ddf9e-5a00-4f85-9ddf-5878ca511a87',
+          type: 'event',
+          index: 'myFakeSignalIndex',
+          depth: 1,
+        },
+      ],
+    };
+    const rule = sampleRule();
+    rule.tags = [
+      'some fake tag 1',
+      'some fake tag 2',
+      `${INTERNAL_RULE_ID_KEY}:rule-1`,
+      `${INTERNAL_IMMUTABLE_KEY}:true`,
+    ];
+
+    const signal = buildAncestor(doc, rule);
+    const expected: Ancestor = {
+      rule: '04128c15-0d1b-4716-a4c5-46997ac7f3bd',
+      id: 'd5e8eb51-a6a0-456d-8a15-4b79bfec3d71',
+      type: 'signal',
+      index: 'myFakeSignalIndex',
+      depth: 2,
+    };
+    expect(signal).toEqual(expected);
+  });
+
   test('it builds a signal ancestor correctly if the parent does not exist', () => {
     const doc = sampleDocNoSortId('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71');
     doc._source.event = {
@@ -258,4 +380,40 @@ describe('buildSignal', () => {
     ];
     expect(signal).toEqual(expected);
   });
+
+  test('it removes internal tags from a typical rule', () => {
+    const rule = sampleRule();
+    rule.tags = [
+      'some fake tag 1',
+      'some fake tag 2',
+      `${INTERNAL_RULE_ID_KEY}:rule-1`,
+      `${INTERNAL_IMMUTABLE_KEY}:true`,
+    ];
+    const noInternals = removeInternalTagsFromRule(rule);
+    expect(noInternals).toEqual(sampleRule());
+  });
+
+  test('it works with an empty array', () => {
+    const rule = sampleRule();
+    rule.tags = [];
+    const noInternals = removeInternalTagsFromRule(rule);
+    const expected = sampleRule();
+    expected.tags = [];
+    expect(noInternals).toEqual(expected);
+  });
+
+  test('it works if tags does not exist', () => {
+    const rule = sampleRule();
+    delete rule.tags;
+    const noInternals = removeInternalTagsFromRule(rule);
+    const expected = sampleRule();
+    delete expected.tags;
+    expect(noInternals).toEqual(expected);
+  });
+
+  test('it works if tags contains normal values and no internal values', () => {
+    const rule = sampleRule();
+    const noInternals = removeInternalTagsFromRule(rule);
+    expect(noInternals).toEqual(rule);
+  });
 });
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_signal.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_signal.ts
index 7a63d6831ea97..684cad99f09f0 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_signal.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_signal.ts
@@ -4,6 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+import { INTERNAL_IDENTIFIER } from '../../../../common/constants';
 import { SignalSourceHit, Signal, Ancestor } from './types';
 import { OutputRuleAlertRest } from '../types';
 
@@ -45,6 +46,7 @@ export const buildAncestorsSignal = (
 };
 
 export const buildSignal = (doc: SignalSourceHit, rule: Partial<OutputRuleAlertRest>): Signal => {
+  const ruleWithoutInternalTags = removeInternalTagsFromRule(rule);
   const parent = buildAncestor(doc, rule);
   const ancestors = buildAncestorsSignal(doc, rule);
   const signal: Signal = {
@@ -52,10 +54,24 @@ export const buildSignal = (doc: SignalSourceHit, rule: Partial<OutputRuleAlertR
     ancestors,
     original_time: doc._source['@timestamp'],
     status: 'open',
-    rule,
+    rule: ruleWithoutInternalTags,
   };
   if (doc._source.event != null) {
     return { ...signal, original_event: doc._source.event };
   }
   return signal;
 };
+
+export const removeInternalTagsFromRule = (
+  rule: Partial<OutputRuleAlertRest>
+): Partial<OutputRuleAlertRest> => {
+  if (rule.tags == null) {
+    return rule;
+  } else {
+    const ruleWithoutInternalTags: Partial<OutputRuleAlertRest> = {
+      ...rule,
+      tags: rule.tags.filter(tag => !tag.startsWith(INTERNAL_IDENTIFIER)),
+    };
+    return ruleWithoutInternalTags;
+  }
+};
diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules_bulk.ts
index be008a34343c4..8e951a31b525c 100644
--- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules_bulk.ts
+++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules_bulk.ts
@@ -79,7 +79,6 @@ export default ({ getService }: FtrProviderContext): void => {
         expect(bodyToCompare).to.eql(getSimpleRuleOutputWithoutRuleId());
       });
 
-      // TODO: This is a valid issue and will be fixed in an upcoming PR and then enabled once that PR is merged
       it('should return a 200 ok but have a 409 conflict if we attempt to create the same rule_id twice', async () => {
         const { body } = await supertest
           .post(`${DETECTION_ENGINE_RULES_URL}/_bulk_create`)

From b5090fae3b9965eb3fe0086dae2d96b3764ec405 Mon Sep 17 00:00:00 2001
From: nnamdifrankie <56440728+nnamdifrankie@users.noreply.github.com>
Date: Tue, 18 Feb 2020 11:59:24 -0500
Subject: [PATCH 027/174] [Endpoint] EMT-128: add os variant and agent name
 (#57618)

[Endpoint] EMT-128: add os variant and agent name
---
 x-pack/plugins/endpoint/common/types.ts       |  2 +
 .../endpoint/store/managing/index.test.ts     |  2 +
 .../store/managing/middleware.test.ts         |  2 +
 .../server/test_data/all_endpoints_data.json  | 24 +++++---
 .../apis/endpoint/endpoints.ts                | 19 +++++++
 .../endpoint/endpoints/api_feature/data.json  | 56 ++++++++++++-------
 .../endpoints/api_feature/mappings.json       | 20 ++++++-
 7 files changed, 97 insertions(+), 28 deletions(-)

diff --git a/x-pack/plugins/endpoint/common/types.ts b/x-pack/plugins/endpoint/common/types.ts
index 5ef9d22e4dd7b..6fc4d8d79a1c5 100644
--- a/x-pack/plugins/endpoint/common/types.ts
+++ b/x-pack/plugins/endpoint/common/types.ts
@@ -101,6 +101,7 @@ export interface EndpointMetadata {
   agent: {
     version: string;
     id: string;
+    name: string;
   };
   host: {
     id: string;
@@ -111,6 +112,7 @@ export interface EndpointMetadata {
       name: string;
       full: string;
       version: string;
+      variant: string;
     };
   };
 }
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts
index dde0ba1e96a8a..56a606f430d9e 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts
@@ -30,6 +30,7 @@ describe('endpoint_list store concerns', () => {
       agent: {
         version: '',
         id: '',
+        name: '',
       },
       host: {
         id: '',
@@ -40,6 +41,7 @@ describe('endpoint_list store concerns', () => {
           name: '',
           full: '',
           version: '',
+          variant: '',
         },
       },
     };
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts
index 095e49a6c4306..250cbc6e312ed 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts
@@ -32,6 +32,7 @@ describe('endpoint list saga', () => {
       agent: {
         version: '',
         id: '',
+        name: '',
       },
       host: {
         id: '',
@@ -42,6 +43,7 @@ describe('endpoint list saga', () => {
           name: '',
           full: '',
           version: '',
+          variant: '',
         },
       },
     };
diff --git a/x-pack/plugins/endpoint/server/test_data/all_endpoints_data.json b/x-pack/plugins/endpoint/server/test_data/all_endpoints_data.json
index f1ad5190c55ff..3c824185ec083 100644
--- a/x-pack/plugins/endpoint/server/test_data/all_endpoints_data.json
+++ b/x-pack/plugins/endpoint/server/test_data/all_endpoints_data.json
@@ -30,7 +30,8 @@
           },
           "agent" : {
             "version" : "6.8.3",
-            "id" : "56a75650-3c8a-4e4f-ac17-6dd729c650e2"
+            "id" : "56a75650-3c8a-4e4f-ac17-6dd729c650e2",
+            "name": "Elastic Endpoint"
           },
           "host" : {
             "id" : "7141a48b-e19f-4ae3-89a0-6e7179a84265",
@@ -41,7 +42,8 @@
             "os" : {
               "name" : "windows 6.2",
               "full" : "Windows Server 2012",
-              "version" : "6.2"
+              "version" : "6.2",
+              "variant" : "Windows Server"
             }
           }
         },
@@ -78,7 +80,8 @@
                     },
                     "agent" : {
                       "version" : "6.8.3",
-                      "id" : "56a75650-3c8a-4e4f-ac17-6dd729c650e2"
+                      "id" : "56a75650-3c8a-4e4f-ac17-6dd729c650e2",
+                      "name": "Elastic Endpoint"
                     },
                     "host" : {
                       "id" : "7141a48b-e19f-4ae3-89a0-6e7179a84265",
@@ -89,7 +92,8 @@
                       "os" : {
                         "name" : "windows 6.2",
                         "full" : "Windows Server 2012",
-                        "version" : "6.2"
+                        "version" : "6.2",
+                        "variant" : "Windows Server"
                       }
                     }
                   },
@@ -118,7 +122,8 @@
           },
           "agent" : {
             "version" : "6.4.3",
-            "id" : "c2d84d8f-d355-40de-8b54-5d318d4d1312"
+            "id" : "c2d84d8f-d355-40de-8b54-5d318d4d1312",
+            "name": "Elastic Endpoint"
           },
           "host" : {
             "id" : "f35ec6c1-6562-45b1-818f-2f14c0854adf",
@@ -129,7 +134,8 @@
             "os" : {
               "name" : "windows 10.0",
               "full" : "Windows 10",
-              "version" : "10.0"
+              "version" : "10.0",
+              "variant" : "Windows Pro"
             }
           }
         },
@@ -166,7 +172,8 @@
                     },
                     "agent" : {
                       "version" : "6.4.3",
-                      "id" : "c2d84d8f-d355-40de-8b54-5d318d4d1312"
+                      "id" : "c2d84d8f-d355-40de-8b54-5d318d4d1312",
+                      "name": "Elastic Endpoint"
                     },
                     "host" : {
                       "id" : "f35ec6c1-6562-45b1-818f-2f14c0854adf",
@@ -177,7 +184,8 @@
                       "os" : {
                         "name" : "windows 10.0",
                         "full" : "Windows 10",
-                        "version" : "10.0"
+                        "version" : "10.0",
+                        "variant" : "Windows Pro"
                       }
                     }
                   },
diff --git a/x-pack/test/api_integration/apis/endpoint/endpoints.ts b/x-pack/test/api_integration/apis/endpoint/endpoints.ts
index 210e9d78d7e18..febe5f523cb6f 100644
--- a/x-pack/test/api_integration/apis/endpoint/endpoints.ts
+++ b/x-pack/test/api_integration/apis/endpoint/endpoints.ts
@@ -142,6 +142,25 @@ export default function({ getService }: FtrProviderContext) {
         expect(body.request_page_size).to.eql(10);
         expect(body.request_page_index).to.eql(0);
       });
+
+      it('endpoints api should return page based on host.os.variant filter.', async () => {
+        const variantValue = 'Windows Pro';
+        const { body } = await supertest
+          .post('/api/endpoint/endpoints')
+          .set('kbn-xsrf', 'xxx')
+          .send({
+            filter: `host.os.variant.keyword:${variantValue}`,
+          })
+          .expect(200);
+        expect(body.total).to.eql(2);
+        const resultOsVariantValue: Set<string> = new Set(
+          body.endpoints.map((metadata: Record<string, any>) => metadata.host.os.variant)
+        );
+        expect(Array.from(resultOsVariantValue)).to.eql([variantValue]);
+        expect(body.endpoints.length).to.eql(2);
+        expect(body.request_page_size).to.eql(10);
+        expect(body.request_page_index).to.eql(0);
+      });
     });
   });
 }
diff --git a/x-pack/test/functional/es_archives/endpoint/endpoints/api_feature/data.json b/x-pack/test/functional/es_archives/endpoint/endpoints/api_feature/data.json
index b481d56df4d52..87720b068f0e8 100644
--- a/x-pack/test/functional/es_archives/endpoint/endpoints/api_feature/data.json
+++ b/x-pack/test/functional/es_archives/endpoint/endpoints/api_feature/data.json
@@ -7,7 +7,8 @@
       "@timestamp": 1579881969541,
       "agent": {
         "id": "963b081e-60d1-482c-befd-a5815fa8290f",
-        "version": "6.6.1"
+        "version": "6.6.1",
+        "name" : "Elastic Endpoint"
       },
       "endpoint": {
         "policy": {
@@ -33,7 +34,8 @@
         "os": {
           "full": "Windows 10",
           "name": "windows 10.0",
-          "version": "10.0"
+          "version": "10.0",
+          "variant" : "Windows Pro"
         }
       }
     }
@@ -49,7 +51,8 @@
       "@timestamp": 1579881969541,
       "agent": {
         "id": "b3412d6f-b022-4448-8fee-21cc936ea86b",
-        "version": "6.0.0"
+        "version": "6.0.0",
+        "name" : "Elastic Endpoint"
       },
       "endpoint": {
         "policy": {
@@ -74,7 +77,8 @@
         "os": {
           "full": "Windows Server 2016",
           "name": "windows 10.0",
-          "version": "10.0"
+          "version": "10.0",
+          "variant" : "Windows Server"
         }
       }
     }
@@ -90,7 +94,8 @@
       "@timestamp": 1579881969541,
       "agent": {
         "id": "3838df35-a095-4af4-8fce-0b6d78793f2e",
-        "version": "6.8.0"
+        "version": "6.8.0",
+        "name" : "Elastic Endpoint"
       },
       "endpoint": {
         "policy": {
@@ -113,7 +118,8 @@
         "os": {
           "full": "Windows 10",
           "name": "windows 10.0",
-          "version": "10.0"
+          "version": "10.0",
+          "variant" : "Windows Pro"
         }
       }
     }
@@ -129,7 +135,8 @@
       "@timestamp": 1579878369541,
       "agent": {
         "id": "963b081e-60d1-482c-befd-a5815fa8290f",
-        "version": "6.6.1"
+        "version": "6.6.1",
+        "name" : "Elastic Endpoint"
       },
       "endpoint": {
         "policy": {
@@ -155,7 +162,8 @@
         "os": {
           "full": "Windows Server 2016",
           "name": "windows 10.0",
-          "version": "10.0"
+          "version": "10.0",
+          "variant" : "Windows Server 2016"
         }
       }
     }
@@ -171,7 +179,8 @@
       "@timestamp": 1579878369541,
       "agent": {
         "id": "b3412d6f-b022-4448-8fee-21cc936ea86b",
-        "version": "6.0.0"
+        "version": "6.0.0",
+        "name" : "Elastic Endpoint"
       },
       "endpoint": {
         "policy": {
@@ -195,7 +204,8 @@
         "os": {
           "full": "Windows Server 2012",
           "name": "windows 6.2",
-          "version": "6.2"
+          "version": "6.2",
+          "variant" : "Windows Server 2012"
         }
       }
     }
@@ -211,7 +221,8 @@
       "@timestamp": 1579878369541,
       "agent": {
         "id": "3838df35-a095-4af4-8fce-0b6d78793f2e",
-        "version": "6.8.0"
+        "version": "6.8.0",
+        "name" : "Elastic Endpoint"
       },
       "endpoint": {
         "policy": {
@@ -235,7 +246,8 @@
         "os": {
           "full": "Windows Server 2012",
           "name": "windows 6.2",
-          "version": "6.2"
+          "version": "6.2",
+          "variant" : "Windows Server 2012"
         }
       }
     }
@@ -251,7 +263,8 @@
       "@timestamp": 1579874769541,
       "agent": {
         "id": "963b081e-60d1-482c-befd-a5815fa8290f",
-        "version": "6.6.1"
+        "version": "6.6.1",
+        "name" : "Elastic Endpoint"
       },
       "endpoint": {
         "policy": {
@@ -276,7 +289,8 @@
         "os": {
           "full": "Windows Server 2012R2",
           "name": "windows 6.3",
-          "version": "6.3"
+          "version": "6.3",
+          "variant" : "Windows Server 2012 R2"
         }
       }
     }
@@ -292,7 +306,8 @@
       "@timestamp": 1579874769541,
       "agent": {
         "id": "b3412d6f-b022-4448-8fee-21cc936ea86b",
-        "version": "6.0.0"
+        "version": "6.0.0",
+        "name" : "Elastic Endpoint"
       },
       "endpoint": {
         "policy": {
@@ -316,7 +331,8 @@
         "os": {
           "full": "Windows Server 2012R2",
           "name": "windows 6.3",
-          "version": "6.3"
+          "version": "6.3",
+          "variant" : "Windows Server 2012 R2"
         }
       }
     }
@@ -332,7 +348,8 @@
       "@timestamp": 1579874769541,
       "agent": {
         "id": "3838df35-a095-4af4-8fce-0b6d78793f2e",
-        "version": "6.8.0"
+        "version": "6.8.0",
+        "name" : "Elastic Endpoint"
       },
       "endpoint": {
         "policy": {
@@ -356,9 +373,10 @@
         "os": {
           "full": "Windows Server 2012",
           "name": "windows 6.2",
-          "version": "6.2"
+          "version": "6.2",
+          "variant" : "Windows Server 2012"
         }
       }
     }
   }
-}
\ No newline at end of file
+}
diff --git a/x-pack/test/functional/es_archives/endpoint/endpoints/api_feature/mappings.json b/x-pack/test/functional/es_archives/endpoint/endpoints/api_feature/mappings.json
index 11766c12b8fff..d6647e62b0191 100644
--- a/x-pack/test/functional/es_archives/endpoint/endpoints/api_feature/mappings.json
+++ b/x-pack/test/functional/es_archives/endpoint/endpoints/api_feature/mappings.json
@@ -28,6 +28,15 @@
                 }
               },
               "type": "text"
+            },
+            "name": {
+              "fields": {
+                "keyword": {
+                  "ignore_above": 256,
+                  "type": "keyword"
+                }
+              },
+              "type": "text"
             }
           }
         },
@@ -122,6 +131,15 @@
                   },
                   "type": "text"
                 },
+                "variant": {
+                  "fields": {
+                    "keyword": {
+                      "ignore_above": 256,
+                      "type": "keyword"
+                    }
+                  },
+                  "type": "text"
+                },
                 "version": {
                   "fields": {
                     "keyword": {
@@ -144,4 +162,4 @@
       }
     }
   }
-}
\ No newline at end of file
+}

From c07ff7174e9cd0c32a695824d0c14f0519d78e1d Mon Sep 17 00:00:00 2001
From: Yuliia Naumenko <jo.naumenko@gmail.com>
Date: Tue, 18 Feb 2020 09:05:56 -0800
Subject: [PATCH 028/174] Alerting plugin migrate to Kibana platform (#57635)

* Moved alerting plugin to new platform

* Fixed type checks

* Fixed failing tests

* Fixed functional tests

* Fixed plugin context initialization

* small typo

* Fixed type checks

* Fixed create alert schema

* Moved alerting files to Kibana platform folder

* Fixed duration validation

* Fixed find page size

* Fixed type check

* Fixed due to comments

* Fixed merge issues

* Added missing

* Fixed alerting functional tests

* Fixed tests

* Fixed update schema validation

* Added throttle update

* Fixed failing tests

* test

* fixed setup alerting dependencies for siem and monitoring plugins

* Fixed siem setup dependancies
---
 .github/CODEOWNERS                            |   2 +
 x-pack/.i18nrc.json                           |   2 +-
 x-pack/legacy/plugins/alerting/index.ts       |  41 +--
 .../extend_route_with_license_check.test.ts   |  31 ---
 .../server/extend_route_with_license_check.ts |  19 --
 .../legacy/plugins/alerting/server/index.ts   |  34 ++-
 x-pack/legacy/plugins/alerting/server/init.ts |  28 ---
 .../alerting/{ => server}/mappings.json       |   0
 .../legacy/plugins/alerting/server/plugin.ts  | 176 -------------
 .../alerting/server/routes/_mock_server.ts    |  52 ----
 .../alerting/server/routes/create.test.ts     | 136 ----------
 .../plugins/alerting/server/routes/create.ts  |  72 ------
 .../alerting/server/routes/delete.test.ts     |  33 ---
 .../plugins/alerting/server/routes/delete.ts  |  35 ---
 .../alerting/server/routes/disable.test.ts    |  22 --
 .../plugins/alerting/server/routes/disable.ts |  23 --
 .../alerting/server/routes/enable.test.ts     |  22 --
 .../plugins/alerting/server/routes/enable.ts  |  23 --
 .../alerting/server/routes/find.test.ts       |  63 -----
 .../plugins/alerting/server/routes/find.ts    |  86 -------
 .../alerting/server/routes/get.test.ts        |  68 -----
 .../plugins/alerting/server/routes/get.ts     |  34 ---
 .../server/routes/get_alert_state.test.ts     |  73 ------
 .../alerting/server/routes/get_alert_state.ts |  35 ---
 .../server/routes/list_alert_types.test.ts    |  28 ---
 .../server/routes/list_alert_types.ts         |  18 --
 .../alerting/server/routes/mute_all.test.ts   |  22 --
 .../alerting/server/routes/mute_all.ts        |  29 ---
 .../server/routes/mute_instance.test.ts       |  22 --
 .../alerting/server/routes/mute_instance.ts   |  30 ---
 .../alerting/server/routes/unmute_all.test.ts |  22 --
 .../alerting/server/routes/unmute_all.ts      |  29 ---
 .../server/routes/unmute_instance.test.ts     |  22 --
 .../alerting/server/routes/unmute_instance.ts |  30 ---
 .../alerting/server/routes/update.test.ts     |  98 --------
 .../plugins/alerting/server/routes/update.ts  |  73 ------
 .../server/routes/update_api_key.test.ts      |  22 --
 .../alerting/server/routes/update_api_key.ts  |  23 --
 x-pack/legacy/plugins/alerting/server/shim.ts | 145 -----------
 x-pack/legacy/plugins/monitoring/index.ts     |   6 +-
 .../public/components/alerts/status.tsx       |   2 +-
 .../server/alerts/license_expiration.test.ts  |   3 +-
 .../server/alerts/license_expiration.ts       |   2 +-
 .../monitoring/server/alerts/types.d.ts       |   2 +-
 .../lib/alerts/license_expiration.lib.ts      |   2 +-
 .../plugins/monitoring/server/plugin.js       |   6 +-
 x-pack/legacy/plugins/siem/index.ts           |   1 -
 .../server/lib/detection_engine/README.md     |   4 +-
 .../routes/__mocks__/clients_service_mock.ts  |   2 +-
 .../detection_engine/rules/create_rules.ts    |   2 +-
 .../lib/detection_engine/rules/find_rules.ts  |   2 +-
 .../get_existing_prepackaged_rules.test.ts    |   2 +-
 .../rules/get_existing_prepackaged_rules.ts   |   2 +-
 .../rules/get_export_all.test.ts              |   2 +-
 .../detection_engine/rules/get_export_all.ts  |   2 +-
 .../rules/get_export_by_object_ids.test.ts    |   2 +-
 .../rules/get_export_by_object_ids.ts         |   2 +-
 .../rules/install_prepacked_rules.ts          |   4 +-
 .../lib/detection_engine/rules/patch_rules.ts |   2 +-
 .../detection_engine/rules/read_rules.test.ts |   2 +-
 .../lib/detection_engine/rules/read_rules.ts  |   2 +-
 .../lib/detection_engine/rules/types.ts       |   4 +-
 .../rules/update_prepacked_rules.ts           |   2 +-
 .../detection_engine/rules/update_rules.ts    |   2 +-
 .../scripts/get_alert_instances.sh            |   2 +-
 .../scripts/get_alert_types.sh                |   2 +-
 .../signals/get_filter.test.ts                |   2 +-
 .../detection_engine/signals/get_filter.ts    |   2 +-
 .../signals/get_input_output_index.test.ts    |   2 +-
 .../signals/get_input_output_index.ts         |   2 +-
 .../signals/search_after_bulk_create.ts       |   2 +-
 .../signals/single_bulk_create.ts             |   2 +-
 .../signals/single_search_after.ts            |   2 +-
 .../lib/detection_engine/signals/types.ts     |   6 +-
 .../lib/detection_engine/signals/utils.ts     |   3 +-
 .../detection_engine/tags/read_tags.test.ts   |   2 +-
 .../lib/detection_engine/tags/read_tags.ts    |   2 +-
 x-pack/legacy/plugins/siem/server/plugin.ts   |  12 +-
 .../siem/server/services/clients.test.ts      |   4 +-
 .../plugins/siem/server/services/clients.ts   |  19 +-
 x-pack/plugins/actions/server/routes/find.ts  |   2 +-
 .../{legacy => }/plugins/alerting/README.md   |   6 +-
 .../plugins/alerting/common/alert.ts          |   0
 .../plugins/alerting/common/alert_instance.ts |   0
 .../alerting/common/alert_task_instance.ts    |   0
 .../alerting/common/date_from_string.test.ts  |   0
 .../alerting/common/date_from_string.ts       |   0
 .../plugins/alerting/common/index.ts          |   0
 x-pack/plugins/alerting/kibana.json           |  10 +
 .../alert_instance/alert_instance.test.ts     |   0
 .../server/alert_instance/alert_instance.ts   |   0
 .../create_alert_instance_factory.test.ts     |   0
 .../create_alert_instance_factory.ts          |   0
 .../alerting/server/alert_instance/index.ts   |   0
 .../server/alert_type_registry.mock.ts        |   0
 .../server/alert_type_registry.test.ts        |   2 +-
 .../alerting/server/alert_type_registry.ts    |   2 +-
 .../alerting/server/alerts_client.mock.ts     |   0
 .../alerting/server/alerts_client.test.ts     |   8 +-
 .../plugins/alerting/server/alerts_client.ts  |   6 +-
 .../server/alerts_client_factory.test.ts      |  49 ++--
 .../alerting/server/alerts_client_factory.ts  |  46 ++--
 .../alerting/server/constants/plugin.ts       |   2 +-
 x-pack/plugins/alerting/server/index.ts       |  27 ++
 .../plugins/alerting/server/lib/index.ts      |   2 +-
 .../alerting/server/lib/license_api_access.ts |  18 ++
 .../alerting/server/lib/license_state.mock.ts |  38 +++
 .../alerting/server/lib/license_state.test.ts |   4 +-
 .../alerting/server/lib/license_state.ts      |   4 +-
 .../server/lib/parse_duration.test.ts         |   0
 .../alerting/server/lib/parse_duration.ts     |  32 ++-
 .../alerting/server/lib/result_type.ts        |   0
 .../plugins/alerting/server/lib/types.test.ts |   0
 .../plugins/alerting/server/lib/types.ts      |   0
 .../lib/validate_alert_type_params.test.ts    |   0
 .../server/lib/validate_alert_type_params.ts  |   0
 x-pack/plugins/alerting/server/mocks.ts       |  30 +++
 .../plugins/alerting/server/plugin.test.ts    |  14 +-
 x-pack/plugins/alerting/server/plugin.ts      | 236 ++++++++++++++++++
 .../server/routes/_mock_handler_arguments.ts  |  42 ++++
 .../alerting/server/routes/create.test.ts     | 172 +++++++++++++
 .../plugins/alerting/server/routes/create.ts  |  68 +++++
 .../alerting/server/routes/delete.test.ts     | 108 ++++++++
 .../plugins/alerting/server/routes/delete.ts  |  45 ++++
 .../alerting/server/routes/disable.test.ts    |  66 +++++
 .../plugins/alerting/server/routes/disable.ts |  45 ++++
 .../alerting/server/routes/enable.test.ts     |  65 +++++
 .../plugins/alerting/server/routes/enable.ts  |  45 ++++
 .../alerting/server/routes/find.test.ts       | 150 +++++++++++
 x-pack/plugins/alerting/server/routes/find.ts |  90 +++++++
 .../alerting/server/routes/get.test.ts        | 140 +++++++++++
 x-pack/plugins/alerting/server/routes/get.ts  |  46 ++++
 .../server/routes/get_alert_state.test.ts     | 170 +++++++++++++
 .../alerting/server/routes/get_alert_state.ts |  45 ++++
 .../plugins/alerting/server/routes/index.ts   |   0
 .../server/routes/list_alert_types.test.ts    | 147 +++++++++++
 .../server/routes/list_alert_types.ts         |  37 +++
 .../alerting/server/routes/mute_all.test.ts   |  65 +++++
 .../alerting/server/routes/mute_all.ts        |  45 ++++
 .../server/routes/mute_instance.test.ts       |  69 +++++
 .../alerting/server/routes/mute_instance.ts   |  46 ++++
 .../alerting/server/routes/unmute_all.test.ts |  64 +++++
 .../alerting/server/routes/unmute_all.ts      |  45 ++++
 .../server/routes/unmute_instance.test.ts     |  69 +++++
 .../alerting/server/routes/unmute_instance.ts |  46 ++++
 .../alerting/server/routes/update.test.ts     | 217 ++++++++++++++++
 .../plugins/alerting/server/routes/update.ts  |  71 ++++++
 .../server/routes/update_api_key.test.ts      |  65 +++++
 .../alerting/server/routes/update_api_key.ts  |  45 ++++
 .../task_runner/alert_task_instance.test.ts   |   2 +-
 .../server/task_runner/alert_task_instance.ts |   2 +-
 .../create_execution_handler.test.ts          |   2 +-
 .../task_runner/create_execution_handler.ts   |   4 +-
 .../task_runner/get_next_run_at.test.ts       |   0
 .../server/task_runner/get_next_run_at.ts     |   0
 .../alerting/server/task_runner/index.ts      |   0
 .../server/task_runner/task_runner.test.ts    |   9 +-
 .../server/task_runner/task_runner.ts         |   6 +-
 .../task_runner/task_runner_factory.test.ts   |   9 +-
 .../server/task_runner/task_runner_factory.ts |   8 +-
 .../transform_action_params.test.ts           |   0
 .../task_runner/transform_action_params.ts    |   0
 .../alerting/server/test_utils/index.ts       |   0
 .../plugins/alerting/server/types.ts          |  12 +-
 .../public/application/lib/alert_api.ts       |   2 +-
 .../components/alert_instances.tsx            |   3 +-
 .../triggers_actions_ui/public/types.ts       |   2 +-
 .../common/fixtures/plugins/alerts/index.ts   |  16 +-
 .../tests/alerting/create.ts                  |  35 +--
 .../tests/alerting/find.ts                    |   2 +-
 .../tests/alerting/update.ts                  |  32 +--
 .../security_and_spaces/tests/index.ts        |   2 +-
 .../spaces_only/tests/alerting/find.ts        |   2 +-
 .../spaces_only/tests/index.ts                |   2 +-
 .../fixtures/plugins/alerts/index.ts          |   6 +-
 x-pack/typings/hapi.d.ts                      |   2 +-
 176 files changed, 2944 insertions(+), 1935 deletions(-)
 delete mode 100644 x-pack/legacy/plugins/alerting/server/extend_route_with_license_check.test.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/extend_route_with_license_check.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/init.ts
 rename x-pack/legacy/plugins/alerting/{ => server}/mappings.json (100%)
 delete mode 100644 x-pack/legacy/plugins/alerting/server/plugin.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/_mock_server.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/create.test.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/create.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/delete.test.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/delete.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/disable.test.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/disable.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/enable.test.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/enable.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/find.test.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/find.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/get.test.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/get.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/get_alert_state.test.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/get_alert_state.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/list_alert_types.test.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/list_alert_types.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/mute_all.test.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/mute_all.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/mute_instance.test.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/mute_instance.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/unmute_all.test.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/unmute_all.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/unmute_instance.test.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/unmute_instance.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/update.test.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/update.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/update_api_key.test.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/routes/update_api_key.ts
 delete mode 100644 x-pack/legacy/plugins/alerting/server/shim.ts
 rename x-pack/{legacy => }/plugins/alerting/README.md (99%)
 rename x-pack/{legacy => }/plugins/alerting/common/alert.ts (100%)
 rename x-pack/{legacy => }/plugins/alerting/common/alert_instance.ts (100%)
 rename x-pack/{legacy => }/plugins/alerting/common/alert_task_instance.ts (100%)
 rename x-pack/{legacy => }/plugins/alerting/common/date_from_string.test.ts (100%)
 rename x-pack/{legacy => }/plugins/alerting/common/date_from_string.ts (100%)
 rename x-pack/{legacy => }/plugins/alerting/common/index.ts (100%)
 create mode 100644 x-pack/plugins/alerting/kibana.json
 rename x-pack/{legacy => }/plugins/alerting/server/alert_instance/alert_instance.test.ts (100%)
 rename x-pack/{legacy => }/plugins/alerting/server/alert_instance/alert_instance.ts (100%)
 rename x-pack/{legacy => }/plugins/alerting/server/alert_instance/create_alert_instance_factory.test.ts (100%)
 rename x-pack/{legacy => }/plugins/alerting/server/alert_instance/create_alert_instance_factory.ts (100%)
 rename x-pack/{legacy => }/plugins/alerting/server/alert_instance/index.ts (100%)
 rename x-pack/{legacy => }/plugins/alerting/server/alert_type_registry.mock.ts (100%)
 rename x-pack/{legacy => }/plugins/alerting/server/alert_type_registry.test.ts (97%)
 rename x-pack/{legacy => }/plugins/alerting/server/alert_type_registry.ts (95%)
 rename x-pack/{legacy => }/plugins/alerting/server/alerts_client.mock.ts (100%)
 rename x-pack/{legacy => }/plugins/alerting/server/alerts_client.test.ts (99%)
 rename x-pack/{legacy => }/plugins/alerting/server/alerts_client.ts (98%)
 rename x-pack/{legacy => }/plugins/alerting/server/alerts_client_factory.test.ts (72%)
 rename x-pack/{legacy => }/plugins/alerting/server/alerts_client_factory.ts (66%)
 rename x-pack/{legacy => }/plugins/alerting/server/constants/plugin.ts (85%)
 create mode 100644 x-pack/plugins/alerting/server/index.ts
 rename x-pack/{legacy => }/plugins/alerting/server/lib/index.ts (83%)
 create mode 100644 x-pack/plugins/alerting/server/lib/license_api_access.ts
 create mode 100644 x-pack/plugins/alerting/server/lib/license_state.mock.ts
 rename x-pack/{legacy => }/plugins/alerting/server/lib/license_state.test.ts (91%)
 rename x-pack/{legacy => }/plugins/alerting/server/lib/license_state.ts (94%)
 rename x-pack/{legacy => }/plugins/alerting/server/lib/parse_duration.test.ts (100%)
 rename x-pack/{legacy => }/plugins/alerting/server/lib/parse_duration.ts (75%)
 rename x-pack/{legacy => }/plugins/alerting/server/lib/result_type.ts (100%)
 rename x-pack/{legacy => }/plugins/alerting/server/lib/types.test.ts (100%)
 rename x-pack/{legacy => }/plugins/alerting/server/lib/types.ts (100%)
 rename x-pack/{legacy => }/plugins/alerting/server/lib/validate_alert_type_params.test.ts (100%)
 rename x-pack/{legacy => }/plugins/alerting/server/lib/validate_alert_type_params.ts (100%)
 create mode 100644 x-pack/plugins/alerting/server/mocks.ts
 rename x-pack/{legacy => }/plugins/alerting/server/plugin.test.ts (91%)
 create mode 100644 x-pack/plugins/alerting/server/plugin.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/_mock_handler_arguments.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/create.test.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/create.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/delete.test.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/delete.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/disable.test.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/disable.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/enable.test.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/enable.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/find.test.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/find.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/get.test.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/get.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/get_alert_state.test.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/get_alert_state.ts
 rename x-pack/{legacy => }/plugins/alerting/server/routes/index.ts (100%)
 create mode 100644 x-pack/plugins/alerting/server/routes/list_alert_types.test.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/list_alert_types.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/mute_all.test.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/mute_all.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/mute_instance.test.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/mute_instance.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/unmute_all.test.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/unmute_all.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/unmute_instance.test.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/unmute_instance.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/update.test.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/update.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/update_api_key.test.ts
 create mode 100644 x-pack/plugins/alerting/server/routes/update_api_key.ts
 rename x-pack/{legacy => }/plugins/alerting/server/task_runner/alert_task_instance.test.ts (98%)
 rename x-pack/{legacy => }/plugins/alerting/server/task_runner/alert_task_instance.ts (94%)
 rename x-pack/{legacy => }/plugins/alerting/server/task_runner/create_execution_handler.test.ts (98%)
 rename x-pack/{legacy => }/plugins/alerting/server/task_runner/create_execution_handler.ts (94%)
 rename x-pack/{legacy => }/plugins/alerting/server/task_runner/get_next_run_at.test.ts (100%)
 rename x-pack/{legacy => }/plugins/alerting/server/task_runner/get_next_run_at.ts (100%)
 rename x-pack/{legacy => }/plugins/alerting/server/task_runner/index.ts (100%)
 rename x-pack/{legacy => }/plugins/alerting/server/task_runner/task_runner.test.ts (97%)
 rename x-pack/{legacy => }/plugins/alerting/server/task_runner/task_runner.ts (97%)
 rename x-pack/{legacy => }/plugins/alerting/server/task_runner/task_runner_factory.test.ts (89%)
 rename x-pack/{legacy => }/plugins/alerting/server/task_runner/task_runner_factory.ts (83%)
 rename x-pack/{legacy => }/plugins/alerting/server/task_runner/transform_action_params.test.ts (100%)
 rename x-pack/{legacy => }/plugins/alerting/server/task_runner/transform_action_params.ts (100%)
 rename x-pack/{legacy => }/plugins/alerting/server/test_utils/index.ts (100%)
 rename x-pack/{legacy => }/plugins/alerting/server/types.ts (91%)

diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index e54b7b56707e8..50d157a1da3dc 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -149,6 +149,7 @@
 # Kibana Alerting Services
 /x-pack/legacy/plugins/alerting/ @elastic/kibana-alerting-services
 /x-pack/legacy/plugins/actions/ @elastic/kibana-alerting-services
+/x-pack/plugins/alerting/ @elastic/kibana-alerting-services
 /x-pack/plugins/actions/ @elastic/kibana-alerting-services
 /x-pack/plugins/event_log/ @elastic/kibana-alerting-services
 /x-pack/plugins/task_manager/ @elastic/kibana-alerting-services
@@ -156,6 +157,7 @@
 /x-pack/test/plugin_api_integration/plugins/task_manager/ @elastic/kibana-alerting-services
 /x-pack/test/plugin_api_integration/test_suites/task_manager/ @elastic/kibana-alerting-services
 /x-pack/legacy/plugins/triggers_actions_ui/ @elastic/kibana-alerting-services
+/x-pack/plugins/triggers_actions_ui/ @elastic/kibana-alerting-services
 /x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/ @elastic/kibana-alerting-services
 /x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/ @elastic/kibana-alerting-services
 
diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json
index 032efcee20afd..99882a6e235e2 100644
--- a/x-pack/.i18nrc.json
+++ b/x-pack/.i18nrc.json
@@ -3,7 +3,7 @@
   "paths": {
     "xpack.actions": "plugins/actions",
     "xpack.advancedUiActions": "plugins/advanced_ui_actions",
-    "xpack.alerting": "legacy/plugins/alerting",
+    "xpack.alerting": "plugins/alerting",
     "xpack.triggersActionsUI": "plugins/triggers_actions_ui",
     "xpack.apm": ["legacy/plugins/apm", "plugins/apm"],
     "xpack.beatsManagement": "legacy/plugins/beats_management",
diff --git a/x-pack/legacy/plugins/alerting/index.ts b/x-pack/legacy/plugins/alerting/index.ts
index 5baec07fa1182..0d0a698841269 100644
--- a/x-pack/legacy/plugins/alerting/index.ts
+++ b/x-pack/legacy/plugins/alerting/index.ts
@@ -4,43 +4,4 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { Legacy } from 'kibana';
-import { Root } from 'joi';
-import { init } from './server';
-import mappings from './mappings.json';
-
-export {
-  AlertingPlugin,
-  AlertsClient,
-  AlertType,
-  AlertExecutorOptions,
-  PluginSetupContract,
-  PluginStartContract,
-} from './server';
-
-export function alerting(kibana: any) {
-  return new kibana.Plugin({
-    id: 'alerting',
-    configPrefix: 'xpack.alerting',
-    require: ['kibana', 'elasticsearch', 'actions', 'task_manager', 'encryptedSavedObjects'],
-    isEnabled(config: Legacy.KibanaConfig) {
-      return (
-        config.get('xpack.alerting.enabled') === true &&
-        config.get('xpack.actions.enabled') === true &&
-        config.get('xpack.encryptedSavedObjects.enabled') === true &&
-        config.get('xpack.task_manager.enabled') === true
-      );
-    },
-    config(Joi: Root) {
-      return Joi.object()
-        .keys({
-          enabled: Joi.boolean().default(true),
-        })
-        .default();
-    },
-    init,
-    uiExports: {
-      mappings,
-    },
-  });
-}
+export * from './server';
diff --git a/x-pack/legacy/plugins/alerting/server/extend_route_with_license_check.test.ts b/x-pack/legacy/plugins/alerting/server/extend_route_with_license_check.test.ts
deleted file mode 100644
index 05c8424881625..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/extend_route_with_license_check.test.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-import { extendRouteWithLicenseCheck } from './extend_route_with_license_check';
-import { LicenseState } from './lib/license_state';
-jest.mock('./lib/license_state', () => ({
-  verifyApiAccessFactory: () => {},
-}));
-
-describe('extendRouteWithLicenseCheck', () => {
-  describe('#actionsextendRouteWithLicenseCheck', () => {
-    let licenseState: jest.Mocked<LicenseState>;
-
-    test('extends route object with license, if config property already exists', () => {
-      const newRoute = extendRouteWithLicenseCheck(
-        { config: { someTestProperty: 'test' } },
-        licenseState
-      );
-      expect(newRoute.config.pre.length > 0);
-    });
-    test('extends route object with license check under options.pre', () => {
-      const newRoute = extendRouteWithLicenseCheck(
-        { options: { someProperty: 'test' } },
-        licenseState
-      );
-      expect(newRoute.options.pre.length > 0);
-    });
-  });
-});
diff --git a/x-pack/legacy/plugins/alerting/server/extend_route_with_license_check.ts b/x-pack/legacy/plugins/alerting/server/extend_route_with_license_check.ts
deleted file mode 100644
index f39dc125071b4..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/extend_route_with_license_check.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-import { LicenseState, verifyApiAccessFactory } from './lib/license_state';
-
-export function extendRouteWithLicenseCheck(route: any, licenseState: LicenseState) {
-  const verifyApiAccessPreRouting = verifyApiAccessFactory(licenseState);
-
-  const key = route.options ? 'options' : 'config';
-  return {
-    ...route,
-    [key]: {
-      ...route[key],
-      pre: [verifyApiAccessPreRouting],
-    },
-  };
-}
diff --git a/x-pack/legacy/plugins/alerting/server/index.ts b/x-pack/legacy/plugins/alerting/server/index.ts
index 44ae1964ec98a..5bf7cda51bda6 100644
--- a/x-pack/legacy/plugins/alerting/server/index.ts
+++ b/x-pack/legacy/plugins/alerting/server/index.ts
@@ -4,10 +4,32 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { AlertsClient as AlertsClientClass } from './alerts_client';
+import { Legacy } from 'kibana';
+import { Root } from 'joi';
+import mappings from './mappings.json';
 
-export type AlertsClient = PublicMethodsOf<AlertsClientClass>;
-
-export { init } from './init';
-export { AlertType, AlertingPlugin, AlertExecutorOptions } from './types';
-export { PluginSetupContract, PluginStartContract } from './plugin';
+export function alerting(kibana: any) {
+  return new kibana.Plugin({
+    id: 'alerting',
+    configPrefix: 'xpack.alerting',
+    require: ['kibana', 'elasticsearch', 'actions', 'task_manager', 'encryptedSavedObjects'],
+    isEnabled(config: Legacy.KibanaConfig) {
+      return (
+        config.get('xpack.alerting.enabled') === true &&
+        config.get('xpack.actions.enabled') === true &&
+        config.get('xpack.encryptedSavedObjects.enabled') === true &&
+        config.get('xpack.task_manager.enabled') === true
+      );
+    },
+    config(Joi: Root) {
+      return Joi.object()
+        .keys({
+          enabled: Joi.boolean().default(true),
+        })
+        .default();
+    },
+    uiExports: {
+      mappings,
+    },
+  });
+}
diff --git a/x-pack/legacy/plugins/alerting/server/init.ts b/x-pack/legacy/plugins/alerting/server/init.ts
deleted file mode 100644
index d76bc33f3fa0a..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/init.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { Server, shim } from './shim';
-import { Plugin } from './plugin';
-import { AlertingPlugin } from './types';
-
-export async function init(server: Server) {
-  const { initializerContext, coreSetup, coreStart, pluginsSetup, pluginsStart } = shim(server);
-
-  const plugin = new Plugin(initializerContext);
-
-  const setupContract = await plugin.setup(coreSetup, pluginsSetup);
-  const startContract = plugin.start(coreStart, pluginsStart);
-
-  server.decorate('request', 'getAlertsClient', function() {
-    return startContract.getAlertsClientWithRequest(this);
-  });
-
-  const exposedFunctions: AlertingPlugin = {
-    setup: setupContract,
-    start: startContract,
-  };
-  server.expose(exposedFunctions);
-}
diff --git a/x-pack/legacy/plugins/alerting/mappings.json b/x-pack/legacy/plugins/alerting/server/mappings.json
similarity index 100%
rename from x-pack/legacy/plugins/alerting/mappings.json
rename to x-pack/legacy/plugins/alerting/server/mappings.json
diff --git a/x-pack/legacy/plugins/alerting/server/plugin.ts b/x-pack/legacy/plugins/alerting/server/plugin.ts
deleted file mode 100644
index 2567e470d2e79..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/plugin.ts
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import Hapi from 'hapi';
-
-import { Services } from './types';
-import { AlertsClient } from './alerts_client';
-import { AlertTypeRegistry } from './alert_type_registry';
-import { TaskRunnerFactory } from './task_runner';
-import { AlertsClientFactory } from './alerts_client_factory';
-import { LicenseState } from './lib/license_state';
-import { IClusterClient, KibanaRequest, Logger } from '../../../../../src/core/server';
-import {
-  AlertingPluginInitializerContext,
-  AlertingCoreSetup,
-  AlertingCoreStart,
-  AlertingPluginsSetup,
-  AlertingPluginsStart,
-} from './shim';
-import {
-  createAlertRoute,
-  deleteAlertRoute,
-  findAlertRoute,
-  getAlertRoute,
-  getAlertStateRoute,
-  listAlertTypesRoute,
-  updateAlertRoute,
-  enableAlertRoute,
-  disableAlertRoute,
-  updateApiKeyRoute,
-  muteAllAlertRoute,
-  unmuteAllAlertRoute,
-  muteAlertInstanceRoute,
-  unmuteAlertInstanceRoute,
-} from './routes';
-import { extendRouteWithLicenseCheck } from './extend_route_with_license_check';
-
-export interface PluginSetupContract {
-  registerType: AlertTypeRegistry['register'];
-}
-export interface PluginStartContract {
-  listTypes: AlertTypeRegistry['list'];
-  getAlertsClientWithRequest(request: Hapi.Request): PublicMethodsOf<AlertsClient>;
-}
-
-export class Plugin {
-  private readonly logger: Logger;
-  private alertTypeRegistry?: AlertTypeRegistry;
-  private readonly taskRunnerFactory: TaskRunnerFactory;
-  private adminClient?: IClusterClient;
-  private serverBasePath?: string;
-  private licenseState: LicenseState | null = null;
-  private isESOUsingEphemeralEncryptionKey?: boolean;
-
-  constructor(initializerContext: AlertingPluginInitializerContext) {
-    this.logger = initializerContext.logger.get('plugins', 'alerting');
-    this.taskRunnerFactory = new TaskRunnerFactory();
-  }
-
-  public async setup(
-    core: AlertingCoreSetup,
-    plugins: AlertingPluginsSetup
-  ): Promise<PluginSetupContract> {
-    this.adminClient = core.elasticsearch.adminClient;
-    this.licenseState = new LicenseState(plugins.licensing.license$);
-    this.isESOUsingEphemeralEncryptionKey =
-      plugins.encryptedSavedObjects.usingEphemeralEncryptionKey;
-
-    if (this.isESOUsingEphemeralEncryptionKey) {
-      this.logger.warn(
-        'APIs are disabled due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml.'
-      );
-    }
-
-    // Encrypted attributes
-    plugins.encryptedSavedObjects.registerType({
-      type: 'alert',
-      attributesToEncrypt: new Set(['apiKey']),
-      attributesToExcludeFromAAD: new Set([
-        'scheduledTaskId',
-        'muteAll',
-        'mutedInstanceIds',
-        'updatedBy',
-      ]),
-    });
-
-    const alertTypeRegistry = new AlertTypeRegistry({
-      taskManager: plugins.taskManager,
-      taskRunnerFactory: this.taskRunnerFactory,
-    });
-    this.alertTypeRegistry = alertTypeRegistry;
-    this.serverBasePath = core.http.basePath.serverBasePath;
-
-    // Register routes
-    core.http.route(extendRouteWithLicenseCheck(createAlertRoute, this.licenseState));
-    core.http.route(extendRouteWithLicenseCheck(deleteAlertRoute, this.licenseState));
-    core.http.route(extendRouteWithLicenseCheck(findAlertRoute, this.licenseState));
-    core.http.route(extendRouteWithLicenseCheck(getAlertRoute, this.licenseState));
-    core.http.route(extendRouteWithLicenseCheck(getAlertStateRoute, this.licenseState));
-    core.http.route(extendRouteWithLicenseCheck(listAlertTypesRoute, this.licenseState));
-    core.http.route(extendRouteWithLicenseCheck(updateAlertRoute, this.licenseState));
-    core.http.route(extendRouteWithLicenseCheck(enableAlertRoute, this.licenseState));
-    core.http.route(extendRouteWithLicenseCheck(disableAlertRoute, this.licenseState));
-    core.http.route(extendRouteWithLicenseCheck(updateApiKeyRoute, this.licenseState));
-    core.http.route(extendRouteWithLicenseCheck(muteAllAlertRoute, this.licenseState));
-    core.http.route(extendRouteWithLicenseCheck(unmuteAllAlertRoute, this.licenseState));
-    core.http.route(extendRouteWithLicenseCheck(muteAlertInstanceRoute, this.licenseState));
-    core.http.route(extendRouteWithLicenseCheck(unmuteAlertInstanceRoute, this.licenseState));
-
-    return {
-      registerType: alertTypeRegistry.register.bind(alertTypeRegistry),
-    };
-  }
-
-  public start(core: AlertingCoreStart, plugins: AlertingPluginsStart): PluginStartContract {
-    const { adminClient, serverBasePath, isESOUsingEphemeralEncryptionKey } = this;
-
-    function spaceIdToNamespace(spaceId?: string): string | undefined {
-      const spacesPlugin = plugins.spaces();
-      return spacesPlugin && spaceId ? spacesPlugin.spaceIdToNamespace(spaceId) : undefined;
-    }
-
-    const alertsClientFactory = new AlertsClientFactory({
-      alertTypeRegistry: this.alertTypeRegistry!,
-      logger: this.logger,
-      taskManager: plugins.taskManager,
-      securityPluginSetup: plugins.security,
-      encryptedSavedObjectsPlugin: plugins.encryptedSavedObjects,
-      spaceIdToNamespace,
-      getSpaceId(request: Hapi.Request) {
-        const spacesPlugin = plugins.spaces();
-        return spacesPlugin ? spacesPlugin.getSpaceId(request) : undefined;
-      },
-    });
-
-    this.taskRunnerFactory.initialize({
-      logger: this.logger,
-      getServices(rawRequest: Hapi.Request): Services {
-        const request = KibanaRequest.from(rawRequest);
-        return {
-          callCluster: (...args) => adminClient!.asScoped(request).callAsCurrentUser(...args),
-          // rawRequest is actually a fake request, converting it to KibanaRequest causes issue in SO access
-          savedObjectsClient: core.savedObjects.getScopedSavedObjectsClient(rawRequest as any),
-        };
-      },
-      spaceIdToNamespace,
-      executeAction: plugins.actions.execute,
-      encryptedSavedObjectsPlugin: plugins.encryptedSavedObjects,
-      getBasePath(spaceId?: string): string {
-        const spacesPlugin = plugins.spaces();
-        return spacesPlugin && spaceId ? spacesPlugin.getBasePath(spaceId) : serverBasePath!;
-      },
-    });
-
-    return {
-      listTypes: this.alertTypeRegistry!.list.bind(this.alertTypeRegistry!),
-      getAlertsClientWithRequest: (request: Hapi.Request) => {
-        if (isESOUsingEphemeralEncryptionKey === true) {
-          throw new Error(
-            `Unable to create alerts client due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml`
-          );
-        }
-        return alertsClientFactory!.create(KibanaRequest.from(request), request);
-      },
-    };
-  }
-
-  public stop() {
-    if (this.licenseState) {
-      this.licenseState.clean();
-    }
-  }
-}
diff --git a/x-pack/legacy/plugins/alerting/server/routes/_mock_server.ts b/x-pack/legacy/plugins/alerting/server/routes/_mock_server.ts
deleted file mode 100644
index 0f865a12067ad..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/_mock_server.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import Hapi from 'hapi';
-import { alertsClientMock } from '../alerts_client.mock';
-import { alertTypeRegistryMock } from '../alert_type_registry.mock';
-
-const defaultConfig = {
-  'kibana.index': '.kibana',
-};
-
-export function createMockServer(config: Record<string, any> = defaultConfig) {
-  const server = new Hapi.Server({
-    port: 0,
-  });
-
-  const alertsClient = alertsClientMock.create();
-  const alertTypeRegistry = alertTypeRegistryMock.create();
-
-  server.config = () => {
-    return {
-      get(key: string) {
-        return config[key];
-      },
-      has(key: string) {
-        return config.hasOwnProperty(key);
-      },
-    };
-  };
-
-  server.register({
-    name: 'alerting',
-    register(pluginServer: Hapi.Server) {
-      pluginServer.expose({
-        setup: {
-          registerType: alertTypeRegistry.register,
-        },
-        start: {
-          listTypes: alertTypeRegistry.list,
-        },
-      });
-    },
-  });
-
-  server.decorate('request', 'getAlertsClient', () => alertsClient);
-  server.decorate('request', 'getBasePath', () => '/s/default');
-
-  return { server, alertsClient, alertTypeRegistry };
-}
diff --git a/x-pack/legacy/plugins/alerting/server/routes/create.test.ts b/x-pack/legacy/plugins/alerting/server/routes/create.test.ts
deleted file mode 100644
index 2a0ae78fd78b2..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/create.test.ts
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { omit } from 'lodash';
-import { createMockServer } from './_mock_server';
-import { createAlertRoute } from './create';
-
-const { server, alertsClient } = createMockServer();
-server.route(createAlertRoute);
-
-const mockedAlert = {
-  alertTypeId: '1',
-  consumer: 'bar',
-  name: 'abc',
-  schedule: { interval: '10s' },
-  tags: ['foo'],
-  params: {
-    bar: true,
-  },
-  throttle: '30s',
-  actions: [
-    {
-      group: 'default',
-      id: '2',
-      params: {
-        foo: true,
-      },
-    },
-  ],
-};
-
-beforeEach(() => jest.resetAllMocks());
-
-test('creates an alert with proper parameters', async () => {
-  const request = {
-    method: 'POST',
-    url: '/api/alert',
-    payload: mockedAlert,
-  };
-
-  const createdAt = new Date();
-  const updatedAt = new Date();
-  alertsClient.create.mockResolvedValueOnce({
-    ...mockedAlert,
-    enabled: true,
-    muteAll: false,
-    createdBy: '',
-    updatedBy: '',
-    apiKey: '',
-    apiKeyOwner: '',
-    mutedInstanceIds: [],
-    createdAt,
-    updatedAt,
-    id: '123',
-    actions: [
-      {
-        ...mockedAlert.actions[0],
-        actionTypeId: 'test',
-      },
-    ],
-  });
-  const { payload, statusCode } = await server.inject(request);
-  expect(statusCode).toBe(200);
-  const response = JSON.parse(payload);
-  expect(new Date(response.createdAt)).toEqual(createdAt);
-  expect(omit(response, 'createdAt', 'updatedAt')).toMatchInlineSnapshot(`
-    Object {
-      "actions": Array [
-        Object {
-          "actionTypeId": "test",
-          "group": "default",
-          "id": "2",
-          "params": Object {
-            "foo": true,
-          },
-        },
-      ],
-      "alertTypeId": "1",
-      "apiKey": "",
-      "apiKeyOwner": "",
-      "consumer": "bar",
-      "createdBy": "",
-      "enabled": true,
-      "id": "123",
-      "muteAll": false,
-      "mutedInstanceIds": Array [],
-      "name": "abc",
-      "params": Object {
-        "bar": true,
-      },
-      "schedule": Object {
-        "interval": "10s",
-      },
-      "tags": Array [
-        "foo",
-      ],
-      "throttle": "30s",
-      "updatedBy": "",
-    }
-  `);
-  expect(alertsClient.create).toHaveBeenCalledTimes(1);
-  expect(alertsClient.create.mock.calls[0]).toMatchInlineSnapshot(`
-    Array [
-      Object {
-        "data": Object {
-          "actions": Array [
-            Object {
-              "group": "default",
-              "id": "2",
-              "params": Object {
-                "foo": true,
-              },
-            },
-          ],
-          "alertTypeId": "1",
-          "consumer": "bar",
-          "enabled": true,
-          "name": "abc",
-          "params": Object {
-            "bar": true,
-          },
-          "schedule": Object {
-            "interval": "10s",
-          },
-          "tags": Array [
-            "foo",
-          ],
-          "throttle": "30s",
-        },
-      },
-    ]
-  `);
-});
diff --git a/x-pack/legacy/plugins/alerting/server/routes/create.ts b/x-pack/legacy/plugins/alerting/server/routes/create.ts
deleted file mode 100644
index 362a23a3fa910..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/create.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import Hapi from 'hapi';
-import Joi from 'joi';
-import { getDurationSchema } from '../lib';
-import { IntervalSchedule } from '../types';
-
-interface ScheduleRequest extends Hapi.Request {
-  payload: {
-    enabled: boolean;
-    name: string;
-    tags: string[];
-    alertTypeId: string;
-    consumer: string;
-    schedule: IntervalSchedule;
-    actions: Array<{
-      group: string;
-      id: string;
-      params: Record<string, any>;
-    }>;
-    params: Record<string, any>;
-    throttle: string | null;
-  };
-}
-
-export const createAlertRoute = {
-  method: 'POST',
-  path: '/api/alert',
-  options: {
-    tags: ['access:alerting-all'],
-    validate: {
-      options: {
-        abortEarly: false,
-      },
-      payload: Joi.object()
-        .keys({
-          enabled: Joi.boolean().default(true),
-          name: Joi.string().required(),
-          tags: Joi.array()
-            .items(Joi.string())
-            .default([]),
-          alertTypeId: Joi.string().required(),
-          consumer: Joi.string().required(),
-          throttle: getDurationSchema().default(null),
-          schedule: Joi.object()
-            .keys({
-              interval: getDurationSchema().required(),
-            })
-            .required(),
-          params: Joi.object().required(),
-          actions: Joi.array()
-            .items(
-              Joi.object().keys({
-                group: Joi.string().required(),
-                id: Joi.string().required(),
-                params: Joi.object().required(),
-              })
-            )
-            .required(),
-        })
-        .required(),
-    },
-  },
-  async handler(request: ScheduleRequest) {
-    const alertsClient = request.getAlertsClient!();
-    return await alertsClient.create({ data: request.payload });
-  },
-};
diff --git a/x-pack/legacy/plugins/alerting/server/routes/delete.test.ts b/x-pack/legacy/plugins/alerting/server/routes/delete.test.ts
deleted file mode 100644
index b7b25538847fa..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/delete.test.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { createMockServer } from './_mock_server';
-import { deleteAlertRoute } from './delete';
-
-const { server, alertsClient } = createMockServer();
-server.route(deleteAlertRoute);
-
-beforeEach(() => jest.resetAllMocks());
-
-test('deletes an alert with proper parameters', async () => {
-  const request = {
-    method: 'DELETE',
-    url: '/api/alert/1',
-  };
-
-  alertsClient.delete.mockResolvedValueOnce({});
-  const { payload, statusCode } = await server.inject(request);
-  expect(statusCode).toBe(204);
-  expect(payload).toEqual('');
-  expect(alertsClient.delete).toHaveBeenCalledTimes(1);
-  expect(alertsClient.delete.mock.calls[0]).toMatchInlineSnapshot(`
-Array [
-  Object {
-    "id": "1",
-  },
-]
-`);
-});
diff --git a/x-pack/legacy/plugins/alerting/server/routes/delete.ts b/x-pack/legacy/plugins/alerting/server/routes/delete.ts
deleted file mode 100644
index 24a1f6777b17b..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/delete.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import Hapi from 'hapi';
-import Joi from 'joi';
-
-interface DeleteRequest extends Hapi.Request {
-  params: {
-    id: string;
-  };
-}
-
-export const deleteAlertRoute = {
-  method: 'DELETE',
-  path: '/api/alert/{id}',
-  config: {
-    tags: ['access:alerting-all'],
-    validate: {
-      params: Joi.object()
-        .keys({
-          id: Joi.string().required(),
-        })
-        .required(),
-    },
-  },
-  async handler(request: DeleteRequest, h: Hapi.ResponseToolkit) {
-    const { id } = request.params;
-    const alertsClient = request.getAlertsClient!();
-    await alertsClient.delete({ id });
-    return h.response().code(204);
-  },
-};
diff --git a/x-pack/legacy/plugins/alerting/server/routes/disable.test.ts b/x-pack/legacy/plugins/alerting/server/routes/disable.test.ts
deleted file mode 100644
index d2214f5c7c175..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/disable.test.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { createMockServer } from './_mock_server';
-import { disableAlertRoute } from './disable';
-
-const { server, alertsClient } = createMockServer();
-server.route(disableAlertRoute);
-
-test('disables an alert', async () => {
-  const request = {
-    method: 'POST',
-    url: '/api/alert/1/_disable',
-  };
-
-  const { statusCode } = await server.inject(request);
-  expect(statusCode).toBe(204);
-  expect(alertsClient.disable).toHaveBeenCalledWith({ id: '1' });
-});
diff --git a/x-pack/legacy/plugins/alerting/server/routes/disable.ts b/x-pack/legacy/plugins/alerting/server/routes/disable.ts
deleted file mode 100644
index 2e88d3b5b96da..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/disable.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import Hapi from 'hapi';
-
-export const disableAlertRoute = {
-  method: 'POST',
-  path: '/api/alert/{id}/_disable',
-  config: {
-    tags: ['access:alerting-all'],
-    response: {
-      emptyStatusCode: 204,
-    },
-  },
-  async handler(request: Hapi.Request, h: Hapi.ResponseToolkit) {
-    const alertsClient = request.getAlertsClient!();
-    await alertsClient.disable({ id: request.params.id });
-    return h.response();
-  },
-};
diff --git a/x-pack/legacy/plugins/alerting/server/routes/enable.test.ts b/x-pack/legacy/plugins/alerting/server/routes/enable.test.ts
deleted file mode 100644
index bc5498af61989..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/enable.test.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { createMockServer } from './_mock_server';
-import { enableAlertRoute } from './enable';
-
-const { server, alertsClient } = createMockServer();
-server.route(enableAlertRoute);
-
-test('enables an alert', async () => {
-  const request = {
-    method: 'POST',
-    url: '/api/alert/1/_enable',
-  };
-
-  const { statusCode } = await server.inject(request);
-  expect(statusCode).toBe(204);
-  expect(alertsClient.enable).toHaveBeenCalledWith({ id: '1' });
-});
diff --git a/x-pack/legacy/plugins/alerting/server/routes/enable.ts b/x-pack/legacy/plugins/alerting/server/routes/enable.ts
deleted file mode 100644
index 28080db7b0230..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/enable.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import Hapi from 'hapi';
-
-export const enableAlertRoute = {
-  method: 'POST',
-  path: '/api/alert/{id}/_enable',
-  config: {
-    tags: ['access:alerting-all'],
-    response: {
-      emptyStatusCode: 204,
-    },
-  },
-  async handler(request: Hapi.Request, h: Hapi.ResponseToolkit) {
-    const alertsClient = request.getAlertsClient!();
-    await alertsClient.enable({ id: request.params.id });
-    return h.response();
-  },
-};
diff --git a/x-pack/legacy/plugins/alerting/server/routes/find.test.ts b/x-pack/legacy/plugins/alerting/server/routes/find.test.ts
deleted file mode 100644
index 7b86fb3fbd28e..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/find.test.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { createMockServer } from './_mock_server';
-import { findAlertRoute } from './find';
-
-const { server, alertsClient } = createMockServer();
-server.route(findAlertRoute);
-
-beforeEach(() => jest.resetAllMocks());
-
-test('sends proper arguments to alert find function', async () => {
-  const request = {
-    method: 'GET',
-    url:
-      '/api/alert/_find?' +
-      'per_page=1&' +
-      'page=1&' +
-      'search=text*&' +
-      'default_search_operator=AND&' +
-      'search_fields=description&' +
-      'sort_field=description&' +
-      'fields=description',
-  };
-
-  const expectedResult = {
-    page: 1,
-    perPage: 1,
-    total: 0,
-    data: [],
-  };
-
-  alertsClient.find.mockResolvedValueOnce(expectedResult);
-  const { payload, statusCode } = await server.inject(request);
-  expect(statusCode).toBe(200);
-  const response = JSON.parse(payload);
-  expect(response).toEqual(expectedResult);
-  expect(alertsClient.find).toHaveBeenCalledTimes(1);
-  expect(alertsClient.find.mock.calls[0]).toMatchInlineSnapshot(`
-    Array [
-      Object {
-        "options": Object {
-          "defaultSearchOperator": "AND",
-          "fields": Array [
-            "description",
-          ],
-          "filter": undefined,
-          "hasReference": undefined,
-          "page": 1,
-          "perPage": 1,
-          "search": "text*",
-          "searchFields": Array [
-            "description",
-          ],
-          "sortField": "description",
-        },
-      },
-    ]
-  `);
-});
diff --git a/x-pack/legacy/plugins/alerting/server/routes/find.ts b/x-pack/legacy/plugins/alerting/server/routes/find.ts
deleted file mode 100644
index 75cf93b18398f..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/find.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import Joi from 'joi';
-import Hapi from 'hapi';
-
-import { WithoutQueryAndParams } from '../types';
-
-interface FindRequest extends WithoutQueryAndParams<Hapi.Request> {
-  query: {
-    per_page: number;
-    page: number;
-    search?: string;
-    default_search_operator: 'AND' | 'OR';
-    search_fields?: string[];
-    sort_field?: string;
-    has_reference?: {
-      type: string;
-      id: string;
-    };
-    fields?: string[];
-    filter?: string;
-  };
-}
-
-export const findAlertRoute = {
-  method: 'GET',
-  path: '/api/alert/_find',
-  config: {
-    tags: ['access:alerting-read'],
-    validate: {
-      query: Joi.object()
-        .keys({
-          per_page: Joi.number()
-            .min(0)
-            .default(20),
-          page: Joi.number()
-            .min(1)
-            .default(1),
-          search: Joi.string()
-            .allow('')
-            .optional(),
-          default_search_operator: Joi.string()
-            .valid('OR', 'AND')
-            .default('OR'),
-          search_fields: Joi.array()
-            .items(Joi.string())
-            .single(),
-          sort_field: Joi.string(),
-          has_reference: Joi.object()
-            .keys({
-              type: Joi.string().required(),
-              id: Joi.string().required(),
-            })
-            .optional(),
-          fields: Joi.array()
-            .items(Joi.string())
-            .single(),
-          filter: Joi.string()
-            .allow('')
-            .optional(),
-        })
-        .default(),
-    },
-  },
-  async handler(request: FindRequest) {
-    const { query } = request;
-    const alertsClient = request.getAlertsClient!();
-    return await alertsClient.find({
-      options: {
-        perPage: query.per_page,
-        page: query.page,
-        search: query.search,
-        defaultSearchOperator: query.default_search_operator,
-        searchFields: query.search_fields,
-        sortField: query.sort_field,
-        hasReference: query.has_reference,
-        fields: query.fields,
-        filter: query.filter,
-      },
-    });
-  },
-};
diff --git a/x-pack/legacy/plugins/alerting/server/routes/get.test.ts b/x-pack/legacy/plugins/alerting/server/routes/get.test.ts
deleted file mode 100644
index 320e9042d87c5..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/get.test.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { createMockServer } from './_mock_server';
-import { getAlertRoute } from './get';
-
-const { server, alertsClient } = createMockServer();
-server.route(getAlertRoute);
-
-const mockedAlert = {
-  id: '1',
-  alertTypeId: '1',
-  schedule: { interval: '10s' },
-  params: {
-    bar: true,
-  },
-  createdAt: new Date(),
-  updatedAt: new Date(),
-  actions: [
-    {
-      group: 'default',
-      id: '2',
-      actionTypeId: 'test',
-      params: {
-        foo: true,
-      },
-    },
-  ],
-  consumer: 'bar',
-  name: 'abc',
-  tags: ['foo'],
-  enabled: true,
-  muteAll: false,
-  createdBy: '',
-  updatedBy: '',
-  apiKey: '',
-  apiKeyOwner: '',
-  throttle: '30s',
-  mutedInstanceIds: [],
-};
-
-beforeEach(() => jest.resetAllMocks());
-
-test('calls get with proper parameters', async () => {
-  const request = {
-    method: 'GET',
-    url: '/api/alert/1',
-  };
-
-  alertsClient.get.mockResolvedValueOnce(mockedAlert);
-  const { payload, statusCode } = await server.inject(request);
-  expect(statusCode).toBe(200);
-  const { createdAt, updatedAt, ...response } = JSON.parse(payload);
-  expect({ createdAt: new Date(createdAt), updatedAt: new Date(updatedAt), ...response }).toEqual(
-    mockedAlert
-  );
-  expect(alertsClient.get).toHaveBeenCalledTimes(1);
-  expect(alertsClient.get.mock.calls[0]).toMatchInlineSnapshot(`
-Array [
-  Object {
-    "id": "1",
-  },
-]
-`);
-});
diff --git a/x-pack/legacy/plugins/alerting/server/routes/get.ts b/x-pack/legacy/plugins/alerting/server/routes/get.ts
deleted file mode 100644
index 67991aa575a21..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/get.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import Joi from 'joi';
-import Hapi from 'hapi';
-
-interface GetRequest extends Hapi.Request {
-  params: {
-    id: string;
-  };
-}
-
-export const getAlertRoute = {
-  method: 'GET',
-  path: `/api/alert/{id}`,
-  options: {
-    tags: ['access:alerting-read'],
-    validate: {
-      params: Joi.object()
-        .keys({
-          id: Joi.string().required(),
-        })
-        .required(),
-    },
-  },
-  async handler(request: GetRequest) {
-    const { id } = request.params;
-    const alertsClient = request.getAlertsClient!();
-    return await alertsClient.get({ id });
-  },
-};
diff --git a/x-pack/legacy/plugins/alerting/server/routes/get_alert_state.test.ts b/x-pack/legacy/plugins/alerting/server/routes/get_alert_state.test.ts
deleted file mode 100644
index 9e3b3b6579ead..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/get_alert_state.test.ts
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { createMockServer } from './_mock_server';
-import { getAlertStateRoute } from './get_alert_state';
-import { SavedObjectsErrorHelpers } from 'src/core/server';
-
-const { server, alertsClient } = createMockServer();
-server.route(getAlertStateRoute);
-
-const mockedAlertState = {
-  alertTypeState: {
-    some: 'value',
-  },
-  alertInstances: {
-    first_instance: {
-      state: {},
-      meta: {
-        lastScheduledActions: {
-          group: 'first_group',
-          date: new Date(),
-        },
-      },
-    },
-    second_instance: {},
-  },
-};
-
-beforeEach(() => jest.resetAllMocks());
-
-test('gets alert state', async () => {
-  const request = {
-    method: 'GET',
-    url: '/api/alert/1/state',
-  };
-
-  alertsClient.getAlertState.mockResolvedValueOnce(mockedAlertState);
-
-  const { statusCode } = await server.inject(request);
-  expect(statusCode).toBe(200);
-  expect(alertsClient.getAlertState).toHaveBeenCalledWith({ id: '1' });
-});
-
-test('returns NO-CONTENT when alert exists but has no task state yet', async () => {
-  const request = {
-    method: 'GET',
-    url: '/api/alert/1/state',
-  };
-
-  alertsClient.getAlertState.mockResolvedValueOnce(undefined);
-
-  const { statusCode } = await server.inject(request);
-  expect(statusCode).toBe(204);
-  expect(alertsClient.getAlertState).toHaveBeenCalledWith({ id: '1' });
-});
-
-test('returns NOT-FOUND when alert is not found', async () => {
-  const request = {
-    method: 'GET',
-    url: '/api/alert/1/state',
-  };
-
-  alertsClient.getAlertState.mockRejectedValue(
-    SavedObjectsErrorHelpers.createGenericNotFoundError('alert', '1')
-  );
-
-  const { statusCode } = await server.inject(request);
-  expect(statusCode).toBe(404);
-  expect(alertsClient.getAlertState).toHaveBeenCalledWith({ id: '1' });
-});
diff --git a/x-pack/legacy/plugins/alerting/server/routes/get_alert_state.ts b/x-pack/legacy/plugins/alerting/server/routes/get_alert_state.ts
deleted file mode 100644
index 12136a975bb19..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/get_alert_state.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import Joi from 'joi';
-import Hapi from 'hapi';
-
-interface GetAlertStateRequest extends Hapi.Request {
-  params: {
-    id: string;
-  };
-}
-
-export const getAlertStateRoute = {
-  method: 'GET',
-  path: '/api/alert/{id}/state',
-  options: {
-    tags: ['access:alerting-read'],
-    validate: {
-      params: Joi.object()
-        .keys({
-          id: Joi.string().required(),
-        })
-        .required(),
-    },
-  },
-  async handler(request: GetAlertStateRequest, h: Hapi.ResponseToolkit) {
-    const { id } = request.params;
-    const alertsClient = request.getAlertsClient!();
-    const state = await alertsClient.getAlertState({ id });
-    return state ? state : h.response().code(204);
-  },
-};
diff --git a/x-pack/legacy/plugins/alerting/server/routes/list_alert_types.test.ts b/x-pack/legacy/plugins/alerting/server/routes/list_alert_types.test.ts
deleted file mode 100644
index 46f02812e0f6c..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/list_alert_types.test.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { createMockServer } from './_mock_server';
-import { listAlertTypesRoute } from './list_alert_types';
-
-const { server, alertTypeRegistry } = createMockServer();
-server.route(listAlertTypesRoute);
-
-beforeEach(() => jest.resetAllMocks());
-
-test('calls the list function', async () => {
-  const request = {
-    method: 'GET',
-    url: '/api/alert/types',
-  };
-
-  alertTypeRegistry.list.mockReturnValueOnce([]);
-  const { payload, statusCode } = await server.inject(request);
-  expect(statusCode).toBe(200);
-  const response = JSON.parse(payload);
-  expect(response).toEqual([]);
-  expect(alertTypeRegistry.list).toHaveBeenCalledTimes(1);
-  expect(alertTypeRegistry.list.mock.calls[0]).toMatchInlineSnapshot(`Array []`);
-});
diff --git a/x-pack/legacy/plugins/alerting/server/routes/list_alert_types.ts b/x-pack/legacy/plugins/alerting/server/routes/list_alert_types.ts
deleted file mode 100644
index 13aaf6bfe452e..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/list_alert_types.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import Hapi from 'hapi';
-
-export const listAlertTypesRoute = {
-  method: 'GET',
-  path: `/api/alert/types`,
-  config: {
-    tags: ['access:alerting-read'],
-  },
-  async handler(request: Hapi.Request) {
-    return request.server.plugins.alerting!.start.listTypes();
-  },
-};
diff --git a/x-pack/legacy/plugins/alerting/server/routes/mute_all.test.ts b/x-pack/legacy/plugins/alerting/server/routes/mute_all.test.ts
deleted file mode 100644
index 408a037edc345..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/mute_all.test.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { createMockServer } from './_mock_server';
-import { muteAllAlertRoute } from './mute_all';
-
-const { server, alertsClient } = createMockServer();
-server.route(muteAllAlertRoute);
-
-test('mutes an alert', async () => {
-  const request = {
-    method: 'POST',
-    url: '/api/alert/1/_mute_all',
-  };
-
-  const { statusCode } = await server.inject(request);
-  expect(statusCode).toBe(204);
-  expect(alertsClient.muteAll).toHaveBeenCalledWith({ id: '1' });
-});
diff --git a/x-pack/legacy/plugins/alerting/server/routes/mute_all.ts b/x-pack/legacy/plugins/alerting/server/routes/mute_all.ts
deleted file mode 100644
index 90ae9b11b2f43..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/mute_all.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import Hapi from 'hapi';
-
-interface MuteAllRequest extends Hapi.Request {
-  params: {
-    id: string;
-  };
-}
-
-export const muteAllAlertRoute = {
-  method: 'POST',
-  path: '/api/alert/{id}/_mute_all',
-  config: {
-    tags: ['access:alerting-all'],
-    response: {
-      emptyStatusCode: 204,
-    },
-  },
-  async handler(request: MuteAllRequest, h: Hapi.ResponseToolkit) {
-    const alertsClient = request.getAlertsClient!();
-    await alertsClient.muteAll(request.params);
-    return h.response();
-  },
-};
diff --git a/x-pack/legacy/plugins/alerting/server/routes/mute_instance.test.ts b/x-pack/legacy/plugins/alerting/server/routes/mute_instance.test.ts
deleted file mode 100644
index 697a68763adf6..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/mute_instance.test.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { createMockServer } from './_mock_server';
-import { muteAlertInstanceRoute } from './mute_instance';
-
-const { server, alertsClient } = createMockServer();
-server.route(muteAlertInstanceRoute);
-
-test('mutes an alert instance', async () => {
-  const request = {
-    method: 'POST',
-    url: '/api/alert/1/alert_instance/2/_mute',
-  };
-
-  const { statusCode } = await server.inject(request);
-  expect(statusCode).toBe(204);
-  expect(alertsClient.muteInstance).toHaveBeenCalledWith({ alertId: '1', alertInstanceId: '2' });
-});
diff --git a/x-pack/legacy/plugins/alerting/server/routes/mute_instance.ts b/x-pack/legacy/plugins/alerting/server/routes/mute_instance.ts
deleted file mode 100644
index 503a07af88bca..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/mute_instance.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import Hapi from 'hapi';
-
-interface MuteInstanceRequest extends Hapi.Request {
-  params: {
-    alertId: string;
-    alertInstanceId: string;
-  };
-}
-
-export const muteAlertInstanceRoute = {
-  method: 'POST',
-  path: '/api/alert/{alertId}/alert_instance/{alertInstanceId}/_mute',
-  config: {
-    tags: ['access:alerting-all'],
-    response: {
-      emptyStatusCode: 204,
-    },
-  },
-  async handler(request: MuteInstanceRequest, h: Hapi.ResponseToolkit) {
-    const alertsClient = request.getAlertsClient!();
-    await alertsClient.muteInstance(request.params);
-    return h.response();
-  },
-};
diff --git a/x-pack/legacy/plugins/alerting/server/routes/unmute_all.test.ts b/x-pack/legacy/plugins/alerting/server/routes/unmute_all.test.ts
deleted file mode 100644
index c28f51b405a3e..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/unmute_all.test.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { createMockServer } from './_mock_server';
-import { unmuteAllAlertRoute } from './unmute_all';
-
-const { server, alertsClient } = createMockServer();
-server.route(unmuteAllAlertRoute);
-
-test('unmutes an alert', async () => {
-  const request = {
-    method: 'POST',
-    url: '/api/alert/1/_unmute_all',
-  };
-
-  const { statusCode } = await server.inject(request);
-  expect(statusCode).toBe(204);
-  expect(alertsClient.unmuteAll).toHaveBeenCalledWith({ id: '1' });
-});
diff --git a/x-pack/legacy/plugins/alerting/server/routes/unmute_all.ts b/x-pack/legacy/plugins/alerting/server/routes/unmute_all.ts
deleted file mode 100644
index 8bb119c600096..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/unmute_all.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import Hapi from 'hapi';
-
-interface UnmuteAllRequest extends Hapi.Request {
-  params: {
-    id: string;
-  };
-}
-
-export const unmuteAllAlertRoute = {
-  method: 'POST',
-  path: '/api/alert/{id}/_unmute_all',
-  config: {
-    tags: ['access:alerting-all'],
-    response: {
-      emptyStatusCode: 204,
-    },
-  },
-  async handler(request: UnmuteAllRequest, h: Hapi.ResponseToolkit) {
-    const alertsClient = request.getAlertsClient!();
-    await alertsClient.unmuteAll(request.params);
-    return h.response();
-  },
-};
diff --git a/x-pack/legacy/plugins/alerting/server/routes/unmute_instance.test.ts b/x-pack/legacy/plugins/alerting/server/routes/unmute_instance.test.ts
deleted file mode 100644
index 5c8f23236e2b0..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/unmute_instance.test.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { createMockServer } from './_mock_server';
-import { unmuteAlertInstanceRoute } from './unmute_instance';
-
-const { server, alertsClient } = createMockServer();
-server.route(unmuteAlertInstanceRoute);
-
-test('unmutes an alert instance', async () => {
-  const request = {
-    method: 'POST',
-    url: '/api/alert/1/alert_instance/2/_unmute',
-  };
-
-  const { statusCode } = await server.inject(request);
-  expect(statusCode).toBe(204);
-  expect(alertsClient.unmuteInstance).toHaveBeenCalledWith({ alertId: '1', alertInstanceId: '2' });
-});
diff --git a/x-pack/legacy/plugins/alerting/server/routes/unmute_instance.ts b/x-pack/legacy/plugins/alerting/server/routes/unmute_instance.ts
deleted file mode 100644
index 1d1145e010741..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/unmute_instance.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import Hapi from 'hapi';
-
-interface UnmuteInstanceRequest extends Hapi.Request {
-  params: {
-    alertId: string;
-    alertInstanceId: string;
-  };
-}
-
-export const unmuteAlertInstanceRoute = {
-  method: 'POST',
-  path: '/api/alert/{alertId}/alert_instance/{alertInstanceId}/_unmute',
-  config: {
-    tags: ['access:alerting-all'],
-    response: {
-      emptyStatusCode: 204,
-    },
-  },
-  async handler(request: UnmuteInstanceRequest, h: Hapi.ResponseToolkit) {
-    const alertsClient = request.getAlertsClient!();
-    await alertsClient.unmuteInstance(request.params);
-    return h.response();
-  },
-};
diff --git a/x-pack/legacy/plugins/alerting/server/routes/update.test.ts b/x-pack/legacy/plugins/alerting/server/routes/update.test.ts
deleted file mode 100644
index 83b70b505234f..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/update.test.ts
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { createMockServer } from './_mock_server';
-import { updateAlertRoute } from './update';
-
-const { server, alertsClient } = createMockServer();
-server.route(updateAlertRoute);
-
-beforeEach(() => jest.resetAllMocks());
-
-const mockedResponse = {
-  id: '1',
-  alertTypeId: '1',
-  tags: ['foo'],
-  schedule: { interval: '12s' },
-  params: {
-    otherField: false,
-  },
-  createdAt: new Date(),
-  updatedAt: new Date(),
-  actions: [
-    {
-      group: 'default',
-      id: '2',
-      actionTypeId: 'test',
-      params: {
-        baz: true,
-      },
-    },
-  ],
-};
-
-test('calls the update function with proper parameters', async () => {
-  const request = {
-    method: 'PUT',
-    url: '/api/alert/1',
-    payload: {
-      throttle: null,
-      name: 'abc',
-      tags: ['bar'],
-      schedule: { interval: '12s' },
-      params: {
-        otherField: false,
-      },
-      actions: [
-        {
-          group: 'default',
-          id: '2',
-          params: {
-            baz: true,
-          },
-        },
-      ],
-    },
-  };
-
-  alertsClient.update.mockResolvedValueOnce(mockedResponse);
-  const { payload, statusCode } = await server.inject(request);
-  expect(statusCode).toBe(200);
-  const { createdAt, updatedAt, ...response } = JSON.parse(payload);
-  expect({ createdAt: new Date(createdAt), updatedAt: new Date(updatedAt), ...response }).toEqual(
-    mockedResponse
-  );
-  expect(alertsClient.update).toHaveBeenCalledTimes(1);
-  expect(alertsClient.update.mock.calls[0]).toMatchInlineSnapshot(`
-    Array [
-      Object {
-        "data": Object {
-          "actions": Array [
-            Object {
-              "group": "default",
-              "id": "2",
-              "params": Object {
-                "baz": true,
-              },
-            },
-          ],
-          "name": "abc",
-          "params": Object {
-            "otherField": false,
-          },
-          "schedule": Object {
-            "interval": "12s",
-          },
-          "tags": Array [
-            "bar",
-          ],
-          "throttle": null,
-        },
-        "id": "1",
-      },
-    ]
-  `);
-});
diff --git a/x-pack/legacy/plugins/alerting/server/routes/update.ts b/x-pack/legacy/plugins/alerting/server/routes/update.ts
deleted file mode 100644
index bc55d48465602..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/update.ts
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import Joi from 'joi';
-import Hapi from 'hapi';
-import { getDurationSchema } from '../lib';
-import { IntervalSchedule } from '../types';
-
-interface UpdateRequest extends Hapi.Request {
-  params: {
-    id: string;
-  };
-  payload: {
-    alertTypeId: string;
-    name: string;
-    tags: string[];
-    schedule: IntervalSchedule;
-    actions: Array<{
-      group: string;
-      id: string;
-      params: Record<string, any>;
-    }>;
-    params: Record<string, any>;
-    throttle: string | null;
-  };
-}
-
-export const updateAlertRoute = {
-  method: 'PUT',
-  path: '/api/alert/{id}',
-  options: {
-    tags: ['access:alerting-all'],
-    validate: {
-      options: {
-        abortEarly: false,
-      },
-      payload: Joi.object()
-        .keys({
-          throttle: getDurationSchema()
-            .required()
-            .allow(null),
-          name: Joi.string().required(),
-          tags: Joi.array()
-            .items(Joi.string())
-            .required(),
-          schedule: Joi.object()
-            .keys({
-              interval: getDurationSchema().required(),
-            })
-            .required(),
-          params: Joi.object().required(),
-          actions: Joi.array()
-            .items(
-              Joi.object().keys({
-                group: Joi.string().required(),
-                id: Joi.string().required(),
-                params: Joi.object().required(),
-              })
-            )
-            .required(),
-        })
-        .required(),
-    },
-  },
-  async handler(request: UpdateRequest) {
-    const { id } = request.params;
-    const alertsClient = request.getAlertsClient!();
-    return await alertsClient.update({ id, data: request.payload });
-  },
-};
diff --git a/x-pack/legacy/plugins/alerting/server/routes/update_api_key.test.ts b/x-pack/legacy/plugins/alerting/server/routes/update_api_key.test.ts
deleted file mode 100644
index 9bfe26d56dabf..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/update_api_key.test.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { createMockServer } from './_mock_server';
-import { updateApiKeyRoute } from './update_api_key';
-
-const { server, alertsClient } = createMockServer();
-server.route(updateApiKeyRoute);
-
-test('updates api key for an alert', async () => {
-  const request = {
-    method: 'POST',
-    url: '/api/alert/1/_update_api_key',
-  };
-
-  const { statusCode } = await server.inject(request);
-  expect(statusCode).toBe(204);
-  expect(alertsClient.updateApiKey).toHaveBeenCalledWith({ id: '1' });
-});
diff --git a/x-pack/legacy/plugins/alerting/server/routes/update_api_key.ts b/x-pack/legacy/plugins/alerting/server/routes/update_api_key.ts
deleted file mode 100644
index 754ea453a27cb..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/routes/update_api_key.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import Hapi from 'hapi';
-
-export const updateApiKeyRoute = {
-  method: 'POST',
-  path: '/api/alert/{id}/_update_api_key',
-  config: {
-    tags: ['access:alerting-all'],
-    response: {
-      emptyStatusCode: 204,
-    },
-  },
-  async handler(request: Hapi.Request, h: Hapi.ResponseToolkit) {
-    const alertsClient = request.getAlertsClient!();
-    await alertsClient.updateApiKey({ id: request.params.id });
-    return h.response();
-  },
-};
diff --git a/x-pack/legacy/plugins/alerting/server/shim.ts b/x-pack/legacy/plugins/alerting/server/shim.ts
deleted file mode 100644
index bc8b0eb863634..0000000000000
--- a/x-pack/legacy/plugins/alerting/server/shim.ts
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import Hapi from 'hapi';
-import { Legacy } from 'kibana';
-import { LegacySpacesPlugin as SpacesPluginStartContract } from '../../spaces';
-import {
-  TaskManagerStartContract,
-  TaskManagerSetupContract,
-} from '../../../../plugins/task_manager/server';
-import { getTaskManagerSetup, getTaskManagerStart } from '../../task_manager/server';
-import { XPackMainPlugin } from '../../xpack_main/server/xpack_main';
-import KbnServer from '../../../../../src/legacy/server/kbn_server';
-import {
-  EncryptedSavedObjectsPluginSetup,
-  EncryptedSavedObjectsPluginStart,
-} from '../../../../plugins/encrypted_saved_objects/server';
-import { SecurityPluginSetup } from '../../../../plugins/security/server';
-import {
-  CoreSetup,
-  LoggerFactory,
-  SavedObjectsLegacyService,
-} from '../../../../../src/core/server';
-import {
-  ActionsPlugin,
-  PluginSetupContract as ActionsPluginSetupContract,
-  PluginStartContract as ActionsPluginStartContract,
-} from '../../../../plugins/actions/server';
-import { LicensingPluginSetup } from '../../../../plugins/licensing/server';
-
-// Extend PluginProperties to indicate which plugins are guaranteed to exist
-// due to being marked as dependencies
-interface Plugins extends Hapi.PluginProperties {
-  actions: ActionsPlugin;
-}
-
-export interface Server extends Legacy.Server {
-  plugins: Plugins;
-}
-
-/**
- * Shim what we're thinking setup and start contracts will look like
- */
-export type SecurityPluginSetupContract = Pick<SecurityPluginSetup, '__legacyCompat'>;
-export type SecurityPluginStartContract = Pick<SecurityPluginSetup, 'authc'>;
-export type XPackMainPluginSetupContract = Pick<XPackMainPlugin, 'registerFeature'>;
-
-/**
- * New platform interfaces
- */
-export interface AlertingPluginInitializerContext {
-  logger: LoggerFactory;
-}
-export interface AlertingCoreSetup {
-  elasticsearch: CoreSetup['elasticsearch'];
-  http: {
-    route: (route: Hapi.ServerRoute) => void;
-    basePath: {
-      serverBasePath: string;
-    };
-  };
-}
-export interface AlertingCoreStart {
-  savedObjects: SavedObjectsLegacyService;
-}
-export interface AlertingPluginsSetup {
-  security?: SecurityPluginSetupContract;
-  taskManager: TaskManagerSetupContract;
-  actions: ActionsPluginSetupContract;
-  xpack_main: XPackMainPluginSetupContract;
-  encryptedSavedObjects: EncryptedSavedObjectsPluginSetup;
-  licensing: LicensingPluginSetup;
-}
-export interface AlertingPluginsStart {
-  actions: ActionsPluginStartContract;
-  security?: SecurityPluginStartContract;
-  spaces: () => SpacesPluginStartContract | undefined;
-  encryptedSavedObjects: EncryptedSavedObjectsPluginStart;
-  taskManager: TaskManagerStartContract;
-}
-
-/**
- * Shim
- *
- * @param server Hapi server instance
- */
-export function shim(
-  server: Server
-): {
-  initializerContext: AlertingPluginInitializerContext;
-  coreSetup: AlertingCoreSetup;
-  coreStart: AlertingCoreStart;
-  pluginsSetup: AlertingPluginsSetup;
-  pluginsStart: AlertingPluginsStart;
-} {
-  const newPlatform = ((server as unknown) as KbnServer).newPlatform;
-
-  const initializerContext: AlertingPluginInitializerContext = {
-    logger: newPlatform.coreContext.logger,
-  };
-
-  const coreSetup: AlertingCoreSetup = {
-    elasticsearch: newPlatform.setup.core.elasticsearch,
-    http: {
-      route: server.route.bind(server),
-      basePath: newPlatform.setup.core.http.basePath,
-    },
-  };
-
-  const coreStart: AlertingCoreStart = {
-    savedObjects: server.savedObjects,
-  };
-
-  const pluginsSetup: AlertingPluginsSetup = {
-    security: newPlatform.setup.plugins.security as SecurityPluginSetupContract | undefined,
-    taskManager: getTaskManagerSetup(server)!,
-    actions: newPlatform.setup.plugins.actions as ActionsPluginSetupContract,
-    xpack_main: server.plugins.xpack_main,
-    encryptedSavedObjects: newPlatform.setup.plugins
-      .encryptedSavedObjects as EncryptedSavedObjectsPluginSetup,
-    licensing: newPlatform.setup.plugins.licensing as LicensingPluginSetup,
-  };
-
-  const pluginsStart: AlertingPluginsStart = {
-    security: newPlatform.setup.plugins.security as SecurityPluginStartContract | undefined,
-    actions: newPlatform.start.plugins.actions as ActionsPluginStartContract,
-    // TODO: Currently a function because it's an optional dependency that
-    // initializes after this function is called
-    spaces: () => server.plugins.spaces,
-    encryptedSavedObjects: newPlatform.start.plugins
-      .encryptedSavedObjects as EncryptedSavedObjectsPluginStart,
-    taskManager: getTaskManagerStart(server)!,
-  };
-
-  return {
-    initializerContext,
-    coreSetup,
-    coreStart,
-    pluginsSetup,
-    pluginsStart,
-  };
-}
diff --git a/x-pack/legacy/plugins/monitoring/index.ts b/x-pack/legacy/plugins/monitoring/index.ts
index 1186fde52dc46..3a23140104e16 100644
--- a/x-pack/legacy/plugins/monitoring/index.ts
+++ b/x-pack/legacy/plugins/monitoring/index.ts
@@ -108,15 +108,16 @@ export const monitoring = (kibana: LegacyPluginApi): LegacyPluginSpec => {
       };
 
       const legacyPlugins = plugins as Partial<typeof plugins> & { infra?: InfraPlugin };
-      const { xpack_main, elasticsearch, infra, alerting } = legacyPlugins;
+      const { xpack_main, elasticsearch, infra } = legacyPlugins;
       const {
         core: coreSetup,
-        plugins: { usageCollection, licensing },
+        plugins: { usageCollection, licensing, alerting },
       } = server.newPlatform.setup;
 
       const pluginsSetup: PluginsSetup = {
         usageCollection,
         licensing,
+        alerting,
       };
 
       const __LEGACY: LegacySetup = {
@@ -125,7 +126,6 @@ export const monitoring = (kibana: LegacyPluginApi): LegacyPluginSpec => {
           xpack_main,
           elasticsearch,
           infra,
-          alerting,
         },
       };
 
diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/status.tsx b/x-pack/legacy/plugins/monitoring/public/components/alerts/status.tsx
index 0ee0015ed39a7..c1019cb64be52 100644
--- a/x-pack/legacy/plugins/monitoring/public/components/alerts/status.tsx
+++ b/x-pack/legacy/plugins/monitoring/public/components/alerts/status.tsx
@@ -19,7 +19,7 @@ import {
 import { i18n } from '@kbn/i18n';
 import { FormattedMessage } from '@kbn/i18n/react';
 import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links';
-import { Alert } from '../../../../alerting/server/types';
+import { Alert } from '../../../../../../plugins/alerting/common';
 import { getSetupModeState, addSetupModeCallback, toggleSetupMode } from '../../lib/setup_mode';
 import { NUMBER_OF_MIGRATED_ALERTS, ALERT_TYPE_PREFIX } from '../../../common/constants';
 import { AlertsConfiguration } from './configuration';
diff --git a/x-pack/legacy/plugins/monitoring/server/alerts/license_expiration.test.ts b/x-pack/legacy/plugins/monitoring/server/alerts/license_expiration.test.ts
index 38b4e6c60ca48..1d52ab04dcdbd 100644
--- a/x-pack/legacy/plugins/monitoring/server/alerts/license_expiration.test.ts
+++ b/x-pack/legacy/plugins/monitoring/server/alerts/license_expiration.test.ts
@@ -11,9 +11,8 @@ import {
   MONITORING_CONFIG_ALERTING_EMAIL_ADDRESS,
 } from '../../common/constants';
 import { Logger } from 'src/core/server';
-import { AlertServices } from '../../../alerting/server/types';
+import { AlertServices, AlertInstance } from '../../../../../plugins/alerting/server';
 import { savedObjectsClientMock } from 'src/core/server/mocks';
-import { AlertInstance } from '../../../alerting/server/alert_instance';
 import {
   AlertState,
   AlertClusterState,
diff --git a/x-pack/legacy/plugins/monitoring/server/alerts/license_expiration.ts b/x-pack/legacy/plugins/monitoring/server/alerts/license_expiration.ts
index 8688a2b08efc4..f968e90f70b2d 100644
--- a/x-pack/legacy/plugins/monitoring/server/alerts/license_expiration.ts
+++ b/x-pack/legacy/plugins/monitoring/server/alerts/license_expiration.ts
@@ -10,7 +10,7 @@ import { Legacy } from 'kibana';
 import { Logger } from 'src/core/server';
 import { i18n } from '@kbn/i18n';
 import { ALERT_TYPE_LICENSE_EXPIRATION, INDEX_PATTERN_ELASTICSEARCH } from '../../common/constants';
-import { AlertType } from '../../../alerting';
+import { AlertType } from '../../../../../plugins/alerting/server';
 import { fetchLicenses } from '../lib/alerts/fetch_licenses';
 import { fetchDefaultEmailAddress } from '../lib/alerts/fetch_default_email_address';
 import { fetchClusters } from '../lib/alerts/fetch_clusters';
diff --git a/x-pack/legacy/plugins/monitoring/server/alerts/types.d.ts b/x-pack/legacy/plugins/monitoring/server/alerts/types.d.ts
index 6346ca00dabbd..76fc7074e411c 100644
--- a/x-pack/legacy/plugins/monitoring/server/alerts/types.d.ts
+++ b/x-pack/legacy/plugins/monitoring/server/alerts/types.d.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 import { Moment } from 'moment';
-import { AlertExecutorOptions } from '../../../alerting';
+import { AlertExecutorOptions } from '../../../../../plugins/alerting/server';
 
 export interface AlertLicense {
   status: string;
diff --git a/x-pack/legacy/plugins/monitoring/server/lib/alerts/license_expiration.lib.ts b/x-pack/legacy/plugins/monitoring/server/lib/alerts/license_expiration.lib.ts
index 8a75fc1fbbd82..520cb31e151ae 100644
--- a/x-pack/legacy/plugins/monitoring/server/lib/alerts/license_expiration.lib.ts
+++ b/x-pack/legacy/plugins/monitoring/server/lib/alerts/license_expiration.lib.ts
@@ -5,7 +5,7 @@
  */
 import { Moment } from 'moment-timezone';
 import { i18n } from '@kbn/i18n';
-import { AlertInstance } from '../../../../alerting/server/alert_instance';
+import { AlertInstance } from '../../../../../../plugins/alerting/server';
 import { AlertLicense } from '../../alerts/types';
 
 const RESOLVED_SUBJECT = i18n.translate(
diff --git a/x-pack/legacy/plugins/monitoring/server/plugin.js b/x-pack/legacy/plugins/monitoring/server/plugin.js
index 304d2c08a1688..3d6d110a01949 100644
--- a/x-pack/legacy/plugins/monitoring/server/plugin.js
+++ b/x-pack/legacy/plugins/monitoring/server/plugin.js
@@ -34,7 +34,7 @@ export class Plugin {
     } = __LEGACY;
     const config = monitoringConfig();
 
-    const { usageCollection, licensing } = pluginsSetup;
+    const { usageCollection, licensing, alerting } = pluginsSetup;
     registerMonitoringCollection();
     /*
      * Register collector objects for stats to show up in the APIs
@@ -152,7 +152,7 @@ export class Plugin {
       };
     });
 
-    if (KIBANA_ALERTING_ENABLED && plugins.alerting) {
+    if (KIBANA_ALERTING_ENABLED && alerting) {
       // this is not ready right away but we need to register alerts right away
       async function getMonitoringCluster() {
         const configs = config.get('xpack.monitoring.elasticsearch');
@@ -174,7 +174,7 @@ export class Plugin {
       function getLogger(contexts) {
         return logger.get('plugins', LOGGING_TAG, ...contexts);
       }
-      plugins.alerting.setup.registerType(
+      alerting.registerType(
         getLicenseExpiration(
           hapiServer,
           getMonitoringCluster,
diff --git a/x-pack/legacy/plugins/siem/index.ts b/x-pack/legacy/plugins/siem/index.ts
index c786dad61c09d..731ef10aa225e 100644
--- a/x-pack/legacy/plugins/siem/index.ts
+++ b/x-pack/legacy/plugins/siem/index.ts
@@ -152,7 +152,6 @@ export const siem = (kibana: any) => {
       const initializerContext = { ...coreContext, env };
       const __legacy = {
         config: server.config,
-        alerting: server.plugins.alerting,
         route: server.route.bind(server),
       };
 
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/README.md b/x-pack/legacy/plugins/siem/server/lib/detection_engine/README.md
index 1d33466a458d2..1e8e3d5e3dd75 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/README.md
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/README.md
@@ -152,8 +152,8 @@ logging.events:
 ```
 
 See these two README.md's pages for more references on the alerting and actions API:
-https://github.com/elastic/kibana/blob/master/x-pack/legacy/plugins/alerting/README.md
-https://github.com/elastic/kibana/tree/master/x-pack/legacy/plugins/actions
+https://github.com/elastic/kibana/blob/master/x-pack/plugins/alerting/README.md
+https://github.com/elastic/kibana/tree/master/x-pack/plugins/actions
 
 ### Signals API
 
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/clients_service_mock.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/clients_service_mock.ts
index f89e938b8a636..29131429d12cb 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/clients_service_mock.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/clients_service_mock.ts
@@ -8,7 +8,7 @@ import {
   elasticsearchServiceMock,
   savedObjectsClientMock,
 } from '../../../../../../../../../src/core/server/mocks';
-import { alertsClientMock } from '../../../../../../alerting/server/alerts_client.mock';
+import { alertsClientMock } from '../../../../../../../../plugins/alerting/server/mocks';
 import { ActionsClient } from '../../../../../../../../plugins/actions/server';
 import { actionsClientMock } from '../../../../../../../../plugins/actions/server/mocks';
 import { GetScopedClients } from '../../../../services';
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.ts
index 61f2e87811509..c8205859407c0 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { Alert } from '../../../../../alerting/common';
+import { Alert } from '../../../../../../../plugins/alerting/common';
 import { APP_ID, SIGNALS_ID } from '../../../../common/constants';
 import { CreateRuleParams } from './types';
 import { addTags } from './add_tags';
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/find_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/find_rules.ts
index e193e123f4281..f333a7c340705 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/find_rules.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/find_rules.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { FindResult } from '../../../../../alerting/server/alerts_client';
+import { FindResult } from '../../../../../../../plugins/alerting/server';
 import { SIGNALS_ID } from '../../../../common/constants';
 import { FindRuleParams } from './types';
 
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.test.ts
index 25bac383ecf72..9774d10a37d6f 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.test.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.test.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { alertsClientMock } from '../../../../../alerting/server/alerts_client.mock';
+import { alertsClientMock } from '../../../../../../../plugins/alerting/server/mocks';
 import {
   getResult,
   getFindResultWithSingleHit,
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.ts
index a48957da7aa94..b5e826ed42723 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.ts
@@ -5,7 +5,7 @@
  */
 
 import { INTERNAL_IMMUTABLE_KEY } from '../../../../common/constants';
-import { AlertsClient } from '../../../../../alerting';
+import { AlertsClient } from '../../../../../../../plugins/alerting/server';
 import { RuleAlertType, isAlertTypes } from './types';
 import { findRules } from './find_rules';
 
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts
index 35d3489dad6fc..304f9a741c6f4 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts
@@ -4,12 +4,12 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { alertsClientMock } from '../../../../../alerting/server/alerts_client.mock';
 import {
   getResult,
   getFindResultWithSingleHit,
   FindHit,
 } from '../routes/__mocks__/request_responses';
+import { alertsClientMock } from '../../../../../../../plugins/alerting/server/mocks';
 import { getExportAll } from './get_export_all';
 
 describe('getExportAll', () => {
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.ts
index dca6eba4e6556..434919f80e149 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { AlertsClient } from '../../../../../alerting';
+import { AlertsClient } from '../../../../../../../plugins/alerting/server';
 import { getNonPackagedRules } from './get_existing_prepackaged_rules';
 import { getExportDetailsNdjson } from './get_export_details_ndjson';
 import { transformAlertsToRules, transformRulesToNdjson } from '../routes/rules/utils';
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts
index 4b6ea527a2027..98f5df4852530 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts
@@ -4,13 +4,13 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { alertsClientMock } from '../../../../../alerting/server/alerts_client.mock';
 import { getExportByObjectIds, getRulesFromObjects, RulesErrors } from './get_export_by_object_ids';
 import {
   getResult,
   getFindResultWithSingleHit,
   FindHit,
 } from '../routes/__mocks__/request_responses';
+import { alertsClientMock } from '../../../../../../../plugins/alerting/server/mocks';
 
 describe('get_export_by_object_ids', () => {
   describe('getExportByObjectIds', () => {
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.ts
index 7e0d61d040617..e3b38a879fc3d 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { AlertsClient } from '../../../../../alerting';
+import { AlertsClient } from '../../../../../../../plugins/alerting/server';
 import { getExportDetailsNdjson } from './get_export_details_ndjson';
 import { isAlertType } from '../rules/types';
 import { readRules } from './read_rules';
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts
index 07e8c6940e747..3d9ec128963f6 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts
@@ -4,9 +4,9 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+import { Alert } from '../../../../../../../plugins/alerting/common';
 import { ActionsClient } from '../../../../../../../plugins/actions/server';
-import { AlertsClient } from '../../../../../alerting';
-import { Alert } from '../../../../../alerting/server/types';
+import { AlertsClient } from '../../../../../../../plugins/alerting/server';
 import { createRules } from './create_rules';
 import { PrepackagedRules } from '../types';
 
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts
index f560b67cdc587..1d904b2b349ae 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts
@@ -5,7 +5,7 @@
  */
 
 import { defaults } from 'lodash/fp';
-import { PartialAlert } from '../../../../../alerting/server/types';
+import { PartialAlert } from '../../../../../../../plugins/alerting/server';
 import { readRules } from './read_rules';
 import { PatchRuleParams, IRuleSavedAttributesSavedObjectAttributes } from './types';
 import { addTags } from './add_tags';
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.test.ts
index c637860c5646a..45507a69f50c2 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.test.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.test.ts
@@ -4,8 +4,8 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { alertsClientMock } from '../../../../../alerting/server/alerts_client.mock';
 import { readRules } from './read_rules';
+import { alertsClientMock } from '../../../../../../../plugins/alerting/server/mocks';
 import { getResult, getFindResultWithSingleHit } from '../routes/__mocks__/request_responses';
 
 describe('read_rules', () => {
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.ts
index e8e883701c6a9..cbe6dbda8449f 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { SanitizedAlert } from '../../../../../alerting/common';
+import { SanitizedAlert } from '../../../../../../../plugins/alerting/common';
 import { INTERNAL_RULE_ID_KEY } from '../../../../common/constants';
 import { findRules } from './find_rules';
 import { ReadRuleParams, isAlertType } from './types';
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts
index 8579447a74c69..fa22765c143e1 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts
@@ -13,12 +13,12 @@ import {
   SavedObjectsFindResponse,
   SavedObjectsClientContract,
 } from 'kibana/server';
+import { AlertsClient } from '../../../../../../../plugins/alerting/server';
+import { Alert } from '../../../../../../../plugins/alerting/common';
 import { SIGNALS_ID } from '../../../../common/constants';
 import { LegacyRequest } from '../../../types';
-import { AlertsClient } from '../../../../../alerting/server';
 import { ActionsClient } from '../../../../../../../plugins/actions/server';
 import { RuleAlertParams, RuleTypeParams, RuleAlertParamsRest } from '../types';
-import { Alert } from '../../../../../alerting/server/types';
 
 export type PatchRuleAlertParamsRest = Partial<RuleAlertParamsRest> & {
   id: string | undefined;
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.ts
index 2fa903f3d713f..c63237c93daf4 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.ts
@@ -6,7 +6,7 @@
 
 import { SavedObjectsClientContract } from 'kibana/server';
 import { ActionsClient } from '../../../../../../../plugins/actions/server';
-import { AlertsClient } from '../../../../../alerting';
+import { AlertsClient } from '../../../../../../../plugins/alerting/server';
 import { patchRules } from './patch_rules';
 import { PrepackagedRules } from '../types';
 
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.ts
index 1dc5d8429fab8..9ead8313b2c91 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { PartialAlert } from '../../../../../alerting/server/types';
+import { PartialAlert } from '../../../../../../../plugins/alerting/server';
 import { readRules } from './read_rules';
 import { IRuleSavedAttributesSavedObjectAttributes, UpdateRuleParams } from './types';
 import { addTags } from './add_tags';
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_alert_instances.sh b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_alert_instances.sh
index f42d4a52594a7..b5f272d0a8a09 100755
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_alert_instances.sh
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_alert_instances.sh
@@ -10,7 +10,7 @@ set -e
 ./check_env_variables.sh
 
 # Example: ./get_alert_instances.sh
-# https://github.com/elastic/kibana/blob/master/x-pack/legacy/plugins/alerting/README.md#get-apialert_find-find-alerts
+# https://github.com/elastic/kibana/blob/master/x-pack/plugins/alerting/README.md#get-apialert_find-find-alerts
 curl -s -k \
   -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
   -X GET ${KIBANA_URL}${SPACE_URL}/api/alert/_find \
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_alert_types.sh b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_alert_types.sh
index a7c6fa567ecdd..28c250e9368a6 100755
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_alert_types.sh
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_alert_types.sh
@@ -10,7 +10,7 @@ set -e
 ./check_env_variables.sh
 
 # Example: ./get_alert_types.sh
-# https://github.com/elastic/kibana/blob/master/x-pack/legacy/plugins/alerting/README.md#get-apialerttypes-list-alert-types
+# https://github.com/elastic/kibana/blob/master/x-pack/plugins/alerting/README.md#get-apialerttypes-list-alert-types
 curl -s -k \
   -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
   -X GET ${KIBANA_URL}${SPACE_URL}/api/alert/types \
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.test.ts
index 534215f5a1228..b49f43ce9e7ac 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.test.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.test.ts
@@ -6,8 +6,8 @@
 
 import { getQueryFilter, getFilter } from './get_filter';
 import { savedObjectsClientMock } from 'src/core/server/mocks';
-import { AlertServices } from '../../../../../alerting/server/types';
 import { PartialFilter } from '../types';
+import { AlertServices } from '../../../../../../../plugins/alerting/server';
 
 describe('get_filter', () => {
   let savedObjectsClient = savedObjectsClientMock.create();
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.ts
index cb6b8fc75f610..bcf091544e52a 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { AlertServices } from '../../../../../alerting/server/types';
+import { AlertServices } from '../../../../../../../plugins/alerting/server';
 import { assertUnreachable } from '../../../utils/build_query';
 import {
   Filter,
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.test.ts
index bd7ba915af9b0..18286dc7754e0 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.test.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.test.ts
@@ -6,9 +6,9 @@
 
 import { savedObjectsClientMock } from 'src/core/server/mocks';
 import { DEFAULT_INDEX_KEY } from '../../../../common/constants';
-import { AlertServices } from '../../../../../alerting/server/types';
 import { getInputIndex } from './get_input_output_index';
 import { defaultIndexPattern } from '../../../../default_index_pattern';
+import { AlertServices } from '../../../../../../../plugins/alerting/server';
 
 describe('get_input_output_index', () => {
   let savedObjectsClient = savedObjectsClientMock.create();
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.ts
index 624e012717820..29d4d2182bd53 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.ts
@@ -4,8 +4,8 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+import { AlertServices } from '../../../../../../../plugins/alerting/server';
 import { defaultIndexPattern } from '../../../../default_index_pattern';
-import { AlertServices } from '../../../../../alerting/server/types';
 import { DEFAULT_INDEX_KEY } from '../../../../common/constants';
 
 export const getInputIndex = async (
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.ts
index 8c8cef5dd3669..1cfd2f812a195 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.ts
@@ -4,8 +4,8 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+import { AlertServices } from '../../../../../../../plugins/alerting/server';
 import { RuleTypeParams } from '../types';
-import { AlertServices } from '../../../../../alerting/server/types';
 import { Logger } from '../../../../../../../../src/core/server';
 import { singleSearchAfter } from './single_search_after';
 import { singleBulkCreate } from './single_bulk_create';
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.ts
index adc7919a09758..7d6d6d99fa422 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.ts
@@ -6,7 +6,7 @@
 
 import { countBy, isEmpty } from 'lodash';
 import { performance } from 'perf_hooks';
-import { AlertServices } from '../../../../../alerting/server/types';
+import { AlertServices } from '../../../../../../../plugins/alerting/server';
 import { SignalSearchResponse, BulkResponse } from './types';
 import { RuleTypeParams } from '../types';
 import { generateId } from './utils';
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_search_after.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_search_after.ts
index 3a99500cb3433..a0e7047ad1cd6 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_search_after.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_search_after.ts
@@ -4,8 +4,8 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+import { AlertServices } from '../../../../../../../plugins/alerting/server';
 import { RuleTypeParams } from '../types';
-import { AlertServices } from '../../../../../alerting/server/types';
 import { Logger } from '../../../../../../../../src/core/server';
 import { SignalSearchResponse } from './types';
 import { buildEventsSearchQuery } from './build_events_query';
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts
index e9159ab87a0c0..d8b7dd72b6a86 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts
@@ -7,7 +7,11 @@
 import { RuleAlertParams, OutputRuleAlertRest } from '../types';
 import { SearchResponse } from '../../types';
 import { LegacyRequest } from '../../../types';
-import { AlertType, State, AlertExecutorOptions } from '../../../../../alerting/server/types';
+import {
+  AlertType,
+  State,
+  AlertExecutorOptions,
+} from '../../../../../../../plugins/alerting/server';
 
 export interface SignalsParams {
   signalIds: string[] | undefined | null;
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/utils.ts
index 940ea8be2ac36..016aed9fabcd6 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/utils.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/utils.ts
@@ -6,8 +6,7 @@
 import { createHash } from 'crypto';
 import moment from 'moment';
 import dateMath from '@elastic/datemath';
-
-import { parseDuration } from '../../../../../alerting/server/lib';
+import { parseDuration } from '../../../../../../../plugins/alerting/server';
 
 export const generateId = (
   docIndex: string,
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/tags/read_tags.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/tags/read_tags.test.ts
index 940011924de79..80c107c991bb7 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/tags/read_tags.test.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/tags/read_tags.test.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { alertsClientMock } from '../../../../../alerting/server/alerts_client.mock';
+import { alertsClientMock } from '../../../../../../../plugins/alerting/server/mocks';
 import { getResult, getFindResultWithMultiHits } from '../routes/__mocks__/request_responses';
 import { INTERNAL_RULE_ID_KEY, INTERNAL_IDENTIFIER } from '../../../../common/constants';
 import { readRawTags, readTags, convertTagsToSet, convertToTags, isTags } from './read_tags';
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/tags/read_tags.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/tags/read_tags.ts
index 02456732df3b4..d343bca8c97bb 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/tags/read_tags.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/tags/read_tags.ts
@@ -6,7 +6,7 @@
 
 import { has } from 'lodash/fp';
 import { INTERNAL_IDENTIFIER } from '../../../../common/constants';
-import { AlertsClient } from '../../../../../alerting';
+import { AlertsClient } from '../../../../../../../plugins/alerting/server';
 import { findRules } from '../rules/find_rules';
 
 export interface TagType {
diff --git a/x-pack/legacy/plugins/siem/server/plugin.ts b/x-pack/legacy/plugins/siem/server/plugin.ts
index e15248e5200ee..6f28fd7d67bd0 100644
--- a/x-pack/legacy/plugins/siem/server/plugin.ts
+++ b/x-pack/legacy/plugins/siem/server/plugin.ts
@@ -6,6 +6,10 @@
 
 import { i18n } from '@kbn/i18n';
 
+import {
+  PluginStartContract as AlertingStart,
+  PluginSetupContract as AlertingSetup,
+} from '../../../../plugins/alerting/server';
 import {
   CoreSetup,
   CoreStart,
@@ -38,10 +42,12 @@ export interface SetupPlugins {
   features: FeaturesSetup;
   security: SecuritySetup;
   spaces?: SpacesSetup;
+  alerting: AlertingSetup;
 }
 
 export interface StartPlugins {
   actions: ActionsStart;
+  alerting: AlertingStart;
 }
 
 export class Plugin {
@@ -130,13 +136,13 @@ export class Plugin {
       },
     });
 
-    if (__legacy.alerting != null) {
+    if (plugins.alerting != null) {
       const type = signalRulesAlertType({
         logger: this.logger,
         version: this.context.env.packageInfo.version,
       });
       if (isAlertExecutor(type)) {
-        __legacy.alerting.setup.registerType(type);
+        plugins.alerting.registerType(type);
       }
     }
 
@@ -145,7 +151,7 @@ export class Plugin {
   }
 
   public start(core: CoreStart, plugins: StartPlugins) {
-    this.clients.start(core.savedObjects, plugins.actions);
+    this.clients.start(core.savedObjects, plugins.actions, plugins.alerting);
 
     this.legacyInitRoutes!(this.clients.createGetScoped());
   }
diff --git a/x-pack/legacy/plugins/siem/server/services/clients.test.ts b/x-pack/legacy/plugins/siem/server/services/clients.test.ts
index 7f63a8f5e949c..f76494d075f08 100644
--- a/x-pack/legacy/plugins/siem/server/services/clients.test.ts
+++ b/x-pack/legacy/plugins/siem/server/services/clients.test.ts
@@ -6,6 +6,7 @@
 
 import { coreMock, httpServerMock } from '../../../../../../src/core/server/mocks';
 import { actionsMock } from '../../../../../plugins/actions/server/mocks';
+import { alertsMock } from '../../../../../plugins/alerting/server/mocks';
 
 import { ClientsService } from './clients';
 
@@ -16,13 +17,14 @@ describe('ClientsService', () => {
         const clients = new ClientsService();
 
         const actions = actionsMock.createStart();
+        const alerting = alertsMock.createStart();
         const { elasticsearch } = coreMock.createSetup();
         const { savedObjects } = coreMock.createStart();
         const request = httpServerMock.createRawRequest();
         const spacesService = undefined;
 
         clients.setup(elasticsearch.dataClient, spacesService);
-        clients.start(savedObjects, actions);
+        clients.start(savedObjects, actions, alerting);
 
         const { spacesClient } = await clients.createGetScoped()(request);
         expect(spacesClient.getSpaceId()).toEqual('default');
diff --git a/x-pack/legacy/plugins/siem/server/services/clients.ts b/x-pack/legacy/plugins/siem/server/services/clients.ts
index ca50eda4e7a6c..7ba7230f88493 100644
--- a/x-pack/legacy/plugins/siem/server/services/clients.ts
+++ b/x-pack/legacy/plugins/siem/server/services/clients.ts
@@ -12,23 +12,23 @@ import {
   SavedObjectsClientContract,
 } from '../../../../../../src/core/server';
 import { ActionsClient } from '../../../../../plugins/actions/server';
-import { AlertsClient } from '../../../../../legacy/plugins/alerting/server';
+import { AlertsClient } from '../../../../../plugins/alerting/server';
 import { SpacesServiceSetup } from '../../../../../plugins/spaces/server';
 import { CoreStart, StartPlugins } from '../plugin';
 
 export interface Clients {
   actionsClient?: ActionsClient;
+  alertsClient?: AlertsClient;
   clusterClient: IScopedClusterClient;
   spacesClient: { getSpaceId: () => string };
   savedObjectsClient: SavedObjectsClientContract;
 }
-interface LegacyClients {
-  alertsClient?: AlertsClient;
-}
-export type GetScopedClients = (request: LegacyRequest) => Promise<Clients & LegacyClients>;
+
+export type GetScopedClients = (request: LegacyRequest) => Promise<Clients>;
 
 export class ClientsService {
   private actions?: StartPlugins['actions'];
+  private alerting?: StartPlugins['alerting'];
   private clusterClient?: IClusterClient;
   private savedObjects?: CoreStart['savedObjects'];
   private spacesService?: SpacesServiceSetup;
@@ -38,9 +38,14 @@ export class ClientsService {
     this.spacesService = spacesService;
   }
 
-  public start(savedObjects: CoreStart['savedObjects'], actions: StartPlugins['actions']) {
+  public start(
+    savedObjects: CoreStart['savedObjects'],
+    actions: StartPlugins['actions'],
+    alerting: StartPlugins['alerting']
+  ) {
     this.savedObjects = savedObjects;
     this.actions = actions;
+    this.alerting = alerting;
   }
 
   public createGetScoped(): GetScopedClients {
@@ -52,7 +57,7 @@ export class ClientsService {
       const kibanaRequest = KibanaRequest.from(request);
 
       return {
-        alertsClient: request.getAlertsClient?.(),
+        alertsClient: await this.alerting?.getAlertsClientWithRequest?.(kibanaRequest),
         actionsClient: await this.actions?.getActionsClientWithRequest?.(kibanaRequest),
         clusterClient: this.clusterClient!.asScoped(kibanaRequest),
         savedObjectsClient: this.savedObjects!.getScopedClient(kibanaRequest),
diff --git a/x-pack/plugins/actions/server/routes/find.ts b/x-pack/plugins/actions/server/routes/find.ts
index 04656d19bfeb4..e791aff4fb598 100644
--- a/x-pack/plugins/actions/server/routes/find.ts
+++ b/x-pack/plugins/actions/server/routes/find.ts
@@ -12,7 +12,7 @@ import {
   IKibanaResponse,
   KibanaResponseFactory,
 } from 'kibana/server';
-import { FindOptions } from '../../../../legacy/plugins/alerting/server/alerts_client';
+import { FindOptions } from '../../../alerting/server';
 import { LicenseState } from '../lib/license_state';
 import { verifyApiAccess } from '../lib/license_api_access';
 
diff --git a/x-pack/legacy/plugins/alerting/README.md b/x-pack/plugins/alerting/README.md
similarity index 99%
rename from x-pack/legacy/plugins/alerting/README.md
rename to x-pack/plugins/alerting/README.md
index 2a10c41f12b85..32ca804198ebd 100644
--- a/x-pack/legacy/plugins/alerting/README.md
+++ b/x-pack/plugins/alerting/README.md
@@ -77,7 +77,7 @@ Note that the `manage_own_api_key` cluster privilege is not enough - it can be u
 
 ### Methods
 
-**server.plugins.alerting.setup.registerType(options)**
+**server.newPlatform.setup.plugins.alerting.registerType(options)**
 
 The following table describes the properties of the `options` object.
 
@@ -119,7 +119,7 @@ This example receives server and threshold as parameters. It will read the CPU u
 ```
 import { schema } from '@kbn/config-schema';
 ...
-server.plugins.alerting.setup.registerType({
+server.newPlatform.setup.plugins.alerting.registerType({
 	id: 'my-alert-type',
 	name: 'My alert type',
 	validate: {
@@ -178,7 +178,7 @@ server.plugins.alerting.setup.registerType({
 This example only receives threshold as a parameter. It will read the CPU usage of all the servers and schedule individual actions if the reading for a server is greater than the threshold. This is a better implementation than above as only one query is performed for all the servers instead of one query per server.
 
 ```
-server.plugins.alerting.setup.registerType({
+server.newPlatform.setup.plugins.alerting.registerType({
 	id: 'my-alert-type',
 	name: 'My alert type',
 	validate: {
diff --git a/x-pack/legacy/plugins/alerting/common/alert.ts b/x-pack/plugins/alerting/common/alert.ts
similarity index 100%
rename from x-pack/legacy/plugins/alerting/common/alert.ts
rename to x-pack/plugins/alerting/common/alert.ts
diff --git a/x-pack/legacy/plugins/alerting/common/alert_instance.ts b/x-pack/plugins/alerting/common/alert_instance.ts
similarity index 100%
rename from x-pack/legacy/plugins/alerting/common/alert_instance.ts
rename to x-pack/plugins/alerting/common/alert_instance.ts
diff --git a/x-pack/legacy/plugins/alerting/common/alert_task_instance.ts b/x-pack/plugins/alerting/common/alert_task_instance.ts
similarity index 100%
rename from x-pack/legacy/plugins/alerting/common/alert_task_instance.ts
rename to x-pack/plugins/alerting/common/alert_task_instance.ts
diff --git a/x-pack/legacy/plugins/alerting/common/date_from_string.test.ts b/x-pack/plugins/alerting/common/date_from_string.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/alerting/common/date_from_string.test.ts
rename to x-pack/plugins/alerting/common/date_from_string.test.ts
diff --git a/x-pack/legacy/plugins/alerting/common/date_from_string.ts b/x-pack/plugins/alerting/common/date_from_string.ts
similarity index 100%
rename from x-pack/legacy/plugins/alerting/common/date_from_string.ts
rename to x-pack/plugins/alerting/common/date_from_string.ts
diff --git a/x-pack/legacy/plugins/alerting/common/index.ts b/x-pack/plugins/alerting/common/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/alerting/common/index.ts
rename to x-pack/plugins/alerting/common/index.ts
diff --git a/x-pack/plugins/alerting/kibana.json b/x-pack/plugins/alerting/kibana.json
new file mode 100644
index 0000000000000..fb5003ede48ce
--- /dev/null
+++ b/x-pack/plugins/alerting/kibana.json
@@ -0,0 +1,10 @@
+{
+  "id": "alerting",
+  "server": true,
+  "version": "8.0.0",
+  "kibanaVersion": "kibana",
+  "configPath": ["xpack", "alerting"],
+  "requiredPlugins": ["licensing", "taskManager", "encryptedSavedObjects", "actions"],
+  "optionalPlugins": ["spaces", "security"],
+  "ui": false
+}
\ No newline at end of file
diff --git a/x-pack/legacy/plugins/alerting/server/alert_instance/alert_instance.test.ts b/x-pack/plugins/alerting/server/alert_instance/alert_instance.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/alerting/server/alert_instance/alert_instance.test.ts
rename to x-pack/plugins/alerting/server/alert_instance/alert_instance.test.ts
diff --git a/x-pack/legacy/plugins/alerting/server/alert_instance/alert_instance.ts b/x-pack/plugins/alerting/server/alert_instance/alert_instance.ts
similarity index 100%
rename from x-pack/legacy/plugins/alerting/server/alert_instance/alert_instance.ts
rename to x-pack/plugins/alerting/server/alert_instance/alert_instance.ts
diff --git a/x-pack/legacy/plugins/alerting/server/alert_instance/create_alert_instance_factory.test.ts b/x-pack/plugins/alerting/server/alert_instance/create_alert_instance_factory.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/alerting/server/alert_instance/create_alert_instance_factory.test.ts
rename to x-pack/plugins/alerting/server/alert_instance/create_alert_instance_factory.test.ts
diff --git a/x-pack/legacy/plugins/alerting/server/alert_instance/create_alert_instance_factory.ts b/x-pack/plugins/alerting/server/alert_instance/create_alert_instance_factory.ts
similarity index 100%
rename from x-pack/legacy/plugins/alerting/server/alert_instance/create_alert_instance_factory.ts
rename to x-pack/plugins/alerting/server/alert_instance/create_alert_instance_factory.ts
diff --git a/x-pack/legacy/plugins/alerting/server/alert_instance/index.ts b/x-pack/plugins/alerting/server/alert_instance/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/alerting/server/alert_instance/index.ts
rename to x-pack/plugins/alerting/server/alert_instance/index.ts
diff --git a/x-pack/legacy/plugins/alerting/server/alert_type_registry.mock.ts b/x-pack/plugins/alerting/server/alert_type_registry.mock.ts
similarity index 100%
rename from x-pack/legacy/plugins/alerting/server/alert_type_registry.mock.ts
rename to x-pack/plugins/alerting/server/alert_type_registry.mock.ts
diff --git a/x-pack/legacy/plugins/alerting/server/alert_type_registry.test.ts b/x-pack/plugins/alerting/server/alert_type_registry.test.ts
similarity index 97%
rename from x-pack/legacy/plugins/alerting/server/alert_type_registry.test.ts
rename to x-pack/plugins/alerting/server/alert_type_registry.test.ts
index 976bed884cd43..1a820d55af8a4 100644
--- a/x-pack/legacy/plugins/alerting/server/alert_type_registry.test.ts
+++ b/x-pack/plugins/alerting/server/alert_type_registry.test.ts
@@ -6,7 +6,7 @@
 
 import { TaskRunnerFactory } from './task_runner';
 import { AlertTypeRegistry } from './alert_type_registry';
-import { taskManagerMock } from '../../../../plugins/task_manager/server/task_manager.mock';
+import { taskManagerMock } from '../../../plugins/task_manager/server/task_manager.mock';
 
 const taskManager = taskManagerMock.setup();
 const alertTypeRegistryParams = {
diff --git a/x-pack/legacy/plugins/alerting/server/alert_type_registry.ts b/x-pack/plugins/alerting/server/alert_type_registry.ts
similarity index 95%
rename from x-pack/legacy/plugins/alerting/server/alert_type_registry.ts
rename to x-pack/plugins/alerting/server/alert_type_registry.ts
index 8c9844d935163..b0976d67c70a0 100644
--- a/x-pack/legacy/plugins/alerting/server/alert_type_registry.ts
+++ b/x-pack/plugins/alerting/server/alert_type_registry.ts
@@ -6,7 +6,7 @@
 
 import Boom from 'boom';
 import { i18n } from '@kbn/i18n';
-import { RunContext, TaskManagerSetupContract } from '../../../../plugins/task_manager/server';
+import { RunContext, TaskManagerSetupContract } from '../../../plugins/task_manager/server';
 import { TaskRunnerFactory } from './task_runner';
 import { AlertType } from './types';
 
diff --git a/x-pack/legacy/plugins/alerting/server/alerts_client.mock.ts b/x-pack/plugins/alerting/server/alerts_client.mock.ts
similarity index 100%
rename from x-pack/legacy/plugins/alerting/server/alerts_client.mock.ts
rename to x-pack/plugins/alerting/server/alerts_client.mock.ts
diff --git a/x-pack/legacy/plugins/alerting/server/alerts_client.test.ts b/x-pack/plugins/alerting/server/alerts_client.test.ts
similarity index 99%
rename from x-pack/legacy/plugins/alerting/server/alerts_client.test.ts
rename to x-pack/plugins/alerting/server/alerts_client.test.ts
index 1555a0537158a..629bd05a8c76a 100644
--- a/x-pack/legacy/plugins/alerting/server/alerts_client.test.ts
+++ b/x-pack/plugins/alerting/server/alerts_client.test.ts
@@ -6,13 +6,13 @@
 import uuid from 'uuid';
 import { schema } from '@kbn/config-schema';
 import { AlertsClient } from './alerts_client';
-import { savedObjectsClientMock, loggingServiceMock } from '../../../../../src/core/server/mocks';
-import { taskManagerMock } from '../../../../plugins/task_manager/server/task_manager.mock';
+import { savedObjectsClientMock, loggingServiceMock } from '../../../../src/core/server/mocks';
+import { taskManagerMock } from '../../../plugins/task_manager/server/task_manager.mock';
 import { alertTypeRegistryMock } from './alert_type_registry.mock';
-import { TaskStatus } from '../../../../plugins/task_manager/server';
+import { TaskStatus } from '../../../plugins/task_manager/server';
 import { IntervalSchedule } from './types';
 import { resolvable } from './test_utils';
-import { encryptedSavedObjectsMock } from '../../../../plugins/encrypted_saved_objects/server/mocks';
+import { encryptedSavedObjectsMock } from '../../../plugins/encrypted_saved_objects/server/mocks';
 
 const taskManager = taskManagerMock.start();
 const alertTypeRegistry = alertTypeRegistryMock.create();
diff --git a/x-pack/legacy/plugins/alerting/server/alerts_client.ts b/x-pack/plugins/alerting/server/alerts_client.ts
similarity index 98%
rename from x-pack/legacy/plugins/alerting/server/alerts_client.ts
rename to x-pack/plugins/alerting/server/alerts_client.ts
index eef6f662a20a2..ad308f819da34 100644
--- a/x-pack/legacy/plugins/alerting/server/alerts_client.ts
+++ b/x-pack/plugins/alerting/server/alerts_client.ts
@@ -29,9 +29,9 @@ import {
   InvalidateAPIKeyParams,
   CreateAPIKeyResult as SecurityPluginCreateAPIKeyResult,
   InvalidateAPIKeyResult as SecurityPluginInvalidateAPIKeyResult,
-} from '../../../../plugins/security/server';
-import { EncryptedSavedObjectsPluginStart } from '../../../../plugins/encrypted_saved_objects/server';
-import { TaskManagerStartContract } from '../../../../plugins/task_manager/server';
+} from '../../../plugins/security/server';
+import { EncryptedSavedObjectsPluginStart } from '../../../plugins/encrypted_saved_objects/server';
+import { TaskManagerStartContract } from '../../../plugins/task_manager/server';
 import { taskInstanceToAlertTaskInstance } from './task_runner/alert_task_instance';
 
 type NormalizedAlertAction = Omit<AlertAction, 'actionTypeId'>;
diff --git a/x-pack/legacy/plugins/alerting/server/alerts_client_factory.test.ts b/x-pack/plugins/alerting/server/alerts_client_factory.test.ts
similarity index 72%
rename from x-pack/legacy/plugins/alerting/server/alerts_client_factory.test.ts
rename to x-pack/plugins/alerting/server/alerts_client_factory.test.ts
index 14c685237bf92..b0558ef1ea98c 100644
--- a/x-pack/legacy/plugins/alerting/server/alerts_client_factory.test.ts
+++ b/x-pack/plugins/alerting/server/alerts_client_factory.test.ts
@@ -5,23 +5,23 @@
  */
 
 import { Request } from 'hapi';
-import { AlertsClientFactory, ConstructorOpts } from './alerts_client_factory';
+import { AlertsClientFactory, AlertsClientFactoryOpts } from './alerts_client_factory';
 import { alertTypeRegistryMock } from './alert_type_registry.mock';
-import { taskManagerMock } from '../../../../plugins/task_manager/server/task_manager.mock';
-import { KibanaRequest } from '../../../../../src/core/server';
-import { loggingServiceMock } from '../../../../../src/core/server/mocks';
-import { encryptedSavedObjectsMock } from '../../../../plugins/encrypted_saved_objects/server/mocks';
+import { taskManagerMock } from '../../../plugins/task_manager/server/task_manager.mock';
+import { KibanaRequest } from '../../../../src/core/server';
+import { loggingServiceMock, savedObjectsClientMock } from '../../../../src/core/server/mocks';
+import { encryptedSavedObjectsMock } from '../../../plugins/encrypted_saved_objects/server/mocks';
 
 jest.mock('./alerts_client');
 
-const savedObjectsClient = jest.fn();
+const savedObjectsClient = savedObjectsClientMock.create();
 const securityPluginSetup = {
   authc: {
     createAPIKey: jest.fn(),
     getCurrentUser: jest.fn(),
   },
 };
-const alertsClientFactoryParams: jest.Mocked<ConstructorOpts> = {
+const alertsClientFactoryParams: jest.Mocked<AlertsClientFactoryOpts> = {
   logger: loggingServiceMock.create().get(),
   taskManager: taskManagerMock.start(),
   alertTypeRegistry: alertTypeRegistryMock.create(),
@@ -52,8 +52,9 @@ beforeEach(() => {
 });
 
 test('creates an alerts client with proper constructor arguments', async () => {
-  const factory = new AlertsClientFactory(alertsClientFactoryParams);
-  factory.create(KibanaRequest.from(fakeRequest), fakeRequest);
+  const factory = new AlertsClientFactory();
+  factory.initialize(alertsClientFactoryParams);
+  factory.create(KibanaRequest.from(fakeRequest), savedObjectsClient);
 
   expect(jest.requireMock('./alerts_client').AlertsClient).toHaveBeenCalledWith({
     savedObjectsClient,
@@ -70,8 +71,9 @@ test('creates an alerts client with proper constructor arguments', async () => {
 });
 
 test('getUserName() returns null when security is disabled', async () => {
-  const factory = new AlertsClientFactory(alertsClientFactoryParams);
-  factory.create(KibanaRequest.from(fakeRequest), fakeRequest);
+  const factory = new AlertsClientFactory();
+  factory.initialize(alertsClientFactoryParams);
+  factory.create(KibanaRequest.from(fakeRequest), savedObjectsClient);
   const constructorCall = jest.requireMock('./alerts_client').AlertsClient.mock.calls[0][0];
 
   const userNameResult = await constructorCall.getUserName();
@@ -79,11 +81,12 @@ test('getUserName() returns null when security is disabled', async () => {
 });
 
 test('getUserName() returns a name when security is enabled', async () => {
-  const factory = new AlertsClientFactory({
+  const factory = new AlertsClientFactory();
+  factory.initialize({
     ...alertsClientFactoryParams,
     securityPluginSetup: securityPluginSetup as any,
   });
-  factory.create(KibanaRequest.from(fakeRequest), fakeRequest);
+  factory.create(KibanaRequest.from(fakeRequest), savedObjectsClient);
   const constructorCall = jest.requireMock('./alerts_client').AlertsClient.mock.calls[0][0];
 
   securityPluginSetup.authc.getCurrentUser.mockReturnValueOnce({ username: 'bob' });
@@ -92,8 +95,9 @@ test('getUserName() returns a name when security is enabled', async () => {
 });
 
 test('createAPIKey() returns { apiKeysEnabled: false } when security is disabled', async () => {
-  const factory = new AlertsClientFactory(alertsClientFactoryParams);
-  factory.create(KibanaRequest.from(fakeRequest), fakeRequest);
+  const factory = new AlertsClientFactory();
+  factory.initialize(alertsClientFactoryParams);
+  factory.create(KibanaRequest.from(fakeRequest), savedObjectsClient);
   const constructorCall = jest.requireMock('./alerts_client').AlertsClient.mock.calls[0][0];
 
   const createAPIKeyResult = await constructorCall.createAPIKey();
@@ -101,8 +105,9 @@ test('createAPIKey() returns { apiKeysEnabled: false } when security is disabled
 });
 
 test('createAPIKey() returns { apiKeysEnabled: false } when security is enabled but ES security is disabled', async () => {
-  const factory = new AlertsClientFactory(alertsClientFactoryParams);
-  factory.create(KibanaRequest.from(fakeRequest), fakeRequest);
+  const factory = new AlertsClientFactory();
+  factory.initialize(alertsClientFactoryParams);
+  factory.create(KibanaRequest.from(fakeRequest), savedObjectsClient);
   const constructorCall = jest.requireMock('./alerts_client').AlertsClient.mock.calls[0][0];
 
   securityPluginSetup.authc.createAPIKey.mockResolvedValueOnce(null);
@@ -111,11 +116,12 @@ test('createAPIKey() returns { apiKeysEnabled: false } when security is enabled
 });
 
 test('createAPIKey() returns an API key when security is enabled', async () => {
-  const factory = new AlertsClientFactory({
+  const factory = new AlertsClientFactory();
+  factory.initialize({
     ...alertsClientFactoryParams,
     securityPluginSetup: securityPluginSetup as any,
   });
-  factory.create(KibanaRequest.from(fakeRequest), fakeRequest);
+  factory.create(KibanaRequest.from(fakeRequest), savedObjectsClient);
   const constructorCall = jest.requireMock('./alerts_client').AlertsClient.mock.calls[0][0];
 
   securityPluginSetup.authc.createAPIKey.mockResolvedValueOnce({ api_key: '123', id: 'abc' });
@@ -127,11 +133,12 @@ test('createAPIKey() returns an API key when security is enabled', async () => {
 });
 
 test('createAPIKey() throws when security plugin createAPIKey throws an error', async () => {
-  const factory = new AlertsClientFactory({
+  const factory = new AlertsClientFactory();
+  factory.initialize({
     ...alertsClientFactoryParams,
     securityPluginSetup: securityPluginSetup as any,
   });
-  factory.create(KibanaRequest.from(fakeRequest), fakeRequest);
+  factory.create(KibanaRequest.from(fakeRequest), savedObjectsClient);
   const constructorCall = jest.requireMock('./alerts_client').AlertsClient.mock.calls[0][0];
 
   securityPluginSetup.authc.createAPIKey.mockRejectedValueOnce(new Error('TLS disabled'));
diff --git a/x-pack/legacy/plugins/alerting/server/alerts_client_factory.ts b/x-pack/plugins/alerting/server/alerts_client_factory.ts
similarity index 66%
rename from x-pack/legacy/plugins/alerting/server/alerts_client_factory.ts
rename to x-pack/plugins/alerting/server/alerts_client_factory.ts
index de789fba0ac38..c502c0e5bf1cf 100644
--- a/x-pack/legacy/plugins/alerting/server/alerts_client_factory.ts
+++ b/x-pack/plugins/alerting/server/alerts_client_factory.ts
@@ -4,36 +4,39 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import Hapi from 'hapi';
 import uuid from 'uuid';
 import { AlertsClient } from './alerts_client';
 import { AlertTypeRegistry, SpaceIdToNamespaceFunction } from './types';
-import { SecurityPluginStartContract } from './shim';
-import { KibanaRequest, Logger } from '../../../../../src/core/server';
-import { InvalidateAPIKeyParams } from '../../../../plugins/security/server';
-import { EncryptedSavedObjectsPluginStart } from '../../../../plugins/encrypted_saved_objects/server';
-import { TaskManagerStartContract } from '../../../../plugins/task_manager/server';
+import { KibanaRequest, Logger, SavedObjectsClientContract } from '../../../../src/core/server';
+import { InvalidateAPIKeyParams, SecurityPluginSetup } from '../../../plugins/security/server';
+import { EncryptedSavedObjectsPluginStart } from '../../../plugins/encrypted_saved_objects/server';
+import { TaskManagerStartContract } from '../../../plugins/task_manager/server';
 
-export interface ConstructorOpts {
+export interface AlertsClientFactoryOpts {
   logger: Logger;
   taskManager: TaskManagerStartContract;
   alertTypeRegistry: AlertTypeRegistry;
-  securityPluginSetup?: SecurityPluginStartContract;
-  getSpaceId: (request: Hapi.Request) => string | undefined;
+  securityPluginSetup?: SecurityPluginSetup;
+  getSpaceId: (request: KibanaRequest) => string | undefined;
   spaceIdToNamespace: SpaceIdToNamespaceFunction;
   encryptedSavedObjectsPlugin: EncryptedSavedObjectsPluginStart;
 }
 
 export class AlertsClientFactory {
-  private readonly logger: Logger;
-  private readonly taskManager: TaskManagerStartContract;
-  private readonly alertTypeRegistry: AlertTypeRegistry;
-  private readonly securityPluginSetup?: SecurityPluginStartContract;
-  private readonly getSpaceId: (request: Hapi.Request) => string | undefined;
-  private readonly spaceIdToNamespace: SpaceIdToNamespaceFunction;
-  private readonly encryptedSavedObjectsPlugin: EncryptedSavedObjectsPluginStart;
+  private isInitialized = false;
+  private logger!: Logger;
+  private taskManager!: TaskManagerStartContract;
+  private alertTypeRegistry!: AlertTypeRegistry;
+  private securityPluginSetup?: SecurityPluginSetup;
+  private getSpaceId!: (request: KibanaRequest) => string | undefined;
+  private spaceIdToNamespace!: SpaceIdToNamespaceFunction;
+  private encryptedSavedObjectsPlugin!: EncryptedSavedObjectsPluginStart;
 
-  constructor(options: ConstructorOpts) {
+  public initialize(options: AlertsClientFactoryOpts) {
+    if (this.isInitialized) {
+      throw new Error('AlertsClientFactory already initialized');
+    }
+    this.isInitialized = true;
     this.logger = options.logger;
     this.getSpaceId = options.getSpaceId;
     this.taskManager = options.taskManager;
@@ -43,15 +46,18 @@ export class AlertsClientFactory {
     this.encryptedSavedObjectsPlugin = options.encryptedSavedObjectsPlugin;
   }
 
-  public create(request: KibanaRequest, legacyRequest: Hapi.Request): AlertsClient {
+  public create(
+    request: KibanaRequest,
+    savedObjectsClient: SavedObjectsClientContract
+  ): AlertsClient {
     const { securityPluginSetup } = this;
-    const spaceId = this.getSpaceId(legacyRequest);
+    const spaceId = this.getSpaceId(request);
     return new AlertsClient({
       spaceId,
       logger: this.logger,
       taskManager: this.taskManager,
       alertTypeRegistry: this.alertTypeRegistry,
-      savedObjectsClient: legacyRequest.getSavedObjectsClient(),
+      savedObjectsClient,
       namespace: this.spaceIdToNamespace(spaceId),
       encryptedSavedObjectsPlugin: this.encryptedSavedObjectsPlugin,
       async getUserName() {
diff --git a/x-pack/legacy/plugins/alerting/server/constants/plugin.ts b/x-pack/plugins/alerting/server/constants/plugin.ts
similarity index 85%
rename from x-pack/legacy/plugins/alerting/server/constants/plugin.ts
rename to x-pack/plugins/alerting/server/constants/plugin.ts
index e3435b09829c6..173aa50013b40 100644
--- a/x-pack/legacy/plugins/alerting/server/constants/plugin.ts
+++ b/x-pack/plugins/alerting/server/constants/plugin.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { LICENSE_TYPE_BASIC, LicenseType } from '../../../../common/constants';
+import { LICENSE_TYPE_BASIC, LicenseType } from '../../../../legacy/common/constants';
 
 export const PLUGIN = {
   ID: 'alerting',
diff --git a/x-pack/plugins/alerting/server/index.ts b/x-pack/plugins/alerting/server/index.ts
new file mode 100644
index 0000000000000..58b77f0f510f7
--- /dev/null
+++ b/x-pack/plugins/alerting/server/index.ts
@@ -0,0 +1,27 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { AlertsClient as AlertsClientClass } from './alerts_client';
+import { PluginInitializerContext } from '../../../../src/core/server';
+import { AlertingPlugin } from './plugin';
+
+export type AlertsClient = PublicMethodsOf<AlertsClientClass>;
+
+export {
+  AlertType,
+  AlertingPlugin,
+  AlertExecutorOptions,
+  AlertActionParams,
+  AlertServices,
+  State,
+  PartialAlert,
+} from './types';
+export { PluginSetupContract, PluginStartContract } from './plugin';
+export { FindOptions, FindResult } from './alerts_client';
+export { AlertInstance } from './alert_instance';
+export { parseDuration } from './lib';
+
+export const plugin = (initContext: PluginInitializerContext) => new AlertingPlugin(initContext);
diff --git a/x-pack/legacy/plugins/alerting/server/lib/index.ts b/x-pack/plugins/alerting/server/lib/index.ts
similarity index 83%
rename from x-pack/legacy/plugins/alerting/server/lib/index.ts
rename to x-pack/plugins/alerting/server/lib/index.ts
index c41ea4a5998ff..c84825cadbd16 100644
--- a/x-pack/legacy/plugins/alerting/server/lib/index.ts
+++ b/x-pack/plugins/alerting/server/lib/index.ts
@@ -4,6 +4,6 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-export { parseDuration, getDurationSchema } from './parse_duration';
+export { parseDuration, validateDurationSchema } from './parse_duration';
 export { LicenseState } from './license_state';
 export { validateAlertTypeParams } from './validate_alert_type_params';
diff --git a/x-pack/plugins/alerting/server/lib/license_api_access.ts b/x-pack/plugins/alerting/server/lib/license_api_access.ts
new file mode 100644
index 0000000000000..2e650ebf5eb17
--- /dev/null
+++ b/x-pack/plugins/alerting/server/lib/license_api_access.ts
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import Boom from 'boom';
+import { LicenseState } from './license_state';
+
+export function verifyApiAccess(licenseState: LicenseState) {
+  const licenseCheckResults = licenseState.getLicenseInformation();
+
+  if (licenseCheckResults.showAppLink && licenseCheckResults.enableAppLink) {
+    return null;
+  }
+
+  throw Boom.forbidden(licenseCheckResults.message);
+}
diff --git a/x-pack/plugins/alerting/server/lib/license_state.mock.ts b/x-pack/plugins/alerting/server/lib/license_state.mock.ts
new file mode 100644
index 0000000000000..f36f3a9eaeade
--- /dev/null
+++ b/x-pack/plugins/alerting/server/lib/license_state.mock.ts
@@ -0,0 +1,38 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { of } from 'rxjs';
+import { LicenseState } from './license_state';
+import { LICENSE_CHECK_STATE, ILicense } from '../../../licensing/server';
+
+export const mockLicenseState = () => {
+  const license: ILicense = {
+    uid: '123',
+    status: 'active',
+    isActive: true,
+    signature: 'sig',
+    isAvailable: true,
+    toJSON: () => ({
+      signature: 'sig',
+    }),
+    getUnavailableReason: () => undefined,
+    hasAtLeast() {
+      return true;
+    },
+    check() {
+      return {
+        state: LICENSE_CHECK_STATE.Valid,
+      };
+    },
+    getFeature() {
+      return {
+        isAvailable: true,
+        isEnabled: true,
+      };
+    },
+  };
+  return new LicenseState(of(license));
+};
diff --git a/x-pack/legacy/plugins/alerting/server/lib/license_state.test.ts b/x-pack/plugins/alerting/server/lib/license_state.test.ts
similarity index 91%
rename from x-pack/legacy/plugins/alerting/server/lib/license_state.test.ts
rename to x-pack/plugins/alerting/server/lib/license_state.test.ts
index 484dc49532567..8feb8d74976fd 100644
--- a/x-pack/legacy/plugins/alerting/server/lib/license_state.test.ts
+++ b/x-pack/plugins/alerting/server/lib/license_state.test.ts
@@ -6,8 +6,8 @@
 
 import expect from '@kbn/expect';
 import { LicenseState } from './license_state';
-import { licensingMock } from '../../../../../plugins/licensing/server/mocks';
-import { LICENSE_CHECK_STATE } from '../../../../../plugins/licensing/server';
+import { licensingMock } from '../../../../plugins/licensing/server/mocks';
+import { LICENSE_CHECK_STATE } from '../../../../plugins/licensing/server';
 
 describe('license_state', () => {
   let getRawLicense: any;
diff --git a/x-pack/legacy/plugins/alerting/server/lib/license_state.ts b/x-pack/plugins/alerting/server/lib/license_state.ts
similarity index 94%
rename from x-pack/legacy/plugins/alerting/server/lib/license_state.ts
rename to x-pack/plugins/alerting/server/lib/license_state.ts
index 344bc9c409edf..690eaed9e2b89 100644
--- a/x-pack/legacy/plugins/alerting/server/lib/license_state.ts
+++ b/x-pack/plugins/alerting/server/lib/license_state.ts
@@ -7,8 +7,8 @@
 import Boom from 'boom';
 import { i18n } from '@kbn/i18n';
 import { Observable, Subscription } from 'rxjs';
-import { ILicense, LICENSE_CHECK_STATE } from '../../../../../plugins/licensing/common/types';
-import { assertNever } from '../../../../../../src/core/utils';
+import { ILicense, LICENSE_CHECK_STATE } from '../../../../plugins/licensing/common/types';
+import { assertNever } from '../../../../../src/core/utils';
 import { PLUGIN } from '../constants/plugin';
 
 export interface AlertingLicenseInformation {
diff --git a/x-pack/legacy/plugins/alerting/server/lib/parse_duration.test.ts b/x-pack/plugins/alerting/server/lib/parse_duration.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/alerting/server/lib/parse_duration.test.ts
rename to x-pack/plugins/alerting/server/lib/parse_duration.test.ts
diff --git a/x-pack/legacy/plugins/alerting/server/lib/parse_duration.ts b/x-pack/plugins/alerting/server/lib/parse_duration.ts
similarity index 75%
rename from x-pack/legacy/plugins/alerting/server/lib/parse_duration.ts
rename to x-pack/plugins/alerting/server/lib/parse_duration.ts
index e343ef6d21684..51f3d746a6869 100644
--- a/x-pack/legacy/plugins/alerting/server/lib/parse_duration.ts
+++ b/x-pack/plugins/alerting/server/lib/parse_duration.ts
@@ -3,9 +3,6 @@
  * or more contributor license agreements. Licensed under the Elastic License;
  * you may not use this file except in compliance with the Elastic License.
  */
-
-import Joi from 'joi';
-
 const SECONDS_REGEX = /^[1-9][0-9]*s$/;
 const MINUTES_REGEX = /^[1-9][0-9]*m$/;
 const HOURS_REGEX = /^[1-9][0-9]*h$/;
@@ -27,21 +24,20 @@ export function parseDuration(duration: string): number {
   );
 }
 
-export function getDurationSchema() {
-  return Joi.alternatives().try(
-    Joi.string()
-      .regex(SECONDS_REGEX, 'seconds')
-      .required(),
-    Joi.string()
-      .regex(MINUTES_REGEX, 'minutes')
-      .required(),
-    Joi.string()
-      .regex(HOURS_REGEX, 'hours')
-      .required(),
-    Joi.string()
-      .regex(DAYS_REGEX, 'days')
-      .required()
-  );
+export function validateDurationSchema(duration: string) {
+  if (duration.match(SECONDS_REGEX)) {
+    return;
+  }
+  if (duration.match(MINUTES_REGEX)) {
+    return;
+  }
+  if (duration.match(HOURS_REGEX)) {
+    return;
+  }
+  if (duration.match(DAYS_REGEX)) {
+    return;
+  }
+  return 'string is not a valid duration: ' + duration;
 }
 
 function isSeconds(duration: string) {
diff --git a/x-pack/legacy/plugins/alerting/server/lib/result_type.ts b/x-pack/plugins/alerting/server/lib/result_type.ts
similarity index 100%
rename from x-pack/legacy/plugins/alerting/server/lib/result_type.ts
rename to x-pack/plugins/alerting/server/lib/result_type.ts
diff --git a/x-pack/legacy/plugins/alerting/server/lib/types.test.ts b/x-pack/plugins/alerting/server/lib/types.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/alerting/server/lib/types.test.ts
rename to x-pack/plugins/alerting/server/lib/types.test.ts
diff --git a/x-pack/legacy/plugins/alerting/server/lib/types.ts b/x-pack/plugins/alerting/server/lib/types.ts
similarity index 100%
rename from x-pack/legacy/plugins/alerting/server/lib/types.ts
rename to x-pack/plugins/alerting/server/lib/types.ts
diff --git a/x-pack/legacy/plugins/alerting/server/lib/validate_alert_type_params.test.ts b/x-pack/plugins/alerting/server/lib/validate_alert_type_params.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/alerting/server/lib/validate_alert_type_params.test.ts
rename to x-pack/plugins/alerting/server/lib/validate_alert_type_params.test.ts
diff --git a/x-pack/legacy/plugins/alerting/server/lib/validate_alert_type_params.ts b/x-pack/plugins/alerting/server/lib/validate_alert_type_params.ts
similarity index 100%
rename from x-pack/legacy/plugins/alerting/server/lib/validate_alert_type_params.ts
rename to x-pack/plugins/alerting/server/lib/validate_alert_type_params.ts
diff --git a/x-pack/plugins/alerting/server/mocks.ts b/x-pack/plugins/alerting/server/mocks.ts
new file mode 100644
index 0000000000000..55ad722dcf881
--- /dev/null
+++ b/x-pack/plugins/alerting/server/mocks.ts
@@ -0,0 +1,30 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { alertsClientMock } from './alerts_client.mock';
+import { PluginSetupContract, PluginStartContract } from './plugin';
+
+export { alertsClientMock };
+
+const createSetupMock = () => {
+  const mock: jest.Mocked<PluginSetupContract> = {
+    registerType: jest.fn(),
+  };
+  return mock;
+};
+
+const createStartMock = () => {
+  const mock: jest.Mocked<PluginStartContract> = {
+    listTypes: jest.fn(),
+    getAlertsClientWithRequest: jest.fn().mockResolvedValue(alertsClientMock.create()),
+  };
+  return mock;
+};
+
+export const alertsMock = {
+  createSetup: createSetupMock,
+  createStart: createStartMock,
+};
diff --git a/x-pack/legacy/plugins/alerting/server/plugin.test.ts b/x-pack/plugins/alerting/server/plugin.test.ts
similarity index 91%
rename from x-pack/legacy/plugins/alerting/server/plugin.test.ts
rename to x-pack/plugins/alerting/server/plugin.test.ts
index 872de720243b2..40e620dd92af0 100644
--- a/x-pack/legacy/plugins/alerting/server/plugin.test.ts
+++ b/x-pack/plugins/alerting/server/plugin.test.ts
@@ -4,16 +4,16 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { Plugin } from './plugin';
-import { coreMock } from '../../../../../src/core/server/mocks';
-import { licensingMock } from '../../../../plugins/licensing/server/mocks';
-import { encryptedSavedObjectsMock } from '../../../../plugins/encrypted_saved_objects/server/mocks';
+import { AlertingPlugin } from './plugin';
+import { coreMock } from '../../../../src/core/server/mocks';
+import { licensingMock } from '../../../plugins/licensing/server/mocks';
+import { encryptedSavedObjectsMock } from '../../../plugins/encrypted_saved_objects/server/mocks';
 
 describe('Alerting Plugin', () => {
   describe('setup()', () => {
     it('should log warning when Encrypted Saved Objects plugin is using an ephemeral encryption key', async () => {
       const context = coreMock.createPluginInitializerContext();
-      const plugin = new Plugin(context);
+      const plugin = new AlertingPlugin(context);
 
       const coreSetup = coreMock.createSetup();
       const encryptedSavedObjectsSetup = encryptedSavedObjectsMock.createSetup();
@@ -49,7 +49,7 @@ describe('Alerting Plugin', () => {
     describe('getAlertsClientWithRequest()', () => {
       it('throws error when encryptedSavedObjects plugin has usingEphemeralEncryptionKey set to true', async () => {
         const context = coreMock.createPluginInitializerContext();
-        const plugin = new Plugin(context);
+        const plugin = new AlertingPlugin(context);
 
         const coreSetup = coreMock.createSetup();
         const encryptedSavedObjectsSetup = encryptedSavedObjectsMock.createSetup();
@@ -87,7 +87,7 @@ describe('Alerting Plugin', () => {
 
       it(`doesn't throw error when encryptedSavedObjects plugin has usingEphemeralEncryptionKey set to false`, async () => {
         const context = coreMock.createPluginInitializerContext();
-        const plugin = new Plugin(context);
+        const plugin = new AlertingPlugin(context);
 
         const coreSetup = coreMock.createSetup();
         const encryptedSavedObjectsSetup = {
diff --git a/x-pack/plugins/alerting/server/plugin.ts b/x-pack/plugins/alerting/server/plugin.ts
new file mode 100644
index 0000000000000..bed163878b5ac
--- /dev/null
+++ b/x-pack/plugins/alerting/server/plugin.ts
@@ -0,0 +1,236 @@
+/*
+ * 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 { SecurityPluginSetup } from '../../security/server';
+import {
+  EncryptedSavedObjectsPluginSetup,
+  EncryptedSavedObjectsPluginStart,
+} from '../../encrypted_saved_objects/server';
+import { TaskManagerSetupContract, TaskManagerStartContract } from '../../task_manager/server';
+import { SpacesPluginSetup, SpacesServiceSetup } from '../../spaces/server';
+import { AlertsClient } from './alerts_client';
+import { AlertTypeRegistry } from './alert_type_registry';
+import { TaskRunnerFactory } from './task_runner';
+import { AlertsClientFactory } from './alerts_client_factory';
+import { LicenseState } from './lib/license_state';
+import {
+  IClusterClient,
+  KibanaRequest,
+  Logger,
+  PluginInitializerContext,
+  CoreSetup,
+  CoreStart,
+  SavedObjectsServiceStart,
+  IContextProvider,
+  RequestHandler,
+} from '../../../../src/core/server';
+
+import {
+  createAlertRoute,
+  deleteAlertRoute,
+  findAlertRoute,
+  getAlertRoute,
+  getAlertStateRoute,
+  listAlertTypesRoute,
+  updateAlertRoute,
+  enableAlertRoute,
+  disableAlertRoute,
+  updateApiKeyRoute,
+  muteAllAlertRoute,
+  unmuteAllAlertRoute,
+  muteAlertInstanceRoute,
+  unmuteAlertInstanceRoute,
+} from './routes';
+import { LicensingPluginSetup } from '../../licensing/server';
+import {
+  PluginSetupContract as ActionsPluginSetupContract,
+  PluginStartContract as ActionsPluginStartContract,
+} from '../../../plugins/actions/server';
+import { Services } from './types';
+
+export interface PluginSetupContract {
+  registerType: AlertTypeRegistry['register'];
+}
+export interface PluginStartContract {
+  listTypes: AlertTypeRegistry['list'];
+  getAlertsClientWithRequest(request: KibanaRequest): PublicMethodsOf<AlertsClient>;
+}
+
+export interface AlertingPluginsSetup {
+  security?: SecurityPluginSetup;
+  taskManager: TaskManagerSetupContract;
+  actions: ActionsPluginSetupContract;
+  encryptedSavedObjects: EncryptedSavedObjectsPluginSetup;
+  licensing: LicensingPluginSetup;
+  spaces?: SpacesPluginSetup;
+}
+export interface AlertingPluginsStart {
+  actions: ActionsPluginStartContract;
+  encryptedSavedObjects: EncryptedSavedObjectsPluginStart;
+  taskManager: TaskManagerStartContract;
+}
+
+export class AlertingPlugin {
+  private readonly logger: Logger;
+  private alertTypeRegistry?: AlertTypeRegistry;
+  private readonly taskRunnerFactory: TaskRunnerFactory;
+  private adminClient?: IClusterClient;
+  private serverBasePath?: string;
+  private licenseState: LicenseState | null = null;
+  private isESOUsingEphemeralEncryptionKey?: boolean;
+  private spaces?: SpacesServiceSetup;
+  private security?: SecurityPluginSetup;
+  private readonly alertsClientFactory: AlertsClientFactory;
+
+  constructor(initializerContext: PluginInitializerContext) {
+    this.logger = initializerContext.logger.get('plugins', 'alerting');
+    this.taskRunnerFactory = new TaskRunnerFactory();
+    this.alertsClientFactory = new AlertsClientFactory();
+  }
+
+  public async setup(core: CoreSetup, plugins: AlertingPluginsSetup): Promise<PluginSetupContract> {
+    this.adminClient = core.elasticsearch.adminClient;
+    this.licenseState = new LicenseState(plugins.licensing.license$);
+    this.spaces = plugins.spaces?.spacesService;
+    this.security = plugins.security;
+    this.isESOUsingEphemeralEncryptionKey =
+      plugins.encryptedSavedObjects.usingEphemeralEncryptionKey;
+
+    if (this.isESOUsingEphemeralEncryptionKey) {
+      this.logger.warn(
+        'APIs are disabled due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml.'
+      );
+    }
+
+    // Encrypted attributes
+    plugins.encryptedSavedObjects.registerType({
+      type: 'alert',
+      attributesToEncrypt: new Set(['apiKey']),
+      attributesToExcludeFromAAD: new Set([
+        'scheduledTaskId',
+        'muteAll',
+        'mutedInstanceIds',
+        'updatedBy',
+      ]),
+    });
+
+    const alertTypeRegistry = new AlertTypeRegistry({
+      taskManager: plugins.taskManager,
+      taskRunnerFactory: this.taskRunnerFactory,
+    });
+    this.alertTypeRegistry = alertTypeRegistry;
+    this.serverBasePath = core.http.basePath.serverBasePath;
+
+    core.http.registerRouteHandlerContext('alerting', this.createRouteHandlerContext());
+
+    // Routes
+    const router = core.http.createRouter();
+    // Register routes
+    createAlertRoute(router, this.licenseState);
+    deleteAlertRoute(router, this.licenseState);
+    findAlertRoute(router, this.licenseState);
+    getAlertRoute(router, this.licenseState);
+    getAlertStateRoute(router, this.licenseState);
+    listAlertTypesRoute(router, this.licenseState);
+    updateAlertRoute(router, this.licenseState);
+    enableAlertRoute(router, this.licenseState);
+    disableAlertRoute(router, this.licenseState);
+    updateApiKeyRoute(router, this.licenseState);
+    muteAllAlertRoute(router, this.licenseState);
+    unmuteAllAlertRoute(router, this.licenseState);
+    muteAlertInstanceRoute(router, this.licenseState);
+    unmuteAlertInstanceRoute(router, this.licenseState);
+
+    return {
+      registerType: alertTypeRegistry.register.bind(alertTypeRegistry),
+    };
+  }
+
+  public start(core: CoreStart, plugins: AlertingPluginsStart): PluginStartContract {
+    const {
+      spaces,
+      isESOUsingEphemeralEncryptionKey,
+      logger,
+      taskRunnerFactory,
+      alertTypeRegistry,
+      alertsClientFactory,
+      security,
+    } = this;
+
+    alertsClientFactory.initialize({
+      alertTypeRegistry: alertTypeRegistry!,
+      logger,
+      taskManager: plugins.taskManager,
+      securityPluginSetup: security,
+      encryptedSavedObjectsPlugin: plugins.encryptedSavedObjects,
+      spaceIdToNamespace: this.spaceIdToNamespace,
+      getSpaceId(request: KibanaRequest) {
+        return spaces?.getSpaceId(request);
+      },
+    });
+
+    taskRunnerFactory.initialize({
+      logger,
+      getServices: this.getServicesFactory(core.savedObjects),
+      spaceIdToNamespace: this.spaceIdToNamespace,
+      executeAction: plugins.actions.execute,
+      encryptedSavedObjectsPlugin: plugins.encryptedSavedObjects,
+      getBasePath: this.getBasePath,
+    });
+
+    return {
+      listTypes: alertTypeRegistry!.list.bind(this.alertTypeRegistry!),
+      // Ability to get an alerts client from legacy code
+      getAlertsClientWithRequest(request: KibanaRequest) {
+        if (isESOUsingEphemeralEncryptionKey === true) {
+          throw new Error(
+            `Unable to create alerts client due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml`
+          );
+        }
+        return alertsClientFactory!.create(request, core.savedObjects.getScopedClient(request));
+      },
+    };
+  }
+
+  private createRouteHandlerContext = (): IContextProvider<
+    RequestHandler<any, any, any>,
+    'alerting'
+  > => {
+    const { alertTypeRegistry, alertsClientFactory } = this;
+    return async function alertsRouteHandlerContext(context, request) {
+      return {
+        getAlertsClient: () => {
+          return alertsClientFactory!.create(request, context.core!.savedObjects.client);
+        },
+        listTypes: alertTypeRegistry!.list.bind(alertTypeRegistry!),
+      };
+    };
+  };
+
+  private getServicesFactory(
+    savedObjects: SavedObjectsServiceStart
+  ): (request: KibanaRequest) => Services {
+    const { adminClient } = this;
+    return request => ({
+      callCluster: adminClient!.asScoped(request).callAsCurrentUser,
+      savedObjectsClient: savedObjects.getScopedClient(request),
+    });
+  }
+
+  private spaceIdToNamespace = (spaceId?: string): string | undefined => {
+    return this.spaces && spaceId ? this.spaces.spaceIdToNamespace(spaceId) : undefined;
+  };
+
+  private getBasePath = (spaceId?: string): string => {
+    return this.spaces && spaceId ? this.spaces.getBasePath(spaceId) : this.serverBasePath!;
+  };
+
+  public stop() {
+    if (this.licenseState) {
+      this.licenseState.clean();
+    }
+  }
+}
diff --git a/x-pack/plugins/alerting/server/routes/_mock_handler_arguments.ts b/x-pack/plugins/alerting/server/routes/_mock_handler_arguments.ts
new file mode 100644
index 0000000000000..9815ad5194af7
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/_mock_handler_arguments.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 { RequestHandlerContext, KibanaRequest, KibanaResponseFactory } from 'kibana/server';
+import { identity } from 'lodash';
+import { httpServerMock } from '../../../../../src/core/server/mocks';
+import { alertsClientMock } from '../alerts_client.mock';
+
+export function mockHandlerArguments(
+  { alertsClient, listTypes: listTypesRes = [] }: any,
+  req: any,
+  res?: Array<MethodKeysOf<KibanaResponseFactory>>
+): [RequestHandlerContext, KibanaRequest<any, any, any, any>, KibanaResponseFactory] {
+  const listTypes = jest.fn(() => listTypesRes);
+  return [
+    ({
+      alerting: {
+        listTypes,
+        getAlertsClient() {
+          return alertsClient || alertsClientMock.create();
+        },
+      },
+    } as unknown) as RequestHandlerContext,
+    req as KibanaRequest<any, any, any, any>,
+    mockResponseFactory(res),
+  ];
+}
+
+export const mockResponseFactory = (resToMock: Array<MethodKeysOf<KibanaResponseFactory>> = []) => {
+  const factory: jest.Mocked<KibanaResponseFactory> = httpServerMock.createResponseFactory();
+  resToMock.forEach((key: string) => {
+    if (key in factory) {
+      Object.defineProperty(factory, key, {
+        value: jest.fn(identity),
+      });
+    }
+  });
+  return (factory as unknown) as KibanaResponseFactory;
+};
diff --git a/x-pack/plugins/alerting/server/routes/create.test.ts b/x-pack/plugins/alerting/server/routes/create.test.ts
new file mode 100644
index 0000000000000..c6a0da2bd9191
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/create.test.ts
@@ -0,0 +1,172 @@
+/*
+ * 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 { createAlertRoute } from './create';
+import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock';
+import { mockLicenseState } from '../lib/license_state.mock';
+import { verifyApiAccess } from '../lib/license_api_access';
+import { mockHandlerArguments } from './_mock_handler_arguments';
+import { alertsClientMock } from '../alerts_client.mock';
+
+const alertsClient = alertsClientMock.create();
+
+jest.mock('../lib/license_api_access.ts', () => ({
+  verifyApiAccess: jest.fn(),
+}));
+
+beforeEach(() => {
+  jest.resetAllMocks();
+});
+
+describe('createAlertRoute', () => {
+  const createdAt = new Date();
+  const updatedAt = new Date();
+
+  const mockedAlert = {
+    alertTypeId: '1',
+    consumer: 'bar',
+    name: 'abc',
+    schedule: { interval: '10s' },
+    tags: ['foo'],
+    params: {
+      bar: true,
+    },
+    throttle: '30s',
+    actions: [
+      {
+        group: 'default',
+        id: '2',
+        params: {
+          foo: true,
+        },
+      },
+    ],
+  };
+
+  const createResult = {
+    ...mockedAlert,
+    enabled: true,
+    muteAll: false,
+    createdBy: '',
+    updatedBy: '',
+    apiKey: '',
+    apiKeyOwner: '',
+    mutedInstanceIds: [],
+    createdAt,
+    updatedAt,
+    id: '123',
+    actions: [
+      {
+        ...mockedAlert.actions[0],
+        actionTypeId: 'test',
+      },
+    ],
+  };
+
+  it('creates an alert with proper parameters', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    createAlertRoute(router, licenseState);
+
+    const [config, handler] = router.post.mock.calls[0];
+
+    expect(config.path).toMatchInlineSnapshot(`"/api/alert"`);
+    expect(config.options).toMatchInlineSnapshot(`
+      Object {
+        "tags": Array [
+          "access:alerting-all",
+        ],
+      }
+    `);
+
+    alertsClient.create.mockResolvedValueOnce(createResult);
+
+    const [context, req, res] = mockHandlerArguments(
+      { alertsClient },
+      {
+        body: mockedAlert,
+      },
+      ['ok']
+    );
+
+    expect(await handler(context, req, res)).toEqual({ body: createResult });
+
+    expect(alertsClient.create).toHaveBeenCalledTimes(1);
+    expect(alertsClient.create.mock.calls[0]).toMatchInlineSnapshot(`
+      Array [
+        Object {
+          "data": Object {
+            "actions": Array [
+              Object {
+                "group": "default",
+                "id": "2",
+                "params": Object {
+                  "foo": true,
+                },
+              },
+            ],
+            "alertTypeId": "1",
+            "consumer": "bar",
+            "name": "abc",
+            "params": Object {
+              "bar": true,
+            },
+            "schedule": Object {
+              "interval": "10s",
+            },
+            "tags": Array [
+              "foo",
+            ],
+            "throttle": "30s",
+          },
+        },
+      ]
+    `);
+
+    expect(res.ok).toHaveBeenCalledWith({
+      body: createResult,
+    });
+  });
+
+  it('ensures the license allows creating alerts', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    createAlertRoute(router, licenseState);
+
+    const [, handler] = router.post.mock.calls[0];
+
+    alertsClient.create.mockResolvedValueOnce(createResult);
+
+    const [context, req, res] = mockHandlerArguments(alertsClient, {});
+
+    await handler(context, req, res);
+
+    expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
+  });
+
+  it('ensures the license check prevents creating alerts', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    (verifyApiAccess as jest.Mock).mockImplementation(() => {
+      throw new Error('OMG');
+    });
+
+    createAlertRoute(router, licenseState);
+
+    const [, handler] = router.post.mock.calls[0];
+
+    alertsClient.create.mockResolvedValueOnce(createResult);
+
+    const [context, req, res] = mockHandlerArguments(alertsClient, {});
+
+    expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`);
+
+    expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
+  });
+});
diff --git a/x-pack/plugins/alerting/server/routes/create.ts b/x-pack/plugins/alerting/server/routes/create.ts
new file mode 100644
index 0000000000000..8d854e0df8467
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/create.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 { schema, TypeOf } from '@kbn/config-schema';
+import {
+  IRouter,
+  RequestHandlerContext,
+  KibanaRequest,
+  IKibanaResponse,
+  KibanaResponseFactory,
+} from 'kibana/server';
+import { LicenseState } from '../lib/license_state';
+import { verifyApiAccess } from '../lib/license_api_access';
+import { validateDurationSchema } from '../lib';
+import { Alert } from '../types';
+
+export const bodySchema = schema.object({
+  name: schema.string(),
+  alertTypeId: schema.string(),
+  enabled: schema.boolean({ defaultValue: true }),
+  consumer: schema.string(),
+  tags: schema.arrayOf(schema.string(), { defaultValue: [] }),
+  throttle: schema.nullable(schema.string({ validate: validateDurationSchema })),
+  params: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }),
+  schedule: schema.object({
+    interval: schema.string({ validate: validateDurationSchema }),
+  }),
+  actions: schema.arrayOf(
+    schema.object({
+      group: schema.string(),
+      id: schema.string(),
+      actionTypeId: schema.maybe(schema.string()),
+      params: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }),
+    }),
+    { defaultValue: [] }
+  ),
+});
+
+export const createAlertRoute = (router: IRouter, licenseState: LicenseState) => {
+  router.post(
+    {
+      path: '/api/alert',
+      validate: {
+        body: bodySchema,
+      },
+      options: {
+        tags: ['access:alerting-all'],
+      },
+    },
+    router.handleLegacyErrors(async function(
+      context: RequestHandlerContext,
+      req: KibanaRequest<any, any, TypeOf<typeof bodySchema>, any>,
+      res: KibanaResponseFactory
+    ): Promise<IKibanaResponse<any>> {
+      verifyApiAccess(licenseState);
+
+      const alertsClient = context.alerting.getAlertsClient();
+      const alert = req.body;
+      const alertRes: Alert = await alertsClient.create({ data: alert });
+      return res.ok({
+        body: alertRes,
+      });
+    })
+  );
+};
diff --git a/x-pack/plugins/alerting/server/routes/delete.test.ts b/x-pack/plugins/alerting/server/routes/delete.test.ts
new file mode 100644
index 0000000000000..36bb485817c15
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/delete.test.ts
@@ -0,0 +1,108 @@
+/*
+ * 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 { deleteAlertRoute } from './delete';
+import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock';
+import { mockLicenseState } from '../lib/license_state.mock';
+import { verifyApiAccess } from '../lib/license_api_access';
+import { mockHandlerArguments } from './_mock_handler_arguments';
+import { alertsClientMock } from '../alerts_client.mock';
+
+const alertsClient = alertsClientMock.create();
+
+jest.mock('../lib/license_api_access.ts', () => ({
+  verifyApiAccess: jest.fn(),
+}));
+
+beforeEach(() => {
+  jest.resetAllMocks();
+});
+
+describe('deleteAlertRoute', () => {
+  it('deletes an alert with proper parameters', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    deleteAlertRoute(router, licenseState);
+
+    const [config, handler] = router.delete.mock.calls[0];
+
+    expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}"`);
+    expect(config.options).toMatchInlineSnapshot(`
+      Object {
+        "tags": Array [
+          "access:alerting-all",
+        ],
+      }
+    `);
+
+    alertsClient.delete.mockResolvedValueOnce({});
+
+    const [context, req, res] = mockHandlerArguments(
+      { alertsClient },
+      {
+        params: {
+          id: '1',
+        },
+      },
+      ['noContent']
+    );
+
+    expect(await handler(context, req, res)).toEqual(undefined);
+
+    expect(alertsClient.delete).toHaveBeenCalledTimes(1);
+    expect(alertsClient.delete.mock.calls[0]).toMatchInlineSnapshot(`
+      Array [
+        Object {
+          "id": "1",
+        },
+      ]
+    `);
+
+    expect(res.noContent).toHaveBeenCalled();
+  });
+
+  it('ensures the license allows deleting alerts', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    deleteAlertRoute(router, licenseState);
+
+    const [, handler] = router.delete.mock.calls[0];
+
+    alertsClient.delete.mockResolvedValueOnce({});
+
+    const [context, req, res] = mockHandlerArguments(alertsClient, {
+      params: { id: '1' },
+    });
+
+    await handler(context, req, res);
+
+    expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
+  });
+
+  it('ensures the license check prevents deleting alerts', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    (verifyApiAccess as jest.Mock).mockImplementation(() => {
+      throw new Error('OMG');
+    });
+
+    deleteAlertRoute(router, licenseState);
+
+    const [, handler] = router.delete.mock.calls[0];
+
+    alertsClient.delete.mockResolvedValueOnce({});
+
+    const [context, req, res] = mockHandlerArguments(alertsClient, {
+      id: '1',
+    });
+
+    expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`);
+
+    expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
+  });
+});
diff --git a/x-pack/plugins/alerting/server/routes/delete.ts b/x-pack/plugins/alerting/server/routes/delete.ts
new file mode 100644
index 0000000000000..0556ef3d66982
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/delete.ts
@@ -0,0 +1,45 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { schema, TypeOf } from '@kbn/config-schema';
+import {
+  IRouter,
+  RequestHandlerContext,
+  KibanaRequest,
+  IKibanaResponse,
+  KibanaResponseFactory,
+} from 'kibana/server';
+import { LicenseState } from '../lib/license_state';
+import { verifyApiAccess } from '../lib/license_api_access';
+
+const paramSchema = schema.object({
+  id: schema.string(),
+});
+
+export const deleteAlertRoute = (router: IRouter, licenseState: LicenseState) => {
+  router.delete(
+    {
+      path: '/api/alert/{id}',
+      validate: {
+        params: paramSchema,
+      },
+      options: {
+        tags: ['access:alerting-all'],
+      },
+    },
+    router.handleLegacyErrors(async function(
+      context: RequestHandlerContext,
+      req: KibanaRequest<TypeOf<typeof paramSchema>, any, any, any>,
+      res: KibanaResponseFactory
+    ): Promise<IKibanaResponse<any>> {
+      verifyApiAccess(licenseState);
+      const alertsClient = context.alerting.getAlertsClient();
+      const { id } = req.params;
+      await alertsClient.delete({ id });
+      return res.noContent();
+    })
+  );
+};
diff --git a/x-pack/plugins/alerting/server/routes/disable.test.ts b/x-pack/plugins/alerting/server/routes/disable.test.ts
new file mode 100644
index 0000000000000..622b562ec6911
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/disable.test.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 { disableAlertRoute } from './disable';
+import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock';
+import { mockLicenseState } from '../lib/license_state.mock';
+import { mockHandlerArguments } from './_mock_handler_arguments';
+import { alertsClientMock } from '../alerts_client.mock';
+
+const alertsClient = alertsClientMock.create();
+
+jest.mock('../lib/license_api_access.ts', () => ({
+  verifyApiAccess: jest.fn(),
+}));
+
+beforeEach(() => {
+  jest.resetAllMocks();
+});
+
+describe('disableAlertRoute', () => {
+  it('disables an alert', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    disableAlertRoute(router, licenseState);
+
+    const [config, handler] = router.post.mock.calls[0];
+
+    expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}/_disable"`);
+    expect(config.options).toMatchInlineSnapshot(`
+      Object {
+        "tags": Array [
+          "access:alerting-all",
+        ],
+      }
+    `);
+
+    alertsClient.disable.mockResolvedValueOnce();
+
+    const [context, req, res] = mockHandlerArguments(
+      { alertsClient },
+      {
+        params: {
+          id: '1',
+        },
+      },
+      ['noContent']
+    );
+
+    expect(await handler(context, req, res)).toEqual(undefined);
+
+    expect(alertsClient.disable).toHaveBeenCalledTimes(1);
+    expect(alertsClient.disable.mock.calls[0]).toMatchInlineSnapshot(`
+      Array [
+        Object {
+          "id": "1",
+        },
+      ]
+    `);
+
+    expect(res.noContent).toHaveBeenCalled();
+  });
+});
diff --git a/x-pack/plugins/alerting/server/routes/disable.ts b/x-pack/plugins/alerting/server/routes/disable.ts
new file mode 100644
index 0000000000000..5c6d977e62c38
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/disable.ts
@@ -0,0 +1,45 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { schema, TypeOf } from '@kbn/config-schema';
+import {
+  IRouter,
+  RequestHandlerContext,
+  KibanaRequest,
+  IKibanaResponse,
+  KibanaResponseFactory,
+} from 'kibana/server';
+import { LicenseState } from '../lib/license_state';
+import { verifyApiAccess } from '../lib/license_api_access';
+
+const paramSchema = schema.object({
+  id: schema.string(),
+});
+
+export const disableAlertRoute = (router: IRouter, licenseState: LicenseState) => {
+  router.post(
+    {
+      path: '/api/alert/{id}/_disable',
+      validate: {
+        params: paramSchema,
+      },
+      options: {
+        tags: ['access:alerting-all'],
+      },
+    },
+    router.handleLegacyErrors(async function(
+      context: RequestHandlerContext,
+      req: KibanaRequest<TypeOf<typeof paramSchema>, any, any, any>,
+      res: KibanaResponseFactory
+    ): Promise<IKibanaResponse<any>> {
+      verifyApiAccess(licenseState);
+      const alertsClient = context.alerting.getAlertsClient();
+      const { id } = req.params;
+      await alertsClient.disable({ id });
+      return res.noContent();
+    })
+  );
+};
diff --git a/x-pack/plugins/alerting/server/routes/enable.test.ts b/x-pack/plugins/alerting/server/routes/enable.test.ts
new file mode 100644
index 0000000000000..5a7b027e8ef54
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/enable.test.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 { enableAlertRoute } from './enable';
+import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock';
+import { mockLicenseState } from '../lib/license_state.mock';
+import { mockHandlerArguments } from './_mock_handler_arguments';
+import { alertsClientMock } from '../alerts_client.mock';
+
+const alertsClient = alertsClientMock.create();
+
+jest.mock('../lib/license_api_access.ts', () => ({
+  verifyApiAccess: jest.fn(),
+}));
+
+beforeEach(() => {
+  jest.resetAllMocks();
+});
+
+describe('enableAlertRoute', () => {
+  it('enables an alert', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    enableAlertRoute(router, licenseState);
+
+    const [config, handler] = router.post.mock.calls[0];
+
+    expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}/_enable"`);
+    expect(config.options).toMatchInlineSnapshot(`
+      Object {
+        "tags": Array [
+          "access:alerting-all",
+        ],
+      }
+    `);
+
+    alertsClient.enable.mockResolvedValueOnce();
+
+    const [context, req, res] = mockHandlerArguments(
+      { alertsClient },
+      {
+        params: {
+          id: '1',
+        },
+      },
+      ['noContent']
+    );
+
+    expect(await handler(context, req, res)).toEqual(undefined);
+
+    expect(alertsClient.enable).toHaveBeenCalledTimes(1);
+    expect(alertsClient.enable.mock.calls[0]).toMatchInlineSnapshot(`
+      Array [
+        Object {
+          "id": "1",
+        },
+      ]
+    `);
+
+    expect(res.noContent).toHaveBeenCalled();
+  });
+});
diff --git a/x-pack/plugins/alerting/server/routes/enable.ts b/x-pack/plugins/alerting/server/routes/enable.ts
new file mode 100644
index 0000000000000..f75344ad85998
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/enable.ts
@@ -0,0 +1,45 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { schema, TypeOf } from '@kbn/config-schema';
+import {
+  IRouter,
+  RequestHandlerContext,
+  KibanaRequest,
+  IKibanaResponse,
+  KibanaResponseFactory,
+} from 'kibana/server';
+import { LicenseState } from '../lib/license_state';
+import { verifyApiAccess } from '../lib/license_api_access';
+
+const paramSchema = schema.object({
+  id: schema.string(),
+});
+
+export const enableAlertRoute = (router: IRouter, licenseState: LicenseState) => {
+  router.post(
+    {
+      path: '/api/alert/{id}/_enable',
+      validate: {
+        params: paramSchema,
+      },
+      options: {
+        tags: ['access:alerting-all'],
+      },
+    },
+    router.handleLegacyErrors(async function(
+      context: RequestHandlerContext,
+      req: KibanaRequest<TypeOf<typeof paramSchema>, any, any, any>,
+      res: KibanaResponseFactory
+    ): Promise<IKibanaResponse<any>> {
+      verifyApiAccess(licenseState);
+      const alertsClient = context.alerting.getAlertsClient();
+      const { id } = req.params;
+      await alertsClient.enable({ id });
+      return res.noContent();
+    })
+  );
+};
diff --git a/x-pack/plugins/alerting/server/routes/find.test.ts b/x-pack/plugins/alerting/server/routes/find.test.ts
new file mode 100644
index 0000000000000..ba0114c99a9bd
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/find.test.ts
@@ -0,0 +1,150 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { findAlertRoute } from './find';
+import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock';
+import { mockLicenseState } from '../lib/license_state.mock';
+import { verifyApiAccess } from '../lib/license_api_access';
+import { mockHandlerArguments } from './_mock_handler_arguments';
+import { alertsClientMock } from '../alerts_client.mock';
+
+const alertsClient = alertsClientMock.create();
+
+jest.mock('../lib/license_api_access.ts', () => ({
+  verifyApiAccess: jest.fn(),
+}));
+
+beforeEach(() => {
+  jest.resetAllMocks();
+});
+
+describe('findAlertRoute', () => {
+  it('finds alerts with proper parameters', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    findAlertRoute(router, licenseState);
+
+    const [config, handler] = router.get.mock.calls[0];
+
+    expect(config.path).toMatchInlineSnapshot(`"/api/alert/_find"`);
+    expect(config.options).toMatchInlineSnapshot(`
+      Object {
+        "tags": Array [
+          "access:alerting-read",
+        ],
+      }
+    `);
+
+    const findResult = {
+      page: 1,
+      perPage: 1,
+      total: 0,
+      data: [],
+    };
+    alertsClient.find.mockResolvedValueOnce(findResult);
+
+    const [context, req, res] = mockHandlerArguments(
+      { alertsClient },
+      {
+        query: {
+          per_page: 1,
+          page: 1,
+          default_search_operator: 'OR',
+        },
+      },
+      ['ok']
+    );
+
+    expect(await handler(context, req, res)).toMatchInlineSnapshot(`
+      Object {
+        "body": Object {
+          "data": Array [],
+          "page": 1,
+          "perPage": 1,
+          "total": 0,
+        },
+      }
+    `);
+
+    expect(alertsClient.find).toHaveBeenCalledTimes(1);
+    expect(alertsClient.find.mock.calls[0]).toMatchInlineSnapshot(`
+      Array [
+        Object {
+          "options": Object {
+            "defaultSearchOperator": "OR",
+            "fields": undefined,
+            "filter": undefined,
+            "page": 1,
+            "perPage": 1,
+            "search": undefined,
+            "sortField": undefined,
+          },
+        },
+      ]
+    `);
+
+    expect(res.ok).toHaveBeenCalledWith({
+      body: findResult,
+    });
+  });
+
+  it('ensures the license allows finding alerts', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    findAlertRoute(router, licenseState);
+
+    const [, handler] = router.get.mock.calls[0];
+
+    alertsClient.find.mockResolvedValueOnce({
+      page: 1,
+      perPage: 1,
+      total: 0,
+      data: [],
+    });
+
+    const [context, req, res] = mockHandlerArguments(alertsClient, {
+      query: {
+        per_page: 1,
+        page: 1,
+        default_search_operator: 'OR',
+      },
+    });
+
+    await handler(context, req, res);
+
+    expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
+  });
+
+  it('ensures the license check prevents finding alerts', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    (verifyApiAccess as jest.Mock).mockImplementation(() => {
+      throw new Error('OMG');
+    });
+
+    findAlertRoute(router, licenseState);
+
+    const [, handler] = router.get.mock.calls[0];
+
+    const [context, req, res] = mockHandlerArguments(
+      {},
+      {
+        query: {
+          per_page: 1,
+          page: 1,
+          default_search_operator: 'OR',
+        },
+      },
+      ['ok']
+    );
+    expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`);
+
+    expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
+  });
+});
diff --git a/x-pack/plugins/alerting/server/routes/find.ts b/x-pack/plugins/alerting/server/routes/find.ts
new file mode 100644
index 0000000000000..16f53aa218895
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/find.ts
@@ -0,0 +1,90 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { schema, TypeOf } from '@kbn/config-schema';
+import {
+  IRouter,
+  RequestHandlerContext,
+  KibanaRequest,
+  IKibanaResponse,
+  KibanaResponseFactory,
+} from 'kibana/server';
+import { FindOptions } from '../../../alerting/server';
+import { LicenseState } from '../lib/license_state';
+import { verifyApiAccess } from '../lib/license_api_access';
+
+// config definition
+const querySchema = schema.object({
+  per_page: schema.number({ defaultValue: 10, min: 0 }),
+  page: schema.number({ defaultValue: 1, min: 1 }),
+  search: schema.maybe(schema.string()),
+  default_search_operator: schema.oneOf([schema.literal('OR'), schema.literal('AND')], {
+    defaultValue: 'OR',
+  }),
+  search_fields: schema.maybe(schema.oneOf([schema.arrayOf(schema.string()), schema.string()])),
+  sort_field: schema.maybe(schema.string()),
+  has_reference: schema.maybe(
+    // use nullable as maybe is currently broken
+    // in config-schema
+    schema.nullable(
+      schema.object({
+        type: schema.string(),
+        id: schema.string(),
+      })
+    )
+  ),
+  fields: schema.maybe(schema.arrayOf(schema.string())),
+  filter: schema.maybe(schema.string()),
+});
+
+export const findAlertRoute = (router: IRouter, licenseState: LicenseState) => {
+  router.get(
+    {
+      path: '/api/alert/_find',
+      validate: {
+        query: querySchema,
+      },
+      options: {
+        tags: ['access:alerting-read'],
+      },
+    },
+    router.handleLegacyErrors(async function(
+      context: RequestHandlerContext,
+      req: KibanaRequest<any, TypeOf<typeof querySchema>, any, any>,
+      res: KibanaResponseFactory
+    ): Promise<IKibanaResponse<any>> {
+      verifyApiAccess(licenseState);
+      const alertsClient = context.alerting.getAlertsClient();
+      const query = req.query;
+      const options: FindOptions['options'] = {
+        perPage: query.per_page,
+        page: query.page,
+        search: query.search,
+        defaultSearchOperator: query.default_search_operator,
+        sortField: query.sort_field,
+        fields: query.fields,
+        filter: query.filter,
+      };
+
+      if (query.search_fields) {
+        options.searchFields = Array.isArray(query.search_fields)
+          ? query.search_fields
+          : [query.search_fields];
+      }
+
+      if (query.has_reference) {
+        options.hasReference = query.has_reference;
+      }
+
+      const findResult = await alertsClient.find({
+        options,
+      });
+      return res.ok({
+        body: findResult,
+      });
+    })
+  );
+};
diff --git a/x-pack/plugins/alerting/server/routes/get.test.ts b/x-pack/plugins/alerting/server/routes/get.test.ts
new file mode 100644
index 0000000000000..4506700c6d9cc
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/get.test.ts
@@ -0,0 +1,140 @@
+/*
+ * 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 { getAlertRoute } from './get';
+import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock';
+import { mockLicenseState } from '../lib/license_state.mock';
+import { verifyApiAccess } from '../lib/license_api_access';
+import { mockHandlerArguments } from './_mock_handler_arguments';
+import { alertsClientMock } from '../alerts_client.mock';
+
+const alertsClient = alertsClientMock.create();
+jest.mock('../lib/license_api_access.ts', () => ({
+  verifyApiAccess: jest.fn(),
+}));
+
+beforeEach(() => {
+  jest.resetAllMocks();
+});
+
+describe('getAlertRoute', () => {
+  const mockedAlert = {
+    id: '1',
+    alertTypeId: '1',
+    schedule: { interval: '10s' },
+    params: {
+      bar: true,
+    },
+    createdAt: new Date(),
+    updatedAt: new Date(),
+    actions: [
+      {
+        group: 'default',
+        id: '2',
+        actionTypeId: 'test',
+        params: {
+          foo: true,
+        },
+      },
+    ],
+    consumer: 'bar',
+    name: 'abc',
+    tags: ['foo'],
+    enabled: true,
+    muteAll: false,
+    createdBy: '',
+    updatedBy: '',
+    apiKey: '',
+    apiKeyOwner: '',
+    throttle: '30s',
+    mutedInstanceIds: [],
+  };
+
+  it('gets an alert with proper parameters', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    getAlertRoute(router, licenseState);
+    const [config, handler] = router.get.mock.calls[0];
+
+    expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}"`);
+    expect(config.options).toMatchInlineSnapshot(`
+      Object {
+        "tags": Array [
+          "access:alerting-read",
+        ],
+      }
+    `);
+
+    alertsClient.get.mockResolvedValueOnce(mockedAlert);
+
+    const [context, req, res] = mockHandlerArguments(
+      { alertsClient },
+      {
+        params: { id: '1' },
+      },
+      ['ok']
+    );
+    await handler(context, req, res);
+
+    expect(alertsClient.get).toHaveBeenCalledTimes(1);
+    expect(alertsClient.get.mock.calls[0][0].id).toEqual('1');
+
+    expect(res.ok).toHaveBeenCalledWith({
+      body: mockedAlert,
+    });
+  });
+
+  it('ensures the license allows getting alerts', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    getAlertRoute(router, licenseState);
+
+    const [, handler] = router.get.mock.calls[0];
+
+    alertsClient.get.mockResolvedValueOnce(mockedAlert);
+
+    const [context, req, res] = mockHandlerArguments(
+      alertsClient,
+      {
+        params: { id: '1' },
+      },
+      ['ok']
+    );
+
+    await handler(context, req, res);
+
+    expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
+  });
+
+  it('ensures the license check prevents getting alerts', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    (verifyApiAccess as jest.Mock).mockImplementation(() => {
+      throw new Error('OMG');
+    });
+
+    getAlertRoute(router, licenseState);
+
+    const [, handler] = router.get.mock.calls[0];
+
+    alertsClient.get.mockResolvedValueOnce(mockedAlert);
+
+    const [context, req, res] = mockHandlerArguments(
+      alertsClient,
+      {
+        params: { id: '1' },
+      },
+      ['ok']
+    );
+
+    expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`);
+
+    expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
+  });
+});
diff --git a/x-pack/plugins/alerting/server/routes/get.ts b/x-pack/plugins/alerting/server/routes/get.ts
new file mode 100644
index 0000000000000..407d80b0f87ab
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/get.ts
@@ -0,0 +1,46 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { schema, TypeOf } from '@kbn/config-schema';
+import {
+  IRouter,
+  RequestHandlerContext,
+  KibanaRequest,
+  IKibanaResponse,
+  KibanaResponseFactory,
+} from 'kibana/server';
+import { LicenseState } from '../lib/license_state';
+import { verifyApiAccess } from '../lib/license_api_access';
+
+const paramSchema = schema.object({
+  id: schema.string(),
+});
+
+export const getAlertRoute = (router: IRouter, licenseState: LicenseState) => {
+  router.get(
+    {
+      path: `/api/alert/{id}`,
+      validate: {
+        params: paramSchema,
+      },
+      options: {
+        tags: ['access:alerting-read'],
+      },
+    },
+    router.handleLegacyErrors(async function(
+      context: RequestHandlerContext,
+      req: KibanaRequest<TypeOf<typeof paramSchema>, any, any, any>,
+      res: KibanaResponseFactory
+    ): Promise<IKibanaResponse<any>> {
+      verifyApiAccess(licenseState);
+      const alertsClient = context.alerting.getAlertsClient();
+      const { id } = req.params;
+      return res.ok({
+        body: await alertsClient.get({ id }),
+      });
+    })
+  );
+};
diff --git a/x-pack/plugins/alerting/server/routes/get_alert_state.test.ts b/x-pack/plugins/alerting/server/routes/get_alert_state.test.ts
new file mode 100644
index 0000000000000..eb51c96b88e5e
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/get_alert_state.test.ts
@@ -0,0 +1,170 @@
+/*
+ * 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 { getAlertStateRoute } from './get_alert_state';
+import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock';
+import { mockLicenseState } from '../lib/license_state.mock';
+import { mockHandlerArguments } from './_mock_handler_arguments';
+import { SavedObjectsErrorHelpers } from 'src/core/server/saved_objects';
+import { alertsClientMock } from '../alerts_client.mock';
+
+const alertsClient = alertsClientMock.create();
+jest.mock('../lib/license_api_access.ts', () => ({
+  verifyApiAccess: jest.fn(),
+}));
+
+beforeEach(() => {
+  jest.resetAllMocks();
+});
+
+describe('getAlertStateRoute', () => {
+  const mockedAlertState = {
+    alertTypeState: {
+      some: 'value',
+    },
+    alertInstances: {
+      first_instance: {
+        state: {},
+        meta: {
+          lastScheduledActions: {
+            group: 'first_group',
+            date: new Date(),
+          },
+        },
+      },
+      second_instance: {},
+    },
+  };
+
+  it('gets alert state', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    getAlertStateRoute(router, licenseState);
+
+    const [config, handler] = router.get.mock.calls[0];
+
+    expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}/state"`);
+    expect(config.options).toMatchInlineSnapshot(`
+      Object {
+        "tags": Array [
+          "access:alerting-read",
+        ],
+      }
+    `);
+
+    alertsClient.getAlertState.mockResolvedValueOnce(mockedAlertState);
+
+    const [context, req, res] = mockHandlerArguments(
+      { alertsClient },
+      {
+        params: {
+          id: '1',
+        },
+      },
+      ['ok']
+    );
+
+    await handler(context, req, res);
+
+    expect(alertsClient.getAlertState).toHaveBeenCalledTimes(1);
+    expect(alertsClient.getAlertState.mock.calls[0]).toMatchInlineSnapshot(`
+      Array [
+        Object {
+          "id": "1",
+        },
+      ]
+    `);
+
+    expect(res.ok).toHaveBeenCalled();
+  });
+
+  it('returns NO-CONTENT when alert exists but has no task state yet', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    getAlertStateRoute(router, licenseState);
+
+    const [config, handler] = router.get.mock.calls[0];
+
+    expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}/state"`);
+    expect(config.options).toMatchInlineSnapshot(`
+      Object {
+        "tags": Array [
+          "access:alerting-read",
+        ],
+      }
+    `);
+
+    alertsClient.getAlertState.mockResolvedValueOnce(undefined);
+
+    const [context, req, res] = mockHandlerArguments(
+      { alertsClient },
+      {
+        params: {
+          id: '1',
+        },
+      },
+      ['noContent']
+    );
+
+    expect(await handler(context, req, res)).toEqual(undefined);
+
+    expect(alertsClient.getAlertState).toHaveBeenCalledTimes(1);
+    expect(alertsClient.getAlertState.mock.calls[0]).toMatchInlineSnapshot(`
+      Array [
+        Object {
+          "id": "1",
+        },
+      ]
+    `);
+
+    expect(res.noContent).toHaveBeenCalled();
+  });
+
+  it('returns NOT-FOUND when alert is not found', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    getAlertStateRoute(router, licenseState);
+
+    const [config, handler] = router.get.mock.calls[0];
+
+    expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}/state"`);
+    expect(config.options).toMatchInlineSnapshot(`
+      Object {
+        "tags": Array [
+          "access:alerting-read",
+        ],
+      }
+    `);
+
+    alertsClient.getAlertState = jest
+      .fn()
+      .mockResolvedValueOnce(SavedObjectsErrorHelpers.createGenericNotFoundError('alert', '1'));
+
+    const [context, req, res] = mockHandlerArguments(
+      { alertsClient },
+      {
+        params: {
+          id: '1',
+        },
+      },
+      ['notFound']
+    );
+
+    expect(await handler(context, req, res)).toEqual(undefined);
+
+    expect(alertsClient.getAlertState).toHaveBeenCalledTimes(1);
+    expect(alertsClient.getAlertState.mock.calls[0]).toMatchInlineSnapshot(`
+      Array [
+        Object {
+          "id": "1",
+        },
+      ]
+    `);
+  });
+});
diff --git a/x-pack/plugins/alerting/server/routes/get_alert_state.ts b/x-pack/plugins/alerting/server/routes/get_alert_state.ts
new file mode 100644
index 0000000000000..b419889eea422
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/get_alert_state.ts
@@ -0,0 +1,45 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { schema, TypeOf } from '@kbn/config-schema';
+import {
+  IRouter,
+  RequestHandlerContext,
+  KibanaRequest,
+  IKibanaResponse,
+  KibanaResponseFactory,
+} from 'kibana/server';
+import { LicenseState } from '../lib/license_state';
+import { verifyApiAccess } from '../lib/license_api_access';
+
+const paramSchema = schema.object({
+  id: schema.string(),
+});
+
+export const getAlertStateRoute = (router: IRouter, licenseState: LicenseState) => {
+  router.get(
+    {
+      path: '/api/alert/{id}/state',
+      validate: {
+        params: paramSchema,
+      },
+      options: {
+        tags: ['access:alerting-read'],
+      },
+    },
+    router.handleLegacyErrors(async function(
+      context: RequestHandlerContext,
+      req: KibanaRequest<TypeOf<typeof paramSchema>, any, any, any>,
+      res: KibanaResponseFactory
+    ): Promise<IKibanaResponse<any>> {
+      verifyApiAccess(licenseState);
+      const alertsClient = context.alerting.getAlertsClient();
+      const { id } = req.params;
+      const state = await alertsClient.getAlertState({ id });
+      return state ? res.ok({ body: state }) : res.noContent();
+    })
+  );
+};
diff --git a/x-pack/legacy/plugins/alerting/server/routes/index.ts b/x-pack/plugins/alerting/server/routes/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/alerting/server/routes/index.ts
rename to x-pack/plugins/alerting/server/routes/index.ts
diff --git a/x-pack/plugins/alerting/server/routes/list_alert_types.test.ts b/x-pack/plugins/alerting/server/routes/list_alert_types.test.ts
new file mode 100644
index 0000000000000..3e9f57d55122d
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/list_alert_types.test.ts
@@ -0,0 +1,147 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { listAlertTypesRoute } from './list_alert_types';
+import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock';
+import { mockLicenseState } from '../lib/license_state.mock';
+import { verifyApiAccess } from '../lib/license_api_access';
+import { mockHandlerArguments } from './_mock_handler_arguments';
+
+jest.mock('../lib/license_api_access.ts', () => ({
+  verifyApiAccess: jest.fn(),
+}));
+
+beforeEach(() => {
+  jest.resetAllMocks();
+});
+
+describe('listAlertTypesRoute', () => {
+  it('lists alert types with proper parameters', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    listAlertTypesRoute(router, licenseState);
+
+    const [config, handler] = router.get.mock.calls[0];
+
+    expect(config.path).toMatchInlineSnapshot(`"/api/alert/types"`);
+    expect(config.options).toMatchInlineSnapshot(`
+      Object {
+        "tags": Array [
+          "access:alerting-read",
+        ],
+      }
+    `);
+
+    const listTypes = [
+      {
+        id: '1',
+        name: 'name',
+        actionGroups: [],
+      },
+    ];
+
+    const [context, req, res] = mockHandlerArguments({ listTypes }, {}, ['ok']);
+
+    expect(await handler(context, req, res)).toMatchInlineSnapshot(`
+      Object {
+        "body": Array [
+          Object {
+            "actionGroups": Array [],
+            "id": "1",
+            "name": "name",
+          },
+        ],
+      }
+    `);
+
+    expect(context.alerting.listTypes).toHaveBeenCalledTimes(1);
+
+    expect(res.ok).toHaveBeenCalledWith({
+      body: listTypes,
+    });
+  });
+
+  it('ensures the license allows listing alert types', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    listAlertTypesRoute(router, licenseState);
+
+    const [config, handler] = router.get.mock.calls[0];
+
+    expect(config.path).toMatchInlineSnapshot(`"/api/alert/types"`);
+    expect(config.options).toMatchInlineSnapshot(`
+      Object {
+        "tags": Array [
+          "access:alerting-read",
+        ],
+      }
+    `);
+
+    const listTypes = [
+      {
+        id: '1',
+        name: 'name',
+        enabled: true,
+      },
+    ];
+
+    const [context, req, res] = mockHandlerArguments(
+      { listTypes },
+      {
+        params: { id: '1' },
+      },
+      ['ok']
+    );
+
+    await handler(context, req, res);
+
+    expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
+  });
+
+  it('ensures the license check prevents listing alert types', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    (verifyApiAccess as jest.Mock).mockImplementation(() => {
+      throw new Error('OMG');
+    });
+
+    listAlertTypesRoute(router, licenseState);
+
+    const [config, handler] = router.get.mock.calls[0];
+
+    expect(config.path).toMatchInlineSnapshot(`"/api/alert/types"`);
+    expect(config.options).toMatchInlineSnapshot(`
+      Object {
+        "tags": Array [
+          "access:alerting-read",
+        ],
+      }
+    `);
+
+    const listTypes = [
+      {
+        id: '1',
+        name: 'name',
+        actionGroups: [],
+      },
+    ];
+
+    const [context, req, res] = mockHandlerArguments(
+      { listTypes },
+      {
+        params: { id: '1' },
+      },
+      ['ok']
+    );
+
+    expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`);
+
+    expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
+  });
+});
diff --git a/x-pack/plugins/alerting/server/routes/list_alert_types.ts b/x-pack/plugins/alerting/server/routes/list_alert_types.ts
new file mode 100644
index 0000000000000..e33bb9a010bf7
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/list_alert_types.ts
@@ -0,0 +1,37 @@
+/*
+ * 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,
+  RequestHandlerContext,
+  KibanaRequest,
+  IKibanaResponse,
+  KibanaResponseFactory,
+} from 'kibana/server';
+import { LicenseState } from '../lib/license_state';
+import { verifyApiAccess } from '../lib/license_api_access';
+
+export const listAlertTypesRoute = (router: IRouter, licenseState: LicenseState) => {
+  router.get(
+    {
+      path: `/api/alert/types`,
+      validate: {},
+      options: {
+        tags: ['access:alerting-read'],
+      },
+    },
+    router.handleLegacyErrors(async function(
+      context: RequestHandlerContext,
+      req: KibanaRequest<any, any, any, any>,
+      res: KibanaResponseFactory
+    ): Promise<IKibanaResponse<any>> {
+      verifyApiAccess(licenseState);
+      return res.ok({
+        body: context.alerting.listTypes(),
+      });
+    })
+  );
+};
diff --git a/x-pack/plugins/alerting/server/routes/mute_all.test.ts b/x-pack/plugins/alerting/server/routes/mute_all.test.ts
new file mode 100644
index 0000000000000..4c880e176d2df
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/mute_all.test.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 { muteAllAlertRoute } from './mute_all';
+import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock';
+import { mockLicenseState } from '../lib/license_state.mock';
+import { mockHandlerArguments } from './_mock_handler_arguments';
+import { alertsClientMock } from '../alerts_client.mock';
+
+const alertsClient = alertsClientMock.create();
+jest.mock('../lib/license_api_access.ts', () => ({
+  verifyApiAccess: jest.fn(),
+}));
+
+beforeEach(() => {
+  jest.resetAllMocks();
+});
+
+describe('muteAllAlertRoute', () => {
+  it('mute an alert', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    muteAllAlertRoute(router, licenseState);
+
+    const [config, handler] = router.post.mock.calls[0];
+
+    expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}/_mute_all"`);
+    expect(config.options).toMatchInlineSnapshot(`
+      Object {
+        "tags": Array [
+          "access:alerting-all",
+        ],
+      }
+    `);
+
+    alertsClient.muteAll.mockResolvedValueOnce();
+
+    const [context, req, res] = mockHandlerArguments(
+      { alertsClient },
+      {
+        params: {
+          id: '1',
+        },
+      },
+      ['noContent']
+    );
+
+    expect(await handler(context, req, res)).toEqual(undefined);
+
+    expect(alertsClient.muteAll).toHaveBeenCalledTimes(1);
+    expect(alertsClient.muteAll.mock.calls[0]).toMatchInlineSnapshot(`
+      Array [
+        Object {
+          "id": "1",
+        },
+      ]
+    `);
+
+    expect(res.noContent).toHaveBeenCalled();
+  });
+});
diff --git a/x-pack/plugins/alerting/server/routes/mute_all.ts b/x-pack/plugins/alerting/server/routes/mute_all.ts
new file mode 100644
index 0000000000000..796efd457f478
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/mute_all.ts
@@ -0,0 +1,45 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { schema, TypeOf } from '@kbn/config-schema';
+import {
+  IRouter,
+  RequestHandlerContext,
+  KibanaRequest,
+  IKibanaResponse,
+  KibanaResponseFactory,
+} from 'kibana/server';
+import { LicenseState } from '../lib/license_state';
+import { verifyApiAccess } from '../lib/license_api_access';
+
+const paramSchema = schema.object({
+  id: schema.string(),
+});
+
+export const muteAllAlertRoute = (router: IRouter, licenseState: LicenseState) => {
+  router.post(
+    {
+      path: '/api/alert/{id}/_mute_all',
+      validate: {
+        params: paramSchema,
+      },
+      options: {
+        tags: ['access:alerting-all'],
+      },
+    },
+    router.handleLegacyErrors(async function(
+      context: RequestHandlerContext,
+      req: KibanaRequest<TypeOf<typeof paramSchema>, any, any, any>,
+      res: KibanaResponseFactory
+    ): Promise<IKibanaResponse<any>> {
+      verifyApiAccess(licenseState);
+      const alertsClient = context.alerting.getAlertsClient();
+      const { id } = req.params;
+      await alertsClient.muteAll({ id });
+      return res.noContent();
+    })
+  );
+};
diff --git a/x-pack/plugins/alerting/server/routes/mute_instance.test.ts b/x-pack/plugins/alerting/server/routes/mute_instance.test.ts
new file mode 100644
index 0000000000000..939864972c35d
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/mute_instance.test.ts
@@ -0,0 +1,69 @@
+/*
+ * 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 { muteAlertInstanceRoute } from './mute_instance';
+import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock';
+import { mockLicenseState } from '../lib/license_state.mock';
+import { mockHandlerArguments } from './_mock_handler_arguments';
+import { alertsClientMock } from '../alerts_client.mock';
+
+const alertsClient = alertsClientMock.create();
+jest.mock('../lib/license_api_access.ts', () => ({
+  verifyApiAccess: jest.fn(),
+}));
+
+beforeEach(() => {
+  jest.resetAllMocks();
+});
+
+describe('muteAlertInstanceRoute', () => {
+  it('mutes an alert instance', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    muteAlertInstanceRoute(router, licenseState);
+
+    const [config, handler] = router.post.mock.calls[0];
+
+    expect(config.path).toMatchInlineSnapshot(
+      `"/api/alert/{alertId}/alert_instance/{alertInstanceId}/_mute"`
+    );
+    expect(config.options).toMatchInlineSnapshot(`
+      Object {
+        "tags": Array [
+          "access:alerting-all",
+        ],
+      }
+    `);
+
+    alertsClient.muteInstance.mockResolvedValueOnce();
+
+    const [context, req, res] = mockHandlerArguments(
+      { alertsClient },
+      {
+        params: {
+          alertId: '1',
+          alertInstanceId: '2',
+        },
+      },
+      ['noContent']
+    );
+
+    expect(await handler(context, req, res)).toEqual(undefined);
+
+    expect(alertsClient.muteInstance).toHaveBeenCalledTimes(1);
+    expect(alertsClient.muteInstance.mock.calls[0]).toMatchInlineSnapshot(`
+      Array [
+        Object {
+          "alertId": "1",
+          "alertInstanceId": "2",
+        },
+      ]
+    `);
+
+    expect(res.noContent).toHaveBeenCalled();
+  });
+});
diff --git a/x-pack/plugins/alerting/server/routes/mute_instance.ts b/x-pack/plugins/alerting/server/routes/mute_instance.ts
new file mode 100644
index 0000000000000..bae7b00548a26
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/mute_instance.ts
@@ -0,0 +1,46 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { schema, TypeOf } from '@kbn/config-schema';
+import {
+  IRouter,
+  RequestHandlerContext,
+  KibanaRequest,
+  IKibanaResponse,
+  KibanaResponseFactory,
+} from 'kibana/server';
+import { LicenseState } from '../lib/license_state';
+import { verifyApiAccess } from '../lib/license_api_access';
+
+const paramSchema = schema.object({
+  alertId: schema.string(),
+  alertInstanceId: schema.string(),
+});
+
+export const muteAlertInstanceRoute = (router: IRouter, licenseState: LicenseState) => {
+  router.post(
+    {
+      path: '/api/alert/{alertId}/alert_instance/{alertInstanceId}/_mute',
+      validate: {
+        params: paramSchema,
+      },
+      options: {
+        tags: ['access:alerting-all'],
+      },
+    },
+    router.handleLegacyErrors(async function(
+      context: RequestHandlerContext,
+      req: KibanaRequest<TypeOf<typeof paramSchema>, any, any, any>,
+      res: KibanaResponseFactory
+    ): Promise<IKibanaResponse<any>> {
+      verifyApiAccess(licenseState);
+      const alertsClient = context.alerting.getAlertsClient();
+      const { alertId, alertInstanceId } = req.params;
+      await alertsClient.muteInstance({ alertId, alertInstanceId });
+      return res.noContent();
+    })
+  );
+};
diff --git a/x-pack/plugins/alerting/server/routes/unmute_all.test.ts b/x-pack/plugins/alerting/server/routes/unmute_all.test.ts
new file mode 100644
index 0000000000000..cd14e9b2e7172
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/unmute_all.test.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 { unmuteAllAlertRoute } from './unmute_all';
+import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock';
+import { mockLicenseState } from '../lib/license_state.mock';
+import { mockHandlerArguments } from './_mock_handler_arguments';
+import { alertsClientMock } from '../alerts_client.mock';
+
+const alertsClient = alertsClientMock.create();
+jest.mock('../lib/license_api_access.ts', () => ({
+  verifyApiAccess: jest.fn(),
+}));
+
+beforeEach(() => {
+  jest.resetAllMocks();
+});
+
+describe('unmuteAllAlertRoute', () => {
+  it('unmutes an alert', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    unmuteAllAlertRoute(router, licenseState);
+
+    const [config, handler] = router.post.mock.calls[0];
+
+    expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}/_unmute_all"`);
+    expect(config.options).toMatchInlineSnapshot(`
+      Object {
+        "tags": Array [
+          "access:alerting-all",
+        ],
+      }
+    `);
+
+    alertsClient.unmuteAll.mockResolvedValueOnce();
+
+    const [context, req, res] = mockHandlerArguments(
+      { alertsClient },
+      {
+        params: {
+          id: '1',
+        },
+      },
+      ['noContent']
+    );
+
+    expect(await handler(context, req, res)).toEqual(undefined);
+
+    expect(alertsClient.unmuteAll).toHaveBeenCalledTimes(1);
+    expect(alertsClient.unmuteAll.mock.calls[0]).toMatchInlineSnapshot(`
+      Array [
+        Object {
+          "id": "1",
+        },
+      ]
+    `);
+
+    expect(res.noContent).toHaveBeenCalled();
+  });
+});
diff --git a/x-pack/plugins/alerting/server/routes/unmute_all.ts b/x-pack/plugins/alerting/server/routes/unmute_all.ts
new file mode 100644
index 0000000000000..5483f691b5462
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/unmute_all.ts
@@ -0,0 +1,45 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { schema, TypeOf } from '@kbn/config-schema';
+import {
+  IRouter,
+  RequestHandlerContext,
+  KibanaRequest,
+  IKibanaResponse,
+  KibanaResponseFactory,
+} from 'kibana/server';
+import { LicenseState } from '../lib/license_state';
+import { verifyApiAccess } from '../lib/license_api_access';
+
+const paramSchema = schema.object({
+  id: schema.string(),
+});
+
+export const unmuteAllAlertRoute = (router: IRouter, licenseState: LicenseState) => {
+  router.post(
+    {
+      path: '/api/alert/{id}/_unmute_all',
+      validate: {
+        params: paramSchema,
+      },
+      options: {
+        tags: ['access:alerting-all'],
+      },
+    },
+    router.handleLegacyErrors(async function(
+      context: RequestHandlerContext,
+      req: KibanaRequest<TypeOf<typeof paramSchema>, any, any, any>,
+      res: KibanaResponseFactory
+    ): Promise<IKibanaResponse<any>> {
+      verifyApiAccess(licenseState);
+      const alertsClient = context.alerting.getAlertsClient();
+      const { id } = req.params;
+      await alertsClient.unmuteAll({ id });
+      return res.noContent();
+    })
+  );
+};
diff --git a/x-pack/plugins/alerting/server/routes/unmute_instance.test.ts b/x-pack/plugins/alerting/server/routes/unmute_instance.test.ts
new file mode 100644
index 0000000000000..d74934f691710
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/unmute_instance.test.ts
@@ -0,0 +1,69 @@
+/*
+ * 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 { unmuteAlertInstanceRoute } from './unmute_instance';
+import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock';
+import { mockLicenseState } from '../lib/license_state.mock';
+import { mockHandlerArguments } from './_mock_handler_arguments';
+import { alertsClientMock } from '../alerts_client.mock';
+
+const alertsClient = alertsClientMock.create();
+jest.mock('../lib/license_api_access.ts', () => ({
+  verifyApiAccess: jest.fn(),
+}));
+
+beforeEach(() => {
+  jest.resetAllMocks();
+});
+
+describe('unmuteAlertInstanceRoute', () => {
+  it('unmutes an alert instance', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    unmuteAlertInstanceRoute(router, licenseState);
+
+    const [config, handler] = router.post.mock.calls[0];
+
+    expect(config.path).toMatchInlineSnapshot(
+      `"/api/alert/{alertId}/alert_instance/{alertInstanceId}/_unmute"`
+    );
+    expect(config.options).toMatchInlineSnapshot(`
+      Object {
+        "tags": Array [
+          "access:alerting-all",
+        ],
+      }
+    `);
+
+    alertsClient.unmuteInstance.mockResolvedValueOnce();
+
+    const [context, req, res] = mockHandlerArguments(
+      { alertsClient },
+      {
+        params: {
+          alertId: '1',
+          alertInstanceId: '2',
+        },
+      },
+      ['noContent']
+    );
+
+    expect(await handler(context, req, res)).toEqual(undefined);
+
+    expect(alertsClient.unmuteInstance).toHaveBeenCalledTimes(1);
+    expect(alertsClient.unmuteInstance.mock.calls[0]).toMatchInlineSnapshot(`
+      Array [
+        Object {
+          "alertId": "1",
+          "alertInstanceId": "2",
+        },
+      ]
+    `);
+
+    expect(res.noContent).toHaveBeenCalled();
+  });
+});
diff --git a/x-pack/plugins/alerting/server/routes/unmute_instance.ts b/x-pack/plugins/alerting/server/routes/unmute_instance.ts
new file mode 100644
index 0000000000000..fc24ea88ddb67
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/unmute_instance.ts
@@ -0,0 +1,46 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { schema, TypeOf } from '@kbn/config-schema';
+import {
+  IRouter,
+  RequestHandlerContext,
+  KibanaRequest,
+  IKibanaResponse,
+  KibanaResponseFactory,
+} from 'kibana/server';
+import { LicenseState } from '../lib/license_state';
+import { verifyApiAccess } from '../lib/license_api_access';
+
+const paramSchema = schema.object({
+  alertId: schema.string(),
+  alertInstanceId: schema.string(),
+});
+
+export const unmuteAlertInstanceRoute = (router: IRouter, licenseState: LicenseState) => {
+  router.post(
+    {
+      path: '/api/alert/{alertId}/alert_instance/{alertInstanceId}/_unmute',
+      validate: {
+        params: paramSchema,
+      },
+      options: {
+        tags: ['access:alerting-all'],
+      },
+    },
+    router.handleLegacyErrors(async function(
+      context: RequestHandlerContext,
+      req: KibanaRequest<TypeOf<typeof paramSchema>, any, any, any>,
+      res: KibanaResponseFactory
+    ): Promise<IKibanaResponse<any>> {
+      verifyApiAccess(licenseState);
+      const alertsClient = context.alerting.getAlertsClient();
+      const { alertId, alertInstanceId } = req.params;
+      await alertsClient.unmuteInstance({ alertId, alertInstanceId });
+      return res.noContent();
+    })
+  );
+};
diff --git a/x-pack/plugins/alerting/server/routes/update.test.ts b/x-pack/plugins/alerting/server/routes/update.test.ts
new file mode 100644
index 0000000000000..005367ef7979c
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/update.test.ts
@@ -0,0 +1,217 @@
+/*
+ * 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 { updateAlertRoute } from './update';
+import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock';
+import { mockLicenseState } from '../lib/license_state.mock';
+import { verifyApiAccess } from '../lib/license_api_access';
+import { mockHandlerArguments } from './_mock_handler_arguments';
+import { alertsClientMock } from '../alerts_client.mock';
+
+const alertsClient = alertsClientMock.create();
+jest.mock('../lib/license_api_access.ts', () => ({
+  verifyApiAccess: jest.fn(),
+}));
+
+beforeEach(() => {
+  jest.resetAllMocks();
+});
+
+describe('updateAlertRoute', () => {
+  const mockedResponse = {
+    id: '1',
+    alertTypeId: '1',
+    tags: ['foo'],
+    schedule: { interval: '12s' },
+    params: {
+      otherField: false,
+    },
+    createdAt: new Date(),
+    updatedAt: new Date(),
+    actions: [
+      {
+        group: 'default',
+        id: '2',
+        actionTypeId: 'test',
+        params: {
+          baz: true,
+        },
+      },
+    ],
+  };
+
+  it('updates an alert with proper parameters', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    updateAlertRoute(router, licenseState);
+
+    const [config, handler] = router.put.mock.calls[0];
+
+    expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}"`);
+    expect(config.options).toMatchInlineSnapshot(`
+      Object {
+        "tags": Array [
+          "access:alerting-all",
+        ],
+      }
+    `);
+
+    alertsClient.update.mockResolvedValueOnce(mockedResponse);
+
+    const [context, req, res] = mockHandlerArguments(
+      { alertsClient },
+      {
+        params: {
+          id: '1',
+        },
+        body: {
+          throttle: null,
+          name: 'abc',
+          tags: ['bar'],
+          schedule: { interval: '12s' },
+          params: {
+            otherField: false,
+          },
+          actions: [
+            {
+              group: 'default',
+              id: '2',
+              params: {
+                baz: true,
+              },
+            },
+          ],
+        },
+      },
+      ['ok']
+    );
+
+    expect(await handler(context, req, res)).toEqual({ body: mockedResponse });
+
+    expect(alertsClient.update).toHaveBeenCalledTimes(1);
+    expect(alertsClient.update.mock.calls[0]).toMatchInlineSnapshot(`
+      Array [
+        Object {
+          "data": Object {
+            "actions": Array [
+              Object {
+                "group": "default",
+                "id": "2",
+                "params": Object {
+                  "baz": true,
+                },
+              },
+            ],
+            "name": "abc",
+            "params": Object {
+              "otherField": false,
+            },
+            "schedule": Object {
+              "interval": "12s",
+            },
+            "tags": Array [
+              "bar",
+            ],
+          },
+          "id": "1",
+        },
+      ]
+    `);
+
+    expect(res.ok).toHaveBeenCalled();
+  });
+
+  it('ensures the license allows updating alerts', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    updateAlertRoute(router, licenseState);
+
+    const [, handler] = router.put.mock.calls[0];
+
+    alertsClient.update.mockResolvedValueOnce(mockedResponse);
+
+    const [context, req, res] = mockHandlerArguments(
+      { alertsClient },
+      {
+        params: {
+          id: '1',
+        },
+        body: {
+          throttle: null,
+          name: 'abc',
+          tags: ['bar'],
+          schedule: { interval: '12s' },
+          params: {
+            otherField: false,
+          },
+          actions: [
+            {
+              group: 'default',
+              id: '2',
+              params: {
+                baz: true,
+              },
+            },
+          ],
+        },
+      },
+      ['ok']
+    );
+
+    await handler(context, req, res);
+
+    expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
+  });
+
+  it('ensures the license check prevents updating alerts', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    (verifyApiAccess as jest.Mock).mockImplementation(() => {
+      throw new Error('OMG');
+    });
+
+    updateAlertRoute(router, licenseState);
+
+    const [, handler] = router.put.mock.calls[0];
+
+    alertsClient.update.mockResolvedValueOnce(mockedResponse);
+
+    const [context, req, res] = mockHandlerArguments(
+      { alertsClient },
+      {
+        params: {
+          id: '1',
+        },
+        body: {
+          throttle: null,
+          name: 'abc',
+          tags: ['bar'],
+          schedule: { interval: '12s' },
+          params: {
+            otherField: false,
+          },
+          actions: [
+            {
+              group: 'default',
+              id: '2',
+              params: {
+                baz: true,
+              },
+            },
+          ],
+        },
+      },
+      ['ok']
+    );
+
+    expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`);
+
+    expect(verifyApiAccess).toHaveBeenCalledWith(licenseState);
+  });
+});
diff --git a/x-pack/plugins/alerting/server/routes/update.ts b/x-pack/plugins/alerting/server/routes/update.ts
new file mode 100644
index 0000000000000..a402d13c5fbab
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/update.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 { schema, TypeOf } from '@kbn/config-schema';
+import {
+  IRouter,
+  RequestHandlerContext,
+  KibanaRequest,
+  IKibanaResponse,
+  KibanaResponseFactory,
+} from 'kibana/server';
+import { LicenseState } from '../lib/license_state';
+import { verifyApiAccess } from '../lib/license_api_access';
+import { validateDurationSchema } from '../lib';
+
+const paramSchema = schema.object({
+  id: schema.string(),
+});
+
+const bodySchema = schema.object({
+  name: schema.string(),
+  tags: schema.arrayOf(schema.string(), { defaultValue: [] }),
+  schedule: schema.object({
+    interval: schema.string({ validate: validateDurationSchema }),
+  }),
+  throttle: schema.nullable(schema.string({ validate: validateDurationSchema })),
+  params: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }),
+  actions: schema.arrayOf(
+    schema.object({
+      group: schema.string(),
+      id: schema.string(),
+      params: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }),
+      actionTypeId: schema.maybe(schema.string()),
+    }),
+    { defaultValue: [] }
+  ),
+});
+
+export const updateAlertRoute = (router: IRouter, licenseState: LicenseState) => {
+  router.put(
+    {
+      path: '/api/alert/{id}',
+      validate: {
+        body: bodySchema,
+        params: paramSchema,
+      },
+      options: {
+        tags: ['access:alerting-all'],
+      },
+    },
+    router.handleLegacyErrors(async function(
+      context: RequestHandlerContext,
+      req: KibanaRequest<TypeOf<typeof paramSchema>, any, TypeOf<typeof bodySchema>, any>,
+      res: KibanaResponseFactory
+    ): Promise<IKibanaResponse<any>> {
+      verifyApiAccess(licenseState);
+      const alertsClient = context.alerting.getAlertsClient();
+      const { id } = req.params;
+      const { name, actions, params, schedule, tags } = req.body;
+      return res.ok({
+        body: await alertsClient.update({
+          id,
+          data: { name, actions, params, schedule, tags },
+        }),
+      });
+    })
+  );
+};
diff --git a/x-pack/plugins/alerting/server/routes/update_api_key.test.ts b/x-pack/plugins/alerting/server/routes/update_api_key.test.ts
new file mode 100644
index 0000000000000..5e9821ac005e2
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/update_api_key.test.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 { updateApiKeyRoute } from './update_api_key';
+import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock';
+import { mockLicenseState } from '../lib/license_state.mock';
+import { mockHandlerArguments } from './_mock_handler_arguments';
+import { alertsClientMock } from '../alerts_client.mock';
+
+const alertsClient = alertsClientMock.create();
+jest.mock('../lib/license_api_access.ts', () => ({
+  verifyApiAccess: jest.fn(),
+}));
+
+beforeEach(() => {
+  jest.resetAllMocks();
+});
+
+describe('updateApiKeyRoute', () => {
+  it('updates api key for an alert', async () => {
+    const licenseState = mockLicenseState();
+    const router: RouterMock = mockRouter.create();
+
+    updateApiKeyRoute(router, licenseState);
+
+    const [config, handler] = router.post.mock.calls[0];
+
+    expect(config.path).toMatchInlineSnapshot(`"/api/alert/{id}/_update_api_key"`);
+    expect(config.options).toMatchInlineSnapshot(`
+      Object {
+        "tags": Array [
+          "access:alerting-all",
+        ],
+      }
+    `);
+
+    alertsClient.updateApiKey.mockResolvedValueOnce();
+
+    const [context, req, res] = mockHandlerArguments(
+      { alertsClient },
+      {
+        params: {
+          id: '1',
+        },
+      },
+      ['noContent']
+    );
+
+    expect(await handler(context, req, res)).toEqual(undefined);
+
+    expect(alertsClient.updateApiKey).toHaveBeenCalledTimes(1);
+    expect(alertsClient.updateApiKey.mock.calls[0]).toMatchInlineSnapshot(`
+      Array [
+        Object {
+          "id": "1",
+        },
+      ]
+    `);
+
+    expect(res.noContent).toHaveBeenCalled();
+  });
+});
diff --git a/x-pack/plugins/alerting/server/routes/update_api_key.ts b/x-pack/plugins/alerting/server/routes/update_api_key.ts
new file mode 100644
index 0000000000000..0951b6c7b939e
--- /dev/null
+++ b/x-pack/plugins/alerting/server/routes/update_api_key.ts
@@ -0,0 +1,45 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { schema, TypeOf } from '@kbn/config-schema';
+import {
+  IRouter,
+  RequestHandlerContext,
+  KibanaRequest,
+  IKibanaResponse,
+  KibanaResponseFactory,
+} from 'kibana/server';
+import { LicenseState } from '../lib/license_state';
+import { verifyApiAccess } from '../lib/license_api_access';
+
+const paramSchema = schema.object({
+  id: schema.string(),
+});
+
+export const updateApiKeyRoute = (router: IRouter, licenseState: LicenseState) => {
+  router.post(
+    {
+      path: '/api/alert/{id}/_update_api_key',
+      validate: {
+        params: paramSchema,
+      },
+      options: {
+        tags: ['access:alerting-all'],
+      },
+    },
+    router.handleLegacyErrors(async function(
+      context: RequestHandlerContext,
+      req: KibanaRequest<TypeOf<typeof paramSchema>, any, any, any>,
+      res: KibanaResponseFactory
+    ): Promise<IKibanaResponse<any>> {
+      verifyApiAccess(licenseState);
+      const alertsClient = context.alerting.getAlertsClient();
+      const { id } = req.params;
+      await alertsClient.updateApiKey({ id });
+      return res.noContent();
+    })
+  );
+};
diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/alert_task_instance.test.ts b/x-pack/plugins/alerting/server/task_runner/alert_task_instance.test.ts
similarity index 98%
rename from x-pack/legacy/plugins/alerting/server/task_runner/alert_task_instance.test.ts
rename to x-pack/plugins/alerting/server/task_runner/alert_task_instance.test.ts
index 9cbe91a4dbced..28b3576dffc6e 100644
--- a/x-pack/legacy/plugins/alerting/server/task_runner/alert_task_instance.test.ts
+++ b/x-pack/plugins/alerting/server/task_runner/alert_task_instance.test.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { ConcreteTaskInstance, TaskStatus } from '../../../../../plugins/task_manager/server';
+import { ConcreteTaskInstance, TaskStatus } from '../../../../plugins/task_manager/server';
 import { AlertTaskInstance, taskInstanceToAlertTaskInstance } from './alert_task_instance';
 import uuid from 'uuid';
 import { SanitizedAlert } from '../types';
diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/alert_task_instance.ts b/x-pack/plugins/alerting/server/task_runner/alert_task_instance.ts
similarity index 94%
rename from x-pack/legacy/plugins/alerting/server/task_runner/alert_task_instance.ts
rename to x-pack/plugins/alerting/server/task_runner/alert_task_instance.ts
index 6bc318070377d..9f5e3851aa29d 100644
--- a/x-pack/legacy/plugins/alerting/server/task_runner/alert_task_instance.ts
+++ b/x-pack/plugins/alerting/server/task_runner/alert_task_instance.ts
@@ -6,7 +6,7 @@
 import * as t from 'io-ts';
 import { pipe } from 'fp-ts/lib/pipeable';
 import { fold } from 'fp-ts/lib/Either';
-import { ConcreteTaskInstance } from '../../../../../plugins/task_manager/server';
+import { ConcreteTaskInstance } from '../../../../plugins/task_manager/server';
 import { SanitizedAlert, AlertTaskState, alertParamsSchema, alertStateSchema } from '../../common';
 
 export interface AlertTaskInstance extends ConcreteTaskInstance {
diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/create_execution_handler.test.ts b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts
similarity index 98%
rename from x-pack/legacy/plugins/alerting/server/task_runner/create_execution_handler.test.ts
rename to x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts
index 02fa09ba97a65..1b3a9ed8d7b06 100644
--- a/x-pack/legacy/plugins/alerting/server/task_runner/create_execution_handler.test.ts
+++ b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts
@@ -6,7 +6,7 @@
 
 import { AlertType } from '../types';
 import { createExecutionHandler } from './create_execution_handler';
-import { loggingServiceMock } from '../../../../../../src/core/server/mocks';
+import { loggingServiceMock } from '../../../../../src/core/server/mocks';
 
 const alertType: AlertType = {
   id: 'test',
diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/create_execution_handler.ts b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts
similarity index 94%
rename from x-pack/legacy/plugins/alerting/server/task_runner/create_execution_handler.ts
rename to x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts
index 737f86a881c1f..9b2d218114c31 100644
--- a/x-pack/legacy/plugins/alerting/server/task_runner/create_execution_handler.ts
+++ b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts
@@ -6,9 +6,9 @@
 
 import { pluck } from 'lodash';
 import { AlertAction, State, Context, AlertType } from '../types';
-import { Logger } from '../../../../../../src/core/server';
+import { Logger } from '../../../../../src/core/server';
 import { transformActionParams } from './transform_action_params';
-import { PluginStartContract as ActionsPluginStartContract } from '../../../../../plugins/actions/server';
+import { PluginStartContract as ActionsPluginStartContract } from '../../../../plugins/actions/server';
 
 interface CreateExecutionHandlerOptions {
   alertId: string;
diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/get_next_run_at.test.ts b/x-pack/plugins/alerting/server/task_runner/get_next_run_at.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/alerting/server/task_runner/get_next_run_at.test.ts
rename to x-pack/plugins/alerting/server/task_runner/get_next_run_at.test.ts
diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/get_next_run_at.ts b/x-pack/plugins/alerting/server/task_runner/get_next_run_at.ts
similarity index 100%
rename from x-pack/legacy/plugins/alerting/server/task_runner/get_next_run_at.ts
rename to x-pack/plugins/alerting/server/task_runner/get_next_run_at.ts
diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/index.ts b/x-pack/plugins/alerting/server/task_runner/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/alerting/server/task_runner/index.ts
rename to x-pack/plugins/alerting/server/task_runner/index.ts
diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts
similarity index 97%
rename from x-pack/legacy/plugins/alerting/server/task_runner/task_runner.test.ts
rename to x-pack/plugins/alerting/server/task_runner/task_runner.test.ts
index b6dd4b3435fcb..af0e7b658240a 100644
--- a/x-pack/legacy/plugins/alerting/server/task_runner/task_runner.test.ts
+++ b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts
@@ -7,14 +7,11 @@
 import sinon from 'sinon';
 import { schema } from '@kbn/config-schema';
 import { AlertExecutorOptions } from '../types';
-import { ConcreteTaskInstance, TaskStatus } from '../../../../../plugins/task_manager/server';
+import { ConcreteTaskInstance, TaskStatus } from '../../../../plugins/task_manager/server';
 import { TaskRunnerContext } from './task_runner_factory';
 import { TaskRunner } from './task_runner';
-import { encryptedSavedObjectsMock } from '../../../../../plugins/encrypted_saved_objects/server/mocks';
-import {
-  savedObjectsClientMock,
-  loggingServiceMock,
-} from '../../../../../../src/core/server/mocks';
+import { encryptedSavedObjectsMock } from '../../../../plugins/encrypted_saved_objects/server/mocks';
+import { savedObjectsClientMock, loggingServiceMock } from '../../../../../src/core/server/mocks';
 
 const alertType = {
   id: 'test',
diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts
similarity index 97%
rename from x-pack/legacy/plugins/alerting/server/task_runner/task_runner.ts
rename to x-pack/plugins/alerting/server/task_runner/task_runner.ts
index 2632decb125f0..34278ee62dbc4 100644
--- a/x-pack/legacy/plugins/alerting/server/task_runner/task_runner.ts
+++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts
@@ -5,10 +5,10 @@
  */
 
 import { pick, mapValues, omit } from 'lodash';
-import { Logger } from '../../../../../../src/core/server';
-import { SavedObject } from '../../../../../../src/core/server';
+import { Logger } from '../../../../../src/core/server';
+import { SavedObject } from '../../../../../src/core/server';
 import { TaskRunnerContext } from './task_runner_factory';
-import { ConcreteTaskInstance } from '../../../../../plugins/task_manager/server';
+import { ConcreteTaskInstance } from '../../../../plugins/task_manager/server';
 import { createExecutionHandler } from './create_execution_handler';
 import { AlertInstance, createAlertInstanceFactory } from '../alert_instance';
 import { getNextRunAt } from './get_next_run_at';
diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/task_runner_factory.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts
similarity index 89%
rename from x-pack/legacy/plugins/alerting/server/task_runner/task_runner_factory.test.ts
rename to x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts
index 7474fcfb4baaa..4dad46ef32f21 100644
--- a/x-pack/legacy/plugins/alerting/server/task_runner/task_runner_factory.test.ts
+++ b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts
@@ -5,13 +5,10 @@
  */
 
 import sinon from 'sinon';
-import { ConcreteTaskInstance, TaskStatus } from '../../../../../plugins/task_manager/server';
+import { ConcreteTaskInstance, TaskStatus } from '../../../../plugins/task_manager/server';
 import { TaskRunnerContext, TaskRunnerFactory } from './task_runner_factory';
-import { encryptedSavedObjectsMock } from '../../../../../plugins/encrypted_saved_objects/server/mocks';
-import {
-  savedObjectsClientMock,
-  loggingServiceMock,
-} from '../../../../../../src/core/server/mocks';
+import { encryptedSavedObjectsMock } from '../../../../plugins/encrypted_saved_objects/server/mocks';
+import { savedObjectsClientMock, loggingServiceMock } from '../../../../../src/core/server/mocks';
 
 const alertType = {
   id: 'test',
diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/task_runner_factory.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts
similarity index 83%
rename from x-pack/legacy/plugins/alerting/server/task_runner/task_runner_factory.ts
rename to x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts
index d2ecfb64c8a81..c598b0f52f197 100644
--- a/x-pack/legacy/plugins/alerting/server/task_runner/task_runner_factory.ts
+++ b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts
@@ -3,10 +3,10 @@
  * or more contributor license agreements. Licensed under the Elastic License;
  * you may not use this file except in compliance with the Elastic License.
  */
-import { Logger } from '../../../../../../src/core/server';
-import { RunContext } from '../../../../../plugins/task_manager/server';
-import { EncryptedSavedObjectsPluginStart } from '../../../../../plugins/encrypted_saved_objects/server';
-import { PluginStartContract as ActionsPluginStartContract } from '../../../../../plugins/actions/server';
+import { Logger } from '../../../../../src/core/server';
+import { RunContext } from '../../../../plugins/task_manager/server';
+import { EncryptedSavedObjectsPluginStart } from '../../../../plugins/encrypted_saved_objects/server';
+import { PluginStartContract as ActionsPluginStartContract } from '../../../../plugins/actions/server';
 import {
   AlertType,
   GetBasePathFunction,
diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/transform_action_params.test.ts b/x-pack/plugins/alerting/server/task_runner/transform_action_params.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/alerting/server/task_runner/transform_action_params.test.ts
rename to x-pack/plugins/alerting/server/task_runner/transform_action_params.test.ts
diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/transform_action_params.ts b/x-pack/plugins/alerting/server/task_runner/transform_action_params.ts
similarity index 100%
rename from x-pack/legacy/plugins/alerting/server/task_runner/transform_action_params.ts
rename to x-pack/plugins/alerting/server/task_runner/transform_action_params.ts
diff --git a/x-pack/legacy/plugins/alerting/server/test_utils/index.ts b/x-pack/plugins/alerting/server/test_utils/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/alerting/server/test_utils/index.ts
rename to x-pack/plugins/alerting/server/test_utils/index.ts
diff --git a/x-pack/legacy/plugins/alerting/server/types.ts b/x-pack/plugins/alerting/server/types.ts
similarity index 91%
rename from x-pack/legacy/plugins/alerting/server/types.ts
rename to x-pack/plugins/alerting/server/types.ts
index 95a96fa384c2c..202a73f076859 100644
--- a/x-pack/legacy/plugins/alerting/server/types.ts
+++ b/x-pack/plugins/alerting/server/types.ts
@@ -7,8 +7,9 @@
 import { AlertInstance } from './alert_instance';
 import { AlertTypeRegistry as OrigAlertTypeRegistry } from './alert_type_registry';
 import { PluginSetupContract, PluginStartContract } from './plugin';
-import { SavedObjectAttributes, SavedObjectsClientContract } from '../../../../../src/core/server';
+import { SavedObjectAttributes, SavedObjectsClientContract } from '../../../../src/core/server';
 import { Alert, AlertActionParams } from '../common';
+import { AlertsClient } from './alerts_client';
 export * from '../common';
 
 export type State = Record<string, any>;
@@ -18,6 +19,15 @@ export type GetServicesFunction = (request: any) => Services;
 export type GetBasePathFunction = (spaceId?: string) => string;
 export type SpaceIdToNamespaceFunction = (spaceId?: string) => string | undefined;
 
+declare module 'src/core/server' {
+  interface RequestHandlerContext {
+    alerting: {
+      getAlertsClient: () => AlertsClient;
+      listTypes: AlertTypeRegistry['list'];
+    };
+  }
+}
+
 export interface Services {
   callCluster(path: string, opts: any): Promise<any>;
   savedObjectsClient: SavedObjectsClientContract;
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts
index 22fd01c1aee81..e0ecae976146c 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts
@@ -8,9 +8,9 @@ import { HttpSetup } from 'kibana/public';
 import * as t from 'io-ts';
 import { pipe } from 'fp-ts/lib/pipeable';
 import { fold } from 'fp-ts/lib/Either';
+import { alertStateSchema } from '../../../../alerting/common';
 import { BASE_ALERT_API_PATH } from '../constants';
 import { Alert, AlertType, AlertWithoutId, AlertTaskState } from '../../types';
-import { alertStateSchema } from '../../../../../legacy/plugins/alerting/common';
 
 export async function loadAlertTypes({ http }: { http: HttpSetup }): Promise<AlertType[]> {
   return await http.get(`${BASE_ALERT_API_PATH}/types`);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx
index 1f0e4f015f229..731489c61d60f 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx
@@ -12,8 +12,7 @@ import { EuiBasicTable, EuiButtonToggle, EuiBadge, EuiHealth } from '@elastic/eu
 import { RIGHT_ALIGNMENT, CENTER_ALIGNMENT } from '@elastic/eui/lib/services';
 import { padLeft, difference } from 'lodash';
 import { FormattedMessage } from '@kbn/i18n/react';
-import { RawAlertInstance } from '../../../../../../../legacy/plugins/alerting/common';
-import { Alert, AlertTaskState } from '../../../../types';
+import { Alert, AlertTaskState, RawAlertInstance } from '../../../../types';
 import {
   ComponentOpts as AlertApis,
   withBulkAlertOperations,
diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts
index 86853e88a81cd..aa1d3d068ed77 100644
--- a/x-pack/plugins/triggers_actions_ui/public/types.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/types.ts
@@ -10,7 +10,7 @@ import {
   AlertAction,
   AlertTaskState,
   RawAlertInstance,
-} from '../../../legacy/plugins/alerting/common';
+} from '../../../plugins/alerting/common';
 export { Alert, AlertAction, AlertTaskState, RawAlertInstance };
 export { ActionType };
 
diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts
index 0cc45a624bc1a..b06e0c9e0f8cd 100644
--- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts
+++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts
@@ -5,7 +5,7 @@
  */
 import { times } from 'lodash';
 import { schema } from '@kbn/config-schema';
-import { AlertExecutorOptions, AlertType } from '../../../../../../legacy/plugins/alerting';
+import { AlertExecutorOptions, AlertType } from '../../../../../../plugins/alerting/server';
 import { ActionTypeExecutorOptions, ActionType } from '../../../../../../plugins/actions/server';
 
 // eslint-disable-next-line import/no-default-export
@@ -392,13 +392,13 @@ export default function(kibana: any) {
         actionGroups: [{ id: 'default', name: 'Default' }],
         async executor({ services, params, state }: AlertExecutorOptions) {},
       };
-      server.plugins.alerting.setup.registerType(alwaysFiringAlertType);
-      server.plugins.alerting.setup.registerType(cumulativeFiringAlertType);
-      server.plugins.alerting.setup.registerType(neverFiringAlertType);
-      server.plugins.alerting.setup.registerType(failingAlertType);
-      server.plugins.alerting.setup.registerType(validationAlertType);
-      server.plugins.alerting.setup.registerType(authorizationAlertType);
-      server.plugins.alerting.setup.registerType(noopAlertType);
+      server.newPlatform.setup.plugins.alerting.registerType(alwaysFiringAlertType);
+      server.newPlatform.setup.plugins.alerting.registerType(cumulativeFiringAlertType);
+      server.newPlatform.setup.plugins.alerting.registerType(neverFiringAlertType);
+      server.newPlatform.setup.plugins.alerting.registerType(failingAlertType);
+      server.newPlatform.setup.plugins.alerting.registerType(validationAlertType);
+      server.newPlatform.setup.plugins.alerting.registerType(authorizationAlertType);
+      server.newPlatform.setup.plugins.alerting.registerType(noopAlertType);
     },
   });
 }
diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts
index 003bb10c0adae..7dae7e4ab7941 100644
--- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts
+++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts
@@ -213,12 +213,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) {
               expect(response.body).to.eql({
                 statusCode: 400,
                 error: 'Bad Request',
-                message:
-                  'child "name" fails because ["name" is required]. child "alertTypeId" fails because ["alertTypeId" is required]. child "consumer" fails because ["consumer" is required]. child "schedule" fails because ["schedule" is required]. child "params" fails because ["params" is required]. child "actions" fails because ["actions" is required]',
-                validation: {
-                  source: 'payload',
-                  keys: ['name', 'alertTypeId', 'consumer', 'schedule', 'params', 'actions'],
-                },
+                message: '[request body.name]: expected value of type [string] but got [undefined]',
               });
               break;
             default:
@@ -285,19 +280,9 @@ export default function createAlertTests({ getService }: FtrProviderContext) {
             case 'space_1_all at space1':
               expect(response.statusCode).to.eql(400);
               expect(response.body).to.eql({
-                statusCode: 400,
                 error: 'Bad Request',
-                message:
-                  'child "schedule" fails because [child "interval" fails because ["interval" with value "10x" fails to match the seconds pattern, "interval" with value "10x" fails to match the minutes pattern, "interval" with value "10x" fails to match the hours pattern, "interval" with value "10x" fails to match the days pattern]]',
-                validation: {
-                  source: 'payload',
-                  keys: [
-                    'schedule.interval',
-                    'schedule.interval',
-                    'schedule.interval',
-                    'schedule.interval',
-                  ],
-                },
+                message: '[request body.schedule.interval]: string is not a valid duration: 10x',
+                statusCode: 400,
               });
               break;
             default:
@@ -327,19 +312,9 @@ export default function createAlertTests({ getService }: FtrProviderContext) {
             case 'space_1_all at space1':
               expect(response.statusCode).to.eql(400);
               expect(response.body).to.eql({
-                statusCode: 400,
                 error: 'Bad Request',
-                message:
-                  'child "schedule" fails because [child "interval" fails because ["interval" with value "0s" fails to match the seconds pattern, "interval" with value "0s" fails to match the minutes pattern, "interval" with value "0s" fails to match the hours pattern, "interval" with value "0s" fails to match the days pattern]]',
-                validation: {
-                  source: 'payload',
-                  keys: [
-                    'schedule.interval',
-                    'schedule.interval',
-                    'schedule.interval',
-                    'schedule.interval',
-                  ],
-                },
+                message: '[request body.schedule.interval]: string is not a valid duration: 0s',
+                statusCode: 400,
               });
               break;
             default:
diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts
index 65ffa9ebe9dfa..f91c06c5299b8 100644
--- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts
+++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts
@@ -202,7 +202,7 @@ export default function createFindTests({ getService }: FtrProviderContext) {
               expect(response.statusCode).to.eql(200);
               expect(response.body).to.eql({
                 page: 1,
-                perPage: 20,
+                perPage: 10,
                 total: 0,
                 data: [],
               });
diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts
index 7a4f73885107c..f7ccc6c97bcf0 100644
--- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts
+++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts
@@ -53,7 +53,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
             },
             schedule: { interval: '12s' },
             actions: [],
-            throttle: '2m',
+            throttle: '1m',
           };
           const response = await supertestWithoutAuth
             .put(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}`)
@@ -134,7 +134,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
             },
             schedule: { interval: '12s' },
             actions: [],
-            throttle: '2m',
+            throttle: '1m',
           };
           const response = await supertestWithoutAuth
             .put(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}`)
@@ -277,11 +277,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
               expect(response.body).to.eql({
                 statusCode: 400,
                 error: 'Bad Request',
-                message: '"alertTypeId" is not allowed',
-                validation: {
-                  source: 'payload',
-                  keys: ['alertTypeId'],
-                },
+                message: '[request body.alertTypeId]: definition for this key is missing',
               });
               break;
             default:
@@ -313,12 +309,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
               expect(response.body).to.eql({
                 statusCode: 400,
                 error: 'Bad Request',
-                message:
-                  'child "throttle" fails because ["throttle" is required]. child "name" fails because ["name" is required]. child "tags" fails because ["tags" is required]. child "schedule" fails because ["schedule" is required]. child "params" fails because ["params" is required]. child "actions" fails because ["actions" is required]',
-                validation: {
-                  source: 'payload',
-                  keys: ['throttle', 'name', 'tags', 'schedule', 'params', 'actions'],
-                },
+                message: '[request body.name]: expected value of type [string] but got [undefined]',
               });
               break;
             default:
@@ -410,18 +401,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
               expect(response.body).to.eql({
                 statusCode: 400,
                 error: 'Bad Request',
-                message:
-                  'child "schedule" fails because [child "interval" fails because ["interval" with value "10x" fails to match the seconds pattern, "interval" with value "10x" fails to match the minutes pattern, "interval" with value "10x" fails to match the hours pattern, "interval" with value "10x" fails to match the days pattern]]. "alertTypeId" is not allowed',
-                validation: {
-                  source: 'payload',
-                  keys: [
-                    'schedule.interval',
-                    'schedule.interval',
-                    'schedule.interval',
-                    'schedule.interval',
-                    'alertTypeId',
-                  ],
-                },
+                message: '[request body.schedule.interval]: string is not a valid duration: 10x',
               });
               break;
             default:
@@ -456,7 +436,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
             },
             schedule: { interval: '1m' },
             actions: [],
-            throttle: '2m',
+            throttle: '1m',
           };
           const response = await supertestWithoutAuth
             .put(`${getUrlPrefix(space.id)}/api/alert/${createdAlert.id}`)
diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts
index 6a99ed366047b..c0f56c55ba850 100644
--- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts
+++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts
@@ -18,7 +18,7 @@ export default function alertingApiIntegrationTests({
   const esArchiver = getService('esArchiver');
 
   describe('alerting api integration security and spaces enabled', function() {
-    this.tags('ciGroup1');
+    this.tags('ciGroup3');
 
     before(async () => {
       for (const space of Spaces) {
diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts
index 70935a462d03e..1dd0426c97cca 100644
--- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts
+++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts
@@ -77,7 +77,7 @@ export default function createFindTests({ getService }: FtrProviderContext) {
         )
         .expect(200, {
           page: 1,
-          perPage: 20,
+          perPage: 10,
           total: 0,
           data: [],
         });
diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/index.ts
index cb2b17980d37a..b118a48fd642c 100644
--- a/x-pack/test/alerting_api_integration/spaces_only/tests/index.ts
+++ b/x-pack/test/alerting_api_integration/spaces_only/tests/index.ts
@@ -16,7 +16,7 @@ export default function alertingApiIntegrationTests({
   const esArchiver = getService('esArchiver');
 
   describe('alerting api integration spaces only', function() {
-    this.tags('ciGroup1');
+    this.tags('ciGroup3');
 
     before(async () => {
       for (const space of Object.values(Spaces)) {
diff --git a/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/index.ts b/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/index.ts
index d7551345203b4..678707af40bae 100644
--- a/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/index.ts
+++ b/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/index.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { AlertType } from '../../../../../legacy/plugins/alerting';
+import { AlertType } from '../../../../../plugins/alerting/server';
 
 // eslint-disable-next-line import/no-default-export
 export default function(kibana: any) {
@@ -12,8 +12,8 @@ export default function(kibana: any) {
     require: ['alerting'],
     name: 'alerts',
     init(server: any) {
-      createNoopAlertType(server.plugins.alerting.setup);
-      createAlwaysFiringAlertType(server.plugins.alerting.setup);
+      createNoopAlertType(server.newPlatform.setup.plugins.alerting);
+      createAlwaysFiringAlertType(server.newPlatform.setup.plugins.alerting);
     },
   });
 }
diff --git a/x-pack/typings/hapi.d.ts b/x-pack/typings/hapi.d.ts
index 8d54d28794351..872e042eb1cdb 100644
--- a/x-pack/typings/hapi.d.ts
+++ b/x-pack/typings/hapi.d.ts
@@ -9,7 +9,7 @@ import 'hapi';
 import { XPackMainPlugin } from '../legacy/plugins/xpack_main/server/xpack_main';
 import { SecurityPlugin } from '../legacy/plugins/security';
 import { ActionsPlugin, ActionsClient } from '../plugins/actions/server';
-import { AlertingPlugin, AlertsClient } from '../legacy/plugins/alerting';
+import { AlertingPlugin, AlertsClient } from '../plugins/alerting/server';
 import { LegacyTaskManagerApi } from '../legacy/plugins/task_manager/server';
 
 declare module 'hapi' {

From a838e6392a407bfffe5bc2db496b414ff1fe0d17 Mon Sep 17 00:00:00 2001
From: Spencer <email@spalger.com>
Date: Tue, 18 Feb 2020 10:57:15 -0700
Subject: [PATCH 029/174] [@kbn/optimizer] prevent error when all bundles are
 cached (#57871)

---
 .../basic_optimization.test.ts                | 33 +++++++++++++++++--
 .../optimizer/handle_optimizer_completion.ts  |  5 +++
 2 files changed, 36 insertions(+), 2 deletions(-)

diff --git a/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts b/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts
index dda818875db23..b35788009dd92 100644
--- a/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts
+++ b/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts
@@ -33,7 +33,7 @@ const MOCK_REPO_DIR = Path.resolve(TMP_DIR, 'mock_repo');
 
 expect.addSnapshotSerializer(createAbsolutePathSerializer(MOCK_REPO_DIR));
 
-beforeEach(async () => {
+beforeAll(async () => {
   await del(TMP_DIR);
   await cpy('**/*', MOCK_REPO_DIR, {
     cwd: MOCK_REPO_SRC,
@@ -42,7 +42,7 @@ beforeEach(async () => {
   });
 });
 
-afterEach(async () => {
+afterAll(async () => {
   await del(TMP_DIR);
 });
 
@@ -153,3 +153,32 @@ it('builds expected bundles, saves bundle counts to metadata', async () => {
     ]
   `);
 });
+
+it('uses cache on second run and exist cleanly', async () => {
+  const config = OptimizerConfig.create({
+    repoRoot: MOCK_REPO_DIR,
+    pluginScanDirs: [Path.resolve(MOCK_REPO_DIR, 'plugins')],
+    maxWorkerCount: 1,
+  });
+
+  const msgs = await runOptimizer(config)
+    .pipe(
+      tap(state => {
+        if (state.event?.type === 'worker stdio') {
+          // eslint-disable-next-line no-console
+          console.log('worker', state.event.stream, state.event.chunk.toString('utf8'));
+        }
+      }),
+      toArray()
+    )
+    .toPromise();
+
+  expect(msgs.map(m => m.state.phase)).toMatchInlineSnapshot(`
+    Array [
+      "initializing",
+      "initializing",
+      "initializing",
+      "initialized",
+    ]
+  `);
+});
diff --git a/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.ts b/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.ts
index fe2fa320818a2..9587902cc4187 100644
--- a/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.ts
+++ b/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.ts
@@ -44,6 +44,11 @@ export function handleOptimizerCompletion(config: OptimizerConfig) {
             return;
           }
 
+          if (prevState?.phase === 'initialized' && prevState.onlineBundles.length === 0) {
+            // all bundles cached
+            return;
+          }
+
           if (prevState?.phase === 'issue') {
             throw createFailError('webpack issue');
           }

From bca0d1c2be33da054de638370fdd495fcd9a0449 Mon Sep 17 00:00:00 2001
From: MadameSheema <snootchie.boochies@gmail.com>
Date: Tue, 18 Feb 2020 19:03:01 +0100
Subject: [PATCH 030/174] refactors 'toggle-column' tests (#57845)

---
 .../timeline/toggle_column.spec.ts            | 94 +++++++------------
 .../siem/cypress/screens/timeline/main.ts     | 12 +++
 .../siem/cypress/tasks/timeline/main.ts       | 41 +++++++-
 3 files changed, 86 insertions(+), 61 deletions(-)

diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/toggle_column.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/toggle_column.spec.ts
index d410a89cf0723..ccd6ae818c6cd 100644
--- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/toggle_column.spec.ts
+++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/toggle_column.spec.ts
@@ -4,92 +4,66 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { drag, drop } from '../../lib/drag_n_drop/helpers';
-import { populateTimeline } from '../../lib/fields_browser/helpers';
-import { createNewTimeline, toggleFirstTimelineEventDetails } from '../../lib/timeline/helpers';
-import { HOSTS_PAGE } from '../../lib/urls';
-import { loginAndWaitForPage, DEFAULT_TIMEOUT } from '../../lib/util/helpers';
+import { HOSTS_PAGE } from '../../../urls/navigation';
+import { loginAndWaitForPage, DEFAULT_TIMEOUT } from '../../../tasks/login';
+import {
+  createNewTimeline,
+  populateTimeline,
+  expandFirstTimelineEventDetails,
+  uncheckTimestampToggleField,
+  checkIdToggleField,
+  dragAndDropIdToggleFieldToTimeline,
+} from '../../../tasks/timeline/main';
+import { openTimeline } from '../../../tasks/siem_main';
+import {
+  TIMESTAMP_TOGGLE_FIELD,
+  ID_TOGGLE_FIELD,
+  TIMESTAMP_HEADER_FIELD,
+  ID_HEADER_FIELD,
+} from '../../../screens/timeline/main';
 
 describe('toggle column in timeline', () => {
   before(() => {
     loginAndWaitForPage(HOSTS_PAGE);
   });
 
+  beforeEach(() => {
+    openTimeline();
+    populateTimeline();
+  });
+
   afterEach(() => {
     createNewTimeline();
   });
 
-  const timestampField = '@timestamp';
-  const idField = '_id';
-
   it('displays a checked Toggle field checkbox for `@timestamp`, a default timeline column', () => {
-    populateTimeline();
-
-    toggleFirstTimelineEventDetails();
-
-    cy.get(`[data-test-subj="toggle-field-${timestampField}"]`).should('be.checked');
+    expandFirstTimelineEventDetails();
+    cy.get(TIMESTAMP_TOGGLE_FIELD).should('be.checked');
   });
 
   it('displays an Unchecked Toggle field checkbox for `_id`, because it is NOT a default timeline column', () => {
-    populateTimeline();
-
-    cy.get(`[data-test-subj="timeline"] [data-test-subj="toggle-field-${idField}"]`).should(
-      'not.be.checked'
-    );
+    cy.get(ID_TOGGLE_FIELD).should('not.be.checked');
   });
 
   it('removes the @timestamp field from the timeline when the user un-checks the toggle', () => {
-    populateTimeline();
-
-    toggleFirstTimelineEventDetails();
-
-    cy.get(`[data-test-subj="timeline"] [data-test-subj="header-text-${timestampField}"]`).should(
-      'exist'
-    );
+    expandFirstTimelineEventDetails();
+    uncheckTimestampToggleField();
 
-    cy.get(`[data-test-subj="timeline"] [data-test-subj="toggle-field-${timestampField}"]`, {
-      timeout: DEFAULT_TIMEOUT,
-    }).uncheck({ force: true });
-
-    cy.get(`[data-test-subj="timeline"] [data-test-subj="header-text-${timestampField}"]`).should(
-      'not.exist'
-    );
+    cy.get(TIMESTAMP_HEADER_FIELD).should('not.exist');
   });
 
   it('adds the _id field to the timeline when the user checks the field', () => {
-    populateTimeline();
-
-    toggleFirstTimelineEventDetails();
-
-    cy.get(`[data-test-subj="timeline"] [data-test-subj="header-text-${idField}"]`).should(
-      'not.exist'
-    );
-
-    cy.get(`[data-test-subj="timeline"] [data-test-subj="toggle-field-${idField}"]`).check({
-      force: true,
-    });
+    expandFirstTimelineEventDetails();
+    checkIdToggleField();
 
-    cy.get(`[data-test-subj="timeline"] [data-test-subj="header-text-${idField}"]`).should('exist');
+    cy.get(ID_HEADER_FIELD).should('exist');
   });
 
   it('adds the _id field to the timeline via drag and drop', () => {
-    populateTimeline();
-
-    toggleFirstTimelineEventDetails();
-
-    cy.get(`[data-test-subj="timeline"] [data-test-subj="header-text-${idField}"]`).should(
-      'not.exist'
-    );
-
-    cy.get(`[data-test-subj="timeline"] [data-test-subj="field-name-${idField}"]`).then(field =>
-      drag(field)
-    );
-
-    cy.get(`[data-test-subj="timeline"] [data-test-subj="headers-group"]`).then(headersDropArea =>
-      drop(headersDropArea)
-    );
+    expandFirstTimelineEventDetails();
+    dragAndDropIdToggleFieldToTimeline();
 
-    cy.get(`[data-test-subj="timeline"] [data-test-subj="header-text-${idField}"]`, {
+    cy.get(ID_HEADER_FIELD, {
       timeout: DEFAULT_TIMEOUT,
     }).should('exist');
   });
diff --git a/x-pack/legacy/plugins/siem/cypress/screens/timeline/main.ts b/x-pack/legacy/plugins/siem/cypress/screens/timeline/main.ts
index 4c722ffa5f215..7b47c159c4a0a 100644
--- a/x-pack/legacy/plugins/siem/cypress/screens/timeline/main.ts
+++ b/x-pack/legacy/plugins/siem/cypress/screens/timeline/main.ts
@@ -37,3 +37,15 @@ export const TIMELINE_FLYOUT_BODY = '[data-test-subj="eui-flyout-body"]';
 
 export const TIMELINE_NOT_READY_TO_DROP_BUTTON =
   '[data-test-subj="flyout-button-not-ready-to-drop"]';
+
+export const TOGGLE_TIMELINE_EXPAND_EVENT = '[data-test-subj="expand-event"]';
+
+export const TIMESTAMP_TOGGLE_FIELD = '[data-test-subj="toggle-field-@timestamp"]';
+
+export const ID_TOGGLE_FIELD = '[data-test-subj="toggle-field-_id"]';
+
+export const TIMESTAMP_HEADER_FIELD = '[data-test-subj="header-text-@timestamp"]';
+
+export const ID_HEADER_FIELD = '[data-test-subj="timeline"] [data-test-subj="header-text-_id"]';
+
+export const ID_FIELD = '[data-test-subj="timeline"] [data-test-subj="field-name-_id"]';
diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/timeline/main.ts b/x-pack/legacy/plugins/siem/cypress/tasks/timeline/main.ts
index f347c072a3584..d26c0f2911f75 100644
--- a/x-pack/legacy/plugins/siem/cypress/tasks/timeline/main.ts
+++ b/x-pack/legacy/plugins/siem/cypress/tasks/timeline/main.ts
@@ -14,12 +14,19 @@ import {
   TIMELINE_INSPECT_BUTTON,
   CREATE_NEW_TIMELINE,
   CLOSE_TIMELINE_BTN,
+  TOGGLE_TIMELINE_EXPAND_EVENT,
+  TIMESTAMP_TOGGLE_FIELD,
+  ID_TOGGLE_FIELD,
+  ID_HEADER_FIELD,
+  ID_FIELD,
 } from '../../screens/timeline/main';
 
+import { drag, drop } from '../../tasks/common';
+
 export const hostExistsQuery = 'host.name: *';
 
 export const populateTimeline = () => {
-  cy.get(`${SEARCH_OR_FILTER_CONTAINER} input`).type(`${hostExistsQuery} {enter}`);
+  executeTimelineKQL(hostExistsQuery);
   cy.get(SERVER_SIDE_EVENT_COUNT, { timeout: DEFAULT_TIMEOUT })
     .invoke('text')
     .should('be.above', 0);
@@ -47,3 +54,35 @@ export const createNewTimeline = () => {
   cy.get(CREATE_NEW_TIMELINE).click();
   cy.get(CLOSE_TIMELINE_BTN).click({ force: true });
 };
+
+export const expandFirstTimelineEventDetails = () => {
+  cy.get(TOGGLE_TIMELINE_EXPAND_EVENT, { timeout: DEFAULT_TIMEOUT })
+    .first()
+    .click({ force: true });
+};
+
+export const uncheckTimestampToggleField = () => {
+  cy.get(TIMESTAMP_TOGGLE_FIELD).should('exist');
+
+  cy.get(TIMESTAMP_TOGGLE_FIELD, {
+    timeout: DEFAULT_TIMEOUT,
+  }).uncheck({ force: true });
+};
+
+export const checkIdToggleField = () => {
+  cy.get(ID_TOGGLE_FIELD).should('not.exist');
+
+  cy.get(ID_TOGGLE_FIELD).check({
+    force: true,
+  });
+};
+
+export const dragAndDropIdToggleFieldToTimeline = () => {
+  cy.get(ID_HEADER_FIELD).should('not.exist');
+
+  cy.get(ID_FIELD).then(field => drag(field));
+
+  cy.get(`[data-test-subj="timeline"] [data-test-subj="headers-group"]`).then(headersDropArea =>
+    drop(headersDropArea)
+  );
+};

From f2478100ab367b132c2ba5826366974ceb0e229b Mon Sep 17 00:00:00 2001
From: Lukas Olson <olson.lukas@gmail.com>
Date: Tue, 18 Feb 2020 11:19:07 -0700
Subject: [PATCH 031/174] Revert switching abort controller libraries (#57216)

* Revert switching abort controller libraries

* Revert package.json in lib

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 package.json                                           |  2 +-
 packages/kbn-ui-shared-deps/package.json               |  2 +-
 packages/kbn-ui-shared-deps/polyfills.js               |  2 +-
 .../server/lib/abortable_request_handler.js            |  2 +-
 .../server/lib/abortable_request_handler.test.js       |  2 +-
 yarn.lock                                              | 10 ++++------
 6 files changed, 9 insertions(+), 11 deletions(-)

diff --git a/package.json b/package.json
index e4fdaf32a014a..04dc5edb6fd99 100644
--- a/package.json
+++ b/package.json
@@ -140,7 +140,7 @@
     "@kbn/ui-framework": "1.0.0",
     "@kbn/ui-shared-deps": "1.0.0",
     "JSONStream": "1.3.5",
-    "abort-controller": "^3.0.0",
+    "abortcontroller-polyfill": "^1.4.0",
     "angular": "^1.7.9",
     "angular-aria": "^1.7.9",
     "angular-elastic": "^2.5.1",
diff --git a/packages/kbn-ui-shared-deps/package.json b/packages/kbn-ui-shared-deps/package.json
index 4b4db9d7f37f3..6795d363a9f1d 100644
--- a/packages/kbn-ui-shared-deps/package.json
+++ b/packages/kbn-ui-shared-deps/package.json
@@ -10,7 +10,7 @@
   },
   "devDependencies": {
     "@elastic/charts": "^17.0.2",
-    "abort-controller": "^3.0.0",
+    "abortcontroller-polyfill": "^1.4.0",
     "@elastic/eui": "19.0.0",
     "@kbn/dev-utils": "1.0.0",
     "@kbn/i18n": "1.0.0",
diff --git a/packages/kbn-ui-shared-deps/polyfills.js b/packages/kbn-ui-shared-deps/polyfills.js
index 612fbb9a78b50..d2305d643e4d2 100644
--- a/packages/kbn-ui-shared-deps/polyfills.js
+++ b/packages/kbn-ui-shared-deps/polyfills.js
@@ -21,6 +21,6 @@ require('core-js/stable');
 require('regenerator-runtime/runtime');
 require('custom-event-polyfill');
 require('whatwg-fetch');
-require('abort-controller/polyfill');
+require('abortcontroller-polyfill/dist/polyfill-patch-fetch');
 require('./vendor/childnode_remove_polyfill');
 require('symbol-observable');
diff --git a/src/legacy/core_plugins/elasticsearch/server/lib/abortable_request_handler.js b/src/legacy/core_plugins/elasticsearch/server/lib/abortable_request_handler.js
index 68216b92840fc..0b8786f0c2841 100644
--- a/src/legacy/core_plugins/elasticsearch/server/lib/abortable_request_handler.js
+++ b/src/legacy/core_plugins/elasticsearch/server/lib/abortable_request_handler.js
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-import { AbortController } from 'abort-controller';
+import { AbortController } from 'abortcontroller-polyfill/dist/cjs-ponyfill';
 
 /*
  * A simple utility for generating a handler that provides a signal to the handler that signals when
diff --git a/src/legacy/core_plugins/elasticsearch/server/lib/abortable_request_handler.test.js b/src/legacy/core_plugins/elasticsearch/server/lib/abortable_request_handler.test.js
index 1c154370d1674..d79dd4ae4e449 100644
--- a/src/legacy/core_plugins/elasticsearch/server/lib/abortable_request_handler.test.js
+++ b/src/legacy/core_plugins/elasticsearch/server/lib/abortable_request_handler.test.js
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-import { AbortSignal } from 'abort-controller';
+import { AbortSignal } from 'abortcontroller-polyfill/dist/cjs-ponyfill';
 import { abortableRequestHandler } from './abortable_request_handler';
 
 describe('abortableRequestHandler', () => {
diff --git a/yarn.lock b/yarn.lock
index 587dff16ffe58..0e830540ccdb2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5767,12 +5767,10 @@ abort-controller@^2.0.3:
   dependencies:
     event-target-shim "^5.0.0"
 
-abort-controller@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
-  integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==
-  dependencies:
-    event-target-shim "^5.0.0"
+abortcontroller-polyfill@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.4.0.tgz#0d5eb58e522a461774af8086414f68e1dda7a6c4"
+  integrity sha512-3ZFfCRfDzx3GFjO6RAkYx81lPGpUS20ISxux9gLxuKnqafNcFQo59+IoZqpO2WvQlyc287B62HDnDdNYRmlvWA==
 
 accept@3.x.x:
   version "3.0.2"

From 05730f9145c015d86bd35a5a8cb7ba7e6b04bf12 Mon Sep 17 00:00:00 2001
From: Spencer <email@spalger.com>
Date: Tue, 18 Feb 2020 11:30:47 -0700
Subject: [PATCH 032/174] [ts/projects] support disabling typecheck (#57882)

---
 src/dev/typescript/project.ts            |  9 +++++++--
 src/dev/typescript/projects.ts           | 16 +++++++++-------
 src/dev/typescript/run_type_check_cli.ts |  2 +-
 3 files changed, 17 insertions(+), 10 deletions(-)

diff --git a/src/dev/typescript/project.ts b/src/dev/typescript/project.ts
index e0d882985bd7c..623c248144ff6 100644
--- a/src/dev/typescript/project.ts
+++ b/src/dev/typescript/project.ts
@@ -52,11 +52,15 @@ export class Project {
   public directory: string;
   public name: string;
   public config: any;
+  public disableTypeCheck: boolean;
 
   private readonly include: IMinimatch[];
   private readonly exclude: IMinimatch[];
 
-  constructor(public tsConfigPath: string, name?: string) {
+  constructor(
+    public tsConfigPath: string,
+    options: { name?: string; disableTypeCheck?: boolean } = {}
+  ) {
     this.config = parseTsConfig(tsConfigPath);
 
     const { files, include, exclude = [] } = this.config as {
@@ -72,7 +76,8 @@ export class Project {
     }
 
     this.directory = dirname(this.tsConfigPath);
-    this.name = name || relative(REPO_ROOT, this.directory) || basename(this.directory);
+    this.disableTypeCheck = options.disableTypeCheck || false;
+    this.name = options.name || relative(REPO_ROOT, this.directory) || basename(this.directory);
     this.include = makeMatchers(this.directory, include);
     this.exclude = makeMatchers(this.directory, exclude);
   }
diff --git a/src/dev/typescript/projects.ts b/src/dev/typescript/projects.ts
index 602657ad73836..b6353e44989ee 100644
--- a/src/dev/typescript/projects.ts
+++ b/src/dev/typescript/projects.ts
@@ -25,14 +25,16 @@ import { Project } from './project';
 
 export const PROJECTS = [
   new Project(resolve(REPO_ROOT, 'tsconfig.json')),
-  new Project(resolve(REPO_ROOT, 'test/tsconfig.json'), 'kibana/test'),
+  new Project(resolve(REPO_ROOT, 'test/tsconfig.json'), { name: 'kibana/test' }),
   new Project(resolve(REPO_ROOT, 'x-pack/tsconfig.json')),
-  new Project(resolve(REPO_ROOT, 'x-pack/test/tsconfig.json'), 'x-pack/test'),
-  new Project(
-    resolve(REPO_ROOT, 'x-pack/legacy/plugins/siem/cypress/tsconfig.json'),
-    'siem/cypress'
-  ),
-  new Project(resolve(REPO_ROOT, 'x-pack/legacy/plugins/apm/cypress/tsconfig.json'), 'apm/cypress'),
+  new Project(resolve(REPO_ROOT, 'x-pack/test/tsconfig.json'), { name: 'x-pack/test' }),
+  new Project(resolve(REPO_ROOT, 'x-pack/legacy/plugins/siem/cypress/tsconfig.json'), {
+    name: 'siem/cypress',
+  }),
+  new Project(resolve(REPO_ROOT, 'x-pack/legacy/plugins/apm/cypress/tsconfig.json'), {
+    name: 'apm/cypress',
+    disableTypeCheck: true,
+  }),
 
   // NOTE: using glob.sync rather than glob-all or globby
   // because it takes less than 10 ms, while the other modules
diff --git a/src/dev/typescript/run_type_check_cli.ts b/src/dev/typescript/run_type_check_cli.ts
index 3deebcc0c18f9..1f0e4b48b7b4b 100644
--- a/src/dev/typescript/run_type_check_cli.ts
+++ b/src/dev/typescript/run_type_check_cli.ts
@@ -80,7 +80,7 @@ export function runTypeCheckCli() {
   }
 
   const tscArgs = ['--noEmit', '--pretty', ...(opts['skip-lib-check'] ? ['--skipLibCheck'] : [])];
-  const projects = filterProjectsByFlag(opts.project);
+  const projects = filterProjectsByFlag(opts.project).filter(p => !p.disableTypeCheck);
 
   if (!projects.length) {
     log.error(`Unable to find project at ${opts.project}`);

From 3f4e16c08fc38d50c1b3c1d6beebb0cfc239f1f3 Mon Sep 17 00:00:00 2001
From: Vadim Dalecky <streamich@users.noreply.github.com>
Date: Tue, 18 Feb 2020 19:41:05 +0100
Subject: [PATCH 033/174] Isomorphic kibana_context (#57552)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* refactor: 💡 make kibana_context receive deps through args

* feat: 🎸 make kibana_context available on server and browser

* refactor: 💡 improve browser plugin setup method

* feat: 🎸 improve kibna_context missing getSavedObject error msg

* docs: ✏️ fix JSDOc typo

* style: 💄 remove unused import

* style: 💄 remove unused function
---
 .../expressions/common/execution/types.ts     | 15 +++++++++++++
 .../expression_functions/specs/index.ts       |  3 +++
 .../specs}/kibana_context.ts                  | 21 ++++++++++++-------
 src/plugins/expressions/public/plugin.test.ts |  8 +++++++
 src/plugins/expressions/public/plugin.ts      | 21 ++++++++++++++-----
 src/plugins/expressions/public/services.ts    |  2 +-
 6 files changed, 57 insertions(+), 13 deletions(-)
 rename src/plugins/expressions/{public/expression_functions => common/expression_functions/specs}/kibana_context.ts (83%)

diff --git a/src/plugins/expressions/common/execution/types.ts b/src/plugins/expressions/common/execution/types.ts
index ac0061c59c031..51538394cd125 100644
--- a/src/plugins/expressions/common/execution/types.ts
+++ b/src/plugins/expressions/common/execution/types.ts
@@ -20,6 +20,7 @@
 import { ExpressionType } from '../expression_types';
 import { DataAdapter, RequestAdapter } from '../../../inspector/common';
 import { TimeRange, Query, Filter } from '../../../data/common';
+import { SavedObject, SavedObjectAttributes } from '../../../../core/public';
 
 /**
  * `ExecutionContext` is an object available to all functions during a single execution;
@@ -55,6 +56,20 @@ export interface ExecutionContext<Input = unknown, InspectorAdapters = DefaultIn
    * Search context in which expression should operate.
    */
   search?: ExecutionContextSearch;
+
+  /**
+   * Allows to fetch saved objects from ElasticSearch. In browser `getSavedObject`
+   * function is provided automatically by the Expressions plugin. On the server
+   * the caller of the expression has to provide this context function. The
+   * reason is because on the browser we always know the user who tries to
+   * fetch a saved object, thus saved object client is scoped automatically to
+   * that user. However, on the server we can scope that saved object client to
+   * any user, or even not scope it at all and execute it as an "internal" user.
+   */
+  getSavedObject?: <T extends SavedObjectAttributes = SavedObjectAttributes>(
+    type: string,
+    id: string
+  ) => Promise<SavedObject<T>>;
 }
 
 /**
diff --git a/src/plugins/expressions/common/expression_functions/specs/index.ts b/src/plugins/expressions/common/expression_functions/specs/index.ts
index 514068da8f10c..f7471a8fd9d75 100644
--- a/src/plugins/expressions/common/expression_functions/specs/index.ts
+++ b/src/plugins/expressions/common/expression_functions/specs/index.ts
@@ -20,6 +20,7 @@
 import { clog } from './clog';
 import { font } from './font';
 import { kibana } from './kibana';
+import { kibanaContextFunction } from './kibana_context';
 import { variableSet } from './var_set';
 import { variable } from './var';
 import { AnyExpressionFunctionDefinition } from '../types';
@@ -28,6 +29,7 @@ export const functionSpecs: AnyExpressionFunctionDefinition[] = [
   clog,
   font,
   kibana,
+  kibanaContextFunction,
   variableSet,
   variable,
 ];
@@ -35,5 +37,6 @@ export const functionSpecs: AnyExpressionFunctionDefinition[] = [
 export * from './clog';
 export * from './font';
 export * from './kibana';
+export * from './kibana_context';
 export * from './var_set';
 export * from './var';
diff --git a/src/plugins/expressions/public/expression_functions/kibana_context.ts b/src/plugins/expressions/common/expression_functions/specs/kibana_context.ts
similarity index 83%
rename from src/plugins/expressions/public/expression_functions/kibana_context.ts
rename to src/plugins/expressions/common/expression_functions/specs/kibana_context.ts
index f997972c33839..4092dfbba00d5 100644
--- a/src/plugins/expressions/public/expression_functions/kibana_context.ts
+++ b/src/plugins/expressions/common/expression_functions/specs/kibana_context.ts
@@ -18,9 +18,8 @@
  */
 
 import { i18n } from '@kbn/i18n';
-import { ExpressionFunctionDefinition } from '../../common';
-import { KibanaContext } from '../../common/expression_types';
-import { savedObjects } from '../services';
+import { ExpressionFunctionDefinition } from '../../expression_functions';
+import { KibanaContext } from '../../expression_types';
 
 interface Arguments {
   q?: string | null;
@@ -36,7 +35,7 @@ export type ExpressionFunctionKibanaContext = ExpressionFunctionDefinition<
   Promise<KibanaContext>
 >;
 
-export const kibanaContext = (): ExpressionFunctionKibanaContext => ({
+export const kibanaContextFunction: ExpressionFunctionKibanaContext = {
   name: 'kibana_context',
   type: 'kibana_context',
   inputTypes: ['kibana_context', 'null'],
@@ -74,13 +73,21 @@ export const kibanaContext = (): ExpressionFunctionKibanaContext => ({
       }),
     },
   },
-  async fn(input, args) {
+
+  async fn(input, args, { getSavedObject }) {
     const queryArg = args.q ? JSON.parse(args.q) : [];
     let queries = Array.isArray(queryArg) ? queryArg : [queryArg];
     let filters = args.filters ? JSON.parse(args.filters) : [];
 
     if (args.savedSearchId) {
-      const obj = await savedObjects.get('search', args.savedSearchId);
+      if (typeof getSavedObject !== 'function') {
+        throw new Error(
+          '"getSavedObject" function not available in execution context. ' +
+            'When you execute expression you need to add extra execution context ' +
+            'as the third argument and provide "getSavedObject" implementation.'
+        );
+      }
+      const obj = await getSavedObject('search', args.savedSearchId);
       const search = obj.attributes.kibanaSavedObjectMeta as { searchSourceJSON: string };
       const data = JSON.parse(search.searchSourceJSON) as { query: string; filter: any[] };
       queries = queries.concat(data.query);
@@ -108,4 +115,4 @@ export const kibanaContext = (): ExpressionFunctionKibanaContext => ({
       timeRange,
     };
   },
-});
+};
diff --git a/src/plugins/expressions/public/plugin.test.ts b/src/plugins/expressions/public/plugin.test.ts
index ac9a2f508e2db..08f7135f033f1 100644
--- a/src/plugins/expressions/public/plugin.test.ts
+++ b/src/plugins/expressions/public/plugin.test.ts
@@ -50,6 +50,14 @@ describe('ExpressionsPublicPlugin', () => {
         const bar = await setup.run('var_set name="foo" value="bar" | var name="foo"', null);
         expect(bar).toBe('bar');
       });
+
+      test('kibana_context function is available', async () => {
+        const { setup } = await expressionsPluginMock.createPlugin();
+        const result = await setup.run('kibana_context', null);
+        expect(result).toMatchObject({
+          type: 'kibana_context',
+        });
+      });
     });
   });
 
diff --git a/src/plugins/expressions/public/plugin.ts b/src/plugins/expressions/public/plugin.ts
index aac429b365c48..4aa8003617652 100644
--- a/src/plugins/expressions/public/plugin.ts
+++ b/src/plugins/expressions/public/plugin.ts
@@ -27,6 +27,7 @@ import {
   ExpressionsService,
   ExpressionsServiceSetup,
   ExpressionsServiceStart,
+  ExecutionContext,
 } from '../common';
 import { Setup as InspectorSetup, Start as InspectorStart } from '../../inspector/public';
 import { BfetchPublicSetup, BfetchPublicStart } from '../../bfetch/public';
@@ -38,7 +39,6 @@ import {
   setNotifications,
   setExpressionsService,
 } from './services';
-import { kibanaContext as kibanaContextFunction } from './expression_functions/kibana_context';
 import { ReactExpressionRenderer } from './react_expression_renderer';
 import { ExpressionLoader, loader } from './loader';
 import { render, ExpressionRenderHandler } from './render';
@@ -106,14 +106,25 @@ export class ExpressionsPublicPlugin
 
   constructor(initializerContext: PluginInitializerContext) {}
 
-  public setup(core: CoreSetup, { inspector, bfetch }: ExpressionsSetupDeps): ExpressionsSetup {
-    const { expressions } = this;
-    const { executor, renderers } = expressions;
+  private configureExecutor(core: CoreSetup) {
+    const { executor } = this.expressions;
+
+    const getSavedObject: ExecutionContext['getSavedObject'] = async (type, id) => {
+      const [start] = await core.getStartServices();
+      return start.savedObjects.client.get(type, id);
+    };
 
     executor.extendContext({
       environment: 'client',
+      getSavedObject,
     });
-    executor.registerFunction(kibanaContextFunction());
+  }
+
+  public setup(core: CoreSetup, { inspector, bfetch }: ExpressionsSetupDeps): ExpressionsSetup {
+    this.configureExecutor(core);
+
+    const { expressions } = this;
+    const { executor, renderers } = expressions;
 
     setRenderersRegistry(renderers);
     setExpressionsService(this.expressions);
diff --git a/src/plugins/expressions/public/services.ts b/src/plugins/expressions/public/services.ts
index 4fdff9b151ac2..a203e87414571 100644
--- a/src/plugins/expressions/public/services.ts
+++ b/src/plugins/expressions/public/services.ts
@@ -24,7 +24,7 @@ import { Start as IInspector } from '../../inspector/public';
 import { ExpressionsSetup } from './plugin';
 import { ExpressionsService } from '../common';
 
-export const { getCoreStart, setCoreStart, savedObjects } = createKibanaUtilsCore();
+export const { getCoreStart, setCoreStart } = createKibanaUtilsCore();
 
 export const [getInspector, setInspector] = createGetterSetter<IInspector>('Inspector');
 

From fbf75e33910840245c0f624e4068388166838566 Mon Sep 17 00:00:00 2001
From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com>
Date: Tue, 18 Feb 2020 13:45:20 -0500
Subject: [PATCH 034/174] [SIEM] fix circular dependency (#57729)

* fix Timeline url dependency

* fix circular dependency

* rename ColumnHeader to ColumnHeaderOptions
---
 .../run_check_circular_deps_cli.js            |  3 +-
 .../alerts_viewer/default_headers.ts          | 11 ++-
 .../components/drag_and_drop/helpers.ts       |  6 +-
 .../components/event_details/columns.tsx      |  8 +-
 .../event_details/event_details.tsx           |  6 +-
 .../event_details/event_fields_browser.tsx    |  6 +-
 .../components/event_details/helpers.tsx      | 11 ++-
 .../event_details/stateful_event_details.tsx  |  6 +-
 .../events_viewer/default_headers.tsx         |  9 ++-
 .../events_viewer/default_model.tsx           |  3 +-
 .../events_viewer/events_viewer.tsx           |  7 +-
 .../public/components/events_viewer/index.tsx | 10 ++-
 .../fields_browser/field_browser.tsx          |  6 +-
 .../fields_browser/field_items.test.tsx       |  6 +-
 .../components/fields_browser/field_items.tsx |  8 +-
 .../components/fields_browser/field_name.tsx  |  6 +-
 .../components/fields_browser/fields_pane.tsx |  6 +-
 .../components/fields_browser/index.test.tsx  |  4 +-
 .../components/fields_browser/index.tsx       |  4 +-
 .../public/components/fields_browser/types.ts |  6 +-
 .../public/components/flyout/header/index.tsx |  5 +-
 .../siem/public/components/flyout/index.tsx   |  2 +-
 .../public/components/navigation/helpers.ts   |  5 +-
 .../navigation/tab_navigation/types.ts        |  4 +-
 .../public/components/navigation/types.ts     |  4 +-
 .../components/open_timeline/helpers.test.ts  |  2 +-
 .../components/open_timeline/helpers.ts       | 11 ++-
 .../public/components/open_timeline/index.tsx |  6 +-
 .../timeline/body/actions/index.test.tsx      |  2 +-
 .../body/column_headers/actions/index.tsx     |  5 +-
 .../body/column_headers/column_header.tsx     | 23 +-----
 .../body/column_headers/default_headers.ts    |  7 +-
 .../body/column_headers/filter/index.test.tsx |  2 +-
 .../body/column_headers/filter/index.tsx      |  4 +-
 .../column_headers/header/header_content.tsx  |  4 +-
 .../body/column_headers/header/helpers.ts     |  6 +-
 .../body/column_headers/header/index.test.tsx |  2 +-
 .../body/column_headers/header/index.tsx      |  4 +-
 .../header_tooltip_content/index.test.tsx     |  4 +-
 .../header_tooltip_content/index.tsx          |  4 +-
 .../body/column_headers/helpers.test.ts       | 48 ++++++++++++
 .../timeline/body/column_headers/helpers.ts   | 48 ++++++++++++
 .../body/column_headers/index.test.tsx        |  2 +-
 .../timeline/body/column_headers/index.tsx    |  7 +-
 .../components/timeline/body/constants.ts     | 21 +++++
 .../body/data_driven_columns/index.tsx        |  6 +-
 .../body/events/event_column_view.tsx         |  4 +-
 .../components/timeline/body/events/index.tsx |  6 +-
 .../timeline/body/events/stateful_event.tsx   |  6 +-
 .../body/events/stateful_event_child.tsx      |  4 +-
 .../components/timeline/body/helpers.test.ts  | 46 +----------
 .../components/timeline/body/helpers.ts       | 52 +------------
 .../public/components/timeline/body/index.tsx |  8 +-
 .../body/renderers/column_renderer.ts         |  4 +-
 .../body/renderers/empty_column_renderer.tsx  |  4 +-
 .../body/renderers/plain_column_renderer.tsx  |  4 +-
 .../timeline/body/stateful_body.test.tsx      |  2 +-
 .../timeline/body/stateful_body.tsx           | 20 ++---
 .../siem/public/components/timeline/events.ts |  4 +-
 .../timeline/expandable_event/index.tsx       |  6 +-
 .../siem/public/components/timeline/index.tsx |  9 +--
 .../timeline/search_or_filter/index.tsx       |  3 +-
 .../public/components/timeline/timeline.tsx   |  7 +-
 .../public/components/url_state/helpers.ts    | 12 +--
 .../url_state/initialize_redux_by_url.tsx     |  8 +-
 .../siem/public/components/url_state/types.ts |  8 +-
 .../plugins/siem/public/mock/global_state.ts  |  2 +-
 .../legacy/plugins/siem/public/mock/header.ts |  7 +-
 .../components/activity_monitor/columns.tsx   |  2 +-
 .../components/activity_monitor/index.tsx     | 25 +-----
 .../components/activity_monitor/types.ts      | 29 +++++++
 .../components/signals/actions.tsx            |  2 +-
 .../components/signals/default_config.tsx     |  8 +-
 .../components/signals/index.tsx              |  3 +-
 .../siem/public/store/timeline/actions.ts     | 15 ++--
 .../siem/public/store/timeline/defaults.ts    | 54 +++++++++++++
 .../siem/public/store/timeline/epic.ts        |  5 +-
 .../siem/public/store/timeline/helpers.ts     | 12 +--
 .../siem/public/store/timeline/model.ts       | 76 +++++++------------
 .../public/store/timeline/reducer.test.ts     | 18 +++--
 80 files changed, 455 insertions(+), 390 deletions(-)
 create mode 100644 x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/helpers.test.ts
 create mode 100644 x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/helpers.ts
 create mode 100644 x-pack/legacy/plugins/siem/public/components/timeline/body/constants.ts
 create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/components/activity_monitor/types.ts
 create mode 100644 x-pack/legacy/plugins/siem/public/store/timeline/defaults.ts

diff --git a/x-pack/legacy/plugins/siem/dev_tools/circular_deps/run_check_circular_deps_cli.js b/x-pack/legacy/plugins/siem/dev_tools/circular_deps/run_check_circular_deps_cli.js
index 7d76b1dd921aa..8ca61b2397d8b 100644
--- a/x-pack/legacy/plugins/siem/dev_tools/circular_deps/run_check_circular_deps_cli.js
+++ b/x-pack/legacy/plugins/siem/dev_tools/circular_deps/run_check_circular_deps_cli.js
@@ -21,8 +21,7 @@ run(
     );
 
     const circularFound = result.circular();
-    // We can only care about SIEM code, we should not be penalyze for others
-    if (circularFound.filter(cf => cf.includes('siem')).length !== 0) {
+    if (circularFound.length !== 0) {
       throw createFailError(
         `SIEM circular dependencies of imports has been found:\n - ${circularFound.join('\n - ')}`
       );
diff --git a/x-pack/legacy/plugins/siem/public/components/alerts_viewer/default_headers.ts b/x-pack/legacy/plugins/siem/public/components/alerts_viewer/default_headers.ts
index af9a5ab765571..b12bd1b6c2a51 100644
--- a/x-pack/legacy/plugins/siem/public/components/alerts_viewer/default_headers.ts
+++ b/x-pack/legacy/plugins/siem/public/components/alerts_viewer/default_headers.ts
@@ -4,12 +4,15 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { ColumnHeader } from '../timeline/body/column_headers/column_header';
 import { defaultColumnHeaderType } from '../timeline/body/column_headers/default_headers';
-import { DEFAULT_COLUMN_MIN_WIDTH, DEFAULT_DATE_COLUMN_MIN_WIDTH } from '../timeline/body/helpers';
-import { timelineDefaults, SubsetTimelineModel } from '../../store/timeline/model';
+import {
+  DEFAULT_COLUMN_MIN_WIDTH,
+  DEFAULT_DATE_COLUMN_MIN_WIDTH,
+} from '../timeline/body/constants';
+import { ColumnHeaderOptions, SubsetTimelineModel } from '../../store/timeline/model';
+import { timelineDefaults } from '../../store/timeline/defaults';
 
-export const alertsHeaders: ColumnHeader[] = [
+export const alertsHeaders: ColumnHeaderOptions[] = [
   {
     columnHeaderType: defaultColumnHeaderType,
     id: '@timestamp',
diff --git a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/helpers.ts b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/helpers.ts
index ae3a8828491e3..82ddd2c9f29d7 100644
--- a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/helpers.ts
+++ b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/helpers.ts
@@ -11,8 +11,8 @@ import { ActionCreator } from 'typescript-fsa';
 
 import { BrowserFields, getAllFieldsByName } from '../../containers/source';
 import { IdToDataProvider } from '../../store/drag_and_drop/model';
-import { ColumnHeader } from '../timeline/body/column_headers/column_header';
-import { DEFAULT_COLUMN_MIN_WIDTH } from '../timeline/body/helpers';
+import { ColumnHeaderOptions } from '../../store/timeline/model';
+import { DEFAULT_COLUMN_MIN_WIDTH } from '../timeline/body/constants';
 
 import { DataProvider } from '../timeline/data_providers/data_provider';
 import { dragAndDropActions, timelineActions } from '../../store/actions';
@@ -124,7 +124,7 @@ interface AddProviderToTimelineParams {
 
 interface AddFieldToTimelineColumnsParams {
   upsertColumn?: ActionCreator<{
-    column: ColumnHeader;
+    column: ColumnHeaderOptions;
     id: string;
     index: number;
   }>;
diff --git a/x-pack/legacy/plugins/siem/public/components/event_details/columns.tsx b/x-pack/legacy/plugins/siem/public/components/event_details/columns.tsx
index 1962850425baa..e9903ce66d799 100644
--- a/x-pack/legacy/plugins/siem/public/components/event_details/columns.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/event_details/columns.tsx
@@ -22,6 +22,7 @@ import styled from 'styled-components';
 import { BrowserFields } from '../../containers/source';
 import { ToStringArray } from '../../graphql/types';
 import { WithCopyToClipboard } from '../../lib/clipboard/with_copy_to_clipboard';
+import { ColumnHeaderOptions } from '../../store/timeline/model';
 import { DragEffects } from '../drag_and_drop/draggable_wrapper';
 import { DroppableWrapper } from '../drag_and_drop/droppable_wrapper';
 import { getDroppableId, getDraggableFieldId, DRAG_TYPE_FIELD } from '../drag_and_drop/helpers';
@@ -29,9 +30,8 @@ import { DraggableFieldBadge } from '../draggables/field_badge';
 import { FieldName } from '../fields_browser/field_name';
 import { SelectableText } from '../selectable_text';
 import { OverflowField } from '../tables/helpers';
-import { ColumnHeader } from '../timeline/body/column_headers/column_header';
 import { defaultColumnHeaderType } from '../timeline/body/column_headers/default_headers';
-import { DEFAULT_COLUMN_MIN_WIDTH } from '../timeline/body/helpers';
+import { DEFAULT_COLUMN_MIN_WIDTH } from '../timeline/body/constants';
 import { MESSAGE_FIELD_NAME } from '../timeline/body/renderers/constants';
 import { FormattedFieldValue } from '../timeline/body/renderers/formatted_field';
 import { OnUpdateColumns } from '../timeline/events';
@@ -63,11 +63,11 @@ export const getColumns = ({
   toggleColumn,
 }: {
   browserFields: BrowserFields;
-  columnHeaders: ColumnHeader[];
+  columnHeaders: ColumnHeaderOptions[];
   eventId: string;
   onUpdateColumns: OnUpdateColumns;
   contextId: string;
-  toggleColumn: (column: ColumnHeader) => void;
+  toggleColumn: (column: ColumnHeaderOptions) => void;
 }) => [
   {
     field: 'field',
diff --git a/x-pack/legacy/plugins/siem/public/components/event_details/event_details.tsx b/x-pack/legacy/plugins/siem/public/components/event_details/event_details.tsx
index f77c703f064f6..9234fe44320f0 100644
--- a/x-pack/legacy/plugins/siem/public/components/event_details/event_details.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/event_details/event_details.tsx
@@ -10,7 +10,7 @@ import styled from 'styled-components';
 
 import { BrowserFields } from '../../containers/source';
 import { DetailItem } from '../../graphql/types';
-import { ColumnHeader } from '../timeline/body/column_headers/column_header';
+import { ColumnHeaderOptions } from '../../store/timeline/model';
 import { OnUpdateColumns } from '../timeline/events';
 import { EventFieldsBrowser } from './event_fields_browser';
 import { JsonView } from './json_view';
@@ -20,14 +20,14 @@ export type View = 'table-view' | 'json-view';
 
 interface Props {
   browserFields: BrowserFields;
-  columnHeaders: ColumnHeader[];
+  columnHeaders: ColumnHeaderOptions[];
   data: DetailItem[];
   id: string;
   view: View;
   onUpdateColumns: OnUpdateColumns;
   onViewSelected: (selected: View) => void;
   timelineId: string;
-  toggleColumn: (column: ColumnHeader) => void;
+  toggleColumn: (column: ColumnHeaderOptions) => void;
 }
 
 const Details = styled.div`
diff --git a/x-pack/legacy/plugins/siem/public/components/event_details/event_fields_browser.tsx b/x-pack/legacy/plugins/siem/public/components/event_details/event_fields_browser.tsx
index f4e9a2b789d71..9a842339cb62e 100644
--- a/x-pack/legacy/plugins/siem/public/components/event_details/event_fields_browser.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/event_details/event_fields_browser.tsx
@@ -8,7 +8,7 @@ import { sortBy } from 'lodash';
 import { EuiInMemoryTable } from '@elastic/eui';
 import React, { useMemo } from 'react';
 
-import { ColumnHeader } from '../timeline/body/column_headers/column_header';
+import { ColumnHeaderOptions } from '../../store/timeline/model';
 import { BrowserFields, getAllFieldsByName } from '../../containers/source';
 import { DetailItem } from '../../graphql/types';
 import { OnUpdateColumns } from '../timeline/events';
@@ -18,12 +18,12 @@ import { search } from './helpers';
 
 interface Props {
   browserFields: BrowserFields;
-  columnHeaders: ColumnHeader[];
+  columnHeaders: ColumnHeaderOptions[];
   data: DetailItem[];
   eventId: string;
   onUpdateColumns: OnUpdateColumns;
   timelineId: string;
-  toggleColumn: (column: ColumnHeader) => void;
+  toggleColumn: (column: ColumnHeaderOptions) => void;
 }
 
 /** Renders a table view or JSON view of the `ECS` `data` */
diff --git a/x-pack/legacy/plugins/siem/public/components/event_details/helpers.tsx b/x-pack/legacy/plugins/siem/public/components/event_details/helpers.tsx
index a4ec8ff2c4907..5d9c9d82490bb 100644
--- a/x-pack/legacy/plugins/siem/public/components/event_details/helpers.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/event_details/helpers.tsx
@@ -7,8 +7,11 @@
 import { get, getOr, isEmpty, uniqBy } from 'lodash/fp';
 
 import { BrowserField, BrowserFields } from '../../containers/source';
-import { ColumnHeader } from '../timeline/body/column_headers/column_header';
-import { DEFAULT_DATE_COLUMN_MIN_WIDTH, DEFAULT_COLUMN_MIN_WIDTH } from '../timeline/body/helpers';
+import { ColumnHeaderOptions } from '../../store/timeline/model';
+import {
+  DEFAULT_DATE_COLUMN_MIN_WIDTH,
+  DEFAULT_COLUMN_MIN_WIDTH,
+} from '../timeline/body/constants';
 import { ToStringArray } from '../../graphql/types';
 
 import * as i18n from './translations';
@@ -46,7 +49,7 @@ export const getColumnHeaderFromBrowserField = ({
 }: {
   browserField: Partial<BrowserField>;
   width?: number;
-}): ColumnHeader => ({
+}): ColumnHeaderOptions => ({
   category: browserField.category,
   columnHeaderType: 'not-filtered',
   description: browserField.description != null ? browserField.description : undefined,
@@ -68,7 +71,7 @@ export const getColumnsWithTimestamp = ({
 }: {
   browserFields: BrowserFields;
   category: string;
-}): ColumnHeader[] => {
+}): ColumnHeaderOptions[] => {
   const emptyFields: Record<string, Partial<BrowserField>> = {};
   const timestamp = get('base.fields.@timestamp', browserFields);
   const categoryFields: Array<Partial<BrowserField>> = [
diff --git a/x-pack/legacy/plugins/siem/public/components/event_details/stateful_event_details.tsx b/x-pack/legacy/plugins/siem/public/components/event_details/stateful_event_details.tsx
index c614fd52316bc..c79f02740253a 100644
--- a/x-pack/legacy/plugins/siem/public/components/event_details/stateful_event_details.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/event_details/stateful_event_details.tsx
@@ -7,20 +7,20 @@
 import React, { useCallback, useState } from 'react';
 
 import { BrowserFields } from '../../containers/source';
-import { ColumnHeader } from '../timeline/body/column_headers/column_header';
 import { DetailItem } from '../../graphql/types';
+import { ColumnHeaderOptions } from '../../store/timeline/model';
 import { OnUpdateColumns } from '../timeline/events';
 
 import { EventDetails, View } from './event_details';
 
 interface Props {
   browserFields: BrowserFields;
-  columnHeaders: ColumnHeader[];
+  columnHeaders: ColumnHeaderOptions[];
   data: DetailItem[];
   id: string;
   onUpdateColumns: OnUpdateColumns;
   timelineId: string;
-  toggleColumn: (column: ColumnHeader) => void;
+  toggleColumn: (column: ColumnHeaderOptions) => void;
 }
 
 export const StatefulEventDetails = React.memo<Props>(
diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/default_headers.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/default_headers.tsx
index fed61c3bdd215..b97e0da5df078 100644
--- a/x-pack/legacy/plugins/siem/public/components/events_viewer/default_headers.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/default_headers.tsx
@@ -4,11 +4,14 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { ColumnHeader } from '../timeline/body/column_headers/column_header';
+import { ColumnHeaderOptions } from '../../store/timeline/model';
 import { defaultColumnHeaderType } from '../timeline/body/column_headers/default_headers';
-import { DEFAULT_COLUMN_MIN_WIDTH, DEFAULT_DATE_COLUMN_MIN_WIDTH } from '../timeline/body/helpers';
+import {
+  DEFAULT_COLUMN_MIN_WIDTH,
+  DEFAULT_DATE_COLUMN_MIN_WIDTH,
+} from '../timeline/body/constants';
 
-export const defaultHeaders: ColumnHeader[] = [
+export const defaultHeaders: ColumnHeaderOptions[] = [
   {
     columnHeaderType: defaultColumnHeaderType,
     id: '@timestamp',
diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/default_model.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/default_model.tsx
index ac385057406e2..59a9f6d061c8d 100644
--- a/x-pack/legacy/plugins/siem/public/components/events_viewer/default_model.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/default_model.tsx
@@ -5,7 +5,8 @@
  */
 
 import { defaultHeaders } from './default_headers';
-import { SubsetTimelineModel, timelineDefaults } from '../../store/timeline/model';
+import { SubsetTimelineModel } from '../../store/timeline/model';
+import { timelineDefaults } from '../../store/timeline/defaults';
 
 export const eventsDefaultModel: SubsetTimelineModel = {
   ...timelineDefaults,
diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx
index cbce1f635310a..b05529f9a497f 100644
--- a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx
@@ -15,9 +15,8 @@ import { BrowserFields } from '../../containers/source';
 import { TimelineQuery } from '../../containers/timeline';
 import { Direction } from '../../graphql/types';
 import { useKibana } from '../../lib/kibana';
-import { KqlMode } from '../../store/timeline/model';
+import { ColumnHeaderOptions, KqlMode } from '../../store/timeline/model';
 import { HeaderSection } from '../header_section';
-import { ColumnHeader } from '../timeline/body/column_headers/column_header';
 import { defaultHeaders } from '../timeline/body/column_headers/default_headers';
 import { Sort } from '../timeline/body/sort';
 import { StatefulBody } from '../timeline/body/stateful_body';
@@ -50,7 +49,7 @@ const StyledEuiPanel = styled(EuiPanel)`
 
 interface Props {
   browserFields: BrowserFields;
-  columns: ColumnHeader[];
+  columns: ColumnHeaderOptions[];
   dataProviders: DataProvider[];
   deletedEventIds: Readonly<string[]>;
   end: number;
@@ -68,7 +67,7 @@ interface Props {
   start: number;
   sort: Sort;
   timelineTypeContext: TimelineTypeContextProps;
-  toggleColumn: (column: ColumnHeader) => void;
+  toggleColumn: (column: ColumnHeaderOptions) => void;
   utilityBar?: (refetch: inputsModel.Refetch, totalCount: number) => React.ReactNode;
 }
 
diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx
index 1e7c7fc2411bb..762ae8497dadb 100644
--- a/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx
@@ -7,10 +7,14 @@
 import { isEqual } from 'lodash/fp';
 import React, { useCallback, useMemo, useEffect } from 'react';
 import { connect, ConnectedProps } from 'react-redux';
+
 import { inputsModel, inputsSelectors, State, timelineSelectors } from '../../store';
 import { inputsActions, timelineActions } from '../../store/actions';
-import { SubsetTimelineModel, TimelineModel } from '../../store/timeline/model';
-import { ColumnHeader } from '../timeline/body/column_headers/column_header';
+import {
+  ColumnHeaderOptions,
+  SubsetTimelineModel,
+  TimelineModel,
+} from '../../store/timeline/model';
 import { OnChangeItemsPerPage } from '../timeline/events';
 import { Filter } from '../../../../../../../src/plugins/data/public';
 
@@ -86,7 +90,7 @@ const StatefulEventsViewerComponent: React.FC<Props> = ({
   );
 
   const toggleColumn = useCallback(
-    (column: ColumnHeader) => {
+    (column: ColumnHeaderOptions) => {
       const exists = columns.findIndex(c => c.id === column.id) !== -1;
 
       if (!exists && upsertColumn != null) {
diff --git a/x-pack/legacy/plugins/siem/public/components/fields_browser/field_browser.tsx b/x-pack/legacy/plugins/siem/public/components/fields_browser/field_browser.tsx
index c8a0eb9da688b..02aeab74f8bab 100644
--- a/x-pack/legacy/plugins/siem/public/components/fields_browser/field_browser.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/fields_browser/field_browser.tsx
@@ -10,7 +10,7 @@ import { noop } from 'lodash/fp';
 import styled from 'styled-components';
 
 import { BrowserFields } from '../../containers/source';
-import { ColumnHeader } from '../timeline/body/column_headers/column_header';
+import { ColumnHeaderOptions } from '../../store/timeline/model';
 import { CategoriesPane } from './categories_pane';
 import { FieldsPane } from './fields_pane';
 import { Header } from './header';
@@ -57,7 +57,7 @@ type Props = Pick<
   /**
    * The current timeline column headers
    */
-  columnHeaders: ColumnHeader[];
+  columnHeaders: ColumnHeaderOptions[];
   /**
    * A map of categoryId -> metadata about the fields in that category,
    * filtered such that the name of every field in the category includes
@@ -95,7 +95,7 @@ type Props = Pick<
   /**
    * Invoked to add or remove a column from the timeline
    */
-  toggleColumn: (column: ColumnHeader) => void;
+  toggleColumn: (column: ColumnHeaderOptions) => void;
 };
 
 /**
diff --git a/x-pack/legacy/plugins/siem/public/components/fields_browser/field_items.test.tsx b/x-pack/legacy/plugins/siem/public/components/fields_browser/field_items.test.tsx
index 4d0c707c46910..226b56dad8c4f 100644
--- a/x-pack/legacy/plugins/siem/public/components/fields_browser/field_items.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/fields_browser/field_items.test.tsx
@@ -9,9 +9,9 @@ import React from 'react';
 
 import { mockBrowserFields } from '../../containers/source/mock';
 import { TestProviders } from '../../mock';
-import { ColumnHeader } from '../timeline/body/column_headers/column_header';
+import { ColumnHeaderOptions } from '../../store/timeline/model';
 import { defaultColumnHeaderType } from '../timeline/body/column_headers/default_headers';
-import { DEFAULT_DATE_COLUMN_MIN_WIDTH } from '../timeline/body/helpers';
+import { DEFAULT_DATE_COLUMN_MIN_WIDTH } from '../timeline/body/constants';
 
 import { Category } from './category';
 import { getFieldColumns, getFieldItems } from './field_items';
@@ -21,7 +21,7 @@ import { useMountAppended } from '../../utils/use_mount_appended';
 const selectedCategoryId = 'base';
 const selectedCategoryFields = mockBrowserFields[selectedCategoryId].fields;
 const timestampFieldId = '@timestamp';
-const columnHeaders: ColumnHeader[] = [
+const columnHeaders: ColumnHeaderOptions[] = [
   {
     category: 'base',
     columnHeaderType: defaultColumnHeaderType,
diff --git a/x-pack/legacy/plugins/siem/public/components/fields_browser/field_items.tsx b/x-pack/legacy/plugins/siem/public/components/fields_browser/field_items.tsx
index 778e9d3d3c744..990c2678b1006 100644
--- a/x-pack/legacy/plugins/siem/public/components/fields_browser/field_items.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/fields_browser/field_items.tsx
@@ -13,6 +13,7 @@ import { Draggable } from 'react-beautiful-dnd';
 import styled from 'styled-components';
 
 import { BrowserField, BrowserFields } from '../../containers/source';
+import { ColumnHeaderOptions } from '../../store/timeline/model';
 import { DragEffects } from '../drag_and_drop/draggable_wrapper';
 import { DroppableWrapper } from '../drag_and_drop/droppable_wrapper';
 import { getDraggableFieldId, getDroppableId, DRAG_TYPE_FIELD } from '../drag_and_drop/helpers';
@@ -20,9 +21,8 @@ import { DraggableFieldBadge } from '../draggables/field_badge';
 import { getEmptyValue } from '../empty_value';
 import { getColumnsWithTimestamp, getExampleText, getIconFromType } from '../event_details/helpers';
 import { SelectableText } from '../selectable_text';
-import { ColumnHeader } from '../timeline/body/column_headers/column_header';
 import { defaultColumnHeaderType } from '../timeline/body/column_headers/default_headers';
-import { DEFAULT_COLUMN_MIN_WIDTH } from '../timeline/body/helpers';
+import { DEFAULT_COLUMN_MIN_WIDTH } from '../timeline/body/constants';
 import { OnUpdateColumns } from '../timeline/events';
 import { TruncatableText } from '../truncatable_text';
 import { FieldName } from './field_name';
@@ -68,10 +68,10 @@ export const getFieldItems = ({
   browserFields: BrowserFields;
   category: Partial<BrowserField>;
   categoryId: string;
-  columnHeaders: ColumnHeader[];
+  columnHeaders: ColumnHeaderOptions[];
   highlight?: string;
   timelineId: string;
-  toggleColumn: (column: ColumnHeader) => void;
+  toggleColumn: (column: ColumnHeaderOptions) => void;
   onUpdateColumns: OnUpdateColumns;
 }): FieldItem[] =>
   uniqBy('name', [
diff --git a/x-pack/legacy/plugins/siem/public/components/fields_browser/field_name.tsx b/x-pack/legacy/plugins/siem/public/components/fields_browser/field_name.tsx
index b8fbe46baa9c5..fe434a6ad63ce 100644
--- a/x-pack/legacy/plugins/siem/public/components/fields_browser/field_name.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/fields_browser/field_name.tsx
@@ -18,7 +18,7 @@ import React, { useContext } from 'react';
 import styled from 'styled-components';
 
 import { WithCopyToClipboard } from '../../lib/clipboard/with_copy_to_clipboard';
-import { ColumnHeader } from '../timeline/body/column_headers/column_header';
+import { ColumnHeaderOptions } from '../../store/timeline/model';
 import { OnUpdateColumns } from '../timeline/events';
 import { TimelineContext } from '../timeline/timeline_context';
 import { WithHoverActions } from '../with_hover_actions';
@@ -107,7 +107,7 @@ ViewCategoryIcon.displayName = 'ViewCategoryIcon';
 interface ToolTipProps {
   categoryId: string;
   onUpdateColumns: OnUpdateColumns;
-  categoryColumns: ColumnHeader[];
+  categoryColumns: ColumnHeaderOptions[];
 }
 
 const ViewCategory = React.memo<ToolTipProps>(
@@ -138,7 +138,7 @@ ViewCategory.displayName = 'ViewCategory';
 /** Renders a field name in it's non-dragging state */
 export const FieldName = React.memo<{
   categoryId: string;
-  categoryColumns: ColumnHeader[];
+  categoryColumns: ColumnHeaderOptions[];
   fieldId: string;
   highlight?: string;
   onUpdateColumns: OnUpdateColumns;
diff --git a/x-pack/legacy/plugins/siem/public/components/fields_browser/fields_pane.tsx b/x-pack/legacy/plugins/siem/public/components/fields_browser/fields_pane.tsx
index fba6e22e4b21f..354b2ae5e5eb8 100644
--- a/x-pack/legacy/plugins/siem/public/components/fields_browser/fields_pane.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/fields_browser/fields_pane.tsx
@@ -9,7 +9,7 @@ import React from 'react';
 import styled from 'styled-components';
 
 import { BrowserFields } from '../../containers/source';
-import { ColumnHeader } from '../timeline/body/column_headers/column_header';
+import { ColumnHeaderOptions } from '../../store/timeline/model';
 
 import { Category } from './category';
 import { FieldBrowserProps } from './types';
@@ -33,7 +33,7 @@ const NoFieldsFlexGroup = styled(EuiFlexGroup)`
 NoFieldsFlexGroup.displayName = 'NoFieldsFlexGroup';
 
 type Props = Pick<FieldBrowserProps, 'onFieldSelected' | 'onUpdateColumns' | 'timelineId'> & {
-  columnHeaders: ColumnHeader[];
+  columnHeaders: ColumnHeaderOptions[];
   /**
    * A map of categoryId -> metadata about the fields in that category,
    * filtered such that the name of every field in the category includes
@@ -56,7 +56,7 @@ type Props = Pick<FieldBrowserProps, 'onFieldSelected' | 'onUpdateColumns' | 'ti
   /**
    * Invoked to add or remove a column from the timeline
    */
-  toggleColumn: (column: ColumnHeader) => void;
+  toggleColumn: (column: ColumnHeaderOptions) => void;
 };
 export const FieldsPane = React.memo<Props>(
   ({
diff --git a/x-pack/legacy/plugins/siem/public/components/fields_browser/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/fields_browser/index.test.tsx
index cf9e4f57d67b7..9e513b890e722 100644
--- a/x-pack/legacy/plugins/siem/public/components/fields_browser/index.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/fields_browser/index.test.tsx
@@ -10,11 +10,11 @@ import { ActionCreator } from 'typescript-fsa';
 
 import { mockBrowserFields } from '../../containers/source/mock';
 import { TestProviders } from '../../mock';
+import { ColumnHeaderOptions } from '../../store/timeline/model';
 
 import { FIELD_BROWSER_HEIGHT, FIELD_BROWSER_WIDTH } from './helpers';
 
 import { StatefulFieldsBrowserComponent } from '.';
-import { ColumnHeader } from '../timeline/body/column_headers/column_header';
 
 // Suppress warnings about "react-beautiful-dnd" until we migrate to @testing-library/react
 /* eslint-disable no-console */
@@ -35,7 +35,7 @@ const removeColumnMock = (jest.fn() as unknown) as ActionCreator<{
 }>;
 
 const upsertColumnMock = (jest.fn() as unknown) as ActionCreator<{
-  column: ColumnHeader;
+  column: ColumnHeaderOptions;
   id: string;
   index: number;
 }>;
diff --git a/x-pack/legacy/plugins/siem/public/components/fields_browser/index.tsx b/x-pack/legacy/plugins/siem/public/components/fields_browser/index.tsx
index b545c6890bd41..3e19ba383b4ec 100644
--- a/x-pack/legacy/plugins/siem/public/components/fields_browser/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/fields_browser/index.tsx
@@ -12,7 +12,7 @@ import styled from 'styled-components';
 
 import { BrowserFields } from '../../containers/source';
 import { timelineActions } from '../../store/actions';
-import { ColumnHeader } from '../timeline/body/column_headers/column_header';
+import { ColumnHeaderOptions } from '../../store/timeline/model';
 import { DEFAULT_CATEGORY_NAME } from '../timeline/body/column_headers/default_headers';
 import { FieldsBrowser } from './field_browser';
 import { filterBrowserFieldsByFieldName, mergeBrowserFieldsWithDefaultCategory } from './helpers';
@@ -124,7 +124,7 @@ export const StatefulFieldsBrowserComponent = React.memo<FieldBrowserProps & Pro
      * columns in the timeline, this function dispatches the action that
      * causes the timeline display those columns.
      */
-    const updateColumnsAndSelectCategoryId = useCallback((columns: ColumnHeader[]) => {
+    const updateColumnsAndSelectCategoryId = useCallback((columns: ColumnHeaderOptions[]) => {
       onUpdateColumns(columns); // show the category columns in the timeline
     }, []);
 
diff --git a/x-pack/legacy/plugins/siem/public/components/fields_browser/types.ts b/x-pack/legacy/plugins/siem/public/components/fields_browser/types.ts
index 21d2d65168fa4..d6b1936fcc52f 100644
--- a/x-pack/legacy/plugins/siem/public/components/fields_browser/types.ts
+++ b/x-pack/legacy/plugins/siem/public/components/fields_browser/types.ts
@@ -5,15 +5,15 @@
  */
 
 import { BrowserFields } from '../../containers/source';
+import { ColumnHeaderOptions } from '../../store/timeline/model';
 import { OnUpdateColumns } from '../timeline/events';
-import { ColumnHeader } from '../timeline/body/column_headers/column_header';
 
 export type OnFieldSelected = (fieldId: string) => void;
 export type OnHideFieldBrowser = () => void;
 
 export interface FieldBrowserProps {
   /** The timeline's current column headers */
-  columnHeaders: ColumnHeader[];
+  columnHeaders: ColumnHeaderOptions[];
   /** A map of categoryId -> metadata about the fields in that category */
   browserFields: BrowserFields;
   /** The height of the field browser */
@@ -31,7 +31,7 @@ export interface FieldBrowserProps {
   /** The timeline associated with this field browser */
   timelineId: string;
   /** Adds or removes a column to / from the timeline */
-  toggleColumn: (column: ColumnHeader) => void;
+  toggleColumn: (column: ColumnHeaderOptions) => void;
   /** The width of the field browser */
   width: number;
 }
diff --git a/x-pack/legacy/plugins/siem/public/components/flyout/header/index.tsx b/x-pack/legacy/plugins/siem/public/components/flyout/header/index.tsx
index 901f7afd1ed48..06e77b51787be 100644
--- a/x-pack/legacy/plugins/siem/public/components/flyout/header/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/flyout/header/index.tsx
@@ -23,8 +23,9 @@ import { Properties } from '../../timeline/properties';
 import { appActions } from '../../../store/app';
 import { inputsActions } from '../../../store/inputs';
 import { timelineActions } from '../../../store/actions';
-import { timelineDefaults, TimelineModel } from '../../../store/timeline/model';
-import { DEFAULT_TIMELINE_WIDTH } from '../../timeline/body/helpers';
+import { TimelineModel } from '../../../store/timeline/model';
+import { timelineDefaults } from '../../../store/timeline/defaults';
+import { DEFAULT_TIMELINE_WIDTH } from '../../timeline/body/constants';
 import { InputsModelId } from '../../../store/inputs/constants';
 
 interface OwnProps {
diff --git a/x-pack/legacy/plugins/siem/public/components/flyout/index.tsx b/x-pack/legacy/plugins/siem/public/components/flyout/index.tsx
index d18c22e44ce94..eb41773bb21c8 100644
--- a/x-pack/legacy/plugins/siem/public/components/flyout/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/flyout/index.tsx
@@ -15,7 +15,7 @@ import { DataProvider } from '../timeline/data_providers/data_provider';
 import { FlyoutButton } from './button';
 import { Pane } from './pane';
 import { timelineActions } from '../../store/actions';
-import { DEFAULT_TIMELINE_WIDTH } from '../timeline/body/helpers';
+import { DEFAULT_TIMELINE_WIDTH } from '../timeline/body/constants';
 
 /** The height in pixels of the flyout header, exported for use in height calculations */
 export const flyoutHeaderHeight: number = 60;
diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/helpers.ts b/x-pack/legacy/plugins/siem/public/components/navigation/helpers.ts
index f010cd67c10bc..9a95d93a2df70 100644
--- a/x-pack/legacy/plugins/siem/public/components/navigation/helpers.ts
+++ b/x-pack/legacy/plugins/siem/public/components/navigation/helpers.ts
@@ -8,8 +8,9 @@ import { isEmpty } from 'lodash/fp';
 import { Location } from 'history';
 
 import { UrlInputsModel } from '../../store/inputs/model';
+import { TimelineUrl } from '../../store/timeline/model';
 import { CONSTANTS } from '../url_state/constants';
-import { URL_STATE_KEYS, KeyUrlState, Timeline } from '../url_state/types';
+import { URL_STATE_KEYS, KeyUrlState } from '../url_state/types';
 import {
   replaceQueryStringInLocation,
   replaceStateKeyInQueryString,
@@ -24,7 +25,7 @@ export const getSearch = (tab: SearchNavTab, urlState: TabNavigationProps): stri
   if (tab && tab.urlKey != null && URL_STATE_KEYS[tab.urlKey] != null) {
     return URL_STATE_KEYS[tab.urlKey].reduce<Location>(
       (myLocation: Location, urlKey: KeyUrlState) => {
-        let urlStateToReplace: UrlInputsModel | Query | Filter[] | Timeline | string = '';
+        let urlStateToReplace: UrlInputsModel | Query | Filter[] | TimelineUrl | string = '';
 
         if (urlKey === CONSTANTS.appQuery && urlState.query != null) {
           if (urlState.query.query === '') {
diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/types.ts b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/types.ts
index bf8e036ad5ce4..fe701ad115d17 100644
--- a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/types.ts
+++ b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/types.ts
@@ -6,8 +6,8 @@
 
 import { UrlInputsModel } from '../../../store/inputs/model';
 import { CONSTANTS } from '../../url_state/constants';
-import { Timeline } from '../../url_state/types';
 import { HostsTableType } from '../../../store/hosts/model';
+import { TimelineUrl } from '../../../store/timeline/model';
 import { Filter, Query } from '../../../../../../../../src/plugins/data/public';
 
 import { SiemNavigationProps } from '../types';
@@ -20,7 +20,7 @@ export interface TabNavigationProps extends SiemNavigationProps {
   [CONSTANTS.filters]?: Filter[];
   [CONSTANTS.savedQuery]?: string;
   [CONSTANTS.timerange]: UrlInputsModel;
-  [CONSTANTS.timeline]: Timeline;
+  [CONSTANTS.timeline]: TimelineUrl;
 }
 
 export interface TabNavigationItemProps {
diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/types.ts b/x-pack/legacy/plugins/siem/public/components/navigation/types.ts
index cc60c470e21f3..96a70e0bc70cc 100644
--- a/x-pack/legacy/plugins/siem/public/components/navigation/types.ts
+++ b/x-pack/legacy/plugins/siem/public/components/navigation/types.ts
@@ -7,8 +7,8 @@
 import { Filter, Query } from '../../../../../../../src/plugins/data/public';
 import { HostsTableType } from '../../store/hosts/model';
 import { UrlInputsModel } from '../../store/inputs/model';
+import { TimelineUrl } from '../../store/timeline/model';
 import { CONSTANTS, UrlStateType } from '../url_state/constants';
-import { Timeline } from '../url_state/types';
 
 export interface SiemNavigationProps {
   display?: 'default' | 'condensed';
@@ -24,7 +24,7 @@ export interface SiemNavigationComponentProps {
     [CONSTANTS.filters]?: Filter[];
     [CONSTANTS.savedQuery]?: string;
     [CONSTANTS.timerange]: UrlInputsModel;
-    [CONSTANTS.timeline]: Timeline;
+    [CONSTANTS.timeline]: TimelineUrl;
   };
 }
 
diff --git a/x-pack/legacy/plugins/siem/public/components/open_timeline/helpers.test.ts b/x-pack/legacy/plugins/siem/public/components/open_timeline/helpers.test.ts
index 2228ee4262400..120d644b3b33a 100644
--- a/x-pack/legacy/plugins/siem/public/components/open_timeline/helpers.test.ts
+++ b/x-pack/legacy/plugins/siem/public/components/open_timeline/helpers.test.ts
@@ -6,7 +6,7 @@
 import { cloneDeep, omit } from 'lodash/fp';
 
 import { mockTimelineResults } from '../../mock/timeline_results';
-import { timelineDefaults } from '../../store/timeline/model';
+import { timelineDefaults } from '../../store/timeline/defaults';
 import {
   defaultTimelineToTimelineModel,
   getNotesCount,
diff --git a/x-pack/legacy/plugins/siem/public/components/open_timeline/helpers.ts b/x-pack/legacy/plugins/siem/public/components/open_timeline/helpers.ts
index 61c3d9f73e0d0..4f7d6cd64f1d9 100644
--- a/x-pack/legacy/plugins/siem/public/components/open_timeline/helpers.ts
+++ b/x-pack/legacy/plugins/siem/public/components/open_timeline/helpers.ts
@@ -19,13 +19,16 @@ import {
   addTimeline as dispatchAddTimeline,
 } from '../../store/timeline/actions';
 
-import { TimelineModel, timelineDefaults } from '../../store/timeline/model';
-import { ColumnHeader } from '../timeline/body/column_headers/column_header';
+import { ColumnHeaderOptions, TimelineModel } from '../../store/timeline/model';
+import { timelineDefaults } from '../../store/timeline/defaults';
 import {
   defaultColumnHeaderType,
   defaultHeaders,
 } from '../timeline/body/column_headers/default_headers';
-import { DEFAULT_DATE_COLUMN_MIN_WIDTH, DEFAULT_COLUMN_MIN_WIDTH } from '../timeline/body/helpers';
+import {
+  DEFAULT_DATE_COLUMN_MIN_WIDTH,
+  DEFAULT_COLUMN_MIN_WIDTH,
+} from '../timeline/body/constants';
 
 import { OpenTimelineResult, UpdateTimeline, DispatchUpdateTimeline } from './types';
 import { getTimeRangeSettings } from '../../utils/default_date_settings';
@@ -78,7 +81,7 @@ export const defaultTimelineToTimelineModel = (
     columns:
       timeline.columns != null
         ? timeline.columns.map(col => {
-            const timelineCols: ColumnHeader = {
+            const timelineCols: ColumnHeaderOptions = {
               ...col,
               columnHeaderType: defaultColumnHeaderType,
               id: col.id != null ? col.id : 'unknown',
diff --git a/x-pack/legacy/plugins/siem/public/components/open_timeline/index.tsx b/x-pack/legacy/plugins/siem/public/components/open_timeline/index.tsx
index daa64f9f16c83..26a7487fee52b 100644
--- a/x-pack/legacy/plugins/siem/public/components/open_timeline/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/open_timeline/index.tsx
@@ -15,12 +15,12 @@ import { AllTimelinesVariables, AllTimelinesQuery } from '../../containers/timel
 import { allTimelinesQuery } from '../../containers/timeline/all/index.gql_query';
 import { DeleteTimelineMutation, SortFieldTimeline, Direction } from '../../graphql/types';
 import { State, timelineSelectors } from '../../store';
-import { timelineDefaults, TimelineModel } from '../../store/timeline/model';
+import { ColumnHeaderOptions, TimelineModel } from '../../store/timeline/model';
+import { timelineDefaults } from '../../store/timeline/defaults';
 import {
   createTimeline as dispatchCreateNewTimeline,
   updateIsLoading as dispatchUpdateIsLoading,
 } from '../../store/timeline/actions';
-import { ColumnHeader } from '../timeline/body/column_headers/column_header';
 import { OpenTimeline } from './open_timeline';
 import { OPEN_TIMELINE_CLASS_NAME, queryTimelineById, dispatchUpdateTimeline } from './helpers';
 import { OpenTimelineModalBody } from './open_timeline_modal/open_timeline_modal_body';
@@ -335,7 +335,7 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({
     show,
   }: {
     id: string;
-    columns: ColumnHeader[];
+    columns: ColumnHeaderOptions[];
     show?: boolean;
   }) => dispatch(dispatchCreateNewTimeline({ id, columns, show })),
   updateIsLoading: ({ id, isLoading }: { id: string; isLoading: boolean }) =>
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/actions/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/actions/index.test.tsx
index 9351fddd90dd5..6055745e9378e 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/actions/index.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/actions/index.test.tsx
@@ -7,7 +7,7 @@ import { mount } from 'enzyme';
 import React from 'react';
 
 import { TestProviders } from '../../../../mock';
-import { DEFAULT_ACTIONS_COLUMN_WIDTH } from '../helpers';
+import { DEFAULT_ACTIONS_COLUMN_WIDTH } from '../constants';
 
 import { Actions } from '.';
 
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/actions/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/actions/index.tsx
index 64e8aa3c7e7b7..d9e04ad873537 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/actions/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/actions/index.tsx
@@ -7,15 +7,16 @@
 import { EuiButtonIcon } from '@elastic/eui';
 import React from 'react';
 
+import { ColumnHeaderOptions } from '../../../../../store/timeline/model';
 import { OnColumnRemoved } from '../../../events';
 import { EventsHeadingExtra, EventsLoading } from '../../../styles';
 import { useTimelineContext } from '../../../timeline_context';
 import { Sort } from '../../sort';
-import { ColumnHeader } from '../column_header';
+
 import * as i18n from '../translations';
 
 interface Props {
-  header: ColumnHeader;
+  header: ColumnHeaderOptions;
   onColumnRemoved: OnColumnRemoved;
   sort: Sort;
 }
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/column_header.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/column_header.tsx
index 9df805bddcc82..c3f28fd513d08 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/column_header.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/column_header.tsx
@@ -7,6 +7,8 @@
 import React from 'react';
 import { Draggable } from 'react-beautiful-dnd';
 import { Resizable, ResizeCallback } from 're-resizable';
+
+import { ColumnHeaderOptions } from '../../../../store/timeline/model';
 import { DragEffects } from '../../../drag_and_drop/draggable_wrapper';
 import { getDraggableFieldId, DRAG_TYPE_FIELD } from '../../../drag_and_drop/helpers';
 import { DraggableFieldBadge } from '../../../draggables/field_badge';
@@ -16,29 +18,10 @@ import { Sort } from '../sort';
 import { DraggingContainer } from './common/dragging_container';
 
 import { Header } from './header';
-import { ColumnId } from '../column_id';
-
-export type ColumnHeaderType = 'not-filtered' | 'text-filter';
-
-/** The specification of a column header */
-export interface ColumnHeader {
-  aggregatable?: boolean;
-  category?: string;
-  columnHeaderType: ColumnHeaderType;
-  description?: string;
-  example?: string;
-  format?: string;
-  id: ColumnId;
-  label?: string;
-  linkField?: string;
-  placeholder?: string;
-  type?: string;
-  width: number;
-}
 
 interface ColumneHeaderProps {
   draggableIndex: number;
-  header: ColumnHeader;
+  header: ColumnHeaderOptions;
   onColumnRemoved: OnColumnRemoved;
   onColumnSorted: OnColumnSorted;
   onColumnResized: OnColumnResized;
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/default_headers.ts b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/default_headers.ts
index 4280ad684e8e0..082b5103a7d86 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/default_headers.ts
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/default_headers.ts
@@ -4,13 +4,12 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { DEFAULT_COLUMN_MIN_WIDTH, DEFAULT_DATE_COLUMN_MIN_WIDTH } from '../helpers';
-
-import { ColumnHeader, ColumnHeaderType } from './column_header';
+import { ColumnHeaderOptions, ColumnHeaderType } from '../../../../store/timeline/model';
+import { DEFAULT_COLUMN_MIN_WIDTH, DEFAULT_DATE_COLUMN_MIN_WIDTH } from '../constants';
 
 export const defaultColumnHeaderType: ColumnHeaderType = 'not-filtered';
 
-export const defaultHeaders: ColumnHeader[] = [
+export const defaultHeaders: ColumnHeaderOptions[] = [
   {
     columnHeaderType: defaultColumnHeaderType,
     id: '@timestamp',
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/filter/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/filter/index.test.tsx
index b9cfee395bafb..f0f6ce8d0ed6f 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/filter/index.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/filter/index.test.tsx
@@ -7,7 +7,7 @@
 import { mount, shallow } from 'enzyme';
 import React from 'react';
 
-import { ColumnHeaderType } from '../column_header';
+import { ColumnHeaderType } from '../../../../../store/timeline/model';
 import { defaultHeaders } from '../default_headers';
 
 import { Filter } from '.';
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/filter/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/filter/index.tsx
index 0b5247e7da678..911a309edfd98 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/filter/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/filter/index.tsx
@@ -8,11 +8,11 @@ import { noop } from 'lodash/fp';
 import React from 'react';
 
 import { OnFilterChange } from '../../../events';
-import { ColumnHeader } from '../column_header';
+import { ColumnHeaderOptions } from '../../../../../store/timeline/model';
 import { TextFilter } from '../text_filter';
 
 interface Props {
-  header: ColumnHeader;
+  header: ColumnHeaderOptions;
   onFilterChange?: OnFilterChange;
 }
 
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header/header_content.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header/header_content.tsx
index c38ae26050c93..84781e6a24300 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header/header_content.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header/header_content.tsx
@@ -8,18 +8,18 @@ import { EuiToolTip } from '@elastic/eui';
 import { noop } from 'lodash/fp';
 import React from 'react';
 
+import { ColumnHeaderOptions } from '../../../../../store/timeline/model';
 import { TruncatableText } from '../../../../truncatable_text';
 import { EventsHeading, EventsHeadingTitleButton, EventsHeadingTitleSpan } from '../../../styles';
 import { useTimelineContext } from '../../../timeline_context';
 import { Sort } from '../../sort';
 import { SortIndicator } from '../../sort/sort_indicator';
-import { ColumnHeader } from '../column_header';
 import { HeaderToolTipContent } from '../header_tooltip_content';
 import { getSortDirection } from './helpers';
 
 interface HeaderContentProps {
   children: React.ReactNode;
-  header: ColumnHeader;
+  header: ColumnHeaderOptions;
   isResizing: boolean;
   onClick: () => void;
   sort: Sort;
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header/helpers.ts b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header/helpers.ts
index 93cb9de7c8355..47ce21e4c9637 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header/helpers.ts
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header/helpers.ts
@@ -6,11 +6,11 @@
 
 import { Direction } from '../../../../../graphql/types';
 import { assertUnreachable } from '../../../../../lib/helpers';
+import { ColumnHeaderOptions } from '../../../../../store/timeline/model';
 import { Sort, SortDirection } from '../../sort';
-import { ColumnHeader } from '../column_header';
 
 interface GetNewSortDirectionOnClickParams {
-  clickedHeader: ColumnHeader;
+  clickedHeader: ColumnHeaderOptions;
   currentSort: Sort;
 }
 
@@ -36,7 +36,7 @@ export const getNextSortDirection = (currentSort: Sort): Direction => {
 };
 
 interface GetSortDirectionParams {
-  header: ColumnHeader;
+  header: ColumnHeaderOptions;
   sort: Sort;
 }
 
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header/index.test.tsx
index fab2e7ee872bf..80ae2aab0a19c 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header/index.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header/index.test.tsx
@@ -9,9 +9,9 @@ import React from 'react';
 
 import { Direction } from '../../../../../graphql/types';
 import { TestProviders } from '../../../../../mock';
+import { ColumnHeaderType } from '../../../../../store/timeline/model';
 import { Sort } from '../../sort';
 import { CloseButton } from '../actions';
-import { ColumnHeaderType } from '../column_header';
 import { defaultHeaders } from '../default_headers';
 
 import { HeaderComponent } from '.';
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header/index.tsx
index c45b9ce425deb..82c5d7eb73f02 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header/index.tsx
@@ -7,16 +7,16 @@
 import { noop } from 'lodash/fp';
 import React, { useCallback } from 'react';
 
+import { ColumnHeaderOptions } from '../../../../../store/timeline/model';
 import { OnColumnRemoved, OnColumnSorted, OnFilterChange } from '../../../events';
 import { Sort } from '../../sort';
 import { Actions } from '../actions';
-import { ColumnHeader } from '../column_header';
 import { Filter } from '../filter';
 import { getNewSortDirectionOnClick } from './helpers';
 import { HeaderContent } from './header_content';
 
 interface Props {
-  header: ColumnHeader;
+  header: ColumnHeaderOptions;
   onColumnRemoved: OnColumnRemoved;
   onColumnSorted: OnColumnSorted;
   onFilterChange?: OnFilterChange;
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header_tooltip_content/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header_tooltip_content/index.test.tsx
index 20c139ae1d050..9afc852373bc6 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header_tooltip_content/index.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header_tooltip_content/index.test.tsx
@@ -8,13 +8,13 @@ import { mount, shallow } from 'enzyme';
 import { cloneDeep } from 'lodash/fp';
 import React from 'react';
 
+import { ColumnHeaderOptions } from '../../../../../store/timeline/model';
 import { defaultHeaders } from '../../../../../mock';
-import { ColumnHeader } from '../column_header';
 
 import { HeaderToolTipContent } from '.';
 
 describe('HeaderToolTipContent', () => {
-  let header: ColumnHeader;
+  let header: ColumnHeaderOptions;
   beforeEach(() => {
     header = cloneDeep(defaultHeaders[0]);
   });
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header_tooltip_content/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header_tooltip_content/index.tsx
index 5deb2c3e66376..bef4bcc42b0c7 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header_tooltip_content/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/header_tooltip_content/index.tsx
@@ -9,8 +9,8 @@ import { isEmpty } from 'lodash/fp';
 import React from 'react';
 import styled from 'styled-components';
 
+import { ColumnHeaderOptions } from '../../../../../store/timeline/model';
 import { getIconFromType } from '../../../../event_details/helpers';
-import { ColumnHeader } from '../column_header';
 import * as i18n from '../translations';
 
 const IconType = styled(EuiIcon)`
@@ -35,7 +35,7 @@ const ToolTipTableValue = styled.span`
 `;
 ToolTipTableValue.displayName = 'ToolTipTableValue';
 
-export const HeaderToolTipContent = React.memo<{ header: ColumnHeader }>(({ header }) => (
+export const HeaderToolTipContent = React.memo<{ header: ColumnHeaderOptions }>(({ header }) => (
   <>
     {!isEmpty(header.category) && (
       <P>
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/helpers.test.ts b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/helpers.test.ts
new file mode 100644
index 0000000000000..d66b75538ef6f
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/helpers.test.ts
@@ -0,0 +1,48 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { getActionsColumnWidth, getColumnWidthFromType } from './helpers';
+import {
+  DEFAULT_COLUMN_MIN_WIDTH,
+  DEFAULT_DATE_COLUMN_MIN_WIDTH,
+  DEFAULT_ACTIONS_COLUMN_WIDTH,
+  SHOW_CHECK_BOXES_COLUMN_WIDTH,
+  EVENTS_VIEWER_ACTIONS_COLUMN_WIDTH,
+} from '../constants';
+
+describe('helpers', () => {
+  describe('getColumnWidthFromType', () => {
+    test('it returns the expected width for a non-date column', () => {
+      expect(getColumnWidthFromType('keyword')).toEqual(DEFAULT_COLUMN_MIN_WIDTH);
+    });
+
+    test('it returns the expected width for a date column', () => {
+      expect(getColumnWidthFromType('date')).toEqual(DEFAULT_DATE_COLUMN_MIN_WIDTH);
+    });
+  });
+
+  describe('getActionsColumnWidth', () => {
+    test('returns the default actions column width when isEventViewer is false', () => {
+      expect(getActionsColumnWidth(false)).toEqual(DEFAULT_ACTIONS_COLUMN_WIDTH);
+    });
+
+    test('returns the default actions column width + checkbox width when isEventViewer is false and showCheckboxes is true', () => {
+      expect(getActionsColumnWidth(false, true)).toEqual(
+        DEFAULT_ACTIONS_COLUMN_WIDTH + SHOW_CHECK_BOXES_COLUMN_WIDTH
+      );
+    });
+
+    test('returns the events viewer actions column width when isEventViewer is true', () => {
+      expect(getActionsColumnWidth(true)).toEqual(EVENTS_VIEWER_ACTIONS_COLUMN_WIDTH);
+    });
+
+    test('returns the events viewer actions column width + checkbox width when isEventViewer is true and showCheckboxes is true', () => {
+      expect(getActionsColumnWidth(true, true)).toEqual(
+        EVENTS_VIEWER_ACTIONS_COLUMN_WIDTH + SHOW_CHECK_BOXES_COLUMN_WIDTH
+      );
+    });
+  });
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/helpers.ts b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/helpers.ts
new file mode 100644
index 0000000000000..6923831f9ef63
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/helpers.ts
@@ -0,0 +1,48 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { get } from 'lodash/fp';
+
+import { BrowserFields } from '../../../../containers/source';
+import { ColumnHeaderOptions } from '../../../../store/timeline/model';
+import {
+  DEFAULT_COLUMN_MIN_WIDTH,
+  DEFAULT_DATE_COLUMN_MIN_WIDTH,
+  SHOW_CHECK_BOXES_COLUMN_WIDTH,
+  EVENTS_VIEWER_ACTIONS_COLUMN_WIDTH,
+  DEFAULT_ACTIONS_COLUMN_WIDTH,
+} from '../constants';
+
+/** Enriches the column headers with field details from the specified browserFields */
+export const getColumnHeaders = (
+  headers: ColumnHeaderOptions[],
+  browserFields: BrowserFields
+): ColumnHeaderOptions[] => {
+  return headers.map(header => {
+    const splitHeader = header.id.split('.'); // source.geo.city_name -> [source, geo, city_name]
+
+    return {
+      ...header,
+      ...get(
+        [splitHeader.length > 1 ? splitHeader[0] : 'base', 'fields', header.id],
+        browserFields
+      ),
+    };
+  });
+};
+
+export const getColumnWidthFromType = (type: string): number =>
+  type !== 'date' ? DEFAULT_COLUMN_MIN_WIDTH : DEFAULT_DATE_COLUMN_MIN_WIDTH;
+
+/** Returns the (fixed) width of the Actions column */
+export const getActionsColumnWidth = (
+  isEventViewer: boolean,
+  showCheckboxes = false,
+  additionalActionWidth = 0
+): number =>
+  (showCheckboxes ? SHOW_CHECK_BOXES_COLUMN_WIDTH : 0) +
+  (isEventViewer ? EVENTS_VIEWER_ACTIONS_COLUMN_WIDTH : DEFAULT_ACTIONS_COLUMN_WIDTH) +
+  additionalActionWidth;
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/index.test.tsx
index 4b97dd7573a45..4fafacfd01633 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/index.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/index.test.tsx
@@ -7,7 +7,7 @@
 import { shallow } from 'enzyme';
 import React from 'react';
 
-import { DEFAULT_ACTIONS_COLUMN_WIDTH } from '../helpers';
+import { DEFAULT_ACTIONS_COLUMN_WIDTH } from '../constants';
 import { defaultHeaders } from './default_headers';
 import { Direction } from '../../../../graphql/types';
 import { mockBrowserFields } from '../../../../../public/containers/source/mock';
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/index.tsx
index 953ffb4d4932b..ab8dc629dd577 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/index.tsx
@@ -10,6 +10,7 @@ import React from 'react';
 import { Droppable } from 'react-beautiful-dnd';
 
 import { BrowserFields } from '../../../../containers/source';
+import { ColumnHeaderOptions } from '../../../../store/timeline/model';
 import { DRAG_TYPE_FIELD, droppableTimelineColumnsPrefix } from '../../../drag_and_drop/helpers';
 import { StatefulFieldsBrowser } from '../../../fields_browser';
 import { FIELD_BROWSER_HEIGHT, FIELD_BROWSER_WIDTH } from '../../../fields_browser/helpers';
@@ -30,13 +31,13 @@ import {
   EventsTrHeader,
 } from '../../styles';
 import { Sort } from '../sort';
-import { ColumnHeader } from './column_header';
 import { EventsSelect } from './events_select';
+import { ColumnHeader } from './column_header';
 
 interface Props {
   actionsColumnWidth: number;
   browserFields: BrowserFields;
-  columnHeaders: ColumnHeader[];
+  columnHeaders: ColumnHeaderOptions[];
   isEventViewer?: boolean;
   isSelectAllChecked: boolean;
   onColumnRemoved: OnColumnRemoved;
@@ -49,7 +50,7 @@ interface Props {
   showSelectAllCheckbox: boolean;
   sort: Sort;
   timelineId: string;
-  toggleColumn: (column: ColumnHeader) => void;
+  toggleColumn: (column: ColumnHeaderOptions) => void;
 }
 
 /** Renders the timeline header columns */
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/constants.ts b/x-pack/legacy/plugins/siem/public/components/timeline/body/constants.ts
new file mode 100644
index 0000000000000..2aeb033c50d6f
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/constants.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.
+ */
+
+/** The (fixed) width of the Actions column */
+export const DEFAULT_ACTIONS_COLUMN_WIDTH = 115; // px;
+/**
+ * The (fixed) width of the Actions column when the timeline body is used as
+ * an events viewer, which has fewer actions than a regular events viewer
+ */
+export const EVENTS_VIEWER_ACTIONS_COLUMN_WIDTH = 32; // px;
+/** Additional column width to include when checkboxes are shown **/
+export const SHOW_CHECK_BOXES_COLUMN_WIDTH = 32; // px;
+/** The default minimum width of a column (when a width for the column type is not specified) */
+export const DEFAULT_COLUMN_MIN_WIDTH = 180; // px
+/** The default minimum width of a column of type `date` */
+export const DEFAULT_DATE_COLUMN_MIN_WIDTH = 190; // px
+
+export const DEFAULT_TIMELINE_WIDTH = 1100; // px
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/data_driven_columns/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/data_driven_columns/index.tsx
index 416c72cfbc255..af32c4632b019 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/data_driven_columns/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/data_driven_columns/index.tsx
@@ -5,18 +5,18 @@
  */
 
 import React from 'react';
-
 import { getOr } from 'lodash/fp';
+
 import { Ecs, TimelineNonEcsData } from '../../../../graphql/types';
+import { ColumnHeaderOptions } from '../../../../store/timeline/model';
 import { OnColumnResized } from '../../events';
 import { EventsTd, EventsTdContent, EventsTdGroupData } from '../../styles';
-import { ColumnHeader } from '../column_headers/column_header';
 import { ColumnRenderer } from '../renderers/column_renderer';
 import { getColumnRenderer } from '../renderers/get_column_renderer';
 
 interface Props {
   _id: string;
-  columnHeaders: ColumnHeader[];
+  columnHeaders: ColumnHeaderOptions[];
   columnRenderers: ColumnRenderer[];
   data: TimelineNonEcsData[];
   ecsData: Ecs;
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/events/event_column_view.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/events/event_column_view.tsx
index 74476c3dc1d61..daf9c3d8b1f96 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/events/event_column_view.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/events/event_column_view.tsx
@@ -9,11 +9,11 @@ import uuid from 'uuid';
 
 import { TimelineNonEcsData, Ecs } from '../../../../graphql/types';
 import { Note } from '../../../../lib/note';
+import { ColumnHeaderOptions } from '../../../../store/timeline/model';
 import { AssociateNote, UpdateNote } from '../../../notes/helpers';
 import { OnColumnResized, OnPinEvent, OnRowSelected, OnUnPinEvent } from '../../events';
 import { EventsTdContent, EventsTrData } from '../../styles';
 import { Actions } from '../actions';
-import { ColumnHeader } from '../column_headers/column_header';
 import { DataDrivenColumns } from '../data_driven_columns';
 import { eventHasNotes, getPinOnClick } from '../helpers';
 import { ColumnRenderer } from '../renderers/column_renderer';
@@ -23,7 +23,7 @@ interface Props {
   id: string;
   actionsColumnWidth: number;
   associateNote: AssociateNote;
-  columnHeaders: ColumnHeader[];
+  columnHeaders: ColumnHeaderOptions[];
   columnRenderers: ColumnRenderer[];
   data: TimelineNonEcsData[];
   ecsData: Ecs;
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/events/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/events/index.tsx
index 6b2e105ad0566..84c4253076dc9 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/events/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/events/index.tsx
@@ -8,6 +8,7 @@ import React from 'react';
 
 import { BrowserFields } from '../../../../containers/source';
 import { TimelineItem, TimelineNonEcsData } from '../../../../graphql/types';
+import { ColumnHeaderOptions } from '../../../../store/timeline/model';
 import { maxDelay } from '../../../../lib/helpers/scheduler';
 import { Note } from '../../../../lib/note';
 import { AddNoteToEvent, UpdateNote } from '../../../notes/helpers';
@@ -19,7 +20,6 @@ import {
   OnUpdateColumns,
 } from '../../events';
 import { EventsTbody } from '../../styles';
-import { ColumnHeader } from '../column_headers/column_header';
 import { ColumnRenderer } from '../renderers/column_renderer';
 import { RowRenderer } from '../renderers/row_renderer';
 import { StatefulEvent } from './stateful_event';
@@ -29,7 +29,7 @@ interface Props {
   actionsColumnWidth: number;
   addNoteToEvent: AddNoteToEvent;
   browserFields: BrowserFields;
-  columnHeaders: ColumnHeader[];
+  columnHeaders: ColumnHeaderOptions[];
   columnRenderers: ColumnRenderer[];
   containerElementRef: HTMLDivElement;
   data: TimelineItem[];
@@ -47,7 +47,7 @@ interface Props {
   rowRenderers: RowRenderer[];
   selectedEventIds: Readonly<Record<string, TimelineNonEcsData[]>>;
   showCheckboxes: boolean;
-  toggleColumn: (column: ColumnHeader) => void;
+  toggleColumn: (column: ColumnHeaderOptions) => void;
   updateNote: UpdateNote;
 }
 
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/events/stateful_event.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/events/stateful_event.tsx
index 5704b6030e7d1..1f09ae4337c42 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/events/stateful_event.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/events/stateful_event.tsx
@@ -13,6 +13,7 @@ import { TimelineDetailsQuery } from '../../../../containers/timeline/details';
 import { TimelineItem, DetailItem, TimelineNonEcsData } from '../../../../graphql/types';
 import { requestIdleCallbackViaScheduler } from '../../../../lib/helpers/scheduler';
 import { Note } from '../../../../lib/note';
+import { ColumnHeaderOptions } from '../../../../store/timeline/model';
 import { AddNoteToEvent, UpdateNote } from '../../../notes/helpers';
 import { SkeletonRow } from '../../../skeleton_row';
 import {
@@ -26,7 +27,6 @@ import { ExpandableEvent } from '../../expandable_event';
 import { STATEFUL_EVENT_CSS_CLASS_NAME } from '../../helpers';
 import { EventsTrGroup, EventsTrSupplement, OFFSET_SCROLLBAR } from '../../styles';
 import { useTimelineWidthContext } from '../../timeline_context';
-import { ColumnHeader } from '../column_headers/column_header';
 import { ColumnRenderer } from '../renderers/column_renderer';
 import { getRowRenderer } from '../renderers/get_row_renderer';
 import { RowRenderer } from '../renderers/row_renderer';
@@ -38,7 +38,7 @@ interface Props {
   containerElementRef: HTMLDivElement;
   addNoteToEvent: AddNoteToEvent;
   browserFields: BrowserFields;
-  columnHeaders: ColumnHeader[];
+  columnHeaders: ColumnHeaderOptions[];
   columnRenderers: ColumnRenderer[];
   event: TimelineItem;
   eventIdToNoteIds: Readonly<Record<string, string[]>>;
@@ -56,7 +56,7 @@ interface Props {
   selectedEventIds: Readonly<Record<string, TimelineNonEcsData[]>>;
   showCheckboxes: boolean;
   timelineId: string;
-  toggleColumn: (column: ColumnHeader) => void;
+  toggleColumn: (column: ColumnHeaderOptions) => void;
   updateNote: UpdateNote;
 }
 
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/events/stateful_event_child.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/events/stateful_event_child.tsx
index 16f89ca916d81..04f4ddf2a6eab 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/events/stateful_event_child.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/events/stateful_event_child.tsx
@@ -9,12 +9,12 @@ import uuid from 'uuid';
 
 import { TimelineNonEcsData, Ecs } from '../../../../graphql/types';
 import { Note } from '../../../../lib/note';
+import { ColumnHeaderOptions } from '../../../../store/timeline/model';
 import { AddNoteToEvent, UpdateNote } from '../../../notes/helpers';
 import { NoteCards } from '../../../notes/note_cards';
 import { OnPinEvent, OnColumnResized, OnUnPinEvent, OnRowSelected } from '../../events';
 import { EventsTrSupplement, OFFSET_SCROLLBAR } from '../../styles';
 import { useTimelineWidthContext } from '../../timeline_context';
-import { ColumnHeader } from '../column_headers/column_header';
 import { ColumnRenderer } from '../renderers/column_renderer';
 import { EventColumnView } from './event_column_view';
 
@@ -23,7 +23,7 @@ interface Props {
   actionsColumnWidth: number;
   addNoteToEvent: AddNoteToEvent;
   onPinEvent: OnPinEvent;
-  columnHeaders: ColumnHeader[];
+  columnHeaders: ColumnHeaderOptions[];
   columnRenderers: ColumnRenderer[];
   data: TimelineNonEcsData[];
   ecsData: Ecs;
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/helpers.test.ts b/x-pack/legacy/plugins/siem/public/components/timeline/body/helpers.test.ts
index 602b88b7ae6d6..f021bf38b56c2 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/helpers.test.ts
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/helpers.test.ts
@@ -6,19 +6,7 @@
 
 import { Ecs } from '../../../graphql/types';
 
-import {
-  DEFAULT_ACTIONS_COLUMN_WIDTH,
-  DEFAULT_COLUMN_MIN_WIDTH,
-  DEFAULT_DATE_COLUMN_MIN_WIDTH,
-  EVENTS_VIEWER_ACTIONS_COLUMN_WIDTH,
-  eventHasNotes,
-  eventIsPinned,
-  getActionsColumnWidth,
-  getColumnWidthFromType,
-  getPinTooltip,
-  stringifyEvent,
-  SHOW_CHECK_BOXES_COLUMN_WIDTH,
-} from './helpers';
+import { eventHasNotes, eventIsPinned, getPinTooltip, stringifyEvent } from './helpers';
 
 describe('helpers', () => {
   describe('stringifyEvent', () => {
@@ -237,36 +225,4 @@ describe('helpers', () => {
       expect(eventIsPinned({ eventId, pinnedEventIds })).toEqual(false);
     });
   });
-
-  describe('getColumnWidthFromType', () => {
-    test('it returns the expected width for a non-date column', () => {
-      expect(getColumnWidthFromType('keyword')).toEqual(DEFAULT_COLUMN_MIN_WIDTH);
-    });
-
-    test('it returns the expected width for a date column', () => {
-      expect(getColumnWidthFromType('date')).toEqual(DEFAULT_DATE_COLUMN_MIN_WIDTH);
-    });
-  });
-
-  describe('getActionsColumnWidth', () => {
-    test('returns the default actions column width when isEventViewer is false', () => {
-      expect(getActionsColumnWidth(false)).toEqual(DEFAULT_ACTIONS_COLUMN_WIDTH);
-    });
-
-    test('returns the default actions column width + checkbox width when isEventViewer is false and showCheckboxes is true', () => {
-      expect(getActionsColumnWidth(false, true)).toEqual(
-        DEFAULT_ACTIONS_COLUMN_WIDTH + SHOW_CHECK_BOXES_COLUMN_WIDTH
-      );
-    });
-
-    test('returns the events viewer actions column width when isEventViewer is true', () => {
-      expect(getActionsColumnWidth(true)).toEqual(EVENTS_VIEWER_ACTIONS_COLUMN_WIDTH);
-    });
-
-    test('returns the events viewer actions column width + checkbox width when isEventViewer is true and showCheckboxes is true', () => {
-      expect(getActionsColumnWidth(true, true)).toEqual(
-        EVENTS_VIEWER_ACTIONS_COLUMN_WIDTH + SHOW_CHECK_BOXES_COLUMN_WIDTH
-      );
-    });
-  });
 });
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/helpers.ts b/x-pack/legacy/plugins/siem/public/components/timeline/body/helpers.ts
index 4b1d72a3af7b0..3d1d165ef4fa6 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/helpers.ts
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/helpers.ts
@@ -3,30 +3,13 @@
  * or more contributor license agreements. Licensed under the Elastic License;
  * you may not use this file except in compliance with the Elastic License.
  */
-import { get, isEmpty, noop } from 'lodash/fp';
+import { isEmpty, noop } from 'lodash/fp';
 
-import { BrowserFields } from '../../../containers/source';
 import { Ecs, TimelineItem, TimelineNonEcsData } from '../../../graphql/types';
 import { EventType } from '../../../store/timeline/model';
 import { OnPinEvent, OnUnPinEvent } from '../events';
-import { ColumnHeader } from './column_headers/column_header';
-import * as i18n from './translations';
-
-/** The (fixed) width of the Actions column */
-export const DEFAULT_ACTIONS_COLUMN_WIDTH = 115; // px;
-/**
- * The (fixed) width of the Actions column when the timeline body is used as
- * an events viewer, which has fewer actions than a regular events viewer
- */
-export const EVENTS_VIEWER_ACTIONS_COLUMN_WIDTH = 32; // px;
-/** Additional column width to include when checkboxes are shown **/
-export const SHOW_CHECK_BOXES_COLUMN_WIDTH = 32; // px;
-/** The default minimum width of a column (when a width for the column type is not specified) */
-export const DEFAULT_COLUMN_MIN_WIDTH = 180; // px
-/** The default minimum width of a column of type `date` */
-export const DEFAULT_DATE_COLUMN_MIN_WIDTH = 190; // px
 
-export const DEFAULT_TIMELINE_WIDTH = 1100; // px
+import * as i18n from './translations';
 
 // eslint-disable-next-line @typescript-eslint/no-explicit-any
 export const omitTypenameAndEmpty = (k: string, v: any): any | undefined =>
@@ -74,37 +57,6 @@ export const getPinOnClick = ({
   return isEventPinned ? () => onUnPinEvent(eventId) : () => onPinEvent(eventId);
 };
 
-export const getColumnWidthFromType = (type: string): number =>
-  type !== 'date' ? DEFAULT_COLUMN_MIN_WIDTH : DEFAULT_DATE_COLUMN_MIN_WIDTH;
-
-/** Enriches the column headers with field details from the specified browserFields */
-export const getColumnHeaders = (
-  headers: ColumnHeader[],
-  browserFields: BrowserFields
-): ColumnHeader[] => {
-  return headers.map(header => {
-    const splitHeader = header.id.split('.'); // source.geo.city_name -> [source, geo, city_name]
-
-    return {
-      ...header,
-      ...get(
-        [splitHeader.length > 1 ? splitHeader[0] : 'base', 'fields', header.id],
-        browserFields
-      ),
-    };
-  });
-};
-
-/** Returns the (fixed) width of the Actions column */
-export const getActionsColumnWidth = (
-  isEventViewer: boolean,
-  showCheckboxes = false,
-  additionalActionWidth = 0
-): number =>
-  (showCheckboxes ? SHOW_CHECK_BOXES_COLUMN_WIDTH : 0) +
-  (isEventViewer ? EVENTS_VIEWER_ACTIONS_COLUMN_WIDTH : DEFAULT_ACTIONS_COLUMN_WIDTH) +
-  additionalActionWidth;
-
 /**
  * Creates mapping of eventID -> fieldData for given fieldsToKeep. Used to store additional field
  * data necessary for custom timeline actions in conjunction with selection state
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/index.tsx
index 7f689a877c6b7..ea80d3351408a 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/index.tsx
@@ -9,6 +9,7 @@ import React, { useMemo, useRef } from 'react';
 import { BrowserFields } from '../../../containers/source';
 import { TimelineItem, TimelineNonEcsData } from '../../../graphql/types';
 import { Note } from '../../../lib/note';
+import { ColumnHeaderOptions } from '../../../store/timeline/model';
 import { AddNoteToEvent, UpdateNote } from '../../notes/helpers';
 import {
   OnColumnRemoved,
@@ -23,9 +24,8 @@ import {
 } from '../events';
 import { EventsTable, TimelineBody, TimelineBodyGlobalStyle } from '../styles';
 import { ColumnHeaders } from './column_headers';
-import { ColumnHeader } from './column_headers/column_header';
+import { getActionsColumnWidth } from './column_headers/helpers';
 import { Events } from './events';
-import { getActionsColumnWidth } from './helpers';
 import { ColumnRenderer } from './renderers/column_renderer';
 import { RowRenderer } from './renderers/row_renderer';
 import { Sort } from './sort';
@@ -34,7 +34,7 @@ import { useTimelineTypeContext } from '../timeline_context';
 export interface BodyProps {
   addNoteToEvent: AddNoteToEvent;
   browserFields: BrowserFields;
-  columnHeaders: ColumnHeader[];
+  columnHeaders: ColumnHeaderOptions[];
   columnRenderers: ColumnRenderer[];
   data: TimelineItem[];
   getNotesByIds: (noteIds: string[]) => Note[];
@@ -58,7 +58,7 @@ export interface BodyProps {
   selectedEventIds: Readonly<Record<string, TimelineNonEcsData[]>>;
   showCheckboxes: boolean;
   sort: Sort;
-  toggleColumn: (column: ColumnHeader) => void;
+  toggleColumn: (column: ColumnHeaderOptions) => void;
   updateNote: UpdateNote;
 }
 
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/column_renderer.ts b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/column_renderer.ts
index d1807c82d188e..a13de90e7aed3 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/column_renderer.ts
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/column_renderer.ts
@@ -5,7 +5,7 @@
  */
 
 import { TimelineNonEcsData } from '../../../../graphql/types';
-import { ColumnHeader } from '../column_headers/column_header';
+import { ColumnHeaderOptions } from '../../../../store/timeline/model';
 
 export interface ColumnRenderer {
   isInstance: (columnName: string, data: TimelineNonEcsData[]) => boolean;
@@ -20,7 +20,7 @@ export interface ColumnRenderer {
   }: {
     columnName: string;
     eventId: string;
-    field: ColumnHeader;
+    field: ColumnHeaderOptions;
     timelineId: string;
     truncate?: boolean;
     values: string[] | null | undefined;
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/empty_column_renderer.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/empty_column_renderer.tsx
index 7e2346ced8785..45ef46616718d 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/empty_column_renderer.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/empty_column_renderer.tsx
@@ -9,12 +9,12 @@
 import React from 'react';
 
 import { TimelineNonEcsData } from '../../../../graphql/types';
+import { ColumnHeaderOptions } from '../../../../store/timeline/model';
 import { DraggableWrapper, DragEffects } from '../../../drag_and_drop/draggable_wrapper';
 import { escapeDataProviderId } from '../../../drag_and_drop/helpers';
 import { getEmptyValue } from '../../../empty_value';
 import { EXISTS_OPERATOR } from '../../data_providers/data_provider';
 import { Provider } from '../../data_providers/provider';
-import { ColumnHeader } from '../column_headers/column_header';
 import { ColumnRenderer } from './column_renderer';
 import { parseQueryValue } from './parse_query_value';
 
@@ -33,7 +33,7 @@ export const emptyColumnRenderer: ColumnRenderer = {
   }: {
     columnName: string;
     eventId: string;
-    field: ColumnHeader;
+    field: ColumnHeaderOptions;
     timelineId: string;
     truncate?: boolean;
   }) => (
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/plain_column_renderer.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/plain_column_renderer.tsx
index deeec05bc0707..f6a61889c501b 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/plain_column_renderer.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/plain_column_renderer.tsx
@@ -8,8 +8,8 @@ import { head } from 'lodash/fp';
 import React from 'react';
 
 import { TimelineNonEcsData } from '../../../../graphql/types';
+import { ColumnHeaderOptions } from '../../../../store/timeline/model';
 import { getEmptyTagValue } from '../../../empty_value';
-import { ColumnHeader } from '../column_headers/column_header';
 import { ColumnRenderer } from './column_renderer';
 import { FormattedFieldValue } from './formatted_field';
 import { parseValue } from './parse_value';
@@ -32,7 +32,7 @@ export const plainColumnRenderer: ColumnRenderer = {
   }: {
     columnName: string;
     eventId: string;
-    field: ColumnHeader;
+    field: ColumnHeaderOptions;
     timelineId: string;
     truncate?: boolean;
     values: string[] | undefined | null;
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/stateful_body.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/stateful_body.test.tsx
index ce59ae8d45048..4945939ac2bdc 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/stateful_body.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/stateful_body.test.tsx
@@ -7,7 +7,7 @@
 import { mockBrowserFields } from '../../../containers/source/mock';
 
 import { defaultHeaders } from './column_headers/default_headers';
-import { getColumnHeaders } from './helpers';
+import { getColumnHeaders } from './column_headers/helpers';
 
 describe('stateful_body', () => {
   describe('getColumnHeaders', () => {
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/stateful_body.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/stateful_body.tsx
index ffb5f2a206f4d..d06dcbb84ad78 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/body/stateful_body.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/stateful_body.tsx
@@ -13,6 +13,9 @@ import { BrowserFields } from '../../../containers/source';
 import { TimelineItem } from '../../../graphql/types';
 import { Note } from '../../../lib/note';
 import { appSelectors, State, timelineSelectors } from '../../../store';
+import { timelineActions, appActions } from '../../../store/actions';
+import { ColumnHeaderOptions, TimelineModel } from '../../../store/timeline/model';
+import { timelineDefaults } from '../../../store/timeline/defaults';
 import { AddNoteToEvent, UpdateNote } from '../../notes/helpers';
 import {
   OnColumnRemoved,
@@ -24,16 +27,13 @@ import {
   OnUnPinEvent,
   OnUpdateColumns,
 } from '../events';
-
-import { ColumnHeader } from './column_headers/column_header';
-import { getColumnHeaders, getEventIdToDataMapping } from './helpers';
+import { useTimelineTypeContext } from '../timeline_context';
+import { getColumnHeaders } from './column_headers/helpers';
+import { getEventIdToDataMapping } from './helpers';
 import { Body } from './index';
 import { columnRenderers, rowRenderers } from './renderers';
 import { Sort } from './sort';
-import { timelineActions, appActions } from '../../../store/actions';
-import { timelineDefaults, TimelineModel } from '../../../store/timeline/model';
 import { plainRowRenderer } from './renderers/plain_row_renderer';
-import { useTimelineTypeContext } from '../timeline_context';
 
 interface OwnProps {
   browserFields: BrowserFields;
@@ -42,12 +42,12 @@ interface OwnProps {
   isEventViewer?: boolean;
   height: number;
   sort: Sort;
-  toggleColumn: (column: ColumnHeader) => void;
+  toggleColumn: (column: ColumnHeaderOptions) => void;
 }
 
 type StatefulBodyComponentProps = OwnProps & PropsFromRedux;
 
-export const emptyColumnHeaders: ColumnHeader[] = [];
+export const emptyColumnHeaders: ColumnHeaderOptions[] = [];
 
 const StatefulBodyComponent = React.memo<StatefulBodyComponentProps>(
   ({
@@ -214,9 +214,9 @@ StatefulBodyComponent.displayName = 'StatefulBodyComponent';
 
 const makeMapStateToProps = () => {
   const memoizedColumnHeaders: (
-    headers: ColumnHeader[],
+    headers: ColumnHeaderOptions[],
     browserFields: BrowserFields
-  ) => ColumnHeader[] = memoizeOne(getColumnHeaders);
+  ) => ColumnHeaderOptions[] = memoizeOne(getColumnHeaders);
 
   const getTimeline = timelineSelectors.getTimelineByIdSelector();
   const getNotesByIds = appSelectors.notesByIdsSelector();
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/events.ts b/x-pack/legacy/plugins/siem/public/components/timeline/events.ts
index b54ed52bb9f18..f977c03ed3053 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/events.ts
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/events.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { ColumnHeader } from './body/column_headers/column_header';
+import { ColumnHeaderOptions } from '../../store/timeline/model';
 import { ColumnId } from './body/column_id';
 import { SortDirection } from './body/sort';
 import { QueryOperator } from './data_providers/data_provider';
@@ -85,7 +85,7 @@ export type OnRowSelected = ({
 export type OnSelectAll = ({ isSelected }: { isSelected: boolean }) => void;
 
 /** Invoked when columns are updated */
-export type OnUpdateColumns = (columns: ColumnHeader[]) => void;
+export type OnUpdateColumns = (columns: ColumnHeaderOptions[]) => void;
 
 /** Invoked when a user unpins an event */
 export type OnUnPinEvent = (eventId: string) => void;
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/expandable_event/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/expandable_event/index.tsx
index 1c5df9d220a62..77cf50342b78b 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/expandable_event/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/expandable_event/index.tsx
@@ -8,7 +8,7 @@ import React from 'react';
 import styled from 'styled-components';
 
 import { BrowserFields } from '../../../containers/source';
-import { ColumnHeader } from '../body/column_headers/column_header';
+import { ColumnHeaderOptions } from '../../../store/timeline/model';
 import { DetailItem } from '../../../graphql/types';
 import { StatefulEventDetails } from '../../event_details/stateful_event_details';
 import { LazyAccordion } from '../../lazy_accordion';
@@ -30,14 +30,14 @@ ExpandableDetails.displayName = 'ExpandableDetails';
 
 interface Props {
   browserFields: BrowserFields;
-  columnHeaders: ColumnHeader[];
+  columnHeaders: ColumnHeaderOptions[];
   id: string;
   event: DetailItem[];
   forceExpand?: boolean;
   hideExpandButton?: boolean;
   onUpdateColumns: OnUpdateColumns;
   timelineId: string;
-  toggleColumn: (column: ColumnHeader) => void;
+  toggleColumn: (column: ColumnHeaderOptions) => void;
 }
 
 export const ExpandableEvent = React.memo<Props>(
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/index.tsx
index 34f760e411ed4..d782d0366f041 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/index.tsx
@@ -9,12 +9,11 @@ import React, { useEffect, useCallback, useMemo } from 'react';
 import { connect, ConnectedProps } from 'react-redux';
 
 import { WithSource } from '../../containers/source';
+import { useSignalIndex } from '../../containers/detection_engine/signals/use_signal_index';
 import { inputsModel, inputsSelectors, State, timelineSelectors } from '../../store';
 import { timelineActions } from '../../store/actions';
-import { timelineDefaults, TimelineModel } from '../../store/timeline/model';
-import { useSignalIndex } from '../../containers/detection_engine/signals/use_signal_index';
-
-import { ColumnHeader } from './body/column_headers/column_header';
+import { ColumnHeaderOptions, TimelineModel } from '../../store/timeline/model';
+import { timelineDefaults } from '../../store/timeline/defaults';
 import { defaultHeaders } from './body/column_headers/default_headers';
 import {
   OnChangeDataProviderKqlQuery,
@@ -137,7 +136,7 @@ const StatefulTimelineComponent = React.memo<Props>(
     );
 
     const toggleColumn = useCallback(
-      (column: ColumnHeader) => {
+      (column: ColumnHeaderOptions) => {
         const exists = columns.findIndex(c => c.id === column.id) !== -1;
 
         if (!exists && upsertColumn != null) {
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/search_or_filter/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/search_or_filter/index.tsx
index 2b139d3948fe1..d1904fd5d9aac 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/search_or_filter/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/search_or_filter/index.tsx
@@ -21,7 +21,8 @@ import {
   inputsSelectors,
 } from '../../../store';
 import { timelineActions } from '../../../store/actions';
-import { KqlMode, timelineDefaults, TimelineModel, EventType } from '../../../store/timeline/model';
+import { KqlMode, TimelineModel, EventType } from '../../../store/timeline/model';
+import { timelineDefaults } from '../../../store/timeline/defaults';
 import { dispatchUpdateReduxTime } from '../../super_date_picker';
 import { SearchOrFilter } from './search_or_filter';
 
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx
index 4b7331ab14c7e..e6aa6cc18f018 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx
@@ -14,8 +14,7 @@ import { BrowserFields } from '../../containers/source';
 import { TimelineQuery } from '../../containers/timeline';
 import { Direction } from '../../graphql/types';
 import { useKibana } from '../../lib/kibana';
-import { KqlMode, EventType } from '../../store/timeline/model';
-import { ColumnHeader } from './body/column_headers/column_header';
+import { ColumnHeaderOptions, KqlMode, EventType } from '../../store/timeline/model';
 import { defaultHeaders } from './body/column_headers/default_headers';
 import { Sort } from './body/sort';
 import { StatefulBody } from './body/stateful_body';
@@ -57,7 +56,7 @@ export const isCompactFooter = (width: number): boolean => width < 600;
 
 interface Props {
   browserFields: BrowserFields;
-  columns: ColumnHeader[];
+  columns: ColumnHeaderOptions[];
   dataProviders: DataProvider[];
   end: number;
   eventType?: EventType;
@@ -84,7 +83,7 @@ interface Props {
   showCallOutUnauthorizedMsg: boolean;
   start: number;
   sort: Sort;
-  toggleColumn: (column: ColumnHeader) => void;
+  toggleColumn: (column: ColumnHeaderOptions) => void;
 }
 
 /** The parent Timeline component */
diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts
index 05329621aa97a..d085af91da1f0 100644
--- a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts
+++ b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts
@@ -4,15 +4,18 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+import { isEmpty } from 'lodash/fp';
 import { parse, stringify } from 'query-string';
 import { decode, encode } from 'rison-node';
 import * as H from 'history';
-import { Query, Filter } from 'src/plugins/data/public';
 
-import { isEmpty } from 'lodash/fp';
+import { Query, Filter } from '../../../../../../../src/plugins/data/public';
+import { url } from '../../../../../../../src/plugins/kibana_utils/public';
+
 import { SiemPageName } from '../../pages/home/types';
 import { inputsSelectors, State, timelineSelectors } from '../../store';
 import { UrlInputsModel } from '../../store/inputs/model';
+import { TimelineUrl } from '../../store/timeline/model';
 import { formatDate } from '../super_date_picker';
 import { NavTab } from '../navigation/types';
 import { CONSTANTS, UrlStateType } from './constants';
@@ -20,12 +23,9 @@ import {
   LocationTypes,
   UrlStateContainerPropTypes,
   ReplaceStateInLocation,
-  Timeline,
   UpdateUrlStateString,
 } from './types';
 
-import { url } from '../../../../../../../src/plugins/kibana_utils/public';
-
 export const decodeRisonUrlState = <T>(value: string | undefined): T | null => {
   try {
     return value ? ((decode(value) as unknown) as T) : null;
@@ -257,7 +257,7 @@ export const updateUrlStateString = ({
       });
     }
   } else if (urlKey === CONSTANTS.timeline) {
-    const queryState = decodeRisonUrlState<Timeline>(newUrlStateString);
+    const queryState = decodeRisonUrlState<TimelineUrl>(newUrlStateString);
     if (queryState != null && queryState.id === '') {
       return replaceStateInLocation({
         history,
diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/initialize_redux_by_url.tsx b/x-pack/legacy/plugins/siem/public/components/url_state/initialize_redux_by_url.tsx
index 772afac6f8ba4..4838fb4499b87 100644
--- a/x-pack/legacy/plugins/siem/public/components/url_state/initialize_redux_by_url.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/url_state/initialize_redux_by_url.tsx
@@ -6,8 +6,8 @@
 
 import { get, isEmpty } from 'lodash/fp';
 import { Dispatch } from 'redux';
-import { Query, Filter } from 'src/plugins/data/public';
 
+import { Query, Filter } from '../../../../../../../src/plugins/data/public';
 import { inputsActions } from '../../store/actions';
 import { InputsModelId, TimeRangeKinds } from '../../store/inputs/constants';
 import {
@@ -16,11 +16,11 @@ import {
   AbsoluteTimeRange,
   RelativeTimeRange,
 } from '../../store/inputs/model';
-
+import { TimelineUrl } from '../../store/timeline/model';
 import { CONSTANTS } from './constants';
 import { decodeRisonUrlState } from './helpers';
 import { normalizeTimeRange } from './normalize_time_range';
-import { DispatchSetInitialStateFromUrl, SetInitialStateFromUrl, Timeline } from './types';
+import { DispatchSetInitialStateFromUrl, SetInitialStateFromUrl } from './types';
 import { queryTimelineById } from '../open_timeline/helpers';
 
 export const dispatchSetInitialStateFromUrl = (
@@ -76,7 +76,7 @@ export const dispatchSetInitialStateFromUrl = (
     }
 
     if (urlKey === CONSTANTS.timeline) {
-      const timeline = decodeRisonUrlState<Timeline>(newUrlStateString);
+      const timeline = decodeRisonUrlState<TimelineUrl>(newUrlStateString);
       if (timeline != null && timeline.id !== '') {
         queryTimelineById({
           apolloClient,
diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/types.ts b/x-pack/legacy/plugins/siem/public/components/url_state/types.ts
index 97979e514aeaf..2cb1b0c96ad79 100644
--- a/x-pack/legacy/plugins/siem/public/components/url_state/types.ts
+++ b/x-pack/legacy/plugins/siem/public/components/url_state/types.ts
@@ -16,6 +16,7 @@ import {
 } from 'src/plugins/data/public';
 
 import { UrlInputsModel } from '../../store/inputs/model';
+import { TimelineUrl } from '../../store/timeline/model';
 import { RouteSpyState } from '../../utils/route/types';
 import { DispatchUpdateTimeline } from '../open_timeline/types';
 import { NavTab } from '../navigation/types';
@@ -75,17 +76,12 @@ export type LocationTypes =
   | CONSTANTS.timelinePage
   | CONSTANTS.unknown;
 
-export interface Timeline {
-  id: string;
-  isOpen: boolean;
-}
-
 export interface UrlState {
   [CONSTANTS.appQuery]?: Query;
   [CONSTANTS.filters]?: Filter[];
   [CONSTANTS.savedQuery]?: string;
   [CONSTANTS.timerange]: UrlInputsModel;
-  [CONSTANTS.timeline]: Timeline;
+  [CONSTANTS.timeline]: TimelineUrl;
 }
 export type KeyUrlState = keyof UrlState;
 
diff --git a/x-pack/legacy/plugins/siem/public/mock/global_state.ts b/x-pack/legacy/plugins/siem/public/mock/global_state.ts
index 31e203d080322..6678c3043a3da 100644
--- a/x-pack/legacy/plugins/siem/public/mock/global_state.ts
+++ b/x-pack/legacy/plugins/siem/public/mock/global_state.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { DEFAULT_TIMELINE_WIDTH } from '../components/timeline/body/helpers';
+import { DEFAULT_TIMELINE_WIDTH } from '../components/timeline/body/constants';
 import {
   Direction,
   FlowTarget,
diff --git a/x-pack/legacy/plugins/siem/public/mock/header.ts b/x-pack/legacy/plugins/siem/public/mock/header.ts
index 387e16ebeb3d6..61af5a5f098b5 100644
--- a/x-pack/legacy/plugins/siem/public/mock/header.ts
+++ b/x-pack/legacy/plugins/siem/public/mock/header.ts
@@ -3,15 +3,14 @@
  * or more contributor license agreements. Licensed under the Elastic License;
  * you may not use this file except in compliance with the Elastic License.
  */
-
-import { ColumnHeader } from '../components/timeline/body/column_headers/column_header';
+import { ColumnHeaderOptions } from '../store/timeline/model';
 import { defaultColumnHeaderType } from '../components/timeline/body/column_headers/default_headers';
 import {
   DEFAULT_COLUMN_MIN_WIDTH,
   DEFAULT_DATE_COLUMN_MIN_WIDTH,
-} from '../components/timeline/body/helpers';
+} from '../components/timeline/body/constants';
 
-export const defaultHeaders: ColumnHeader[] = [
+export const defaultHeaders: ColumnHeaderOptions[] = [
   {
     category: 'base',
     columnHeaderType: defaultColumnHeaderType,
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/activity_monitor/columns.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/activity_monitor/columns.tsx
index 2ad37960d0423..78315d3ba79d4 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/activity_monitor/columns.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/activity_monitor/columns.tsx
@@ -15,7 +15,7 @@ import {
 } from '@elastic/eui';
 import React from 'react';
 import { getEmptyTagValue } from '../../../../components/empty_value';
-import { ColumnTypes } from './index';
+import { ColumnTypes } from './types';
 
 const actions: EuiTableActionsColumnType<ColumnTypes>['actions'] = [
   {
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/activity_monitor/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/activity_monitor/index.tsx
index 5f017a3a1f67f..4c7cfac33c546 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/activity_monitor/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/activity_monitor/index.tsx
@@ -15,30 +15,7 @@ import {
   UtilityBarText,
 } from '../../../../components/detection_engine/utility_bar';
 import { columns } from './columns';
-
-export interface RuleTypes {
-  href: string;
-  name: string;
-}
-
-export interface ColumnTypes {
-  id: number;
-  rule: RuleTypes;
-  ran: string;
-  lookedBackTo: string;
-  status: string;
-  response?: string | undefined;
-}
-
-export interface PageTypes {
-  index: number;
-  size: number;
-}
-
-export interface SortTypes {
-  field: keyof ColumnTypes;
-  direction: 'asc' | 'desc';
-}
+import { ColumnTypes, PageTypes, SortTypes } from './types';
 
 export const ActivityMonitor = React.memo(() => {
   const sampleTableData: ColumnTypes[] = [
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/activity_monitor/types.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/activity_monitor/types.ts
new file mode 100644
index 0000000000000..816992ff940dd
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/activity_monitor/types.ts
@@ -0,0 +1,29 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export interface RuleTypes {
+  href: string;
+  name: string;
+}
+
+export interface ColumnTypes {
+  id: number;
+  rule: RuleTypes;
+  ran: string;
+  lookedBackTo: string;
+  status: string;
+  response?: string | undefined;
+}
+
+export interface PageTypes {
+  index: number;
+  size: number;
+}
+
+export interface SortTypes {
+  field: keyof ColumnTypes;
+  direction: 'asc' | 'desc';
+}
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/actions.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/actions.tsx
index 003d2baa53dbc..b23b051e8b2e8 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/actions.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/actions.tsx
@@ -17,7 +17,7 @@ import {
   formatTimelineResultToModel,
 } from '../../../../components/open_timeline/helpers';
 import { convertKueryToElasticSearchQuery } from '../../../../lib/keury';
-import { timelineDefaults } from '../../../../store/timeline/model';
+import { timelineDefaults } from '../../../../store/timeline/defaults';
 import {
   replaceTemplateFieldFromQuery,
   replaceTemplateFieldFromMatchFilters,
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/default_config.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/default_config.tsx
index a0d24f53c6b4e..44c48b1879e89 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/default_config.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/default_config.tsx
@@ -11,14 +11,14 @@ import ApolloClient from 'apollo-client';
 import React from 'react';
 
 import { Filter } from '../../../../../../../../../src/plugins/data/common/es_query';
-import { ColumnHeader } from '../../../../components/timeline/body/column_headers/column_header';
 import { TimelineAction, TimelineActionProps } from '../../../../components/timeline/body/actions';
 import { defaultColumnHeaderType } from '../../../../components/timeline/body/column_headers/default_headers';
 import {
   DEFAULT_COLUMN_MIN_WIDTH,
   DEFAULT_DATE_COLUMN_MIN_WIDTH,
-} from '../../../../components/timeline/body/helpers';
-import { SubsetTimelineModel, timelineDefaults } from '../../../../store/timeline/model';
+} from '../../../../components/timeline/body/constants';
+import { ColumnHeaderOptions, SubsetTimelineModel } from '../../../../store/timeline/model';
+import { timelineDefaults } from '../../../../store/timeline/defaults';
 
 import { FILTER_OPEN } from './signals_filter_group';
 import { sendSignalToTimelineAction, updateSignalStatusAction } from './actions';
@@ -85,7 +85,7 @@ export const buildSignalsRuleIdFilter = (ruleId: string): Filter[] => [
   },
 ];
 
-export const signalsHeaders: ColumnHeader[] = [
+export const signalsHeaders: ColumnHeaderOptions[] = [
   {
     columnHeaderType: defaultColumnHeaderType,
     id: '@timestamp',
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx
index aacc6d951f4c9..7cd26ac0cc41b 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx
@@ -18,7 +18,8 @@ import { combineQueries } from '../../../../components/timeline/helpers';
 import { useKibana } from '../../../../lib/kibana';
 import { inputsSelectors, State, inputsModel } from '../../../../store';
 import { timelineActions, timelineSelectors } from '../../../../store/timeline';
-import { timelineDefaults, TimelineModel } from '../../../../store/timeline/model';
+import { TimelineModel } from '../../../../store/timeline/model';
+import { timelineDefaults } from '../../../../store/timeline/defaults';
 import { useApolloClient } from '../../../../utils/apollo_context';
 
 import { updateSignalStatusAction } from './actions';
diff --git a/x-pack/legacy/plugins/siem/public/store/timeline/actions.ts b/x-pack/legacy/plugins/siem/public/store/timeline/actions.ts
index f05512787f0f7..51043b999c27e 100644
--- a/x-pack/legacy/plugins/siem/public/store/timeline/actions.ts
+++ b/x-pack/legacy/plugins/siem/public/store/timeline/actions.ts
@@ -7,7 +7,6 @@
 import actionCreatorFactory from 'typescript-fsa';
 
 import { Filter } from '../../../../../../../src/plugins/data/public';
-import { ColumnHeader } from '../../components/timeline/body/column_headers/column_header';
 import { Sort } from '../../components/timeline/body/sort';
 import {
   DataProvider,
@@ -15,7 +14,7 @@ import {
 } from '../../components/timeline/data_providers/data_provider';
 import { KueryFilterQuery, SerializedFilterQuery } from '../model';
 
-import { EventType, KqlMode, TimelineModel } from './model';
+import { EventType, KqlMode, TimelineModel, ColumnHeaderOptions } from './model';
 import { TimelineNonEcsData } from '../../graphql/types';
 
 const actionCreator = actionCreatorFactory('x-pack/siem/local/timeline');
@@ -28,9 +27,11 @@ export const addNoteToEvent = actionCreator<{ id: string; noteId: string; eventI
   'ADD_NOTE_TO_EVENT'
 );
 
-export const upsertColumn = actionCreator<{ column: ColumnHeader; id: string; index: number }>(
-  'UPSERT_COLUMN'
-);
+export const upsertColumn = actionCreator<{
+  column: ColumnHeaderOptions;
+  id: string;
+  index: number;
+}>('UPSERT_COLUMN');
 
 export const addProvider = actionCreator<{ id: string; provider: DataProvider }>('ADD_PROVIDER');
 
@@ -56,7 +57,7 @@ export const createTimeline = actionCreator<{
     end: number;
   };
   filters?: Filter[];
-  columns: ColumnHeader[];
+  columns: ColumnHeaderOptions[];
   itemsPerPage?: number;
   kqlQuery?: {
     filterQuery: SerializedFilterQuery | null;
@@ -110,7 +111,7 @@ export const updateIsLoading = actionCreator<{
 
 export const updateColumns = actionCreator<{
   id: string;
-  columns: ColumnHeader[];
+  columns: ColumnHeaderOptions[];
 }>('UPDATE_COLUMNS');
 
 export const updateDataProviderEnabled = actionCreator<{
diff --git a/x-pack/legacy/plugins/siem/public/store/timeline/defaults.ts b/x-pack/legacy/plugins/siem/public/store/timeline/defaults.ts
new file mode 100644
index 0000000000000..bbaf2a3fb6e30
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/store/timeline/defaults.ts
@@ -0,0 +1,54 @@
+/*
+ * 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 { Direction } from '../../graphql/types';
+import { DEFAULT_TIMELINE_WIDTH } from '../../components/timeline/body/constants';
+import { defaultHeaders } from '../../components/timeline/body/column_headers/default_headers';
+import { SubsetTimelineModel, TimelineModel } from './model';
+
+export const timelineDefaults: SubsetTimelineModel & Pick<TimelineModel, 'filters'> = {
+  columns: defaultHeaders,
+  dataProviders: [],
+  deletedEventIds: [],
+  description: '',
+  eventType: 'raw',
+  eventIdToNoteIds: {},
+  highlightedDropAndProviderId: '',
+  historyIds: [],
+  filters: [],
+  isFavorite: false,
+  isLive: false,
+  isSelectAllChecked: false,
+  isLoading: false,
+  isSaving: false,
+  itemsPerPage: 25,
+  itemsPerPageOptions: [10, 25, 50, 100],
+  kqlMode: 'filter',
+  kqlQuery: {
+    filterQuery: null,
+    filterQueryDraft: null,
+  },
+  loadingEventIds: [],
+  title: '',
+  noteIds: [],
+  pinnedEventIds: {},
+  pinnedEventsSaveObject: {},
+  dateRange: {
+    start: 0,
+    end: 0,
+  },
+  savedObjectId: null,
+  selectedEventIds: {},
+  show: false,
+  showCheckboxes: false,
+  showRowRenderers: true,
+  sort: {
+    columnId: '@timestamp',
+    sortDirection: Direction.desc,
+  },
+  width: DEFAULT_TIMELINE_WIDTH,
+  version: null,
+};
diff --git a/x-pack/legacy/plugins/siem/public/store/timeline/epic.ts b/x-pack/legacy/plugins/siem/public/store/timeline/epic.ts
index c243221a1b8c7..e6acff8736492 100644
--- a/x-pack/legacy/plugins/siem/public/store/timeline/epic.ts
+++ b/x-pack/legacy/plugins/siem/public/store/timeline/epic.ts
@@ -29,7 +29,6 @@ import {
 } from 'rxjs/operators';
 
 import { esFilters, Filter, MatchAllFilter } from '../../../../../../../src/plugins/data/public';
-import { ColumnHeader } from '../../components/timeline/body/column_headers/column_header';
 import { persistTimelineMutation } from '../../containers/timeline/persist.gql_query';
 import {
   PersistTimelineMutation,
@@ -70,7 +69,7 @@ import {
   addTimeline,
   showCallOutUnauthorizedMsg,
 } from './actions';
-import { TimelineModel } from './model';
+import { ColumnHeaderOptions, TimelineModel } from './model';
 import { epicPersistNote, timelineNoteActionsType } from './epic_note';
 import { epicPersistPinnedEvent, timelinePinnedEventActionsType } from './epic_pinned_event';
 import { epicPersistTimelineFavorite, timelineFavoriteActionsType } from './epic_favorite';
@@ -273,7 +272,7 @@ export const convertTimelineAsInput = (
       } else if (key === 'columns' && get(key, timeline) != null) {
         return set(
           key,
-          get(key, timeline).map((col: ColumnHeader) => omit(['width', '__typename'], col)),
+          get(key, timeline).map((col: ColumnHeaderOptions) => omit(['width', '__typename'], col)),
           acc
         );
       } else if (key === 'filters' && get(key, timeline) != null) {
diff --git a/x-pack/legacy/plugins/siem/public/store/timeline/helpers.ts b/x-pack/legacy/plugins/siem/public/store/timeline/helpers.ts
index 4155e25a67688..f56bbc34ce165 100644
--- a/x-pack/legacy/plugins/siem/public/store/timeline/helpers.ts
+++ b/x-pack/legacy/plugins/siem/public/store/timeline/helpers.ts
@@ -7,8 +7,7 @@
 import { getOr, omit, uniq, isEmpty, isEqualWith, union } from 'lodash/fp';
 
 import { Filter } from '../../../../../../../src/plugins/data/public';
-import { ColumnHeader } from '../../components/timeline/body/column_headers/column_header';
-import { getColumnWidthFromType } from '../../components/timeline/body/helpers';
+import { getColumnWidthFromType } from '../../components/timeline/body/column_headers/helpers';
 import { Sort } from '../../components/timeline/body/sort';
 import {
   DataProvider,
@@ -17,7 +16,8 @@ import {
 } from '../../components/timeline/data_providers/data_provider';
 import { KueryFilterQuery, SerializedFilterQuery } from '../model';
 
-import { KqlMode, timelineDefaults, TimelineModel, EventType } from './model';
+import { timelineDefaults } from './defaults';
+import { ColumnHeaderOptions, KqlMode, TimelineModel, EventType } from './model';
 import { TimelineById, TimelineState } from './types';
 import { TimelineNonEcsData } from '../../graphql/types';
 
@@ -129,7 +129,7 @@ export const addTimelineToStore = ({
 });
 
 interface AddNewTimelineParams {
-  columns: ColumnHeader[];
+  columns: ColumnHeaderOptions[];
   dataProviders?: DataProvider[];
   dateRange?: {
     start: number;
@@ -353,7 +353,7 @@ const addProviderToTimeline = (
 };
 
 interface AddTimelineColumnParams {
-  column: ColumnHeader;
+  column: ColumnHeaderOptions;
   id: string;
   index: number;
   timelineById: TimelineById;
@@ -566,7 +566,7 @@ export const updateKqlFilterQueryDraft = ({
 
 interface UpdateTimelineColumnsParams {
   id: string;
-  columns: ColumnHeader[];
+  columns: ColumnHeaderOptions[];
   timelineById: TimelineById;
 }
 
diff --git a/x-pack/legacy/plugins/siem/public/store/timeline/model.ts b/x-pack/legacy/plugins/siem/public/store/timeline/model.ts
index 1c54031dfe3fd..ef46a8d061c2e 100644
--- a/x-pack/legacy/plugins/siem/public/store/timeline/model.ts
+++ b/x-pack/legacy/plugins/siem/public/store/timeline/model.ts
@@ -5,20 +5,39 @@
  */
 
 import { Filter } from '../../../../../../../src/plugins/data/public';
-import { ColumnHeader } from '../../components/timeline/body/column_headers/column_header';
 import { DataProvider } from '../../components/timeline/data_providers/data_provider';
-import { DEFAULT_TIMELINE_WIDTH } from '../../components/timeline/body/helpers';
-import { defaultHeaders } from '../../components/timeline/body/column_headers/default_headers';
 import { Sort } from '../../components/timeline/body/sort';
-import { Direction, PinnedEvent, TimelineNonEcsData } from '../../graphql/types';
+import { PinnedEvent, TimelineNonEcsData } from '../../graphql/types';
 import { KueryFilterQuery, SerializedFilterQuery } from '../model';
 
 export const DEFAULT_PAGE_COUNT = 2; // Eui Pager will not render unless this is a minimum of 2 pages
 export type KqlMode = 'filter' | 'search';
 export type EventType = 'all' | 'raw' | 'signal';
+
+export type ColumnHeaderType = 'not-filtered' | 'text-filter';
+
+/** Uniquely identifies a column */
+export type ColumnId = string;
+
+/** The specification of a column header */
+export interface ColumnHeaderOptions {
+  aggregatable?: boolean;
+  category?: string;
+  columnHeaderType: ColumnHeaderType;
+  description?: string;
+  example?: string;
+  format?: string;
+  id: ColumnId;
+  label?: string;
+  linkField?: string;
+  placeholder?: string;
+  type?: string;
+  width: number;
+}
+
 export interface TimelineModel {
   /** The columns displayed in the timeline */
-  columns: ColumnHeader[];
+  columns: ColumnHeaderOptions[];
   /** The sources of the event data shown in the timeline */
   dataProviders: DataProvider[];
   /** Events to not be rendered **/
@@ -124,46 +143,7 @@ export type SubsetTimelineModel = Readonly<
   >
 >;
 
-export const timelineDefaults: SubsetTimelineModel & Pick<TimelineModel, 'filters'> = {
-  columns: defaultHeaders,
-  dataProviders: [],
-  deletedEventIds: [],
-  description: '',
-  eventType: 'raw',
-  eventIdToNoteIds: {},
-  highlightedDropAndProviderId: '',
-  historyIds: [],
-  filters: [],
-  isFavorite: false,
-  isLive: false,
-  isSelectAllChecked: false,
-  isLoading: false,
-  isSaving: false,
-  itemsPerPage: 25,
-  itemsPerPageOptions: [10, 25, 50, 100],
-  kqlMode: 'filter',
-  kqlQuery: {
-    filterQuery: null,
-    filterQueryDraft: null,
-  },
-  loadingEventIds: [],
-  title: '',
-  noteIds: [],
-  pinnedEventIds: {},
-  pinnedEventsSaveObject: {},
-  dateRange: {
-    start: 0,
-    end: 0,
-  },
-  savedObjectId: null,
-  selectedEventIds: {},
-  show: false,
-  showCheckboxes: false,
-  showRowRenderers: true,
-  sort: {
-    columnId: '@timestamp',
-    sortDirection: Direction.desc,
-  },
-  width: DEFAULT_TIMELINE_WIDTH,
-  version: null,
-};
+export interface TimelineUrl {
+  id: string;
+  isOpen: boolean;
+}
diff --git a/x-pack/legacy/plugins/siem/public/store/timeline/reducer.test.ts b/x-pack/legacy/plugins/siem/public/store/timeline/reducer.test.ts
index 0b82bab3dbb8e..58fc1c7e1e3df 100644
--- a/x-pack/legacy/plugins/siem/public/store/timeline/reducer.test.ts
+++ b/x-pack/legacy/plugins/siem/public/store/timeline/reducer.test.ts
@@ -6,7 +6,6 @@
 
 import { cloneDeep, set } from 'lodash/fp';
 
-import { ColumnHeader } from '../../components/timeline/body/column_headers/column_header';
 import {
   IS_OPERATOR,
   DataProvider,
@@ -15,9 +14,9 @@ import {
 import { defaultColumnHeaderType } from '../../components/timeline/body/column_headers/default_headers';
 import {
   DEFAULT_COLUMN_MIN_WIDTH,
-  getColumnWidthFromType,
   DEFAULT_TIMELINE_WIDTH,
-} from '../../components/timeline/body/helpers';
+} from '../../components/timeline/body/constants';
+import { getColumnWidthFromType } from '../../components/timeline/body/column_headers/helpers';
 import { Direction } from '../../graphql/types';
 import { defaultHeaders } from '../../mock';
 
@@ -41,7 +40,8 @@ import {
   updateTimelineTitle,
   upsertTimelineColumn,
 } from './helpers';
-import { timelineDefaults } from './model';
+import { ColumnHeaderOptions } from './model';
+import { timelineDefaults } from './defaults';
 import { TimelineById } from './types';
 
 const timelineByIdMock: TimelineById = {
@@ -101,7 +101,11 @@ const timelineByIdMock: TimelineById = {
   },
 };
 
-const columnsMock: ColumnHeader[] = [defaultHeaders[0], defaultHeaders[1], defaultHeaders[2]];
+const columnsMock: ColumnHeaderOptions[] = [
+  defaultHeaders[0],
+  defaultHeaders[1],
+  defaultHeaders[2],
+];
 
 describe('Timeline', () => {
   describe('#add saved object Timeline to store ', () => {
@@ -183,8 +187,8 @@ describe('Timeline', () => {
 
   describe('#upsertTimelineColumn', () => {
     let timelineById: TimelineById = {};
-    let columns: ColumnHeader[] = [];
-    let columnToAdd: ColumnHeader;
+    let columns: ColumnHeaderOptions[] = [];
+    let columnToAdd: ColumnHeaderOptions;
 
     beforeEach(() => {
       timelineById = cloneDeep(timelineByIdMock);

From 1b15872ab853614cf8354ff0603bff33585f61ae Mon Sep 17 00:00:00 2001
From: Yuliia Naumenko <jo.naumenko@gmail.com>
Date: Tue, 18 Feb 2020 11:12:07 -0800
Subject: [PATCH 035/174] Guide for creating alert / action types in the UI
 (#55963)

* Guide for creating alert type UI

* Type fixed

* Extended documentation for alert type and expression components usage

* fixed merge issues

* Added info for embedding create alert flyout

* Added alert type registration details

* Fixed table definition

* Fixed the rest of the tables styles

* Added builtin action types description

* Added example for action type

* Extended action create description

* Fixed some parts

* Fixed review comments

* Update x-pack/plugins/triggers_actions_ui/README.md

Co-Authored-By: gchaps <33642766+gchaps@users.noreply.github.com>

* Update x-pack/plugins/triggers_actions_ui/README.md

Co-Authored-By: gchaps <33642766+gchaps@users.noreply.github.com>

* Update x-pack/plugins/triggers_actions_ui/README.md

Co-Authored-By: gchaps <33642766+gchaps@users.noreply.github.com>

* Update x-pack/plugins/triggers_actions_ui/README.md

Co-Authored-By: gchaps <33642766+gchaps@users.noreply.github.com>

* Update x-pack/plugins/triggers_actions_ui/README.md

Co-Authored-By: gchaps <33642766+gchaps@users.noreply.github.com>

* Update x-pack/plugins/triggers_actions_ui/README.md

Co-Authored-By: gchaps <33642766+gchaps@users.noreply.github.com>

* Update x-pack/plugins/triggers_actions_ui/README.md

Co-Authored-By: gchaps <33642766+gchaps@users.noreply.github.com>

* Update x-pack/plugins/triggers_actions_ui/README.md

Co-Authored-By: gchaps <33642766+gchaps@users.noreply.github.com>

* Update x-pack/plugins/triggers_actions_ui/README.md

Co-Authored-By: gchaps <33642766+gchaps@users.noreply.github.com>

* Update x-pack/plugins/triggers_actions_ui/README.md

Co-Authored-By: gchaps <33642766+gchaps@users.noreply.github.com>

* Update x-pack/plugins/triggers_actions_ui/README.md

Co-Authored-By: gchaps <33642766+gchaps@users.noreply.github.com>

* Update x-pack/plugins/triggers_actions_ui/README.md

Co-Authored-By: gchaps <33642766+gchaps@users.noreply.github.com>

* Update x-pack/plugins/triggers_actions_ui/README.md

Co-Authored-By: gchaps <33642766+gchaps@users.noreply.github.com>

* Update x-pack/plugins/triggers_actions_ui/README.md

Co-Authored-By: gchaps <33642766+gchaps@users.noreply.github.com>

* Update x-pack/plugins/triggers_actions_ui/README.md

Co-Authored-By: gchaps <33642766+gchaps@users.noreply.github.com>

* Update x-pack/plugins/triggers_actions_ui/README.md

Co-Authored-By: gchaps <33642766+gchaps@users.noreply.github.com>

* Update x-pack/plugins/triggers_actions_ui/README.md

Co-Authored-By: gchaps <33642766+gchaps@users.noreply.github.com>

* Update x-pack/plugins/triggers_actions_ui/README.md

Co-Authored-By: gchaps <33642766+gchaps@users.noreply.github.com>

* Update x-pack/plugins/triggers_actions_ui/README.md

Co-Authored-By: gchaps <33642766+gchaps@users.noreply.github.com>

* Fixed review comments

Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com>
---
 x-pack/plugins/triggers_actions_ui/README.md | 1200 ++++++++++++++++++
 1 file changed, 1200 insertions(+)
 create mode 100644 x-pack/plugins/triggers_actions_ui/README.md

diff --git a/x-pack/plugins/triggers_actions_ui/README.md b/x-pack/plugins/triggers_actions_ui/README.md
new file mode 100644
index 0000000000000..c6a7808356b86
--- /dev/null
+++ b/x-pack/plugins/triggers_actions_ui/README.md
@@ -0,0 +1,1200 @@
+# Kibana Alerts and Actions UI
+
+The Kibana alerts and actions UI plugin provides a user interface for managing alerts and actions. 
+As a developer you can reuse and extend built-in alerts and actions UI functionality:
+
+- Create and register a new Alert Type.
+- Create and register a new Action Type.
+- Embed the Create Alert flyout within any Kibana plugin.
+
+To enable Alerts and Actions UIs, the following configuration settings are needed:
+```
+xpack.triggers_actions_ui.enabled: true
+xpack.triggers_actions_ui.createAlertUiEnabled: true
+```
+
+-----
+
+
+Table of Contents
+
+- [Kibana Alerts and Actions UI](#kibana-alerts-and-actions-ui)
+  - [Build and register Alert Types](#build-and-register-alert-types)
+    - [Built-in Alert Types](#built-in-alert-types)
+      - [Index Threshold Alert](#index-threshold-alert)
+    - [Alert type model definition](#alert-type-model-definition)
+    - [Register alert type model](#register-alert-type-model)
+    - [Create and register new alert type UI example](#create-and-register-new-alert-type-ui-example)
+    - [Common expression components](#common-expression-components)
+      - [WHEN expression component](#when-expression-component)
+      - [OF expression component](#of-expression-component)
+      - [GROUPED BY expression component](#grouped-by-expression-component)
+      - [FOR THE LAST expression component](#for-the-last-expression-component)
+      - [THRESHOLD expression component](#threshold-expression-component)
+    - [Embed the Create Alert flyout within any Kibana plugin](#embed-the-create-alert-flyout-within-any-kibana-plugin)
+  - [Build and register Action Types](#build-and-register-action-types)
+    - [Built-in Action Types](#built-in-action-types)
+      - [Server log](#server-log)
+      - [Email](#email)
+      - [Slack](#slack)
+      - [Index](#index)
+      - [Webhook](#webhook)
+      - [PagerDuty](#pagerduty)
+    - [Action type model definition](#action-type-model-definition)
+    - [Register action type model](#register-action-type-model)
+    - [Create and register new action type UI example](#reate-and-register-new-action-type-ui-example)
+
+## Built-in Alert Types
+
+Kibana ships with several built-in alert types:
+
+|Type|Id|Description|
+|---|---|---|
+|[Index Threshold](#index-threshold-alert)|`threshold`|Index Threshold Alert|
+
+Every alert type must be registered server side, and can optionally be registered client side.
+Only alert types registered on both client and server will be displayed in the Create Alert flyout, as a part of the UI.
+Built-in alert types UI are located under the folder `x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types`
+and this is a file `x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/index.ts` for client side registration.
+
+### Index Threshold Alert
+
+ID: `threshold`
+
+In the Kibana UI, this alert type is available as a select card on the Create Alert flyout:
+![Index Threshold select card](https://i.imgur.com/a0bqLwC.png)
+
+AlertTypeModel:
+
+```
+export function getAlertType(): AlertTypeModel {
+  return {
+    id: 'threshold',
+    name: 'Index Threshold',
+    iconClass: 'alert',
+    alertParamsExpression: IndexThresholdAlertTypeExpression,
+    validate: validateAlertType,
+  };
+}
+```
+
+alertParamsExpression form represented as an expression using `EuiExpression` components:
+![Index Threshold Alert expression form](https://i.imgur.com/Ysk1ljY.png)
+
+```
+interface IndexThresholdProps {
+  alertParams: IndexThresholdAlertParams;
+  setAlertParams: (property: string, value: any) => void;
+  setAlertProperty: (key: string, value: any) => void;
+  errors: { [key: string]: string[] };
+  alertsContext: AlertsContextValue;
+}
+```
+
+|Property|Description|
+|---|---|
+|alertParams|Set of Alert params relevant for the index threshold alert type.|
+|setAlertParams|Alert reducer method, which is used to create a new copy of alert object with the changed params property any subproperty value.|
+|setAlertProperty|Alert reducer method, which is used to create a new copy of alert object with the changed any direct alert property value.|
+|errors|Alert level errors tracking object.|
+|alertsContext|Alert context, which is used to pass down common objects like http client.|
+
+
+Alert reducer is defined on the AlertAdd functional component level and passed down to the subcomponents to provide a new state of Alert object:
+
+```
+const [{ alert }, dispatch] = useReducer(alertReducer, { alert: initialAlert });
+
+...
+
+const setAlertProperty = (key: string, value: any) => {
+    dispatch({ command: { type: 'setProperty' }, payload: { key, value } });
+  };
+
+  const setAlertParams = (key: string, value: any) => {
+    dispatch({ command: { type: 'setAlertParams' }, payload: { key, value } });
+  };
+
+  const setScheduleProperty = (key: string, value: any) => {
+    dispatch({ command: { type: 'setScheduleProperty' }, payload: { key, value } });
+  };
+
+  const setActionParamsProperty = (key: string, value: any, index: number) => {
+    dispatch({ command: { type: 'setAlertActionParams' }, payload: { key, value, index } });
+  };
+
+  const setActionProperty = (key: string, value: any, index: number) => {
+    dispatch({ command: { type: 'setAlertActionProperty' }, payload: { key, value, index } });
+  };
+
+```
+
+'x-pack/plugins/triggers_actions_ui/public/application/sections/alert_add/alert_reducer.ts' define the methods for changing different type of alert properties:
+```
+export const alertReducer = (state: any, action: AlertReducerAction) => {
+  const { command, payload } = action;
+  const { alert } = state;
+
+  switch (command.type) {
+    // create a new alert state with a new alert value
+    case 'setAlert': {
+    ....
+    //  create a new alert state with set new value to one alert property by key
+    case 'setProperty': {
+    ....
+    // create a new alert state with set new value to any subproperty for a 'schedule' alert property
+    case 'setScheduleProperty': {
+    ....
+    // create a new alert state with set new value to action subproperty by index from the array of alert actions
+    case 'setAlertActionParams': {   //
+    ....
+    // create a new alert state with set new value to any subproperty for a 'params' alert property
+    case 'setAlertParams': {
+      const { key, value } = payload;
+      if (isEqual(alert.params[key], value)) {
+        return state;
+      } else {
+        return {
+          ...state,
+          alert: {
+            ...alert,
+            params: {
+              ...alert.params,
+              [key]: value,
+            },
+          },
+        };
+      }
+    }
+    // create a new alert state with add or remove action from alert actions array
+    case 'setAlertActionProperty': {
+    ....
+    }
+  }
+
+```
+
+
+```
+export const IndexThresholdAlertTypeExpression: React.FunctionComponent<IndexThresholdProps> = ({
+  alertParams,
+  setAlertParams,
+  setAlertProperty,
+  errors,
+  alertsContext,
+}) => {
+
+  ....
+
+  // expression validation
+  const hasExpressionErrors = !!Object.keys(errors).find(
+    errorKey =>
+      expressionFieldsWithValidation.includes(errorKey) &&
+      errors[errorKey].length >= 1 &&
+      (alertParams as { [key: string]: any })[errorKey] !== undefined
+  );
+
+  ....
+
+  // loading indeces and set default expression values
+  useEffect(() => {
+    getIndexPatterns();
+  }, []);
+
+  useEffect(() => {
+    setDefaultExpressionValues();
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, []);
+
+  ....
+
+  return (
+    <Fragment>
+      {hasExpressionErrors ? (
+        <Fragment>
+          <EuiSpacer />
+          <EuiCallOut color="danger" size="s" title={expressionErrorMessage} />
+          <EuiSpacer />
+        </Fragment>
+      ) : null}
+      <EuiSpacer size="l" />
+      <EuiFormLabel>
+        <FormattedMessage
+          defaultMessage="Select Index to query:"
+          id="xpack.triggersActionsUI.sections.alertAdd.selectIndex"
+        />
+  ....
+      </Fragment>
+  );
+};
+```
+
+Index Threshold Alert form with validation:
+![Index Threshold Alert validation](https://i.imgur.com/TV8c7hL.png)
+
+## Alert type model definition
+
+Each alert type should be defined as `AlertTypeModel` object with the these properties:
+```
+  id: string;
+  name: string;
+  iconClass: string;
+  validate: (alertParams: any) => ValidationResult;
+  alertParamsExpression: React.FunctionComponent<any>;
+  defaultActionMessage?: string;
+```
+|Property|Description|
+|---|---|
+|id|Alert type id. Should be the same as on the server side.|
+|name|Name of the alert type that will be displayed on the select card in the UI.|
+|iconClass|Icon of the alert type that will be displayed on the select card in the UI.|
+|validate|Validation function for the alert params.|
+|alertParamsExpression|React functional component for building UI of the current alert type params.|
+|defaultActionMessage|Optional property for providing default message for all added actions with `message` property.|
+
+IMPORTANT: The current UI supports a single action group only. 
+Action groups are mapped from the server API result for [GET /api/alert/types: List alert types](https://github.com/elastic/kibana/tree/master/x-pack/legacy/plugins/alerting#get-apialerttypes-list-alert-types).
+Server side alert type model:
+```
+export interface AlertType {
+  id: string;
+  name: string;
+  validate?: {
+    params?: { validate: (object: any) => any };
+  };
+  actionGroups: string[];
+  executor: ({ services, params, state }: AlertExecutorOptions) => Promise<State | void>;
+}
+```
+Only the default (which means first item of the array) action group is displayed in the current UI.
+Design of user interface and server API for multiple action groups is under discussion and development.
+
+## Register alert type model
+
+There are two ways of registering a new alert type:
+
+1. Directly in the `triggers_actions_ui` plugin. In this case, the alert type will be available in the Create Alert flyout of the Alerts and Actions management section.
+Registration code for a new alert type model should be added to the file `x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/index.ts`
+Only registered alert types are available in UI.
+
+2. Register the alert type in another plugin. In this case, the alert type will be available only in the current plugin UI. 
+It should be done by importing dependency `TriggersAndActionsUIPublicPluginSetup` and adding the next code on plugin setup:
+
+```
+function getSomeNewAlertType() {
+  return { ... } as AlertTypeModel;
+}
+
+triggers_actions_ui.alertTypeRegistry.register(getSomeNewAlertType());
+```
+
+## Create and register new alert type UI example
+
+Before registering a UI for a new Alert Type, you should first register the type on the server-side by following the Alerting guide: https://github.com/elastic/kibana/tree/master/x-pack/legacy/plugins/alerting#example 
+
+Alert type UI is expected to be defined as `AlertTypeModel` object.
+
+Below is a list of steps that should be done to build and register a new alert type with the name `Example Alert Type`:
+
+1. At any suitable place in Kibana, create a file, which will expose an object implementing interface [AlertTypeModel](https://github.com/elastic/kibana/blob/55b7905fb5265b73806006e7265739545d7521d0/x-pack/legacy/plugins/triggers_actions_ui/np_ready/public/types.ts#L83). Example:
+```
+import { AlertTypeModel } from '../../../../types';
+import { ExampleExpression } from './expression';
+import { validateExampleAlertType } from './validation';
+
+export function getAlertType(): AlertTypeModel {
+  return {
+    id: 'example',
+    name: 'Example Alert Type',
+    iconClass: 'bell',
+    alertParamsExpression: ExampleExpression,
+    validate: validateExampleAlertType,
+    defaultActionMessage: 'Alert [{{ctx.metadata.name}}] has exceeded the threshold',
+  };
+}
+```
+Fields of this object `AlertTypeModel` will be mapped properly in the UI below.
+
+2. Define `alertParamsExpression` as `React.FunctionComponent` - this is the form for filling Alert params based on the current Alert type.
+```
+import React, { Fragment, useState } from 'react';
+import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
+import { WhenExpression, OfExpression } from '../../../../common/expression_items';
+import { builtInAggregationTypes } from '../../../../common/constants';
+
+interface ExampleProps {
+  testAggType?: string;
+  testAggField?: string;
+  errors: { [key: string]: string[] };
+}
+
+export const ExampleExpression: React.FunctionComponent<ExampleProps> = ({
+  testAggType,
+  testAggField,
+  errors,
+}) => {
+  const [aggType, setAggType] = useState<string>('count');
+  return (
+    <Fragment>
+      <EuiFlexGroup gutterSize="s" wrap>
+        <EuiFlexItem grow={false}>
+          <WhenExpression
+            aggType={testAggType ?? 'count'} // defult is 'count'
+            onChangeSelectedAggType={(selectedAggType: string) => {
+              console.log(`Set alert type params field "aggType" value as ${selectedAggType}`);
+              setAggType(selectedAggType);
+            }}
+          />
+        </EuiFlexItem>
+        {aggType && builtInAggregationTypes[aggType].fieldRequired ? (
+          <EuiFlexItem grow={false}>
+            <OfExpression
+              aggField={testAggField}
+              fields={[{ normalizedType: 'number', name: 'test' }]} // can be some data from server API
+              aggType={aggType}
+              errors={errors}
+              onChangeSelectedAggField={(selectedAggField?: string) =>
+                console.log(`Set alert type params field "aggField" value as ${selectedAggField}`)
+              }
+            />
+          </EuiFlexItem>
+        ) : null}
+      </EuiFlexGroup>
+    </Fragment>
+  );
+};
+
+```
+This alert type form becomes available, when the card of `Example Alert Type` is selected.
+Each expression word here is `EuiExpression` component and implements the basic aggregation, grouping and comparison methods.
+Expression components, which can be embedded to different alert types, are described here [Common expression components](#common-expression-components).
+
+3. Define alert type params validation using the property of `AlertTypeModel` `validate`: 
+```
+import { i18n } from '@kbn/i18n';
+import { ValidationResult } from '../../../../types';
+
+export function validateExampleAlertType({
+  testAggField,
+}: {
+  testAggField: string;
+}): ValidationResult {
+  const validationResult = { errors: {} };
+  const errors = {
+    aggField: new Array<string>(),
+  };
+  validationResult.errors = errors;
+  if (!testAggField) {
+    errors.aggField.push(
+      i18n.translate('xpack.triggersActionsUI.components.example.error.requiredTestAggFieldText', {
+        defaultMessage: 'Test aggregation field is required.',
+      })
+    );
+  }
+  return validationResult;
+}
+```
+
+4. Extend registration code with the new alert type register in the file `x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/index.ts`
+```
+import { getAlertType as getExampledAlertType } from './example';
+...
+
+...
+alertTypeRegistry.register(getExampledAlertType());
+```
+
+After these four steps, the new `Example Alert Type` is available in UI of Create flyout:
+![Example Alert Type is in the select cards list](https://i.imgur.com/j71AEQV.png)
+
+Click on the select card for `Example Alert Type` to open the expression form that was created in step 2:
+![Example Alert Type expression with validation](https://i.imgur.com/Z0jIwCS.png)
+
+## Common expression components
+
+### WHEN expression component
+
+![WHEN](https://i.imgur.com/7bYlxXK.png)
+
+```
+<WhenExpression
+  aggType={aggType ?? DEFAULT_VALUES.AGGREGATION_TYPE}
+  onChangeSelectedAggType={(selectedAggType: string) =>
+    setAlertParams('aggType', selectedAggType)
+  }
+/>
+```
+
+Props definition:
+```
+interface WhenExpressionProps {
+  aggType: string;
+  customAggTypesOptions?: { [key: string]: AggregationType };
+  onChangeSelectedAggType: (selectedAggType: string) => void;
+  popupPosition?: 'upCenter' | 'upLeft' | 'upRight' | 'downCenter' | 'downLeft'
+    | 'downRight' | 'leftCenter' | 'leftUp' | 'leftDown' | 'rightCenter' | 'rightUp' | 'rightDown';
+}
+```
+
+|Property|Description|
+|---|---|
+|aggType|Selected aggregation type that will be set as the alert type property.|
+|customAggTypesOptions|(Optional) List of aggregation types that replaces the default options defined in constants `x-pack/plugins/triggers_actions_ui/public/common/constants/aggregation_types.ts`.|
+|onChangeSelectedAggType|event handler that will be executed when selected aggregation type is changed.|
+|popupPosition|(Optional) expression popup position. Default is `downLeft`.  Recommend changing it for a small parent window space.|
+
+### OF expression component
+
+![OF](https://i.imgur.com/4MC8Kbb.png)
+
+OF expression is available, if aggregation type requires selecting data fields for aggregating.
+
+```
+<OfExpression
+  aggField={aggField}
+  fields={esFields}
+  aggType={aggType}
+  errors={errors}
+  onChangeSelectedAggField={(selectedAggField?: string) =>
+    setAlertParams('aggField', selectedAggField)
+  }
+/>
+```
+
+Props definition:
+```
+interface OfExpressionProps {
+  aggType: string;
+  aggField?: string;
+  errors: { [key: string]: string[] };
+  onChangeSelectedAggField: (selectedAggType?: string) => void;
+  fields: Record<string, any>;
+  customAggTypesOptions?: {
+    [key: string]: AggregationType;
+  };
+  popupPosition?: 'upCenter' | 'upLeft' | 'upRight' | 'downCenter' | 'downLeft'
+    | 'downRight' | 'leftCenter' | 'leftUp' | 'leftDown' | 'rightCenter' | 'rightUp' | 'rightDown';
+}
+```
+
+|Property|Description|
+|---|---|
+|aggType|Selected aggregation type that will be set as the alert type property.|
+|aggField|Selected aggregation field that will be set as the alert type property.|
+|errors|List of errors with proper messages for the alert params that should be validated. In current component is validated `aggField`.|
+|onChangeSelectedAggField|Event handler that will be excuted if selected aggregation field is changed.|
+|fields|Fields list that will be available in the OF `Select a field` dropdown.|
+|customAggTypesOptions|(Optional) List of aggregation types that replaces the default options defined in constants `x-pack/plugins/triggers_actions_ui/public/common/constants/aggregation_types.ts`.|
+|popupPosition|(Optional) expression popup position. Default is `downRight`. Recommend changing it for a small parent window space.|
+
+### GROUPED BY expression component
+
+![GROUPED BY](https://i.imgur.com/eej7WIw.png)
+
+```
+<GroupByExpression
+  groupBy={groupBy || DEFAULT_VALUES.GROUP_BY}
+  termField={termField}
+  termSize={termSize}
+  errors={errors}
+  fields={esFields}
+  onChangeSelectedGroupBy={selectedGroupBy => setAlertParams('groupBy', selectedGroupBy)}
+  onChangeSelectedTermField={selectedTermField =>
+    setAlertParams('termField', selectedTermField)
+  }
+  onChangeSelectedTermSize={selectedTermSize =>
+    setAlertParams('termSize', selectedTermSize)
+  }
+/>
+```
+
+Props definition:
+```
+interface GroupByExpressionProps {
+  groupBy: string;
+  termSize?: number;
+  termField?: string;
+  errors: { [key: string]: string[] };
+  onChangeSelectedTermSize: (selectedTermSize?: number) => void;
+  onChangeSelectedTermField: (selectedTermField?: string) => void;
+  onChangeSelectedGroupBy: (selectedGroupBy?: string) => void;
+  fields: Record<string, any>;
+  customGroupByTypes?: {
+    [key: string]: GroupByType;
+  };
+  popupPosition?: 'upCenter' | 'upLeft' | 'upRight' | 'downCenter' | 'downLeft'
+    | 'downRight' | 'leftCenter' | 'leftUp' | 'leftDown' | 'rightCenter' | 'rightUp' | 'rightDown';
+}
+```
+
+|Property|Description|
+|---|---|
+|groupBy|Selected group by type that will be set as the alert type property.|
+|termSize|Selected term size that will be set as the alert type property.|
+|termField|Selected term field that will be set as the alert type property.|
+|errors|List of errors with proper messages for the alert params that should be validated. In current component is validated `termSize` and `termField`.|
+|onChangeSelectedTermSize|Event handler that will be excuted if selected term size is changed.|
+|onChangeSelectedTermField|Event handler that will be excuted if selected term field is changed.|
+|onChangeSelectedGroupBy|Event handler that will be excuted if selected group by is changed.|
+|fields|Fields list with options for the `termField` dropdown.|
+|customGroupByTypes|(Optional) List of group by types that replaces the default options defined in constants `x-pack/plugins/triggers_actions_ui/public/common/constants/group_by_types.ts`.|
+|popupPosition|(Optional) expression popup position. Default is `downLeft`. Recommend changing it for a small parent window space.|
+
+### FOR THE LAST expression component
+
+![FOR THE LAST](https://i.imgur.com/vYJTo8F.png)
+
+```
+<ForLastExpression
+  timeWindowSize={timeWindowSize || 1}
+  timeWindowUnit={timeWindowUnit || ''}
+  errors={errors}
+  onChangeWindowSize={(selectedWindowSize: any) =>
+    setAlertParams('timeWindowSize', selectedWindowSize)
+  }
+  onChangeWindowUnit={(selectedWindowUnit: any) =>
+    setAlertParams('timeWindowUnit', selectedWindowUnit)
+  }
+/>
+```
+
+Props definition:
+```
+interface ForLastExpressionProps {
+  timeWindowSize?: number;
+  timeWindowUnit?: string;
+  errors: { [key: string]: string[] };
+  onChangeWindowSize: (selectedWindowSize: number | '') => void;
+  onChangeWindowUnit: (selectedWindowUnit: string) => void;
+  popupPosition?: 'upCenter' | 'upLeft' | 'upRight' | 'downCenter' | 'downLeft'
+    | 'downRight' | 'leftCenter' | 'leftUp' | 'leftDown' | 'rightCenter' | 'rightUp' | 'rightDown';
+}
+```
+
+|Property|Description|
+|---|---|
+|timeWindowSize|Selected time window size that will be set as the alert type property.|
+|timeWindowUnit|Selected time window unit that will be set as the alert type property.|
+|errors|List of errors with proper messages for the alert params that should be validated. In current component is validated `termWindowSize`.|
+|onChangeWindowSize|Event handler that will be excuted if selected window size is changed.|
+|onChangeWindowUnit|Event handler that will be excuted if selected window unit is changed.|
+|popupPosition|(Optional) expression popup position. Default is `downLeft`. Recommend changing it for a small parent window space.|
+
+### THRESHOLD expression component
+
+![THRESHOLD](https://i.imgur.com/B92ZcT8.png)
+
+```
+<ThresholdExpression
+  thresholdComparator={thresholdComparator ?? DEFAULT_VALUES.THRESHOLD_COMPARATOR}
+  threshold={threshold}
+  errors={errors}
+  onChangeSelectedThreshold={selectedThresholds =>
+    setAlertParams('threshold', selectedThresholds)
+  }
+  onChangeSelectedThresholdComparator={selectedThresholdComparator =>
+    setAlertParams('thresholdComparator', selectedThresholdComparator)
+  }
+/>
+```
+
+Props definition:
+```
+interface ThresholdExpressionProps {
+  thresholdComparator: string;
+  errors: { [key: string]: string[] };
+  onChangeSelectedThresholdComparator: (selectedThresholdComparator?: string) => void;
+  onChangeSelectedThreshold: (selectedThreshold?: number[]) => void;
+  customComparators?: {
+    [key: string]: Comparator;
+  };
+  threshold?: number[];
+  popupPosition?: 'upCenter' | 'upLeft' | 'upRight' | 'downCenter' | 'downLeft'
+    | 'downRight' | 'leftCenter' | 'leftUp' | 'leftDown' | 'rightCenter' | 'rightUp' | 'rightDown';
+}
+```
+
+|Property|Description|
+|---|---|
+|thresholdComparator|Selected time window size that will be set as the alert type property.|
+|threshold|Selected time window size that will be set as the alert type property.|
+|errors|List of errors with proper messages for the alert params that should be validated. In current component is validated `threshold0` and `threshold1`.|
+|onChangeSelectedThresholdComparator|Event handler that will be excuted if selected threshold comparator is changed.|
+|onChangeSelectedThreshold|Event handler that will be excuted if selected threshold is changed.|
+|customComparators|(Optional) List of comparators that replaces the default options defined in constants `x-pack/plugins/triggers_actions_ui/public/common/constants/comparators.ts`.|
+|popupPosition|(Optional) expression popup position. Default is `downLeft`. Recommend changing it for a small parent window space.|
+
+## Embed the Create Alert flyout within any Kibana plugin
+
+Follow the instructions bellow to embed the Create Alert flyout within any Kibana plugin:
+1. Add TriggersAndActionsUIPublicPluginSetup to Kibana plugin setup dependencies:
+
+```
+triggers_actions_ui: TriggersAndActionsUIPublicPluginSetup;
+```
+Then this dependency will be used to embed Create Alert flyout or register new alert/action type.
+
+2. Add Create Alert flyout to React component:
+```
+// import section
+import { AlertsContextProvider, AlertAdd } from '../../../../../../../triggers_actions_ui/public';
+
+// in the component state definition section
+const [alertFlyoutVisible, setAlertFlyoutVisibility] = useState<boolean>(false);
+
+// UI control item for open flyout
+<EuiButton
+  fill
+  iconType="plusInCircle"
+  iconSide="left"
+  onClick={() => setAlertFlyoutVisibility(true)}
+>
+  <FormattedMessage
+    id="emptyButton"
+    defaultMessage="Create alert"
+  />
+</EuiButton>
+
+// in render section of component
+<AlertsContextProvider
+  value={{
+    addFlyoutVisible: alertFlyoutVisible,
+    setAddFlyoutVisibility: setAlertFlyoutVisibility,
+    http,
+    actionTypeRegistry: triggers_actions_ui.actionTypeRegistry,
+    alertTypeRegistry: triggers_actions_ui.alertTypeRegistry,
+    toastNotifications: toasts,
+    uiSettings,
+    charts,
+    dataFieldsFormats,
+  }}
+>
+  <AlertAdd consumer={'watcher'}  />
+</AlertsContextProvider>
+```
+
+AlertAdd Props definition:
+```
+interface AlertAddProps {
+  consumer: string;
+  alertTypeId?: string;
+  canChangeTrigger?: boolean;
+}
+```
+
+|Property|Description|
+|---|---|
+|consumer|Name of the plugin that creates an alert.|
+|alertTypeId|Optional property to preselect alert type.|
+|canChangeTrigger|Optional property, that hides change alert type possibility.|
+
+AlertsContextProvider value options:
+```
+export interface AlertsContextValue {
+  addFlyoutVisible: boolean;
+  setAddFlyoutVisibility: React.Dispatch<React.SetStateAction<boolean>>;
+  reloadAlerts?: () => Promise<void>;
+  http: HttpSetup;
+  alertTypeRegistry: TypeRegistry<AlertTypeModel>;
+  actionTypeRegistry: TypeRegistry<ActionTypeModel>;
+  uiSettings?: IUiSettingsClient;
+  toastNotifications?: Pick<
+    ToastsApi,
+    'get$' | 'add' | 'remove' | 'addSuccess' | 'addWarning' | 'addDanger' | 'addError'
+  >;
+  charts?: ChartsPluginSetup;
+  dataFieldsFormats?: Pick<FieldFormatsRegistry, 'register'>;
+}
+```
+
+|Property|Description|
+|---|---|
+|addFlyoutVisible|Visibility state of the Create Alert flyout.|
+|setAddFlyoutVisibility|Function for changing visibility state of the Create Alert flyout.|
+|reloadAlerts|Optional function, which will be executed if alert was saved sucsessfuly.|
+|http|HttpSetup needed for executing API calls.|
+|alertTypeRegistry|Registry for alert types.|
+|actionTypeRegistry|Registry for action types.|
+|uiSettings|Optional property, which is needed to display visualization of alert type expression. Will be changed after visualization refactoring.|
+|toastNotifications|Optional toast messages.|
+|charts|Optional property, which is needed to display visualization of alert type expression. Will be changed after visualization refactoring.|
+|dataFieldsFormats|Optional property, which is needed to display visualization of alert type expression. Will be changed after visualization refactoring.|
+
+## Build and register Action Types
+
+Kibana ships with a set of built-in action types UI:
+
+|Type|Id|Description|
+|---|---|---|
+|[Server log](#server-log)|`.log`|Logs messages to the Kibana log using `server.log()`|
+|[Email](#email)|`.email`|Sends an email using SMTP|
+|[Slack](#slack)|`.slack`|Posts a message to a Slack channel|
+|[Index](#index)|`.index`|Indexes document(s) into Elasticsearch|
+|[Webhook](#webhook)|`.webhook`|Sends a payload to a web service using HTTP POST or PUT|
+|[PagerDuty](#pagerduty)|`.pagerduty`|Triggers, resolves, or acknowledges an incident to a PagerDuty service|
+
+Every action type should be registered server side, and can be optionally registered client side. 
+Only action types registered on both client and server will be displayed in the Alerts and Actions UI.
+Built-in action types UI is located under the folder `x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types`
+and this is a file `x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/index.ts` for client side registration.
+
+
+### Server log
+
+Action type model definition:
+```
+export function getActionType(): ActionTypeModel {
+  return {
+    id: '.server-log',
+    iconClass: 'logsApp',
+    selectMessage: i18n.translate(
+      'xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.selectMessageText',
+      {
+        defaultMessage: 'Add a message to a Kibana log.',
+      }
+    ),
+    actionTypeTitle: i18n.translate(
+      'xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.actionTypeTitle',
+      {
+        defaultMessage: 'Send to Server log',
+      }
+    ),
+    validateConnector: (): ValidationResult => {
+      return { errors: {} };
+    },
+    validateParams: (actionParams: ServerLogActionParams): ValidationResult => {
+      // validation of action params implementation
+    },
+    actionConnectorFields: null,
+    actionParamsFields: ServerLogParamsFields,
+  };
+}
+```
+Server log has a connector UI:
+
+![Server log connector card](https://i.imgur.com/ZIWhV89.png)
+
+![Server log connector form](https://i.imgur.com/rkc3U59.png)
+
+and action params form available in Create Alert form:
+![Server log action form](https://i.imgur.com/c0ds76T.png)
+
+### Email
+
+Action type model definition:
+```
+export function getActionType(): ActionTypeModel {
+  const mailformat = /^[^@\s]+@[^@\s]+$/;
+  return {
+    id: '.email',
+    iconClass: 'email',
+    selectMessage: i18n.translate(
+      'xpack.triggersActionsUI.components.builtinActionTypes.emailAction.selectMessageText',
+      {
+        defaultMessage: 'Send email from your server.',
+      }
+    ),
+    actionTypeTitle: i18n.translate(
+      'xpack.triggersActionsUI.components.builtinActionTypes.emailAction.actionTypeTitle',
+      {
+        defaultMessage: 'Send to email',
+      }
+    ),
+    validateConnector: (action: EmailActionConnector): ValidationResult => {
+      // validation of connector properties implementation
+    },
+    validateParams: (actionParams: EmailActionParams): ValidationResult => {
+      // validation of action params implementation
+    },
+    actionConnectorFields: EmailActionConnectorFields,
+    actionParamsFields: EmailParamsFields,
+  };
+}
+```
+![Email connector card](https://i.imgur.com/d8kCbjQ.png)
+
+![Email connector form](https://i.imgur.com/Uf6HU7X.png)
+
+and action params form available in Create Alert form:
+![Email action form](https://i.imgur.com/lhkUEHf.png)
+
+### Slack
+
+Action type model definition:
+```
+export function getActionType(): ActionTypeModel {
+  return {
+    id: '.slack',
+    iconClass: 'logoSlack',
+    selectMessage: i18n.translate(
+      'xpack.triggersActionsUI.components.builtinActionTypes.slackAction.selectMessageText',
+      {
+        defaultMessage: 'Send a message to a Slack channel or user.',
+      }
+    ),
+    actionTypeTitle: i18n.translate(
+      'xpack.triggersActionsUI.components.builtinActionTypes.slackAction.actionTypeTitle',
+      {
+        defaultMessage: 'Send to Slack',
+      }
+    ),
+    validateConnector: (action: SlackActionConnector): ValidationResult => {
+      // validation of connector properties implementation
+    },
+    validateParams: (actionParams: SlackActionParams): ValidationResult => {
+      // validation of action params implementation 
+    },
+    actionConnectorFields: SlackActionFields,
+    actionParamsFields: SlackParamsFields,
+  };
+}
+```
+
+![Slack connector card](https://i.imgur.com/JbvmNOy.png)
+
+![Slack connector form](https://i.imgur.com/IqdnmF9.png)
+
+and action params form available in Create Alert form:
+![Slack action form](https://i.imgur.com/GUEVZWK.png)
+
+### Index
+
+Action type model definition:
+```
+export function getActionType(): ActionTypeModel {
+  return {
+    id: '.index',
+    iconClass: 'indexOpen',
+    selectMessage: i18n.translate(
+      'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.selectMessageText',
+      {
+        defaultMessage: 'Index data into Elasticsearch.',
+      }
+    ),
+    validateConnector: (): ValidationResult => {
+      return { errors: {} };
+    },
+    actionConnectorFields: IndexActionConnectorFields,
+    actionParamsFields: IndexParamsFields,
+    validateParams: (): ValidationResult => {
+      return { errors: {} };
+    },
+  };
+}
+```
+
+![Index connector card](https://i.imgur.com/fflsmu5.png)
+
+![Index connector form](https://i.imgur.com/tbgyvAL.png)
+
+and action params form available in Create Alert form:
+![Index action form](https://i.imgur.com/VsWMLeU.png)
+
+### Webhook
+
+Action type model definition:
+```
+export function getActionType(): ActionTypeModel {
+  return {
+    id: '.webhook',
+    iconClass: 'logoWebhook',
+    selectMessage: i18n.translate(
+      'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.selectMessageText',
+      {
+        defaultMessage: 'Send a request to a web service.',
+      }
+    ),
+    validateConnector: (action: WebhookActionConnector): ValidationResult => {
+      // validation of connector properties implementation
+    },
+    validateParams: (actionParams: WebhookActionParams): ValidationResult => {
+      // validation of action params implementation
+    },
+    actionConnectorFields: WebhookActionConnectorFields,
+    actionParamsFields: WebhookParamsFields,
+  };
+}
+```
+
+![Webhook connector card](https://i.imgur.com/IBgn75T.png)
+
+![Webhook connector form](https://i.imgur.com/xqORAJ7.png)
+
+and action params form available in Create Alert form:
+![Webhook action form](https://i.imgur.com/mBGfeuC.png)
+
+
+### PagerDuty
+
+Action type model definition:
+```
+export function getActionType(): ActionTypeModel {
+  return {
+    id: '.pagerduty',
+    iconClass: 'apps',
+    selectMessage: i18n.translate(
+      'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.selectMessageText',
+      {
+        defaultMessage: 'Send an event in PagerDuty.',
+      }
+    ),
+    actionTypeTitle: i18n.translate(
+      'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.actionTypeTitle',
+      {
+        defaultMessage: 'Send to PagerDuty',
+      }
+    ),
+    validateConnector: (action: PagerDutyActionConnector): ValidationResult => {
+      // validation of connector properties implementation
+    },
+    validateParams: (actionParams: PagerDutyActionParams): ValidationResult => {
+      // validation of action params implementation
+    },
+    actionConnectorFields: PagerDutyActionConnectorFields,
+    actionParamsFields: PagerDutyParamsFields,
+  };
+}
+```
+
+![PagerDuty connector card](https://i.imgur.com/Br8MuKG.png)
+
+![PagerDuty connector form](https://i.imgur.com/DZpCfRv.png)
+
+and action params form available in Create Alert form:
+![PagerDuty action form](https://i.imgur.com/xxXmhMK.png)
+
+## Action type model definition
+
+Each action type should be defined as an `ActionTypeModel` object with the following properties:
+```
+  id: string;
+  iconClass: string;
+  selectMessage: string;
+  actionTypeTitle?: string;
+  validateConnector: (connector: any) => ValidationResult;
+  validateParams: (actionParams: any) => ValidationResult;
+  actionConnectorFields: React.FunctionComponent<any> | null;
+  actionParamsFields: any;
+```
+|Property|Description|
+|---|---|
+|id|Action type id. Should be the same as on server side.|
+|iconClass|Icon of action type, that will be displayed on the select card in UI.|
+|selectMessage|Short description of action type responsibility, that will be displayed on the select card in UI.|
+|validateConnector|Validation function for action connector.|
+|validateParams|Validation function for action params.|
+|actionConnectorFields|React functional component for building UI of current action type connector.|
+|actionParamsFields|React functional component for building UI of current action type params. Displayed as a part of Create Alert flyout.|
+
+## Register action type model
+
+There are two ways to register a new action type UI:
+
+1. Directly in `triggers_actions_ui` plugin. In this case, the action type will be available in the Alerts and Actions management section.
+Registration code for a new action type model should be added to the file `x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/index.ts`
+Only registered action types are available in UI.
+
+2. Register action type in another plugin. In this case, the action type will be available only in the current plugin UI.
+It should be done by importing dependency `TriggersAndActionsUIPublicPluginSetup` and adding the next code on plugin setup:
+
+```
+function getSomeNewActionType() {
+  return { ... } as ActionTypeModel;
+}
+
+triggers_actions_ui.actionTypeRegistry.register(getSomeNewActionType());
+```
+
+## Create and register new action type UI
+
+Before starting the UI implementation, the [server side registration](https://github.com/elastic/kibana/tree/master/x-pack/plugins/actions#kibana-actions-configuration) should be done first.
+
+Action type UI is expected to be defined as `ActionTypeModel` object.
+
+Below is a list of steps that should be done to build and register a new action type with the name `Example Action Type`:
+
+1. At any suitable place in Kibana, create a file, which will expose an object implementing interface [ActionTypeModel]:
+```
+import React, { Fragment } from 'react';
+import { i18n } from '@kbn/i18n';
+import {
+  ActionTypeModel,
+  ValidationResult,
+  ActionConnectorFieldsProps,
+  ActionParamsProps,
+} from '../../../types';
+
+interface ExampleActionParams {
+  message: string;
+}
+
+export function getActionType(): ActionTypeModel {
+  return {
+    id: '.example-action',
+    iconClass: 'logoGmail',
+    selectMessage: i18n.translate(
+      'xpack.triggersActionsUI.components.builtinActionTypes.exampleAction.selectMessageText',
+      {
+        defaultMessage: 'Example Action is used to show how to create new action type UI.',
+      }
+    ),
+    actionTypeTitle: i18n.translate(
+      'xpack.triggersActionsUI.components.builtinActionTypes.exampleAction.actionTypeTitle',
+      {
+        defaultMessage: 'Example Action',
+      }
+    ),
+    validateConnector: (action: ExampleActionConnector): ValidationResult => {
+      const validationResult = { errors: {} };
+      const errors = {
+        someConnectorField: new Array<string>(),
+      };
+      validationResult.errors = errors;
+      if (!action.config.someConnectorField) {
+        errors.someConnectorField.push(
+          i18n.translate(
+            'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredSomeConnectorFieldeText',
+            {
+              defaultMessage: 'SomeConnectorField is required.',
+            }
+          )
+        );
+      }
+      return validationResult;
+    },
+    validateParams: (actionParams: ExampleActionParams): ValidationResult => {
+      const validationResult = { errors: {} };
+      const errors = {
+        message: new Array<string>(),
+      };
+      validationResult.errors = errors;
+      if (!actionParams.message?.length) {
+        errors.message.push(
+          i18n.translate(
+            'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredExampleMessageText',
+            {
+              defaultMessage: 'Message is required.',
+            }
+          )
+        );
+      }
+      return validationResult;
+    },
+    actionConnectorFields: ExampleConnectorFields,
+    actionParamsFields: ExampleParamsFields,
+  };
+}
+```
+
+2. Define `actionConnectorFields` as `React.FunctionComponent` - this is the form for action connector.
+```
+import React, { Fragment } from 'react';
+import { i18n } from '@kbn/i18n';
+import { EuiFieldText } from '@elastic/eui';
+import { EuiTextArea } from '@elastic/eui';
+import {
+  ActionTypeModel,
+  ValidationResult,
+  ActionConnectorFieldsProps,
+  ActionParamsProps,
+} from '../../../types';
+
+interface ExampleActionConnector {
+  config: {
+    someConnectorField: string;
+  };
+}
+
+const ExampleConnectorFields: React.FunctionComponent<ActionConnectorFieldsProps<
+  ExampleActionConnector
+>> = ({ action, editActionConfig, errors }) => {
+  const { someConnectorField } = action.config;
+  return (
+    <Fragment>
+      <EuiFieldText
+        fullWidth
+        isInvalid={errors.someConnectorField.length > 0 && someConnectorField !== undefined}
+        name="someConnectorField"
+        value={someConnectorField || ''}
+        onChange={e => {
+          editActionConfig('someConnectorField', e.target.value);
+        }}
+        onBlur={() => {
+          if (!someConnectorField) {
+            editActionConfig('someConnectorField', '');
+          }
+        }}
+      />
+    </Fragment>
+  );
+};
+```
+
+3. Define action type params fields using the property of `ActionTypeModel` `actionParamsFields`: 
+```
+import React, { Fragment } from 'react';
+import { i18n } from '@kbn/i18n';
+import { EuiFieldText } from '@elastic/eui';
+import { EuiTextArea } from '@elastic/eui';
+import {
+  ActionTypeModel,
+  ValidationResult,
+  ActionConnectorFieldsProps,
+  ActionParamsProps,
+} from '../../../types';
+
+interface ExampleActionParams {
+  message: string;
+}
+
+const ExampleParamsFields: React.FunctionComponent<ActionParamsProps<ExampleActionParams>> = ({
+  actionParams,
+  editAction,
+  index,
+  errors,
+}) => {
+  const { message } = actionParams;
+  return (
+    <Fragment>
+      <EuiTextArea
+        fullWidth
+        isInvalid={errors.message.length > 0 && message !== undefined}
+        name="message"
+        value={message || ''}
+        onChange={e => {
+          editAction('message', e.target.value, index);
+        }}
+        onBlur={() => {
+          if (!message) {
+            editAction('message', '', index);
+          }
+        }}
+      />
+    </Fragment>
+  );
+};
+```
+
+4. Extend registration code with the new action type register in the file `x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/index.ts`
+```
+import { getActionType as getExampledActionType } from './example';
+...
+
+...
+actionTypeRegistry.register(getExampledActionType());
+```
+
+After these four steps, the new `Example Action Type` is available in UI of Create connector:
+![Example Action Type is in the select cards list](https://i.imgur.com/PTYdBos.png)
+
+Clicking on the select card for `Example Action Type` will open the connector form that was created in step 2:
+![Example Action Type connector](https://i.imgur.com/KdxAXAs.png)
+
+Example Action Type is in the select cards list of Create Alert flyout:
+![Example Action Type is in the select cards list of Create Alert flyout](https://i.imgur.com/CSRBkFe.png)
+
+Clicking on the select card for `Example Action Type` will open the action type Add Action form:
+![Example Action Type with existing connectors list](https://i.imgur.com/8FA3NAW.png)
+
+or create a new connector:
+![Example Action Type with empty connectors list](https://i.imgur.com/EamA9Xv.png)

From 4f27e1927a2d9d8f8b791275e90eb61b3795947f Mon Sep 17 00:00:00 2001
From: Jonathan Buttner <56361221+jonathan-buttner@users.noreply.github.com>
Date: Tue, 18 Feb 2020 14:22:14 -0500
Subject: [PATCH 036/174] [Endpoint] EDVT-72 EDVT-25 Initial Resolver Backend
 Refactored (#57593)

* Stubbed route, static tree
Changing commit author to me

* Starting the queries

* Built out children api

* using agent id instead of labels and implementing the get node route

* Adding pagination, need to write tests

* Removing track_total_hits because it defaults to 10k

* Allowing null for origin information

* Building out tests

* Adding more response tests

* Adding test for children route

* Forgot to save

* Reverting first commit and keeping my changes to resolver route

* Working search handler tests

* Adding api test

* Trying to figure out the query issue

* Working api tests

* A little refactoring of common types

* Fixing tests

* Some clean up and fixing bad merge

* Working api

* Changing phse0 names

* Refactoring duplicate code in route

* Adding test for count query and fixing api test

* Renaming phase 1 to elastic endpoint

* Removing test files without tests

* Restructuring things

* Building events for process handler

* Working unit tests

* Working integration tests

* Pagination test is failing

* More refactoring

* Add alternative fields to support other datasources

* Working refactored routes

* Strip out 'synthetic id' stuff

* allow ppid to be undefined

* Some clean up and fixing typescript lint errors

* Removing changes to alerts

* Remove the additional query with the _id cursor

* Use Buffer.from instead of new Buffer

* Fixing import

* Fix decoding

* Fix Promise return type

* Fixing linter used before assigned problem

* Removing unused import

* gzipping the test file

* Addressing feedback, more clean next cursor

* Fixing cast for search_after

* Fixing test failure and adding comments

* Fixing timestamp string type failure

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
Co-authored-by: Andrew Stucki <andrew.stucki@gmail.com>
---
 x-pack/plugins/endpoint/common/types.ts       |   42 +
 x-pack/plugins/endpoint/server/plugin.ts      |    7 +-
 .../endpoint/server/routes/resolver.ts        |   42 +
 .../server/routes/resolver/children.ts        |   90 +
 .../server/routes/resolver/lifecycle.ts       |   94 +
 .../server/routes/resolver/queries/base.ts    |   42 +
 .../routes/resolver/queries/children.test.ts  |   94 +
 .../routes/resolver/queries/children.ts       |   74 +
 .../routes/resolver/queries/lifecycle.test.ts |   63 +
 .../routes/resolver/queries/lifecycle.ts      |   64 +
 .../resolver/queries/related_events.test.ts   |   96 +
 .../routes/resolver/queries/related_events.ts |   69 +
 .../server/routes/resolver/related_events.ts  |   73 +
 .../server/routes/resolver/utils/normalize.ts |   33 +
 .../routes/resolver/utils/pagination.ts       |   91 +
 .../api_integration/apis/endpoint/resolver.ts |  224 +-
 .../resolver/api_feature/data.json.gz         |  Bin 0 -> 18309 bytes
 .../resolver/api_feature/mappings.json        | 2703 +++++++++++++++++
 18 files changed, 3888 insertions(+), 13 deletions(-)
 create mode 100644 x-pack/plugins/endpoint/server/routes/resolver.ts
 create mode 100644 x-pack/plugins/endpoint/server/routes/resolver/children.ts
 create mode 100644 x-pack/plugins/endpoint/server/routes/resolver/lifecycle.ts
 create mode 100644 x-pack/plugins/endpoint/server/routes/resolver/queries/base.ts
 create mode 100644 x-pack/plugins/endpoint/server/routes/resolver/queries/children.test.ts
 create mode 100644 x-pack/plugins/endpoint/server/routes/resolver/queries/children.ts
 create mode 100644 x-pack/plugins/endpoint/server/routes/resolver/queries/lifecycle.test.ts
 create mode 100644 x-pack/plugins/endpoint/server/routes/resolver/queries/lifecycle.ts
 create mode 100644 x-pack/plugins/endpoint/server/routes/resolver/queries/related_events.test.ts
 create mode 100644 x-pack/plugins/endpoint/server/routes/resolver/queries/related_events.ts
 create mode 100644 x-pack/plugins/endpoint/server/routes/resolver/related_events.ts
 create mode 100644 x-pack/plugins/endpoint/server/routes/resolver/utils/normalize.ts
 create mode 100644 x-pack/plugins/endpoint/server/routes/resolver/utils/pagination.ts
 create mode 100644 x-pack/test/functional/es_archives/endpoint/resolver/api_feature/data.json.gz
 create mode 100644 x-pack/test/functional/es_archives/endpoint/resolver/api_feature/mappings.json

diff --git a/x-pack/plugins/endpoint/common/types.ts b/x-pack/plugins/endpoint/common/types.ts
index 6fc4d8d79a1c5..f0fd9dc610e4e 100644
--- a/x-pack/plugins/endpoint/common/types.ts
+++ b/x-pack/plugins/endpoint/common/types.ts
@@ -25,6 +25,11 @@ export type ImmutableObject<T> = { readonly [K in keyof T]: Immutable<T[K]> };
 export class EndpointAppConstants {
   static ALERT_INDEX_NAME = 'my-index';
   static ENDPOINT_INDEX_NAME = 'endpoint-agent*';
+  static EVENT_INDEX_NAME = 'endpoint-events-*';
+  /**
+   * Legacy events are stored in indices with endgame-* prefix
+   */
+  static LEGACY_EVENT_INDEX_NAME = 'endgame-*';
 }
 
 export interface AlertResultList {
@@ -117,6 +122,43 @@ export interface EndpointMetadata {
   };
 }
 
+export interface LegacyEndpointEvent {
+  '@timestamp': Date;
+  endgame: {
+    event_type_full: string;
+    event_subtype_full: string;
+    unique_pid: number;
+    unique_ppid: number;
+    serial_event_id: number;
+  };
+  agent: {
+    id: string;
+    type: string;
+  };
+}
+
+export interface EndpointEvent {
+  '@timestamp': Date;
+  event: {
+    category: string;
+    type: string;
+    id: string;
+  };
+  endpoint: {
+    process: {
+      entity_id: string;
+      parent: {
+        entity_id: string;
+      };
+    };
+  };
+  agent: {
+    type: string;
+  };
+}
+
+export type ResolverEvent = EndpointEvent | LegacyEndpointEvent;
+
 /**
  * The PageId type is used for the payload when firing userNavigatedToPage actions
  */
diff --git a/x-pack/plugins/endpoint/server/plugin.ts b/x-pack/plugins/endpoint/server/plugin.ts
index 3fed4ca480b85..afed5199b7d72 100644
--- a/x-pack/plugins/endpoint/server/plugin.ts
+++ b/x-pack/plugins/endpoint/server/plugin.ts
@@ -5,12 +5,14 @@
  */
 import { Plugin, CoreSetup, PluginInitializerContext, Logger } from 'kibana/server';
 import { first } from 'rxjs/operators';
-import { addRoutes } from './routes';
 import { PluginSetupContract as FeaturesPluginSetupContract } from '../../features/server';
 import { createConfig$, EndpointConfigType } from './config';
-import { registerEndpointRoutes } from './routes/endpoints';
 import { EndpointAppContext } from './types';
+
+import { addRoutes } from './routes';
+import { registerEndpointRoutes } from './routes/endpoints';
 import { registerAlertRoutes } from './routes/alerts';
+import { registerResolverRoutes } from './routes/resolver';
 
 export type EndpointPluginStart = void;
 export type EndpointPluginSetup = void;
@@ -69,6 +71,7 @@ export class EndpointPlugin
     const router = core.http.createRouter();
     addRoutes(router);
     registerEndpointRoutes(router, endpointContext);
+    registerResolverRoutes(router, endpointContext);
     registerAlertRoutes(router, endpointContext);
   }
 
diff --git a/x-pack/plugins/endpoint/server/routes/resolver.ts b/x-pack/plugins/endpoint/server/routes/resolver.ts
new file mode 100644
index 0000000000000..946ada51c40e9
--- /dev/null
+++ b/x-pack/plugins/endpoint/server/routes/resolver.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 { IRouter } from 'kibana/server';
+import { EndpointAppContext } from '../types';
+import { handleRelatedEvents, validateRelatedEvents } from './resolver/related_events';
+import { handleChildren, validateChildren } from './resolver/children';
+import { handleLifecycle, validateLifecycle } from './resolver/lifecycle';
+
+export function registerResolverRoutes(router: IRouter, endpointAppContext: EndpointAppContext) {
+  const log = endpointAppContext.logFactory.get('resolver');
+
+  router.get(
+    {
+      path: '/api/endpoint/resolver/{id}/related',
+      validate: validateRelatedEvents,
+      options: { authRequired: true },
+    },
+    handleRelatedEvents(log)
+  );
+
+  router.get(
+    {
+      path: '/api/endpoint/resolver/{id}/children',
+      validate: validateChildren,
+      options: { authRequired: true },
+    },
+    handleChildren(log)
+  );
+
+  router.get(
+    {
+      path: '/api/endpoint/resolver/{id}',
+      validate: validateLifecycle,
+      options: { authRequired: true },
+    },
+    handleLifecycle(log)
+  );
+}
diff --git a/x-pack/plugins/endpoint/server/routes/resolver/children.ts b/x-pack/plugins/endpoint/server/routes/resolver/children.ts
new file mode 100644
index 0000000000000..f97c742b18d67
--- /dev/null
+++ b/x-pack/plugins/endpoint/server/routes/resolver/children.ts
@@ -0,0 +1,90 @@
+/*
+ * 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 _ from 'lodash';
+import { schema } from '@kbn/config-schema';
+import { RequestHandler, Logger } from 'kibana/server';
+import { extractEntityID } from './utils/normalize';
+import { getPaginationParams } from './utils/pagination';
+import { LifecycleQuery } from './queries/lifecycle';
+import { ChildrenQuery } from './queries/children';
+
+interface ChildrenQueryParams {
+  after?: string;
+  limit: number;
+  /**
+   * legacyEndpointID is optional because there are two different types of identifiers:
+   *
+   * Legacy
+   * A legacy Entity ID is made up of the agent.id and unique_pid fields. The client will need to identify if
+   * it's looking at a legacy event and use those fields when making requests to the backend. The
+   * request would be /resolver/{id}?legacyEndpointID=<some uuid>and the {id} would be the unique_pid.
+   *
+   * Elastic Endpoint
+   * When interacting with the new form of data the client doesn't need the legacyEndpointID because it's already a
+   * part of the entityID in the new type of event. So for the same request the client would just hit resolver/{id}
+   * and the {id} would be entityID stored in the event's process.entity_id field.
+   */
+  legacyEndpointID?: string;
+}
+
+interface ChildrenPathParams {
+  id: string;
+}
+
+export const validateChildren = {
+  params: schema.object({ id: schema.string() }),
+  query: schema.object({
+    after: schema.maybe(schema.string()),
+    limit: schema.number({ defaultValue: 10, min: 1, max: 100 }),
+    legacyEndpointID: schema.maybe(schema.string()),
+  }),
+};
+
+export function handleChildren(
+  log: Logger
+): RequestHandler<ChildrenPathParams, ChildrenQueryParams> {
+  return async (context, req, res) => {
+    const {
+      params: { id },
+      query: { limit, after, legacyEndpointID },
+    } = req;
+    try {
+      const pagination = getPaginationParams(limit, after);
+
+      const client = context.core.elasticsearch.dataClient;
+      const childrenQuery = new ChildrenQuery(legacyEndpointID, pagination);
+      const lifecycleQuery = new LifecycleQuery(legacyEndpointID);
+
+      // Retrieve the related child process events for a given process
+      const { total, results: events, nextCursor } = await childrenQuery.search(client, id);
+      const childIDs = events.map(extractEntityID);
+
+      // Retrieve the lifecycle events for the child processes (e.g. started, terminated etc)
+      // this needs to fire after the above since we don't yet have the entity ids until we
+      // run the first query
+      const { results: lifecycleEvents } = await lifecycleQuery.search(client, ...childIDs);
+
+      // group all of the lifecycle events by the child process id
+      const lifecycleGroups = Object.values(_.groupBy(lifecycleEvents, extractEntityID));
+      const children = lifecycleGroups.map(group => ({ lifecycle: group }));
+
+      return res.ok({
+        body: {
+          children,
+          pagination: {
+            total,
+            next: nextCursor,
+            limit,
+          },
+        },
+      });
+    } catch (err) {
+      log.warn(err);
+      return res.internalError({ body: err });
+    }
+  };
+}
diff --git a/x-pack/plugins/endpoint/server/routes/resolver/lifecycle.ts b/x-pack/plugins/endpoint/server/routes/resolver/lifecycle.ts
new file mode 100644
index 0000000000000..9895344174014
--- /dev/null
+++ b/x-pack/plugins/endpoint/server/routes/resolver/lifecycle.ts
@@ -0,0 +1,94 @@
+/*
+ * 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 _ from 'lodash';
+import { schema } from '@kbn/config-schema';
+import { RequestHandler, Logger } from 'kibana/server';
+import { extractParentEntityID } from './utils/normalize';
+import { LifecycleQuery } from './queries/lifecycle';
+import { ResolverEvent } from '../../../common/types';
+
+interface LifecycleQueryParams {
+  ancestors: number;
+  /**
+   * legacyEndpointID is optional because there are two different types of identifiers:
+   *
+   * Legacy
+   * A legacy Entity ID is made up of the agent.id and unique_pid fields. The client will need to identify if
+   * it's looking at a legacy event and use those fields when making requests to the backend. The
+   * request would be /resolver/{id}?legacyEndpointID=<some uuid>and the {id} would be the unique_pid.
+   *
+   * Elastic Endpoint
+   * When interacting with the new form of data the client doesn't need the legacyEndpointID because it's already a
+   * part of the entityID in the new type of event. So for the same request the client would just hit resolver/{id}
+   * and the {id} would be entityID stored in the event's process.entity_id field.
+   */
+  legacyEndpointID?: string;
+}
+
+interface LifecyclePathParams {
+  id: string;
+}
+
+export const validateLifecycle = {
+  params: schema.object({ id: schema.string() }),
+  query: schema.object({
+    ancestors: schema.number({ defaultValue: 0, min: 0, max: 10 }),
+    legacyEndpointID: schema.maybe(schema.string()),
+  }),
+};
+
+function getParentEntityID(results: ResolverEvent[]) {
+  return results.length === 0 ? undefined : extractParentEntityID(results[0]);
+}
+
+export function handleLifecycle(
+  log: Logger
+): RequestHandler<LifecyclePathParams, LifecycleQueryParams> {
+  return async (context, req, res) => {
+    const {
+      params: { id },
+      query: { ancestors, legacyEndpointID },
+    } = req;
+    try {
+      const ancestorLifecycles = [];
+      const client = context.core.elasticsearch.dataClient;
+
+      const lifecycleQuery = new LifecycleQuery(legacyEndpointID);
+      const { results: processLifecycle } = await lifecycleQuery.search(client, id);
+      let nextParentID = getParentEntityID(processLifecycle);
+
+      if (nextParentID) {
+        for (let i = 0; i < ancestors; i++) {
+          const { results: lifecycle } = await lifecycleQuery.search(client, nextParentID);
+          nextParentID = getParentEntityID(lifecycle);
+
+          if (!nextParentID) {
+            break;
+          }
+
+          ancestorLifecycles.push({
+            lifecycle,
+          });
+        }
+      }
+
+      return res.ok({
+        body: {
+          lifecycle: processLifecycle,
+          ancestors: ancestorLifecycles,
+          pagination: {
+            next: nextParentID || null,
+            ancestors,
+          },
+        },
+      });
+    } catch (err) {
+      log.warn(err);
+      return res.internalError({ body: err });
+    }
+  };
+}
diff --git a/x-pack/plugins/endpoint/server/routes/resolver/queries/base.ts b/x-pack/plugins/endpoint/server/routes/resolver/queries/base.ts
new file mode 100644
index 0000000000000..be83efc39ca4c
--- /dev/null
+++ b/x-pack/plugins/endpoint/server/routes/resolver/queries/base.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 { IScopedClusterClient } from 'kibana/server';
+import { EndpointAppConstants } from '../../../../common/types';
+import { paginate, paginatedResults, PaginationParams } from '../utils/pagination';
+import { JsonObject } from '../../../../../../../src/plugins/kibana_utils/public';
+
+export abstract class ResolverQuery {
+  constructor(
+    private readonly endpointID?: string,
+    private readonly pagination?: PaginationParams
+  ) {}
+
+  protected paginateBy(field: string, query: JsonObject) {
+    if (!this.pagination) {
+      return query;
+    }
+    return paginate(this.pagination, field, query);
+  }
+
+  build(...ids: string[]) {
+    if (this.endpointID) {
+      return this.legacyQuery(this.endpointID, ids, EndpointAppConstants.LEGACY_EVENT_INDEX_NAME);
+    }
+    return this.query(ids, EndpointAppConstants.EVENT_INDEX_NAME);
+  }
+
+  async search(client: IScopedClusterClient, ...ids: string[]) {
+    return paginatedResults(await client.callAsCurrentUser('search', this.build(...ids)));
+  }
+
+  protected abstract legacyQuery(
+    endpointID: string,
+    uniquePIDs: string[],
+    index: string
+  ): JsonObject;
+  protected abstract query(entityIDs: string[], index: string): JsonObject;
+}
diff --git a/x-pack/plugins/endpoint/server/routes/resolver/queries/children.test.ts b/x-pack/plugins/endpoint/server/routes/resolver/queries/children.test.ts
new file mode 100644
index 0000000000000..2dd2e0c2d1d5f
--- /dev/null
+++ b/x-pack/plugins/endpoint/server/routes/resolver/queries/children.test.ts
@@ -0,0 +1,94 @@
+/*
+ * 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 { ChildrenQuery } from './children';
+import { EndpointAppConstants } from '../../../../common/types';
+
+describe('children events query', () => {
+  it('generates the correct legacy queries', () => {
+    const timestamp = new Date();
+    expect(
+      new ChildrenQuery('awesome-id', { size: 1, timestamp, eventID: 'foo' }).build('5')
+    ).toStrictEqual({
+      body: {
+        query: {
+          bool: {
+            filter: [
+              {
+                terms: { 'endgame.unique_ppid': ['5'] },
+              },
+              {
+                term: { 'agent.id': 'awesome-id' },
+              },
+              {
+                term: { 'event.category': 'process' },
+              },
+              {
+                term: { 'event.type': 'process_start' },
+              },
+            ],
+          },
+        },
+        aggs: {
+          total: {
+            value_count: {
+              field: 'endgame.serial_event_id',
+            },
+          },
+        },
+        search_after: [timestamp.getTime(), 'foo'],
+        size: 1,
+        sort: [{ '@timestamp': 'asc' }, { 'endgame.serial_event_id': 'asc' }],
+      },
+      index: EndpointAppConstants.LEGACY_EVENT_INDEX_NAME,
+    });
+  });
+
+  it('generates the correct non-legacy queries', () => {
+    const timestamp = new Date();
+
+    expect(
+      new ChildrenQuery(undefined, { size: 1, timestamp, eventID: 'bar' }).build('baz')
+    ).toStrictEqual({
+      body: {
+        query: {
+          bool: {
+            filter: [
+              {
+                bool: {
+                  should: [
+                    {
+                      terms: { 'endpoint.process.parent.entity_id': ['baz'] },
+                    },
+                    {
+                      terms: { 'process.parent.entity_id': ['baz'] },
+                    },
+                  ],
+                },
+              },
+              {
+                term: { 'event.category': 'process' },
+              },
+              {
+                term: { 'event.type': 'start' },
+              },
+            ],
+          },
+        },
+        aggs: {
+          total: {
+            value_count: {
+              field: 'event.id',
+            },
+          },
+        },
+        search_after: [timestamp.getTime(), 'bar'],
+        size: 1,
+        sort: [{ '@timestamp': 'asc' }, { 'event.id': 'asc' }],
+      },
+      index: EndpointAppConstants.EVENT_INDEX_NAME,
+    });
+  });
+});
diff --git a/x-pack/plugins/endpoint/server/routes/resolver/queries/children.ts b/x-pack/plugins/endpoint/server/routes/resolver/queries/children.ts
new file mode 100644
index 0000000000000..6d084a0cf20e5
--- /dev/null
+++ b/x-pack/plugins/endpoint/server/routes/resolver/queries/children.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 { ResolverQuery } from './base';
+
+export class ChildrenQuery extends ResolverQuery {
+  protected legacyQuery(endpointID: string, uniquePIDs: string[], index: string) {
+    return {
+      body: this.paginateBy('endgame.serial_event_id', {
+        query: {
+          bool: {
+            filter: [
+              {
+                terms: { 'endgame.unique_ppid': uniquePIDs },
+              },
+              {
+                term: { 'agent.id': endpointID },
+              },
+              {
+                term: { 'event.category': 'process' },
+              },
+              {
+                // Corner case, we could only have a process_running or process_terminated
+                // so to solve this we'll probably want to either search for all of them and only return one if that's
+                // possible in elastic search or in memory pull out a single event to return
+                // https://github.com/elastic/endpoint-app-team/issues/168
+                term: { 'event.type': 'process_start' },
+              },
+            ],
+          },
+        },
+      }),
+      index,
+    };
+  }
+
+  protected query(entityIDs: string[], index: string) {
+    return {
+      body: this.paginateBy('event.id', {
+        query: {
+          bool: {
+            filter: [
+              {
+                bool: {
+                  should: [
+                    {
+                      terms: { 'endpoint.process.parent.entity_id': entityIDs },
+                    },
+                    {
+                      terms: { 'process.parent.entity_id': entityIDs },
+                    },
+                  ],
+                },
+              },
+              {
+                term: { 'event.category': 'process' },
+              },
+              {
+                // Corner case, we could only have a process_running or process_terminated
+                // so to solve this we'll probably want to either search for all of them and only return one if that's
+                // possible in elastic search or in memory pull out a single event to return
+                // https://github.com/elastic/endpoint-app-team/issues/168
+                term: { 'event.type': 'start' },
+              },
+            ],
+          },
+        },
+      }),
+      index,
+    };
+  }
+}
diff --git a/x-pack/plugins/endpoint/server/routes/resolver/queries/lifecycle.test.ts b/x-pack/plugins/endpoint/server/routes/resolver/queries/lifecycle.test.ts
new file mode 100644
index 0000000000000..b1b47bfb9de7f
--- /dev/null
+++ b/x-pack/plugins/endpoint/server/routes/resolver/queries/lifecycle.test.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.
+ */
+import { EndpointAppConstants } from '../../../../common/types';
+import { LifecycleQuery } from './lifecycle';
+
+describe('lifecycle query', () => {
+  it('generates the correct legacy queries', () => {
+    expect(new LifecycleQuery('awesome-id').build('5')).toStrictEqual({
+      body: {
+        query: {
+          bool: {
+            filter: [
+              {
+                terms: { 'endgame.unique_pid': ['5'] },
+              },
+              {
+                term: { 'agent.id': 'awesome-id' },
+              },
+              {
+                term: { 'event.category': 'process' },
+              },
+            ],
+          },
+        },
+        sort: [{ '@timestamp': 'asc' }],
+      },
+      index: EndpointAppConstants.LEGACY_EVENT_INDEX_NAME,
+    });
+  });
+
+  it('generates the correct non-legacy queries', () => {
+    expect(new LifecycleQuery().build('baz')).toStrictEqual({
+      body: {
+        query: {
+          bool: {
+            filter: [
+              {
+                bool: {
+                  should: [
+                    {
+                      terms: { 'endpoint.process.entity_id': ['baz'] },
+                    },
+                    {
+                      terms: { 'process.entity_id': ['baz'] },
+                    },
+                  ],
+                },
+              },
+              {
+                term: { 'event.category': 'process' },
+              },
+            ],
+          },
+        },
+        sort: [{ '@timestamp': 'asc' }],
+      },
+      index: EndpointAppConstants.EVENT_INDEX_NAME,
+    });
+  });
+});
diff --git a/x-pack/plugins/endpoint/server/routes/resolver/queries/lifecycle.ts b/x-pack/plugins/endpoint/server/routes/resolver/queries/lifecycle.ts
new file mode 100644
index 0000000000000..290c601e0e9d8
--- /dev/null
+++ b/x-pack/plugins/endpoint/server/routes/resolver/queries/lifecycle.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 { ResolverQuery } from './base';
+import { JsonObject } from '../../../../../../../src/plugins/kibana_utils/public';
+
+// consider limiting the response size to a reasonable value in case we have a bunch of lifecycle events
+export class LifecycleQuery extends ResolverQuery {
+  protected legacyQuery(endpointID: string, uniquePIDs: string[], index: string): JsonObject {
+    return {
+      body: {
+        query: {
+          bool: {
+            filter: [
+              {
+                terms: { 'endgame.unique_pid': uniquePIDs },
+              },
+              {
+                term: { 'agent.id': endpointID },
+              },
+              {
+                term: { 'event.category': 'process' },
+              },
+            ],
+          },
+        },
+        sort: [{ '@timestamp': 'asc' }],
+      },
+      index,
+    };
+  }
+
+  protected query(entityIDs: string[], index: string): JsonObject {
+    return {
+      body: {
+        query: {
+          bool: {
+            filter: [
+              {
+                bool: {
+                  should: [
+                    {
+                      terms: { 'endpoint.process.entity_id': entityIDs },
+                    },
+                    {
+                      terms: { 'process.entity_id': entityIDs },
+                    },
+                  ],
+                },
+              },
+              {
+                term: { 'event.category': 'process' },
+              },
+            ],
+          },
+        },
+        sort: [{ '@timestamp': 'asc' }],
+      },
+      index,
+    };
+  }
+}
diff --git a/x-pack/plugins/endpoint/server/routes/resolver/queries/related_events.test.ts b/x-pack/plugins/endpoint/server/routes/resolver/queries/related_events.test.ts
new file mode 100644
index 0000000000000..8ef680a168310
--- /dev/null
+++ b/x-pack/plugins/endpoint/server/routes/resolver/queries/related_events.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 { RelatedEventsQuery } from './related_events';
+import { EndpointAppConstants } from '../../../../common/types';
+
+describe('related events query', () => {
+  it('generates the correct legacy queries', () => {
+    const timestamp = new Date();
+    expect(
+      new RelatedEventsQuery('awesome-id', { size: 1, timestamp, eventID: 'foo' }).build('5')
+    ).toStrictEqual({
+      body: {
+        query: {
+          bool: {
+            filter: [
+              {
+                terms: { 'endgame.unique_pid': ['5'] },
+              },
+              {
+                term: { 'agent.id': 'awesome-id' },
+              },
+              {
+                bool: {
+                  must_not: {
+                    term: { 'event.category': 'process' },
+                  },
+                },
+              },
+            ],
+          },
+        },
+        aggs: {
+          total: {
+            value_count: {
+              field: 'endgame.serial_event_id',
+            },
+          },
+        },
+        search_after: [timestamp.getTime(), 'foo'],
+        size: 1,
+        sort: [{ '@timestamp': 'asc' }, { 'endgame.serial_event_id': 'asc' }],
+      },
+      index: EndpointAppConstants.LEGACY_EVENT_INDEX_NAME,
+    });
+  });
+
+  it('generates the correct non-legacy queries', () => {
+    const timestamp = new Date();
+
+    expect(
+      new RelatedEventsQuery(undefined, { size: 1, timestamp, eventID: 'bar' }).build('baz')
+    ).toStrictEqual({
+      body: {
+        query: {
+          bool: {
+            filter: [
+              {
+                bool: {
+                  should: [
+                    {
+                      terms: { 'endpoint.process.entity_id': ['baz'] },
+                    },
+                    {
+                      terms: { 'process.entity_id': ['baz'] },
+                    },
+                  ],
+                },
+              },
+              {
+                bool: {
+                  must_not: {
+                    term: { 'event.category': 'process' },
+                  },
+                },
+              },
+            ],
+          },
+        },
+        aggs: {
+          total: {
+            value_count: {
+              field: 'event.id',
+            },
+          },
+        },
+        search_after: [timestamp.getTime(), 'bar'],
+        size: 1,
+        sort: [{ '@timestamp': 'asc' }, { 'event.id': 'asc' }],
+      },
+      index: EndpointAppConstants.EVENT_INDEX_NAME,
+    });
+  });
+});
diff --git a/x-pack/plugins/endpoint/server/routes/resolver/queries/related_events.ts b/x-pack/plugins/endpoint/server/routes/resolver/queries/related_events.ts
new file mode 100644
index 0000000000000..cc5afe8face8d
--- /dev/null
+++ b/x-pack/plugins/endpoint/server/routes/resolver/queries/related_events.ts
@@ -0,0 +1,69 @@
+/*
+ * 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 { ResolverQuery } from './base';
+import { JsonObject } from '../../../../../../../src/plugins/kibana_utils/public';
+
+export class RelatedEventsQuery extends ResolverQuery {
+  protected legacyQuery(endpointID: string, uniquePIDs: string[], index: string): JsonObject {
+    return {
+      body: this.paginateBy('endgame.serial_event_id', {
+        query: {
+          bool: {
+            filter: [
+              {
+                terms: { 'endgame.unique_pid': uniquePIDs },
+              },
+              {
+                term: { 'agent.id': endpointID },
+              },
+              {
+                bool: {
+                  must_not: {
+                    term: { 'event.category': 'process' },
+                  },
+                },
+              },
+            ],
+          },
+        },
+      }),
+      index,
+    };
+  }
+
+  protected query(entityIDs: string[], index: string): JsonObject {
+    return {
+      body: this.paginateBy('event.id', {
+        query: {
+          bool: {
+            filter: [
+              {
+                bool: {
+                  should: [
+                    {
+                      terms: { 'endpoint.process.entity_id': entityIDs },
+                    },
+                    {
+                      terms: { 'process.entity_id': entityIDs },
+                    },
+                  ],
+                },
+              },
+              {
+                bool: {
+                  must_not: {
+                    term: { 'event.category': 'process' },
+                  },
+                },
+              },
+            ],
+          },
+        },
+      }),
+      index,
+    };
+  }
+}
diff --git a/x-pack/plugins/endpoint/server/routes/resolver/related_events.ts b/x-pack/plugins/endpoint/server/routes/resolver/related_events.ts
new file mode 100644
index 0000000000000..804400522065c
--- /dev/null
+++ b/x-pack/plugins/endpoint/server/routes/resolver/related_events.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 { schema } from '@kbn/config-schema';
+import { RequestHandler, Logger } from 'kibana/server';
+import { getPaginationParams } from './utils/pagination';
+import { RelatedEventsQuery } from './queries/related_events';
+
+interface RelatedEventsQueryParams {
+  after?: string;
+  limit: number;
+  /**
+   * legacyEndpointID is optional because there are two different types of identifiers:
+   *
+   * Legacy
+   * A legacy Entity ID is made up of the agent.id and unique_pid fields. The client will need to identify if
+   * it's looking at a legacy event and use those fields when making requests to the backend. The
+   * request would be /resolver/{id}?legacyEndpointID=<some uuid>and the {id} would be the unique_pid.
+   *
+   * Elastic Endpoint
+   * When interacting with the new form of data the client doesn't need the legacyEndpointID because it's already a
+   * part of the entityID in the new type of event. So for the same request the client would just hit resolver/{id}
+   * and the {id} would be entityID stored in the event's process.entity_id field.
+   */
+  legacyEndpointID?: string;
+}
+
+interface RelatedEventsPathParams {
+  id: string;
+}
+
+export const validateRelatedEvents = {
+  params: schema.object({ id: schema.string() }),
+  query: schema.object({
+    after: schema.maybe(schema.string()),
+    limit: schema.number({ defaultValue: 100, min: 1, max: 1000 }),
+    legacyEndpointID: schema.maybe(schema.string()),
+  }),
+};
+
+export function handleRelatedEvents(
+  log: Logger
+): RequestHandler<RelatedEventsPathParams, RelatedEventsQueryParams> {
+  return async (context, req, res) => {
+    const {
+      params: { id },
+      query: { limit, after, legacyEndpointID },
+    } = req;
+    try {
+      const pagination = getPaginationParams(limit, after);
+
+      const client = context.core.elasticsearch.dataClient;
+      // Retrieve the related non-process events for a given process
+      const relatedEventsQuery = new RelatedEventsQuery(legacyEndpointID, pagination);
+      const relatedEvents = await relatedEventsQuery.search(client, id);
+
+      const { total, results: events, nextCursor } = relatedEvents;
+
+      return res.ok({
+        body: {
+          events,
+          pagination: { total, next: nextCursor, limit },
+        },
+      });
+    } catch (err) {
+      log.warn(err);
+      return res.internalError({ body: err });
+    }
+  };
+}
diff --git a/x-pack/plugins/endpoint/server/routes/resolver/utils/normalize.ts b/x-pack/plugins/endpoint/server/routes/resolver/utils/normalize.ts
new file mode 100644
index 0000000000000..86dd4c053e8fa
--- /dev/null
+++ b/x-pack/plugins/endpoint/server/routes/resolver/utils/normalize.ts
@@ -0,0 +1,33 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { ResolverEvent, LegacyEndpointEvent } from '../../../../common/types';
+
+function isLegacyData(data: ResolverEvent): data is LegacyEndpointEvent {
+  return data.agent.type === 'endgame';
+}
+
+export function extractEventID(event: ResolverEvent) {
+  if (isLegacyData(event)) {
+    return String(event.endgame.serial_event_id);
+  }
+  return event.event.id;
+}
+
+export function extractEntityID(event: ResolverEvent) {
+  if (isLegacyData(event)) {
+    return String(event.endgame.unique_pid);
+  }
+  return event.endpoint.process.entity_id;
+}
+
+export function extractParentEntityID(event: ResolverEvent) {
+  if (isLegacyData(event)) {
+    const ppid = event.endgame.unique_ppid;
+    return ppid && String(ppid); // if unique_ppid is undefined return undefined
+  }
+  return event.endpoint.process.parent?.entity_id;
+}
diff --git a/x-pack/plugins/endpoint/server/routes/resolver/utils/pagination.ts b/x-pack/plugins/endpoint/server/routes/resolver/utils/pagination.ts
new file mode 100644
index 0000000000000..33eb698479308
--- /dev/null
+++ b/x-pack/plugins/endpoint/server/routes/resolver/utils/pagination.ts
@@ -0,0 +1,91 @@
+/*
+ * 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 { SearchResponse } from 'elasticsearch';
+import { ResolverEvent } from '../../../../common/types';
+import { extractEventID } from './normalize';
+import { JsonObject } from '../../../../../../../src/plugins/kibana_utils/public';
+
+export interface PaginationParams {
+  size: number;
+  timestamp?: Date;
+  eventID?: string;
+}
+
+interface PaginationCursor {
+  timestamp: Date;
+  eventID: string;
+}
+
+function urlEncodeCursor(data: PaginationCursor) {
+  const value = JSON.stringify(data);
+  return Buffer.from(value, 'utf8')
+    .toString('base64')
+    .replace(/\+/g, '-')
+    .replace(/\//g, '_')
+    .replace(/=+$/g, '');
+}
+
+function urlDecodeCursor(value: string): PaginationCursor {
+  value = value.replace(/\-/g, '+').replace(/_/g, '/');
+  const data = Buffer.from(value, 'base64').toString('utf8');
+  const { timestamp, eventID } = JSON.parse(data);
+  // take some extra care to only grab the things we want
+  // convert the timestamp string to date object
+  return { timestamp: new Date(timestamp), eventID };
+}
+
+export function getPaginationParams(limit: number, after?: string): PaginationParams {
+  if (after) {
+    try {
+      const cursor = urlDecodeCursor(after);
+      if (cursor.timestamp && cursor.eventID) {
+        return {
+          size: limit,
+          timestamp: cursor.timestamp,
+          eventID: cursor.eventID,
+        };
+      }
+    } catch (err) {
+      /* tslint:disable:no-empty */
+    } // ignore invalid cursor values
+  }
+  return { size: limit };
+}
+
+export function paginate(pagination: PaginationParams, field: string, query: JsonObject) {
+  const { size, timestamp, eventID } = pagination;
+  query.sort = [{ '@timestamp': 'asc' }, { [field]: 'asc' }];
+  query.aggs = { total: { value_count: { field } } };
+  query.size = size;
+  if (timestamp && eventID) {
+    query.search_after = [timestamp.getTime(), eventID] as Array<number | string>;
+  }
+  return query;
+}
+
+export function paginatedResults(
+  response: SearchResponse<ResolverEvent>
+): { total: number; results: ResolverEvent[]; nextCursor: string | null } {
+  const total = response.aggregations?.total?.value || 0;
+  if (response.hits.hits.length === 0) {
+    return { total, results: [], nextCursor: null };
+  }
+
+  const results: ResolverEvent[] = [];
+  for (const hit of response.hits.hits) {
+    results.push(hit._source);
+  }
+
+  // results will be at least 1 because of length check at the top of the function
+  const next = results[results.length - 1];
+  const cursor = {
+    timestamp: next['@timestamp'],
+    eventID: extractEventID(next),
+  };
+
+  return { total, results, nextCursor: urlEncodeCursor(cursor) };
+}
diff --git a/x-pack/test/api_integration/apis/endpoint/resolver.ts b/x-pack/test/api_integration/apis/endpoint/resolver.ts
index 96d16e0d76e40..c1e9240c09951 100644
--- a/x-pack/test/api_integration/apis/endpoint/resolver.ts
+++ b/x-pack/test/api_integration/apis/endpoint/resolver.ts
@@ -6,24 +6,226 @@
 
 import expect from '@kbn/expect';
 import { FtrProviderContext } from '../../ftr_provider_context';
-
 const commonHeaders = {
-  Accept: 'application/json',
+  accept: 'application/json',
   'kbn-xsrf': 'some-xsrf-token',
 };
 
 // eslint-disable-next-line import/no-default-export
 export default function resolverAPIIntegrationTests({ getService }: FtrProviderContext) {
   const supertest = getService('supertest');
-  describe('Resolver api', function() {
-    it('should respond to hello-world', async function() {
-      const { body } = await supertest
-        .get('/api/endpoint/hello-world')
-        .set(commonHeaders)
-        .expect('Content-Type', /application\/json/)
-        .expect(200);
-
-      expect(body).to.eql({ hello: 'world' });
+  const esArchiver = getService('esArchiver');
+
+  describe('Resolver', () => {
+    before(() => esArchiver.load('endpoint/resolver/api_feature'));
+    after(() => esArchiver.unload('endpoint/resolver/api_feature'));
+
+    describe('related events endpoint', () => {
+      const endpointID = '5a0c957f-b8e7-4538-965e-57e8bb86ad3a';
+      const entityID = '94042';
+      const cursor = 'eyJ0aW1lc3RhbXAiOjE1ODE0NTYyNTUwMDAsImV2ZW50SUQiOiI5NDA0MyJ9';
+
+      it('should return details for the root node', async () => {
+        const { body } = await supertest
+          .get(`/api/endpoint/resolver/${entityID}/related?legacyEndpointID=${endpointID}`)
+          .set(commonHeaders)
+          .expect(200);
+        expect(body.events.length).to.eql(1);
+        expect(body.pagination.next).to.eql(cursor);
+        expect(body.pagination.total).to.eql(1);
+        // default limit
+        expect(body.pagination.limit).to.eql(100);
+      });
+
+      it('returns no values when there is no more data', async () => {
+        const { body } = await supertest
+          // after is set to the document id of the last event so there shouldn't be any more after it
+          .get(
+            `/api/endpoint/resolver/${entityID}/related?legacyEndpointID=${endpointID}&after=${cursor}`
+          )
+          .set(commonHeaders)
+          .expect(200);
+        expect(body.events).be.empty();
+        expect(body.pagination.next).to.eql(null);
+        expect(body.pagination.total).to.eql(1);
+      });
+
+      it('should return the first page of information when the cursor is invalid', async () => {
+        const { body } = await supertest
+          .get(
+            `/api/endpoint/resolver/${entityID}/related?legacyEndpointID=${endpointID}&after=blah`
+          )
+          .set(commonHeaders)
+          .expect(200);
+        expect(body.pagination.total).to.eql(1);
+        expect(body.pagination.next).to.not.eql(null);
+      });
+
+      it('should error on invalid pagination values', async () => {
+        await supertest
+          .get(`/api/endpoint/resolver/${entityID}/related?limit=0`)
+          .set(commonHeaders)
+          .expect(400);
+        await supertest
+          .get(`/api/endpoint/resolver/${entityID}/related?limit=2000`)
+          .set(commonHeaders)
+          .expect(400);
+        await supertest
+          .get(`/api/endpoint/resolver/${entityID}/related?limit=-1`)
+          .set(commonHeaders)
+          .expect(400);
+      });
+
+      it('should not find any events', async () => {
+        const { body } = await supertest
+          .get(`/api/endpoint/resolver/5555/related`)
+          .set(commonHeaders)
+          .expect(200);
+        expect(body.pagination.total).to.eql(0);
+        expect(body.pagination.next).to.eql(null);
+        expect(body.events).to.be.empty();
+      });
+
+      it('should return no results for an invalid endpoint ID', async () => {
+        const { body } = await supertest
+          .get(`/api/endpoint/resolver/${entityID}/related?legacyEndpointID=foo`)
+          .set(commonHeaders)
+          .expect(200);
+        expect(body.pagination.total).to.eql(0);
+        expect(body.pagination.next).to.eql(null);
+        expect(body.events).to.be.empty();
+      });
+    });
+
+    describe('lifecycle events endpoint', () => {
+      const endpointID = '5a0c957f-b8e7-4538-965e-57e8bb86ad3a';
+      const entityID = '94042';
+
+      it('should return details for the root node', async () => {
+        const { body } = await supertest
+          .get(`/api/endpoint/resolver/${entityID}?legacyEndpointID=${endpointID}&ancestors=5`)
+          .set(commonHeaders)
+          .expect(200);
+        expect(body.lifecycle.length).to.eql(2);
+        expect(body.ancestors.length).to.eql(1);
+        expect(body.pagination.next).to.eql(null);
+        // 5 is default parameter
+        expect(body.pagination.ancestors).to.eql(5);
+      });
+
+      it('should have a populated next parameter', async () => {
+        const { body } = await supertest
+          .get(`/api/endpoint/resolver/${entityID}?legacyEndpointID=${endpointID}`)
+          .set(commonHeaders)
+          .expect(200);
+        expect(body.pagination.next).to.eql('94041');
+      });
+
+      it('should handle an ancestors param request', async () => {
+        let { body } = await supertest
+          .get(`/api/endpoint/resolver/${entityID}?legacyEndpointID=${endpointID}`)
+          .set(commonHeaders)
+          .expect(200);
+        const next = body.pagination.next;
+
+        ({ body } = await supertest
+          .get(`/api/endpoint/resolver/${next}?legacyEndpointID=${endpointID}&ancestors=1`)
+          .set(commonHeaders)
+          .expect(200));
+        expect(body.lifecycle.length).to.eql(1);
+        expect(body.ancestors.length).to.eql(0);
+        expect(body.pagination.next).to.eql(null);
+      });
+
+      it('should handle an invalid id', async () => {
+        const { body } = await supertest
+          .get(`/api/endpoint/resolver/alskdjflasj`)
+          .set(commonHeaders)
+          .expect(200);
+        expect(body.lifecycle.length).to.eql(0);
+        expect(body.ancestors.length).to.eql(0);
+        expect(body.pagination.next).to.eql(null);
+      });
+    });
+
+    describe('children endpoint', () => {
+      const endpointID = '5a0c957f-b8e7-4538-965e-57e8bb86ad3a';
+      const entityID = '94041';
+      const cursor = 'eyJ0aW1lc3RhbXAiOjE1ODE0NTYyNTUwMDAsImV2ZW50SUQiOiI5NDA0MiJ9';
+
+      it('returns child process lifecycle events', async () => {
+        const { body } = await supertest
+          .get(`/api/endpoint/resolver/${entityID}/children?legacyEndpointID=${endpointID}`)
+          .set(commonHeaders)
+          .expect(200);
+        expect(body.pagination.total).to.eql(1);
+        expect(body.pagination.next).to.eql(cursor);
+        // default limit
+        expect(body.pagination.limit).to.eql(10);
+
+        expect(body.children.length).to.eql(1);
+        expect(body.children[0].lifecycle.length).to.eql(2);
+        expect(body.children[0].lifecycle[0].endgame.unique_pid).to.eql(94042);
+      });
+
+      it('returns no values when there is no more data', async () => {
+        const { body } = await supertest
+          // after is set to the document id of the last event so there shouldn't be any more after it
+          .get(
+            `/api/endpoint/resolver/${entityID}/children?legacyEndpointID=${endpointID}&after=${cursor}`
+          )
+          .set(commonHeaders)
+          .expect(200);
+        expect(body.children).be.empty();
+        expect(body.pagination.next).to.eql(null);
+        expect(body.pagination.total).to.eql(1);
+      });
+
+      it('returns the first page of information when the cursor is invalid', async () => {
+        const { body } = await supertest
+          .get(
+            `/api/endpoint/resolver/${entityID}/children?legacyEndpointID=${endpointID}&after=blah`
+          )
+          .set(commonHeaders)
+          .expect(200);
+        expect(body.pagination.total).to.eql(1);
+        expect(body.pagination.next).to.not.eql(null);
+      });
+
+      it('errors on invalid pagination values', async () => {
+        await supertest
+          .get(`/api/endpoint/resolver/${entityID}/children?limit=0`)
+          .set(commonHeaders)
+          .expect(400);
+        await supertest
+          .get(`/api/endpoint/resolver/${entityID}/children?limit=2000`)
+          .set(commonHeaders)
+          .expect(400);
+        await supertest
+          .get(`/api/endpoint/resolver/${entityID}/children?limit=-1`)
+          .set(commonHeaders)
+          .expect(400);
+      });
+
+      it('returns empty events without a matching entity id', async () => {
+        const { body } = await supertest
+          .get(`/api/endpoint/resolver/5555/children`)
+          .set(commonHeaders)
+          .expect(200);
+        expect(body.pagination.total).to.eql(0);
+        expect(body.pagination.next).to.eql(null);
+        expect(body.children).to.be.empty();
+      });
+
+      it('returns empty events with an invalid endpoint id', async () => {
+        const { body } = await supertest
+          .get(`/api/endpoint/resolver/${entityID}/children?legacyEndpointID=foo`)
+          .set(commonHeaders)
+          .expect(200);
+        expect(body.pagination.total).to.eql(0);
+        expect(body.pagination.next).to.eql(null);
+        expect(body.children).to.be.empty();
+      });
     });
   });
 }
diff --git a/x-pack/test/functional/es_archives/endpoint/resolver/api_feature/data.json.gz b/x-pack/test/functional/es_archives/endpoint/resolver/api_feature/data.json.gz
new file mode 100644
index 0000000000000000000000000000000000000000..92e4af68bf22e3e13f31cea08c3514ce465f0a6d
GIT binary patch
literal 18309
zcmZ^}Wl&ws8Z?>^+@0XIafgjNA;881K{oCl+}&L_PSD^4cXxMp4ek!XFYh_$z4umq
z_0{ult*V(?Gqbw;>1omkczE@737rq0jPxD#nJpY_tUsJ&SSqhb%@1r$6lS~DuvCj|
zuxQ?X7hDCAeH8PJEn_Ni`V?TZSnZdZ_Hweqz8F_3!PTW&=~ZB10qZ1VUEDJ9aQk+%
zTOpj3l(aoy()e7Fzni_q^2X6L$vG!)zsvFz@=}8dD3&bSzL4?q$|bBrjX64<SgSc`
zO%@#bhSnVdP*z7Pmu1q@{|#O73n$?@;HhajN`B<+LCa(JZCjejYM@}gR*g+9#jZTA
zlKZ75t4}NFmd9x#TVRz;*8#uGl|F$XQn~II44PUaa^gOnK9oh$+rxGFtELbhTgEJk
z=BadUX5O@lyjtIt!}3Oe0d##zVsltlbv~k@e>P72%23<qvT2p4J*hm>Gr@j+NTkvN
z{aErfH(}Y17rrEgTKJ@msgIzFy%lND70h{u^<Mq#idM9zpX1*5EhXM=TEv&eX%v<U
zcc*|=^D>D-H?R-=sQvG$(BzB<KA4Gv+MfB5n1kg4`=v8ikczHEh6U%4in;Sr{zlP4
zyd>b9a6rEdU-!;_-)3S}6K(rR;+QL|Vd{oZR0&>oT4jNOS2VnUrJkVur9D82n{@kF
zBBONJShAoXtRi04GQ-(&?Dy<~&M-0evqVK`)rKrhotU!IZ1>D=H{yZukk3=$$k(j3
z0u`&w!ij|zU1hoHjospHv^ro_c%sIf8E@8QQ{uO%AnE(f+N$h8Te;dFp->Fuj?JTX
zl=Uzrvn<+P=X6z5VHw34?z`oNGLs%?k-!O;BI|K7{I@0M$c;r$tPr)RdBc)K<Hz(c
zIsQnGyAtC0l_*ud+xg`co#_i@%cyMSG4&>`%L>BNoM{@hM<+{vSE|OlpA6~{oN%?2
z7t%U{`3lu6(nXyzE>C5-w}Z}<CW{*;G;5<M?IUxzME8C2M9xDj6)edpRVGmRUc7R!
zk_Kf6O#ev0O^1VSu|;d;GD*2{$J8PBrSY*_N`-hW1%;Se3cF)pk#9v&DCH0pXSj)H
zq}P~9?$!03U8`ET`b!doE_tJ9&Z;JgG>bIq&B?{{Y7L12$IUjhesY<4U58)GloU<a
zbN#g=QpPIP{i&(u78!H~mvyRi2iFradEVRaVPW1^P_lTGI&Z7{$e(e-UU*qoCnb89
zsa2(^(6(8jC59?$PqI1}ilp6m99;WxB$1r0U<vaE`Px>Kw7$1EW~rugT;XmODqWD(
zYi6Q*Rqbf5U!|UhZhwB_P4cMxBS@RN^RFc2@t)M@nNtXqIkZhH2XWzl^~Io(hCB;G
zcJe-3e{Qxt(nf0hZCP$r!6IIPmzB`<VId&>1FlNRR=$nL529!o?Y8Hp$hO}v8#;9_
zZ0)klW%cJjo2)*CH(Sqk*X{e4w|OcvZMLb7*k(VA{DvowA;Yx@ik+vwq3jDC{eF`j
zlbzmW5Neuj8~WCqxU9azoxlR){N;9Ak6&^v?_%8Pt*FFFj3oY$XaV-V{a4QNB<oJj
zMM?;iG|&AnR%v%iRf1GnMJbB5^tNQ%sbl5YFG7aBs{S!vqqSkJy;BxU6be|HDAYzg
zM*ijs%6o(bP4kQK;aCcGnTLhUIy++MVRY^okC1!&$ky<piprou5X%sJ3S0^|)U{4;
z@<R5P`sBiPiw+mO590O}zuiNuGZ=BxNk8Czz|FI9Bg7SW#>5lwO(wm&qU21;RLD>9
zQ-H}r6ykh(-Z)n7V5wCI%0_|yp|wur&t<JJL4#ErXZRsNLJJd7l#nc@i6(|EfD6sC
zLG0X(@b;;!rzgT@<-}jR>}StfIGX5BNB|e6KNl~gm6pjYcY_?{&&To6sX((><>;Fs
zXL%HHwc8f%q%o$(3?-Y4%y3+=I^5(`OMY(OtZ}3ogd4{wy3HJFs(?gmd3=f?ic?tT
z6Whiq<3{RA9nO4xB4mfx-Ttc#LeU$gI=js6g?F)1P4VyCH7ENSb?@f$i!0|lb5v-P
zaC{1!1@cQ2QEGItM<Gl<sv2MPLcG1Et*+h5ja%6#KH>1j(T9!R`@Jh6e&^J0W|?pV
z(gk13wzBG&3>aO66V3&jJ-Rq}mq8&-j$Q$;;>a7`Y?v@4VYR3!A+fCDZWwWyp(#~~
zqabt`3R{KV3!|b$y#)GT0jrKS6KR=#6(jLLuL&~wu=5A@YK6IUgMO&)vfI!`cA+81
zjztwec>|0<c6Q)TmlSn&(qCDBITG8VO;-hJP}pC#Zh-3j`btkfX1#wV8J@sZa`aSQ
zOFNva+RGl(H(j0I><=wh<Z+^`Zf+j=OxtX$1_Y08IvIq9lw90;|M@{}UM(>+<j2Ar
z(K?y<W{>KV=x!jiTex74yNgpko>P2l$K2GB?Z8NF?_nGy*5?^4#wOPQf)7q2LPnuK
z-AA7@X1MwCRHUagrI^Mcf{r35#ZKJ+d}~%T+{>0R$pFL<kLxxwtAE<v&hc-m{L6$f
zRi!}`DDD?PgN=FGV3e9F9E+CVEqKHI5*6WO@H{^?H5L=GqOkA5KQ|t7j!x?(lMS>K
z?MF_ezo8C)9?O{)qEbi#zNmuoiVyh#W9C89J`9k2*fail+n^!ewH%~2bQ!0s1<T$g
z(TF+TCy26!O_r6E#JmTIs=<C`^@kg+33nD(YB05=G};(c^7dE;b^%>MB$f(5(8QaA
zL2#LkX^8uzTdJf6oRBX7r6@=>0jC&cn6=s%Kl4{>IAx}QXSd3q-<w_GBU)7bx3A53
zneAtsOa`g6et~cUYUWT)Wx=M(WaxgZX4=_KZ7`)G$L@Hni8-0N3(YsQWiom>i4;Yy
zd%|YgOIM%L##qzZK*!5}T^BvgtaXVlBak|3+y>8?>*dF1zdqkW)s|P+F{5axWdhqE
zQt_iS=G{=r>Uzy0DPc4LsrZ7OL8;<qWlbhQDF{fieysaRtk;a-TlsphaSx65M#UN_
z=yFnPqkTR4X9%eu)rhuU$#srDKE==>BWsifRU9DE0R+hg`<pbWv!;S?u8vt#qYwG?
z+1WlLDEXT#DSY^)CGv|%t>*diRQYqoanIiNc2D2jkYq<h2l0t^`pG=Lt;u1Uu{ZB3
za@?LHaO8KjRQxA%sP`HdFh4^0Iv{x(K~vhL2Oq^V3NOW>=}k8Fm1<10{^cSgZcW<w
z`nmRH>$1T9QM<vPssGFmC_^)rx(>9F&ZCQ|n90LG6`xT@pomRTm<~MP5g)*V=b*tP
zrb&GXPERo}gUJQ9uwjS~__5-Jyni+Rx9342FX5-ww#&j;ei}*sW8G+m7R`MoMW&Ic
zXw6StBLLovgTGE-K(Q~t@4ZaZ0URD*U->}M$WfY><OuqGId)_qvLP6Xp$N-1r#CWT
zVj`<Ko<ov!n{ra1#fHoFFixz|5P)}P>ijTvNA5Qfq}Nplg!yX&{Y8f?#Ry(*=R1$T
z<PZc?Cc^Vh82#mp=EFB;vTxCZFG5F0r+@#BtPRCrc|R_7l)Mw1g1zaNbJ3fYlY@pj
z>l)d#nK17S5+b$fyoXP4%BiMd2(i@fwRaM4Vj?r3OEdOuq=zbQaT!&kRe<Q<g_N_|
zzq`5oBaK<3FVFScW8?mL#fF=-z4FYIY-}3jP_PiK5C&}fN<G%JYS$9@DU6|xQ1jSH
zS-WnID5P&6(?!m*OyhE1<8+|@$A%@PH8W3+`r$98hO8`7M*b@!k&28(DYY|~FAnD0
zs=oTt*nz$(-m1Jr3dpNK%>k^5-pnZ#Fa#}QMx~0i2iYX0O@KX>yBJo%qzsCr4lqyP
zJepwCCn}nv4_nKIR?D`B%+X$acGi);u6NdOGteVg@vwXFhr#P4Q*5VV2j9fCK&?&Q
zG#FgQ$O)_31_gowjq`yDgu5!@jqYx=@IZ)RFkgtI>=C3erLMvNzcF|PmT$PA`s;<X
zoGvwF+}BWST(+@4f|6t)Mg^v^av^|Y<1w=MYY>Dr0NEs#30GF$NWn}@Vw@}<Q-+>4
z1mEZdnrs*bFkCfJ6~W!^Dg0Y<BYJ`PE~dD(#cb8HwWV(D2$!ek1m6hSBz6}x!`}{y
zH)+H=l??*w``~QKPBNMjbL<+iI?TS(D}hI`p#w~spaYvy0#i+g;?FMSPWf9lY<^bg
z^rU}(skBqvAK0L;RMuTeqdKX8&<x?m0?d*)M&5tDB-D8GL!)KZV%~H^IPs=Qao%zh
zg!G3@;7BU<*r8cc6a*;azZVY_t$`l1w_}Z$HR(geuQQM&2chq)`<o#AZ%UTar<NDN
z3J-sw4S_RKo3-4Ux`<Cs%}&|AR5J67`hD`zfS*+6NjutSSx{-aku~qr=lARw?SZGi
zT{Ggu1yZ+k`u_j^cdc{l^~}s85BJ&@pPR;~N*`ODwZ#fAEgHPeRAb8|>^<<F)U|w)
zc$$?E2n#4P$%qHXacBgTNwYSQA2ouoG6lkpVZuAGA#hAUt{iLXbeg=-pSB*>-6ABO
zxL4M+ueZyaQ5eDHCZKD1&tNHgzBFSQl8pk;71Yy<x3E5B_B<9)Shvq1-)8yI#c3?@
z^fu_=_D?+f=S_D-@RKn2pX16z8RE_vr(E1}!DJhz<ObXe9YSLsMel5tz*tFiq&hJv
zOPt3z196j*Iz0umf;#5xw#KQ;iUEo?t#RyOl)2D=J)*ieD87Z4T>6<Pr_}AR3Y0T1
z(3iR#{lPC=Y-lhEwr5}U^O{7|&5eGEkcZva-|2LJt-$++_7Dk9i10iyERfrwDp#?@
za7$Y(VI}V&$CC9(W8YvMtLxAFp6(icEj!_e`D-uz+troM+>pmE4PBSX+z02HI<V8f
zQj6@%zfudnwhbcU8_x^t7x*S8*jSs9tCof{QCqS0`jQdnUshCK773V$CY}%Mf`E@=
zcq9yilvC_3#5cD0qK>fOZGU87Q?Cvz>g_t@3ye4J>mR}a@_G@V@Uu1u`?XM8Ubieh
z4|9F+?S0t4x)Wk)5P`J+6(Cb$FAI-+Ub6E(*RGZ@DK7U45?8eI@)HWN{lz2^i=N{P
z2Q}pD`cVn_@B;=hLICzcX7}_Mc^ohhh8)+g6PT@~j6@QQ5Y3qVSwRyzN7il+D=Y|=
zle5>v(0kC_2x`lixA&WuJkJ(15(t|<5bIFNQ$8PY*AS=g;Li!Sxrv>lXeVwbE?j-2
zVdokF#9W(7?KR_-S|Qrfc31dt+JuTG8KFh~m82wVLGy1zYWxrZHcdH>NAeaL>tjpX
z^PnH0^k`WPW5tCM>QZ@(_RtHAoR8>KBV&|R`PAPvhz$Y(xaAwt_ObMfg>1%Xs!z4X
zQe}RMLmA^sImfR=DxQ**gdiS7xR#HO{;I0%=)GmtzP=M|=*X7Ff|AucDr2JvCXx#6
zc68*=K7j3Vch`>Z#Y?kuQ;l(}K#bE-cjR2&yUMuwZ4dGRjcG+&tD;T56**YF*l2RO
zZ~Q$K3*&o{BA<tLK9^;K459BrC-TUgtt>)DAb%!*1p$^rMG#LU{U3+rXn>edLGANG
zY@vFmUo&sq(3iGK`^1g$;q)!ww)z$Os))Z3Qh1sV0lN9n`39%MaxvEiyAf2vrznhu
zJVVK0#tQx{e2i+jPsq>awX*0)T(+69AHDproT|U<mOn(Q`-8Av?@nm~4V4-Dz=lPm
z3ku^5r~J<kT=QzKe>C4y#70qf)Ydto3J)^<DyyT5bimnz*7b+qmikVoVoV--SQ^^=
z_P=qN!(tOUkCsKD2EDIQx@|PHJ?<~>o^zaQ7H|uXJz5yAnvdr;S5o6#gi1qcy_`;E
z&$rn#=IYj}3&h3`-P|ghaa9+X(M?RmMmi!>t#+uHm6S>uCAyC^$VU4BqXIIoODKRJ
z^S@gp`8^~T?|C;YN=Sw4xk-efWRhB%lg4g@m}bD*D@>Mqo9USbPyhr#6AxgBrbYpY
z&?zuX38M7t>m>%Y2t)*eBn~NcC$b5YbFvS%Rv>hTGhY%cO0O7UscdjudZ+Hf1lbS(
zDXKIDNH7b4Fl7_!&GOC+)Q@8l){li$yXqHzdJ@SWGnj<YcUv)b8>h}~T-d!sE8x!t
zSzOBWI*#I^+A4g)%i>{eO_ru?xMujF<Y^vX6sHAb5cjt}n);jY9;fi88YV9lfszP7
z7ILkO{qI!qKVkNu@<+iXu`G9doBNg4xy4k|{w-p0G1Ri56GCN~#32~-j)S{~$Bk*!
zLpG*c-{t9Y9NbArVga!`e`50n8Drm!Av?Ky-z+GKm1tYWDF1mu5pT5nWkh#{!ZPOB
zN?F?dwlRWG(#QF(J;e5p_7Gr5pTsyC5Q7@s0|Yrw{JN%#1*mC!c<0u4c4Y-=V5NEY
zv%~{%bHz4>z%HTS+>h@|ivV~KhpflsuIxGQ$6ArRHlkX0_dNBK<z`p8Tvnj~!1N%E
zPp|-9SqRM)k97RbTC}iUF9?!na^0HDP2`|FYF*<oit+`&S*OU*x@erFm^)x#6<H-K
zLZhip)*0UT%?m*=L>QXL;nU~kis_B$9NSS8A_tDlWco!tj;1#?bnkNRMFBxtq^Nym
zqaZ4Y%3iY^M^!HT43%hV)f{(aIsLG2!XIf}+Uf4)WFntAT74RvT{hTT?6~2%r}oFj
z8(MCE|3CvQRwsYf=bfQAaZLK0Q726y_euhC(4eqtP|~bM;cO=oYWacNyLfLG2thWZ
zWvj1?_Sy#JD~cylEFztBYGE`mYYt;b<NB~qy;eDbS19_SCUZQ`zom{@?+FF7N8m&V
z95)6)pGjdO?tCNqMv>oU0zEAh*Pk-VO6&AJY(7OlBE4TS#ZowwG1TDrh%QPj66W#*
zhK>qF@GDf-qWGEeuTS}$mt&J#(b0&G_RM(dW&m&;`_BjTp5aoHiXC+xA%tw_yBLMx
ze_J;TzuG5%d`3kV-7T7R69~0sLVE9CNX_4<KWCrI%N19NWpiNc8s2g@M$+zMiAcE`
z24Ac}%WlDL=&C(K8T#YEf+q3%%iSI@Tv2*fCEx1OezKmMd-`U-FcV#u74&Nx>IEVp
z6wzN4WH>nQm@PpTfs4$DJwY$aBbt4|jBYJIB&WUetaS^^HNNmrcK&R1MDp=Z<mg@E
zPb7)f3qN0lkoMj4zmOT!CkOi7<s;(XA?W+3#0Cre5j6^(&cTrV%1GEk6xk<$hO{-(
z`S?Rzmds3#b(aSqT1^Iku(M<2`+aYpUlkf~Ry|Gw>_KKif971*>H2>ZyXn>6%y&j%
z{qSC_sK^PpZBU<GSy)+@a&9`C-tOyw7rKQux(E8VMH$=fnF(~_`syf|*aGHv4;>5f
zue6r^Y_8M1w|T$kzx?AT$O<0~qw35)cAR`uWO4f@&l@_k`L!zoRq*ZJ{hRW<>vzU!
zh_j{lo*q|z*{fb8LW135$?d{ht2v4BU&SyM;_&ND_cx5gUQ;k3M;~%r_;KvtU@|Ot
zaxc=bPAIL5?wyfHpUm|gnN;v8$NY}Wif{j~fcJv~LDk`Zm~POG6!(*)DEqWW4nTe&
z^>DdM;F)Cl@T^<4w<{#;Wq3Z~)nkVziLcgrJNJDW6o?^?Yxi($vmN?H3ciT^3*;K8
zLQVDsa+bA!bC|p8E2RcKT7{R2#d0A|iztMlncS}G|E=V+ebvhur?LinIg}*yVk7FG
zgwG3u$vlFEDQq#e{EN`J`QHGo<*a8={ZNwV!$o=Y2s{+Uj5-l!8_nXc03jzuVl*ge
z(=cOKRCP}kV^=+mr-PE=;!7rdm0f;n--#&B!)UH5l5|d_=N^?rq*48_-}F>+t|Y@C
z8*T>o5fw%we~24xY6#@PC2@4bQF?D~Rf&QQSQoExMSZWxPWt*RvOzR+uS|8#)S(jh
zV<+8}MvXhU!Q~@KedE5h7WCwc;5Ec8@tF7`S()9Yx@5zT_@XPd@{{*^D*p1o&Gu+|
z3r9R|nsS&i1&2PIC%TD#5Pn@A^5b!fu7~Cd+RuT4^UCZgj{DljMFUvI)Kk>I^b0>(
zm`ve3E7V#t6L#cAgmwJ^Oq3CHpir>bXth>~Y<^#hpdl(X45o%sSv(s8+sMT}+&5gg
zgou8|^Kv-i`%0JhK6RDV_1MvLVPli9>;+X~b9B@&sp;&kJv}9#g#)rDednXA>NX%9
zXNAB|1zhPkRq1fj*f=pJ`MUvo+A`Q=Q%}IxcB?w0eSVwuc=xK4b^RxB4Lw1g{?^sY
z=Uir#m;B&X($Hvi>Gv;QP8;&P&w_H;90}+t5%{Ni+aGO}+YtqmIQI+qLe>X09PCls
zy4RKV6lK-yP=i`^%MCeW??)W%8<d^Ra&u#XE#OTYBFTz-q3x1dTamB4v!f`?47*yh
z)aF7On14}UEaN>fvkk13RY=se^?V5(FiN|x`LrlKY};ivYJcTjMlXeV<=s>V*m+?e
zFrs(sfMElL>BZg?l79G>q?iz~_tKgAJN|y8378yzZ`p(oipNn-L$3);F%C;9p3&cH
z)qoiS6hv}-WRpQR;eFqn9b;Qv<&aM#l|4Q4ac|dq?yhzo^TIQ)B<k$jDO9uc;((=b
zdXu4lH{^Q?GWio$O1j!_z5O4fRlVpnp8OwvG3L<XMS-u*OP(&aPq_CA3h$$x*IGyi
zZnJL8UGF}Oj|!L|)FEP+JG<9y#82Ne1e_X6#goNSOJU`udYD5T^FVtbWpeWln<q3W
z2hR_A2-8FxdKl)f2f~hM&TCr;e9teAiYuNX`^UE>d@H5h^4_U3@xH*^=>&Wb7M^li
zEOE$*Ygh{J1CD2UYR+C^)o86jS0EkglHGql(#X^|SevHE#Re9|tD);|wMWl&-gB9^
z8CJ&RCvhR|1Vl^T`MDes^1e*gOz%f*l#Lf0USlO|kGdu)l8}$P`-@G+b`P<S@tSFD
zx0ms0Zh0&Zc)S=n5a5_Ubb~1E0A?zH%kY>{KSn0-X3^1})Z?3jozbw9bT~1PTt?`+
z<(-E-`WuEImiL@ea#*vL-5TMH8lV*DT4x2d67V|Qrpa=MZ>$(;@I`krv&lwla5Gr3
zF!XtET7YUP1?VafpgE6Z{?=9e&^0H6zKWU;=6P47u;sv2B(qKE@L~~%KsxS4yP`iS
z2c6{IQu3=GEENtW*P{a(BR(S;7Vw%T#4pJeoZ@vEpuP%9=LVnvQ(=$yYKLxaCV`LL
zy41Z5S<l?S;Q?tb_I4^@s`mikqN$2{ZHjt6y2jMsXTiihE|sx(tk&yA@>P-6@#mp8
z3#Io;Sh3afnalEP_Ec-zI8)cD@{90#T8;Cua4F}?-2oH{Y1@GV`dehNn^BR5f3aI*
zb!&P7>W{62FUktqz3R#i{z2H^snRt1h@L&Zw@=JoDk!L?MB}RuxD>xwyR(r~PC9qh
zX0Yz~gs=P1eA#qierTJ_OYS_MI`-R8ekN%IU;a!E-FQni(IlB{TRU9)n;XY%;-57|
zPUBqM4VO2@sY-ywFRc&9(35D-yImfUe;KL(Dy?-g`mPaPd-;@4?DqSs_Xa(|&n*{%
zpIBp|)Iwq;_s|d$JHfoq(<Id#F}AUPmQ{EX++O$-VW!DPud6Pg3rKd;qz7D5Hba{S
z?KtmJGst;4#0_%1<cJLv4Ey(;tFHqIO|1kr7Sk6~za0V$L)t^>3^g<24_M<C*=g^Z
z{CqjlAC&_$d3{;V_Jpw(C`6(OyD_bvqcL6`dO*#eq<=+in<o2w*@iJG7AG*wz63^v
zfSHgnDxvX{$R<3|)cpm7Jm!+gY8=xLE1P6ugYu-{%k>bh{(hisSR8)QQvTTB9d*2t
zDCzC7*mQ{6F^|K2@3IgeWCi{60n-RpCYEyAGhs<8z8hAXOfJ^sHGgR4kfC$$$7Ft7
zo|yE<K5H8|ku<WgOBCF=J%zCQma(zzr$XgdfOGKv&#TRl#l_FHHX>=MGFV&(;ZoO4
z8kTa>*Eg%W!}W*>rN4kPnCsH<jAr%Ey8S}GKlzXcwWX8tUhWod>#kb8)vLYCn|swH
zyKVXq)}B-Bw=Y>7+{|mrxwI|#9IZTgc#TJFoVjA-`zD~`6v=_kZ(rJ2*!Ban7f7Sx
z>`W`d_j+5If2B#4NXDpiGk>S&Tc+C2j<=2n@kPl)bNl_=Yotpg+N$1L{l2fbV*(7p
zt%U)ITasxgmzYIK(P;GSJNeDZvI{)+lWTAYeK?}6=M%yyD|Y60{aLc_^=IpJK3fSx
z^jMPg$1oH0R9a~+r@#UeMKV^IC_AOOScKF&RB^mbC*-(C#mG2V>%_`PKqr09;}!Za
z4Q(E6QyB{6Mzu!sM!qj88yG2wsOWvE$(ZXn2WCbiOK~`ui|MdZ$fc|3RA$$qPBy|3
z+$V&l9^Ql?=W`gQ(gAXnrj1O*wn_d=mOFj}Ejv55ZN_(Zqz9Wc{OzvRTAXO~Kg_r@
z?Y2qcjx~aJfoH`kmaln$ko~ScOu!H)E*U5mAjLiaA!3@A`vOrPj|U8hDgJV_C;udc
z2>?@oGS6b=o>$8SJS5RHS6qIgoy4h~ozPHD$_r?MmOml`c$tQ>+B8_{hR*8UI1EZ4
zdH)@71A@%t{u~I{pEIzf4<yrKd!Gdp@E{3Ez9n8OI=Np?%#GL>8)I;oZ7tgxzg?fh
zmtr>rvB&Pz0u)wD0pNDrm^%sL=d^N+(H`f8Ny9xKESL5IjgY%WX5AS42x$U7N*_U!
z0%ijNvpR99lEC&TW!>JJ*M+ophd0XuwoidfMlq=!@mnhjSoM>^Bpfx=Q9ku?cVh86
z-4e^W3qCs&fnqj!UJx)D-rtw~Tem;lj$pX5!o1r_1Ahuj?QaPg^Eo4YDFUD33m1)v
zKcgbU1uJko1Ao)<FxVH~Ce}Hf2*qY@T+OV&vERi+U7W>4$*<`8yhI*X?GLp4-6hn6
zr?Q)q506mi^jww}QyEvz^a|urwzvz9$#n?>mvgub41l?zPyWJ(KH>2T?b+at(Wj^!
zfF1c#<}AMeEdNlA#2E3W6Kwn)t@Ck}8mm)a%^iM4>B-BD+InM#a7OtCQGok$M2kXJ
zq1YI^5-wq;%Ed+(rn#e06YK)FEn~10AR@4<0~`kGsF$(C+_625omv}Tr>~3Ap_Q}X
z858!U^ldP<mBTo>gVDF$dUL};5}w2w_57hNo(RoTj9~6Cwo(`7_`uJ&7c|DNdH6ku
zQ8JdKD0)GX4S@%9{IG8mQNY7!&MNG5wolJBlXGG-YGMZ#H;;iKIe7<3;2c6}zjhHi
z^aQBg7HrzYm-(vnbM+jc0kpK=d}mA0{h5Eh$Q}`?<3bCXps6YA&GY%e#nX#<gMHiX
zT@E(wwo<m1O>uj=Qg%%#Bs;x48<33C{xac6%YtUN@`FF^Ay;LSjV%kGZcaVMEBh$K
zvJ^<Ys&}Gi+^EECy6RK~B)Oyu+s%xQ)R75mua2e13vZWLw}{(l2%l2JT^9E8J7Azm
z!;td~6gWcw2>x-GV#^a0cO)aqYjnmz9TV`Or0{cuzxHGl@GF*VDww_`rsny@Y_ApN
zC6W4P6g@2Hj?*brFbY3wrt>n85YG!myIsA`emIC)fbEgsk)j~C0<p~jM*rF2lfIhX
zL1A|}2i!&9rwSkH@`fw`G<Z9>G7$ZkU*his3e6!ZZB?aArglMxua$o((x_6HsHy~}
z^@}|$c;emIQluxq%a?^K*_pYv?>e#;+Yli+w#HSh5eakR&T$DV1?&CwvIwSk)8qU;
z^u=xbC~^No2aS-M#q+pD)R&xxw(5dtrU|w?3~<#|`U1wR;i7W?486!8RW6J!B6(S&
z&55Ffk>^*mG=3#)qVAm2Upl5Z6FJK{?Gs*G?09bPPAP1A1`2eyG$qXiHbiQ`aG{Wy
zJrcu%9GNS{oW@pCU{Yf-ENoF`>o4+?EbFPoaAXf~fuKcc$?faqB;OghJTX){?PU3W
zi1TtiSemkv?mGd~2mg^#rMW(>8~C-3oF|v!dBH1mVWIXjJDq!*@gNf8d~C-z64DQT
z*qUTnN3So9-jp}xEp|_pZjRrD*9@GTwe6yv(@c<(#7$OTR@R@hl#Iw*eE7jZnc{5u
z2)mq$I?5y_;X`YdLgS;AB-<%;fz$~Ose`h#6(99+Ra3snyb7>yj7hYK5zX)my?Uv1
zEOjidS+DuDLCctE>Evdc8j3@#RoP?^AkZLxfCKZ8BVyg$YM+OaSvYOaf3WQ;2B7Kv
zn_^{<_fo#xn%U+YO2Eo)VyzKA`^jY<5?NkB+XfJ9a2BK+`kCa@4h_<f@CS%O$c8*+
zlnSup(~}=@rOd(#xb|S*#~)!FYB?4i5Ii9k{vphbyw10_bj$r$!m=X(;~zZS<7;Un
z0>Oeo5b6KmXR0NrWQg-CX>wlX$=A;eLsMqtJfA@2e(&S^0Nk9K&(Y`~56%t`tGvCc
zO@sf|HfstmH5ro)Sy2_^OWA8c7Um)WL*aCwqCvm#bbEdF*|GiYl=KFyrpiySTcP$v
zgZTez1ypyw@60E1)mqk1jAxcx65eJ{q^B)=t_QBV?BVe8EvzK46jBs!%@f2=Bi9EQ
zm*!$i;r_2za7!sxrhBF~Tj!J3mqcO?3Jm79BpbCy<ggg3Xc4Sldf0*~QlTN#c5G5B
z*2?0FI#B6^r1mp{WOe<CK=!<BBPN`bpb`UVOoJr!8b&<oFZ|9vMDY$-o6eoFL|Z%1
zJiUxPS)}4%;5TN1no{nYy`J3F3RUivbmZT78w_*uPxhvht+1vhL^ZX!nwj}c=_$q>
zzf<y=3xMP%@A4dzjUzFf*nh8}U-FjE|If}C{rlr?#!}YYB%{Qm`#t96rq9-=+Jded
z#8Z}c2YwhKoRvoXY$IBx9m!ZV&lzz5wtqHG6ylZys8R5uhalUaVpD>`s3gR&p#F1a
z-lVQviyC0-NY~<jV|hE9m9KbudFfi)9IU~`8r4I3D@l#TAwR&D^3qp`9x}y?U6s+8
z00hWr6TptXZX<A1Q<5&f4)6!c0Al%m-gZbq*2rAmTA3TLg>|=&qEW-vZPy+Z9)?*N
zg#Qc&sElT(7?Q8M^H74kI#;$~gK&W@D95z=#Ky&}_=s5EQ1RE8t4cDZ_@YDlAazOn
z*JrZfUt#uBP6^tDPOAAo31<`6=W{P-_w*{?Y$uo2&U`s)bR<Fc8815He8YaFYqHWc
z(PORxQYvYI*Ek6#8r?ss!Hq9ZvWZdr%`}}>d;CF>==Ty;0!V30Z^=~?r=7Y|MlAc1
z@}3Wee_dLb3Nd*9!v2;hJgw;PZoNHJULo;m`htI#a9N9pZjWHlt_p8n=7~isVRZ1T
zT@a0yUBE7oMzdA+i|u3+cg}_omR}qPO#-)*pg}gC0~2*A-u<InGP|lL<M-e-+lvwI
zL&a0Yjs14p)Cd6!Qhx4>Ph&9u!j%s~N0RO374kPc{Ql53bXdaCTCB!g1T{;H#<c$v
zv+VxRkAImkMkYC>3)B$%mAchC^~OMeDz&5ghgguVeJZR|aC4pLYz%tDqX^x5n5KR1
zawg+L@Ra2?^ZBgIjB%$I&gTm^*vRMeRrijQo=cZu@XyR!Ra~Syi`!Ei{0xU@Q){i7
zH@S@`t__?|9SbCqWw>^Iu4hr1Pm4s);i-Q%yoJ0Yh5{<0E3qPTd7Y~$PkFW;i7Y{0
zT~%LT7PbnH#xQbDc1CnXI|_C;`CeFwx(nML^o>FWo2r0rFCNlEL#{GYUEN})H<D<J
z6^-Zmk@$x^P%XWzbnwQAD<jfXHdGX0&Ve*ATz=USeTKoXVq_a;KSBfG=dCxTd)j<q
zqgC>&0b(ik0mLEn(P5}W6p^*D2$gq%cikyrO`$bGxx5Ov@}pfHl9x}<@qc&Z9!-fv
zK0tl+gCGEXNJ3*Ic-xMr6kctv2vbSY=5^lmJfFIJz65w$@i4Hra!Lf8%zpKFBf4nH
zp<e5vo)4&@@i(hwemw0PmB2d37rb(j4}W1jPZ>0T3NT!Z(#g6INx<F!_ic)^)wESw
zqtBywr=~8^YLq1BD~}MZtFnz(iz$hRNw4n_&ZGFdMd+==ETFhszYr`mFvL9HRXWTN
zdzUd-pS}A$FM@;nCN0*nqA=3pkRBZg2%{McVku#uQ$T*V5xh%Q=T0jHHWd}sVhP$j
zN;H};tqkq(*98<IG(ph0wI=_-O3T3F=W*`y*}1v-ek>l2Yo`D<86|tb*lsDI=b&_2
zt{}IneA<Wx_g@7NSKQrF{io#fGG0qIP!B#5lHtp3=IGlB@o&m)833ivwJ9<JmANh!
zHjh!9oj=7#cf0dCc7dO%9B7i~2Cw^7h&48J*cbphLE^`Z_xO(ln;aTMZNG0FH#P}Y
z(Tko*(7`i&Uml=Klx!SDtZaZ)hEVa>G7}nYj5HU*UX&6SXqhYx>jX9csgNnmX;><e
z74E+(Ss2lstiZ)F52o!2Oj1oFI9|Gem7}N<;d@XgS>Sw)`ly%u!81C^_v110^!MCO
zb0-_8z}{;Z;*-^z;K~*0ey}nsc%}$oECB)ZB>;@%$SpOPWeaAYQYRu7%qfcdBfG5`
z-rK(fD(u}~;F-OJOz;NaSmF7(CE@1L9L^;=>)eIqg^!2hxRfrW)LcBRC^-)3_D<Jq
zGNIt4kW}{kl-->?{1I7Z{3@SxvHSr7_?q<Y$R+Z}&AZJ3+lpoPR;%W}(yPKNZck70
z+lPa`R2<RZ5&@Zax{x_B0Sx)iBYzovCyd>0BR)16Htvti0K-tak8C5CAJL5wzBZYS
zUUhD3+uJv;vwJ+DIbOsQ>k4p}r@<w1-PDc<n9$j05bMg1UKOcb)8z9CpfM+r$(5fZ
zd=obp`)5>Red;+^`}!wVj;a(!u`t5|m_4&|^B}T+c%zWj(7DiD$Le4^V<p&bVw_%B
zp}BBE6}jzq8RY>N<j3eVs-9cYj=e$BfVBi<_I>qw8Ocq+^s<=gsMPyTb%q-7{BD$?
z;CABwdT{-WYrnUDwv;_lUcDTYY`xsYkVJFEw2fPPRiKTJ@|c)QHVDW#ytAZXY7~$Y
z%G@6)n)Z-vLce6kxW`mi>+*{%33*6gCf4NdbpUXLe$4uGXZrT199Zzt=We{NIIL`G
zTeol7W*q7GpjlTZxFwwM6U8581@JK<208-#V8r#(1R{hdK2;V<wqL7xc)8isL=MvA
z_@?So@tVtd<BnC)v2I?)&Cjw?@yVP^KFvWpgqL{QiW_@IR*h5&>%AP#Kpd8?S@LO3
zb)xxsVL{nY%Aw|{&L7^RLwBT=Q0CsN0L#B;rB3@U*7MxnrE8U%>yL5mEZ;=Z<l+kg
z`9>+sxxR-i{r1-upAO`cE$Y#t7#*dv5GLjrsH6QO4qpHrz&DB~k}?8H`I=;P-oECu
zAhaRAKees$GIW+PGxqYbmZ2T0!KD>Kx8IC3<#@!pB4<2+mjL(=`|tV>X#fgo0zmRb
zP>G#gsAR)bfMJkfQfF!nc1^nFbh`Ygy;{9t5|D*!)tB6oN)9m(;k|h8^FYVoOUeNz
z(*5s%*_~e+1#s_8{fi$kIT0vKYJIdlzj6k)+~3qRJ&jm7^t9Q&&gIs<^8*snj5X<K
z3K37Qu2_cyZ;>G;+JOrmmt!t(ocLW&HrE~Xh3OhyR-bb~m8Z)@R6U9yYs!g)fY55J
zF@M+F)ct!GZNq%FEmXsR#BFKtcw?P9p8^zBJ|mCc`t8M!&wN(CK7zSmp1ap8rLvln
zwS{yQ_6Dcb0r@L8`~;%rLMkBGUYJ7!%;mkhtr(pMa1ob~^f*A&A|dqjp$sz-kN1t;
z*C9I$yZxsDqToY(&jP4!rJsWg5%BIO;(>t=zCm~??rD+1z%JG<jJiSfs86}?d4SMf
zF*rFCPzW0yPgFnBPYz_`h6*C{x=E;ge#gDOwh(4<omEX`n4@*yLLVfBtn#xT&5~_Q
zZ$moq8_|}PyB$k5KTXHju~a9Kd>}zSnY_gd*$MvIbuRerAGRx24wPR-&yB1Z20bZQ
zRLG;(3E!|oeUOFrGnDqY?=!x^h~M`orN-vu-2S>OESOB0p%UBveVujo_iI|8wpUcf
zOsin}C#lzq6-c{O2j^e4ZHxzc$BD<SRns{8##1dXFEZWl^=&@kC*yG2zj3(J3?rgs
zQHNlo>+(eZbJ_o9fpU?C&A5Vnkwz}jPf)%U)8-RMjku2+o&I&ugwdtfbug(P-YU#c
zDR!qFBAJLCVpa!OhJVleep4(QCjTixu4t>lZ-CylEWtp0&*s)zloFmsDI1@~6-*x;
z|C3XkXDBR6-6O{fqdLUefX?VTvzB1L8nGITV`Ldj3b&MKLsH3Ac~_(E5lEVnUK=8(
zY%|kICl9qr`9ljPNVpqAU}AmN(vF_4^#J9SAXwpD!Lqh;ldyVz?)jnoStz0o9X%%)
z{+1_jFGlhpC`@tx%J=$n4oS1u?nSx~vcnMlR#Ryn6veE_SdI7?dJajXPBr|kxK0UX
z6mRw=D*@>Tf(74x4w-hCl+*O!3ZeAi->euj>!i9o7v7d{HM%GUs1?jM-mAFLgcqBa
z4`A1q3pri2NP0BV<TPJ$K-*cm9QF(`pEY$35b2dNR307l)p+b9L$OqaQGLk(WrTH)
z8{Q_@W<930rhL|8K3i7vAWYo#t}+&Op<Ug>>`o5f#`Z(rc<s(b2pvL!*4G(Lhb{!w
z1dKo;(Li3x3?=sDO@859U7EiMYHFs!(l1VS`eRprE#ou=uUTu57CDH*3fIK}JomI?
z1Jf!Efn*G|3M~2ei!I;4s_E3;;uM%X63}kj=u{N6M`2<DML<wOnxR<jOc->i;rv~J
z52D8@nECm;Ji0k{A!J@2Jy!>o4F0`BLGZtd`&y9X(0dSm??_}@Fmf0@su)hskg>_5
zAvpI-=PTw(V_119NTehbwi1*Oc!&*K#<@}zHnWRPBQfCqsD5j2Xie%Ri_PazqQH`L
zr;K%ecWHa!lcoqbA{(WV<BR$qQsAL!Vnof6KO-5;z`oxot74o>Ee{b>Ei;sbaXg2C
zDxi6k_E^9ew$jcfN(M1Kq(z@UL6xNX9+iF`AuPaCD8nPHw!y)#lH~E-UrKW?lcPh6
zzJiKH*96>f$uTXV8--00TJ+%-s>)7RNto(e_VpU#X^iTWsh`~?8aPyl6B??9jWr>C
ztTX5wdC}yPX+^Fx2$+&lW8m^jE)h2bF_66MiW{zc|E@7+Q1*FnbJXbzD7MSIF)OdM
zFe{H>ML{V{$Avgd6c+BldF#+w3_sm)Mdy@c?Nv+r=~mUo>Wl<y{ulS@THC}Vuv#CV
zh;xC7vXC-hJFO&kHiF0~gYF=0MJZ@tmN@;nUa$$48jTC_xKv5TryDHlXS|<=?@S0L
zj$LSHe%Q?qEkM9hGEl<!hV6cHGy`1mG7`bKx)9Ofj9*Z`z6koP2i_^2txS-ZpqF@N
zlTcyhoL5u{>~jyug3_+}AW#Q28ux?L_~YIV&@&CH6TWvB845YDn+zK^r*g<n+L+^7
z0WIjTR8bt?G|Y&pNE{P8NB2W4lR&K8XGDJe<AyB!r4vV0v^y5v*T?y#x6&5xs47DE
zAWlNW84K#6i;Cdx=!TU6FeH-Djv$BCuh#$qIJohX-%#qx8%HP+z@Y|ZVvOjq<0H>A
zz(8lTzy6PhoE|CqIu=(ZveqjqRuP)2?)7tTZCYxYoF|PtQ&Em@sE)ZN@_30e+PAI0
z<k9&7|Ec{fphJXqrhMA=ym^EXE3{G(pA|x0>f5VG+K6--OGC5K>9a2B)FC;zNhE8d
zRzs4MU*1tuBrPi5q>A165L7dRZEis(_g-TUG-IVb9<b)oSQfZLdbDG_v3q)O?^#uo
zw~q|5)RsRgoa-m|x^5qTvT%FPg<2QNZ17&TqvS2|nB!^-#1>cu1KBiW|6kHB%}3?X
z9%f5fV(6L;<Unys`Ioa#owzsJ*Kj3WG&TP~8a95U%-{7bANGaF=nY96v(6-EnL^U#
ziT4_Uk67^%|4Z9c{-C4@RRu!o^OZi;u6GG_y+ImkKNV^}okC^1$JocXp9U<%su4J>
z=Lb(e`@ejjk2YkF$ZLunvmuCuK1uhIad~Os^-8q9R-q;0-Yl`4@$y=(s%S~>V4kUY
zX*ST)&GREaYy(b{7pF!gu#Ut!jlzK32E!__IiUtT*C}VA63dU0uWesFBO=+0T2Gd?
zt8-0*iAD&5D9daogXu6;Hs$d{aP0i(gAKCTvNMUu_9$<hYV(6(iix_H+5Bql;`nn*
zX~H$|a<2~^!zHlOzphjCmBnNhx<%hYwpX9DVROX)7^&Wv)#*ompPCQ&PNul+34$5_
zdY?K|0PjCWAu3XeXrO}r+a23()7?PDRm&^G2vd0odv*cE`GXT9my-a4>Mif9)$e;X
zf$IBn1()u#p{d)h`<D?f;&qYIl0$Wz;Kybt^GwdKX_FbS|LC-jQZ<>JJr72v_~wYI
zR>7E1QZ*f)-e1(b`Qk`j10fk`zn%Ly>hG=&2s+NBJ=zSo)$TB2O=j{y+5B>Tz>bl-
z21fJXs75xNX{|}lo=V6kReMGZlldA*KqQKpMHpv)H35e{6&(6rH>OD(70c-1E0?xr
zj_TvB=KkFB+}z4fmwipb{Z7bO`3{)_aZUQTbTrZmG6)m#KUpQ-4FKqf9K<JIp#BX+
zD|v|ao{D<UMG1mPet?!=AKZFe5^l<3w$8TkL|Igwg;VcEAY%{!OzIyfkaNpvJR^w2
zV8Q47m<seB8U`F2G_G6Pv_Dl0Ew1_e{EuAddziC3NOG8)3Q+hGzqwS$E8U+1nUu(+
zlSDG6At<&=M;>|mi?`QzZ+rdAA?Z$@%wd)AM!;)3<d#6gE|<4{anNY!$ELar0rWsH
zVrFKZ((5*igQ_BTy<q<IR0v9Jh3OZhfpuN;B%7*fJUkV4q^%xOk2e9?-jpCp4s>3}
zcp0AR9avMvYOCkQ{akEX2SCb<JV`U^OCss?XFn!8*}GX)Z25-7Kj>vi;)GlIoy>?w
zv%(KmXbi$;*ibNqb(F9er9Wz??MTgm-`H?CB~$D}qH@9wf8~;QgXFDxmyt3w$sAOi
zv1Ig<%*XMktN?MSx}mMv4mK=I8Zq>GOg<iI5PN2}l9xiD_D}1u4k*tILr<@~A(ldg
z8Ns6;4+?iz$eNYwa~&>$0ZeaWuSxdaOetOdy*Kfmeb=%|Z4$)^_>%mQO8F-_zt#TK
zvF|gUJ#4qor}$8WsF4?~5xylHb{S3zoG-C=G@Vb;e`w>bA00(TDZ^{=Sq`ow?5!jB
zMf$kPi?+FkSec2@NxTpuX$OMtDQQMSgSPea#q;x}VS;gWu($QN`*Eyv(ooL@u3aco
ze!Gm1!$XB?M1iy2Z=M@>glgojCWi$6Qu-(2&r1W+K?7CWea)Aa8OFx={GKU)PnC0n
zyjx?{8q_4A-xA(*9A8?LWmM~(&wH0iuoJmtA;{s<NC3ALMOg?1djOy*R*sMZlo@4|
zEymKM4EaJdwN-^(MG`+sDp1v|r;V8U5Wj&7*m$~lUAW%)SxrcKq5w0Jm|<xGFS{2#
zxHEoz+$H1fhHqv*`xHT`omepLWg;XO^)0N~(n+Cbdf-<vBd71o`C7zB`S`^``$f8)
zfRXB=e6D<8oCez;w9D_4ldgm+apb2c-iVxIP*t<iWF1ydKp8Sp;h<th%N$oWL-1rc
zoW>=i3XjaDMmP8YhQT-?h$=CX9Hz|mk^jROF5y__TzH{JS4)=jorbUis>=S=z+P1&
zf416BnzduBP)E+S@Q^PInNa5I?`S}#2n9tI51&9r8g?9}7<*7GAr4J}<vMwQ5+%D6
z>?;C7*-4QJ6q*gh^22cvXQ$7&qu4CUkM?t*hj9>9Bn5Pdzr~Y2DPqV^*KJFH^~HLV
z82Vy+gxJr3oc?b6Ob8knhKcjgnheEQ{p93;?a{vfqMcGy>JCoWH+0&M7SMhWY6BIQ
zwP1KCjY^R`#G~>(LB&K(oTnR+xkg;ZwBgLZ1hsli8y{<{7Hiq`*XHiad>U*rbgn&q
zJj-|Zn6LNq`&^xloO#5K{AfH2|ALeR1v(pa9)#@N7P_*+KxDC$s$u~MV1+?<W(-*+
zZ3R;^#{MqAv3U%7x{V(cSmC`3q#Op-WVSn0{t~wG7It>_>8NXQxx8e)6u%#NM4i3o
z&bXDG|B(rD-~&&rojQ*~_WFVeS&cICiia2>dsjw++U{x<1&Oyr^3?P3Vd5~KYKc+@
zfixrwi2+#TJo-kCG|e=7FA{>(vG<>>{aZI^NiDiUm;=x<eh!l#=Oc~N(fo1=b+=)Y
zV6&I3<&5MnA}U$Z-T%dcmiT3Art^#Y9Pr9dGpA^#|5&7$HI&Shi6Q~!S;5<?BRP*9
z0R5+KHW+g(asYUsC;pvLqt~F4jo^zJcDT5WK<=SWo%3OW)Wa&60p4G97abiMy?28;
zG)8W$dx@f=hSa)gL(s!6vAlCZ-{F(sJ<Vy84UZfdM#`Lo)R+@l$~P)=y=k+3S2{T0
zj05Sey+Qo#s=}Q;k>zS`{}TLQN}To6_Zh9zN$9mZ7-i<rKp(`aY-3vq$=yg+Q4jf6
zz&8n!i%{#QVAllXkL<FNCIMfk;pm}3Qex1!!-RCxlz*TS)gAWX?bfjraN;AOnXXus
z?4P6&sFvvEsqp1!d+-d3R=k&=2ESuEuD~^?zF9x<Ue9@?zzW$`I{7<4xlyW5S-xQi
z{c(dW)`|a)=tD`=w_#Z;9G3jsqBbVB$dx0hX6jY6fqZdq1iSnveke1_+Jh~@k&I6p
zLCUOJGB-|Y#rAd0#p6~)JBgr~$atT$Td=DN{An`>{uC-PcZ3vw{T9^zP^(OdEe=`|
z@RE?Un&8$}WDqg?%qFmOZz5OvqG)9A+c6Vu@lvjkteilP4sV5u_#k9m{0n(r9U?Qm
zgb}S%cVBfpm3?>99tcgD)24_E2u%`bjO!-N)4<ldz7m=!LG2y{AgF>CQj8!n1gt@a
z5SuKEuW*cNM3`}c8oHo^KVivg%DrbN_{ur!mFx^~CW1ao8h(r~ac1<@_}mHA>2Ipn
zwck&@9Ki@^8&&TF7GQOEUgT^}c)WV9x@X>8NlBxZ$X5H_L!Tq8*O;HXEq<4Scb!PR
zd^f05Pzwg$xO32$NEJ)bnMl7Mc}0B9k=gMYNa9)b&}BTzY7xVscGbOTpSWn()jMl;
zT*rg-)|`Fo<2lXsh#B_C4{-AME`=E&2@~^(9CMVGHX)|KaA2--{72!y{MOVV;5WU)
zmAS&Xd47L?XX0e!4Ud2a*-b4J6j&X~O;zFd|E)!Mnw%*)%v42m*T9jQ;r^ON@LzZ7
z6;zX?`m7!`c5jyBBkAb}B>n%arD4|x_hVxhuWgglfEFMliOZTovzNy(QLM>!9@z0x
zB(2&jfod#%=E58?j}Bc&b{e{mM$uD)Vzjgn)Irjqq^g&LLt~gXBzlPe@1T!RGYDa@
zarRz)RTqBlL_=Gfm?QSKYplNq5x+MzP%>O4e-n@w1>$!cv9rV)$RX*VTf0#3IIqlm
z_Ru`#`>oNyZDTm!6vt<AvUex)UyW~=ROoDz;iO8`<a~Cr7l7R$tZ_VWtS0Qd{=t-s
zxMnu*f@v@GA2A!bG9sInt;*(Lfis9znEPwhKClRR+<uoSf4Fipb{vH7tMT<6nwdgR
zxF2%33?c*d1}TR^zs9}mY+P8$A;U+wEAsHa3f?DQ;z{#Lqg5G7DeJm0>BPT_8q(()
z1#8HA8UZXUiXmwdhQ-_hv1Y$a<aadSq5>7e-|y!>aE{Y+GRJ6qwW2>BGKnK2JLX-8
zqaSqViXyDed`IO!d~u~PT?qz*?odA%1%Ak;+*%FYvMf&_HB@!H9T&CPe`ZNF|6SL@
z2{Tm`T$AabEzF%!y3f6IW(2xIx>F_x3B*^?z7zI4WrIKv@`N^M1!|E0K)wJ~+-EHM
z66MT)BL6bt|4#t+1qu3+cH?CK*Wr;QX2tBSo^E~Kawi|Zz5JJ5zXy_Rlq3nB>#XK<
zNb-kmf6J0&*oYg`AV~rw2_y+5Ip-uvvb7{nCrLVvBo&d#U`SQH;1@^|NOFltvUR<B
zvcIoy=!eaBAAW7#zS}-HU3qu*rbS>SM#CgYfh3D0DVLcfm#03iUZt8bWoPx&tQDnf
zmZN4Z0b4eSJc_g^<^)yX(vvF0K*t972r9%L=|eBJ!G|t`51pGDbHUQv9eIe72IZW$
z1{<ZMbkxf9w&%*4p~k!hf1O<<HK;L{T1dg9@|I!lBzMVodx9P1J}~bzrY<>h@=t#3
zLydV%udOhgx`=E#T1uS$Yn-9!w2B1%|LvVyQ`<Nc$G-~iJKNFy@<_L2nUXS_mclNb
z9Y(ffNRz^nq%FgzUpdqviWNH{!6e~4V#zlN|IaT+=jdc9$5=Qa^JGQqTN5HJB7~+4
zGmC=afb$7LXbLgrKoCF>FHI1Gh6sXgB0<1@2wT3Q1N$K~O&o#QUSUb6u3CVi9upLn
zu!rlU0jOiQsbi)65H1fT?R@{cWxGn+{S$Y8axUoY+mELP;)a^u55cS3Mv~*?VP0ZC
zgh5ljsvXT{1kwc3d|jlumYuf<txITBOcG8MtKP;HNHctXAkEiGn)34>gZ*yrP8_+j
z!?OX+j=moJ_|u%<_78wG8z;@J&USnz(o8zNY0~@$r-dG@nKXB~rg;A0n`?XFMR5Ey
zzb9sR73ZxDD?08cYUn2;@6+-s=j@)@E2O3t_Od9fMZm7$t+MfaSPJDDD~$<HF*aDG
zT&aXJo+MbNmN*)$zD>ReUDB_MS@@fshM8^?{L4kN)OS3*OzYr2zm!k~<(X=4yU6`E
zueQ0z${@SZze4`2BsK!Yaw;d95ltLImRo_OC5c6fA#D-SnJ`FkohTgGo~h+jX$Z3p
z3o9_Slxr%DGfZ$q6J!#N9ivQfmJ*4DcA6UEyRp=9ni(sSa5*(HeQ~7KoMRd8(Giws
zpBadvu_)fIP&s6^dI>iA-Av<Y<P!uu1w4)5=^_FihetEG%#VDT9+L;dH97L{^wWl{
z8I!Q9{O_6hSzh5+lauudN)+s>t;_5H)Q{WzOFp8vm)i<5U&u;Dc)h@`{QRdt{4)3)
z8l|asE{stHMCa$mBxL5)^`h~p-4Gh*5yGzqjYl`%)2qGV%{Mo?>Rw-N8zlerprm{L
zU7dYXkH{l8_*S@cS0ZJ1*BxC%V(ur$i%U#5{t{oUxX~OZ*hp`X634NY)CrCa6?x^)
zC|;A2vONP{bA*=1>+!$PLhjsB&rO7E7DNdw0NK(QffJH4DTp^;-lBSXby=b%(MDJ)
zaGD94syJlRObFysty5wU!8*wjnR1(Xi<c>;GR+aD-cB1s702Gp8X{83Ns?i0g$_bH
zN3ltaLN+H@C=u2PD?zr3G(NV<<siHA%LCR1)?SIV=h%V8NqbhB#<)-z#YF&ouEteR
z_!5R-HN9D{;#5+2*&4hUhA(8zDlj}hA256d!+$n~;R<3QAQocoH6RwE=)LK&5PUtc
z5Ue)pR!N#~$VVU+qG$jB-faM0DHekDJEOPmKlSMp^*-O|7Si%ZyL+}Tk73{E#$zEE
zMBSE2v#D4JO*fD<cQQkKYdyEixM6z_>Jz4DkH?J?gd;ylD-bbut&IV@xzPv#fyLfD
z%5R!-EG1X-eUenk7@ZY(w(Pn;xFmtd)=XqibxD?-gfZ7zR9K?kwTd$yk*NZw>V>Mp
zZI++>6ztjr;8`oc+{;dX8emRN-Ls?(QAkh+R~fj<w%=7I)=~$J@H$tSrZQi^mDSZN
zz*QDLKY-a5gV{>1vg^a+_Fbyq*-ssOq(A&--e>o=_x1eahatGins$|OaFvxv6SW&6
z%^giphtd{M+Ts;qX0Y?((=oFyJ6$u6nM#xD>~tt?0ff1;gjr_E_U#RBQFZ^GSlsH5
k_V-2BKOUlY@&Ex#He1?)Z*>Ct!lf<#2h0nzfx!?409PMg@&Et;

literal 0
HcmV?d00001

diff --git a/x-pack/test/functional/es_archives/endpoint/resolver/api_feature/mappings.json b/x-pack/test/functional/es_archives/endpoint/resolver/api_feature/mappings.json
new file mode 100644
index 0000000000000..e1c41ed7111ba
--- /dev/null
+++ b/x-pack/test/functional/es_archives/endpoint/resolver/api_feature/mappings.json
@@ -0,0 +1,2703 @@
+{
+  "type": "index",
+  "value": {
+    "aliases": {
+      "endgame-4.21.0": {
+        "is_write_index": true
+      }
+    },
+    "index": "endgame-4.21.0-000001",
+    "mappings": {
+      "_meta": {
+        "version": "1.4.0"
+      },
+      "date_detection": false,
+      "dynamic_templates": [
+        {
+          "strings_as_keyword": {
+            "mapping": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "match_mapping_type": "string"
+          }
+        }
+      ],
+      "properties": {
+        "@timestamp": {
+          "type": "date"
+        },
+        "agent": {
+          "properties": {
+            "ephemeral_id": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "id": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "name": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "type": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "version": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "as": {
+          "properties": {
+            "number": {
+              "type": "long"
+            },
+            "organization": {
+              "properties": {
+                "name": {
+                  "fields": {
+                    "text": {
+                      "norms": false,
+                      "type": "text"
+                    }
+                  },
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            }
+          }
+        },
+        "client": {
+          "properties": {
+            "address": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "as": {
+              "properties": {
+                "number": {
+                  "type": "long"
+                },
+                "organization": {
+                  "properties": {
+                    "name": {
+                      "fields": {
+                        "text": {
+                          "norms": false,
+                          "type": "text"
+                        }
+                      },
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    }
+                  }
+                }
+              }
+            },
+            "bytes": {
+              "type": "long"
+            },
+            "domain": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "geo": {
+              "properties": {
+                "city_name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "continent_name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "country_iso_code": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "country_name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "location": {
+                  "type": "geo_point"
+                },
+                "name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "region_iso_code": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "region_name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "ip": {
+              "type": "ip"
+            },
+            "mac": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "nat": {
+              "properties": {
+                "ip": {
+                  "type": "ip"
+                },
+                "port": {
+                  "type": "long"
+                }
+              }
+            },
+            "packets": {
+              "type": "long"
+            },
+            "port": {
+              "type": "long"
+            },
+            "registered_domain": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "top_level_domain": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "user": {
+              "properties": {
+                "domain": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "email": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "full_name": {
+                  "fields": {
+                    "text": {
+                      "norms": false,
+                      "type": "text"
+                    }
+                  },
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "group": {
+                  "properties": {
+                    "domain": {
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    },
+                    "id": {
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    },
+                    "name": {
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    }
+                  }
+                },
+                "hash": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "id": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "name": {
+                  "fields": {
+                    "text": {
+                      "norms": false,
+                      "type": "text"
+                    }
+                  },
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            }
+          }
+        },
+        "cloud": {
+          "properties": {
+            "account": {
+              "properties": {
+                "id": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "availability_zone": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "instance": {
+              "properties": {
+                "id": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "machine": {
+              "properties": {
+                "type": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "provider": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "region": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "container": {
+          "properties": {
+            "id": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "image": {
+              "properties": {
+                "name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "tag": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "labels": {
+              "type": "object"
+            },
+            "name": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "runtime": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "destination": {
+          "properties": {
+            "address": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "as": {
+              "properties": {
+                "number": {
+                  "type": "long"
+                },
+                "organization": {
+                  "properties": {
+                    "name": {
+                      "fields": {
+                        "text": {
+                          "norms": false,
+                          "type": "text"
+                        }
+                      },
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    }
+                  }
+                }
+              }
+            },
+            "bytes": {
+              "type": "long"
+            },
+            "domain": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "geo": {
+              "properties": {
+                "city_name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "continent_name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "country_iso_code": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "country_name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "location": {
+                  "type": "geo_point"
+                },
+                "name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "region_iso_code": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "region_name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "ip": {
+              "type": "ip"
+            },
+            "mac": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "nat": {
+              "properties": {
+                "ip": {
+                  "type": "ip"
+                },
+                "port": {
+                  "type": "long"
+                }
+              }
+            },
+            "packets": {
+              "type": "long"
+            },
+            "port": {
+              "type": "long"
+            },
+            "registered_domain": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "top_level_domain": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "user": {
+              "properties": {
+                "domain": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "email": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "full_name": {
+                  "fields": {
+                    "text": {
+                      "norms": false,
+                      "type": "text"
+                    }
+                  },
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "group": {
+                  "properties": {
+                    "domain": {
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    },
+                    "id": {
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    },
+                    "name": {
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    }
+                  }
+                },
+                "hash": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "id": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "name": {
+                  "fields": {
+                    "text": {
+                      "norms": false,
+                      "type": "text"
+                    }
+                  },
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            }
+          }
+        },
+        "dns": {
+          "properties": {
+            "answers": {
+              "properties": {
+                "class": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "data": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "ttl": {
+                  "type": "long"
+                },
+                "type": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "header_flags": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "id": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "op_code": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "question": {
+              "properties": {
+                "class": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "registered_domain": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "subdomain": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "top_level_domain": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "type": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "resolved_ip": {
+              "type": "ip"
+            },
+            "response_code": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "type": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "ecs": {
+          "properties": {
+            "version": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "endgame": {
+          "properties": {
+            "command_line": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "destination_address": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "destination_port": {
+              "type": "long"
+            },
+            "effective_gid": {
+              "type": "long"
+            },
+            "effective_group_name": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "effective_uid": {
+              "type": "long"
+            },
+            "effective_user_name": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "event_message": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "event_subtype_full": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "event_type_full": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "exit_code": {
+              "type": "long"
+            },
+            "exit_code_full": {
+              "type": "long"
+            },
+            "file_name": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "file_path": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "md5": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "old_file_path": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "opcode": {
+              "type": "long"
+            },
+            "parent_process_name": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "parent_process_path": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "pid": {
+              "type": "long"
+            },
+            "ppid": {
+              "type": "long"
+            },
+            "process_name": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "process_path": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "protocol": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "real_gid": {
+              "type": "long"
+            },
+            "real_group_name": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "real_uid": {
+              "type": "long"
+            },
+            "real_user_name": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "serial_event_id": {
+              "type": "long"
+            },
+            "session_id": {
+              "type": "long"
+            },
+            "sha1": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "sha256": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "source_address": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "source_port": {
+              "type": "long"
+            },
+            "tid": {
+              "type": "long"
+            },
+            "timestamp": {
+              "type": "long"
+            },
+            "total_in_bytes": {
+              "type": "long"
+            },
+            "total_out_bytes": {
+              "type": "long"
+            },
+            "unique_pid": {
+              "type": "long"
+            },
+            "unique_ppid": {
+              "type": "long"
+            }
+          }
+        },
+        "error": {
+          "properties": {
+            "code": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "id": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "message": {
+              "norms": false,
+              "type": "text"
+            },
+            "stack_trace": {
+              "doc_values": false,
+              "fields": {
+                "text": {
+                  "norms": false,
+                  "type": "text"
+                }
+              },
+              "ignore_above": 1024,
+              "index": false,
+              "type": "keyword"
+            },
+            "type": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "event": {
+          "properties": {
+            "action": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "category": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "code": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "created": {
+              "type": "date"
+            },
+            "dataset": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "duration": {
+              "type": "long"
+            },
+            "end": {
+              "type": "date"
+            },
+            "hash": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "id": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "ingested": {
+              "type": "date"
+            },
+            "kind": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "module": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "original": {
+              "doc_values": false,
+              "ignore_above": 1024,
+              "index": false,
+              "type": "keyword"
+            },
+            "outcome": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "provider": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "risk_score": {
+              "type": "float"
+            },
+            "risk_score_norm": {
+              "type": "float"
+            },
+            "sequence": {
+              "type": "long"
+            },
+            "severity": {
+              "type": "long"
+            },
+            "start": {
+              "type": "date"
+            },
+            "timezone": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "type": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "file": {
+          "properties": {
+            "accessed": {
+              "type": "date"
+            },
+            "attributes": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "created": {
+              "type": "date"
+            },
+            "ctime": {
+              "type": "date"
+            },
+            "device": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "directory": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "drive_letter": {
+              "ignore_above": 1,
+              "type": "keyword"
+            },
+            "extension": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "gid": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "group": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "hash": {
+              "properties": {
+                "md5": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "sha1": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "sha256": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "sha512": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "inode": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "mode": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "mtime": {
+              "type": "date"
+            },
+            "name": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "owner": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "path": {
+              "fields": {
+                "text": {
+                  "norms": false,
+                  "type": "text"
+                }
+              },
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "size": {
+              "type": "long"
+            },
+            "target_path": {
+              "fields": {
+                "text": {
+                  "norms": false,
+                  "type": "text"
+                }
+              },
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "type": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "uid": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "geo": {
+          "properties": {
+            "city_name": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "continent_name": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "country_iso_code": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "country_name": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "location": {
+              "type": "geo_point"
+            },
+            "name": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "region_iso_code": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "region_name": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "group": {
+          "properties": {
+            "domain": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "id": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "name": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "hash": {
+          "properties": {
+            "md5": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "sha1": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "sha256": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "sha512": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "host": {
+          "properties": {
+            "architecture": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "domain": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "geo": {
+              "properties": {
+                "city_name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "continent_name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "country_iso_code": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "country_name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "location": {
+                  "type": "geo_point"
+                },
+                "name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "region_iso_code": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "region_name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "hostname": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "id": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "ip": {
+              "type": "ip"
+            },
+            "mac": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "name": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "os": {
+              "properties": {
+                "family": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "full": {
+                  "fields": {
+                    "text": {
+                      "norms": false,
+                      "type": "text"
+                    }
+                  },
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "kernel": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "name": {
+                  "fields": {
+                    "text": {
+                      "norms": false,
+                      "type": "text"
+                    }
+                  },
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "platform": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "version": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "type": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "uptime": {
+              "type": "long"
+            },
+            "user": {
+              "properties": {
+                "domain": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "email": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "full_name": {
+                  "fields": {
+                    "text": {
+                      "norms": false,
+                      "type": "text"
+                    }
+                  },
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "group": {
+                  "properties": {
+                    "domain": {
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    },
+                    "id": {
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    },
+                    "name": {
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    }
+                  }
+                },
+                "hash": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "id": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "name": {
+                  "fields": {
+                    "text": {
+                      "norms": false,
+                      "type": "text"
+                    }
+                  },
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            }
+          }
+        },
+        "http": {
+          "properties": {
+            "request": {
+              "properties": {
+                "body": {
+                  "properties": {
+                    "bytes": {
+                      "type": "long"
+                    },
+                    "content": {
+                      "fields": {
+                        "text": {
+                          "norms": false,
+                          "type": "text"
+                        }
+                      },
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    }
+                  }
+                },
+                "bytes": {
+                  "type": "long"
+                },
+                "method": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "referrer": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "response": {
+              "properties": {
+                "body": {
+                  "properties": {
+                    "bytes": {
+                      "type": "long"
+                    },
+                    "content": {
+                      "fields": {
+                        "text": {
+                          "norms": false,
+                          "type": "text"
+                        }
+                      },
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    }
+                  }
+                },
+                "bytes": {
+                  "type": "long"
+                },
+                "status_code": {
+                  "type": "long"
+                }
+              }
+            },
+            "version": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "labels": {
+          "properties": {
+            "account_id": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "endpoint_id": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "log": {
+          "properties": {
+            "level": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "logger": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "origin": {
+              "properties": {
+                "file": {
+                  "properties": {
+                    "line": {
+                      "type": "integer"
+                    },
+                    "name": {
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    }
+                  }
+                },
+                "function": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "original": {
+              "doc_values": false,
+              "ignore_above": 1024,
+              "index": false,
+              "type": "keyword"
+            },
+            "syslog": {
+              "properties": {
+                "facility": {
+                  "properties": {
+                    "code": {
+                      "type": "long"
+                    },
+                    "name": {
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    }
+                  }
+                },
+                "priority": {
+                  "type": "long"
+                },
+                "severity": {
+                  "properties": {
+                    "code": {
+                      "type": "long"
+                    },
+                    "name": {
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    }
+                  }
+                }
+              }
+            }
+          }
+        },
+        "message": {
+          "norms": false,
+          "type": "text"
+        },
+        "network": {
+          "properties": {
+            "application": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "bytes": {
+              "type": "long"
+            },
+            "community_id": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "direction": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "forwarded_ip": {
+              "type": "ip"
+            },
+            "iana_number": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "name": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "packets": {
+              "type": "long"
+            },
+            "protocol": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "transport": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "type": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "observer": {
+          "properties": {
+            "geo": {
+              "properties": {
+                "city_name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "continent_name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "country_iso_code": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "country_name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "location": {
+                  "type": "geo_point"
+                },
+                "name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "region_iso_code": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "region_name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "hostname": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "ip": {
+              "type": "ip"
+            },
+            "mac": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "name": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "os": {
+              "properties": {
+                "family": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "full": {
+                  "fields": {
+                    "text": {
+                      "norms": false,
+                      "type": "text"
+                    }
+                  },
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "kernel": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "name": {
+                  "fields": {
+                    "text": {
+                      "norms": false,
+                      "type": "text"
+                    }
+                  },
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "platform": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "version": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "product": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "serial_number": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "type": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "vendor": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "version": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "organization": {
+          "properties": {
+            "id": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "name": {
+              "fields": {
+                "text": {
+                  "norms": false,
+                  "type": "text"
+                }
+              },
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "os": {
+          "properties": {
+            "family": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "full": {
+              "fields": {
+                "text": {
+                  "norms": false,
+                  "type": "text"
+                }
+              },
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "kernel": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "name": {
+              "fields": {
+                "text": {
+                  "norms": false,
+                  "type": "text"
+                }
+              },
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "platform": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "version": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "package": {
+          "properties": {
+            "architecture": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "build_version": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "checksum": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "description": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "install_scope": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "installed": {
+              "type": "date"
+            },
+            "license": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "name": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "path": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "reference": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "size": {
+              "type": "long"
+            },
+            "type": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "version": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "process": {
+          "properties": {
+            "args": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "args_count": {
+              "type": "long"
+            },
+            "command_line": {
+              "fields": {
+                "text": {
+                  "norms": false,
+                  "type": "text"
+                }
+              },
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "executable": {
+              "fields": {
+                "text": {
+                  "norms": false,
+                  "type": "text"
+                }
+              },
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "exit_code": {
+              "type": "long"
+            },
+            "hash": {
+              "properties": {
+                "md5": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "sha1": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "sha256": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "sha512": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "name": {
+              "fields": {
+                "text": {
+                  "norms": false,
+                  "type": "text"
+                }
+              },
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "parent": {
+              "properties": {
+                "args": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "args_count": {
+                  "type": "long"
+                },
+                "command_line": {
+                  "fields": {
+                    "text": {
+                      "norms": false,
+                      "type": "text"
+                    }
+                  },
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "executable": {
+                  "fields": {
+                    "text": {
+                      "norms": false,
+                      "type": "text"
+                    }
+                  },
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "exit_code": {
+                  "type": "long"
+                },
+                "name": {
+                  "fields": {
+                    "text": {
+                      "norms": false,
+                      "type": "text"
+                    }
+                  },
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "pgid": {
+                  "type": "long"
+                },
+                "pid": {
+                  "type": "long"
+                },
+                "ppid": {
+                  "type": "long"
+                },
+                "start": {
+                  "type": "date"
+                },
+                "thread": {
+                  "properties": {
+                    "id": {
+                      "type": "long"
+                    },
+                    "name": {
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    }
+                  }
+                },
+                "title": {
+                  "fields": {
+                    "text": {
+                      "norms": false,
+                      "type": "text"
+                    }
+                  },
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "uptime": {
+                  "type": "long"
+                },
+                "working_directory": {
+                  "fields": {
+                    "text": {
+                      "norms": false,
+                      "type": "text"
+                    }
+                  },
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "pgid": {
+              "type": "long"
+            },
+            "pid": {
+              "type": "long"
+            },
+            "ppid": {
+              "type": "long"
+            },
+            "start": {
+              "type": "date"
+            },
+            "thread": {
+              "properties": {
+                "id": {
+                  "type": "long"
+                },
+                "name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "title": {
+              "fields": {
+                "text": {
+                  "norms": false,
+                  "type": "text"
+                }
+              },
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "uptime": {
+              "type": "long"
+            },
+            "working_directory": {
+              "fields": {
+                "text": {
+                  "norms": false,
+                  "type": "text"
+                }
+              },
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "registry": {
+          "properties": {
+            "data": {
+              "properties": {
+                "bytes": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "strings": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "type": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "hive": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "key": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "path": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "value": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "related": {
+          "properties": {
+            "ip": {
+              "type": "ip"
+            },
+            "user": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "rule": {
+          "properties": {
+            "category": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "description": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "id": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "name": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "reference": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "ruleset": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "uuid": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "version": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "server": {
+          "properties": {
+            "address": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "as": {
+              "properties": {
+                "number": {
+                  "type": "long"
+                },
+                "organization": {
+                  "properties": {
+                    "name": {
+                      "fields": {
+                        "text": {
+                          "norms": false,
+                          "type": "text"
+                        }
+                      },
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    }
+                  }
+                }
+              }
+            },
+            "bytes": {
+              "type": "long"
+            },
+            "domain": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "geo": {
+              "properties": {
+                "city_name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "continent_name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "country_iso_code": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "country_name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "location": {
+                  "type": "geo_point"
+                },
+                "name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "region_iso_code": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "region_name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "ip": {
+              "type": "ip"
+            },
+            "mac": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "nat": {
+              "properties": {
+                "ip": {
+                  "type": "ip"
+                },
+                "port": {
+                  "type": "long"
+                }
+              }
+            },
+            "packets": {
+              "type": "long"
+            },
+            "port": {
+              "type": "long"
+            },
+            "registered_domain": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "top_level_domain": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "user": {
+              "properties": {
+                "domain": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "email": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "full_name": {
+                  "fields": {
+                    "text": {
+                      "norms": false,
+                      "type": "text"
+                    }
+                  },
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "group": {
+                  "properties": {
+                    "domain": {
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    },
+                    "id": {
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    },
+                    "name": {
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    }
+                  }
+                },
+                "hash": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "id": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "name": {
+                  "fields": {
+                    "text": {
+                      "norms": false,
+                      "type": "text"
+                    }
+                  },
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            }
+          }
+        },
+        "service": {
+          "properties": {
+            "ephemeral_id": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "id": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "name": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "node": {
+              "properties": {
+                "name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "state": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "type": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "version": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "source": {
+          "properties": {
+            "address": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "as": {
+              "properties": {
+                "number": {
+                  "type": "long"
+                },
+                "organization": {
+                  "properties": {
+                    "name": {
+                      "fields": {
+                        "text": {
+                          "norms": false,
+                          "type": "text"
+                        }
+                      },
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    }
+                  }
+                }
+              }
+            },
+            "bytes": {
+              "type": "long"
+            },
+            "domain": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "geo": {
+              "properties": {
+                "city_name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "continent_name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "country_iso_code": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "country_name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "location": {
+                  "type": "geo_point"
+                },
+                "name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "region_iso_code": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "region_name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "ip": {
+              "type": "ip"
+            },
+            "mac": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "nat": {
+              "properties": {
+                "ip": {
+                  "type": "ip"
+                },
+                "port": {
+                  "type": "long"
+                }
+              }
+            },
+            "packets": {
+              "type": "long"
+            },
+            "port": {
+              "type": "long"
+            },
+            "registered_domain": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "top_level_domain": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "user": {
+              "properties": {
+                "domain": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "email": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "full_name": {
+                  "fields": {
+                    "text": {
+                      "norms": false,
+                      "type": "text"
+                    }
+                  },
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "group": {
+                  "properties": {
+                    "domain": {
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    },
+                    "id": {
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    },
+                    "name": {
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    }
+                  }
+                },
+                "hash": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "id": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "name": {
+                  "fields": {
+                    "text": {
+                      "norms": false,
+                      "type": "text"
+                    }
+                  },
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            }
+          }
+        },
+        "tags": {
+          "ignore_above": 1024,
+          "type": "keyword"
+        },
+        "threat": {
+          "properties": {
+            "framework": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "tactic": {
+              "properties": {
+                "id": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "reference": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "technique": {
+              "properties": {
+                "id": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "name": {
+                  "fields": {
+                    "text": {
+                      "norms": false,
+                      "type": "text"
+                    }
+                  },
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "reference": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            }
+          }
+        },
+        "tls": {
+          "properties": {
+            "cipher": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "client": {
+              "properties": {
+                "certificate": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "certificate_chain": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "hash": {
+                  "properties": {
+                    "md5": {
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    },
+                    "sha1": {
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    },
+                    "sha256": {
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    }
+                  }
+                },
+                "issuer": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "ja3": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "not_after": {
+                  "type": "date"
+                },
+                "not_before": {
+                  "type": "date"
+                },
+                "server_name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "subject": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "supported_ciphers": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "curve": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "established": {
+              "type": "boolean"
+            },
+            "next_protocol": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "resumed": {
+              "type": "boolean"
+            },
+            "server": {
+              "properties": {
+                "certificate": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "certificate_chain": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "hash": {
+                  "properties": {
+                    "md5": {
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    },
+                    "sha1": {
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    },
+                    "sha256": {
+                      "ignore_above": 1024,
+                      "type": "keyword"
+                    }
+                  }
+                },
+                "issuer": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "ja3s": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "not_after": {
+                  "type": "date"
+                },
+                "not_before": {
+                  "type": "date"
+                },
+                "subject": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "version": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "version_protocol": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "trace": {
+          "properties": {
+            "id": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "transaction": {
+          "properties": {
+            "id": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "url": {
+          "properties": {
+            "domain": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "extension": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "fragment": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "full": {
+              "fields": {
+                "text": {
+                  "norms": false,
+                  "type": "text"
+                }
+              },
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "original": {
+              "fields": {
+                "text": {
+                  "norms": false,
+                  "type": "text"
+                }
+              },
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "password": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "path": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "port": {
+              "type": "long"
+            },
+            "query": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "registered_domain": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "scheme": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "top_level_domain": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "username": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "user": {
+          "properties": {
+            "domain": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "email": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "full_name": {
+              "fields": {
+                "text": {
+                  "norms": false,
+                  "type": "text"
+                }
+              },
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "group": {
+              "properties": {
+                "domain": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "id": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "hash": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "id": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "name": {
+              "fields": {
+                "text": {
+                  "norms": false,
+                  "type": "text"
+                }
+              },
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "user_agent": {
+          "properties": {
+            "device": {
+              "properties": {
+                "name": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "name": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "original": {
+              "fields": {
+                "text": {
+                  "norms": false,
+                  "type": "text"
+                }
+              },
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "os": {
+              "properties": {
+                "family": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "full": {
+                  "fields": {
+                    "text": {
+                      "norms": false,
+                      "type": "text"
+                    }
+                  },
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "kernel": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "name": {
+                  "fields": {
+                    "text": {
+                      "norms": false,
+                      "type": "text"
+                    }
+                  },
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "platform": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                },
+                "version": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "version": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "vulnerability": {
+          "properties": {
+            "category": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "classification": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "description": {
+              "fields": {
+                "text": {
+                  "norms": false,
+                  "type": "text"
+                }
+              },
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "enumeration": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "id": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "reference": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "report_id": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            },
+            "scanner": {
+              "properties": {
+                "vendor": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "score": {
+              "properties": {
+                "base": {
+                  "type": "float"
+                },
+                "environmental": {
+                  "type": "float"
+                },
+                "temporal": {
+                  "type": "float"
+                },
+                "version": {
+                  "ignore_above": 1024,
+                  "type": "keyword"
+                }
+              }
+            },
+            "severity": {
+              "ignore_above": 1024,
+              "type": "keyword"
+            }
+          }
+        },
+        "winlog": {
+          "properties": {
+            "opcode": {
+              "type": "long"
+            }
+          }
+        }
+      }
+    },
+    "settings": {
+      "index": {
+        "lifecycle": {
+          "name": "endgame_policy-4.21.0",
+          "rollover_alias": "endgame-4.21.0"
+        },
+        "mapping": {
+          "ignore_malformed": "true",
+          "total_fields": {
+            "limit": "10000"
+          }
+        },
+        "number_of_replicas": "0",
+        "number_of_shards": "5",
+        "refresh_interval": "5s"
+      }
+    }
+  }
+}
\ No newline at end of file

From 0a6c748cc837c016901f69ff05d81395aa2d41c8 Mon Sep 17 00:00:00 2001
From: Kerry Gallagher <k.gallagher.05@gmail.com>
Date: Tue, 18 Feb 2020 19:22:27 +0000
Subject: [PATCH 037/174] [Logs / Metrics] New Platform migration (full
 cutover) (#54583)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Fully migrates metrics and logs to the NP

Co-authored-by: Jason Rhodes <jason.matthew.rhodes@gmail.com>
Co-authored-by: John Schulz <github.com@jfsiii.org>
Co-authored-by: Felix Stürmer <weltenwort@users.noreply.github.com>
---
 test/functional/page_objects/common_page.ts   |   2 +
 x-pack/.i18nrc.json                           |   2 +-
 .../app/ErrorGroupDetails/index.tsx           |   2 +-
 .../app/ErrorGroupOverview/index.tsx          |   2 +-
 .../components/app/ServiceOverview/index.tsx  |   2 +-
 .../AddEditFlyout/index.tsx                   |   7 +-
 .../AddEditFlyout/saveConfig.ts               |   8 +-
 .../Settings/AgentConfigurations/index.tsx    |   2 +-
 .../components/app/TraceOverview/index.tsx    |   2 +-
 .../app/TransactionDetails/index.tsx          |   2 +-
 .../app/TransactionOverview/index.tsx         |   2 +-
 .../shared/Links/InfraLink.test.tsx           |   6 +-
 .../components/shared/Links/InfraLink.tsx     |  13 +-
 .../__test__/sections.test.ts                 |  26 ++-
 .../shared/TransactionActionMenu/sections.ts  |   7 +
 .../TransactionBreakdownGraph/index.tsx       |  17 +-
 .../TransactionBreakdownKpiList.tsx           |   6 +-
 .../shared/TransactionBreakdown/index.tsx     |  10 +-
 .../apm/public/new-platform/plugin.tsx        |  33 +--
 x-pack/legacy/plugins/infra/index.ts          | 112 +---------
 x-pack/legacy/plugins/infra/package.json      |  19 --
 x-pack/legacy/plugins/infra/public/app.ts     |  56 -----
 .../infra/public/hooks/use_track_metric.tsx   |  89 --------
 x-pack/legacy/plugins/infra/public/index.ts   |  14 --
 .../infra/public/legacy_register_feature.ts   |  15 --
 .../infra/public/new_platform_plugin.ts       |  74 -------
 x-pack/legacy/plugins/infra/public/routes.tsx |  66 ------
 .../plugins/infra/public/utils/fetch.ts       |  17 --
 .../public/utils/use_kibana_injected_var.ts   |  18 --
 .../infra/server/new_platform_index.ts        |  15 --
 .../infra/server/new_platform_plugin.ts       | 137 ------------
 .../server/routes/metrics_explorer/types.ts   |  36 ---
 x-pack/legacy/plugins/infra/tsconfig.json     |   3 -
 x-pack/legacy/plugins/infra/yarn.lock         |   1 -
 .../logs/__snapshots__/logs.test.js.snap      |   8 +-
 .../monitoring/public/components/logs/logs.js |   2 +-
 .../__snapshots__/get_infra_href.test.ts.snap |  18 +-
 .../get_logging_href.test.ts.snap             |  12 +-
 .../get_infra_href.ts                         |  13 +-
 .../get_logging_href.ts                       |   8 +-
 .../plugins/uptime/public/pages/monitor.tsx   |   2 +-
 .../plugins/uptime/public/pages/overview.tsx  |   2 +-
 x-pack/{legacy => }/plugins/infra/README.md   |   0
 .../infra/common/color_palette.test.ts        |   0
 .../plugins/infra/common/color_palette.ts     |   0
 .../infra/common/ecs_allowed_list.test.ts     |   0
 .../plugins/infra/common/ecs_allowed_list.ts  |   0
 .../plugins/infra/common/errors/index.ts      |   0
 .../plugins/infra/common/errors/metrics.ts    |   0
 .../infra/common/graphql/root/index.ts        |   0
 .../infra/common/graphql/root/schema.gql.ts   |   0
 .../graphql/shared/fragments.gql_query.ts     |   0
 .../infra/common/graphql/shared/index.ts      |   0
 .../infra/common/graphql/shared/schema.gql.ts |   0
 .../plugins/infra/common/graphql/types.ts     |   0
 .../plugins/infra/common/http_api/index.ts    |   0
 .../common/http_api/inventory_meta_api.ts     |   0
 .../common/http_api/ip_to_hostname/index.ts   |   9 +
 .../common/http_api/log_analysis/index.ts     |   0
 .../http_api/log_analysis/results/index.ts    |   0
 .../results/log_entry_categories.ts           |   0
 .../results/log_entry_category_datasets.ts    |   0
 .../log_analysis/results/log_entry_rate.ts    |   0
 .../http_api/log_analysis/validation/index.ts |   0
 .../validation/log_entry_rate_indices.ts      |   0
 .../common/http_api/log_entries/common.ts     |   0
 .../common/http_api/log_entries/entries.ts    |   0
 .../common/http_api/log_entries/highlights.ts |   0
 .../common/http_api/log_entries/index.ts      |   0
 .../infra/common/http_api/log_entries/item.ts |   0
 .../common/http_api/log_entries/summary.ts    |   0
 .../log_entries/summary_highlights.ts         |   0
 .../infra/common/http_api/metadata_api.ts     |   0
 .../common/http_api/metrics_explorer/index.ts |  18 ++
 .../infra/common/http_api/node_details_api.ts |   0
 .../infra/common/http_api/shared/errors.ts    |   0
 .../infra/common/http_api/shared/index.ts     |   0
 .../http_api/shared/metric_statistics.ts      |   0
 .../common/http_api/shared/time_range.ts      |   0
 .../infra/common/http_api/shared/timing.ts    |   0
 .../infra/common/http_api/snapshot_api.ts     |   0
 .../common/inventory_models/aws_ec2/index.ts  |   0
 .../inventory_models/aws_ec2/layout.tsx       |   9 +-
 .../inventory_models/aws_ec2/metrics/index.ts |   0
 .../aws_ec2/metrics/snapshot/cpu.ts           |   0
 .../metrics/snapshot/disk_io_read_bytes.ts    |   0
 .../metrics/snapshot/disk_io_write_bytes.ts   |   0
 .../aws_ec2/metrics/snapshot/rx.ts            |   0
 .../aws_ec2/metrics/snapshot/tx.ts            |   0
 .../metrics/tsvb/aws_ec2_cpu_utilization.ts   |   0
 .../metrics/tsvb/aws_ec2_diskio_bytes.ts      |   0
 .../metrics/tsvb/aws_ec2_network_traffic.ts   |   0
 .../aws_ec2/toolbar_items.tsx                 |   1 +
 .../common/inventory_models/aws_rds/index.ts  |   0
 .../inventory_models/aws_rds/layout.tsx       |   8 +-
 .../inventory_models/aws_rds/metrics/index.ts |   0
 .../aws_rds/metrics/snapshot/cpu.ts           |   0
 .../snapshot/rds_active_transactions.ts       |   0
 .../metrics/snapshot/rds_connections.ts       |   0
 .../aws_rds/metrics/snapshot/rds_latency.ts   |   0
 .../metrics/snapshot/rds_queries_executed.ts  |   0
 .../tsvb/aws_rds_active_transactions.ts       |   0
 .../metrics/tsvb/aws_rds_connections.ts       |   0
 .../aws_rds/metrics/tsvb/aws_rds_cpu_total.ts |   0
 .../aws_rds/metrics/tsvb/aws_rds_latency.ts   |   0
 .../metrics/tsvb/aws_rds_queries_executed.ts  |   0
 .../aws_rds/toolbar_items.tsx                 |   2 +-
 .../common/inventory_models/aws_s3/index.ts   |   0
 .../common/inventory_models/aws_s3/layout.tsx |   8 +-
 .../inventory_models/aws_s3/metrics/index.ts  |   0
 .../aws_s3/metrics/snapshot/s3_bucket_size.ts |   0
 .../metrics/snapshot/s3_download_bytes.ts     |   0
 .../metrics/snapshot/s3_number_of_objects.ts  |   0
 .../metrics/snapshot/s3_total_requests.ts     |   0
 .../metrics/snapshot/s3_upload_bytes.ts       |   0
 .../aws_s3/metrics/tsvb/aws_s3_bucket_size.ts |   0
 .../metrics/tsvb/aws_s3_download_bytes.ts     |   0
 .../metrics/tsvb/aws_s3_number_of_objects.ts  |   0
 .../metrics/tsvb/aws_s3_total_requests.ts     |   0
 .../metrics/tsvb/aws_s3_upload_bytes.ts       |   0
 .../inventory_models/aws_s3/toolbar_items.tsx |   1 +
 .../common/inventory_models/aws_sqs/index.ts  |   0
 .../inventory_models/aws_sqs/layout.tsx       |   8 +-
 .../inventory_models/aws_sqs/metrics/index.ts |   0
 .../metrics/snapshot/sqs_messages_delayed.ts  |   0
 .../metrics/snapshot/sqs_messages_empty.ts    |   0
 .../metrics/snapshot/sqs_messages_sent.ts     |   0
 .../metrics/snapshot/sqs_messages_visible.ts  |   0
 .../metrics/snapshot/sqs_oldest_message.ts    |   0
 .../metrics/tsvb/aws_sqs_messages_delayed.ts  |   0
 .../metrics/tsvb/aws_sqs_messages_empty.ts    |   0
 .../metrics/tsvb/aws_sqs_messages_sent.ts     |   0
 .../metrics/tsvb/aws_sqs_messages_visible.ts  |   0
 .../metrics/tsvb/aws_sqs_oldest_message.ts    |   0
 .../aws_sqs/toolbar_items.tsx                 |   1 +
 .../inventory_models/container/index.ts       |   0
 .../inventory_models/container/layout.tsx     |  10 +-
 .../container/metrics/index.ts                |   0
 .../container/metrics/snapshot/cpu.ts         |   0
 .../container/metrics/snapshot/memory.ts      |   0
 .../container/metrics/snapshot/rx.ts          |   0
 .../container/metrics/snapshot/tx.ts          |   0
 .../metrics/tsvb/container_cpu_kernel.ts      |   0
 .../metrics/tsvb/container_cpu_usage.ts       |   0
 .../metrics/tsvb/container_disk_io_bytes.ts   |   0
 .../metrics/tsvb/container_diskio_ops.ts      |   0
 .../metrics/tsvb/container_memory.ts          |   0
 .../metrics/tsvb/container_network_traffic.ts |   0
 .../metrics/tsvb/container_overview.ts        |   0
 .../container/toolbar_items.tsx               |   1 +
 .../inventory_models/create_tsvb_model.ts     |   0
 .../common/inventory_models/host/index.ts     |   0
 .../common/inventory_models/host/layout.tsx   |  10 +-
 .../inventory_models/host/metrics/index.ts    |   0
 .../host/metrics/snapshot/cpu.ts              |   0
 .../host/metrics/snapshot/load.ts             |   0
 .../host/metrics/snapshot/log_rate.ts         |   0
 .../host/metrics/snapshot/memory.ts           |   0
 .../host/metrics/snapshot/rx.ts               |   0
 .../host/metrics/snapshot/tx.ts               |   0
 .../host/metrics/tsvb/host_cpu_usage.ts       |   0
 .../host/metrics/tsvb/host_docker_info.ts     |   0
 .../host/metrics/tsvb/host_docker_overview.ts |   0
 .../metrics/tsvb/host_docker_top_5_by_cpu.ts  |   0
 .../tsvb/host_docker_top_5_by_memory.ts       |   0
 .../host/metrics/tsvb/host_filesystem.ts      |   0
 .../host/metrics/tsvb/host_k8s_cpu_cap.ts     |   0
 .../host/metrics/tsvb/host_k8s_disk_cap.ts    |   0
 .../host/metrics/tsvb/host_k8s_memory_cap.ts  |   0
 .../host/metrics/tsvb/host_k8s_overview.ts    |   0
 .../host/metrics/tsvb/host_k8s_pod_cap.ts     |   0
 .../host/metrics/tsvb/host_load.ts            |   0
 .../host/metrics/tsvb/host_memory_usage.ts    |   0
 .../host/metrics/tsvb/host_network_traffic.ts |   0
 .../host/metrics/tsvb/host_system_overview.ts |   0
 .../inventory_models/host/toolbar_items.tsx   |   1 +
 .../infra/common/inventory_models/index.ts    |   0
 .../common/inventory_models/intl_strings.ts   |   0
 .../infra/common/inventory_models/layouts.ts  |   1 +
 .../infra/common/inventory_models/metrics.ts  |   0
 .../common/inventory_models/pod/index.ts      |   0
 .../common/inventory_models/pod/layout.tsx    |  10 +-
 .../inventory_models/pod/metrics/index.ts     |   0
 .../pod/metrics/snapshot/cpu.ts               |   0
 .../pod/metrics/snapshot/memory.ts            |   0
 .../pod/metrics/snapshot/rx.ts                |   0
 .../pod/metrics/snapshot/tx.ts                |   0
 .../pod/metrics/tsvb/pod_cpu_usage.ts         |   0
 .../pod/metrics/tsvb/pod_log_usage.ts         |   0
 .../pod/metrics/tsvb/pod_memory_usage.ts      |   0
 .../pod/metrics/tsvb/pod_network_traffic.ts   |   0
 .../pod/metrics/tsvb/pod_overview.ts          |   0
 .../inventory_models/pod/toolbar_items.tsx    |   1 +
 .../compontents/cloud_toolbar_items.tsx       |   3 +
 .../metrics_and_groupby_toolbar_items.tsx     |   4 +
 .../inventory_models/shared/layouts/aws.tsx   |   8 +-
 .../inventory_models/shared/layouts/nginx.tsx |   7 +-
 .../inventory_models/shared/metrics/index.ts  |   0
 .../shared/metrics/required_metrics.ts        |   0
 .../shared/metrics/snapshot/count.ts          |   0
 .../metrics/snapshot/network_traffic.ts       |   0
 .../network_traffic_with_interfaces.ts        |   0
 .../shared/metrics/snapshot/rate.ts           |   0
 .../metrics/tsvb/aws_cpu_utilization.ts       |   0
 .../shared/metrics/tsvb/aws_diskio_bytes.ts   |   0
 .../shared/metrics/tsvb/aws_diskio_ops.ts     |   0
 .../shared/metrics/tsvb/aws_network_bytes.ts  |   0
 .../metrics/tsvb/aws_network_packets.ts       |   0
 .../shared/metrics/tsvb/aws_overview.ts       |   0
 .../metrics/tsvb/nginx_active_connections.ts  |   0
 .../shared/metrics/tsvb/nginx_hits.ts         |   0
 .../shared/metrics/tsvb/nginx_request_rate.ts |   0
 .../tsvb/nginx_requests_per_connection.ts     |   0
 .../infra/common/inventory_models/toolbars.ts |   1 +
 .../infra/common/inventory_models/types.ts    |   0
 .../infra/common/log_analysis/index.ts        |   0
 .../common/log_analysis/job_parameters.ts     |   0
 .../infra/common/log_analysis/log_analysis.ts |   0
 .../log_analysis/log_analysis_results.ts      |   0
 .../log_entry_categories_analysis.ts          |   0
 .../log_analysis/log_entry_rate_analysis.ts   |   0
 .../plugins/infra/common/log_entry/index.ts   |   0
 .../infra/common/log_entry/log_entry.ts       |   0
 .../infra/common/log_search_result/index.ts   |   0
 .../log_search_result/log_search_result.ts    |   0
 .../infra/common/log_search_summary/index.ts  |   0
 .../log_search_summary/log_search_summary.ts  |   0
 .../infra/common/log_text_scale/index.ts      |   0
 .../common/log_text_scale/log_text_scale.ts   |   0
 .../infra/common/performance_tracing.ts       |   0
 .../plugins/infra/common/runtime_types.ts     |   0
 .../common/saved_objects/inventory_view.ts    |   3 +
 .../saved_objects/metrics_explorer_view.ts    |   3 +
 .../plugins/infra/common/time/index.ts        |   0
 .../plugins/infra/common/time/time_key.ts     |   4 +-
 .../plugins/infra/common/time/time_scale.ts   |   0
 .../plugins/infra/common/time/time_unit.ts    |   0
 .../plugins/infra/common/typed_json.ts        |   0
 .../plugins/infra/common/utility_types.ts     |   0
 .../{legacy => }/plugins/infra/docs/arch.md   |   0
 .../plugins/infra/docs/arch_client.md         |   0
 .../plugins/infra/docs/assets/arch.png        | Bin
 .../docs/assets/infra_metricbeat_aws.jpg      | Bin
 .../plugins/infra/docs/graphql.md             |   0
 .../docs/test_setups/infra_metricbeat_aws.md  |   0
 .../infra_metricbeat_docker_nginx.md          |   0
 x-pack/plugins/infra/kibana.json              |   5 +-
 .../plugins/infra/public/apps/start_app.tsx   |  46 ++--
 .../infra/public/apps/start_legacy_app.tsx    | 100 +++++++++
 .../infra/public/components/auto_sizer.tsx    |   2 +-
 .../autocomplete_field/autocomplete_field.tsx |   5 +-
 .../components/autocomplete_field/index.ts    |   0
 .../autocomplete_field/suggestion_item.tsx    |  33 +--
 .../infra/public/components/beta_badge.tsx    |   0
 .../public/components/document_title.tsx      |   0
 .../public/components/empty_states/index.tsx  |   0
 .../components/empty_states/no_data.tsx       |   2 +-
 .../components/empty_states/no_indices.tsx    |   2 +-
 .../infra/public/components/error_page.tsx    |   2 +-
 .../infra/public/components/eui/index.ts      |   0
 .../public/components/eui/toolbar/index.ts    |   0
 .../public/components/eui/toolbar/toolbar.tsx |   2 +-
 .../public/components/fixed_datepicker.tsx    |   2 +-
 .../public/components/formatted_time.tsx      |   0
 .../infra/public/components/header/header.tsx |   2 +-
 .../infra/public/components/header/index.ts   |   0
 .../public/components/help_center_content.tsx |   2 +-
 .../public/components/inventory/layout.tsx    |   0
 .../components/inventory/toolbars/toolbar.tsx |   2 +-
 .../inventory/toolbars/toolbar_wrapper.tsx    |   0
 .../loading/__examples__/index.stories.tsx    |   0
 .../infra/public/components/loading/index.tsx |   2 +-
 .../components/loading_overlay_wrapper.tsx    |   2 +-
 .../infra/public/components/loading_page.tsx  |   0
 .../logging/log_analysis_job_status/index.ts  |   0
 .../job_configuration_outdated_callout.tsx    |   0
 .../job_definition_outdated_callout.tsx       |   0
 .../job_stopped_callout.tsx                   |   0
 .../log_analysis_job_problem_indicator.tsx    |   0
 .../recreate_job_button.tsx                   |   0
 .../recreate_job_callout.tsx                  |   0
 .../analyze_in_ml_button.tsx                  |   4 +-
 .../first_use_callout.tsx                     |   0
 .../logging/log_analysis_results/index.ts     |   0
 .../logging/log_analysis_setup/index.ts       |   0
 .../analysis_setup_indices_form.tsx           |   0
 .../analysis_setup_timerange_form.tsx         |   0
 .../initial_configuration_step/index.ts       |   0
 .../initial_configuration_step.tsx            |   0
 .../initial_configuration_step/validation.tsx |   0
 .../missing_results_privileges_prompt.tsx     |   2 +-
 .../missing_setup_privileges_prompt.tsx       |   2 +-
 .../ml_unavailable_prompt.tsx                 |   2 +-
 .../process_step/create_ml_jobs_button.tsx    |   0
 .../log_analysis_setup/process_step/index.ts  |   0
 .../process_step/process_step.tsx             |   0
 .../process_step/recreate_ml_jobs_button.tsx  |   0
 .../logging/log_analysis_setup/setup_page.tsx |   2 +-
 .../setup_status_unknown_prompt.tsx           |   2 +-
 .../user_management_link.tsx                  |   0
 .../logging/log_customization_menu.tsx        |   2 +-
 .../logging/log_entry_flyout/index.tsx        |   0
 .../log_entry_actions_menu.test.tsx           |   0
 .../log_entry_actions_menu.tsx                |   4 +-
 .../log_entry_flyout/log_entry_flyout.tsx     |   2 +-
 .../logging/log_highlights_menu.tsx           |   2 +-
 .../logging/log_minimap/density_chart.tsx     |   4 +-
 .../log_minimap/highlighted_interval.tsx      |   2 +-
 .../components/logging/log_minimap/index.ts   |   0
 .../logging/log_minimap/log_minimap.tsx       |   2 +-
 .../logging/log_minimap/search_marker.tsx     |   2 +-
 .../log_minimap/search_marker_tooltip.tsx     |   0
 .../logging/log_minimap/search_markers.tsx    |   0
 .../logging/log_minimap/time_ruler.tsx        |   2 +-
 .../components/logging/log_minimap/types.ts   |   0
 .../logging/log_minimap_scale_controls.tsx    |   0
 .../logging/log_search_controls/index.ts      |   0
 .../log_search_buttons.tsx                    |   0
 .../log_search_controls.tsx                   |   0
 .../log_search_controls/log_search_input.tsx  |   2 +-
 .../components/logging/log_statusbar.tsx      |   2 +-
 .../logging/log_text_scale_controls.tsx       |   0
 .../log_text_stream/column_headers.tsx        |   2 +-
 .../logging/log_text_stream/highlighting.tsx  |   2 +-
 .../logging/log_text_stream/index.ts          |   0
 .../logging/log_text_stream/item.ts           |   0
 .../logging/log_text_stream/jump_to_tail.tsx  |   2 +-
 .../log_text_stream/loading_item_view.tsx     |   2 +-
 .../logging/log_text_stream/log_date_row.tsx  |   0
 .../log_text_stream/log_entry_column.tsx      |   2 +-
 .../log_entry_field_column.test.tsx           |   2 +-
 .../log_entry_field_column.tsx                |   2 +-
 .../log_text_stream/log_entry_icon_column.tsx |   2 +-
 .../log_entry_message_column.tsx              |   2 +-
 .../logging/log_text_stream/log_entry_row.tsx |   2 +-
 .../log_entry_timestamp_column.tsx            |   2 +-
 .../log_text_stream/measurable_item_view.tsx  |   0
 .../scrollable_log_text_stream_view.tsx       |   2 +-
 .../logging/log_text_stream/text_styles.tsx   |   2 +-
 .../log_text_stream/vertical_scroll_panel.tsx |  31 ++-
 .../logging/log_text_wrap_controls.tsx        |   0
 .../components/logging/log_time_controls.tsx  |   0
 .../metrics_explorer/aggregation.tsx          |   2 +-
 .../components/metrics_explorer/chart.tsx     |   6 +-
 .../chart_context_menu.test.tsx               | 167 +++++++-------
 .../metrics_explorer/chart_context_menu.tsx   |  13 +-
 .../metrics_explorer/chart_options.tsx        |   0
 .../components/metrics_explorer/charts.tsx    |   2 +-
 .../metrics_explorer/empty_chart.tsx          |   0
 .../components/metrics_explorer/group_by.tsx  |   0
 .../helpers/calculate_domain.ts               |  24 +-
 .../helpers/calculate_domian.test.ts          |   2 +-
 .../helpers/create_formatter_for_metric.ts    |   2 +-
 .../create_formatter_for_metrics.test.ts      |   2 +-
 .../helpers/create_metric_label.test.ts       |   2 +-
 .../helpers/create_metric_label.ts            |   2 +-
 .../helpers/create_tsvb_link.test.ts          |   0
 .../helpers/create_tsvb_link.ts               |   2 +-
 .../helpers/get_chart_theme.ts                |   0
 .../helpers/metric_to_format.test.ts          |   2 +-
 .../helpers/metric_to_format.ts               |   2 +-
 .../components/metrics_explorer/kuery_bar.tsx |   2 +-
 .../components/metrics_explorer/metrics.tsx   |   2 +-
 .../metrics_explorer/no_metrics.tsx           |   0
 .../metrics_explorer/series_chart.tsx         |   2 +-
 .../components/metrics_explorer/toolbar.tsx   |   2 +-
 .../components/navigation/app_navigation.tsx  |   2 +-
 .../components/navigation/routed_tabs.tsx     |  40 ++--
 .../components/nodes_overview/index.tsx       |   2 +-
 .../components/nodes_overview/table.tsx       |  13 +-
 .../plugins/infra/public/components/page.tsx  |   2 +-
 .../components/saved_views/create_modal.tsx   |   0
 .../saved_views/toolbar_control.tsx           |   2 +-
 .../saved_views/view_list_flyout.tsx          |   0
 .../add_log_column_popover.tsx                |   2 +-
 .../fields_configuration_panel.tsx            |   0
 .../components/source_configuration/index.ts  |   0
 .../indices_configuration_form_state.ts       |   0
 .../indices_configuration_panel.tsx           |   0
 .../source_configuration/input_fields.tsx     |   0
 .../log_columns_configuration_form_state.tsx  |   0
 .../log_columns_configuration_panel.tsx       |   2 +-
 .../name_configuration_panel.tsx              |   0
 .../source_configuration_form_state.tsx       |   0
 .../source_configuration_settings.tsx         |   0
 .../view_source_configuration_button.tsx      |   0
 .../public/components/source_error_page.tsx   |   0
 .../public/components/source_loading_page.tsx |   0
 .../components/waffle/conditional_tooltip.tsx |   0
 .../components/waffle/custom_field_panel.tsx  |   0
 .../components/waffle/gradient_legend.tsx     |   2 +-
 .../public/components/waffle/group_name.tsx   |   2 +-
 .../components/waffle/group_of_groups.tsx     |   2 +-
 .../components/waffle/group_of_nodes.tsx      |   2 +-
 .../infra/public/components/waffle/legend.tsx |   2 +-
 .../components/waffle/legend_controls.tsx     |   2 +-
 .../waffle/lib/apply_wafflemap_layout.ts      |   0
 .../components/waffle/lib/color_from_value.ts |   0
 .../waffle/lib/create_uptime_link.test.ts     |  12 +-
 .../waffle/lib/create_uptime_link.ts          |   6 +-
 .../waffle/lib/field_to_display_name.ts       |   0
 .../components/waffle/lib/size_of_squares.ts  |   0
 .../components/waffle/lib/type_guards.ts      |   0
 .../infra/public/components/waffle/map.tsx    |   2 +-
 .../infra/public/components/waffle/node.tsx   |   2 +-
 .../components/waffle/node_context_menu.tsx   |  38 ++--
 .../public/components/waffle/steps_legend.tsx |   2 +-
 .../components/waffle/view_switcher.tsx       |   0
 .../waffle/waffle_accounts_controls.tsx       |   0
 .../waffle/waffle_group_by_controls.tsx       |   2 +-
 .../waffle/waffle_inventory_switcher.tsx      |   0
 .../waffle/waffle_metric_controls.tsx         |   0
 .../waffle/waffle_region_controls.tsx         |   0
 .../waffle/waffle_time_controls.tsx           |   0
 .../inventory_metadata/use_inventory_meta.ts  |   0
 .../logs/log_analysis/api/ml_api_types.ts     |   0
 .../logs/log_analysis/api/ml_cleanup.ts       |  10 +-
 .../api/ml_get_jobs_summary_api.ts            |   4 +-
 .../logs/log_analysis/api/ml_get_module.ts    |   4 +-
 .../log_analysis/api/ml_setup_module_api.ts   |   4 +-
 .../logs/log_analysis/api/validate_indices.ts |   4 +-
 .../containers/logs/log_analysis/index.ts     |   0
 .../log_analysis_capabilities.tsx             |   4 +-
 .../log_analysis/log_analysis_cleanup.tsx     |   0
 .../logs/log_analysis/log_analysis_module.tsx |   0
 .../log_analysis_module_status.tsx            |   0
 .../log_analysis/log_analysis_module_types.ts |   0
 .../log_analysis/log_analysis_setup_state.tsx |   0
 .../log_entries/api/fetch_log_entries_item.ts |   5 +-
 .../logs/log_entries/gql_queries.ts           |   0
 .../containers/logs/log_entries/index.ts      |   0
 .../containers/logs/log_entries/types.ts      |   0
 .../containers/logs/log_filter/index.ts       |   0
 .../logs/log_filter/log_filter_state.ts       |   2 +-
 .../log_filter/with_log_filter_url_state.tsx  |   0
 .../public/containers/logs/log_flyout.tsx     |   0
 .../api/fetch_log_summary_highlights.ts       |   6 +-
 .../containers/logs/log_highlights/index.ts   |   0
 .../log_entry_highlights.gql_query.ts         |   0
 .../log_highlights/log_entry_highlights.tsx   |   0
 .../logs/log_highlights/log_highlights.tsx    |   0
 .../log_highlights/log_summary_highlights.ts  |   0
 .../logs/log_highlights/next_and_previous.tsx |   0
 .../containers/logs/log_position/index.ts     |   0
 .../logs/log_position/log_position_state.ts   |   0
 .../with_log_position_url_state.tsx           |   0
 .../logs/log_summary/api/fetch_log_summary.ts |   6 +-
 .../containers/logs/log_summary/index.ts      |   0
 .../logs/log_summary/log_summary.test.tsx     |   0
 .../logs/log_summary/log_summary.tsx          |   0
 .../use_log_summary_buffer_interval.ts        |   0
 .../logs/log_summary/with_summary.ts          |   0
 .../logs/log_view_configuration.test.tsx      |   0
 .../logs/log_view_configuration.tsx           |   0
 .../containers/logs/with_log_minimap.tsx      |   0
 .../containers/logs/with_log_textview.tsx     |   0
 .../containers/logs/with_stream_items.ts      |   0
 .../metadata/lib/get_filtered_metrics.ts      |   0
 .../containers/metadata/use_metadata.ts       |   0
 .../use_metrics_explorer_data.test.tsx        |  51 +++--
 .../use_metrics_explorer_data.ts              |  75 ++++---
 .../use_metrics_explorer_options.test.tsx     |   0
 .../use_metrics_explorer_options.ts           |   2 +-
 ...ith_metrics_explorer_options_url_state.tsx |   0
 .../node_details/use_node_details.ts          |   0
 .../source/create_source.gql_query.ts         |   0
 .../infra/public/containers/source/index.ts   |   0
 .../source/query_source.gql_query.ts          |   0
 .../infra/public/containers/source/source.tsx |   0
 .../source_fields_fragment.gql_query.ts       |   0
 .../source/update_source.gql_query.ts         |   0
 .../public/containers/source_id/index.ts      |   0
 .../public/containers/source_id/source_id.ts  |   0
 .../infra/public/containers/waffle/index.ts   |   0
 .../containers/waffle/nodes_to_wafflemap.ts   |   0
 .../public/containers/waffle/type_guards.ts   |   0
 .../public/containers/waffle/use_snaphot.ts   |   0
 .../waffle/waffle_nodes.gql_query.ts          |   0
 .../containers/waffle/with_waffle_filters.tsx |   0
 .../containers/waffle/with_waffle_options.tsx |   0
 .../containers/waffle/with_waffle_time.tsx    |   0
 .../waffle/with_waffle_view_state.tsx         |   0
 .../containers/with_kuery_autocompletion.tsx  |  21 +-
 .../infra/public/containers/with_options.tsx  |   0
 .../public/containers/with_source/index.ts    |   0
 .../containers/with_source/with_source.tsx    |   0
 .../containers/with_state_from_location.tsx   |   4 +-
 .../infra/public/graphql/introspection.json   |   0
 .../public/graphql/log_entries.gql_query.ts   |   0
 .../plugins/infra/public/graphql/types.ts     |   0
 .../hooks/use_bulk_get_saved_object.tsx       |  12 +-
 .../public/hooks/use_create_saved_object.tsx  |  12 +-
 .../public/hooks/use_delete_saved_object.tsx  |   7 +-
 .../public/hooks/use_find_saved_object.tsx    |  19 +-
 .../infra/public/hooks/use_http_request.tsx   |   2 +-
 .../infra/public/hooks/use_interval.ts        |   0
 .../hooks/use_prefix_path_with_basepath.tsx   |  22 ++
 .../infra/public/hooks/use_saved_view.ts      |   0
 .../plugins/infra/public/images/docker.svg    |   0
 .../plugins/infra/public/images/hosts.svg     |   0
 .../infra/public/images/infra_mono_white.svg  |   0
 .../plugins/infra/public/images/k8.svg        |   0
 .../public/images/logging_mono_white.svg      |   0
 .../plugins/infra/public/images/services.svg  |   0
 .../plugins/infra/public/index.scss           |   0
 x-pack/plugins/infra/public/index.ts          |  22 ++
 .../infra/public/legacy_singletons.ts}        |  10 +-
 .../observable_api/kibana_observable_api.ts   |   0
 .../plugins/infra/public/lib/lib.ts           |   0
 .../plugins/infra/public/pages/404.tsx        |   0
 .../plugins/infra/public/pages/error.tsx      |   2 +-
 .../public/pages/infrastructure/index.tsx     |  14 +-
 .../infrastructure/metrics_explorer/index.tsx |   2 +-
 .../use_metric_explorer_state.test.tsx        | 121 ++++++----
 .../use_metric_explorer_state.ts              |   5 +-
 .../public/pages/infrastructure/settings.tsx  |   2 +-
 .../pages/infrastructure/snapshot/index.tsx   |   4 +-
 .../infrastructure/snapshot/page_content.tsx  |   0
 .../pages/infrastructure/snapshot/toolbar.tsx |   0
 .../infra/public/pages/link_to/index.ts       |   0
 .../infra/public/pages/link_to/link_to.tsx    |  38 ++--
 .../public/pages/link_to/query_params.ts      |   0
 .../redirect_to_host_detail_via_ip.tsx        |   2 +-
 .../pages/link_to/redirect_to_logs.test.tsx   |   6 +-
 .../public/pages/link_to/redirect_to_logs.tsx |   4 +-
 .../pages/link_to/redirect_to_node_detail.tsx |   4 +-
 .../link_to/redirect_to_node_logs.test.tsx    |  12 +-
 .../pages/link_to/redirect_to_node_logs.tsx   |   8 +-
 .../pages/link_to/use_host_ip_to_name.test.ts |  26 ++-
 .../pages/link_to/use_host_ip_to_name.ts      |  21 +-
 .../plugins/infra/public/pages/logs/index.tsx |   0
 .../pages/logs/log_entry_categories/index.ts  |   0
 .../log_entry_categories/module_descriptor.ts |   0
 .../pages/logs/log_entry_categories/page.tsx  |   0
 .../log_entry_categories/page_content.tsx     |   0
 .../log_entry_categories/page_providers.tsx   |   0
 .../page_results_content.tsx                  |   6 +-
 .../page_setup_content.tsx                    |   2 +-
 .../anomaly_severity_indicator.tsx            |   0
 .../top_categories/category_expression.tsx    |   2 +-
 .../sections/top_categories/datasets_list.tsx |   0
 .../top_categories/datasets_selector.tsx      |   0
 .../sections/top_categories/index.ts          |   0
 .../log_entry_count_sparkline.tsx             |   0
 .../single_metric_comparison.tsx              |   2 +-
 .../single_metric_sparkline.tsx               |   0
 .../top_categories/top_categories_section.tsx |   0
 .../top_categories/top_categories_table.tsx   |   2 +-
 .../get_log_entry_category_datasets.ts        |  31 ++-
 .../get_top_log_entry_categories.ts           |   4 +-
 .../use_log_entry_categories_module.tsx       |   0
 .../use_log_entry_categories_results.ts       |   0
 ...log_entry_categories_results_url_state.tsx |   0
 .../use_log_entry_categories_setup.tsx        |   0
 .../public/pages/logs/log_entry_rate/index.ts |   0
 .../logs/log_entry_rate/module_descriptor.ts  |   0
 .../public/pages/logs/log_entry_rate/page.tsx |   0
 .../logs/log_entry_rate/page_content.tsx      |   0
 .../logs/log_entry_rate/page_providers.tsx    |   0
 .../log_entry_rate/page_results_content.tsx   |   4 +-
 .../log_entry_rate/page_setup_content.tsx     |   2 +-
 .../sections/anomalies/chart.tsx              |   0
 .../sections/anomalies/expanded_row.tsx       |   0
 .../sections/anomalies/index.tsx              |   2 +-
 .../sections/anomalies/table.tsx              |   2 +-
 .../sections/helpers/data_formatters.tsx      |   0
 .../sections/log_rate/bar_chart.tsx           |   0
 .../sections/log_rate/index.tsx               |   0
 .../service_calls/get_log_entry_rate.ts       |   4 +-
 .../use_log_entry_rate_module.tsx             |   0
 .../use_log_entry_rate_results.ts             |   0
 .../use_log_entry_rate_results_url_state.tsx  |   0
 .../use_log_entry_rate_setup.tsx              |   0
 .../plugins/infra/public/pages/logs/page.tsx  |   2 +-
 .../infra/public/pages/logs/page_content.tsx  |  16 +-
 .../public/pages/logs/page_providers.tsx      |   0
 .../infra/public/pages/logs/settings.tsx      |   2 +-
 .../infra/public/pages/logs/stream/index.ts   |   0
 .../infra/public/pages/logs/stream/page.tsx   |   2 +-
 .../public/pages/logs/stream/page_content.tsx |   0
 .../public/pages/logs/stream/page_header.tsx  |   0
 .../pages/logs/stream/page_logs_content.tsx   |   2 +-
 .../logs/stream/page_no_indices_content.tsx   |   2 +-
 .../pages/logs/stream/page_providers.tsx      |   0
 .../public/pages/logs/stream/page_toolbar.tsx |   0
 .../metrics/components/chart_section_vis.tsx  |   2 +-
 .../metrics/components/error_message.tsx      |   0
 .../metrics/components/gauges_section_vis.tsx |   2 +-
 .../pages/metrics/components/helpers.ts       |   0
 .../pages/metrics/components/invalid_node.tsx |   4 +-
 .../metrics/components/layout_content.tsx     |   2 +-
 .../metrics/components/metadata_details.tsx   |   2 +-
 .../metrics/components/node_details_page.tsx  |   2 +-
 .../pages/metrics/components/page_body.tsx    |   0
 .../pages/metrics/components/page_error.tsx   |   0
 .../pages/metrics/components/section.tsx      |   0
 .../pages/metrics/components/series_chart.tsx |   0
 .../pages/metrics/components/side_nav.tsx     |   2 +-
 .../pages/metrics/components/sub_section.tsx  |   0
 .../metrics/components/time_controls.test.tsx |   0
 .../metrics/components/time_controls.tsx      |   2 +-
 .../metrics/containers/metadata_context.ts    |   0
 .../metrics/containers/metrics.gql_query.ts   |   0
 .../metrics/containers/metrics_time.test.tsx  |   0
 .../metrics/containers/with_metrics_time.tsx  |   0
 .../infra/public/pages/metrics/index.tsx      |   4 +-
 .../pages/metrics/lib/side_nav_context.ts     |   0
 .../public/pages/metrics/page_providers.tsx   |   0
 .../infra/public/pages/metrics/types.ts       |   2 +-
 x-pack/plugins/infra/public/plugin.ts         | 209 ++++++++++++++++++
 .../infra/public/register_feature.ts}         |  26 ++-
 x-pack/plugins/infra/public/routers/index.ts  |  15 ++
 .../infra/public/routers/logs_router.tsx      |  31 +++
 .../infra/public/routers/metrics_router.tsx   |  41 ++++
 .../plugins/infra/public/store/actions.ts     |   0
 .../plugins/infra/public/store/epics.ts       |   0
 .../plugins/infra/public/store/index.ts       |   0
 .../infra/public/store/local/actions.ts       |   0
 .../plugins/infra/public/store/local/epic.ts  |   0
 .../plugins/infra/public/store/local/index.ts |   0
 .../infra/public/store/local/reducer.ts       |   0
 .../infra/public/store/local/selectors.ts     |   0
 .../store/local/waffle_filter/actions.ts      |   0
 .../public/store/local/waffle_filter/index.ts |   0
 .../store/local/waffle_filter/reducer.ts      |   0
 .../store/local/waffle_filter/selectors.ts    |   2 +-
 .../store/local/waffle_options/actions.ts     |   0
 .../store/local/waffle_options/index.ts       |   0
 .../store/local/waffle_options/reducer.ts     |   0
 .../store/local/waffle_options/selector.ts    |   0
 .../public/store/local/waffle_time/actions.ts |   0
 .../public/store/local/waffle_time/epic.ts    |   0
 .../public/store/local/waffle_time/index.ts   |   0
 .../public/store/local/waffle_time/reducer.ts |   0
 .../store/local/waffle_time/selectors.ts      |   0
 .../plugins/infra/public/store/reducer.ts     |   0
 .../plugins/infra/public/store/selectors.ts   |   0
 .../plugins/infra/public/store/store.ts       |   0
 .../infra/public/utils/apollo_context.ts      |   0
 .../infra/public/utils/cancellable_effect.ts  |   0
 .../utils/convert_interval_to_string.ts       |   0
 .../infra/public/utils/enzyme_helpers.tsx     |   0
 .../public/utils/fixtures/metrics_explorer.ts |   2 +-
 .../public/utils/formatters/bytes.test.ts     |   0
 .../infra/public/utils/formatters/bytes.ts    |   0
 .../infra/public/utils/formatters/datetime.ts |   0
 .../public/utils/formatters/high_precision.ts |   0
 .../infra/public/utils/formatters/index.ts    |   0
 .../infra/public/utils/formatters/number.ts   |   0
 .../infra/public/utils/formatters/percent.ts  |   0
 .../plugins/infra/public/utils/handlers.ts    |   2 +-
 .../infra/public/utils/history_context.ts     |   0
 .../infra/public/utils/is_displayable.test.ts |   0
 .../infra/public/utils/is_displayable.ts      |   0
 .../plugins/infra/public/utils/kuery.ts       |   2 +-
 .../infra/public/utils/loading_state/index.ts |   0
 .../utils/loading_state/loading_policy.ts     |   0
 .../utils/loading_state/loading_progress.ts   |   0
 .../utils/loading_state/loading_result.ts     |   0
 .../utils/loading_state/loading_state.ts      |   0
 .../infra/public/utils/log_entry/index.ts     |   0
 .../infra/public/utils/log_entry/log_entry.ts |   0
 .../utils/log_entry/log_entry_highlight.ts    |   0
 ...picker_quickranges_to_datepicker_ranges.ts |   0
 .../utils/redirect_with_query_params.tsx      |  50 +++--
 .../infra/public/utils/redux_context.tsx      |   0
 .../public/utils/source_configuration.ts      |   0
 .../plugins/infra/public/utils/styles.ts      |   7 +-
 .../infra/public/utils/typed_react.tsx        |   4 +-
 .../plugins/infra/public/utils/typed_redux.ts |   0
 .../plugins/infra/public/utils/url_state.tsx  |   8 +-
 .../infra/public/utils/use_kibana_space_id.ts |   9 +-
 .../public/utils/use_kibana_ui_setting.ts     |  27 +--
 .../infra/public/utils/use_tracked_promise.ts |   0
 .../infra/public/utils/use_url_state.ts       |   2 +-
 .../public/utils/use_visibility_state.ts      |   0
 .../plugins/infra/scripts/combined_schema.ts  |   2 +-
 .../scripts/generate_types_from_graphql.js    |   2 +-
 .../plugins/infra/scripts/gql_gen_client.json |   0
 .../plugins/infra/scripts/gql_gen_server.json |   0
 .../plugins/infra/scripts/storybook.js        |   0
 .../plugins/infra/server/features.ts          |   4 +-
 .../plugins/infra/server/graphql/index.ts     |   0
 .../infra/server/graphql/log_entries/index.ts |   0
 .../server/graphql/log_entries/resolvers.ts   |   0
 .../server/graphql/log_entries/schema.gql.ts  |   0
 .../server/graphql/source_status/index.ts     |   0
 .../server/graphql/source_status/resolvers.ts |   0
 .../graphql/source_status/schema.gql.ts       |   0
 .../infra/server/graphql/sources/index.ts     |   0
 .../infra/server/graphql/sources/resolvers.ts |   0
 .../server/graphql/sources/schema.gql.ts      |   0
 .../plugins/infra/server/graphql/types.ts     |   0
 x-pack/plugins/infra/server/index.ts          |  21 +-
 .../plugins/infra/server/infra_server.ts      |   0
 .../plugins/infra/server/kibana.index.ts      |   0
 .../lib/adapters/fields/adapter_types.ts      |   0
 .../fields/framework_fields_adapter.ts        |   0
 .../infra/server/lib/adapters/fields/index.ts |   0
 .../lib/adapters/framework/adapter_types.ts   |  12 +-
 .../server/lib/adapters/framework/index.ts    |   0
 .../framework/kibana_framework_adapter.ts     |  21 +-
 .../lib/adapters/log_entries/adapter_types.ts |   0
 .../server/lib/adapters/log_entries/index.ts  |   0
 .../log_entries/kibana_log_entries_adapter.ts |  16 +-
 .../lib/adapters/metrics/adapter_types.ts     |   0
 .../server/lib/adapters/metrics/index.ts      |   0
 .../metrics/kibana_metrics_adapter.ts         |   0
 .../adapters/metrics/lib/check_valid_node.ts  |   0
 .../server/lib/adapters/metrics/lib/errors.ts |   0
 .../elasticsearch_source_status_adapter.ts    |   0
 .../lib/adapters/source_status/index.ts       |   0
 .../infra/server/lib/compose/kibana.ts        |   4 +-
 .../plugins/infra/server/lib/constants.ts     |   0
 .../infra/server/lib/domains/fields_domain.ts |   0
 .../builtin_rules/filebeat_apache2.test.ts    |   0
 .../builtin_rules/filebeat_apache2.ts         |   0
 .../builtin_rules/filebeat_auditd.test.ts     |   0
 .../builtin_rules/filebeat_auditd.ts          |   0
 .../builtin_rules/filebeat_haproxy.test.ts    |   0
 .../builtin_rules/filebeat_haproxy.ts         |   0
 .../builtin_rules/filebeat_icinga.test.ts     |   0
 .../builtin_rules/filebeat_icinga.ts          |   0
 .../builtin_rules/filebeat_iis.test.ts        |   0
 .../builtin_rules/filebeat_iis.ts             |   0
 .../builtin_rules/filebeat_kafka.test.ts      |   0
 .../builtin_rules/filebeat_logstash.test.ts   |   0
 .../builtin_rules/filebeat_logstash.ts        |   0
 .../builtin_rules/filebeat_mongodb.test.ts    |   0
 .../builtin_rules/filebeat_mongodb.ts         |   0
 .../builtin_rules/filebeat_mysql.test.ts      |   0
 .../builtin_rules/filebeat_mysql.ts           |   0
 .../builtin_rules/filebeat_nginx.test.ts      |   0
 .../builtin_rules/filebeat_nginx.ts           |   0
 .../builtin_rules/filebeat_osquery.test.ts    |   0
 .../builtin_rules/filebeat_osquery.ts         |   0
 .../builtin_rules/filebeat_redis.ts           |   0
 .../builtin_rules/filebeat_system.ts          |   0
 .../builtin_rules/filebeat_traefik.test.ts    |   0
 .../builtin_rules/filebeat_traefik.ts         |   0
 .../builtin_rules/generic.test.ts             |   0
 .../builtin_rules/generic.ts                  |   0
 .../builtin_rules/generic_webserver.ts        |   0
 .../builtin_rules/helpers.ts                  |   0
 .../log_entries_domain/builtin_rules/index.ts |   0
 ...document_source_to_log_item_fields.test.ts |   0
 ...vert_document_source_to_log_item_fields.ts |   0
 .../lib/domains/log_entries_domain/index.ts   |   0
 .../log_entries_domain/log_entries_domain.ts  |   0
 .../lib/domains/log_entries_domain/message.ts |   0
 .../domains/log_entries_domain/rule_types.ts  |   0
 .../server/lib/domains/metrics_domain.ts      |   0
 .../plugins/infra/server/lib/infra_types.ts   |   4 +-
 .../infra/server/lib/log_analysis/errors.ts   |   0
 .../infra/server/lib/log_analysis/index.ts    |   0
 .../log_entry_categories_analysis.ts          |   2 +-
 .../log_analysis/log_entry_rate_analysis.ts   |   2 +-
 .../server/lib/log_analysis/queries/common.ts |   0
 .../server/lib/log_analysis/queries/index.ts  |   0
 .../queries/log_entry_categories.ts           |   0
 .../queries/log_entry_category_histograms.ts  |   0
 .../queries/log_entry_data_sets.ts            |   0
 .../log_analysis/queries/log_entry_rate.ts    |   0
 .../queries/top_log_entry_categories.ts       |   0
 .../infra/server/lib/snapshot/constants.ts    |   0
 .../create_timerange_with_interval.ts         |   0
 .../infra/server/lib/snapshot/index.ts        |   0
 .../server/lib/snapshot/query_helpers.ts      |   0
 .../lib/snapshot/response_helpers.test.ts     |   0
 .../server/lib/snapshot/response_helpers.ts   |   0
 .../infra/server/lib/snapshot/snapshot.ts     |   0
 .../infra/server/lib/snapshot/types.ts        |   2 +-
 .../plugins/infra/server/lib/source_status.ts |   0
 .../infra/server/lib/sources/defaults.ts      |   0
 .../infra/server/lib/sources/errors.ts        |   0
 .../plugins/infra/server/lib/sources/index.ts |   0
 .../lib/sources/saved_object_mappings.ts      |   0
 .../infra/server/lib/sources/sources.test.ts  |   0
 .../infra/server/lib/sources/sources.ts       |   2 +-
 .../plugins/infra/server/lib/sources/types.ts |   0
 x-pack/plugins/infra/server/plugin.ts         | 164 ++++++++++++--
 .../server/routes/inventory_metadata/index.ts |   0
 .../lib/get_cloud_metadata.ts                 |   0
 .../infra/server/routes/ip_to_hostname.ts     |   4 -
 .../infra/server/routes/log_analysis/index.ts |   0
 .../routes/log_analysis/results/index.ts      |   0
 .../results/log_entry_categories.ts           |   0
 .../results/log_entry_category_datasets.ts    |   0
 .../log_analysis/results/log_entry_rate.ts    |   0
 .../routes/log_analysis/validation/index.ts   |   0
 .../routes/log_analysis/validation/indices.ts |   0
 .../server/routes/log_entries/entries.ts      |   0
 .../server/routes/log_entries/highlights.ts   |   0
 .../infra/server/routes/log_entries/index.ts  |   0
 .../infra/server/routes/log_entries/item.ts   |   0
 .../server/routes/log_entries/summary.ts      |   0
 .../routes/log_entries/summary_highlights.ts  |   0
 .../infra/server/routes/metadata/index.ts     |   0
 .../metadata/lib/get_cloud_metric_metadata.ts |   0
 .../metadata/lib/get_metric_metadata.ts       |   0
 .../routes/metadata/lib/get_node_info.ts      |   0
 .../routes/metadata/lib/get_pod_node_name.ts  |   0
 .../routes/metadata/lib/has_apm_data.ts       |   0
 .../routes/metadata/lib/pick_feature_name.ts  |   0
 .../server/routes/metrics_explorer/index.ts   |   0
 .../lib/create_metrics_model.ts               |   2 +-
 .../lib/get_dataset_for_field.ts              |   0
 .../metrics_explorer/lib/get_groupings.ts     |   5 +-
 .../lib/populate_series_with_tsvb_data.ts     |   2 +-
 .../infra/server/routes/node_details/index.ts |   0
 .../infra/server/routes/snapshot/index.ts     |   0
 .../plugins/infra/server/saved_objects.ts     |   0
 .../infra/server/usage/usage_collector.ts     |   0
 .../plugins/infra/server/utils/README.md      |   0
 .../server/utils/calculate_metric_interval.ts |   0
 .../server/utils/create_afterkey_handler.ts   |   0
 .../utils/elasticsearch_runtime_types.ts      |   0
 .../server/utils/get_all_composite_data.ts    |   0
 .../server/utils/get_interval_in_seconds.ts   |   0
 .../infra/server/utils/serialized_query.ts    |   0
 .../utils/typed_elasticsearch_mappings.ts     |   0
 .../infra/server/utils/typed_resolvers.ts     |   0
 .../{legacy => }/plugins/infra/types/eui.d.ts |   0
 .../plugins/infra/types/eui_experimental.d.ts |   0
 .../plugins/infra/types/graphql_fields.d.ts   |   0
 .../plugins/infra/types/redux_observable.d.ts |   0
 .../public/hooks/use_track_metric.tsx         |  92 ++++++++
 x-pack/plugins/observability/public/index.ts  |  10 +
 .../public/typings/eui_draggable/index.ts     |  17 ++
 .../public/typings/eui_styled_components.tsx  |  45 ++++
 .../observability/public/typings/index.ts     |   8 +
 .../translations/translations/ja-JP.json      |   6 -
 .../translations/translations/zh-CN.json      |   6 -
 .../apis/infra/ip_to_hostname.ts              |   4 +-
 .../apis/infra/log_analysis.ts                |   7 +-
 .../api_integration/apis/infra/log_entries.ts |  11 +-
 .../apis/infra/log_entry_highlights.ts        |  11 +-
 .../api_integration/apis/infra/log_item.ts    |   2 +-
 .../api_integration/apis/infra/log_summary.ts |   7 +-
 .../apis/infra/logs_without_millis.ts         |  11 +-
 .../api_integration/apis/infra/metadata.ts    |   4 +-
 .../api_integration/apis/infra/metrics.ts     |   9 +-
 .../apis/infra/metrics_explorer.ts            |  54 ++---
 .../api_integration/apis/infra/sources.ts     |   8 +-
 .../test/api_integration/apis/infra/waffle.ts |   4 +-
 .../services/infraops_graphql_client.ts       |   2 +-
 .../services/infraops_source_configuration.ts |   5 +-
 .../feature_controls/endpoint_spaces.ts       |  15 +-
 .../infrastructure_security.ts                |  65 ++----
 .../feature_controls/infrastructure_spaces.ts | 132 ++---------
 .../infra/feature_controls/logs_security.ts   |  23 +-
 .../infra/feature_controls/logs_spaces.ts     |  21 +-
 x-pack/test/functional/apps/infra/link_to.ts  |  19 +-
 .../infra/metrics_source_configuration.ts     |   4 +-
 x-pack/test/functional/config.js              |   5 +-
 .../page_objects/infra_logs_page.ts           |   2 +-
 .../infra/types => typings}/rison_node.d.ts   |   0
 yarn.lock                                     |  12 -
 858 files changed, 1946 insertions(+), 1782 deletions(-)
 delete mode 100644 x-pack/legacy/plugins/infra/package.json
 delete mode 100644 x-pack/legacy/plugins/infra/public/app.ts
 delete mode 100644 x-pack/legacy/plugins/infra/public/hooks/use_track_metric.tsx
 delete mode 100644 x-pack/legacy/plugins/infra/public/index.ts
 delete mode 100644 x-pack/legacy/plugins/infra/public/legacy_register_feature.ts
 delete mode 100644 x-pack/legacy/plugins/infra/public/new_platform_plugin.ts
 delete mode 100644 x-pack/legacy/plugins/infra/public/routes.tsx
 delete mode 100644 x-pack/legacy/plugins/infra/public/utils/fetch.ts
 delete mode 100644 x-pack/legacy/plugins/infra/public/utils/use_kibana_injected_var.ts
 delete mode 100644 x-pack/legacy/plugins/infra/server/new_platform_index.ts
 delete mode 100644 x-pack/legacy/plugins/infra/server/new_platform_plugin.ts
 delete mode 100644 x-pack/legacy/plugins/infra/server/routes/metrics_explorer/types.ts
 delete mode 100644 x-pack/legacy/plugins/infra/tsconfig.json
 delete mode 120000 x-pack/legacy/plugins/infra/yarn.lock
 rename x-pack/{legacy => }/plugins/infra/README.md (100%)
 rename x-pack/{legacy => }/plugins/infra/common/color_palette.test.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/color_palette.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/ecs_allowed_list.test.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/ecs_allowed_list.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/errors/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/errors/metrics.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/graphql/root/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/graphql/root/schema.gql.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/graphql/shared/fragments.gql_query.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/graphql/shared/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/graphql/shared/schema.gql.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/graphql/types.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/http_api/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/http_api/inventory_meta_api.ts (100%)
 create mode 100644 x-pack/plugins/infra/common/http_api/ip_to_hostname/index.ts
 rename x-pack/{legacy => }/plugins/infra/common/http_api/log_analysis/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/http_api/log_analysis/results/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/http_api/log_analysis/results/log_entry_categories.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/http_api/log_analysis/results/log_entry_category_datasets.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/http_api/log_analysis/validation/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/http_api/log_analysis/validation/log_entry_rate_indices.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/http_api/log_entries/common.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/http_api/log_entries/entries.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/http_api/log_entries/highlights.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/http_api/log_entries/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/http_api/log_entries/item.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/http_api/log_entries/summary.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/http_api/log_entries/summary_highlights.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/http_api/metadata_api.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/http_api/metrics_explorer/index.ts (78%)
 rename x-pack/{legacy => }/plugins/infra/common/http_api/node_details_api.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/http_api/shared/errors.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/http_api/shared/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/http_api/shared/metric_statistics.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/http_api/shared/time_range.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/http_api/shared/timing.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/http_api/snapshot_api.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_ec2/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_ec2/layout.tsx (89%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_ec2/metrics/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_ec2/metrics/snapshot/cpu.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_ec2/metrics/snapshot/disk_io_read_bytes.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_ec2/metrics/snapshot/disk_io_write_bytes.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_ec2/metrics/snapshot/rx.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_ec2/metrics/snapshot/tx.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_ec2/metrics/tsvb/aws_ec2_cpu_utilization.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_ec2/metrics/tsvb/aws_ec2_diskio_bytes.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_ec2/metrics/tsvb/aws_ec2_network_traffic.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_ec2/toolbar_items.tsx (95%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_rds/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_rds/layout.tsx (93%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_rds/metrics/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_rds/metrics/snapshot/cpu.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_rds/metrics/snapshot/rds_active_transactions.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_rds/metrics/snapshot/rds_connections.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_rds/metrics/snapshot/rds_latency.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_rds/metrics/snapshot/rds_queries_executed.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_rds/metrics/tsvb/aws_rds_active_transactions.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_rds/metrics/tsvb/aws_rds_connections.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_rds/metrics/tsvb/aws_rds_cpu_total.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_rds/metrics/tsvb/aws_rds_latency.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_rds/metrics/tsvb/aws_rds_queries_executed.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_rds/toolbar_items.tsx (95%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_s3/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_s3/layout.tsx (91%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_s3/metrics/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_s3/metrics/snapshot/s3_bucket_size.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_s3/metrics/snapshot/s3_download_bytes.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_s3/metrics/snapshot/s3_number_of_objects.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_s3/metrics/snapshot/s3_total_requests.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_s3/metrics/snapshot/s3_upload_bytes.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_s3/metrics/tsvb/aws_s3_bucket_size.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_s3/metrics/tsvb/aws_s3_download_bytes.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_s3/metrics/tsvb/aws_s3_number_of_objects.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_s3/metrics/tsvb/aws_s3_total_requests.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_s3/metrics/tsvb/aws_s3_upload_bytes.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_s3/toolbar_items.tsx (94%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_sqs/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_sqs/layout.tsx (92%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_sqs/metrics/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_sqs/metrics/snapshot/sqs_messages_delayed.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_sqs/metrics/snapshot/sqs_messages_empty.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_sqs/metrics/snapshot/sqs_messages_sent.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_sqs/metrics/snapshot/sqs_messages_visible.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_sqs/metrics/snapshot/sqs_oldest_message.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_sqs/metrics/tsvb/aws_sqs_messages_delayed.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_sqs/metrics/tsvb/aws_sqs_messages_empty.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_sqs/metrics/tsvb/aws_sqs_messages_sent.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_sqs/metrics/tsvb/aws_sqs_messages_visible.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_sqs/metrics/tsvb/aws_sqs_oldest_message.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/aws_sqs/toolbar_items.tsx (94%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/container/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/container/layout.tsx (93%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/container/metrics/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/container/metrics/snapshot/cpu.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/container/metrics/snapshot/memory.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/container/metrics/snapshot/rx.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/container/metrics/snapshot/tx.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/container/metrics/tsvb/container_cpu_kernel.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/container/metrics/tsvb/container_cpu_usage.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/container/metrics/tsvb/container_disk_io_bytes.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/container/metrics/tsvb/container_diskio_ops.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/container/metrics/tsvb/container_memory.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/container/metrics/tsvb/container_network_traffic.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/container/metrics/tsvb/container_overview.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/container/toolbar_items.tsx (94%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/create_tsvb_model.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/host/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/host/layout.tsx (95%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/host/metrics/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/host/metrics/snapshot/cpu.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/host/metrics/snapshot/load.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/host/metrics/snapshot/log_rate.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/host/metrics/snapshot/memory.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/host/metrics/snapshot/rx.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/host/metrics/snapshot/tx.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/host/metrics/tsvb/host_cpu_usage.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/host/metrics/tsvb/host_docker_info.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/host/metrics/tsvb/host_docker_overview.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/host/metrics/tsvb/host_docker_top_5_by_cpu.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/host/metrics/tsvb/host_docker_top_5_by_memory.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/host/metrics/tsvb/host_filesystem.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/host/metrics/tsvb/host_k8s_cpu_cap.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/host/metrics/tsvb/host_k8s_disk_cap.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/host/metrics/tsvb/host_k8s_memory_cap.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/host/metrics/tsvb/host_k8s_overview.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/host/metrics/tsvb/host_k8s_pod_cap.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/host/metrics/tsvb/host_load.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/host/metrics/tsvb/host_memory_usage.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/host/metrics/tsvb/host_network_traffic.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/host/metrics/tsvb/host_system_overview.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/host/toolbar_items.tsx (94%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/intl_strings.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/layouts.ts (96%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/metrics.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/pod/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/pod/layout.tsx (91%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/pod/metrics/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/pod/metrics/snapshot/cpu.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/pod/metrics/snapshot/memory.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/pod/metrics/snapshot/rx.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/pod/metrics/snapshot/tx.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_cpu_usage.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_log_usage.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_memory_usage.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_network_traffic.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_overview.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/pod/toolbar_items.tsx (93%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/shared/compontents/cloud_toolbar_items.tsx (87%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/shared/compontents/metrics_and_groupby_toolbar_items.tsx (88%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/shared/layouts/aws.tsx (94%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/shared/layouts/nginx.tsx (90%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/shared/metrics/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/shared/metrics/required_metrics.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/shared/metrics/snapshot/count.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/shared/metrics/snapshot/network_traffic.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/shared/metrics/snapshot/network_traffic_with_interfaces.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/shared/metrics/snapshot/rate.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_cpu_utilization.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_diskio_bytes.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_diskio_ops.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_network_bytes.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_network_packets.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_overview.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/shared/metrics/tsvb/nginx_active_connections.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/shared/metrics/tsvb/nginx_hits.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/shared/metrics/tsvb/nginx_request_rate.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/shared/metrics/tsvb/nginx_requests_per_connection.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/toolbars.ts (96%)
 rename x-pack/{legacy => }/plugins/infra/common/inventory_models/types.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/log_analysis/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/log_analysis/job_parameters.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/log_analysis/log_analysis.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/log_analysis/log_analysis_results.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/log_analysis/log_entry_categories_analysis.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/log_analysis/log_entry_rate_analysis.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/log_entry/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/log_entry/log_entry.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/log_search_result/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/log_search_result/log_search_result.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/log_search_summary/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/log_search_summary/log_search_summary.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/log_text_scale/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/log_text_scale/log_text_scale.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/performance_tracing.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/runtime_types.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/saved_objects/inventory_view.ts (91%)
 rename x-pack/{legacy => }/plugins/infra/common/saved_objects/metrics_explorer_view.ts (92%)
 rename x-pack/{legacy => }/plugins/infra/common/time/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/time/time_key.ts (97%)
 rename x-pack/{legacy => }/plugins/infra/common/time/time_scale.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/time/time_unit.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/typed_json.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/common/utility_types.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/docs/arch.md (100%)
 rename x-pack/{legacy => }/plugins/infra/docs/arch_client.md (100%)
 rename x-pack/{legacy => }/plugins/infra/docs/assets/arch.png (100%)
 rename x-pack/{legacy => }/plugins/infra/docs/assets/infra_metricbeat_aws.jpg (100%)
 rename x-pack/{legacy => }/plugins/infra/docs/graphql.md (100%)
 rename x-pack/{legacy => }/plugins/infra/docs/test_setups/infra_metricbeat_aws.md (100%)
 rename x-pack/{legacy => }/plugins/infra/docs/test_setups/infra_metricbeat_docker_nginx.md (100%)
 rename x-pack/{legacy => }/plugins/infra/public/apps/start_app.tsx (65%)
 create mode 100644 x-pack/plugins/infra/public/apps/start_legacy_app.tsx
 rename x-pack/{legacy => }/plugins/infra/public/components/auto_sizer.tsx (99%)
 rename x-pack/{legacy => }/plugins/infra/public/components/autocomplete_field/autocomplete_field.tsx (98%)
 rename x-pack/{legacy => }/plugins/infra/public/components/autocomplete_field/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/autocomplete_field/suggestion_item.tsx (79%)
 rename x-pack/{legacy => }/plugins/infra/public/components/beta_badge.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/document_title.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/empty_states/index.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/empty_states/no_data.tsx (93%)
 rename x-pack/{legacy => }/plugins/infra/public/components/empty_states/no_indices.tsx (91%)
 rename x-pack/{legacy => }/plugins/infra/public/components/error_page.tsx (96%)
 rename x-pack/{legacy => }/plugins/infra/public/components/eui/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/eui/toolbar/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/eui/toolbar/toolbar.tsx (88%)
 rename x-pack/{legacy => }/plugins/infra/public/components/fixed_datepicker.tsx (90%)
 rename x-pack/{legacy => }/plugins/infra/public/components/formatted_time.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/header/header.tsx (94%)
 rename x-pack/{legacy => }/plugins/infra/public/components/header/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/help_center_content.tsx (90%)
 rename x-pack/{legacy => }/plugins/infra/public/components/inventory/layout.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/inventory/toolbars/toolbar.tsx (97%)
 rename x-pack/{legacy => }/plugins/infra/public/components/inventory/toolbars/toolbar_wrapper.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/loading/__examples__/index.stories.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/loading/index.tsx (94%)
 rename x-pack/{legacy => }/plugins/infra/public/components/loading_overlay_wrapper.tsx (94%)
 rename x-pack/{legacy => }/plugins/infra/public/components/loading_page.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_analysis_job_status/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_analysis_job_status/job_definition_outdated_callout.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_analysis_job_status/job_stopped_callout.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_analysis_job_status/log_analysis_job_problem_indicator.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_analysis_job_status/recreate_job_button.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_analysis_job_status/recreate_job_callout.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx (93%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_analysis_results/first_use_callout.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_analysis_results/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_analysis_setup/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/analysis_setup_indices_form.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/analysis_setup_timerange_form.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/initial_configuration_step.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/validation.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_analysis_setup/missing_results_privileges_prompt.tsx (94%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_analysis_setup/missing_setup_privileges_prompt.tsx (94%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_analysis_setup/ml_unavailable_prompt.tsx (95%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_analysis_setup/process_step/create_ml_jobs_button.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_analysis_setup/process_step/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_analysis_setup/process_step/process_step.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_analysis_setup/process_step/recreate_ml_jobs_button.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_analysis_setup/setup_page.tsx (95%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_analysis_setup/setup_status_unknown_prompt.tsx (93%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_analysis_setup/user_management_link.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_customization_menu.tsx (95%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_entry_flyout/index.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_entry_flyout/log_entry_actions_menu.test.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_entry_flyout/log_entry_actions_menu.tsx (95%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx (98%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_highlights_menu.tsx (98%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_minimap/density_chart.tsx (95%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_minimap/highlighted_interval.tsx (95%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_minimap/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx (99%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_minimap/search_marker.tsx (97%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_minimap/search_marker_tooltip.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_minimap/search_markers.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_minimap/time_ruler.tsx (96%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_minimap/types.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_minimap_scale_controls.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_search_controls/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_search_controls/log_search_buttons.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_search_controls/log_search_controls.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_search_controls/log_search_input.tsx (96%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_statusbar.tsx (91%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_text_scale_controls.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_text_stream/column_headers.tsx (98%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_text_stream/highlighting.tsx (96%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_text_stream/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_text_stream/item.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_text_stream/jump_to_tail.tsx (95%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx (98%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_text_stream/log_date_row.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_text_stream/log_entry_column.tsx (97%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx (97%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx (97%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_text_stream/log_entry_icon_column.tsx (96%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.tsx (97%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx (98%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_text_stream/log_entry_timestamp_column.tsx (95%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_text_stream/measurable_item_view.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx (99%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_text_stream/text_styles.tsx (96%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_text_stream/vertical_scroll_panel.tsx (91%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_text_wrap_controls.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/logging/log_time_controls.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/metrics_explorer/aggregation.tsx (96%)
 rename x-pack/{legacy => }/plugins/infra/public/components/metrics_explorer/chart.tsx (95%)
 rename x-pack/{legacy => }/plugins/infra/public/components/metrics_explorer/chart_context_menu.test.tsx (61%)
 rename x-pack/{legacy => }/plugins/infra/public/components/metrics_explorer/chart_context_menu.tsx (92%)
 rename x-pack/{legacy => }/plugins/infra/public/components/metrics_explorer/chart_options.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/metrics_explorer/charts.tsx (97%)
 rename x-pack/{legacy => }/plugins/infra/public/components/metrics_explorer/empty_chart.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/metrics_explorer/group_by.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/metrics_explorer/helpers/calculate_domain.ts (56%)
 rename x-pack/{legacy => }/plugins/infra/public/components/metrics_explorer/helpers/calculate_domian.test.ts (95%)
 rename x-pack/{legacy => }/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metric.ts (90%)
 rename x-pack/{legacy => }/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metrics.test.ts (94%)
 rename x-pack/{legacy => }/plugins/infra/public/components/metrics_explorer/helpers/create_metric_label.test.ts (89%)
 rename x-pack/{legacy => }/plugins/infra/public/components/metrics_explorer/helpers/create_metric_label.ts (80%)
 rename x-pack/{legacy => }/plugins/infra/public/components/metrics_explorer/helpers/create_tsvb_link.test.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/metrics_explorer/helpers/create_tsvb_link.ts (97%)
 rename x-pack/{legacy => }/plugins/infra/public/components/metrics_explorer/helpers/get_chart_theme.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.test.ts (94%)
 rename x-pack/{legacy => }/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.ts (89%)
 rename x-pack/{legacy => }/plugins/infra/public/components/metrics_explorer/kuery_bar.tsx (96%)
 rename x-pack/{legacy => }/plugins/infra/public/components/metrics_explorer/metrics.tsx (96%)
 rename x-pack/{legacy => }/plugins/infra/public/components/metrics_explorer/no_metrics.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/metrics_explorer/series_chart.tsx (97%)
 rename x-pack/{legacy => }/plugins/infra/public/components/metrics_explorer/toolbar.tsx (98%)
 rename x-pack/{legacy => }/plugins/infra/public/components/navigation/app_navigation.tsx (93%)
 rename x-pack/{legacy => }/plugins/infra/public/components/navigation/routed_tabs.tsx (53%)
 rename x-pack/{legacy => }/plugins/infra/public/components/nodes_overview/index.tsx (99%)
 rename x-pack/{legacy => }/plugins/infra/public/components/nodes_overview/table.tsx (94%)
 rename x-pack/{legacy => }/plugins/infra/public/components/page.tsx (90%)
 rename x-pack/{legacy => }/plugins/infra/public/components/saved_views/create_modal.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/saved_views/toolbar_control.tsx (98%)
 rename x-pack/{legacy => }/plugins/infra/public/components/saved_views/view_list_flyout.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/source_configuration/add_log_column_popover.tsx (98%)
 rename x-pack/{legacy => }/plugins/infra/public/components/source_configuration/fields_configuration_panel.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/source_configuration/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/source_configuration/indices_configuration_form_state.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/source_configuration/indices_configuration_panel.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/source_configuration/input_fields.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/source_configuration/log_columns_configuration_form_state.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/source_configuration/log_columns_configuration_panel.tsx (99%)
 rename x-pack/{legacy => }/plugins/infra/public/components/source_configuration/name_configuration_panel.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/source_configuration/source_configuration_form_state.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/source_configuration/source_configuration_settings.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/source_configuration/view_source_configuration_button.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/source_error_page.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/source_loading_page.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/waffle/conditional_tooltip.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/waffle/custom_field_panel.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/waffle/gradient_legend.tsx (97%)
 rename x-pack/{legacy => }/plugins/infra/public/components/waffle/group_name.tsx (97%)
 rename x-pack/{legacy => }/plugins/infra/public/components/waffle/group_of_groups.tsx (96%)
 rename x-pack/{legacy => }/plugins/infra/public/components/waffle/group_of_nodes.tsx (96%)
 rename x-pack/{legacy => }/plugins/infra/public/components/waffle/legend.tsx (96%)
 rename x-pack/{legacy => }/plugins/infra/public/components/waffle/legend_controls.tsx (98%)
 rename x-pack/{legacy => }/plugins/infra/public/components/waffle/lib/apply_wafflemap_layout.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/waffle/lib/color_from_value.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/waffle/lib/create_uptime_link.test.ts (86%)
 rename x-pack/{legacy => }/plugins/infra/public/components/waffle/lib/create_uptime_link.ts (81%)
 rename x-pack/{legacy => }/plugins/infra/public/components/waffle/lib/field_to_display_name.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/waffle/lib/size_of_squares.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/waffle/lib/type_guards.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/waffle/map.tsx (98%)
 rename x-pack/{legacy => }/plugins/infra/public/components/waffle/node.tsx (98%)
 rename x-pack/{legacy => }/plugins/infra/public/components/waffle/node_context_menu.tsx (89%)
 rename x-pack/{legacy => }/plugins/infra/public/components/waffle/steps_legend.tsx (96%)
 rename x-pack/{legacy => }/plugins/infra/public/components/waffle/view_switcher.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/waffle/waffle_accounts_controls.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/waffle/waffle_group_by_controls.tsx (98%)
 rename x-pack/{legacy => }/plugins/infra/public/components/waffle/waffle_inventory_switcher.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/waffle/waffle_metric_controls.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/waffle/waffle_region_controls.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/components/waffle/waffle_time_controls.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/inventory_metadata/use_inventory_meta.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_analysis/api/ml_api_types.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_analysis/api/ml_cleanup.ts (88%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts (93%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_analysis/api/ml_get_module.ts (88%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_analysis/api/ml_setup_module_api.ts (95%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_analysis/api/validate_indices.ts (88%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_analysis/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_analysis/log_analysis_capabilities.tsx (95%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_analysis/log_analysis_cleanup.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_analysis/log_analysis_module.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_status.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_types.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_analysis/log_analysis_setup_state.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_entries/api/fetch_log_entries_item.ts (87%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_entries/gql_queries.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_entries/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_entries/types.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_filter/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_filter/log_filter_state.ts (97%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_filter/with_log_filter_url_state.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_flyout.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_highlights/api/fetch_log_summary_highlights.ts (87%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_highlights/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.gql_query.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_highlights/next_and_previous.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_position/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_position/log_position_state.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_summary/api/fetch_log_summary.ts (87%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_summary/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_summary/log_summary.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_summary/use_log_summary_buffer_interval.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_summary/with_summary.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_view_configuration.test.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/log_view_configuration.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/with_log_minimap.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/with_log_textview.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/logs/with_stream_items.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/metadata/lib/get_filtered_metrics.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/metadata/use_metadata.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.test.tsx (81%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.ts (59%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_options.test.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_options.ts (98%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/metrics_explorer/with_metrics_explorer_options_url_state.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/node_details/use_node_details.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/source/create_source.gql_query.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/source/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/source/query_source.gql_query.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/source/source.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/source/source_fields_fragment.gql_query.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/source/update_source.gql_query.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/source_id/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/source_id/source_id.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/waffle/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/waffle/nodes_to_wafflemap.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/waffle/type_guards.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/waffle/use_snaphot.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/waffle/waffle_nodes.gql_query.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/waffle/with_waffle_filters.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/waffle/with_waffle_options.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/waffle/with_waffle_time.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/waffle/with_waffle_view_state.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/with_kuery_autocompletion.tsx (77%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/with_options.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/with_source/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/with_source/with_source.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/containers/with_state_from_location.tsx (96%)
 rename x-pack/{legacy => }/plugins/infra/public/graphql/introspection.json (100%)
 rename x-pack/{legacy => }/plugins/infra/public/graphql/log_entries.gql_query.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/graphql/types.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/hooks/use_bulk_get_saved_object.tsx (71%)
 rename x-pack/{legacy => }/plugins/infra/public/hooks/use_create_saved_object.tsx (74%)
 rename x-pack/{legacy => }/plugins/infra/public/hooks/use_delete_saved_object.tsx (81%)
 rename x-pack/{legacy => }/plugins/infra/public/hooks/use_find_saved_object.tsx (67%)
 rename x-pack/{legacy => }/plugins/infra/public/hooks/use_http_request.tsx (97%)
 rename x-pack/{legacy => }/plugins/infra/public/hooks/use_interval.ts (100%)
 create mode 100644 x-pack/plugins/infra/public/hooks/use_prefix_path_with_basepath.tsx
 rename x-pack/{legacy => }/plugins/infra/public/hooks/use_saved_view.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/images/docker.svg (100%)
 rename x-pack/{legacy => }/plugins/infra/public/images/hosts.svg (100%)
 rename x-pack/{legacy => }/plugins/infra/public/images/infra_mono_white.svg (100%)
 rename x-pack/{legacy => }/plugins/infra/public/images/k8.svg (100%)
 rename x-pack/{legacy => }/plugins/infra/public/images/logging_mono_white.svg (100%)
 rename x-pack/{legacy => }/plugins/infra/public/images/services.svg (100%)
 rename x-pack/{legacy => }/plugins/infra/public/index.scss (100%)
 create mode 100644 x-pack/plugins/infra/public/index.ts
 rename x-pack/{legacy/plugins/infra/public/new_platform_index.ts => plugins/infra/public/legacy_singletons.ts} (55%)
 rename x-pack/{legacy => }/plugins/infra/public/lib/adapters/observable_api/kibana_observable_api.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/lib/lib.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/404.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/error.tsx (96%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/infrastructure/index.tsx (89%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/infrastructure/metrics_explorer/index.tsx (97%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.test.tsx (68%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.ts (97%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/infrastructure/settings.tsx (89%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/infrastructure/snapshot/index.tsx (96%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/infrastructure/snapshot/page_content.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/infrastructure/snapshot/toolbar.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/link_to/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/link_to/link_to.tsx (54%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/link_to/query_params.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/link_to/redirect_to_host_detail_via_ip.tsx (96%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/link_to/redirect_to_logs.test.tsx (79%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/link_to/redirect_to_logs.tsx (92%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/link_to/redirect_to_node_detail.tsx (88%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/link_to/redirect_to_node_logs.test.tsx (79%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx (93%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/link_to/use_host_ip_to_name.test.ts (81%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/link_to/use_host_ip_to_name.ts (61%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/index.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_categories/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_categories/module_descriptor.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_categories/page.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_categories/page_content.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_categories/page_providers.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx (97%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_categories/page_setup_content.tsx (97%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/anomaly_severity_indicator.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_expression.tsx (96%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/datasets_list.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/datasets_selector.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/log_entry_count_sparkline.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/single_metric_comparison.tsx (95%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/single_metric_sparkline.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_section.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_table.tsx (97%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_datasets.ts (69%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_top_log_entry_categories.ts (92%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_module.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results_url_state.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_setup.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_rate/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_rate/module_descriptor.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_rate/page.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_rate/page_content.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_rate/page_providers.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx (98%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_rate/page_setup_content.tsx (97%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/chart.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/expanded_row.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/index.tsx (99%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/table.tsx (98%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_rate/sections/helpers/data_formatters.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_rate/sections/log_rate/bar_chart.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_rate/sections/log_rate/index.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_rate.ts (89%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_module.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_results.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_results_url_state.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_setup.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/page.tsx (91%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/page_content.tsx (89%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/page_providers.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/settings.tsx (88%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/stream/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/stream/page.tsx (92%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/stream/page_content.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/stream/page_header.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/stream/page_logs_content.tsx (98%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/stream/page_no_indices_content.tsx (96%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/stream/page_providers.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/logs/stream/page_toolbar.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/metrics/components/chart_section_vis.tsx (97%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/metrics/components/error_message.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/metrics/components/gauges_section_vis.tsx (97%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/metrics/components/helpers.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/metrics/components/invalid_node.tsx (93%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/metrics/components/layout_content.tsx (83%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/metrics/components/metadata_details.tsx (98%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/metrics/components/node_details_page.tsx (98%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/metrics/components/page_body.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/metrics/components/page_error.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/metrics/components/section.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/metrics/components/series_chart.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/metrics/components/side_nav.tsx (95%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/metrics/components/sub_section.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/metrics/components/time_controls.test.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/metrics/components/time_controls.tsx (97%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/metrics/containers/metadata_context.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/metrics/containers/metrics.gql_query.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/metrics/containers/metrics_time.test.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/metrics/containers/with_metrics_time.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/metrics/index.tsx (96%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/metrics/lib/side_nav_context.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/metrics/page_providers.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/pages/metrics/types.ts (96%)
 create mode 100644 x-pack/plugins/infra/public/plugin.ts
 rename x-pack/{legacy/plugins/infra/public/feature_catalogue_entry.ts => plugins/infra/public/register_feature.ts} (75%)
 create mode 100644 x-pack/plugins/infra/public/routers/index.ts
 create mode 100644 x-pack/plugins/infra/public/routers/logs_router.tsx
 create mode 100644 x-pack/plugins/infra/public/routers/metrics_router.tsx
 rename x-pack/{legacy => }/plugins/infra/public/store/actions.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/store/epics.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/store/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/store/local/actions.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/store/local/epic.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/store/local/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/store/local/reducer.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/store/local/selectors.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/store/local/waffle_filter/actions.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/store/local/waffle_filter/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/store/local/waffle_filter/reducer.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/store/local/waffle_filter/selectors.ts (93%)
 rename x-pack/{legacy => }/plugins/infra/public/store/local/waffle_options/actions.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/store/local/waffle_options/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/store/local/waffle_options/reducer.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/store/local/waffle_options/selector.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/store/local/waffle_time/actions.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/store/local/waffle_time/epic.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/store/local/waffle_time/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/store/local/waffle_time/reducer.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/store/local/waffle_time/selectors.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/store/reducer.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/store/selectors.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/store/store.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/apollo_context.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/cancellable_effect.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/convert_interval_to_string.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/enzyme_helpers.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/fixtures/metrics_explorer.ts (97%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/formatters/bytes.test.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/formatters/bytes.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/formatters/datetime.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/formatters/high_precision.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/formatters/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/formatters/number.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/formatters/percent.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/handlers.ts (93%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/history_context.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/is_displayable.test.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/is_displayable.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/kuery.ts (87%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/loading_state/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/loading_state/loading_policy.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/loading_state/loading_progress.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/loading_state/loading_result.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/loading_state/loading_state.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/log_entry/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/log_entry/log_entry.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/log_entry/log_entry_highlight.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/map_timepicker_quickranges_to_datepicker_ranges.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/redirect_with_query_params.tsx (59%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/redux_context.tsx (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/source_configuration.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/styles.ts (94%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/typed_react.tsx (95%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/typed_redux.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/url_state.tsx (96%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/use_kibana_space_id.ts (64%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/use_kibana_ui_setting.ts (72%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/use_tracked_promise.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/use_url_state.ts (98%)
 rename x-pack/{legacy => }/plugins/infra/public/utils/use_visibility_state.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/scripts/combined_schema.ts (91%)
 rename x-pack/{legacy => }/plugins/infra/scripts/generate_types_from_graphql.js (97%)
 rename x-pack/{legacy => }/plugins/infra/scripts/gql_gen_client.json (100%)
 rename x-pack/{legacy => }/plugins/infra/scripts/gql_gen_server.json (100%)
 rename x-pack/{legacy => }/plugins/infra/scripts/storybook.js (100%)
 rename x-pack/{legacy => }/plugins/infra/server/features.ts (96%)
 rename x-pack/{legacy => }/plugins/infra/server/graphql/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/graphql/log_entries/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/graphql/log_entries/resolvers.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/graphql/log_entries/schema.gql.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/graphql/source_status/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/graphql/source_status/resolvers.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/graphql/source_status/schema.gql.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/graphql/sources/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/graphql/sources/resolvers.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/graphql/sources/schema.gql.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/graphql/types.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/infra_server.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/kibana.index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/adapters/fields/adapter_types.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/adapters/fields/framework_fields_adapter.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/adapters/fields/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/adapters/framework/adapter_types.ts (89%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/adapters/framework/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts (92%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/adapters/log_entries/adapter_types.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/adapters/log_entries/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts (97%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/adapters/metrics/adapter_types.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/adapters/metrics/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/adapters/metrics/lib/check_valid_node.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/adapters/metrics/lib/errors.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/adapters/source_status/elasticsearch_source_status_adapter.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/adapters/source_status/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/compose/kibana.ts (94%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/constants.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/fields_domain.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_apache2.test.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_apache2.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_auditd.test.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_auditd.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_haproxy.test.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_haproxy.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_icinga.test.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_icinga.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_iis.test.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_iis.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_kafka.test.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_logstash.test.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_logstash.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_mongodb.test.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_mongodb.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_mysql.test.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_mysql.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_nginx.test.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_nginx.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_osquery.test.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_osquery.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_redis.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_system.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_traefik.test.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_traefik.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/generic.test.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/generic.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/generic_webserver.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/helpers.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/convert_document_source_to_log_item_fields.test.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/convert_document_source_to_log_item_fields.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/message.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/log_entries_domain/rule_types.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/domains/metrics_domain.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/infra_types.ts (92%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/log_analysis/errors.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/log_analysis/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/log_analysis/log_entry_categories_analysis.ts (99%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/log_analysis/log_entry_rate_analysis.ts (98%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/log_analysis/queries/common.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/log_analysis/queries/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/log_analysis/queries/log_entry_categories.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/log_analysis/queries/log_entry_category_histograms.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/log_analysis/queries/log_entry_data_sets.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/log_analysis/queries/log_entry_rate.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/log_analysis/queries/top_log_entry_categories.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/snapshot/constants.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/snapshot/create_timerange_with_interval.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/snapshot/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/snapshot/query_helpers.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/snapshot/response_helpers.test.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/snapshot/response_helpers.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/snapshot/snapshot.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/snapshot/types.ts (90%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/source_status.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/sources/defaults.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/sources/errors.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/sources/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/sources/saved_object_mappings.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/sources/sources.test.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/sources/sources.ts (99%)
 rename x-pack/{legacy => }/plugins/infra/server/lib/sources/types.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/inventory_metadata/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/inventory_metadata/lib/get_cloud_metadata.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/ip_to_hostname.ts (96%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/log_analysis/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/log_analysis/results/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/log_analysis/results/log_entry_categories.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/log_analysis/validation/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/log_analysis/validation/indices.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/log_entries/entries.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/log_entries/highlights.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/log_entries/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/log_entries/item.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/log_entries/summary.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/log_entries/summary_highlights.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/metadata/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/metadata/lib/get_cloud_metric_metadata.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/metadata/lib/get_metric_metadata.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/metadata/lib/get_node_info.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/metadata/lib/get_pod_node_name.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/metadata/lib/has_apm_data.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/metadata/lib/pick_feature_name.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/metrics_explorer/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/metrics_explorer/lib/create_metrics_model.ts (96%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/metrics_explorer/lib/get_dataset_for_field.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/metrics_explorer/lib/get_groupings.ts (96%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/metrics_explorer/lib/populate_series_with_tsvb_data.ts (98%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/node_details/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/routes/snapshot/index.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/saved_objects.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/usage/usage_collector.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/utils/README.md (100%)
 rename x-pack/{legacy => }/plugins/infra/server/utils/calculate_metric_interval.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/utils/create_afterkey_handler.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/utils/elasticsearch_runtime_types.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/utils/get_all_composite_data.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/utils/get_interval_in_seconds.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/utils/serialized_query.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/utils/typed_elasticsearch_mappings.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/server/utils/typed_resolvers.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/types/eui.d.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/types/eui_experimental.d.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/types/graphql_fields.d.ts (100%)
 rename x-pack/{legacy => }/plugins/infra/types/redux_observable.d.ts (100%)
 create mode 100644 x-pack/plugins/observability/public/hooks/use_track_metric.tsx
 create mode 100644 x-pack/plugins/observability/public/typings/eui_draggable/index.ts
 create mode 100644 x-pack/plugins/observability/public/typings/eui_styled_components.tsx
 create mode 100644 x-pack/plugins/observability/public/typings/index.ts
 rename x-pack/{legacy/plugins/infra/types => typings}/rison_node.d.ts (100%)

diff --git a/test/functional/page_objects/common_page.ts b/test/functional/page_objects/common_page.ts
index 347923670c413..f909852b0c936 100644
--- a/test/functional/page_objects/common_page.ts
+++ b/test/functional/page_objects/common_page.ts
@@ -192,6 +192,7 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo
     public async navigateToUrlWithBrowserHistory(
       appName: string,
       subUrl?: string,
+      search?: string,
       {
         basePath = '',
         ensureCurrentUrl = true,
@@ -203,6 +204,7 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo
       const appConfig = {
         // subUrl following the basePath, assumes no hashes.  Ex: 'app/endpoint/management'
         pathname: `${basePath}${config.get(['apps', appName]).pathname}${subUrl}`,
+        search,
       };
 
       await this.navigate({
diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json
index 99882a6e235e2..775c661dd75e0 100644
--- a/x-pack/.i18nrc.json
+++ b/x-pack/.i18nrc.json
@@ -17,7 +17,7 @@
     "xpack.grokDebugger": "legacy/plugins/grokdebugger",
     "xpack.idxMgmt": "legacy/plugins/index_management",
     "xpack.indexLifecycleMgmt": "legacy/plugins/index_lifecycle_management",
-    "xpack.infra": "legacy/plugins/infra",
+    "xpack.infra": "plugins/infra",
     "xpack.data": "plugins/data_enhanced",
     "xpack.lens": "legacy/plugins/lens",
     "xpack.licenseMgmt": "legacy/plugins/license_management",
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx
index a247390704e72..d79f2a4ed481d 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx
@@ -25,7 +25,7 @@ import { DetailView } from './DetailView';
 import { ErrorDistribution } from './Distribution';
 import { useLocation } from '../../../hooks/useLocation';
 import { useUrlParams } from '../../../hooks/useUrlParams';
-import { useTrackPageview } from '../../../../../infra/public';
+import { useTrackPageview } from '../../../../../../../plugins/observability/public';
 
 const Titles = styled.div`
   margin-bottom: ${px(units.plus)};
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/index.tsx
index 2daf5e55d4e72..9f7ff65d37d36 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/index.tsx
@@ -17,7 +17,7 @@ import { useFetcher } from '../../../hooks/useFetcher';
 import { ErrorDistribution } from '../ErrorGroupDetails/Distribution';
 import { ErrorGroupList } from './List';
 import { useUrlParams } from '../../../hooks/useUrlParams';
-import { useTrackPageview } from '../../../../../infra/public';
+import { useTrackPageview } from '../../../../../../../plugins/observability/public';
 import { PROJECTION } from '../../../../common/projections/typings';
 import { LocalUIFilters } from '../../shared/LocalUIFilters';
 
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/index.tsx
index b522736c80f9b..762c10c0f48a7 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/index.tsx
@@ -14,7 +14,7 @@ import { useFetcher } from '../../../hooks/useFetcher';
 import { NoServicesMessage } from './NoServicesMessage';
 import { ServiceList } from './ServiceList';
 import { useUrlParams } from '../../../hooks/useUrlParams';
-import { useTrackPageview } from '../../../../../infra/public';
+import { useTrackPageview } from '../../../../../../../plugins/observability/public';
 import { PROJECTION } from '../../../../common/projections/typings';
 import { LocalUIFilters } from '../../shared/LocalUIFilters';
 import { useApmPluginContext } from '../../../hooks/useApmPluginContext';
diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx
index 7243a86404f04..4f808b3b4b5d9 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx
@@ -34,6 +34,7 @@ import { isRumAgentName } from '../../../../../../common/agent_name';
 import { ALL_OPTION_VALUE } from '../../../../../../common/agent_configuration_constants';
 import { saveConfig } from './saveConfig';
 import { useApmPluginContext } from '../../../../../hooks/useApmPluginContext';
+import { useUiTracker } from '../../../../../../../../../plugins/observability/public';
 
 const defaultSettings = {
   TRANSACTION_SAMPLE_RATE: '1.0',
@@ -59,6 +60,9 @@ export function AddEditFlyout({
 
   const callApmApiFromHook = useCallApmApi();
 
+  // get a telemetry UI event tracker
+  const trackApmEvent = useUiTracker({ app: 'apm' });
+
   // config conditions (service)
   const [serviceName, setServiceName] = useState<string>(
     selectedConfig ? selectedConfig.service.name || ALL_OPTION_VALUE : ''
@@ -133,7 +137,8 @@ export function AddEditFlyout({
       transactionMaxSpans,
       configurationId: selectedConfig ? selectedConfig.id : undefined,
       agentName,
-      toasts
+      toasts,
+      trackApmEvent
     });
     setIsSaving(false);
     onSaved();
diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts
index 7ca221d642b08..a0c7c97e012a4 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts
+++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts
@@ -7,12 +7,12 @@
 import { i18n } from '@kbn/i18n';
 import { NotificationsStart } from 'kibana/public';
 import { APMClient } from '../../../../../services/rest/createCallApmApi';
-import { trackEvent } from '../../../../../../../infra/public/hooks/use_track_metric';
 import { isRumAgentName } from '../../../../../../common/agent_name';
 import {
   getOptionLabel,
   omitAllOption
 } from '../../../../../../common/agent_configuration_constants';
+import { UiTracker } from '../../../../../../../../../plugins/observability/public';
 
 interface Settings {
   transaction_sample_rate: number;
@@ -29,7 +29,8 @@ export async function saveConfig({
   transactionMaxSpans,
   configurationId,
   agentName,
-  toasts
+  toasts,
+  trackApmEvent
 }: {
   callApmApi: APMClient;
   serviceName: string;
@@ -40,8 +41,9 @@ export async function saveConfig({
   configurationId?: string;
   agentName?: string;
   toasts: NotificationsStart['toasts'];
+  trackApmEvent: UiTracker;
 }) {
-  trackEvent({ app: 'apm', name: 'save_agent_configuration' });
+  trackApmEvent({ metric: 'save_agent_configuration' });
 
   try {
     const settings: Settings = {
diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/index.tsx
index 9e95cfd0fad7f..8812cccd2edaf 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/index.tsx
@@ -18,7 +18,7 @@ import { isEmpty } from 'lodash';
 import { useFetcher } from '../../../../hooks/useFetcher';
 import { AgentConfigurationListAPIResponse } from '../../../../../server/lib/settings/agent_configuration/list_configurations';
 import { AgentConfigurationList } from './AgentConfigurationList';
-import { useTrackPageview } from '../../../../../../infra/public';
+import { useTrackPageview } from '../../../../../../../../plugins/observability/public';
 import { AddEditFlyout } from './AddEditFlyout';
 
 export type Config = AgentConfigurationListAPIResponse[0];
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TraceOverview/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TraceOverview/index.tsx
index dd3e2d13826dc..3bdcc3231cddc 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TraceOverview/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TraceOverview/index.tsx
@@ -9,7 +9,7 @@ import React, { useMemo } from 'react';
 import { FETCH_STATUS, useFetcher } from '../../../hooks/useFetcher';
 import { TraceList } from './TraceList';
 import { useUrlParams } from '../../../hooks/useUrlParams';
-import { useTrackPageview } from '../../../../../infra/public';
+import { useTrackPageview } from '../../../../../../../plugins/observability/public';
 import { LocalUIFilters } from '../../shared/LocalUIFilters';
 import { PROJECTION } from '../../../../common/projections/typings';
 
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/index.tsx
index 9d6639b000762..a6712becf7968 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/index.tsx
@@ -26,7 +26,7 @@ import { useUrlParams } from '../../../hooks/useUrlParams';
 import { FETCH_STATUS } from '../../../hooks/useFetcher';
 import { TransactionBreakdown } from '../../shared/TransactionBreakdown';
 import { ChartsSyncContextProvider } from '../../../context/ChartsSyncContext';
-import { useTrackPageview } from '../../../../../infra/public';
+import { useTrackPageview } from '../../../../../../../plugins/observability/public';
 import { PROJECTION } from '../../../../common/projections/typings';
 import { LocalUIFilters } from '../../shared/LocalUIFilters';
 import { HeightRetainer } from '../../shared/HeightRetainer';
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/index.tsx
index 2cf01f8b40a09..73824f235ab02 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/index.tsx
@@ -27,7 +27,7 @@ import { getHasMLJob } from '../../../services/rest/ml';
 import { history } from '../../../utils/history';
 import { useLocation } from '../../../hooks/useLocation';
 import { ChartsSyncContextProvider } from '../../../context/ChartsSyncContext';
-import { useTrackPageview } from '../../../../../infra/public';
+import { useTrackPageview } from '../../../../../../../plugins/observability/public';
 import { fromQuery, toQuery } from '../../shared/Links/url_helpers';
 import { LocalUIFilters } from '../../shared/LocalUIFilters';
 import { PROJECTION } from '../../../../common/projections/typings';
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/InfraLink.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/InfraLink.test.tsx
index 42022a3741495..0b39aa9ada756 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Links/InfraLink.test.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/InfraLink.test.tsx
@@ -11,13 +11,15 @@ import { InfraLink } from './InfraLink';
 
 test('InfraLink produces the correct URL', async () => {
   const href = await getRenderedHref(
-    () => <InfraLink path="/some/path" query={{ time: 1554687198 }} />,
+    () => (
+      <InfraLink app="metrics" path="/some/path" query={{ time: 1554687198 }} />
+    ),
     {
       search: '?rangeFrom=now-5h&rangeTo=now-2h'
     } as Location
   );
 
   expect(href).toMatchInlineSnapshot(
-    `"/basepath/app/infra#/some/path?time=1554687198"`
+    `"/basepath/app/metrics/some/path?time=1554687198"`
   );
 });
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/InfraLink.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/InfraLink.tsx
index e4f3557a2ce51..7efe5cb96cfbd 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Links/InfraLink.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/InfraLink.tsx
@@ -5,12 +5,12 @@
  */
 
 import { EuiLink, EuiLinkAnchorProps } from '@elastic/eui';
-import { compact } from 'lodash';
 import React from 'react';
 import url from 'url';
 import { fromQuery } from './url_helpers';
 import { useApmPluginContext } from '../../../hooks/useApmPluginContext';
 import { AppMountContextBasePath } from '../../../context/ApmPluginContext';
+import { InfraAppId } from '../../../../../../../plugins/infra/public';
 
 interface InfraQueryParams {
   time?: number;
@@ -20,29 +20,32 @@ interface InfraQueryParams {
 }
 
 interface Props extends EuiLinkAnchorProps {
+  app: InfraAppId;
   path?: string;
   query: InfraQueryParams;
   children?: React.ReactNode;
 }
 
 export const getInfraHref = ({
+  app,
   basePath,
   query,
   path
 }: {
+  app: InfraAppId;
   basePath: AppMountContextBasePath;
   query: InfraQueryParams;
   path?: string;
 }) => {
   const nextSearch = fromQuery(query);
   return url.format({
-    pathname: basePath.prepend('/app/infra'),
-    hash: compact([path, nextSearch]).join('?')
+    pathname: basePath.prepend(`/app/${app}${path || ''}`),
+    search: nextSearch
   });
 };
 
-export function InfraLink({ path, query = {}, ...rest }: Props) {
+export function InfraLink({ app, path, query = {}, ...rest }: Props) {
   const { core } = useApmPluginContext();
-  const href = getInfraHref({ basePath: core.http.basePath, query, path });
+  const href = getInfraHref({ app, basePath: core.http.basePath, query, path });
   return <EuiLink {...rest} href={href} />;
 }
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts
index 52c2d27eabb82..9f07bd508c95c 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts
+++ b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts
@@ -10,7 +10,9 @@ import { AppMountContextBasePath } from '../../../../context/ApmPluginContext';
 
 describe('Transaction action menu', () => {
   const basePath = ({
-    prepend: jest.fn()
+    prepend: (url: string) => {
+      return `some-basepath${url}`;
+    }
   } as unknown) as AppMountContextBasePath;
   const date = '2020-02-06T11:00:00.000Z';
   const timestamp = { us: new Date(date).getTime() };
@@ -40,7 +42,7 @@ describe('Transaction action menu', () => {
               key: 'traceLogs',
               label: 'Trace logs',
               href:
-                '#/link-to/logs?time=1580986800&filter=trace.id:%22123%22%20OR%20123',
+                'some-basepath/app/logs/link-to/logs?time=1580986800&filter=trace.id:%22123%22%20OR%20123',
               condition: true
             }
           ]
@@ -54,7 +56,7 @@ describe('Transaction action menu', () => {
               key: 'sampleDocument',
               label: 'View sample document',
               href:
-                '#/discover?_g=(refreshInterval:(pause:true,value:\'0\'),time:(from:now-24h,to:now))&_a=(index:apm_static_index_pattern_id,interval:auto,query:(language:kuery,query:\'processor.event:"transaction" AND transaction.id:"123" AND trace.id:"123"\'))',
+                'some-basepath/app/kibana#/discover?_g=(refreshInterval:(pause:true,value:\'0\'),time:(from:now-24h,to:now))&_a=(index:apm_static_index_pattern_id,interval:auto,query:(language:kuery,query:\'processor.event:"transaction" AND transaction.id:"123" AND trace.id:"123"\'))',
               condition: true
             }
           ]
@@ -89,14 +91,15 @@ describe('Transaction action menu', () => {
             {
               key: 'podLogs',
               label: 'Pod logs',
-              href: '#/link-to/pod-logs/123?time=1580986800',
+              href:
+                'some-basepath/app/logs/link-to/pod-logs/123?time=1580986800',
               condition: true
             },
             {
               key: 'podMetrics',
               label: 'Pod metrics',
               href:
-                '#/link-to/pod-detail/123?from=1580986500000&to=1580987100000',
+                'some-basepath/app/metrics/link-to/pod-detail/123?from=1580986500000&to=1580987100000',
               condition: true
             }
           ]
@@ -110,7 +113,7 @@ describe('Transaction action menu', () => {
               key: 'traceLogs',
               label: 'Trace logs',
               href:
-                '#/link-to/logs?time=1580986800&filter=trace.id:%22123%22%20OR%20123',
+                'some-basepath/app/logs/link-to/logs?time=1580986800&filter=trace.id:%22123%22%20OR%20123',
               condition: true
             }
           ]
@@ -124,7 +127,7 @@ describe('Transaction action menu', () => {
               key: 'sampleDocument',
               label: 'View sample document',
               href:
-                '#/discover?_g=(refreshInterval:(pause:true,value:\'0\'),time:(from:now-24h,to:now))&_a=(index:apm_static_index_pattern_id,interval:auto,query:(language:kuery,query:\'processor.event:"transaction" AND transaction.id:"123" AND trace.id:"123"\'))',
+                'some-basepath/app/kibana#/discover?_g=(refreshInterval:(pause:true,value:\'0\'),time:(from:now-24h,to:now))&_a=(index:apm_static_index_pattern_id,interval:auto,query:(language:kuery,query:\'processor.event:"transaction" AND transaction.id:"123" AND trace.id:"123"\'))',
               condition: true
             }
           ]
@@ -158,14 +161,15 @@ describe('Transaction action menu', () => {
             {
               key: 'hostLogs',
               label: 'Host logs',
-              href: '#/link-to/host-logs/foo?time=1580986800',
+              href:
+                'some-basepath/app/logs/link-to/host-logs/foo?time=1580986800',
               condition: true
             },
             {
               key: 'hostMetrics',
               label: 'Host metrics',
               href:
-                '#/link-to/host-detail/foo?from=1580986500000&to=1580987100000',
+                'some-basepath/app/metrics/link-to/host-detail/foo?from=1580986500000&to=1580987100000',
               condition: true
             }
           ]
@@ -179,7 +183,7 @@ describe('Transaction action menu', () => {
               key: 'traceLogs',
               label: 'Trace logs',
               href:
-                '#/link-to/logs?time=1580986800&filter=trace.id:%22123%22%20OR%20123',
+                'some-basepath/app/logs/link-to/logs?time=1580986800&filter=trace.id:%22123%22%20OR%20123',
               condition: true
             }
           ]
@@ -193,7 +197,7 @@ describe('Transaction action menu', () => {
               key: 'sampleDocument',
               label: 'View sample document',
               href:
-                '#/discover?_g=(refreshInterval:(pause:true,value:\'0\'),time:(from:now-24h,to:now))&_a=(index:apm_static_index_pattern_id,interval:auto,query:(language:kuery,query:\'processor.event:"transaction" AND transaction.id:"123" AND trace.id:"123"\'))',
+                'some-basepath/app/kibana#/discover?_g=(refreshInterval:(pause:true,value:\'0\'),time:(from:now-24h,to:now))&_a=(index:apm_static_index_pattern_id,interval:auto,query:(language:kuery,query:\'processor.event:"transaction" AND transaction.id:"123" AND trace.id:"123"\'))',
               condition: true
             }
           ]
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/sections.ts b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/sections.ts
index 77445a2600960..31efdb6355563 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/sections.ts
+++ b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/sections.ts
@@ -82,6 +82,7 @@ export const getSections = ({
         { defaultMessage: 'Pod logs' }
       ),
       href: getInfraHref({
+        app: 'logs',
         basePath,
         path: `/link-to/pod-logs/${podId}`,
         query: { time }
@@ -95,6 +96,7 @@ export const getSections = ({
         { defaultMessage: 'Pod metrics' }
       ),
       href: getInfraHref({
+        app: 'metrics',
         basePath,
         path: `/link-to/pod-detail/${podId}`,
         query: infraMetricsQuery
@@ -111,6 +113,7 @@ export const getSections = ({
         { defaultMessage: 'Container logs' }
       ),
       href: getInfraHref({
+        app: 'logs',
         basePath,
         path: `/link-to/container-logs/${containerId}`,
         query: { time }
@@ -124,6 +127,7 @@ export const getSections = ({
         { defaultMessage: 'Container metrics' }
       ),
       href: getInfraHref({
+        app: 'metrics',
         basePath,
         path: `/link-to/container-detail/${containerId}`,
         query: infraMetricsQuery
@@ -140,6 +144,7 @@ export const getSections = ({
         { defaultMessage: 'Host logs' }
       ),
       href: getInfraHref({
+        app: 'logs',
         basePath,
         path: `/link-to/host-logs/${hostName}`,
         query: { time }
@@ -153,6 +158,7 @@ export const getSections = ({
         { defaultMessage: 'Host metrics' }
       ),
       href: getInfraHref({
+        app: 'metrics',
         basePath,
         path: `/link-to/host-detail/${hostName}`,
         query: infraMetricsQuery
@@ -169,6 +175,7 @@ export const getSections = ({
         { defaultMessage: 'Trace logs' }
       ),
       href: getInfraHref({
+        app: 'logs',
         basePath,
         path: `/link-to/logs`,
         query: {
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownGraph/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownGraph/index.tsx
index c4e7ed86df8b7..a964b425073b5 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownGraph/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownGraph/index.tsx
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import React from 'react';
+import React, { useMemo } from 'react';
 import numeral from '@elastic/numeral';
 import { throttle } from 'lodash';
 import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n';
@@ -14,7 +14,7 @@ import { TransactionLineChart } from '../../charts/TransactionCharts/Transaction
 import { asPercent } from '../../../../utils/formatters';
 import { unit } from '../../../../style/variables';
 import { isValidCoordinateValue } from '../../../../utils/isValidCoordinateValue';
-import { trackEvent } from '../../../../../../infra/public/hooks/use_track_metric';
+import { useUiTracker } from '../../../../../../../../plugins/observability/public';
 
 interface Props {
   timeseries: TimeSeries[];
@@ -30,13 +30,14 @@ const formatTooltipValue = (coordinate: Coordinate) => {
     : NOT_AVAILABLE_LABEL;
 };
 
-const trackHoverBreakdownChart = throttle(
-  () => trackEvent({ app: 'apm', name: 'hover_breakdown_chart' }),
-  60000
-);
-
 const TransactionBreakdownGraph: React.FC<Props> = props => {
   const { timeseries } = props;
+  const trackApmEvent = useUiTracker({ app: 'apm' });
+  const handleHover = useMemo(
+    () =>
+      throttle(() => trackApmEvent({ metric: 'hover_breakdown_chart' }), 60000),
+    [trackApmEvent]
+  );
 
   return (
     <TransactionLineChart
@@ -46,7 +47,7 @@ const TransactionBreakdownGraph: React.FC<Props> = props => {
       yMax={1}
       height={unit * 12}
       stacked={true}
-      onHover={trackHoverBreakdownChart}
+      onHover={handleHover}
     />
   );
 };
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownKpiList.tsx b/x-pack/legacy/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownKpiList.tsx
index 0e31cfff9a295..91f5f4e0a7176 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownKpiList.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownKpiList.tsx
@@ -13,8 +13,10 @@ import {
   EuiIcon
 } from '@elastic/eui';
 import styled from 'styled-components';
-import { InfraFormatterType } from '../../../../../infra/public/lib/lib';
-import { FORMATTERS } from '../../../../../infra/public/utils/formatters';
+import {
+  FORMATTERS,
+  InfraFormatterType
+} from '../../../../../../../plugins/infra/public';
 
 interface TransactionBreakdownKpi {
   name: string;
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionBreakdown/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/TransactionBreakdown/index.tsx
index d8b7c5059ec62..85f5f83fb920e 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionBreakdown/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/TransactionBreakdown/index.tsx
@@ -10,8 +10,8 @@ import { useTransactionBreakdown } from '../../../hooks/useTransactionBreakdown'
 import { TransactionBreakdownHeader } from './TransactionBreakdownHeader';
 import { TransactionBreakdownKpiList } from './TransactionBreakdownKpiList';
 import { TransactionBreakdownGraph } from './TransactionBreakdownGraph';
-import { trackEvent } from '../../../../../infra/public/hooks/use_track_metric';
 import { FETCH_STATUS } from '../../../hooks/useFetcher';
+import { useUiTracker } from '../../../../../../../plugins/observability/public';
 
 const emptyMessage = i18n.translate('xpack.apm.transactionBreakdown.noData', {
   defaultMessage: 'No data within this time range.'
@@ -21,11 +21,9 @@ const TransactionBreakdown: React.FC<{
   initialIsOpen?: boolean;
 }> = ({ initialIsOpen }) => {
   const [showChart, setShowChart] = useState(!!initialIsOpen);
-
   const { data, status } = useTransactionBreakdown();
-
+  const trackApmEvent = useUiTracker({ app: 'apm' });
   const { kpis, timeseries } = data;
-
   const noHits = data.kpis.length === 0 && status === FETCH_STATUS.SUCCESS;
   const showEmptyMessage = noHits && !showChart;
 
@@ -38,9 +36,9 @@ const TransactionBreakdown: React.FC<{
             onToggleClick={() => {
               setShowChart(!showChart);
               if (showChart) {
-                trackEvent({ app: 'apm', name: 'hide_breakdown_chart' });
+                trackApmEvent({ metric: 'hide_breakdown_chart' });
               } else {
-                trackEvent({ app: 'apm', name: 'show_breakdown_chart' });
+                trackApmEvent({ metric: 'show_breakdown_chart' });
               }
             }}
           />
diff --git a/x-pack/legacy/plugins/apm/public/new-platform/plugin.tsx b/x-pack/legacy/plugins/apm/public/new-platform/plugin.tsx
index 58a41014f2fe9..64deff9f1ae39 100644
--- a/x-pack/legacy/plugins/apm/public/new-platform/plugin.tsx
+++ b/x-pack/legacy/plugins/apm/public/new-platform/plugin.tsx
@@ -37,6 +37,7 @@ import { getConfigFromInjectedMetadata } from './getConfigFromInjectedMetadata';
 import { setHelpExtension } from './setHelpExtension';
 import { toggleAppLinkInNav } from './toggleAppLinkInNav';
 import { setReadonlyBadge } from './updateBadge';
+import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
 import { Permission } from '../components/app/Permission';
 
 export const REACT_APP_ROOT_ID = 'react-apm-root';
@@ -135,21 +136,23 @@ export class ApmPlugin
 
     ReactDOM.render(
       <ApmPluginContext.Provider value={apmPluginContextValue}>
-        <i18nCore.Context>
-          <Router history={history}>
-            <LocationProvider>
-              <MatchedRouteProvider routes={routes}>
-                <UrlParamsProvider>
-                  <LoadingIndicatorProvider>
-                    <LicenseProvider>
-                      <App />
-                    </LicenseProvider>
-                  </LoadingIndicatorProvider>
-                </UrlParamsProvider>
-              </MatchedRouteProvider>
-            </LocationProvider>
-          </Router>
-        </i18nCore.Context>
+        <KibanaContextProvider services={{ ...core, ...plugins }}>
+          <i18nCore.Context>
+            <Router history={history}>
+              <LocationProvider>
+                <MatchedRouteProvider routes={routes}>
+                  <UrlParamsProvider>
+                    <LoadingIndicatorProvider>
+                      <LicenseProvider>
+                        <App />
+                      </LicenseProvider>
+                    </LoadingIndicatorProvider>
+                  </UrlParamsProvider>
+                </MatchedRouteProvider>
+              </LocationProvider>
+            </Router>
+          </i18nCore.Context>
+        </KibanaContextProvider>
       </ApmPluginContext.Provider>,
       document.getElementById(REACT_APP_ROOT_ID)
     );
diff --git a/x-pack/legacy/plugins/infra/index.ts b/x-pack/legacy/plugins/infra/index.ts
index 4ab2cde082498..6ef273924a346 100644
--- a/x-pack/legacy/plugins/infra/index.ts
+++ b/x-pack/legacy/plugins/infra/index.ts
@@ -3,115 +3,23 @@
  * or more contributor license agreements. Licensed under the Elastic License;
  * you may not use this file except in compliance with the Elastic License.
  */
-
-import { i18n } from '@kbn/i18n';
-import JoiNamespace from 'joi';
-import { resolve } from 'path';
-import { PluginInitializerContext } from 'src/core/server';
-import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
-import KbnServer from 'src/legacy/server/kbn_server';
-import { getConfigSchema } from './server/kibana.index';
-import { savedObjectMappings } from './server/saved_objects';
-import { plugin, InfraServerPluginDeps } from './server/new_platform_index';
-import { InfraSetup } from '../../../plugins/infra/server';
-import { PluginSetupContract as FeaturesPluginSetup } from '../../../plugins/features/server';
-import { SpacesPluginSetup } from '../../../plugins/spaces/server';
-import { VisTypeTimeseriesSetup } from '../../../../src/plugins/vis_type_timeseries/server';
-import { APMPluginContract } from '../../../plugins/apm/server';
-import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils';
-
-export const APP_ID = 'infra';
+import { Root } from 'joi';
+import { savedObjectMappings } from '../../../plugins/infra/server';
 
 export function infra(kibana: any) {
   return new kibana.Plugin({
-    id: APP_ID,
+    id: 'infra',
     configPrefix: 'xpack.infra',
-    publicDir: resolve(__dirname, 'public'),
-    require: ['kibana', 'elasticsearch', 'metrics'],
+    require: ['kibana', 'elasticsearch'],
     uiExports: {
-      app: {
-        description: i18n.translate('xpack.infra.infrastructureDescription', {
-          defaultMessage: 'Explore your metrics',
-        }),
-        icon: 'plugins/infra/images/infra_mono_white.svg',
-        main: 'plugins/infra/app',
-        title: i18n.translate('xpack.infra.infrastructureTitle', {
-          defaultMessage: 'Metrics',
-        }),
-        listed: false,
-        url: `/app/${APP_ID}#/infrastructure`,
-      },
-      styleSheetPaths: resolve(__dirname, 'public/index.scss'),
-      home: ['plugins/infra/legacy_register_feature'],
-      links: [
-        {
-          description: i18n.translate('xpack.infra.linkInfrastructureDescription', {
-            defaultMessage: 'Explore your metrics',
-          }),
-          icon: 'plugins/infra/images/infra_mono_white.svg',
-          euiIconType: 'metricsApp',
-          id: 'infra:home',
-          order: 8000,
-          title: i18n.translate('xpack.infra.linkInfrastructureTitle', {
-            defaultMessage: 'Metrics',
-          }),
-          url: `/app/${APP_ID}#/infrastructure`,
-          category: DEFAULT_APP_CATEGORIES.observability,
-        },
-        {
-          description: i18n.translate('xpack.infra.linkLogsDescription', {
-            defaultMessage: 'Explore your logs',
-          }),
-          icon: 'plugins/infra/images/logging_mono_white.svg',
-          euiIconType: 'logsApp',
-          id: 'infra:logs',
-          order: 8001,
-          title: i18n.translate('xpack.infra.linkLogsTitle', {
-            defaultMessage: 'Logs',
-          }),
-          url: `/app/${APP_ID}#/logs`,
-          category: DEFAULT_APP_CATEGORIES.observability,
-        },
-      ],
       mappings: savedObjectMappings,
     },
-    config(Joi: typeof JoiNamespace) {
-      return getConfigSchema(Joi);
-    },
-    init(legacyServer: any) {
-      const { newPlatform } = legacyServer as KbnServer;
-      const { core, plugins } = newPlatform.setup;
-
-      const infraSetup = (plugins.infra as unknown) as InfraSetup; // chef's kiss
-
-      const initContext = ({
-        config: infraSetup.__legacy.config,
-      } as unknown) as PluginInitializerContext;
-      // NP_TODO: Use real types from the other plugins as they are migrated
-      const pluginDeps: InfraServerPluginDeps = {
-        home: legacyServer.newPlatform.setup.plugins.home,
-        usageCollection: plugins.usageCollection as UsageCollectionSetup,
-        indexPatterns: {
-          indexPatternsServiceFactory: legacyServer.indexPatternsServiceFactory,
-        },
-        metrics: plugins.metrics as VisTypeTimeseriesSetup,
-        spaces: plugins.spaces as SpacesPluginSetup,
-        features: plugins.features as FeaturesPluginSetup,
-        apm: plugins.apm as APMPluginContract,
-      };
-
-      const infraPluginInstance = plugin(initContext);
-      infraPluginInstance.setup(core, pluginDeps);
-
-      // NP_TODO: EVERYTHING BELOW HERE IS LEGACY
-
-      const libs = infraPluginInstance.getLibs();
-
-      // NP_NOTE: Left here for now for legacy plugins to consume
-      legacyServer.expose(
-        'defineInternalSourceConfiguration',
-        libs.sources.defineInternalSourceConfiguration.bind(libs.sources)
-      );
+    config(Joi: Root) {
+      return Joi.object({
+        enabled: Joi.boolean().default(true),
+      })
+        .unknown()
+        .default();
     },
   });
 }
diff --git a/x-pack/legacy/plugins/infra/package.json b/x-pack/legacy/plugins/infra/package.json
deleted file mode 100644
index 7aa8cb9b5269a..0000000000000
--- a/x-pack/legacy/plugins/infra/package.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
-  "author": "Elastic",
-  "name": "infra",
-  "version": "7.0.0",
-  "private": true,
-  "license": "Elastic-License",
-  "scripts": {
-    "build-graphql-types": "node scripts/generate_types_from_graphql.js"
-  },
-  "devDependencies": {
-    "@types/boom": "7.2.1",
-    "@types/lodash": "^4.14.110"
-  },
-  "dependencies": {
-    "@types/color": "^3.0.0",
-    "boom": "7.3.0",
-    "lodash": "^4.17.15"
-  }
-}
diff --git a/x-pack/legacy/plugins/infra/public/app.ts b/x-pack/legacy/plugins/infra/public/app.ts
deleted file mode 100644
index 7a13d3a59cc0d..0000000000000
--- a/x-pack/legacy/plugins/infra/public/app.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-// NP_TODO: This app.ts layer is needed until we migrate 100% to the NP.
-// This is so other plugins can import from our public/index file without trying to
-// actually mount and run our application. Once in the NP this won't be an issue
-// as the NP will look for an export named "plugin" and run that from the index file.
-
-import { npStart, npSetup } from 'ui/new_platform';
-import { PluginInitializerContext } from 'kibana/public';
-import chrome from 'ui/chrome';
-// @ts-ignore
-import { uiModules } from 'ui/modules';
-import uiRoutes from 'ui/routes';
-// @ts-ignore
-import { timezoneProvider } from 'ui/vis/lib/timezone';
-import { plugin } from './new_platform_index';
-
-const ROOT_ELEMENT_ID = 'react-infra-root';
-export { ROOT_ELEMENT_ID };
-
-const { core, plugins } = npStart;
-const __LEGACY = {
-  uiModules,
-  uiRoutes,
-  timezoneProvider,
-};
-// This will be moved to core.application.register when the new platform
-// migration is complete.
-// @ts-ignore
-chrome.setRootTemplate(`
-  <main
-    id="${ROOT_ELEMENT_ID}"
-    class="infReactRoot"
-  ></main>
-`);
-
-const checkForRoot = () => {
-  return new Promise(resolve => {
-    const ready = !!document.getElementById(ROOT_ELEMENT_ID);
-    if (ready) {
-      resolve();
-    } else {
-      setTimeout(() => resolve(checkForRoot()), 10);
-    }
-  });
-};
-
-checkForRoot().then(() => {
-  const pluginInstance = plugin({} as PluginInitializerContext);
-  pluginInstance.setup(npSetup.core, { home: npSetup.plugins.home });
-  pluginInstance.start(core, plugins, __LEGACY);
-});
diff --git a/x-pack/legacy/plugins/infra/public/hooks/use_track_metric.tsx b/x-pack/legacy/plugins/infra/public/hooks/use_track_metric.tsx
deleted file mode 100644
index c5945ab808202..0000000000000
--- a/x-pack/legacy/plugins/infra/public/hooks/use_track_metric.tsx
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { useEffect } from 'react';
-import {
-  createUiStatsReporter,
-  UiStatsMetricType,
-  METRIC_TYPE,
-} from '../../../../../../src/legacy/core_plugins/ui_metric/public';
-
-/**
- * Note: The UI Metric plugin will take care of sending this data to the telemetry server.
- * You can find these metrics stored at:
- * stack_stats.kibana.plugins.ui_metric.{app}.{metric}(__delayed_{n}ms)?
- * which will be an array of objects each containing a key, representing the metric, and
- * a value, which will be a counter
- */
-
-type ObservabilityApp = 'infra_metrics' | 'infra_logs' | 'apm' | 'uptime';
-
-const trackerCache = new Map<string, ReturnType<typeof createUiStatsReporter>>();
-
-function getTrackerForApp(app: string) {
-  const cached = trackerCache.get(app);
-  if (cached) {
-    return cached;
-  }
-
-  const tracker = createUiStatsReporter(app);
-  trackerCache.set(app, tracker);
-
-  return tracker;
-}
-
-interface TrackOptions {
-  app: ObservabilityApp;
-  metricType?: UiStatsMetricType;
-  delay?: number; // in ms
-}
-type EffectDeps = unknown[];
-
-type TrackMetricOptions = TrackOptions & { metric: string };
-
-export { METRIC_TYPE };
-export function useTrackMetric(
-  { app, metric, metricType = METRIC_TYPE.COUNT, delay = 0 }: TrackMetricOptions,
-  effectDependencies: EffectDeps = []
-) {
-  useEffect(() => {
-    let decoratedMetric = metric;
-    if (delay > 0) {
-      decoratedMetric += `__delayed_${delay}ms`;
-    }
-    const trackUiMetric = getTrackerForApp(app);
-    const id = setTimeout(() => trackUiMetric(metricType, decoratedMetric), Math.max(delay, 0));
-    return () => clearTimeout(id);
-
-    // the dependencies are managed externally
-    // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, effectDependencies);
-}
-
-/**
- * useTrackPageview is a convenience wrapper for tracking a pageview
- * Its metrics will be found at:
- * stack_stats.kibana.plugins.ui_metric.{app}.pageview__{path}(__delayed_{n}ms)?
- */
-type TrackPageviewProps = TrackOptions & { path: string };
-
-export function useTrackPageview(
-  { path, ...rest }: TrackPageviewProps,
-  effectDependencies: EffectDeps = []
-) {
-  useTrackMetric({ ...rest, metric: `pageview__${path}` }, effectDependencies);
-}
-
-interface TrackEventProps {
-  app: ObservabilityApp;
-  name: string;
-  metricType?: UiStatsMetricType;
-}
-
-export function trackEvent({ app, name, metricType = METRIC_TYPE.CLICK }: TrackEventProps) {
-  const trackUiMetric = getTrackerForApp(app);
-  trackUiMetric(metricType, `event__${name}`);
-}
diff --git a/x-pack/legacy/plugins/infra/public/index.ts b/x-pack/legacy/plugins/infra/public/index.ts
deleted file mode 100644
index 5e68ca85681fe..0000000000000
--- a/x-pack/legacy/plugins/infra/public/index.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-// NP_NOTE: Whilst we are in the transition period of the NP migration, this index file
-// is exclusively for our static code exports that other plugins (e.g. APM) use.
-// When we switch over to the real NP, and an export of "plugin" is expected and called,
-// we can do away with the middle "app.ts" layer. The "app.ts" layer is needed for now,
-// and needs to be situated differently to this index file, so that our code for setting the root template
-// and attempting to start the app doesn't try to run just because another plugin is importing from this file.
-
-export { useTrackPageview } from './hooks/use_track_metric';
diff --git a/x-pack/legacy/plugins/infra/public/legacy_register_feature.ts b/x-pack/legacy/plugins/infra/public/legacy_register_feature.ts
deleted file mode 100644
index 7b10a1e062f75..0000000000000
--- a/x-pack/legacy/plugins/infra/public/legacy_register_feature.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { npSetup } from 'ui/new_platform';
-import { featureCatalogueEntries } from './feature_catalogue_entry';
-
-const {
-  plugins: { home },
-} = npSetup;
-
-home.featureCatalogue.register(featureCatalogueEntries.metrics);
-home.featureCatalogue.register(featureCatalogueEntries.logs);
diff --git a/x-pack/legacy/plugins/infra/public/new_platform_plugin.ts b/x-pack/legacy/plugins/infra/public/new_platform_plugin.ts
deleted file mode 100644
index f438b65794653..0000000000000
--- a/x-pack/legacy/plugins/infra/public/new_platform_plugin.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-import { CoreStart, CoreSetup, PluginInitializerContext } from 'kibana/public';
-import { InMemoryCache, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
-import ApolloClient from 'apollo-client';
-import { ApolloLink } from 'apollo-link';
-import { HttpLink } from 'apollo-link-http';
-import { withClientState } from 'apollo-link-state';
-import { startApp } from './apps/start_app';
-import { InfraFrontendLibs } from './lib/lib';
-import introspectionQueryResultData from './graphql/introspection.json';
-import { InfraKibanaObservableApiAdapter } from './lib/adapters/observable_api/kibana_observable_api';
-import { HomePublicPluginSetup } from '../../../../../src/plugins/home/public';
-import { featureCatalogueEntries } from './feature_catalogue_entry';
-
-type ClientPlugins = any;
-type LegacyDeps = any;
-interface InfraPluginSetupDependencies {
-  home: HomePublicPluginSetup;
-}
-
-export class Plugin {
-  constructor(context: PluginInitializerContext) {}
-
-  setup(core: CoreSetup, { home }: InfraPluginSetupDependencies) {
-    home.featureCatalogue.register(featureCatalogueEntries.metrics);
-    home.featureCatalogue.register(featureCatalogueEntries.logs);
-  }
-
-  start(core: CoreStart, plugins: ClientPlugins, __LEGACY: LegacyDeps) {
-    startApp(this.composeLibs(core, plugins, __LEGACY), core, plugins);
-  }
-
-  composeLibs(core: CoreStart, plugins: ClientPlugins, legacy: LegacyDeps) {
-    const cache = new InMemoryCache({
-      addTypename: false,
-      fragmentMatcher: new IntrospectionFragmentMatcher({
-        introspectionQueryResultData,
-      }),
-    });
-
-    const observableApi = new InfraKibanaObservableApiAdapter({
-      basePath: core.http.basePath.get(),
-    });
-
-    const graphQLOptions = {
-      cache,
-      link: ApolloLink.from([
-        withClientState({
-          cache,
-          resolvers: {},
-        }),
-        new HttpLink({
-          credentials: 'same-origin',
-          headers: {
-            'kbn-xsrf': true,
-          },
-          uri: `${core.http.basePath.get()}/api/infra/graphql`,
-        }),
-      ]),
-    };
-
-    const apolloClient = new ApolloClient(graphQLOptions);
-
-    const libs: InfraFrontendLibs = {
-      apolloClient,
-      observableApi,
-    };
-    return libs;
-  }
-}
diff --git a/x-pack/legacy/plugins/infra/public/routes.tsx b/x-pack/legacy/plugins/infra/public/routes.tsx
deleted file mode 100644
index fd69c6e842448..0000000000000
--- a/x-pack/legacy/plugins/infra/public/routes.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { History } from 'history';
-import React from 'react';
-import { Route, Router, Switch } from 'react-router-dom';
-
-import { NotFoundPage } from './pages/404';
-import { InfrastructurePage } from './pages/infrastructure';
-import { LinkToPage } from './pages/link_to';
-import { LogsPage } from './pages/logs';
-import { MetricDetail } from './pages/metrics';
-import { RedirectWithQueryParams } from './utils/redirect_with_query_params';
-import { useKibana } from '../../../../../src/plugins/kibana_react/public';
-
-interface RouterProps {
-  history: History;
-}
-
-export const PageRouter: React.FC<RouterProps> = ({ history }) => {
-  const uiCapabilities = useKibana().services.application?.capabilities;
-  return (
-    <Router history={history}>
-      <Switch>
-        {uiCapabilities?.infrastructure?.show && (
-          <RedirectWithQueryParams from="/" exact={true} to="/infrastructure/inventory" />
-        )}
-        {uiCapabilities?.infrastructure?.show && (
-          <RedirectWithQueryParams
-            from="/infrastructure"
-            exact={true}
-            to="/infrastructure/inventory"
-          />
-        )}
-        {uiCapabilities?.infrastructure?.show && (
-          <RedirectWithQueryParams
-            from="/infrastructure/snapshot"
-            exact={true}
-            to="/infrastructure/inventory"
-          />
-        )}
-        {uiCapabilities?.infrastructure?.show && (
-          <RedirectWithQueryParams from="/home" exact={true} to="/infrastructure/inventory" />
-        )}
-        {uiCapabilities?.infrastructure?.show && (
-          <Route path="/infrastructure/metrics/:type/:node" component={MetricDetail} />
-        )}
-        {uiCapabilities?.infrastructure?.show && (
-          <RedirectWithQueryParams from="/metrics" to="/infrastructure/metrics" />
-        )}
-        {uiCapabilities?.logs?.show && (
-          <RedirectWithQueryParams from="/logs" exact={true} to="/logs/stream" />
-        )}
-        {uiCapabilities?.logs?.show && <Route path="/logs" component={LogsPage} />}
-        {uiCapabilities?.infrastructure?.show && (
-          <Route path="/infrastructure" component={InfrastructurePage} />
-        )}
-        <Route path="/link-to" component={LinkToPage} />
-        <Route component={NotFoundPage} />
-      </Switch>
-    </Router>
-  );
-};
diff --git a/x-pack/legacy/plugins/infra/public/utils/fetch.ts b/x-pack/legacy/plugins/infra/public/utils/fetch.ts
deleted file mode 100644
index 50afc64bb5b78..0000000000000
--- a/x-pack/legacy/plugins/infra/public/utils/fetch.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import axios from 'axios';
-const FETCH_TIMEOUT = 30000;
-
-export const fetch = axios.create({
-  headers: {
-    Accept: 'application/json',
-    'Content-Type': 'application/json',
-    'kbn-xsrf': 'professionally-crafted-string-of-text',
-  },
-  timeout: FETCH_TIMEOUT,
-});
diff --git a/x-pack/legacy/plugins/infra/public/utils/use_kibana_injected_var.ts b/x-pack/legacy/plugins/infra/public/utils/use_kibana_injected_var.ts
deleted file mode 100644
index 4afcb4fd88430..0000000000000
--- a/x-pack/legacy/plugins/infra/public/utils/use_kibana_injected_var.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { npSetup } from 'ui/new_platform';
-
-/**
- * This hook provides access to the "injected variables" provided by the
- * platform. While it doesn't need to be a hook right now, it has been written
- * as one in order to keep the API stable despite the upcoming injection
- * through the context after the new platform migration.
- */
-export const useKibanaInjectedVar = (name: string, defaultValue?: unknown) => {
-  const injectedMetadata = npSetup.core.injectedMetadata;
-  return injectedMetadata.getInjectedVar(name, defaultValue);
-};
diff --git a/x-pack/legacy/plugins/infra/server/new_platform_index.ts b/x-pack/legacy/plugins/infra/server/new_platform_index.ts
deleted file mode 100644
index e59897a6b241d..0000000000000
--- a/x-pack/legacy/plugins/infra/server/new_platform_index.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { PluginInitializerContext } from 'src/core/server';
-import { InfraServerPlugin, InfraPluginSetup } from './new_platform_plugin';
-import { config, InfraConfig } from '../../../../plugins/infra/server';
-import { InfraServerPluginDeps } from './lib/adapters/framework';
-export { config, InfraConfig, InfraServerPluginDeps, InfraPluginSetup };
-
-export function plugin(context: PluginInitializerContext) {
-  return new InfraServerPlugin(context);
-}
diff --git a/x-pack/legacy/plugins/infra/server/new_platform_plugin.ts b/x-pack/legacy/plugins/infra/server/new_platform_plugin.ts
deleted file mode 100644
index d3c6f7a5f70a1..0000000000000
--- a/x-pack/legacy/plugins/infra/server/new_platform_plugin.ts
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-import { CoreSetup, PluginInitializerContext } from 'src/core/server';
-import { i18n } from '@kbn/i18n';
-import { Server } from 'hapi';
-import { InfraConfig } from '../../../../plugins/infra/server';
-import { initInfraServer } from './infra_server';
-import { InfraBackendLibs, InfraDomainLibs } from './lib/infra_types';
-import { FrameworkFieldsAdapter } from './lib/adapters/fields/framework_fields_adapter';
-import { KibanaFramework } from './lib/adapters/framework/kibana_framework_adapter';
-import { InfraKibanaLogEntriesAdapter } from './lib/adapters/log_entries/kibana_log_entries_adapter';
-import { KibanaMetricsAdapter } from './lib/adapters/metrics/kibana_metrics_adapter';
-import { InfraElasticsearchSourceStatusAdapter } from './lib/adapters/source_status';
-import { InfraFieldsDomain } from './lib/domains/fields_domain';
-import { InfraLogEntriesDomain } from './lib/domains/log_entries_domain';
-import { InfraMetricsDomain } from './lib/domains/metrics_domain';
-import { LogEntryCategoriesAnalysis, LogEntryRateAnalysis } from './lib/log_analysis';
-import { InfraSnapshot } from './lib/snapshot';
-import { InfraSourceStatus } from './lib/source_status';
-import { InfraSources } from './lib/sources';
-import { InfraServerPluginDeps } from './lib/adapters/framework';
-import { METRICS_FEATURE, LOGS_FEATURE } from './features';
-import { UsageCollector } from './usage/usage_collector';
-import { APP_ID } from '../index';
-import { InfraStaticSourceConfiguration } from './lib/sources/types';
-
-export interface KbnServer extends Server {
-  usage: any;
-}
-
-const logsSampleDataLinkLabel = i18n.translate('xpack.infra.sampleDataLinkLabel', {
-  defaultMessage: 'Logs',
-});
-
-export interface InfraPluginSetup {
-  defineInternalSourceConfiguration: (
-    sourceId: string,
-    sourceProperties: InfraStaticSourceConfiguration
-  ) => void;
-}
-
-const DEFAULT_CONFIG: InfraConfig = {
-  enabled: true,
-  query: {
-    partitionSize: 75,
-    partitionFactor: 1.2,
-  },
-};
-
-export class InfraServerPlugin {
-  public config: InfraConfig = DEFAULT_CONFIG;
-  public libs: InfraBackendLibs | undefined;
-
-  constructor(context: PluginInitializerContext) {
-    const config$ = context.config.create<InfraConfig>();
-    config$.subscribe(configValue => {
-      this.config = {
-        ...DEFAULT_CONFIG,
-        enabled: configValue.enabled,
-        query: {
-          ...DEFAULT_CONFIG.query,
-          ...configValue.query,
-        },
-      };
-    });
-  }
-
-  getLibs() {
-    if (!this.libs) {
-      throw new Error('libs not set up yet');
-    }
-    return this.libs;
-  }
-
-  setup(core: CoreSetup, plugins: InfraServerPluginDeps) {
-    const framework = new KibanaFramework(core, this.config, plugins);
-    const sources = new InfraSources({
-      config: this.config,
-    });
-    const sourceStatus = new InfraSourceStatus(
-      new InfraElasticsearchSourceStatusAdapter(framework),
-      {
-        sources,
-      }
-    );
-    const snapshot = new InfraSnapshot({ sources, framework });
-    const logEntryCategoriesAnalysis = new LogEntryCategoriesAnalysis({ framework });
-    const logEntryRateAnalysis = new LogEntryRateAnalysis({ framework });
-
-    // TODO: separate these out individually and do away with "domains" as a temporary group
-    const domainLibs: InfraDomainLibs = {
-      fields: new InfraFieldsDomain(new FrameworkFieldsAdapter(framework), {
-        sources,
-      }),
-      logEntries: new InfraLogEntriesDomain(new InfraKibanaLogEntriesAdapter(framework), {
-        sources,
-      }),
-      metrics: new InfraMetricsDomain(new KibanaMetricsAdapter(framework)),
-    };
-
-    this.libs = {
-      configuration: this.config,
-      framework,
-      logEntryCategoriesAnalysis,
-      logEntryRateAnalysis,
-      snapshot,
-      sources,
-      sourceStatus,
-      ...domainLibs,
-    };
-
-    plugins.features.registerFeature(METRICS_FEATURE);
-    plugins.features.registerFeature(LOGS_FEATURE);
-
-    plugins.home.sampleData.addAppLinksToSampleDataset('logs', [
-      {
-        path: `/app/${APP_ID}#/logs`,
-        label: logsSampleDataLinkLabel,
-        icon: 'logsApp',
-      },
-    ]);
-
-    initInfraServer(this.libs);
-
-    // Telemetry
-    UsageCollector.registerUsageCollector(plugins.usageCollection);
-
-    return {
-      defineInternalSourceConfiguration(sourceId, sourceProperties) {
-        sources.defineInternalSourceConfiguration(sourceId, sourceProperties);
-      },
-    } as InfraPluginSetup;
-  }
-}
diff --git a/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/types.ts b/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/types.ts
deleted file mode 100644
index f4c5e26c5c6d1..0000000000000
--- a/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/types.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import * as rt from 'io-ts';
-import {
-  metricsExplorerMetricRT,
-  metricsExplorerPageInfoRT,
-  metricsExplorerColumnRT,
-  metricsExplorerRowRT,
-  metricsExplorerSeriesRT,
-  metricsExplorerRequestBodyRT,
-  metricsExplorerResponseRT,
-  metricsExplorerAggregationRT,
-  metricsExplorerColumnTypeRT,
-} from '../../../common/http_api';
-
-export type MetricsExplorerAggregation = rt.TypeOf<typeof metricsExplorerAggregationRT>;
-
-export type MetricsExplorerColumnType = rt.TypeOf<typeof metricsExplorerColumnTypeRT>;
-
-export type MetricsExplorerMetric = rt.TypeOf<typeof metricsExplorerMetricRT>;
-
-export type MetricsExplorerPageInfo = rt.TypeOf<typeof metricsExplorerPageInfoRT>;
-
-export type MetricsExplorerColumn = rt.TypeOf<typeof metricsExplorerColumnRT>;
-
-export type MetricsExplorerRow = rt.TypeOf<typeof metricsExplorerRowRT>;
-
-export type MetricsExplorerSeries = rt.TypeOf<typeof metricsExplorerSeriesRT>;
-
-export type MetricsExplorerRequestBody = rt.TypeOf<typeof metricsExplorerRequestBodyRT>;
-
-export type MetricsExplorerResponse = rt.TypeOf<typeof metricsExplorerResponseRT>;
diff --git a/x-pack/legacy/plugins/infra/tsconfig.json b/x-pack/legacy/plugins/infra/tsconfig.json
deleted file mode 100644
index 0a26f297882e4..0000000000000
--- a/x-pack/legacy/plugins/infra/tsconfig.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-  "extends": "../../../tsconfig.json",
-}
\ No newline at end of file
diff --git a/x-pack/legacy/plugins/infra/yarn.lock b/x-pack/legacy/plugins/infra/yarn.lock
deleted file mode 120000
index 4b16253de2abe..0000000000000
--- a/x-pack/legacy/plugins/infra/yarn.lock
+++ /dev/null
@@ -1 +0,0 @@
-../../../../yarn.lock
\ No newline at end of file
diff --git a/x-pack/legacy/plugins/monitoring/public/components/logs/__snapshots__/logs.test.js.snap b/x-pack/legacy/plugins/monitoring/public/components/logs/__snapshots__/logs.test.js.snap
index de02fb10a5937..a8eebdaf6aa39 100644
--- a/x-pack/legacy/plugins/monitoring/public/components/logs/__snapshots__/logs.test.js.snap
+++ b/x-pack/legacy/plugins/monitoring/public/components/logs/__snapshots__/logs.test.js.snap
@@ -13,7 +13,7 @@ exports[`Logs should render a link to filter by cluster uuid 1`] = `
       values={
         Object {
           "link": <ForwardRef
-            href="/app/infra#/link-to/internal-stack-monitoring/logs?filter=elasticsearch.cluster.uuid:12345"
+            href="/app/logs/link-to/internal-stack-monitoring/logs?filter=elasticsearch.cluster.uuid:12345"
           >
             Logs
           </ForwardRef>,
@@ -37,7 +37,7 @@ exports[`Logs should render a link to filter by cluster uuid and index uuid 1`]
       values={
         Object {
           "link": <ForwardRef
-            href="/app/infra#/link-to/internal-stack-monitoring/logs?filter=elasticsearch.cluster.uuid:12345 and elasticsearch.index.name:6789"
+            href="/app/logs/link-to/internal-stack-monitoring/logs?filter=elasticsearch.cluster.uuid:12345 and elasticsearch.index.name:6789"
           >
             Logs
           </ForwardRef>,
@@ -61,7 +61,7 @@ exports[`Logs should render a link to filter by cluster uuid and node uuid 1`] =
       values={
         Object {
           "link": <ForwardRef
-            href="/app/infra#/link-to/internal-stack-monitoring/logs?filter=elasticsearch.cluster.uuid:12345 and elasticsearch.node.id:6789"
+            href="/app/logs/link-to/internal-stack-monitoring/logs?filter=elasticsearch.cluster.uuid:12345 and elasticsearch.node.id:6789"
           >
             Logs
           </ForwardRef>,
@@ -287,7 +287,7 @@ exports[`Logs should render normally 1`] = `
         values={
           Object {
             "link": <ForwardRef
-              href="/app/infra#/link-to/internal-stack-monitoring/logs"
+              href="/app/logs/link-to/internal-stack-monitoring/logs"
             >
               Logs
             </ForwardRef>,
diff --git a/x-pack/legacy/plugins/monitoring/public/components/logs/logs.js b/x-pack/legacy/plugins/monitoring/public/components/logs/logs.js
index 744ebb5a7ceb4..3590199048352 100644
--- a/x-pack/legacy/plugins/monitoring/public/components/logs/logs.js
+++ b/x-pack/legacy/plugins/monitoring/public/components/logs/logs.js
@@ -110,7 +110,7 @@ const clusterColumns = [
 ];
 
 function getLogsUiLink(clusterUuid, nodeId, indexUuid) {
-  const base = `${chrome.getBasePath()}/app/infra#/link-to/${INFRA_SOURCE_ID}/logs`;
+  const base = `${chrome.getBasePath()}/app/logs/link-to/${INFRA_SOURCE_ID}/logs`;
 
   const params = [];
   if (clusterUuid) {
diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/__tests__/__snapshots__/get_infra_href.test.ts.snap b/x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/__tests__/__snapshots__/get_infra_href.test.ts.snap
index 5094aab1226a1..e79eb50d384a8 100644
--- a/x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/__tests__/__snapshots__/get_infra_href.test.ts.snap
+++ b/x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/__tests__/__snapshots__/get_infra_href.test.ts.snap
@@ -1,19 +1,19 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`getInfraHref getInfraContainerHref creates a link for valid parameters 1`] = `"foo/app/infra#/link-to/container-detail/test-container-id"`;
+exports[`getInfraHref getInfraContainerHref creates a link for valid parameters 1`] = `"foo/app/metrics/link-to/container-detail/test-container-id"`;
 
-exports[`getInfraHref getInfraContainerHref does not specify a base path when none is available 1`] = `"/app/infra#/link-to/container-detail/test-container-id"`;
+exports[`getInfraHref getInfraContainerHref does not specify a base path when none is available 1`] = `"/app/metrics/link-to/container-detail/test-container-id"`;
 
-exports[`getInfraHref getInfraContainerHref returns the first item when multiple container ids are supplied 1`] = `"bar/app/infra#/link-to/container-detail/test-container-id"`;
+exports[`getInfraHref getInfraContainerHref returns the first item when multiple container ids are supplied 1`] = `"bar/app/metrics/link-to/container-detail/test-container-id"`;
 
-exports[`getInfraHref getInfraIpHref creates a link for valid parameters 1`] = `"bar/app/infra#/infrastructure/inventory?waffleFilter=(expression:'host.ip%20%3A%20151.101.202.217',kind:kuery)"`;
+exports[`getInfraHref getInfraIpHref creates a link for valid parameters 1`] = `"bar/app/metrics/inventory?waffleFilter=(expression:'host.ip%20%3A%20151.101.202.217',kind:kuery)"`;
 
-exports[`getInfraHref getInfraIpHref does not specify a base path when none is available 1`] = `"/app/infra#/infrastructure/inventory?waffleFilter=(expression:'host.ip%20%3A%20151.101.202.217',kind:kuery)"`;
+exports[`getInfraHref getInfraIpHref does not specify a base path when none is available 1`] = `"/app/metrics/inventory?waffleFilter=(expression:'host.ip%20%3A%20151.101.202.217',kind:kuery)"`;
 
-exports[`getInfraHref getInfraIpHref returns a url for ors between multiple ips 1`] = `"foo/app/infra#/infrastructure/inventory?waffleFilter=(expression:'host.ip%20%3A%20152.151.23.192%20or%20host.ip%20%3A%20151.101.202.217',kind:kuery)"`;
+exports[`getInfraHref getInfraIpHref returns a url for ors between multiple ips 1`] = `"foo/app/metrics/inventory?waffleFilter=(expression:'host.ip%20%3A%20152.151.23.192%20or%20host.ip%20%3A%20151.101.202.217',kind:kuery)"`;
 
-exports[`getInfraHref getInfraKubernetesHref creates a link for valid parameters 1`] = `"foo/app/infra#/link-to/pod-detail/test-pod-uid"`;
+exports[`getInfraHref getInfraKubernetesHref creates a link for valid parameters 1`] = `"foo/app/metrics/link-to/pod-detail/test-pod-uid"`;
 
-exports[`getInfraHref getInfraKubernetesHref does not specify a base path when none is available 1`] = `"/app/infra#/link-to/pod-detail/test-pod-uid"`;
+exports[`getInfraHref getInfraKubernetesHref does not specify a base path when none is available 1`] = `"/app/metrics/link-to/pod-detail/test-pod-uid"`;
 
-exports[`getInfraHref getInfraKubernetesHref selects the first pod uid when there are multiple 1`] = `"/app/infra#/link-to/pod-detail/test-pod-uid"`;
+exports[`getInfraHref getInfraKubernetesHref selects the first pod uid when there are multiple 1`] = `"/app/metrics/link-to/pod-detail/test-pod-uid"`;
diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/__tests__/__snapshots__/get_logging_href.test.ts.snap b/x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/__tests__/__snapshots__/get_logging_href.test.ts.snap
index 67402d16d9a27..cfac6ce133c8a 100644
--- a/x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/__tests__/__snapshots__/get_logging_href.test.ts.snap
+++ b/x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/__tests__/__snapshots__/get_logging_href.test.ts.snap
@@ -1,13 +1,13 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`getLoggingHref creates a container href with base path when present 1`] = `"bar/app/infra#/logs?logFilter=(expression:'container.id%20:%20test-container-id',kind:kuery)"`;
+exports[`getLoggingHref creates a container href with base path when present 1`] = `"bar/app/logs?logFilter=(expression:'container.id%20:%20test-container-id',kind:kuery)"`;
 
-exports[`getLoggingHref creates a container href without a base path if it's an empty string 1`] = `"/app/infra#/logs?logFilter=(expression:'container.id%20:%20test-container-id',kind:kuery)"`;
+exports[`getLoggingHref creates a container href without a base path if it's an empty string 1`] = `"/app/logs?logFilter=(expression:'container.id%20:%20test-container-id',kind:kuery)"`;
 
-exports[`getLoggingHref creates a pod href with base path when present 1`] = `"bar/app/infra#/logs?logFilter=(expression:'pod.uid%20:%20test-pod-id',kind:kuery)"`;
+exports[`getLoggingHref creates a pod href with base path when present 1`] = `"bar/app/logs?logFilter=(expression:'pod.uid%20:%20test-pod-id',kind:kuery)"`;
 
-exports[`getLoggingHref creates a pod href without a base path when it's an empty string 1`] = `"/app/infra#/logs?logFilter=(expression:'pod.uid%20:%20test-pod-id',kind:kuery)"`;
+exports[`getLoggingHref creates a pod href without a base path when it's an empty string 1`] = `"/app/logs?logFilter=(expression:'pod.uid%20:%20test-pod-id',kind:kuery)"`;
 
-exports[`getLoggingHref creates an ip href with base path when present 1`] = `"bar/app/infra#/logs?logFilter=(expression:'pod.uid%20:%20test-pod-id',kind:kuery)"`;
+exports[`getLoggingHref creates an ip href with base path when present 1`] = `"bar/app/logs?logFilter=(expression:'pod.uid%20:%20test-pod-id',kind:kuery)"`;
 
-exports[`getLoggingHref creates an ip href without a base path when it's an empty string 1`] = `"/app/infra#/logs?logFilter=(expression:'host.ip%20%3A%20151.101.202.217',kind:kuery)"`;
+exports[`getLoggingHref creates an ip href without a base path when it's an empty string 1`] = `"/app/logs?logFilter=(expression:'host.ip%20%3A%20151.101.202.217',kind:kuery)"`;
diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/get_infra_href.ts b/x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/get_infra_href.ts
index 12b51bbad0074..73065be395c76 100644
--- a/x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/get_infra_href.ts
+++ b/x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/get_infra_href.ts
@@ -17,7 +17,10 @@ export const getInfraContainerHref = (
       return undefined;
     }
     const ret = !Array.isArray(value) ? value : value[0];
-    return addBasePath(basePath, `/app/infra#/link-to/container-detail/${encodeURIComponent(ret)}`);
+    return addBasePath(
+      basePath,
+      `/app/metrics/link-to/container-detail/${encodeURIComponent(ret)}`
+    );
   };
   return buildHref(summary.state.checks || [], 'container.id', getHref);
 };
@@ -31,7 +34,7 @@ export const getInfraKubernetesHref = (
       return undefined;
     }
     const ret = !Array.isArray(value) ? value : value[0];
-    return addBasePath(basePath, `/app/infra#/link-to/pod-detail/${encodeURIComponent(ret)}`);
+    return addBasePath(basePath, `/app/metrics/link-to/pod-detail/${encodeURIComponent(ret)}`);
   };
 
   return buildHref(summary.state.checks || [], 'kubernetes.pod.uid', getHref);
@@ -46,7 +49,7 @@ export const getInfraIpHref = (summary: MonitorSummary, basePath: string) => {
       const expression = encodeURIComponent(`host.ip : ${value}`);
       return addBasePath(
         basePath,
-        `/app/infra#/infrastructure/inventory?waffleFilter=(expression:'${expression}',kind:kuery)`
+        `/app/metrics/inventory?waffleFilter=(expression:'${expression}',kind:kuery)`
       );
     }
     const ips = value.reduce(
@@ -57,9 +60,7 @@ export const getInfraIpHref = (summary: MonitorSummary, basePath: string) => {
       ? undefined
       : addBasePath(
           basePath,
-          `/app/infra#/infrastructure/inventory?waffleFilter=(expression:'${encodeURIComponent(
-            ips
-          )}',kind:kuery)`
+          `/app/metrics/inventory?waffleFilter=(expression:'${encodeURIComponent(ips)}',kind:kuery)`
         );
   };
   return buildHref(summary.state.checks || [], 'monitor.ip', getHref);
diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/get_logging_href.ts b/x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/get_logging_href.ts
index b2235231028fc..b97b5a34855fb 100644
--- a/x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/get_logging_href.ts
+++ b/x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/get_logging_href.ts
@@ -19,7 +19,7 @@ export const getLoggingContainerHref = (
     const ret = !Array.isArray(value) ? value : value[0];
     return addBasePath(
       basePath,
-      `/app/infra#/logs?logFilter=${encodeURI(`(expression:'container.id : ${ret}',kind:kuery)`)}`
+      `/app/logs?logFilter=${encodeURI(`(expression:'container.id : ${ret}',kind:kuery)`)}`
     );
   };
   return buildHref(summary.state.checks || [], 'container.id', getHref);
@@ -33,7 +33,7 @@ export const getLoggingKubernetesHref = (summary: MonitorSummary, basePath: stri
     const ret = !Array.isArray(value) ? value : value[0];
     return addBasePath(
       basePath,
-      `/app/infra#/logs?logFilter=${encodeURI(`(expression:'pod.uid : ${ret}',kind:kuery)`)}`
+      `/app/logs?logFilter=${encodeURI(`(expression:'pod.uid : ${ret}',kind:kuery)`)}`
     );
   };
   return buildHref(summary.state.checks || [], 'kubernetes.pod.uid', getHref);
@@ -47,9 +47,7 @@ export const getLoggingIpHref = (summary: MonitorSummary, basePath: string) => {
     const ret = !Array.isArray(value) ? value : value[0];
     return addBasePath(
       basePath,
-      `/app/infra#/logs?logFilter=(expression:'${encodeURIComponent(
-        `host.ip : ${ret}`
-      )}',kind:kuery)`
+      `/app/logs?logFilter=(expression:'${encodeURIComponent(`host.ip : ${ret}`)}',kind:kuery)`
     );
   };
   return buildHref(summary.state.checks || [], 'monitor.ip', getHref);
diff --git a/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx b/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx
index 1d45c7b7ab99b..380cc041ae87e 100644
--- a/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx
+++ b/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx
@@ -10,7 +10,7 @@ import { useParams } from 'react-router-dom';
 import { MonitorCharts, PingList } from '../components/functional';
 import { UptimeRefreshContext, UptimeThemeContext } from '../contexts';
 import { useUptimeTelemetry, useUrlParams, UptimePage } from '../hooks';
-import { useTrackPageview } from '../../../infra/public';
+import { useTrackPageview } from '../../../../../plugins/observability/public';
 import { MonitorStatusDetails } from '../components/connected';
 
 export const MonitorPage = () => {
diff --git a/x-pack/legacy/plugins/uptime/public/pages/overview.tsx b/x-pack/legacy/plugins/uptime/public/pages/overview.tsx
index 5360d66f87e99..cf3631eda042a 100644
--- a/x-pack/legacy/plugins/uptime/public/pages/overview.tsx
+++ b/x-pack/legacy/plugins/uptime/public/pages/overview.tsx
@@ -15,7 +15,7 @@ import {
 } from '../components/functional';
 import { useUrlParams, useUptimeTelemetry, UptimePage } from '../hooks';
 import { stringifyUrlParams } from '../lib/helper/stringify_url_params';
-import { useTrackPageview } from '../../../infra/public';
+import { useTrackPageview } from '../../../../../plugins/observability/public';
 import { DataPublicPluginSetup, IIndexPattern } from '../../../../../../src/plugins/data/public';
 import { UptimeThemeContext } from '../contexts';
 import { FilterGroup, KueryBar } from '../components/connected';
diff --git a/x-pack/legacy/plugins/infra/README.md b/x-pack/plugins/infra/README.md
similarity index 100%
rename from x-pack/legacy/plugins/infra/README.md
rename to x-pack/plugins/infra/README.md
diff --git a/x-pack/legacy/plugins/infra/common/color_palette.test.ts b/x-pack/plugins/infra/common/color_palette.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/color_palette.test.ts
rename to x-pack/plugins/infra/common/color_palette.test.ts
diff --git a/x-pack/legacy/plugins/infra/common/color_palette.ts b/x-pack/plugins/infra/common/color_palette.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/color_palette.ts
rename to x-pack/plugins/infra/common/color_palette.ts
diff --git a/x-pack/legacy/plugins/infra/common/ecs_allowed_list.test.ts b/x-pack/plugins/infra/common/ecs_allowed_list.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/ecs_allowed_list.test.ts
rename to x-pack/plugins/infra/common/ecs_allowed_list.test.ts
diff --git a/x-pack/legacy/plugins/infra/common/ecs_allowed_list.ts b/x-pack/plugins/infra/common/ecs_allowed_list.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/ecs_allowed_list.ts
rename to x-pack/plugins/infra/common/ecs_allowed_list.ts
diff --git a/x-pack/legacy/plugins/infra/common/errors/index.ts b/x-pack/plugins/infra/common/errors/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/errors/index.ts
rename to x-pack/plugins/infra/common/errors/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/errors/metrics.ts b/x-pack/plugins/infra/common/errors/metrics.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/errors/metrics.ts
rename to x-pack/plugins/infra/common/errors/metrics.ts
diff --git a/x-pack/legacy/plugins/infra/common/graphql/root/index.ts b/x-pack/plugins/infra/common/graphql/root/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/graphql/root/index.ts
rename to x-pack/plugins/infra/common/graphql/root/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/graphql/root/schema.gql.ts b/x-pack/plugins/infra/common/graphql/root/schema.gql.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/graphql/root/schema.gql.ts
rename to x-pack/plugins/infra/common/graphql/root/schema.gql.ts
diff --git a/x-pack/legacy/plugins/infra/common/graphql/shared/fragments.gql_query.ts b/x-pack/plugins/infra/common/graphql/shared/fragments.gql_query.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/graphql/shared/fragments.gql_query.ts
rename to x-pack/plugins/infra/common/graphql/shared/fragments.gql_query.ts
diff --git a/x-pack/legacy/plugins/infra/common/graphql/shared/index.ts b/x-pack/plugins/infra/common/graphql/shared/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/graphql/shared/index.ts
rename to x-pack/plugins/infra/common/graphql/shared/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/graphql/shared/schema.gql.ts b/x-pack/plugins/infra/common/graphql/shared/schema.gql.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/graphql/shared/schema.gql.ts
rename to x-pack/plugins/infra/common/graphql/shared/schema.gql.ts
diff --git a/x-pack/legacy/plugins/infra/common/graphql/types.ts b/x-pack/plugins/infra/common/graphql/types.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/graphql/types.ts
rename to x-pack/plugins/infra/common/graphql/types.ts
diff --git a/x-pack/legacy/plugins/infra/common/http_api/index.ts b/x-pack/plugins/infra/common/http_api/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/http_api/index.ts
rename to x-pack/plugins/infra/common/http_api/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/http_api/inventory_meta_api.ts b/x-pack/plugins/infra/common/http_api/inventory_meta_api.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/http_api/inventory_meta_api.ts
rename to x-pack/plugins/infra/common/http_api/inventory_meta_api.ts
diff --git a/x-pack/plugins/infra/common/http_api/ip_to_hostname/index.ts b/x-pack/plugins/infra/common/http_api/ip_to_hostname/index.ts
new file mode 100644
index 0000000000000..4ebd5fdda6f99
--- /dev/null
+++ b/x-pack/plugins/infra/common/http_api/ip_to_hostname/index.ts
@@ -0,0 +1,9 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export interface IpToHostResponse {
+  host: string;
+}
diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_analysis/index.ts b/x-pack/plugins/infra/common/http_api/log_analysis/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/http_api/log_analysis/index.ts
rename to x-pack/plugins/infra/common/http_api/log_analysis/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/index.ts b/x-pack/plugins/infra/common/http_api/log_analysis/results/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/index.ts
rename to x-pack/plugins/infra/common/http_api/log_analysis/results/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_categories.ts b/x-pack/plugins/infra/common/http_api/log_analysis/results/log_entry_categories.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_categories.ts
rename to x-pack/plugins/infra/common/http_api/log_analysis/results/log_entry_categories.ts
diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_category_datasets.ts b/x-pack/plugins/infra/common/http_api/log_analysis/results/log_entry_category_datasets.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_category_datasets.ts
rename to x-pack/plugins/infra/common/http_api/log_analysis/results/log_entry_category_datasets.ts
diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts b/x-pack/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts
rename to x-pack/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts
diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_analysis/validation/index.ts b/x-pack/plugins/infra/common/http_api/log_analysis/validation/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/http_api/log_analysis/validation/index.ts
rename to x-pack/plugins/infra/common/http_api/log_analysis/validation/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_analysis/validation/log_entry_rate_indices.ts b/x-pack/plugins/infra/common/http_api/log_analysis/validation/log_entry_rate_indices.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/http_api/log_analysis/validation/log_entry_rate_indices.ts
rename to x-pack/plugins/infra/common/http_api/log_analysis/validation/log_entry_rate_indices.ts
diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_entries/common.ts b/x-pack/plugins/infra/common/http_api/log_entries/common.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/http_api/log_entries/common.ts
rename to x-pack/plugins/infra/common/http_api/log_entries/common.ts
diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_entries/entries.ts b/x-pack/plugins/infra/common/http_api/log_entries/entries.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/http_api/log_entries/entries.ts
rename to x-pack/plugins/infra/common/http_api/log_entries/entries.ts
diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_entries/highlights.ts b/x-pack/plugins/infra/common/http_api/log_entries/highlights.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/http_api/log_entries/highlights.ts
rename to x-pack/plugins/infra/common/http_api/log_entries/highlights.ts
diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_entries/index.ts b/x-pack/plugins/infra/common/http_api/log_entries/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/http_api/log_entries/index.ts
rename to x-pack/plugins/infra/common/http_api/log_entries/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_entries/item.ts b/x-pack/plugins/infra/common/http_api/log_entries/item.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/http_api/log_entries/item.ts
rename to x-pack/plugins/infra/common/http_api/log_entries/item.ts
diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_entries/summary.ts b/x-pack/plugins/infra/common/http_api/log_entries/summary.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/http_api/log_entries/summary.ts
rename to x-pack/plugins/infra/common/http_api/log_entries/summary.ts
diff --git a/x-pack/legacy/plugins/infra/common/http_api/log_entries/summary_highlights.ts b/x-pack/plugins/infra/common/http_api/log_entries/summary_highlights.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/http_api/log_entries/summary_highlights.ts
rename to x-pack/plugins/infra/common/http_api/log_entries/summary_highlights.ts
diff --git a/x-pack/legacy/plugins/infra/common/http_api/metadata_api.ts b/x-pack/plugins/infra/common/http_api/metadata_api.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/http_api/metadata_api.ts
rename to x-pack/plugins/infra/common/http_api/metadata_api.ts
diff --git a/x-pack/legacy/plugins/infra/common/http_api/metrics_explorer/index.ts b/x-pack/plugins/infra/common/http_api/metrics_explorer/index.ts
similarity index 78%
rename from x-pack/legacy/plugins/infra/common/http_api/metrics_explorer/index.ts
rename to x-pack/plugins/infra/common/http_api/metrics_explorer/index.ts
index c10f86c40ad46..93655f931f45d 100644
--- a/x-pack/legacy/plugins/infra/common/http_api/metrics_explorer/index.ts
+++ b/x-pack/plugins/infra/common/http_api/metrics_explorer/index.ts
@@ -94,3 +94,21 @@ export const metricsExplorerResponseRT = rt.type({
   series: rt.array(metricsExplorerSeriesRT),
   pageInfo: metricsExplorerPageInfoRT,
 });
+
+export type MetricsExplorerAggregation = rt.TypeOf<typeof metricsExplorerAggregationRT>;
+
+export type MetricsExplorerColumnType = rt.TypeOf<typeof metricsExplorerColumnTypeRT>;
+
+export type MetricsExplorerMetric = rt.TypeOf<typeof metricsExplorerMetricRT>;
+
+export type MetricsExplorerPageInfo = rt.TypeOf<typeof metricsExplorerPageInfoRT>;
+
+export type MetricsExplorerColumn = rt.TypeOf<typeof metricsExplorerColumnRT>;
+
+export type MetricsExplorerRow = rt.TypeOf<typeof metricsExplorerRowRT>;
+
+export type MetricsExplorerSeries = rt.TypeOf<typeof metricsExplorerSeriesRT>;
+
+export type MetricsExplorerRequestBody = rt.TypeOf<typeof metricsExplorerRequestBodyRT>;
+
+export type MetricsExplorerResponse = rt.TypeOf<typeof metricsExplorerResponseRT>;
diff --git a/x-pack/legacy/plugins/infra/common/http_api/node_details_api.ts b/x-pack/plugins/infra/common/http_api/node_details_api.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/http_api/node_details_api.ts
rename to x-pack/plugins/infra/common/http_api/node_details_api.ts
diff --git a/x-pack/legacy/plugins/infra/common/http_api/shared/errors.ts b/x-pack/plugins/infra/common/http_api/shared/errors.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/http_api/shared/errors.ts
rename to x-pack/plugins/infra/common/http_api/shared/errors.ts
diff --git a/x-pack/legacy/plugins/infra/common/http_api/shared/index.ts b/x-pack/plugins/infra/common/http_api/shared/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/http_api/shared/index.ts
rename to x-pack/plugins/infra/common/http_api/shared/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/http_api/shared/metric_statistics.ts b/x-pack/plugins/infra/common/http_api/shared/metric_statistics.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/http_api/shared/metric_statistics.ts
rename to x-pack/plugins/infra/common/http_api/shared/metric_statistics.ts
diff --git a/x-pack/legacy/plugins/infra/common/http_api/shared/time_range.ts b/x-pack/plugins/infra/common/http_api/shared/time_range.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/http_api/shared/time_range.ts
rename to x-pack/plugins/infra/common/http_api/shared/time_range.ts
diff --git a/x-pack/legacy/plugins/infra/common/http_api/shared/timing.ts b/x-pack/plugins/infra/common/http_api/shared/timing.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/http_api/shared/timing.ts
rename to x-pack/plugins/infra/common/http_api/shared/timing.ts
diff --git a/x-pack/legacy/plugins/infra/common/http_api/snapshot_api.ts b/x-pack/plugins/infra/common/http_api/snapshot_api.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/http_api/snapshot_api.ts
rename to x-pack/plugins/infra/common/http_api/snapshot_api.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_ec2/index.ts b/x-pack/plugins/infra/common/inventory_models/aws_ec2/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_ec2/index.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_ec2/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_ec2/layout.tsx b/x-pack/plugins/infra/common/inventory_models/aws_ec2/layout.tsx
similarity index 89%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_ec2/layout.tsx
rename to x-pack/plugins/infra/common/inventory_models/aws_ec2/layout.tsx
index a3074b78f9f3b..c8e0680287526 100644
--- a/x-pack/legacy/plugins/infra/common/inventory_models/aws_ec2/layout.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/aws_ec2/layout.tsx
@@ -5,12 +5,19 @@
  */
 import React from 'react';
 import { i18n } from '@kbn/i18n';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { LayoutPropsWithTheme } from '../../../public/pages/metrics/types';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { Section } from '../../../public/pages/metrics/components/section';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { SubSection } from '../../../public/pages/metrics/components/sub_section';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { LayoutContent } from '../../../public/pages/metrics/components/layout_content';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ChartSectionVis } from '../../../public/pages/metrics/components/chart_section_vis';
-import { withTheme } from '../../../../../common/eui_styled_components';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { withTheme } from '../../../../observability/public';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { MetadataDetails } from '../../../public/pages/metrics/components/metadata_details';
 
 export const Layout = withTheme(({ metrics, theme }: LayoutPropsWithTheme) => (
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_ec2/metrics/index.ts b/x-pack/plugins/infra/common/inventory_models/aws_ec2/metrics/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_ec2/metrics/index.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_ec2/metrics/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_ec2/metrics/snapshot/cpu.ts b/x-pack/plugins/infra/common/inventory_models/aws_ec2/metrics/snapshot/cpu.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_ec2/metrics/snapshot/cpu.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_ec2/metrics/snapshot/cpu.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_ec2/metrics/snapshot/disk_io_read_bytes.ts b/x-pack/plugins/infra/common/inventory_models/aws_ec2/metrics/snapshot/disk_io_read_bytes.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_ec2/metrics/snapshot/disk_io_read_bytes.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_ec2/metrics/snapshot/disk_io_read_bytes.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_ec2/metrics/snapshot/disk_io_write_bytes.ts b/x-pack/plugins/infra/common/inventory_models/aws_ec2/metrics/snapshot/disk_io_write_bytes.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_ec2/metrics/snapshot/disk_io_write_bytes.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_ec2/metrics/snapshot/disk_io_write_bytes.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_ec2/metrics/snapshot/rx.ts b/x-pack/plugins/infra/common/inventory_models/aws_ec2/metrics/snapshot/rx.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_ec2/metrics/snapshot/rx.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_ec2/metrics/snapshot/rx.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_ec2/metrics/snapshot/tx.ts b/x-pack/plugins/infra/common/inventory_models/aws_ec2/metrics/snapshot/tx.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_ec2/metrics/snapshot/tx.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_ec2/metrics/snapshot/tx.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_ec2/metrics/tsvb/aws_ec2_cpu_utilization.ts b/x-pack/plugins/infra/common/inventory_models/aws_ec2/metrics/tsvb/aws_ec2_cpu_utilization.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_ec2/metrics/tsvb/aws_ec2_cpu_utilization.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_ec2/metrics/tsvb/aws_ec2_cpu_utilization.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_ec2/metrics/tsvb/aws_ec2_diskio_bytes.ts b/x-pack/plugins/infra/common/inventory_models/aws_ec2/metrics/tsvb/aws_ec2_diskio_bytes.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_ec2/metrics/tsvb/aws_ec2_diskio_bytes.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_ec2/metrics/tsvb/aws_ec2_diskio_bytes.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_ec2/metrics/tsvb/aws_ec2_network_traffic.ts b/x-pack/plugins/infra/common/inventory_models/aws_ec2/metrics/tsvb/aws_ec2_network_traffic.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_ec2/metrics/tsvb/aws_ec2_network_traffic.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_ec2/metrics/tsvb/aws_ec2_network_traffic.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_ec2/toolbar_items.tsx b/x-pack/plugins/infra/common/inventory_models/aws_ec2/toolbar_items.tsx
similarity index 95%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_ec2/toolbar_items.tsx
rename to x-pack/plugins/infra/common/inventory_models/aws_ec2/toolbar_items.tsx
index 4b16728a52bec..683851fec4d77 100644
--- a/x-pack/legacy/plugins/infra/common/inventory_models/aws_ec2/toolbar_items.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/aws_ec2/toolbar_items.tsx
@@ -5,6 +5,7 @@
  */
 
 import React from 'react';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ToolbarProps } from '../../../public/components/inventory/toolbars/toolbar';
 import { MetricsAndGroupByToolbarItems } from '../shared/compontents/metrics_and_groupby_toolbar_items';
 import { CloudToolbarItems } from '../shared/compontents/cloud_toolbar_items';
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/index.ts b/x-pack/plugins/infra/common/inventory_models/aws_rds/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/index.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_rds/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/layout.tsx b/x-pack/plugins/infra/common/inventory_models/aws_rds/layout.tsx
similarity index 93%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/layout.tsx
rename to x-pack/plugins/infra/common/inventory_models/aws_rds/layout.tsx
index debb569fcd5bb..4bc4562ab2760 100644
--- a/x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/layout.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/aws_rds/layout.tsx
@@ -5,11 +5,17 @@
  */
 import React from 'react';
 import { i18n } from '@kbn/i18n';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { LayoutPropsWithTheme } from '../../../public/pages/metrics/types';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { Section } from '../../../public/pages/metrics/components/section';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { SubSection } from '../../../public/pages/metrics/components/sub_section';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ChartSectionVis } from '../../../public/pages/metrics/components/chart_section_vis';
-import { withTheme } from '../../../../../common/eui_styled_components';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { withTheme } from '../../../../observability/public';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { LayoutContent } from '../../../public/pages/metrics/components/layout_content';
 
 export const Layout = withTheme(({ metrics, theme }: LayoutPropsWithTheme) => (
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/metrics/index.ts b/x-pack/plugins/infra/common/inventory_models/aws_rds/metrics/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/metrics/index.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_rds/metrics/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/metrics/snapshot/cpu.ts b/x-pack/plugins/infra/common/inventory_models/aws_rds/metrics/snapshot/cpu.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/metrics/snapshot/cpu.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_rds/metrics/snapshot/cpu.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/metrics/snapshot/rds_active_transactions.ts b/x-pack/plugins/infra/common/inventory_models/aws_rds/metrics/snapshot/rds_active_transactions.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/metrics/snapshot/rds_active_transactions.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_rds/metrics/snapshot/rds_active_transactions.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/metrics/snapshot/rds_connections.ts b/x-pack/plugins/infra/common/inventory_models/aws_rds/metrics/snapshot/rds_connections.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/metrics/snapshot/rds_connections.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_rds/metrics/snapshot/rds_connections.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/metrics/snapshot/rds_latency.ts b/x-pack/plugins/infra/common/inventory_models/aws_rds/metrics/snapshot/rds_latency.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/metrics/snapshot/rds_latency.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_rds/metrics/snapshot/rds_latency.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/metrics/snapshot/rds_queries_executed.ts b/x-pack/plugins/infra/common/inventory_models/aws_rds/metrics/snapshot/rds_queries_executed.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/metrics/snapshot/rds_queries_executed.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_rds/metrics/snapshot/rds_queries_executed.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/metrics/tsvb/aws_rds_active_transactions.ts b/x-pack/plugins/infra/common/inventory_models/aws_rds/metrics/tsvb/aws_rds_active_transactions.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/metrics/tsvb/aws_rds_active_transactions.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_rds/metrics/tsvb/aws_rds_active_transactions.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/metrics/tsvb/aws_rds_connections.ts b/x-pack/plugins/infra/common/inventory_models/aws_rds/metrics/tsvb/aws_rds_connections.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/metrics/tsvb/aws_rds_connections.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_rds/metrics/tsvb/aws_rds_connections.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/metrics/tsvb/aws_rds_cpu_total.ts b/x-pack/plugins/infra/common/inventory_models/aws_rds/metrics/tsvb/aws_rds_cpu_total.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/metrics/tsvb/aws_rds_cpu_total.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_rds/metrics/tsvb/aws_rds_cpu_total.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/metrics/tsvb/aws_rds_latency.ts b/x-pack/plugins/infra/common/inventory_models/aws_rds/metrics/tsvb/aws_rds_latency.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/metrics/tsvb/aws_rds_latency.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_rds/metrics/tsvb/aws_rds_latency.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/metrics/tsvb/aws_rds_queries_executed.ts b/x-pack/plugins/infra/common/inventory_models/aws_rds/metrics/tsvb/aws_rds_queries_executed.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/metrics/tsvb/aws_rds_queries_executed.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_rds/metrics/tsvb/aws_rds_queries_executed.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/toolbar_items.tsx b/x-pack/plugins/infra/common/inventory_models/aws_rds/toolbar_items.tsx
similarity index 95%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/toolbar_items.tsx
rename to x-pack/plugins/infra/common/inventory_models/aws_rds/toolbar_items.tsx
index e4654825a8de1..24f05fd91589c 100644
--- a/x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/toolbar_items.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/aws_rds/toolbar_items.tsx
@@ -5,8 +5,8 @@
  */
 
 import React from 'react';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ToolbarProps } from '../../../public/components/inventory/toolbars/toolbar';
-
 import { MetricsAndGroupByToolbarItems } from '../shared/compontents/metrics_and_groupby_toolbar_items';
 import { CloudToolbarItems } from '../shared/compontents/cloud_toolbar_items';
 import { SnapshotMetricType } from '../types';
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/index.ts b/x-pack/plugins/infra/common/inventory_models/aws_s3/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/index.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_s3/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/layout.tsx b/x-pack/plugins/infra/common/inventory_models/aws_s3/layout.tsx
similarity index 91%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/layout.tsx
rename to x-pack/plugins/infra/common/inventory_models/aws_s3/layout.tsx
index 955960f5baeda..c40c8e7b8c6d3 100644
--- a/x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/layout.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/aws_s3/layout.tsx
@@ -5,11 +5,17 @@
  */
 import React from 'react';
 import { i18n } from '@kbn/i18n';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { LayoutPropsWithTheme } from '../../../public/pages/metrics/types';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { Section } from '../../../public/pages/metrics/components/section';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { SubSection } from '../../../public/pages/metrics/components/sub_section';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ChartSectionVis } from '../../../public/pages/metrics/components/chart_section_vis';
-import { withTheme } from '../../../../../common/eui_styled_components';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { withTheme } from '../../../../observability/public';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { LayoutContent } from '../../../public/pages/metrics/components/layout_content';
 
 export const Layout = withTheme(({ metrics, theme }: LayoutPropsWithTheme) => (
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/metrics/index.ts b/x-pack/plugins/infra/common/inventory_models/aws_s3/metrics/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/metrics/index.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_s3/metrics/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/metrics/snapshot/s3_bucket_size.ts b/x-pack/plugins/infra/common/inventory_models/aws_s3/metrics/snapshot/s3_bucket_size.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/metrics/snapshot/s3_bucket_size.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_s3/metrics/snapshot/s3_bucket_size.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/metrics/snapshot/s3_download_bytes.ts b/x-pack/plugins/infra/common/inventory_models/aws_s3/metrics/snapshot/s3_download_bytes.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/metrics/snapshot/s3_download_bytes.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_s3/metrics/snapshot/s3_download_bytes.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/metrics/snapshot/s3_number_of_objects.ts b/x-pack/plugins/infra/common/inventory_models/aws_s3/metrics/snapshot/s3_number_of_objects.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/metrics/snapshot/s3_number_of_objects.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_s3/metrics/snapshot/s3_number_of_objects.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/metrics/snapshot/s3_total_requests.ts b/x-pack/plugins/infra/common/inventory_models/aws_s3/metrics/snapshot/s3_total_requests.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/metrics/snapshot/s3_total_requests.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_s3/metrics/snapshot/s3_total_requests.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/metrics/snapshot/s3_upload_bytes.ts b/x-pack/plugins/infra/common/inventory_models/aws_s3/metrics/snapshot/s3_upload_bytes.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/metrics/snapshot/s3_upload_bytes.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_s3/metrics/snapshot/s3_upload_bytes.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/metrics/tsvb/aws_s3_bucket_size.ts b/x-pack/plugins/infra/common/inventory_models/aws_s3/metrics/tsvb/aws_s3_bucket_size.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/metrics/tsvb/aws_s3_bucket_size.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_s3/metrics/tsvb/aws_s3_bucket_size.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/metrics/tsvb/aws_s3_download_bytes.ts b/x-pack/plugins/infra/common/inventory_models/aws_s3/metrics/tsvb/aws_s3_download_bytes.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/metrics/tsvb/aws_s3_download_bytes.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_s3/metrics/tsvb/aws_s3_download_bytes.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/metrics/tsvb/aws_s3_number_of_objects.ts b/x-pack/plugins/infra/common/inventory_models/aws_s3/metrics/tsvb/aws_s3_number_of_objects.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/metrics/tsvb/aws_s3_number_of_objects.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_s3/metrics/tsvb/aws_s3_number_of_objects.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/metrics/tsvb/aws_s3_total_requests.ts b/x-pack/plugins/infra/common/inventory_models/aws_s3/metrics/tsvb/aws_s3_total_requests.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/metrics/tsvb/aws_s3_total_requests.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_s3/metrics/tsvb/aws_s3_total_requests.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/metrics/tsvb/aws_s3_upload_bytes.ts b/x-pack/plugins/infra/common/inventory_models/aws_s3/metrics/tsvb/aws_s3_upload_bytes.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/metrics/tsvb/aws_s3_upload_bytes.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_s3/metrics/tsvb/aws_s3_upload_bytes.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/toolbar_items.tsx b/x-pack/plugins/infra/common/inventory_models/aws_s3/toolbar_items.tsx
similarity index 94%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/toolbar_items.tsx
rename to x-pack/plugins/infra/common/inventory_models/aws_s3/toolbar_items.tsx
index d1aa48260db47..54c3196ae2833 100644
--- a/x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/toolbar_items.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/aws_s3/toolbar_items.tsx
@@ -5,6 +5,7 @@
  */
 
 import React from 'react';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ToolbarProps } from '../../../public/components/inventory/toolbars/toolbar';
 import { MetricsAndGroupByToolbarItems } from '../shared/compontents/metrics_and_groupby_toolbar_items';
 import { CloudToolbarItems } from '../shared/compontents/cloud_toolbar_items';
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/index.ts b/x-pack/plugins/infra/common/inventory_models/aws_sqs/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/index.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_sqs/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/layout.tsx b/x-pack/plugins/infra/common/inventory_models/aws_sqs/layout.tsx
similarity index 92%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/layout.tsx
rename to x-pack/plugins/infra/common/inventory_models/aws_sqs/layout.tsx
index 5d460c971ec3b..7f2dc92f42205 100644
--- a/x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/layout.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/aws_sqs/layout.tsx
@@ -5,11 +5,17 @@
  */
 import React from 'react';
 import { i18n } from '@kbn/i18n';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { LayoutPropsWithTheme } from '../../../public/pages/metrics/types';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { Section } from '../../../public/pages/metrics/components/section';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { SubSection } from '../../../public/pages/metrics/components/sub_section';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ChartSectionVis } from '../../../public/pages/metrics/components/chart_section_vis';
-import { withTheme } from '../../../../../common/eui_styled_components';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { withTheme } from '../../../../observability/public';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { LayoutContent } from '../../../public/pages/metrics/components/layout_content';
 
 export const Layout = withTheme(({ metrics, theme }: LayoutPropsWithTheme) => (
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/metrics/index.ts b/x-pack/plugins/infra/common/inventory_models/aws_sqs/metrics/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/metrics/index.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_sqs/metrics/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/metrics/snapshot/sqs_messages_delayed.ts b/x-pack/plugins/infra/common/inventory_models/aws_sqs/metrics/snapshot/sqs_messages_delayed.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/metrics/snapshot/sqs_messages_delayed.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_sqs/metrics/snapshot/sqs_messages_delayed.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/metrics/snapshot/sqs_messages_empty.ts b/x-pack/plugins/infra/common/inventory_models/aws_sqs/metrics/snapshot/sqs_messages_empty.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/metrics/snapshot/sqs_messages_empty.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_sqs/metrics/snapshot/sqs_messages_empty.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/metrics/snapshot/sqs_messages_sent.ts b/x-pack/plugins/infra/common/inventory_models/aws_sqs/metrics/snapshot/sqs_messages_sent.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/metrics/snapshot/sqs_messages_sent.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_sqs/metrics/snapshot/sqs_messages_sent.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/metrics/snapshot/sqs_messages_visible.ts b/x-pack/plugins/infra/common/inventory_models/aws_sqs/metrics/snapshot/sqs_messages_visible.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/metrics/snapshot/sqs_messages_visible.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_sqs/metrics/snapshot/sqs_messages_visible.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/metrics/snapshot/sqs_oldest_message.ts b/x-pack/plugins/infra/common/inventory_models/aws_sqs/metrics/snapshot/sqs_oldest_message.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/metrics/snapshot/sqs_oldest_message.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_sqs/metrics/snapshot/sqs_oldest_message.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/metrics/tsvb/aws_sqs_messages_delayed.ts b/x-pack/plugins/infra/common/inventory_models/aws_sqs/metrics/tsvb/aws_sqs_messages_delayed.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/metrics/tsvb/aws_sqs_messages_delayed.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_sqs/metrics/tsvb/aws_sqs_messages_delayed.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/metrics/tsvb/aws_sqs_messages_empty.ts b/x-pack/plugins/infra/common/inventory_models/aws_sqs/metrics/tsvb/aws_sqs_messages_empty.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/metrics/tsvb/aws_sqs_messages_empty.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_sqs/metrics/tsvb/aws_sqs_messages_empty.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/metrics/tsvb/aws_sqs_messages_sent.ts b/x-pack/plugins/infra/common/inventory_models/aws_sqs/metrics/tsvb/aws_sqs_messages_sent.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/metrics/tsvb/aws_sqs_messages_sent.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_sqs/metrics/tsvb/aws_sqs_messages_sent.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/metrics/tsvb/aws_sqs_messages_visible.ts b/x-pack/plugins/infra/common/inventory_models/aws_sqs/metrics/tsvb/aws_sqs_messages_visible.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/metrics/tsvb/aws_sqs_messages_visible.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_sqs/metrics/tsvb/aws_sqs_messages_visible.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/metrics/tsvb/aws_sqs_oldest_message.ts b/x-pack/plugins/infra/common/inventory_models/aws_sqs/metrics/tsvb/aws_sqs_oldest_message.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/metrics/tsvb/aws_sqs_oldest_message.ts
rename to x-pack/plugins/infra/common/inventory_models/aws_sqs/metrics/tsvb/aws_sqs_oldest_message.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/toolbar_items.tsx b/x-pack/plugins/infra/common/inventory_models/aws_sqs/toolbar_items.tsx
similarity index 94%
rename from x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/toolbar_items.tsx
rename to x-pack/plugins/infra/common/inventory_models/aws_sqs/toolbar_items.tsx
index 9c5ff51b2d9a2..22cb2740a4a7f 100644
--- a/x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/toolbar_items.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/aws_sqs/toolbar_items.tsx
@@ -5,6 +5,7 @@
  */
 
 import React from 'react';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ToolbarProps } from '../../../public/components/inventory/toolbars/toolbar';
 import { MetricsAndGroupByToolbarItems } from '../shared/compontents/metrics_and_groupby_toolbar_items';
 import { CloudToolbarItems } from '../shared/compontents/cloud_toolbar_items';
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/container/index.ts b/x-pack/plugins/infra/common/inventory_models/container/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/container/index.ts
rename to x-pack/plugins/infra/common/inventory_models/container/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/container/layout.tsx b/x-pack/plugins/infra/common/inventory_models/container/layout.tsx
similarity index 93%
rename from x-pack/legacy/plugins/infra/common/inventory_models/container/layout.tsx
rename to x-pack/plugins/infra/common/inventory_models/container/layout.tsx
index e207687cf8643..b61e8eed09cce 100644
--- a/x-pack/legacy/plugins/infra/common/inventory_models/container/layout.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/container/layout.tsx
@@ -5,13 +5,21 @@
  */
 import React from 'react';
 import { i18n } from '@kbn/i18n';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { LayoutPropsWithTheme } from '../../../public/pages/metrics/types';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { Section } from '../../../public/pages/metrics/components/section';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { SubSection } from '../../../public/pages/metrics/components/sub_section';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { GaugesSectionVis } from '../../../public/pages/metrics/components/gauges_section_vis';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ChartSectionVis } from '../../../public/pages/metrics/components/chart_section_vis';
-import { withTheme } from '../../../../../common/eui_styled_components';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { withTheme } from '../../../../observability/public';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { LayoutContent } from '../../../public/pages/metrics/components/layout_content';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { MetadataDetails } from '../../../public/pages/metrics/components/metadata_details';
 
 export const Layout = withTheme(({ metrics, theme }: LayoutPropsWithTheme) => (
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/container/metrics/index.ts b/x-pack/plugins/infra/common/inventory_models/container/metrics/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/container/metrics/index.ts
rename to x-pack/plugins/infra/common/inventory_models/container/metrics/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/container/metrics/snapshot/cpu.ts b/x-pack/plugins/infra/common/inventory_models/container/metrics/snapshot/cpu.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/container/metrics/snapshot/cpu.ts
rename to x-pack/plugins/infra/common/inventory_models/container/metrics/snapshot/cpu.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/container/metrics/snapshot/memory.ts b/x-pack/plugins/infra/common/inventory_models/container/metrics/snapshot/memory.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/container/metrics/snapshot/memory.ts
rename to x-pack/plugins/infra/common/inventory_models/container/metrics/snapshot/memory.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/container/metrics/snapshot/rx.ts b/x-pack/plugins/infra/common/inventory_models/container/metrics/snapshot/rx.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/container/metrics/snapshot/rx.ts
rename to x-pack/plugins/infra/common/inventory_models/container/metrics/snapshot/rx.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/container/metrics/snapshot/tx.ts b/x-pack/plugins/infra/common/inventory_models/container/metrics/snapshot/tx.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/container/metrics/snapshot/tx.ts
rename to x-pack/plugins/infra/common/inventory_models/container/metrics/snapshot/tx.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/container/metrics/tsvb/container_cpu_kernel.ts b/x-pack/plugins/infra/common/inventory_models/container/metrics/tsvb/container_cpu_kernel.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/container/metrics/tsvb/container_cpu_kernel.ts
rename to x-pack/plugins/infra/common/inventory_models/container/metrics/tsvb/container_cpu_kernel.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/container/metrics/tsvb/container_cpu_usage.ts b/x-pack/plugins/infra/common/inventory_models/container/metrics/tsvb/container_cpu_usage.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/container/metrics/tsvb/container_cpu_usage.ts
rename to x-pack/plugins/infra/common/inventory_models/container/metrics/tsvb/container_cpu_usage.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/container/metrics/tsvb/container_disk_io_bytes.ts b/x-pack/plugins/infra/common/inventory_models/container/metrics/tsvb/container_disk_io_bytes.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/container/metrics/tsvb/container_disk_io_bytes.ts
rename to x-pack/plugins/infra/common/inventory_models/container/metrics/tsvb/container_disk_io_bytes.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/container/metrics/tsvb/container_diskio_ops.ts b/x-pack/plugins/infra/common/inventory_models/container/metrics/tsvb/container_diskio_ops.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/container/metrics/tsvb/container_diskio_ops.ts
rename to x-pack/plugins/infra/common/inventory_models/container/metrics/tsvb/container_diskio_ops.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/container/metrics/tsvb/container_memory.ts b/x-pack/plugins/infra/common/inventory_models/container/metrics/tsvb/container_memory.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/container/metrics/tsvb/container_memory.ts
rename to x-pack/plugins/infra/common/inventory_models/container/metrics/tsvb/container_memory.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/container/metrics/tsvb/container_network_traffic.ts b/x-pack/plugins/infra/common/inventory_models/container/metrics/tsvb/container_network_traffic.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/container/metrics/tsvb/container_network_traffic.ts
rename to x-pack/plugins/infra/common/inventory_models/container/metrics/tsvb/container_network_traffic.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/container/metrics/tsvb/container_overview.ts b/x-pack/plugins/infra/common/inventory_models/container/metrics/tsvb/container_overview.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/container/metrics/tsvb/container_overview.ts
rename to x-pack/plugins/infra/common/inventory_models/container/metrics/tsvb/container_overview.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/container/toolbar_items.tsx b/x-pack/plugins/infra/common/inventory_models/container/toolbar_items.tsx
similarity index 94%
rename from x-pack/legacy/plugins/infra/common/inventory_models/container/toolbar_items.tsx
rename to x-pack/plugins/infra/common/inventory_models/container/toolbar_items.tsx
index f1e7ea721cabd..dbb77cb3b677e 100644
--- a/x-pack/legacy/plugins/infra/common/inventory_models/container/toolbar_items.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/container/toolbar_items.tsx
@@ -5,6 +5,7 @@
  */
 
 import React from 'react';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ToolbarProps } from '../../../public/components/inventory/toolbars/toolbar';
 import { MetricsAndGroupByToolbarItems } from '../shared/compontents/metrics_and_groupby_toolbar_items';
 import { SnapshotMetricType } from '../types';
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/create_tsvb_model.ts b/x-pack/plugins/infra/common/inventory_models/create_tsvb_model.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/create_tsvb_model.ts
rename to x-pack/plugins/infra/common/inventory_models/create_tsvb_model.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/index.ts b/x-pack/plugins/infra/common/inventory_models/host/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/host/index.ts
rename to x-pack/plugins/infra/common/inventory_models/host/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/layout.tsx b/x-pack/plugins/infra/common/inventory_models/host/layout.tsx
similarity index 95%
rename from x-pack/legacy/plugins/infra/common/inventory_models/host/layout.tsx
rename to x-pack/plugins/infra/common/inventory_models/host/layout.tsx
index ca53193e64ca2..15e5530e8db53 100644
--- a/x-pack/legacy/plugins/infra/common/inventory_models/host/layout.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/host/layout.tsx
@@ -5,15 +5,23 @@
  */
 import React from 'react';
 import { i18n } from '@kbn/i18n';
-import { withTheme } from '../../../../../common/eui_styled_components/eui_styled_components';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { withTheme } from '../../../../observability/public';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { LayoutPropsWithTheme } from '../../../public/pages/metrics/types';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { Section } from '../../../public/pages/metrics/components/section';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { SubSection } from '../../../public/pages/metrics/components/sub_section';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { GaugesSectionVis } from '../../../public/pages/metrics/components/gauges_section_vis';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ChartSectionVis } from '../../../public/pages/metrics/components/chart_section_vis';
 import * as Aws from '../shared/layouts/aws';
 import * as Ngnix from '../shared/layouts/nginx';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { MetadataDetails } from '../../../public/pages/metrics/components/metadata_details';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { LayoutContent } from '../../../public/pages/metrics/components/layout_content';
 
 export const Layout = withTheme(({ metrics, theme }: LayoutPropsWithTheme) => (
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/index.ts b/x-pack/plugins/infra/common/inventory_models/host/metrics/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/index.ts
rename to x-pack/plugins/infra/common/inventory_models/host/metrics/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/snapshot/cpu.ts b/x-pack/plugins/infra/common/inventory_models/host/metrics/snapshot/cpu.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/snapshot/cpu.ts
rename to x-pack/plugins/infra/common/inventory_models/host/metrics/snapshot/cpu.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/snapshot/load.ts b/x-pack/plugins/infra/common/inventory_models/host/metrics/snapshot/load.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/snapshot/load.ts
rename to x-pack/plugins/infra/common/inventory_models/host/metrics/snapshot/load.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/snapshot/log_rate.ts b/x-pack/plugins/infra/common/inventory_models/host/metrics/snapshot/log_rate.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/snapshot/log_rate.ts
rename to x-pack/plugins/infra/common/inventory_models/host/metrics/snapshot/log_rate.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/snapshot/memory.ts b/x-pack/plugins/infra/common/inventory_models/host/metrics/snapshot/memory.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/snapshot/memory.ts
rename to x-pack/plugins/infra/common/inventory_models/host/metrics/snapshot/memory.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/snapshot/rx.ts b/x-pack/plugins/infra/common/inventory_models/host/metrics/snapshot/rx.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/snapshot/rx.ts
rename to x-pack/plugins/infra/common/inventory_models/host/metrics/snapshot/rx.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/snapshot/tx.ts b/x-pack/plugins/infra/common/inventory_models/host/metrics/snapshot/tx.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/snapshot/tx.ts
rename to x-pack/plugins/infra/common/inventory_models/host/metrics/snapshot/tx.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_cpu_usage.ts b/x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_cpu_usage.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_cpu_usage.ts
rename to x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_cpu_usage.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_docker_info.ts b/x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_docker_info.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_docker_info.ts
rename to x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_docker_info.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_docker_overview.ts b/x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_docker_overview.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_docker_overview.ts
rename to x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_docker_overview.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_docker_top_5_by_cpu.ts b/x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_docker_top_5_by_cpu.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_docker_top_5_by_cpu.ts
rename to x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_docker_top_5_by_cpu.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_docker_top_5_by_memory.ts b/x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_docker_top_5_by_memory.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_docker_top_5_by_memory.ts
rename to x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_docker_top_5_by_memory.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_filesystem.ts b/x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_filesystem.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_filesystem.ts
rename to x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_filesystem.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_k8s_cpu_cap.ts b/x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_k8s_cpu_cap.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_k8s_cpu_cap.ts
rename to x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_k8s_cpu_cap.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_k8s_disk_cap.ts b/x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_k8s_disk_cap.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_k8s_disk_cap.ts
rename to x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_k8s_disk_cap.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_k8s_memory_cap.ts b/x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_k8s_memory_cap.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_k8s_memory_cap.ts
rename to x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_k8s_memory_cap.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_k8s_overview.ts b/x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_k8s_overview.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_k8s_overview.ts
rename to x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_k8s_overview.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_k8s_pod_cap.ts b/x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_k8s_pod_cap.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_k8s_pod_cap.ts
rename to x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_k8s_pod_cap.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_load.ts b/x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_load.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_load.ts
rename to x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_load.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_memory_usage.ts b/x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_memory_usage.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_memory_usage.ts
rename to x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_memory_usage.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_network_traffic.ts b/x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_network_traffic.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_network_traffic.ts
rename to x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_network_traffic.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_system_overview.ts b/x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_system_overview.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/host/metrics/tsvb/host_system_overview.ts
rename to x-pack/plugins/infra/common/inventory_models/host/metrics/tsvb/host_system_overview.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/toolbar_items.tsx b/x-pack/plugins/infra/common/inventory_models/host/toolbar_items.tsx
similarity index 94%
rename from x-pack/legacy/plugins/infra/common/inventory_models/host/toolbar_items.tsx
rename to x-pack/plugins/infra/common/inventory_models/host/toolbar_items.tsx
index 6e0563f2f555b..fc7696ee53c9d 100644
--- a/x-pack/legacy/plugins/infra/common/inventory_models/host/toolbar_items.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/host/toolbar_items.tsx
@@ -5,6 +5,7 @@
  */
 
 import React from 'react';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ToolbarProps } from '../../../public/components/inventory/toolbars/toolbar';
 import { MetricsAndGroupByToolbarItems } from '../shared/compontents/metrics_and_groupby_toolbar_items';
 import { SnapshotMetricType } from '../types';
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/index.ts b/x-pack/plugins/infra/common/inventory_models/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/index.ts
rename to x-pack/plugins/infra/common/inventory_models/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/intl_strings.ts b/x-pack/plugins/infra/common/inventory_models/intl_strings.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/intl_strings.ts
rename to x-pack/plugins/infra/common/inventory_models/intl_strings.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/layouts.ts b/x-pack/plugins/infra/common/inventory_models/layouts.ts
similarity index 96%
rename from x-pack/legacy/plugins/infra/common/inventory_models/layouts.ts
rename to x-pack/plugins/infra/common/inventory_models/layouts.ts
index d9008753adf7b..b59ce010361ec 100644
--- a/x-pack/legacy/plugins/infra/common/inventory_models/layouts.ts
+++ b/x-pack/plugins/infra/common/inventory_models/layouts.ts
@@ -22,6 +22,7 @@ import { Layout as AwsS3Layout } from './aws_s3/layout';
 import { Layout as AwsRDSLayout } from './aws_rds/layout';
 import { Layout as AwsSQSLayout } from './aws_sqs/layout';
 import { InventoryItemType } from './types';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { LayoutProps } from '../../public/pages/metrics/types';
 
 interface Layouts {
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/metrics.ts b/x-pack/plugins/infra/common/inventory_models/metrics.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/metrics.ts
rename to x-pack/plugins/infra/common/inventory_models/metrics.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/pod/index.ts b/x-pack/plugins/infra/common/inventory_models/pod/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/pod/index.ts
rename to x-pack/plugins/infra/common/inventory_models/pod/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/pod/layout.tsx b/x-pack/plugins/infra/common/inventory_models/pod/layout.tsx
similarity index 91%
rename from x-pack/legacy/plugins/infra/common/inventory_models/pod/layout.tsx
rename to x-pack/plugins/infra/common/inventory_models/pod/layout.tsx
index f0c27ccff13b1..43b95d73f6d95 100644
--- a/x-pack/legacy/plugins/infra/common/inventory_models/pod/layout.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/pod/layout.tsx
@@ -5,14 +5,22 @@
  */
 import React from 'react';
 import { i18n } from '@kbn/i18n';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { LayoutPropsWithTheme } from '../../../public/pages/metrics/types';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { Section } from '../../../public/pages/metrics/components/section';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { SubSection } from '../../../public/pages/metrics/components/sub_section';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { GaugesSectionVis } from '../../../public/pages/metrics/components/gauges_section_vis';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ChartSectionVis } from '../../../public/pages/metrics/components/chart_section_vis';
-import { withTheme } from '../../../../../common/eui_styled_components';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { withTheme } from '../../../../observability/public';
 import * as Nginx from '../shared/layouts/nginx';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { MetadataDetails } from '../../../public/pages/metrics/components/metadata_details';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { LayoutContent } from '../../../public/pages/metrics/components/layout_content';
 
 export const Layout = withTheme(({ metrics, theme }: LayoutPropsWithTheme) => (
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/pod/metrics/index.ts b/x-pack/plugins/infra/common/inventory_models/pod/metrics/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/pod/metrics/index.ts
rename to x-pack/plugins/infra/common/inventory_models/pod/metrics/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/pod/metrics/snapshot/cpu.ts b/x-pack/plugins/infra/common/inventory_models/pod/metrics/snapshot/cpu.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/pod/metrics/snapshot/cpu.ts
rename to x-pack/plugins/infra/common/inventory_models/pod/metrics/snapshot/cpu.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/pod/metrics/snapshot/memory.ts b/x-pack/plugins/infra/common/inventory_models/pod/metrics/snapshot/memory.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/pod/metrics/snapshot/memory.ts
rename to x-pack/plugins/infra/common/inventory_models/pod/metrics/snapshot/memory.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/pod/metrics/snapshot/rx.ts b/x-pack/plugins/infra/common/inventory_models/pod/metrics/snapshot/rx.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/pod/metrics/snapshot/rx.ts
rename to x-pack/plugins/infra/common/inventory_models/pod/metrics/snapshot/rx.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/pod/metrics/snapshot/tx.ts b/x-pack/plugins/infra/common/inventory_models/pod/metrics/snapshot/tx.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/pod/metrics/snapshot/tx.ts
rename to x-pack/plugins/infra/common/inventory_models/pod/metrics/snapshot/tx.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_cpu_usage.ts b/x-pack/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_cpu_usage.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_cpu_usage.ts
rename to x-pack/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_cpu_usage.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_log_usage.ts b/x-pack/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_log_usage.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_log_usage.ts
rename to x-pack/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_log_usage.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_memory_usage.ts b/x-pack/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_memory_usage.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_memory_usage.ts
rename to x-pack/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_memory_usage.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_network_traffic.ts b/x-pack/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_network_traffic.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_network_traffic.ts
rename to x-pack/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_network_traffic.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_overview.ts b/x-pack/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_overview.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_overview.ts
rename to x-pack/plugins/infra/common/inventory_models/pod/metrics/tsvb/pod_overview.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/pod/toolbar_items.tsx b/x-pack/plugins/infra/common/inventory_models/pod/toolbar_items.tsx
similarity index 93%
rename from x-pack/legacy/plugins/infra/common/inventory_models/pod/toolbar_items.tsx
rename to x-pack/plugins/infra/common/inventory_models/pod/toolbar_items.tsx
index b075f10a15680..d48c27efd88fd 100644
--- a/x-pack/legacy/plugins/infra/common/inventory_models/pod/toolbar_items.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/pod/toolbar_items.tsx
@@ -5,6 +5,7 @@
  */
 
 import React from 'react';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ToolbarProps } from '../../../public/components/inventory/toolbars/toolbar';
 import { MetricsAndGroupByToolbarItems } from '../shared/compontents/metrics_and_groupby_toolbar_items';
 import { SnapshotMetricType } from '../types';
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/shared/compontents/cloud_toolbar_items.tsx b/x-pack/plugins/infra/common/inventory_models/shared/compontents/cloud_toolbar_items.tsx
similarity index 87%
rename from x-pack/legacy/plugins/infra/common/inventory_models/shared/compontents/cloud_toolbar_items.tsx
rename to x-pack/plugins/infra/common/inventory_models/shared/compontents/cloud_toolbar_items.tsx
index 7150d58be4eb7..766a8ae8142f5 100644
--- a/x-pack/legacy/plugins/infra/common/inventory_models/shared/compontents/cloud_toolbar_items.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/shared/compontents/cloud_toolbar_items.tsx
@@ -6,8 +6,11 @@
 
 import React from 'react';
 import { EuiFlexItem } from '@elastic/eui';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ToolbarProps } from '../../../../public/components/inventory/toolbars/toolbar';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { WaffleAccountsControls } from '../../../../public/components/waffle/waffle_accounts_controls';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { WaffleRegionControls } from '../../../../public/components/waffle/waffle_region_controls';
 
 type Props = ToolbarProps;
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/shared/compontents/metrics_and_groupby_toolbar_items.tsx b/x-pack/plugins/infra/common/inventory_models/shared/compontents/metrics_and_groupby_toolbar_items.tsx
similarity index 88%
rename from x-pack/legacy/plugins/infra/common/inventory_models/shared/compontents/metrics_and_groupby_toolbar_items.tsx
rename to x-pack/plugins/infra/common/inventory_models/shared/compontents/metrics_and_groupby_toolbar_items.tsx
index dd7be5619df46..fcaedcdd080b8 100644
--- a/x-pack/legacy/plugins/infra/common/inventory_models/shared/compontents/metrics_and_groupby_toolbar_items.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/shared/compontents/metrics_and_groupby_toolbar_items.tsx
@@ -6,12 +6,16 @@
 
 import React, { useMemo } from 'react';
 import { EuiFlexItem } from '@elastic/eui';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ToolbarProps } from '../../../../public/components/inventory/toolbars/toolbar';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { WaffleMetricControls } from '../../../../public/components/waffle/waffle_metric_controls';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { WaffleGroupByControls } from '../../../../public/components/waffle/waffle_group_by_controls';
 import {
   toGroupByOpt,
   toMetricOpt,
+  // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 } from '../../../../public/components/inventory/toolbars/toolbar_wrapper';
 import { SnapshotMetricType } from '../../types';
 
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/shared/layouts/aws.tsx b/x-pack/plugins/infra/common/inventory_models/shared/layouts/aws.tsx
similarity index 94%
rename from x-pack/legacy/plugins/infra/common/inventory_models/shared/layouts/aws.tsx
rename to x-pack/plugins/infra/common/inventory_models/shared/layouts/aws.tsx
index 2cabbe4c33ff3..fba48c4224e6b 100644
--- a/x-pack/legacy/plugins/infra/common/inventory_models/shared/layouts/aws.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/shared/layouts/aws.tsx
@@ -5,12 +5,18 @@
  */
 import React from 'react';
 import { i18n } from '@kbn/i18n';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { LayoutPropsWithTheme } from '../../../../public/pages/metrics/types';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { Section } from '../../../../public/pages/metrics/components/section';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { SubSection } from '../../../../public/pages/metrics/components/sub_section';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { GaugesSectionVis } from '../../../../public/pages/metrics/components/gauges_section_vis';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ChartSectionVis } from '../../../../public/pages/metrics/components/chart_section_vis';
-import { withTheme } from '../../../../../../common/eui_styled_components';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { withTheme } from '../../../../../observability/public';
 
 export const Layout = withTheme(({ metrics, theme }: LayoutPropsWithTheme) => (
   <React.Fragment>
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/shared/layouts/nginx.tsx b/x-pack/plugins/infra/common/inventory_models/shared/layouts/nginx.tsx
similarity index 90%
rename from x-pack/legacy/plugins/infra/common/inventory_models/shared/layouts/nginx.tsx
rename to x-pack/plugins/infra/common/inventory_models/shared/layouts/nginx.tsx
index 9d31ffa775d21..eff0a9bbca85a 100644
--- a/x-pack/legacy/plugins/infra/common/inventory_models/shared/layouts/nginx.tsx
+++ b/x-pack/plugins/infra/common/inventory_models/shared/layouts/nginx.tsx
@@ -5,11 +5,16 @@
  */
 import React from 'react';
 import { i18n } from '@kbn/i18n';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { LayoutPropsWithTheme } from '../../../../public/pages/metrics/types';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { Section } from '../../../../public/pages/metrics/components/section';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { SubSection } from '../../../../public/pages/metrics/components/sub_section';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ChartSectionVis } from '../../../../public/pages/metrics/components/chart_section_vis';
-import { withTheme } from '../../../../../../common/eui_styled_components';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { withTheme } from '../../../../../observability/public';
 
 export const Layout = withTheme(({ metrics, theme }: LayoutPropsWithTheme) => (
   <React.Fragment>
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/index.ts b/x-pack/plugins/infra/common/inventory_models/shared/metrics/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/index.ts
rename to x-pack/plugins/infra/common/inventory_models/shared/metrics/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/required_metrics.ts b/x-pack/plugins/infra/common/inventory_models/shared/metrics/required_metrics.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/required_metrics.ts
rename to x-pack/plugins/infra/common/inventory_models/shared/metrics/required_metrics.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/snapshot/count.ts b/x-pack/plugins/infra/common/inventory_models/shared/metrics/snapshot/count.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/snapshot/count.ts
rename to x-pack/plugins/infra/common/inventory_models/shared/metrics/snapshot/count.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/snapshot/network_traffic.ts b/x-pack/plugins/infra/common/inventory_models/shared/metrics/snapshot/network_traffic.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/snapshot/network_traffic.ts
rename to x-pack/plugins/infra/common/inventory_models/shared/metrics/snapshot/network_traffic.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/snapshot/network_traffic_with_interfaces.ts b/x-pack/plugins/infra/common/inventory_models/shared/metrics/snapshot/network_traffic_with_interfaces.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/snapshot/network_traffic_with_interfaces.ts
rename to x-pack/plugins/infra/common/inventory_models/shared/metrics/snapshot/network_traffic_with_interfaces.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/snapshot/rate.ts b/x-pack/plugins/infra/common/inventory_models/shared/metrics/snapshot/rate.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/snapshot/rate.ts
rename to x-pack/plugins/infra/common/inventory_models/shared/metrics/snapshot/rate.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_cpu_utilization.ts b/x-pack/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_cpu_utilization.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_cpu_utilization.ts
rename to x-pack/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_cpu_utilization.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_diskio_bytes.ts b/x-pack/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_diskio_bytes.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_diskio_bytes.ts
rename to x-pack/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_diskio_bytes.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_diskio_ops.ts b/x-pack/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_diskio_ops.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_diskio_ops.ts
rename to x-pack/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_diskio_ops.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_network_bytes.ts b/x-pack/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_network_bytes.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_network_bytes.ts
rename to x-pack/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_network_bytes.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_network_packets.ts b/x-pack/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_network_packets.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_network_packets.ts
rename to x-pack/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_network_packets.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_overview.ts b/x-pack/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_overview.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_overview.ts
rename to x-pack/plugins/infra/common/inventory_models/shared/metrics/tsvb/aws_overview.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/tsvb/nginx_active_connections.ts b/x-pack/plugins/infra/common/inventory_models/shared/metrics/tsvb/nginx_active_connections.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/tsvb/nginx_active_connections.ts
rename to x-pack/plugins/infra/common/inventory_models/shared/metrics/tsvb/nginx_active_connections.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/tsvb/nginx_hits.ts b/x-pack/plugins/infra/common/inventory_models/shared/metrics/tsvb/nginx_hits.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/tsvb/nginx_hits.ts
rename to x-pack/plugins/infra/common/inventory_models/shared/metrics/tsvb/nginx_hits.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/tsvb/nginx_request_rate.ts b/x-pack/plugins/infra/common/inventory_models/shared/metrics/tsvb/nginx_request_rate.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/tsvb/nginx_request_rate.ts
rename to x-pack/plugins/infra/common/inventory_models/shared/metrics/tsvb/nginx_request_rate.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/tsvb/nginx_requests_per_connection.ts b/x-pack/plugins/infra/common/inventory_models/shared/metrics/tsvb/nginx_requests_per_connection.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/tsvb/nginx_requests_per_connection.ts
rename to x-pack/plugins/infra/common/inventory_models/shared/metrics/tsvb/nginx_requests_per_connection.ts
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/toolbars.ts b/x-pack/plugins/infra/common/inventory_models/toolbars.ts
similarity index 96%
rename from x-pack/legacy/plugins/infra/common/inventory_models/toolbars.ts
rename to x-pack/plugins/infra/common/inventory_models/toolbars.ts
index 05def078c7f2d..39e9f5a260f7a 100644
--- a/x-pack/legacy/plugins/infra/common/inventory_models/toolbars.ts
+++ b/x-pack/plugins/infra/common/inventory_models/toolbars.ts
@@ -10,6 +10,7 @@ import { InventoryItemType } from './types';
 import { HostToolbarItems } from './host/toolbar_items';
 import { ContainerToolbarItems } from './container/toolbar_items';
 import { PodToolbarItems } from './pod/toolbar_items';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ToolbarProps } from '../../public/components/inventory/toolbars/toolbar';
 import { AwsEC2ToolbarItems } from './aws_ec2/toolbar_items';
 import { AwsS3ToolbarItems } from './aws_s3/toolbar_items';
diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/types.ts b/x-pack/plugins/infra/common/inventory_models/types.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/inventory_models/types.ts
rename to x-pack/plugins/infra/common/inventory_models/types.ts
diff --git a/x-pack/legacy/plugins/infra/common/log_analysis/index.ts b/x-pack/plugins/infra/common/log_analysis/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/log_analysis/index.ts
rename to x-pack/plugins/infra/common/log_analysis/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/log_analysis/job_parameters.ts b/x-pack/plugins/infra/common/log_analysis/job_parameters.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/log_analysis/job_parameters.ts
rename to x-pack/plugins/infra/common/log_analysis/job_parameters.ts
diff --git a/x-pack/legacy/plugins/infra/common/log_analysis/log_analysis.ts b/x-pack/plugins/infra/common/log_analysis/log_analysis.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/log_analysis/log_analysis.ts
rename to x-pack/plugins/infra/common/log_analysis/log_analysis.ts
diff --git a/x-pack/legacy/plugins/infra/common/log_analysis/log_analysis_results.ts b/x-pack/plugins/infra/common/log_analysis/log_analysis_results.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/log_analysis/log_analysis_results.ts
rename to x-pack/plugins/infra/common/log_analysis/log_analysis_results.ts
diff --git a/x-pack/legacy/plugins/infra/common/log_analysis/log_entry_categories_analysis.ts b/x-pack/plugins/infra/common/log_analysis/log_entry_categories_analysis.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/log_analysis/log_entry_categories_analysis.ts
rename to x-pack/plugins/infra/common/log_analysis/log_entry_categories_analysis.ts
diff --git a/x-pack/legacy/plugins/infra/common/log_analysis/log_entry_rate_analysis.ts b/x-pack/plugins/infra/common/log_analysis/log_entry_rate_analysis.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/log_analysis/log_entry_rate_analysis.ts
rename to x-pack/plugins/infra/common/log_analysis/log_entry_rate_analysis.ts
diff --git a/x-pack/legacy/plugins/infra/common/log_entry/index.ts b/x-pack/plugins/infra/common/log_entry/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/log_entry/index.ts
rename to x-pack/plugins/infra/common/log_entry/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/log_entry/log_entry.ts b/x-pack/plugins/infra/common/log_entry/log_entry.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/log_entry/log_entry.ts
rename to x-pack/plugins/infra/common/log_entry/log_entry.ts
diff --git a/x-pack/legacy/plugins/infra/common/log_search_result/index.ts b/x-pack/plugins/infra/common/log_search_result/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/log_search_result/index.ts
rename to x-pack/plugins/infra/common/log_search_result/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/log_search_result/log_search_result.ts b/x-pack/plugins/infra/common/log_search_result/log_search_result.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/log_search_result/log_search_result.ts
rename to x-pack/plugins/infra/common/log_search_result/log_search_result.ts
diff --git a/x-pack/legacy/plugins/infra/common/log_search_summary/index.ts b/x-pack/plugins/infra/common/log_search_summary/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/log_search_summary/index.ts
rename to x-pack/plugins/infra/common/log_search_summary/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/log_search_summary/log_search_summary.ts b/x-pack/plugins/infra/common/log_search_summary/log_search_summary.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/log_search_summary/log_search_summary.ts
rename to x-pack/plugins/infra/common/log_search_summary/log_search_summary.ts
diff --git a/x-pack/legacy/plugins/infra/common/log_text_scale/index.ts b/x-pack/plugins/infra/common/log_text_scale/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/log_text_scale/index.ts
rename to x-pack/plugins/infra/common/log_text_scale/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/log_text_scale/log_text_scale.ts b/x-pack/plugins/infra/common/log_text_scale/log_text_scale.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/log_text_scale/log_text_scale.ts
rename to x-pack/plugins/infra/common/log_text_scale/log_text_scale.ts
diff --git a/x-pack/legacy/plugins/infra/common/performance_tracing.ts b/x-pack/plugins/infra/common/performance_tracing.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/performance_tracing.ts
rename to x-pack/plugins/infra/common/performance_tracing.ts
diff --git a/x-pack/legacy/plugins/infra/common/runtime_types.ts b/x-pack/plugins/infra/common/runtime_types.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/runtime_types.ts
rename to x-pack/plugins/infra/common/runtime_types.ts
diff --git a/x-pack/legacy/plugins/infra/common/saved_objects/inventory_view.ts b/x-pack/plugins/infra/common/saved_objects/inventory_view.ts
similarity index 91%
rename from x-pack/legacy/plugins/infra/common/saved_objects/inventory_view.ts
rename to x-pack/plugins/infra/common/saved_objects/inventory_view.ts
index c86be102f85a8..3ab9042947f97 100644
--- a/x-pack/legacy/plugins/infra/common/saved_objects/inventory_view.ts
+++ b/x-pack/plugins/infra/common/saved_objects/inventory_view.ts
@@ -4,10 +4,13 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ElasticsearchMappingOf } from '../../server/utils/typed_elasticsearch_mappings';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { WaffleViewState } from '../../public/containers/waffle/with_waffle_view_state';
 
 export const inventoryViewSavedObjectType = 'inventory-view';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { SavedViewSavedObject } from '../../public/hooks/use_saved_view';
 
 export const inventoryViewSavedObjectMappings: {
diff --git a/x-pack/legacy/plugins/infra/common/saved_objects/metrics_explorer_view.ts b/x-pack/plugins/infra/common/saved_objects/metrics_explorer_view.ts
similarity index 92%
rename from x-pack/legacy/plugins/infra/common/saved_objects/metrics_explorer_view.ts
rename to x-pack/plugins/infra/common/saved_objects/metrics_explorer_view.ts
index e4ec71907eaa8..a9f1194844640 100644
--- a/x-pack/legacy/plugins/infra/common/saved_objects/metrics_explorer_view.ts
+++ b/x-pack/plugins/infra/common/saved_objects/metrics_explorer_view.ts
@@ -4,12 +4,15 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { ElasticsearchMappingOf } from '../../server/utils/typed_elasticsearch_mappings';
 import {
   MetricsExplorerOptions,
   MetricsExplorerChartOptions,
   MetricsExplorerTimeOptions,
+  // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 } from '../../public/containers/metrics_explorer/use_metrics_explorer_options';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { SavedViewSavedObject } from '../../public/hooks/use_saved_view';
 
 interface MetricsExplorerSavedView {
diff --git a/x-pack/legacy/plugins/infra/common/time/index.ts b/x-pack/plugins/infra/common/time/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/time/index.ts
rename to x-pack/plugins/infra/common/time/index.ts
diff --git a/x-pack/legacy/plugins/infra/common/time/time_key.ts b/x-pack/plugins/infra/common/time/time_key.ts
similarity index 97%
rename from x-pack/legacy/plugins/infra/common/time/time_key.ts
rename to x-pack/plugins/infra/common/time/time_key.ts
index 117cd38314de0..e4f41615eb484 100644
--- a/x-pack/legacy/plugins/infra/common/time/time_key.ts
+++ b/x-pack/plugins/infra/common/time/time_key.ts
@@ -5,7 +5,7 @@
  */
 
 import { ascending, bisector } from 'd3-array';
-import pick from 'lodash/fp/pick';
+import { pick } from 'lodash';
 
 export interface TimeKey {
   time: number;
@@ -27,7 +27,7 @@ export const isTimeKey = (value: any): value is TimeKey =>
   typeof value.tiebreaker === 'number';
 
 export const pickTimeKey = <T extends TimeKey>(value: T): TimeKey =>
-  pick(['time', 'tiebreaker'], value);
+  pick(value, ['time', 'tiebreaker']);
 
 export function compareTimeKeys(
   firstKey: TimeKey,
diff --git a/x-pack/legacy/plugins/infra/common/time/time_scale.ts b/x-pack/plugins/infra/common/time/time_scale.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/time/time_scale.ts
rename to x-pack/plugins/infra/common/time/time_scale.ts
diff --git a/x-pack/legacy/plugins/infra/common/time/time_unit.ts b/x-pack/plugins/infra/common/time/time_unit.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/time/time_unit.ts
rename to x-pack/plugins/infra/common/time/time_unit.ts
diff --git a/x-pack/legacy/plugins/infra/common/typed_json.ts b/x-pack/plugins/infra/common/typed_json.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/typed_json.ts
rename to x-pack/plugins/infra/common/typed_json.ts
diff --git a/x-pack/legacy/plugins/infra/common/utility_types.ts b/x-pack/plugins/infra/common/utility_types.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/common/utility_types.ts
rename to x-pack/plugins/infra/common/utility_types.ts
diff --git a/x-pack/legacy/plugins/infra/docs/arch.md b/x-pack/plugins/infra/docs/arch.md
similarity index 100%
rename from x-pack/legacy/plugins/infra/docs/arch.md
rename to x-pack/plugins/infra/docs/arch.md
diff --git a/x-pack/legacy/plugins/infra/docs/arch_client.md b/x-pack/plugins/infra/docs/arch_client.md
similarity index 100%
rename from x-pack/legacy/plugins/infra/docs/arch_client.md
rename to x-pack/plugins/infra/docs/arch_client.md
diff --git a/x-pack/legacy/plugins/infra/docs/assets/arch.png b/x-pack/plugins/infra/docs/assets/arch.png
similarity index 100%
rename from x-pack/legacy/plugins/infra/docs/assets/arch.png
rename to x-pack/plugins/infra/docs/assets/arch.png
diff --git a/x-pack/legacy/plugins/infra/docs/assets/infra_metricbeat_aws.jpg b/x-pack/plugins/infra/docs/assets/infra_metricbeat_aws.jpg
similarity index 100%
rename from x-pack/legacy/plugins/infra/docs/assets/infra_metricbeat_aws.jpg
rename to x-pack/plugins/infra/docs/assets/infra_metricbeat_aws.jpg
diff --git a/x-pack/legacy/plugins/infra/docs/graphql.md b/x-pack/plugins/infra/docs/graphql.md
similarity index 100%
rename from x-pack/legacy/plugins/infra/docs/graphql.md
rename to x-pack/plugins/infra/docs/graphql.md
diff --git a/x-pack/legacy/plugins/infra/docs/test_setups/infra_metricbeat_aws.md b/x-pack/plugins/infra/docs/test_setups/infra_metricbeat_aws.md
similarity index 100%
rename from x-pack/legacy/plugins/infra/docs/test_setups/infra_metricbeat_aws.md
rename to x-pack/plugins/infra/docs/test_setups/infra_metricbeat_aws.md
diff --git a/x-pack/legacy/plugins/infra/docs/test_setups/infra_metricbeat_docker_nginx.md b/x-pack/plugins/infra/docs/test_setups/infra_metricbeat_docker_nginx.md
similarity index 100%
rename from x-pack/legacy/plugins/infra/docs/test_setups/infra_metricbeat_docker_nginx.md
rename to x-pack/plugins/infra/docs/test_setups/infra_metricbeat_docker_nginx.md
diff --git a/x-pack/plugins/infra/kibana.json b/x-pack/plugins/infra/kibana.json
index ec5420a4d28d5..7ba81127c1e67 100644
--- a/x-pack/plugins/infra/kibana.json
+++ b/x-pack/plugins/infra/kibana.json
@@ -2,5 +2,8 @@
   "id": "infra",
   "version": "8.0.0",
   "kibanaVersion": "kibana",
-  "server": true
+  "requiredPlugins": ["features", "apm", "usageCollection", "spaces", "home", "data", "data_enhanced", "metrics"],
+  "server": true,
+  "ui": true,
+  "configPath": ["xpack", "infra"]
 }
diff --git a/x-pack/legacy/plugins/infra/public/apps/start_app.tsx b/x-pack/plugins/infra/public/apps/start_app.tsx
similarity index 65%
rename from x-pack/legacy/plugins/infra/public/apps/start_app.tsx
rename to x-pack/plugins/infra/public/apps/start_app.tsx
index dbdc827478a45..300d97d3c45b1 100644
--- a/x-pack/legacy/plugins/infra/public/apps/start_app.tsx
+++ b/x-pack/plugins/infra/public/apps/start_app.tsx
@@ -4,20 +4,19 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { createHashHistory } from 'history';
+import { createBrowserHistory } from 'history';
 import React from 'react';
 import ReactDOM from 'react-dom';
 import { ApolloProvider } from 'react-apollo';
 import { Provider as ReduxStoreProvider } from 'react-redux';
 import { BehaviorSubject } from 'rxjs';
 import { pluck } from 'rxjs/operators';
-import { CoreStart } from 'kibana/public';
+import { CoreStart, AppMountParameters } from 'kibana/public';
 
 // TODO use theme provided from parentApp when kibana supports it
 import { EuiErrorBoundary } from '@elastic/eui';
-import { EuiThemeProvider } from '../../../../common/eui_styled_components';
+import { EuiThemeProvider } from '../../../observability/public';
 import { InfraFrontendLibs } from '../lib/lib';
-import { PageRouter } from '../routes';
 import { createStore } from '../store';
 import { ApolloClientContext } from '../utils/apollo_context';
 import { ReduxStateContextProvider } from '../utils/redux_context';
@@ -25,12 +24,20 @@ import { HistoryContext } from '../utils/history_context';
 import {
   useUiSetting$,
   KibanaContextProvider,
-} from '../../../../../../src/plugins/kibana_react/public';
-import { ROOT_ELEMENT_ID } from '../app';
+} from '../../../../../src/plugins/kibana_react/public';
+import { AppRouter } from '../routers';
 
-// NP_TODO: Type plugins
-export async function startApp(libs: InfraFrontendLibs, core: CoreStart, plugins: any) {
-  const history = createHashHistory();
+export const CONTAINER_CLASSNAME = 'infra-container-element';
+
+export async function startApp(
+  libs: InfraFrontendLibs,
+  core: CoreStart,
+  plugins: object,
+  params: AppMountParameters,
+  Router: AppRouter
+) {
+  const { element, appBasePath } = params;
+  const history = createBrowserHistory({ basename: appBasePath });
   const libs$ = new BehaviorSubject(libs);
   const store = createStore({
     apolloClient: libs$.pipe(pluck('apolloClient')),
@@ -49,7 +56,7 @@ export async function startApp(libs: InfraFrontendLibs, core: CoreStart, plugins
                 <ApolloClientContext.Provider value={libs.apolloClient}>
                   <EuiThemeProvider darkMode={darkMode}>
                     <HistoryContext.Provider value={history}>
-                      <PageRouter history={history} />
+                      <Router history={history} />
                     </HistoryContext.Provider>
                   </EuiThemeProvider>
                 </ApolloClientContext.Provider>
@@ -61,15 +68,22 @@ export async function startApp(libs: InfraFrontendLibs, core: CoreStart, plugins
     );
   };
 
-  const node = await document.getElementById(ROOT_ELEMENT_ID);
-
-  const App = (
+  const App: React.FunctionComponent = () => (
     <KibanaContextProvider services={{ ...core, ...plugins }}>
       <InfraPluginRoot />
     </KibanaContextProvider>
   );
 
-  if (node) {
-    ReactDOM.render(App, node);
-  }
+  // Ensure the element we're handed from application mounting takes up
+  // the full size it can, so that our inner application styles work as
+  // expected.
+  element.style.height = '100%';
+  element.style.display = 'flex';
+  element.className += ` ${CONTAINER_CLASSNAME}`;
+
+  ReactDOM.render(<App />, element);
+
+  return () => {
+    ReactDOM.unmountComponentAtNode(element);
+  };
 }
diff --git a/x-pack/plugins/infra/public/apps/start_legacy_app.tsx b/x-pack/plugins/infra/public/apps/start_legacy_app.tsx
new file mode 100644
index 0000000000000..6e5960ceb2081
--- /dev/null
+++ b/x-pack/plugins/infra/public/apps/start_legacy_app.tsx
@@ -0,0 +1,100 @@
+/*
+ * 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 { createBrowserHistory } from 'history';
+import React from 'react';
+import url from 'url';
+import ReactDOM from 'react-dom';
+import { AppMountParameters } from 'kibana/public';
+import { Route, Router, Switch, RouteProps } from 'react-router-dom';
+// TODO use theme provided from parentApp when kibana supports it
+import { EuiErrorBoundary } from '@elastic/eui';
+
+// This exists purely to facilitate legacy app/infra URL redirects.
+// It will be removed in 8.0.0.
+export async function startLegacyApp(params: AppMountParameters) {
+  const { element } = params;
+  const history = createBrowserHistory();
+
+  const App: React.FunctionComponent = () => {
+    return (
+      <EuiErrorBoundary>
+        <Router history={history}>
+          <Switch>
+            <Route
+              path={'/'}
+              render={({ location }: RouteProps) => {
+                if (!location) {
+                  return null;
+                }
+
+                let nextPath = '';
+                let nextBasePath = '';
+                let nextSearch;
+
+                if (
+                  location.hash.indexOf('#infrastructure') > -1 ||
+                  location.hash.indexOf('#/infrastructure') > -1
+                ) {
+                  nextPath = location.hash.replace(
+                    new RegExp(
+                      '#infrastructure/|#/infrastructure/|#/infrastructure|#infrastructure',
+                      'g'
+                    ),
+                    ''
+                  );
+                  nextBasePath = location.pathname.replace('app/infra', 'app/metrics');
+                } else if (
+                  location.hash.indexOf('#logs') > -1 ||
+                  location.hash.indexOf('#/logs') > -1
+                ) {
+                  nextPath = location.hash.replace(
+                    new RegExp('#logs/|#/logs/|#/logs|#logs', 'g'),
+                    ''
+                  );
+                  nextBasePath = location.pathname.replace('app/infra', 'app/logs');
+                } else {
+                  // This covers /app/infra and /app/infra/home (both of which used to render
+                  // the metrics inventory page)
+                  nextPath = 'inventory';
+                  nextBasePath = location.pathname.replace('app/infra', 'app/metrics');
+                  nextSearch = undefined;
+                }
+
+                // app/inra#infrastructure/metrics/:type/:node was changed to app/metrics/detail/:type/:node, this
+                // accounts for that edge case
+                nextPath = nextPath.replace('metrics/', 'detail/');
+
+                // Query parameters (location.search) will arrive as part of location.hash and not location.search
+                const nextPathParts = nextPath.split('?');
+                nextPath = nextPathParts[0];
+                nextSearch = nextPathParts[1] ? nextPathParts[1] : undefined;
+
+                let nextUrl = url.format({
+                  pathname: `${nextBasePath}/${nextPath}`,
+                  hash: undefined,
+                  search: nextSearch,
+                });
+
+                nextUrl = nextUrl.replace('//', '/');
+
+                window.location.href = nextUrl;
+
+                return null;
+              }}
+            />
+          </Switch>
+        </Router>
+      </EuiErrorBoundary>
+    );
+  };
+
+  ReactDOM.render(<App />, element);
+
+  return () => {
+    ReactDOM.unmountComponentAtNode(element);
+  };
+}
diff --git a/x-pack/legacy/plugins/infra/public/components/auto_sizer.tsx b/x-pack/plugins/infra/public/components/auto_sizer.tsx
similarity index 99%
rename from x-pack/legacy/plugins/infra/public/components/auto_sizer.tsx
rename to x-pack/plugins/infra/public/components/auto_sizer.tsx
index 1d6798aacebe5..284b5295111c6 100644
--- a/x-pack/legacy/plugins/infra/public/components/auto_sizer.tsx
+++ b/x-pack/plugins/infra/public/components/auto_sizer.tsx
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import isEqual from 'lodash/fp/isEqual';
+import { isEqual } from 'lodash';
 import React from 'react';
 import ResizeObserver from 'resize-observer-polyfill';
 
diff --git a/x-pack/legacy/plugins/infra/public/components/autocomplete_field/autocomplete_field.tsx b/x-pack/plugins/infra/public/components/autocomplete_field/autocomplete_field.tsx
similarity index 98%
rename from x-pack/legacy/plugins/infra/public/components/autocomplete_field/autocomplete_field.tsx
rename to x-pack/plugins/infra/public/components/autocomplete_field/autocomplete_field.tsx
index f483f2b1b3f57..2abef7d71e65a 100644
--- a/x-pack/legacy/plugins/infra/public/components/autocomplete_field/autocomplete_field.tsx
+++ b/x-pack/plugins/infra/public/components/autocomplete_field/autocomplete_field.tsx
@@ -12,9 +12,8 @@ import {
 } from '@elastic/eui';
 import React from 'react';
 
-import { QuerySuggestion } from '../../../../../../../src/plugins/data/public';
-
-import euiStyled from '../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../observability/public';
+import { QuerySuggestion } from '../../../../../../src/plugins/data/public';
 import { composeStateUpdaters } from '../../utils/typed_react';
 import { SuggestionItem } from './suggestion_item';
 
diff --git a/x-pack/legacy/plugins/infra/public/components/autocomplete_field/index.ts b/x-pack/plugins/infra/public/components/autocomplete_field/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/autocomplete_field/index.ts
rename to x-pack/plugins/infra/public/components/autocomplete_field/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/components/autocomplete_field/suggestion_item.tsx b/x-pack/plugins/infra/public/components/autocomplete_field/suggestion_item.tsx
similarity index 79%
rename from x-pack/legacy/plugins/infra/public/components/autocomplete_field/suggestion_item.tsx
rename to x-pack/plugins/infra/public/components/autocomplete_field/suggestion_item.tsx
index 689eb47f289c2..fb0c1127df3d1 100644
--- a/x-pack/legacy/plugins/infra/public/components/autocomplete_field/suggestion_item.tsx
+++ b/x-pack/plugins/infra/public/components/autocomplete_field/suggestion_item.tsx
@@ -7,9 +7,8 @@
 import { EuiIcon } from '@elastic/eui';
 import { transparentize } from 'polished';
 import React from 'react';
-
-import { QuerySuggestion } from '../../../../../../../src/plugins/data/public';
-import euiStyled from '../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../observability/public';
+import { QuerySuggestion, QuerySuggestionTypes } from '../../../../../../src/plugins/data/public';
 
 interface Props {
   isSelected?: boolean;
@@ -57,7 +56,9 @@ const SuggestionItemField = euiStyled.div`
   padding: ${props => props.theme.eui.euiSizeXS};
 `;
 
-const SuggestionItemIconField = euiStyled(SuggestionItemField)<{ suggestionType: string }>`
+const SuggestionItemIconField = euiStyled(SuggestionItemField)<{
+  suggestionType: QuerySuggestionTypes;
+}>`
   background-color: ${props =>
     transparentize(0.9, getEuiIconColor(props.theme, props.suggestionType))};
   color: ${props => getEuiIconColor(props.theme, props.suggestionType)};
@@ -83,34 +84,34 @@ const SuggestionItemDescriptionField = euiStyled(SuggestionItemField)`
   }
 `;
 
-const getEuiIconType = (suggestionType: string) => {
+const getEuiIconType = (suggestionType: QuerySuggestionTypes) => {
   switch (suggestionType) {
-    case 'field':
+    case QuerySuggestionTypes.Field:
       return 'kqlField';
-    case 'value':
+    case QuerySuggestionTypes.Value:
       return 'kqlValue';
-    case 'recentSearch':
+    case QuerySuggestionTypes.RecentSearch:
       return 'search';
-    case 'conjunction':
+    case QuerySuggestionTypes.Conjunction:
       return 'kqlSelector';
-    case 'operator':
+    case QuerySuggestionTypes.Operator:
       return 'kqlOperand';
     default:
       return 'empty';
   }
 };
 
-const getEuiIconColor = (theme: any, suggestionType: string): string => {
+const getEuiIconColor = (theme: any, suggestionType: QuerySuggestionTypes): string => {
   switch (suggestionType) {
-    case 'field':
+    case QuerySuggestionTypes.Field:
       return theme.eui.euiColorVis7;
-    case 'value':
+    case QuerySuggestionTypes.Value:
       return theme.eui.euiColorVis0;
-    case 'operator':
+    case QuerySuggestionTypes.Operator:
       return theme.eui.euiColorVis1;
-    case 'conjunction':
+    case QuerySuggestionTypes.Conjunction:
       return theme.eui.euiColorVis2;
-    case 'recentSearch':
+    case QuerySuggestionTypes.RecentSearch:
     default:
       return theme.eui.euiColorMediumShade;
   }
diff --git a/x-pack/legacy/plugins/infra/public/components/beta_badge.tsx b/x-pack/plugins/infra/public/components/beta_badge.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/beta_badge.tsx
rename to x-pack/plugins/infra/public/components/beta_badge.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/document_title.tsx b/x-pack/plugins/infra/public/components/document_title.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/document_title.tsx
rename to x-pack/plugins/infra/public/components/document_title.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/empty_states/index.tsx b/x-pack/plugins/infra/public/components/empty_states/index.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/empty_states/index.tsx
rename to x-pack/plugins/infra/public/components/empty_states/index.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/empty_states/no_data.tsx b/x-pack/plugins/infra/public/components/empty_states/no_data.tsx
similarity index 93%
rename from x-pack/legacy/plugins/infra/public/components/empty_states/no_data.tsx
rename to x-pack/plugins/infra/public/components/empty_states/no_data.tsx
index 7519e3fe10779..97dc7ac1f8520 100644
--- a/x-pack/legacy/plugins/infra/public/components/empty_states/no_data.tsx
+++ b/x-pack/plugins/infra/public/components/empty_states/no_data.tsx
@@ -7,7 +7,7 @@
 import { EuiButton, EuiEmptyPrompt } from '@elastic/eui';
 import React from 'react';
 
-import euiStyled from '../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../observability/public';
 
 interface NoDataProps {
   titleText: string;
diff --git a/x-pack/legacy/plugins/infra/public/components/empty_states/no_indices.tsx b/x-pack/plugins/infra/public/components/empty_states/no_indices.tsx
similarity index 91%
rename from x-pack/legacy/plugins/infra/public/components/empty_states/no_indices.tsx
rename to x-pack/plugins/infra/public/components/empty_states/no_indices.tsx
index bfe282d2cee04..b453a4f0bc342 100644
--- a/x-pack/legacy/plugins/infra/public/components/empty_states/no_indices.tsx
+++ b/x-pack/plugins/infra/public/components/empty_states/no_indices.tsx
@@ -7,7 +7,7 @@
 import { EuiEmptyPrompt } from '@elastic/eui';
 import React from 'react';
 
-import euiStyled from '../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../observability/public';
 
 interface NoIndicesProps {
   message: string;
diff --git a/x-pack/legacy/plugins/infra/public/components/error_page.tsx b/x-pack/plugins/infra/public/components/error_page.tsx
similarity index 96%
rename from x-pack/legacy/plugins/infra/public/components/error_page.tsx
rename to x-pack/plugins/infra/public/components/error_page.tsx
index fea76c83292c2..5c8b576c161d7 100644
--- a/x-pack/legacy/plugins/infra/public/components/error_page.tsx
+++ b/x-pack/plugins/infra/public/components/error_page.tsx
@@ -15,7 +15,7 @@ import {
 import { FormattedMessage } from '@kbn/i18n/react';
 import React from 'react';
 
-import euiStyled from '../../../../common/eui_styled_components';
+import { euiStyled } from '../../../observability/public';
 import { FlexPage } from './page';
 
 interface Props {
diff --git a/x-pack/legacy/plugins/infra/public/components/eui/index.ts b/x-pack/plugins/infra/public/components/eui/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/eui/index.ts
rename to x-pack/plugins/infra/public/components/eui/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/components/eui/toolbar/index.ts b/x-pack/plugins/infra/public/components/eui/toolbar/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/eui/toolbar/index.ts
rename to x-pack/plugins/infra/public/components/eui/toolbar/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/components/eui/toolbar/toolbar.tsx b/x-pack/plugins/infra/public/components/eui/toolbar/toolbar.tsx
similarity index 88%
rename from x-pack/legacy/plugins/infra/public/components/eui/toolbar/toolbar.tsx
rename to x-pack/plugins/infra/public/components/eui/toolbar/toolbar.tsx
index 2c3b5cb924e36..8446587e8671d 100644
--- a/x-pack/legacy/plugins/infra/public/components/eui/toolbar/toolbar.tsx
+++ b/x-pack/plugins/infra/public/components/eui/toolbar/toolbar.tsx
@@ -6,7 +6,7 @@
 
 import { EuiPanel } from '@elastic/eui';
 
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 
 export const Toolbar = euiStyled(EuiPanel).attrs(() => ({
   grow: false,
diff --git a/x-pack/legacy/plugins/infra/public/components/fixed_datepicker.tsx b/x-pack/plugins/infra/public/components/fixed_datepicker.tsx
similarity index 90%
rename from x-pack/legacy/plugins/infra/public/components/fixed_datepicker.tsx
rename to x-pack/plugins/infra/public/components/fixed_datepicker.tsx
index aab1bcd1da873..fcebb89561826 100644
--- a/x-pack/legacy/plugins/infra/public/components/fixed_datepicker.tsx
+++ b/x-pack/plugins/infra/public/components/fixed_datepicker.tsx
@@ -7,7 +7,7 @@
 import React from 'react';
 
 import { EuiDatePicker, EuiDatePickerProps } from '@elastic/eui';
-import euiStyled from '../../../../common/eui_styled_components';
+import { euiStyled } from '../../../observability/public';
 
 export const FixedDatePicker = euiStyled(
   ({
diff --git a/x-pack/legacy/plugins/infra/public/components/formatted_time.tsx b/x-pack/plugins/infra/public/components/formatted_time.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/formatted_time.tsx
rename to x-pack/plugins/infra/public/components/formatted_time.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/header/header.tsx b/x-pack/plugins/infra/public/components/header/header.tsx
similarity index 94%
rename from x-pack/legacy/plugins/infra/public/components/header/header.tsx
rename to x-pack/plugins/infra/public/components/header/header.tsx
index 731e62b927ae4..fa71426f83645 100644
--- a/x-pack/legacy/plugins/infra/public/components/header/header.tsx
+++ b/x-pack/plugins/infra/public/components/header/header.tsx
@@ -7,7 +7,7 @@
 import { useCallback, useEffect } from 'react';
 import { i18n } from '@kbn/i18n';
 import { ChromeBreadcrumb } from 'src/core/public';
-import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
+import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
 
 interface HeaderProps {
   breadcrumbs?: ChromeBreadcrumb[];
diff --git a/x-pack/legacy/plugins/infra/public/components/header/index.ts b/x-pack/plugins/infra/public/components/header/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/header/index.ts
rename to x-pack/plugins/infra/public/components/header/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/components/help_center_content.tsx b/x-pack/plugins/infra/public/components/help_center_content.tsx
similarity index 90%
rename from x-pack/legacy/plugins/infra/public/components/help_center_content.tsx
rename to x-pack/plugins/infra/public/components/help_center_content.tsx
index 4b27c6cfce53f..acaf4b069dc1c 100644
--- a/x-pack/legacy/plugins/infra/public/components/help_center_content.tsx
+++ b/x-pack/plugins/infra/public/components/help_center_content.tsx
@@ -5,7 +5,7 @@
  */
 
 import React, { useEffect } from 'react';
-import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
+import { useKibana } from '../../../../../src/plugins/kibana_react/public';
 
 interface HelpCenterContentProps {
   feedbackLink: string;
diff --git a/x-pack/legacy/plugins/infra/public/components/inventory/layout.tsx b/x-pack/plugins/infra/public/components/inventory/layout.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/inventory/layout.tsx
rename to x-pack/plugins/infra/public/components/inventory/layout.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/inventory/toolbars/toolbar.tsx b/x-pack/plugins/infra/public/components/inventory/toolbars/toolbar.tsx
similarity index 97%
rename from x-pack/legacy/plugins/infra/public/components/inventory/toolbars/toolbar.tsx
rename to x-pack/plugins/infra/public/components/inventory/toolbars/toolbar.tsx
index ee0eceee6d157..a2882a3cd3124 100644
--- a/x-pack/legacy/plugins/infra/public/components/inventory/toolbars/toolbar.tsx
+++ b/x-pack/plugins/infra/public/components/inventory/toolbars/toolbar.tsx
@@ -17,7 +17,7 @@ import { InfraGroupByOptions } from '../../../lib/lib';
 import { WithWaffleViewState } from '../../../containers/waffle/with_waffle_view_state';
 import { SavedViewsToolbarControls } from '../../saved_views/toolbar_control';
 import { inventoryViewSavedObjectType } from '../../../../common/saved_objects/inventory_view';
-import { IIndexPattern } from '../../../../../../../../src/plugins/data/public';
+import { IIndexPattern } from '../../../../../../../src/plugins/data/public';
 import { InventoryItemType } from '../../../../common/inventory_models/types';
 
 export interface ToolbarProps {
diff --git a/x-pack/legacy/plugins/infra/public/components/inventory/toolbars/toolbar_wrapper.tsx b/x-pack/plugins/infra/public/components/inventory/toolbars/toolbar_wrapper.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/inventory/toolbars/toolbar_wrapper.tsx
rename to x-pack/plugins/infra/public/components/inventory/toolbars/toolbar_wrapper.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/loading/__examples__/index.stories.tsx b/x-pack/plugins/infra/public/components/loading/__examples__/index.stories.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/loading/__examples__/index.stories.tsx
rename to x-pack/plugins/infra/public/components/loading/__examples__/index.stories.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/loading/index.tsx b/x-pack/plugins/infra/public/components/loading/index.tsx
similarity index 94%
rename from x-pack/legacy/plugins/infra/public/components/loading/index.tsx
rename to x-pack/plugins/infra/public/components/loading/index.tsx
index 30dfef9ed48bd..8c25dfc2380f9 100644
--- a/x-pack/legacy/plugins/infra/public/components/loading/index.tsx
+++ b/x-pack/plugins/infra/public/components/loading/index.tsx
@@ -7,7 +7,7 @@
 import { EuiLoadingChart, EuiPanel, EuiText } from '@elastic/eui';
 import * as React from 'react';
 
-import euiStyled from '../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../observability/public';
 
 interface InfraLoadingProps {
   text: string | JSX.Element;
diff --git a/x-pack/legacy/plugins/infra/public/components/loading_overlay_wrapper.tsx b/x-pack/plugins/infra/public/components/loading_overlay_wrapper.tsx
similarity index 94%
rename from x-pack/legacy/plugins/infra/public/components/loading_overlay_wrapper.tsx
rename to x-pack/plugins/infra/public/components/loading_overlay_wrapper.tsx
index 5df1fc07e83b9..599969d9b15a8 100644
--- a/x-pack/legacy/plugins/infra/public/components/loading_overlay_wrapper.tsx
+++ b/x-pack/plugins/infra/public/components/loading_overlay_wrapper.tsx
@@ -8,7 +8,7 @@ import { EuiLoadingSpinner } from '@elastic/eui';
 import { transparentize } from 'polished';
 import React from 'react';
 
-import { euiStyled } from '../../../../common/eui_styled_components';
+import { euiStyled } from '../../../observability/public';
 
 export const LoadingOverlayWrapper: React.FC<React.HTMLAttributes<HTMLDivElement> & {
   isLoading: boolean;
diff --git a/x-pack/legacy/plugins/infra/public/components/loading_page.tsx b/x-pack/plugins/infra/public/components/loading_page.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/loading_page.tsx
rename to x-pack/plugins/infra/public/components/loading_page.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/index.ts b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/index.ts
rename to x-pack/plugins/infra/public/components/logging/log_analysis_job_status/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx
rename to x-pack/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_definition_outdated_callout.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/job_definition_outdated_callout.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_definition_outdated_callout.tsx
rename to x-pack/plugins/infra/public/components/logging/log_analysis_job_status/job_definition_outdated_callout.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_stopped_callout.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/job_stopped_callout.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_stopped_callout.tsx
rename to x-pack/plugins/infra/public/components/logging/log_analysis_job_status/job_stopped_callout.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/log_analysis_job_problem_indicator.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/log_analysis_job_problem_indicator.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/log_analysis_job_problem_indicator.tsx
rename to x-pack/plugins/infra/public/components/logging/log_analysis_job_status/log_analysis_job_problem_indicator.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/recreate_job_button.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/recreate_job_button.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/recreate_job_button.tsx
rename to x-pack/plugins/infra/public/components/logging/log_analysis_job_status/recreate_job_button.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/recreate_job_callout.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/recreate_job_callout.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/recreate_job_callout.tsx
rename to x-pack/plugins/infra/public/components/logging/log_analysis_job_status/recreate_job_callout.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx
similarity index 93%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx
rename to x-pack/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx
index 9fbba94407dc0..6b946898f6330 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_analysis_results/analyze_in_ml_button.tsx
@@ -10,9 +10,9 @@ import React from 'react';
 import { encode } from 'rison-node';
 import url from 'url';
 import { stringify } from 'query-string';
-import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public';
+import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
 import { TimeRange } from '../../../../common/http_api/shared/time_range';
-import { url as urlUtils } from '../../../../../../../../src/plugins/kibana_utils/public';
+import { url as urlUtils } from '../../../../../../../src/plugins/kibana_utils/public';
 
 export const AnalyzeInMlButton: React.FunctionComponent<{
   jobId: string;
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_results/first_use_callout.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_results/first_use_callout.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_analysis_results/first_use_callout.tsx
rename to x-pack/plugins/infra/public/components/logging/log_analysis_results/first_use_callout.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_results/index.ts b/x-pack/plugins/infra/public/components/logging/log_analysis_results/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_analysis_results/index.ts
rename to x-pack/plugins/infra/public/components/logging/log_analysis_results/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/index.ts b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/index.ts
rename to x-pack/plugins/infra/public/components/logging/log_analysis_setup/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/analysis_setup_indices_form.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/analysis_setup_indices_form.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/analysis_setup_indices_form.tsx
rename to x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/analysis_setup_indices_form.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/analysis_setup_timerange_form.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/analysis_setup_timerange_form.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/analysis_setup_timerange_form.tsx
rename to x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/analysis_setup_timerange_form.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/index.ts b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/index.ts
rename to x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/initial_configuration_step.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/initial_configuration_step.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/initial_configuration_step.tsx
rename to x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/initial_configuration_step.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/validation.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/validation.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/validation.tsx
rename to x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/validation.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/missing_results_privileges_prompt.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/missing_results_privileges_prompt.tsx
similarity index 94%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/missing_results_privileges_prompt.tsx
rename to x-pack/plugins/infra/public/components/logging/log_analysis_setup/missing_results_privileges_prompt.tsx
index 0c3393b0e15e3..2d378508e2b58 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/missing_results_privileges_prompt.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/missing_results_privileges_prompt.tsx
@@ -8,7 +8,7 @@ import { EuiEmptyPrompt, EuiCode } from '@elastic/eui';
 import { FormattedMessage } from '@kbn/i18n/react';
 import React from 'react';
 
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 import { UserManagementLink } from './user_management_link';
 
 export const MissingResultsPrivilegesPrompt: React.FunctionComponent = () => (
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/missing_setup_privileges_prompt.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/missing_setup_privileges_prompt.tsx
similarity index 94%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/missing_setup_privileges_prompt.tsx
rename to x-pack/plugins/infra/public/components/logging/log_analysis_setup/missing_setup_privileges_prompt.tsx
index 1549ab9120777..db89ff415a6f7 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/missing_setup_privileges_prompt.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/missing_setup_privileges_prompt.tsx
@@ -8,7 +8,7 @@ import { EuiEmptyPrompt, EuiCode } from '@elastic/eui';
 import { FormattedMessage } from '@kbn/i18n/react';
 import React from 'react';
 
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 import { UserManagementLink } from './user_management_link';
 
 export const MissingSetupPrivilegesPrompt: React.FunctionComponent = () => (
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/ml_unavailable_prompt.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/ml_unavailable_prompt.tsx
similarity index 95%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/ml_unavailable_prompt.tsx
rename to x-pack/plugins/infra/public/components/logging/log_analysis_setup/ml_unavailable_prompt.tsx
index 63e1bb23a2d82..4b15ce19ef096 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/ml_unavailable_prompt.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/ml_unavailable_prompt.tsx
@@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n';
 import { FormattedMessage } from '@kbn/i18n/react';
 import React from 'react';
 
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 
 export const MlUnavailablePrompt: React.FunctionComponent<{}> = () => (
   <EmptyPrompt
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/process_step/create_ml_jobs_button.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/process_step/create_ml_jobs_button.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/process_step/create_ml_jobs_button.tsx
rename to x-pack/plugins/infra/public/components/logging/log_analysis_setup/process_step/create_ml_jobs_button.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/process_step/index.ts b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/process_step/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/process_step/index.ts
rename to x-pack/plugins/infra/public/components/logging/log_analysis_setup/process_step/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/process_step/process_step.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/process_step/process_step.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/process_step/process_step.tsx
rename to x-pack/plugins/infra/public/components/logging/log_analysis_setup/process_step/process_step.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/process_step/recreate_ml_jobs_button.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/process_step/recreate_ml_jobs_button.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/process_step/recreate_ml_jobs_button.tsx
rename to x-pack/plugins/infra/public/components/logging/log_analysis_setup/process_step/recreate_ml_jobs_button.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/setup_page.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_page.tsx
similarity index 95%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/setup_page.tsx
rename to x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_page.tsx
index a1c35aff0cf83..b4e1b7448640c 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/setup_page.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_page.tsx
@@ -16,7 +16,7 @@ import {
 } from '@elastic/eui';
 import React from 'react';
 
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 
 export const LogAnalysisSetupPage: React.FunctionComponent<CommonProps> = ({
   children,
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/setup_status_unknown_prompt.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_status_unknown_prompt.tsx
similarity index 93%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/setup_status_unknown_prompt.tsx
rename to x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_status_unknown_prompt.tsx
index f65ff6a1eec78..334aaa251f524 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/setup_status_unknown_prompt.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_status_unknown_prompt.tsx
@@ -8,7 +8,7 @@ import React from 'react';
 import { i18n } from '@kbn/i18n';
 import { FormattedMessage } from '@kbn/i18n/react';
 import { EuiEmptyPrompt, EuiButton } from '@elastic/eui';
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 
 interface Props {
   retry: () => void;
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/user_management_link.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/user_management_link.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_analysis_setup/user_management_link.tsx
rename to x-pack/plugins/infra/public/components/logging/log_analysis_setup/user_management_link.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_customization_menu.tsx b/x-pack/plugins/infra/public/components/logging/log_customization_menu.tsx
similarity index 95%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_customization_menu.tsx
rename to x-pack/plugins/infra/public/components/logging/log_customization_menu.tsx
index 9ca0d4329a90b..febfdddfcfb0e 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_customization_menu.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_customization_menu.tsx
@@ -8,7 +8,7 @@ import { EuiButtonEmpty, EuiPopover } from '@elastic/eui';
 import { FormattedMessage } from '@kbn/i18n/react';
 import * as React from 'react';
 
-import euiStyled from '../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../observability/public';
 
 interface LogCustomizationMenuState {
   isShown: boolean;
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_entry_flyout/index.tsx b/x-pack/plugins/infra/public/components/logging/log_entry_flyout/index.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_entry_flyout/index.tsx
rename to x-pack/plugins/infra/public/components/logging/log_entry_flyout/index.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_entry_flyout/log_entry_actions_menu.test.tsx b/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_actions_menu.test.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_entry_flyout/log_entry_actions_menu.test.tsx
rename to x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_actions_menu.test.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_entry_flyout/log_entry_actions_menu.tsx b/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_actions_menu.tsx
similarity index 95%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_entry_flyout/log_entry_actions_menu.tsx
rename to x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_actions_menu.tsx
index 4137027f35c12..60e50f486d22a 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_entry_flyout/log_entry_actions_menu.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_actions_menu.tsx
@@ -8,9 +8,9 @@ import { EuiButtonEmpty, EuiContextMenuItem, EuiContextMenuPanel, EuiPopover } f
 import { FormattedMessage } from '@kbn/i18n/react';
 import React, { useMemo } from 'react';
 import url from 'url';
-import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public';
+import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
 import { useVisibilityState } from '../../../utils/use_visibility_state';
-import { getTraceUrl } from '../../../../../apm/public/components/shared/Links/apm/ExternalLinks';
+import { getTraceUrl } from '../../../../../../legacy/plugins/apm/public/components/shared/Links/apm/ExternalLinks';
 import { LogEntriesItem } from '../../../../common/http_api';
 
 const UPTIME_FIELDS = ['container.id', 'host.ip', 'kubernetes.pod.uid'];
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx b/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx
similarity index 98%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx
rename to x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx
index d2cb9cf9370dd..57f27ee76184b 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx
@@ -20,7 +20,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
 import moment from 'moment';
 import React, { useCallback, useMemo } from 'react';
 
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 import { TimeKey } from '../../../../common/time';
 import { InfraLoadingPanel } from '../../loading';
 import { LogEntryActionsMenu } from './log_entry_actions_menu';
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_highlights_menu.tsx b/x-pack/plugins/infra/public/components/logging/log_highlights_menu.tsx
similarity index 98%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_highlights_menu.tsx
rename to x-pack/plugins/infra/public/components/logging/log_highlights_menu.tsx
index d13ccde7466cd..f51ed693e7d80 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_highlights_menu.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_highlights_menu.tsx
@@ -18,7 +18,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
 import { debounce } from 'lodash';
 import React, { useCallback, useMemo, useState } from 'react';
 
-import euiStyled from '../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../observability/public';
 import { useVisibilityState } from '../../utils/use_visibility_state';
 
 interface LogHighlightsMenuProps {
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/density_chart.tsx b/x-pack/plugins/infra/public/components/logging/log_minimap/density_chart.tsx
similarity index 95%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_minimap/density_chart.tsx
rename to x-pack/plugins/infra/public/components/logging/log_minimap/density_chart.tsx
index b31afe6abea28..729689e65739e 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/density_chart.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_minimap/density_chart.tsx
@@ -6,10 +6,10 @@
 
 import { scaleLinear, scaleTime } from 'd3-scale';
 import { area, curveMonotoneY } from 'd3-shape';
-import max from 'lodash/fp/max';
+import { max } from 'lodash';
 import * as React from 'react';
 
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 import { SummaryBucket } from './types';
 
 interface DensityChartProps {
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/highlighted_interval.tsx b/x-pack/plugins/infra/public/components/logging/log_minimap/highlighted_interval.tsx
similarity index 95%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_minimap/highlighted_interval.tsx
rename to x-pack/plugins/infra/public/components/logging/log_minimap/highlighted_interval.tsx
index 4711a7ac6ffde..2e45bcea42109 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/highlighted_interval.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_minimap/highlighted_interval.tsx
@@ -6,7 +6,7 @@
 
 import * as React from 'react';
 
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 
 interface HighlightedIntervalProps {
   className?: string;
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/index.ts b/x-pack/plugins/infra/public/components/logging/log_minimap/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_minimap/index.ts
rename to x-pack/plugins/infra/public/components/logging/log_minimap/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx b/x-pack/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx
similarity index 99%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx
rename to x-pack/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx
index 75d8c5a47d32d..e3a7e5aa30633 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_minimap/log_minimap.tsx
@@ -7,7 +7,7 @@
 import { scaleLinear } from 'd3-scale';
 import * as React from 'react';
 
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 import { LogEntryTime } from '../../../../common/log_entry';
 import { DensityChart } from './density_chart';
 import { HighlightedInterval } from './highlighted_interval';
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/search_marker.tsx b/x-pack/plugins/infra/public/components/logging/log_minimap/search_marker.tsx
similarity index 97%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_minimap/search_marker.tsx
rename to x-pack/plugins/infra/public/components/logging/log_minimap/search_marker.tsx
index 5b661562a451e..8b87aa15f16f0 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/search_marker.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_minimap/search_marker.tsx
@@ -7,7 +7,7 @@
 import { FormattedMessage } from '@kbn/i18n/react';
 import * as React from 'react';
 
-import euiStyled, { keyframes } from '../../../../../../common/eui_styled_components';
+import { euiStyled, keyframes } from '../../../../../observability/public';
 import { LogEntryTime } from '../../../../common/log_entry';
 import { SearchMarkerTooltip } from './search_marker_tooltip';
 import { SummaryHighlightBucket } from './types';
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/search_marker_tooltip.tsx b/x-pack/plugins/infra/public/components/logging/log_minimap/search_marker_tooltip.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_minimap/search_marker_tooltip.tsx
rename to x-pack/plugins/infra/public/components/logging/log_minimap/search_marker_tooltip.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/search_markers.tsx b/x-pack/plugins/infra/public/components/logging/log_minimap/search_markers.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_minimap/search_markers.tsx
rename to x-pack/plugins/infra/public/components/logging/log_minimap/search_markers.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/time_ruler.tsx b/x-pack/plugins/infra/public/components/logging/log_minimap/time_ruler.tsx
similarity index 96%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_minimap/time_ruler.tsx
rename to x-pack/plugins/infra/public/components/logging/log_minimap/time_ruler.tsx
index c72403539563d..b610737663e8d 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/time_ruler.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_minimap/time_ruler.tsx
@@ -7,7 +7,7 @@
 import { scaleTime } from 'd3-scale';
 import * as React from 'react';
 
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 
 interface TimeRulerProps {
   end: number;
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap/types.ts b/x-pack/plugins/infra/public/components/logging/log_minimap/types.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_minimap/types.ts
rename to x-pack/plugins/infra/public/components/logging/log_minimap/types.ts
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_minimap_scale_controls.tsx b/x-pack/plugins/infra/public/components/logging/log_minimap_scale_controls.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_minimap_scale_controls.tsx
rename to x-pack/plugins/infra/public/components/logging/log_minimap_scale_controls.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_search_controls/index.ts b/x-pack/plugins/infra/public/components/logging/log_search_controls/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_search_controls/index.ts
rename to x-pack/plugins/infra/public/components/logging/log_search_controls/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_search_controls/log_search_buttons.tsx b/x-pack/plugins/infra/public/components/logging/log_search_controls/log_search_buttons.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_search_controls/log_search_buttons.tsx
rename to x-pack/plugins/infra/public/components/logging/log_search_controls/log_search_buttons.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_search_controls/log_search_controls.tsx b/x-pack/plugins/infra/public/components/logging/log_search_controls/log_search_controls.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_search_controls/log_search_controls.tsx
rename to x-pack/plugins/infra/public/components/logging/log_search_controls/log_search_controls.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_search_controls/log_search_input.tsx b/x-pack/plugins/infra/public/components/logging/log_search_controls/log_search_input.tsx
similarity index 96%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_search_controls/log_search_input.tsx
rename to x-pack/plugins/infra/public/components/logging/log_search_controls/log_search_input.tsx
index c5eedef83e824..a5277260d56e0 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_search_controls/log_search_input.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_search_controls/log_search_input.tsx
@@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n';
 import classNames from 'classnames';
 import * as React from 'react';
 
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 
 interface LogSearchInputProps {
   className?: string;
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_statusbar.tsx b/x-pack/plugins/infra/public/components/logging/log_statusbar.tsx
similarity index 91%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_statusbar.tsx
rename to x-pack/plugins/infra/public/components/logging/log_statusbar.tsx
index 4bda5a5a4b009..8a0f1290c2af3 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_statusbar.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_statusbar.tsx
@@ -6,7 +6,7 @@
 
 import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
 
-import euiStyled from '../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../observability/public';
 
 export const LogStatusbar = euiStyled(EuiFlexGroup).attrs(() => ({
   alignItems: 'center',
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_scale_controls.tsx b/x-pack/plugins/infra/public/components/logging/log_text_scale_controls.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_text_scale_controls.tsx
rename to x-pack/plugins/infra/public/components/logging/log_text_scale_controls.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/column_headers.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/column_headers.tsx
similarity index 98%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/column_headers.tsx
rename to x-pack/plugins/infra/public/components/logging/log_text_stream/column_headers.tsx
index 6516993397e02..72d6aea5ecfc6 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/column_headers.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/column_headers.tsx
@@ -7,7 +7,7 @@
 import React, { useContext } from 'react';
 import { transparentize } from 'polished';
 
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 import {
   LogColumnConfiguration,
   isTimestampLogColumnConfiguration,
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/highlighting.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/highlighting.tsx
similarity index 96%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/highlighting.tsx
rename to x-pack/plugins/infra/public/components/logging/log_text_stream/highlighting.tsx
index a48f3c96958be..a6cb8def4f6c4 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/highlighting.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/highlighting.tsx
@@ -6,7 +6,7 @@
 
 import React from 'react';
 
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 import { chooseLightOrDarkColor, tintOrShade } from '../../../utils/styles';
 
 export const ActiveHighlightMarker = euiStyled.mark`
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/index.ts b/x-pack/plugins/infra/public/components/logging/log_text_stream/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/index.ts
rename to x-pack/plugins/infra/public/components/logging/log_text_stream/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/item.ts b/x-pack/plugins/infra/public/components/logging/log_text_stream/item.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/item.ts
rename to x-pack/plugins/infra/public/components/logging/log_text_stream/item.ts
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/jump_to_tail.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/jump_to_tail.tsx
similarity index 95%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/jump_to_tail.tsx
rename to x-pack/plugins/infra/public/components/logging/log_text_stream/jump_to_tail.tsx
index 05f85ceed49c0..50595cfe971d8 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/jump_to_tail.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/jump_to_tail.tsx
@@ -10,7 +10,7 @@ import { EuiButtonEmpty, EuiText } from '@elastic/eui';
 import { FormattedMessage } from '@kbn/i18n/react';
 import * as React from 'react';
 
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 
 interface LogTextStreamJumpToTailProps {
   onClickJump?: () => void;
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx
similarity index 98%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx
rename to x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx
index 4cefbea7225ec..8c48d9e176d3b 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/loading_item_view.tsx
@@ -10,7 +10,7 @@ import { EuiButtonEmpty, EuiIcon, EuiProgress, EuiText } from '@elastic/eui';
 import { FormattedMessage, FormattedRelative } from '@kbn/i18n/react';
 import * as React from 'react';
 
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 
 interface LogTextStreamLoadingItemViewProps {
   alignment: 'top' | 'bottom';
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_date_row.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_date_row.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_date_row.tsx
rename to x-pack/plugins/infra/public/components/logging/log_text_stream/log_date_row.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_column.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_column.tsx
similarity index 97%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_column.tsx
rename to x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_column.tsx
index 643f98018cb0a..bcb5400c72209 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_column.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_column.tsx
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 import {
   LogColumnConfiguration,
   isMessageLogColumnConfiguration,
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx
similarity index 97%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx
rename to x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx
index f947a0fb1adcd..8589f82ba15dd 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.test.tsx
@@ -7,7 +7,7 @@
 import { mount } from 'enzyme';
 import React from 'react';
 
-import { EuiThemeProvider } from '../../../../../../common/eui_styled_components';
+import { EuiThemeProvider } from '../../../../../observability/public';
 import { LogEntryColumn } from '../../../utils/log_entry';
 import { LogEntryFieldColumn } from './log_entry_field_column';
 
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx
similarity index 97%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx
rename to x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx
index 6252b3a396d1b..705a7f4c12de4 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx
@@ -8,7 +8,7 @@ import stringify from 'json-stable-stringify';
 import { darken, transparentize } from 'polished';
 import React, { useMemo } from 'react';
 
-import euiStyled, { css } from '../../../../../../common/eui_styled_components';
+import { euiStyled, css } from '../../../../../observability/public';
 import {
   isFieldColumn,
   isHighlightFieldColumn,
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_icon_column.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_icon_column.tsx
similarity index 96%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_icon_column.tsx
rename to x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_icon_column.tsx
index f7d841bcce94f..a4099cdf5a1fb 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_icon_column.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_icon_column.tsx
@@ -10,7 +10,7 @@ import React from 'react';
 
 import { LogEntryColumnContent } from './log_entry_column';
 import { hoveredContentStyle } from './text_styles';
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 
 interface LogEntryIconColumnProps {
   isHighlighted: boolean;
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.tsx
similarity index 97%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.tsx
rename to x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.tsx
index 11d73736463e2..10bc2a7b4597a 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_message_column.tsx
@@ -6,7 +6,7 @@
 
 import React, { memo, useMemo } from 'react';
 
-import euiStyled, { css } from '../../../../../../common/eui_styled_components';
+import { euiStyled, css } from '../../../../../observability/public';
 import {
   isConstantSegment,
   isFieldSegment,
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx
similarity index 98%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx
rename to x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx
index 0da601ae52088..9c1a1bb5962e4 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_row.tsx
@@ -7,7 +7,7 @@
 // import { darken, transparentize } from 'polished';
 import React, { useState, useCallback, useMemo } from 'react';
 
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 import {
   LogEntry,
   LogEntryHighlight,
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_timestamp_column.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_timestamp_column.tsx
similarity index 95%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_timestamp_column.tsx
rename to x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_timestamp_column.tsx
index 8e161367b428d..e3c1e80a43a1a 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/log_entry_timestamp_column.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_timestamp_column.tsx
@@ -7,7 +7,7 @@
 import { darken, transparentize } from 'polished';
 import React, { memo } from 'react';
 
-import euiStyled, { css } from '../../../../../../common/eui_styled_components';
+import { euiStyled, css } from '../../../../../observability/public';
 import { useFormattedTime } from '../../formatted_time';
 import { LogEntryColumnContent } from './log_entry_column';
 
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/measurable_item_view.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/measurable_item_view.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/measurable_item_view.tsx
rename to x-pack/plugins/infra/public/components/logging/log_text_stream/measurable_item_view.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx
similarity index 99%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx
rename to x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx
index 15d3c83ffebe9..0bf121cf6c1eb 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/scrollable_log_text_stream_view.tsx
@@ -9,7 +9,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
 import React, { Fragment, useMemo } from 'react';
 import moment from 'moment';
 
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 import { TextScale } from '../../../../common/log_text_scale';
 import { TimeKey, UniqueTimeKey } from '../../../../common/time';
 import { callWithoutRepeats } from '../../../utils/handlers';
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/text_styles.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/text_styles.tsx
similarity index 96%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/text_styles.tsx
rename to x-pack/plugins/infra/public/components/logging/log_text_stream/text_styles.tsx
index e95ac6aa7923b..6857f94105dad 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/text_styles.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/text_styles.tsx
@@ -7,7 +7,7 @@
 import { darken, transparentize } from 'polished';
 import React, { useMemo, useState, useCallback } from 'react';
 
-import euiStyled, { css } from '../../../../../../common/eui_styled_components';
+import { euiStyled, css } from '../../../../../observability/public';
 import { TextScale } from '../../../../common/log_text_scale';
 
 export const monospaceTextStyle = (scale: TextScale) => css`
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/vertical_scroll_panel.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/vertical_scroll_panel.tsx
similarity index 91%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/vertical_scroll_panel.tsx
rename to x-pack/plugins/infra/public/components/logging/log_text_stream/vertical_scroll_panel.tsx
index a57c25bf77bc8..199f24f5d3f53 100644
--- a/x-pack/legacy/plugins/infra/public/components/logging/log_text_stream/vertical_scroll_panel.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/vertical_scroll_panel.tsx
@@ -5,11 +5,10 @@
  */
 
 import { bisector } from 'd3-array';
-import sortBy from 'lodash/fp/sortBy';
-import throttle from 'lodash/fp/throttle';
+import { sortBy, throttle } from 'lodash';
 import * as React from 'react';
 
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 import { Rect } from './measurable_item_view';
 
 interface VerticalScrollPanelProps<Child> {
@@ -57,17 +56,14 @@ export class VerticalScrollPanel<Child> extends React.PureComponent<
   public childDimensions = new Map<Child, Rect>();
   private nextScrollEventFromCenterTarget = false;
 
-  public handleScroll: React.UIEventHandler<HTMLDivElement> = throttle(
-    SCROLL_THROTTLE_INTERVAL,
-    () => {
-      // If this event was fired by the centerTarget method modifying the scrollTop,
-      // then don't send `fromScroll: true` to reportVisibleChildren. The rest of the
-      // app needs to respond differently depending on whether the user is scrolling through
-      // the pane manually, versus whether the pane is updating itself in response to new data
-      this.reportVisibleChildren(!this.nextScrollEventFromCenterTarget);
-      this.nextScrollEventFromCenterTarget = false;
-    }
-  );
+  public handleScroll: React.UIEventHandler<HTMLDivElement> = throttle(() => {
+    // If this event was fired by the centerTarget method modifying the scrollTop,
+    // then don't send `fromScroll: true` to reportVisibleChildren. The rest of the
+    // app needs to respond differently depending on whether the user is scrolling through
+    // the pane manually, versus whether the pane is updating itself in response to new data
+    this.reportVisibleChildren(!this.nextScrollEventFromCenterTarget);
+    this.nextScrollEventFromCenterTarget = false;
+  }, SCROLL_THROTTLE_INTERVAL);
 
   public registerChild = (key: any, element: MeasurableChild | null) => {
     if (element === null) {
@@ -79,7 +75,7 @@ export class VerticalScrollPanel<Child> extends React.PureComponent<
 
   public updateChildDimensions = () => {
     this.childDimensions = new Map<Child, Rect>(
-      sortDimensionsByTop(
+      sortBy<[any, Rect]>(
         Array.from(this.childRefs.entries()).reduce((accumulatedDimensions, [key, child]) => {
           const currentOffsetRect = child.getOffsetRect();
 
@@ -88,7 +84,8 @@ export class VerticalScrollPanel<Child> extends React.PureComponent<
           }
 
           return accumulatedDimensions;
-        }, [] as Array<[any, Rect]>)
+        }, [] as Array<[any, Rect]>),
+        '1.top'
       )
     );
   };
@@ -302,7 +299,5 @@ const getVisibleChildren = <Child extends {}>(
   };
 };
 
-const sortDimensionsByTop = sortBy<[any, Rect]>('1.top');
-
 const getChildIndexBefore = bisector<[any, Rect], number>(([key, rect]) => rect.top + rect.height)
   .left;
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_text_wrap_controls.tsx b/x-pack/plugins/infra/public/components/logging/log_text_wrap_controls.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_text_wrap_controls.tsx
rename to x-pack/plugins/infra/public/components/logging/log_text_wrap_controls.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_time_controls.tsx b/x-pack/plugins/infra/public/components/logging/log_time_controls.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/logging/log_time_controls.tsx
rename to x-pack/plugins/infra/public/components/logging/log_time_controls.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/aggregation.tsx b/x-pack/plugins/infra/public/components/metrics_explorer/aggregation.tsx
similarity index 96%
rename from x-pack/legacy/plugins/infra/public/components/metrics_explorer/aggregation.tsx
rename to x-pack/plugins/infra/public/components/metrics_explorer/aggregation.tsx
index 2abc03d6ea9b9..76fa519ab3756 100644
--- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/aggregation.tsx
+++ b/x-pack/plugins/infra/public/components/metrics_explorer/aggregation.tsx
@@ -8,7 +8,7 @@ import { EuiSelect } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
 
 import React, { useCallback } from 'react';
-import { MetricsExplorerAggregation } from '../../../server/routes/metrics_explorer/types';
+import { MetricsExplorerAggregation } from '../../../common/http_api/metrics_explorer';
 import { MetricsExplorerOptions } from '../../containers/metrics_explorer/use_metrics_explorer_options';
 import {
   metricsExplorerAggregationRT,
diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/chart.tsx b/x-pack/plugins/infra/public/components/metrics_explorer/chart.tsx
similarity index 95%
rename from x-pack/legacy/plugins/infra/public/components/metrics_explorer/chart.tsx
rename to x-pack/plugins/infra/public/components/metrics_explorer/chart.tsx
index f66ae867eef5a..43b08f45eed34 100644
--- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/chart.tsx
+++ b/x-pack/plugins/infra/public/components/metrics_explorer/chart.tsx
@@ -10,14 +10,14 @@ import { EuiTitle, EuiToolTip, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
 import { Axis, Chart, niceTimeFormatter, Position, Settings, TooltipValue } from '@elastic/charts';
 import { first, last } from 'lodash';
 import moment from 'moment';
-import { MetricsExplorerSeries } from '../../../server/routes/metrics_explorer/types';
+import { MetricsExplorerSeries } from '../../../common/http_api/metrics_explorer';
 import {
   MetricsExplorerOptions,
   MetricsExplorerTimeOptions,
   MetricsExplorerYAxisMode,
   MetricsExplorerChartOptions,
 } from '../../containers/metrics_explorer/use_metrics_explorer_options';
-import euiStyled from '../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../observability/public';
 import { createFormatterForMetric } from './helpers/create_formatter_for_metric';
 import { MetricExplorerSeriesChart } from './series_chart';
 import { MetricsExplorerChartContextMenu } from './chart_context_menu';
@@ -27,7 +27,7 @@ import { MetricsExplorerNoMetrics } from './no_metrics';
 import { getChartTheme } from './helpers/get_chart_theme';
 import { useKibanaUiSetting } from '../../utils/use_kibana_ui_setting';
 import { calculateDomain } from './helpers/calculate_domain';
-import { useKibana, useUiSetting } from '../../../../../../../src/plugins/kibana_react/public';
+import { useKibana, useUiSetting } from '../../../../../../src/plugins/kibana_react/public';
 
 interface Props {
   title?: string | null;
diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/chart_context_menu.test.tsx b/x-pack/plugins/infra/public/components/metrics_explorer/chart_context_menu.test.tsx
similarity index 61%
rename from x-pack/legacy/plugins/infra/public/components/metrics_explorer/chart_context_menu.test.tsx
rename to x-pack/plugins/infra/public/components/metrics_explorer/chart_context_menu.test.tsx
index 4dd46e9ef233a..9c3319d467ae2 100644
--- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/chart_context_menu.test.tsx
+++ b/x-pack/plugins/infra/public/components/metrics_explorer/chart_context_menu.test.tsx
@@ -5,12 +5,12 @@
  */
 
 import React from 'react';
-import { MetricsExplorerChartContextMenu, createNodeDetailLink } from './chart_context_menu';
-import { mount } from 'enzyme';
+import { MetricsExplorerChartContextMenu, createNodeDetailLink, Props } from './chart_context_menu';
+import { ReactWrapper, mount } from 'enzyme';
 import { options, source, timeRange, chartOptions } from '../../utils/fixtures/metrics_explorer';
 import DateMath from '@elastic/datemath';
-import { ReactWrapper } from 'enzyme';
 import { Capabilities } from 'src/core/public';
+import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
 
 const series = { id: 'exmaple-01', rows: [], columns: [] };
 const uiCapabilities: Capabilities = {
@@ -24,22 +24,36 @@ const getTestSubject = (component: ReactWrapper, name: string) => {
   return component.find(`[data-test-subj="${name}"]`).hostNodes();
 };
 
+const mountComponentWithProviders = (props: Props): ReactWrapper => {
+  const services = {
+    http: {
+      fetch: jest.fn(),
+    },
+    application: {
+      getUrlForApp: jest.fn(),
+    },
+  };
+
+  return mount(
+    <KibanaContextProvider services={services}>
+      <MetricsExplorerChartContextMenu {...props} />
+    </KibanaContextProvider>
+  );
+};
+
 describe('MetricsExplorerChartContextMenu', () => {
   describe('component', () => {
     it('should just work', async () => {
       const onFilter = jest.fn().mockImplementation((query: string) => void 0);
-      const component = mount(
-        <MetricsExplorerChartContextMenu
-          timeRange={timeRange}
-          source={source}
-          series={series}
-          options={options}
-          onFilter={onFilter}
-          uiCapabilities={uiCapabilities}
-          chartOptions={chartOptions}
-        />
-      );
-
+      const component = mountComponentWithProviders({
+        timeRange,
+        source,
+        series,
+        options,
+        onFilter,
+        uiCapabilities,
+        chartOptions,
+      });
       component.find('button').simulate('click');
       expect(getTestSubject(component, 'metricsExplorerAction-AddFilter').length).toBe(1);
       expect(getTestSubject(component, 'metricsExplorerAction-OpenInTSVB').length).toBe(1);
@@ -49,33 +63,28 @@ describe('MetricsExplorerChartContextMenu', () => {
     it('should not display View metrics for incompatible groupBy', async () => {
       const customOptions = { ...options, groupBy: 'system.network.name' };
       const onFilter = jest.fn().mockImplementation((query: string) => void 0);
-      const component = mount(
-        <MetricsExplorerChartContextMenu
-          timeRange={timeRange}
-          source={source}
-          series={series}
-          options={customOptions}
-          onFilter={onFilter}
-          uiCapabilities={uiCapabilities}
-          chartOptions={chartOptions}
-        />
-      );
+      const component = mountComponentWithProviders({
+        timeRange,
+        source,
+        series,
+        options: customOptions,
+        onFilter,
+        uiCapabilities,
+        chartOptions,
+      });
       component.find('button').simulate('click');
       expect(getTestSubject(component, 'metricsExplorerAction-ViewNodeMetrics').length).toBe(0);
     });
 
     it('should not display "Add Filter" without onFilter', async () => {
-      const component = mount(
-        <MetricsExplorerChartContextMenu
-          timeRange={timeRange}
-          source={source}
-          series={series}
-          options={options}
-          uiCapabilities={uiCapabilities}
-          chartOptions={chartOptions}
-        />
-      );
-
+      const component = mountComponentWithProviders({
+        timeRange,
+        source,
+        series,
+        options,
+        uiCapabilities,
+        chartOptions,
+      });
       component.find('button').simulate('click');
       expect(getTestSubject(component, 'metricsExplorerAction-AddFilter').length).toBe(0);
     });
@@ -83,35 +92,29 @@ describe('MetricsExplorerChartContextMenu', () => {
     it('should not display "Add Filter" without options.groupBy', async () => {
       const customOptions = { ...options, groupBy: void 0 };
       const onFilter = jest.fn().mockImplementation((query: string) => void 0);
-      const component = mount(
-        <MetricsExplorerChartContextMenu
-          timeRange={timeRange}
-          source={source}
-          series={series}
-          options={customOptions}
-          onFilter={onFilter}
-          uiCapabilities={uiCapabilities}
-          chartOptions={chartOptions}
-        />
-      );
-
+      const component = mountComponentWithProviders({
+        timeRange,
+        source,
+        series,
+        options: customOptions,
+        onFilter,
+        uiCapabilities,
+        chartOptions,
+      });
       component.find('button').simulate('click');
       expect(getTestSubject(component, 'metricsExplorerAction-AddFilter').length).toBe(0);
     });
 
     it('should disable "Open in Visualize" when options.metrics is empty', async () => {
       const customOptions = { ...options, metrics: [] };
-      const component = mount(
-        <MetricsExplorerChartContextMenu
-          timeRange={timeRange}
-          source={source}
-          series={series}
-          options={customOptions}
-          uiCapabilities={uiCapabilities}
-          chartOptions={chartOptions}
-        />
-      );
-
+      const component = mountComponentWithProviders({
+        timeRange,
+        source,
+        series,
+        options: customOptions,
+        uiCapabilities,
+        chartOptions,
+      });
       component.find('button').simulate('click');
       expect(
         getTestSubject(component, 'metricsExplorerAction-OpenInTSVB').prop('disabled')
@@ -121,17 +124,15 @@ describe('MetricsExplorerChartContextMenu', () => {
     it('should not display "Open in Visualize" when unavailble in uiCapabilities', async () => {
       const customUICapabilities = { ...uiCapabilities, visualize: { show: false } };
       const onFilter = jest.fn().mockImplementation((query: string) => void 0);
-      const component = mount(
-        <MetricsExplorerChartContextMenu
-          timeRange={timeRange}
-          source={source}
-          series={series}
-          options={options}
-          onFilter={onFilter}
-          uiCapabilities={customUICapabilities}
-          chartOptions={chartOptions}
-        />
-      );
+      const component = mountComponentWithProviders({
+        timeRange,
+        source,
+        series,
+        options,
+        onFilter,
+        uiCapabilities: customUICapabilities,
+        chartOptions,
+      });
 
       component.find('button').simulate('click');
       expect(getTestSubject(component, 'metricsExplorerAction-OpenInTSVB').length).toBe(0);
@@ -141,17 +142,15 @@ describe('MetricsExplorerChartContextMenu', () => {
       const customUICapabilities = { ...uiCapabilities, visualize: { show: false } };
       const onFilter = jest.fn().mockImplementation((query: string) => void 0);
       const customOptions = { ...options, groupBy: void 0 };
-      const component = mount(
-        <MetricsExplorerChartContextMenu
-          timeRange={timeRange}
-          source={source}
-          series={series}
-          options={customOptions}
-          onFilter={onFilter}
-          uiCapabilities={customUICapabilities}
-          chartOptions={chartOptions}
-        />
-      );
+      const component = mountComponentWithProviders({
+        timeRange,
+        source,
+        series,
+        options: customOptions,
+        onFilter,
+        uiCapabilities: customUICapabilities,
+        chartOptions,
+      });
       expect(component.find('button').length).toBe(0);
     });
   });
@@ -163,9 +162,7 @@ describe('MetricsExplorerChartContextMenu', () => {
       const to = DateMath.parse(toDateStrig, { roundUp: true })!;
       const from = DateMath.parse(fromDateStrig)!;
       const link = createNodeDetailLink('host', 'example-01', fromDateStrig, toDateStrig);
-      expect(link).toBe(
-        `#/link-to/host-detail/example-01?to=${to.valueOf()}&from=${from.valueOf()}`
-      );
+      expect(link).toBe(`link-to/host-detail/example-01?to=${to.valueOf()}&from=${from.valueOf()}`);
     });
   });
 });
diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/chart_context_menu.tsx b/x-pack/plugins/infra/public/components/metrics_explorer/chart_context_menu.tsx
similarity index 92%
rename from x-pack/legacy/plugins/infra/public/components/metrics_explorer/chart_context_menu.tsx
rename to x-pack/plugins/infra/public/components/metrics_explorer/chart_context_menu.tsx
index 298f7dd8f8d17..f7c97033f8d50 100644
--- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/chart_context_menu.tsx
+++ b/x-pack/plugins/infra/public/components/metrics_explorer/chart_context_menu.tsx
@@ -14,7 +14,7 @@ import {
 } from '@elastic/eui';
 import DateMath from '@elastic/datemath';
 import { Capabilities } from 'src/core/public';
-import { MetricsExplorerSeries } from '../../../server/routes/metrics_explorer/types';
+import { MetricsExplorerSeries } from '../../../common/http_api/metrics_explorer';
 import {
   MetricsExplorerOptions,
   MetricsExplorerTimeOptions,
@@ -24,8 +24,9 @@ import { createTSVBLink } from './helpers/create_tsvb_link';
 import { getNodeDetailUrl } from '../../pages/link_to/redirect_to_node_detail';
 import { SourceConfiguration } from '../../utils/source_configuration';
 import { InventoryItemType } from '../../../common/inventory_models/types';
+import { usePrefixPathWithBasepath } from '../../hooks/use_prefix_path_with_basepath';
 
-interface Props {
+export interface Props {
   options: MetricsExplorerOptions;
   onFilter?: (query: string) => void;
   series: MetricsExplorerSeries;
@@ -70,7 +71,7 @@ export const createNodeDetailLink = (
   });
 };
 
-export const MetricsExplorerChartContextMenu = ({
+export const MetricsExplorerChartContextMenu: React.FC<Props> = ({
   onFilter,
   options,
   series,
@@ -79,6 +80,7 @@ export const MetricsExplorerChartContextMenu = ({
   uiCapabilities,
   chartOptions,
 }: Props) => {
+  const urlPrefixer = usePrefixPathWithBasepath();
   const [isPopoverOpen, setPopoverState] = useState(false);
   const supportFiltering = options.groupBy != null && onFilter != null;
   const handleFilter = useCallback(() => {
@@ -115,7 +117,10 @@ export const MetricsExplorerChartContextMenu = ({
             values: { name: nodeType },
           }),
           icon: 'metricsApp',
-          href: createNodeDetailLink(nodeType, series.id, timeRange.from, timeRange.to),
+          href: urlPrefixer(
+            'metrics',
+            createNodeDetailLink(nodeType, series.id, timeRange.from, timeRange.to)
+          ),
           'data-test-subj': 'metricsExplorerAction-ViewNodeMetrics',
         },
       ]
diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/chart_options.tsx b/x-pack/plugins/infra/public/components/metrics_explorer/chart_options.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/metrics_explorer/chart_options.tsx
rename to x-pack/plugins/infra/public/components/metrics_explorer/chart_options.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/charts.tsx b/x-pack/plugins/infra/public/components/metrics_explorer/charts.tsx
similarity index 97%
rename from x-pack/legacy/plugins/infra/public/components/metrics_explorer/charts.tsx
rename to x-pack/plugins/infra/public/components/metrics_explorer/charts.tsx
index 32c9ac36b5c52..64e7b27f5722f 100644
--- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/charts.tsx
+++ b/x-pack/plugins/infra/public/components/metrics_explorer/charts.tsx
@@ -8,7 +8,7 @@ import { EuiButton, EuiFlexGrid, EuiFlexItem, EuiText, EuiHorizontalRule } from
 import { i18n } from '@kbn/i18n';
 import { FormattedMessage } from '@kbn/i18n/react';
 import React from 'react';
-import { MetricsExplorerResponse } from '../../../server/routes/metrics_explorer/types';
+import { MetricsExplorerResponse } from '../../../common/http_api/metrics_explorer';
 import {
   MetricsExplorerOptions,
   MetricsExplorerTimeOptions,
diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/empty_chart.tsx b/x-pack/plugins/infra/public/components/metrics_explorer/empty_chart.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/metrics_explorer/empty_chart.tsx
rename to x-pack/plugins/infra/public/components/metrics_explorer/empty_chart.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/group_by.tsx b/x-pack/plugins/infra/public/components/metrics_explorer/group_by.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/metrics_explorer/group_by.tsx
rename to x-pack/plugins/infra/public/components/metrics_explorer/group_by.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/calculate_domain.ts b/x-pack/plugins/infra/public/components/metrics_explorer/helpers/calculate_domain.ts
similarity index 56%
rename from x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/calculate_domain.ts
rename to x-pack/plugins/infra/public/components/metrics_explorer/helpers/calculate_domain.ts
index fd18adea3aad0..90569854b833b 100644
--- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/calculate_domain.ts
+++ b/x-pack/plugins/infra/public/components/metrics_explorer/helpers/calculate_domain.ts
@@ -3,10 +3,20 @@
  * or more contributor license agreements. Licensed under the Elastic License;
  * you may not use this file except in compliance with the Elastic License.
  */
-import { min, max, sum } from 'lodash';
-import { MetricsExplorerSeries } from '../../../../server/routes/metrics_explorer/types';
+import { min, max, sum, isNumber } from 'lodash';
+import { MetricsExplorerSeries } from '../../../../common/http_api/metrics_explorer';
 import { MetricsExplorerOptionsMetric } from '../../../containers/metrics_explorer/use_metrics_explorer_options';
 
+const getMin = (values: Array<number | null>) => {
+  const minValue = min(values);
+  return isNumber(minValue) && Number.isFinite(minValue) ? minValue : undefined;
+};
+
+const getMax = (values: Array<number | null>) => {
+  const maxValue = max(values);
+  return isNumber(maxValue) && Number.isFinite(maxValue) ? maxValue : undefined;
+};
+
 export const calculateDomain = (
   series: MetricsExplorerSeries,
   metrics: MetricsExplorerOptionsMetric[],
@@ -18,13 +28,13 @@ export const calculateDomain = (
         .map((m, index) => {
           return (row[`metric_${index}`] as number) || null;
         })
-        .filter(v => v);
-      const minValue = min(rowValues);
+        .filter(v => isNumber(v));
+      const minValue = getMin(rowValues);
       // For stacked domains we want to add 10% head room so the charts have
       // enough room to draw the 2 pixel line as well.
-      const maxValue = stacked ? sum(rowValues) * 1.1 : max(rowValues);
+      const maxValue = stacked ? sum(rowValues) * 1.1 : getMax(rowValues);
       return acc.concat([minValue || null, maxValue || null]);
     }, [] as Array<number | null>)
-    .filter(v => v);
-  return { min: min(values) || 0, max: max(values) || 0 };
+    .filter(v => isNumber(v));
+  return { min: getMin(values) || 0, max: getMax(values) || 0 };
 };
diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/calculate_domian.test.ts b/x-pack/plugins/infra/public/components/metrics_explorer/helpers/calculate_domian.test.ts
similarity index 95%
rename from x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/calculate_domian.test.ts
rename to x-pack/plugins/infra/public/components/metrics_explorer/helpers/calculate_domian.test.ts
index e85cd67060d2c..4b45534d41db8 100644
--- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/calculate_domian.test.ts
+++ b/x-pack/plugins/infra/public/components/metrics_explorer/helpers/calculate_domian.test.ts
@@ -5,7 +5,7 @@
  */
 
 import { calculateDomain } from './calculate_domain';
-import { MetricsExplorerSeries } from '../../../../server/routes/metrics_explorer/types';
+import { MetricsExplorerSeries } from '../../../../common/http_api/metrics_explorer';
 import { MetricsExplorerOptionsMetric } from '../../../containers/metrics_explorer/use_metrics_explorer_options';
 import { MetricsExplorerColor } from '../../../../common/color_palette';
 describe('calculateDomain()', () => {
diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metric.ts b/x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metric.ts
similarity index 90%
rename from x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metric.ts
rename to x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metric.ts
index 1e9902337e032..33ec2ce2715a3 100644
--- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metric.ts
+++ b/x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metric.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { MetricsExplorerMetric } from '../../../../server/routes/metrics_explorer/types';
+import { MetricsExplorerMetric } from '../../../../common/http_api/metrics_explorer';
 import { createFormatter } from '../../../utils/formatters';
 import { InfraFormatterType } from '../../../lib/lib';
 import { metricToFormat } from './metric_to_format';
diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metrics.test.ts b/x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metrics.test.ts
similarity index 94%
rename from x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metrics.test.ts
rename to x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metrics.test.ts
index 592dd70345bfa..ec41e90e441a4 100644
--- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metrics.test.ts
+++ b/x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metrics.test.ts
@@ -5,7 +5,7 @@
  */
 
 import { createFormatterForMetric } from './create_formatter_for_metric';
-import { MetricsExplorerMetric } from '../../../../server/routes/metrics_explorer/types';
+import { MetricsExplorerMetric } from '../../../../common/http_api/metrics_explorer';
 
 describe('createFormatterForMetric()', () => {
   it('should just work for count', () => {
diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_metric_label.test.ts b/x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_metric_label.test.ts
similarity index 89%
rename from x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_metric_label.test.ts
rename to x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_metric_label.test.ts
index 0c52d8d56a6e9..cbf6904d246c7 100644
--- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_metric_label.test.ts
+++ b/x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_metric_label.test.ts
@@ -5,7 +5,7 @@
  */
 
 import { createMetricLabel } from './create_metric_label';
-import { MetricsExplorerMetric } from '../../../../server/routes/metrics_explorer/types';
+import { MetricsExplorerMetric } from '../../../../common/http_api/metrics_explorer';
 
 describe('createMetricLabel()', () => {
   it('should work with metrics with fields', () => {
diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_metric_label.ts b/x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_metric_label.ts
similarity index 80%
rename from x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_metric_label.ts
rename to x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_metric_label.ts
index 489520177f07d..b6453a81317b1 100644
--- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_metric_label.ts
+++ b/x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_metric_label.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { MetricsExplorerMetric } from '../../../../server/routes/metrics_explorer/types';
+import { MetricsExplorerMetric } from '../../../../common/http_api/metrics_explorer';
 
 export const createMetricLabel = (metric: MetricsExplorerMetric) => {
   return `${metric.aggregation}(${metric.field || ''})`;
diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_tsvb_link.test.ts b/x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_tsvb_link.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_tsvb_link.test.ts
rename to x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_tsvb_link.test.ts
diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_tsvb_link.ts b/x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_tsvb_link.ts
similarity index 97%
rename from x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_tsvb_link.ts
rename to x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_tsvb_link.ts
index 2fe81123594e9..cb23a96b9c163 100644
--- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/create_tsvb_link.ts
+++ b/x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_tsvb_link.ts
@@ -8,7 +8,7 @@ import { encode } from 'rison-node';
 import uuid from 'uuid';
 import { set } from 'lodash';
 import { colorTransformer, MetricsExplorerColor } from '../../../../common/color_palette';
-import { MetricsExplorerSeries } from '../../../../server/routes/metrics_explorer/types';
+import { MetricsExplorerSeries } from '../../../../common/http_api/metrics_explorer';
 import {
   MetricsExplorerOptions,
   MetricsExplorerOptionsMetric,
diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/get_chart_theme.ts b/x-pack/plugins/infra/public/components/metrics_explorer/helpers/get_chart_theme.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/get_chart_theme.ts
rename to x-pack/plugins/infra/public/components/metrics_explorer/helpers/get_chart_theme.ts
diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.test.ts b/x-pack/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.test.ts
similarity index 94%
rename from x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.test.ts
rename to x-pack/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.test.ts
index a38a9ac54d828..4cb27b4fb65c3 100644
--- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.test.ts
+++ b/x-pack/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.test.ts
@@ -5,7 +5,7 @@
  */
 
 import { metricToFormat } from './metric_to_format';
-import { MetricsExplorerMetric } from '../../../../server/routes/metrics_explorer/types';
+import { MetricsExplorerMetric } from '../../../../common/http_api/metrics_explorer';
 import { InfraFormatterType } from '../../../lib/lib';
 describe('metricToFormat()', () => {
   it('should just work for numeric metrics', () => {
diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.ts b/x-pack/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.ts
similarity index 89%
rename from x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.ts
rename to x-pack/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.ts
index 69f2bd50ef42f..63272c86a5dc7 100644
--- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.ts
+++ b/x-pack/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.ts
@@ -5,7 +5,7 @@
  */
 
 import { last } from 'lodash';
-import { MetricsExplorerMetric } from '../../../../server/routes/metrics_explorer/types';
+import { MetricsExplorerMetric } from '../../../../common/http_api/metrics_explorer';
 import { InfraFormatterType } from '../../../lib/lib';
 export const metricToFormat = (metric?: MetricsExplorerMetric) => {
   if (metric && metric.field) {
diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/kuery_bar.tsx b/x-pack/plugins/infra/public/components/metrics_explorer/kuery_bar.tsx
similarity index 96%
rename from x-pack/legacy/plugins/infra/public/components/metrics_explorer/kuery_bar.tsx
rename to x-pack/plugins/infra/public/components/metrics_explorer/kuery_bar.tsx
index 7114217920998..0e18deedd404c 100644
--- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/kuery_bar.tsx
+++ b/x-pack/plugins/infra/public/components/metrics_explorer/kuery_bar.tsx
@@ -10,7 +10,7 @@ import React, { useEffect, useState } from 'react';
 import { WithKueryAutocompletion } from '../../containers/with_kuery_autocompletion';
 import { AutocompleteField } from '../autocomplete_field';
 import { isDisplayable } from '../../utils/is_displayable';
-import { esKuery, IIndexPattern } from '../../../../../../../src/plugins/data/public';
+import { esKuery, IIndexPattern } from '../../../../../../src/plugins/data/public';
 
 interface Props {
   derivedIndexPattern: IIndexPattern;
diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/metrics.tsx b/x-pack/plugins/infra/public/components/metrics_explorer/metrics.tsx
similarity index 96%
rename from x-pack/legacy/plugins/infra/public/components/metrics_explorer/metrics.tsx
rename to x-pack/plugins/infra/public/components/metrics_explorer/metrics.tsx
index 0010fce7efa49..79d4122733c55 100644
--- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/metrics.tsx
+++ b/x-pack/plugins/infra/public/components/metrics_explorer/metrics.tsx
@@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n';
 import React, { useCallback, useState } from 'react';
 import { IFieldType } from 'src/plugins/data/public';
 import { colorTransformer, MetricsExplorerColor } from '../../../common/color_palette';
-import { MetricsExplorerMetric } from '../../../server/routes/metrics_explorer/types';
+import { MetricsExplorerMetric } from '../../../common/http_api/metrics_explorer';
 import { MetricsExplorerOptions } from '../../containers/metrics_explorer/use_metrics_explorer_options';
 import { isDisplayable } from '../../utils/is_displayable';
 
diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/no_metrics.tsx b/x-pack/plugins/infra/public/components/metrics_explorer/no_metrics.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/metrics_explorer/no_metrics.tsx
rename to x-pack/plugins/infra/public/components/metrics_explorer/no_metrics.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/series_chart.tsx b/x-pack/plugins/infra/public/components/metrics_explorer/series_chart.tsx
similarity index 97%
rename from x-pack/legacy/plugins/infra/public/components/metrics_explorer/series_chart.tsx
rename to x-pack/plugins/infra/public/components/metrics_explorer/series_chart.tsx
index f365faa134d93..65911a02c938a 100644
--- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/series_chart.tsx
+++ b/x-pack/plugins/infra/public/components/metrics_explorer/series_chart.tsx
@@ -13,7 +13,7 @@ import {
   AreaSeriesStyle,
   BarSeriesStyle,
 } from '@elastic/charts';
-import { MetricsExplorerSeries } from '../../../server/routes/metrics_explorer/types';
+import { MetricsExplorerSeries } from '../../../common/http_api/metrics_explorer';
 import { colorTransformer, MetricsExplorerColor } from '../../../common/color_palette';
 import { createMetricLabel } from './helpers/create_metric_label';
 import {
diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/toolbar.tsx b/x-pack/plugins/infra/public/components/metrics_explorer/toolbar.tsx
similarity index 98%
rename from x-pack/legacy/plugins/infra/public/components/metrics_explorer/toolbar.tsx
rename to x-pack/plugins/infra/public/components/metrics_explorer/toolbar.tsx
index 839e40e057c9a..9e96819a36cac 100644
--- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/toolbar.tsx
+++ b/x-pack/plugins/infra/public/components/metrics_explorer/toolbar.tsx
@@ -11,7 +11,7 @@ import { IIndexPattern } from 'src/plugins/data/public';
 import {
   MetricsExplorerMetric,
   MetricsExplorerAggregation,
-} from '../../../server/routes/metrics_explorer/types';
+} from '../../../common/http_api/metrics_explorer';
 import {
   MetricsExplorerOptions,
   MetricsExplorerTimeOptions,
diff --git a/x-pack/legacy/plugins/infra/public/components/navigation/app_navigation.tsx b/x-pack/plugins/infra/public/components/navigation/app_navigation.tsx
similarity index 93%
rename from x-pack/legacy/plugins/infra/public/components/navigation/app_navigation.tsx
rename to x-pack/plugins/infra/public/components/navigation/app_navigation.tsx
index 79785c11a3ebe..b229fb4a6b494 100644
--- a/x-pack/legacy/plugins/infra/public/components/navigation/app_navigation.tsx
+++ b/x-pack/plugins/infra/public/components/navigation/app_navigation.tsx
@@ -6,7 +6,7 @@
 
 import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
 import React from 'react';
-import euiStyled from '../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../observability/public';
 
 interface AppNavigationProps {
   'aria-label': string;
diff --git a/x-pack/legacy/plugins/infra/public/components/navigation/routed_tabs.tsx b/x-pack/plugins/infra/public/components/navigation/routed_tabs.tsx
similarity index 53%
rename from x-pack/legacy/plugins/infra/public/components/navigation/routed_tabs.tsx
rename to x-pack/plugins/infra/public/components/navigation/routed_tabs.tsx
index c43ade12ded6d..2838ac6cda6dd 100644
--- a/x-pack/legacy/plugins/infra/public/components/navigation/routed_tabs.tsx
+++ b/x-pack/plugins/infra/public/components/navigation/routed_tabs.tsx
@@ -8,7 +8,7 @@ import { EuiLink, EuiTab, EuiTabs } from '@elastic/eui';
 import React from 'react';
 import { Route } from 'react-router-dom';
 
-import euiStyled from '../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../observability/public';
 
 interface TabConfiguration {
   title: string | React.ReactNode;
@@ -32,23 +32,27 @@ export class RoutedTabs extends React.Component<RoutedTabsProps> {
         <Route
           key={`${tab.path}-${tab.title}`}
           path={tab.path}
-          children={({ match, history }) => (
-            <TabContainer className="euiTab">
-              {/* eslint-disable-next-line @elastic/eui/href-or-on-click */}
-              <EuiLink
-                href={`#${tab.path}`}
-                data-test-subj={`infrastructureNavLink_${tab.path}`}
-                onClick={(e: React.MouseEvent<HTMLAnchorElement>) => {
-                  e.preventDefault();
-                  history.push(tab.path);
-                }}
-              >
-                <EuiTab onClick={noop} isSelected={match !== null}>
-                  {tab.title}
-                </EuiTab>
-              </EuiLink>
-            </TabContainer>
-          )}
+          children={({ match, history }) => {
+            return (
+              <TabContainer className="euiTab">
+                {/* eslint-disable-next-line @elastic/eui/href-or-on-click */}
+                <EuiLink
+                  href={history.createHref({
+                    pathname: tab.path,
+                  })}
+                  data-test-subj={`infrastructureNavLink_${tab.path}`}
+                  onClick={(e: React.MouseEvent<HTMLAnchorElement>) => {
+                    e.preventDefault();
+                    history.push(tab.path);
+                  }}
+                >
+                  <EuiTab onClick={noop} isSelected={match !== null}>
+                    {tab.title}
+                  </EuiTab>
+                </EuiLink>
+              </TabContainer>
+            );
+          }}
         />
       );
     });
diff --git a/x-pack/legacy/plugins/infra/public/components/nodes_overview/index.tsx b/x-pack/plugins/infra/public/components/nodes_overview/index.tsx
similarity index 99%
rename from x-pack/legacy/plugins/infra/public/components/nodes_overview/index.tsx
rename to x-pack/plugins/infra/public/components/nodes_overview/index.tsx
index 8e8015ce6a82e..8cd3faabd1e12 100644
--- a/x-pack/legacy/plugins/infra/public/components/nodes_overview/index.tsx
+++ b/x-pack/plugins/infra/public/components/nodes_overview/index.tsx
@@ -10,7 +10,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
 import { get, max, min } from 'lodash';
 import React from 'react';
 
-import euiStyled from '../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../observability/public';
 import { InfraFormatterType, InfraWaffleMapBounds, InfraWaffleMapOptions } from '../../lib/lib';
 import { KueryFilterQuery } from '../../store/local/waffle_filter';
 import { createFormatter } from '../../utils/formatters';
diff --git a/x-pack/legacy/plugins/infra/public/components/nodes_overview/table.tsx b/x-pack/plugins/infra/public/components/nodes_overview/table.tsx
similarity index 94%
rename from x-pack/legacy/plugins/infra/public/components/nodes_overview/table.tsx
rename to x-pack/plugins/infra/public/components/nodes_overview/table.tsx
index 5c793f670119c..82991076255ee 100644
--- a/x-pack/legacy/plugins/infra/public/components/nodes_overview/table.tsx
+++ b/x-pack/plugins/infra/public/components/nodes_overview/table.tsx
@@ -15,7 +15,7 @@ import { fieldToName } from '../waffle/lib/field_to_display_name';
 import { NodeContextMenu } from '../waffle/node_context_menu';
 import { InventoryItemType } from '../../../common/inventory_models/types';
 import { SnapshotNode, SnapshotNodePath } from '../../../common/http_api/snapshot_api';
-import { ROOT_ELEMENT_ID } from '../../app';
+import { CONTAINER_CLASSNAME } from '../../apps/start_app';
 
 interface Props {
   nodes: SnapshotNode[];
@@ -57,10 +57,13 @@ export const TableView = (props: Props) => {
   );
 
   useEffect(() => {
-    if (openPopovers.length > 0) {
-      document.getElementById(ROOT_ELEMENT_ID)!.style.overflowY = 'hidden';
-    } else {
-      document.getElementById(ROOT_ELEMENT_ID)!.style.overflowY = 'auto';
+    const el = document.getElementsByClassName(CONTAINER_CLASSNAME)[0];
+    if (el instanceof HTMLElement) {
+      if (openPopovers.length > 0) {
+        el.style.overflowY = 'hidden';
+      } else {
+        el.style.overflowY = 'auto';
+      }
     }
   }, [openPopovers]);
 
diff --git a/x-pack/legacy/plugins/infra/public/components/page.tsx b/x-pack/plugins/infra/public/components/page.tsx
similarity index 90%
rename from x-pack/legacy/plugins/infra/public/components/page.tsx
rename to x-pack/plugins/infra/public/components/page.tsx
index ca029e43ac37b..b51afdd8ca803 100644
--- a/x-pack/legacy/plugins/infra/public/components/page.tsx
+++ b/x-pack/plugins/infra/public/components/page.tsx
@@ -6,7 +6,7 @@
 
 import { EuiPage } from '@elastic/eui';
 
-import euiStyled from '../../../../common/eui_styled_components';
+import { euiStyled } from '../../../observability/public';
 
 export const ColumnarPage = euiStyled.div`
   display: flex;
diff --git a/x-pack/legacy/plugins/infra/public/components/saved_views/create_modal.tsx b/x-pack/plugins/infra/public/components/saved_views/create_modal.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/saved_views/create_modal.tsx
rename to x-pack/plugins/infra/public/components/saved_views/create_modal.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/saved_views/toolbar_control.tsx b/x-pack/plugins/infra/public/components/saved_views/toolbar_control.tsx
similarity index 98%
rename from x-pack/legacy/plugins/infra/public/components/saved_views/toolbar_control.tsx
rename to x-pack/plugins/infra/public/components/saved_views/toolbar_control.tsx
index e03b7fcc8ffa4..c66aea669682e 100644
--- a/x-pack/legacy/plugins/infra/public/components/saved_views/toolbar_control.tsx
+++ b/x-pack/plugins/infra/public/components/saved_views/toolbar_control.tsx
@@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n';
 import { useSavedView } from '../../hooks/use_saved_view';
 import { SavedViewCreateModal } from './create_modal';
 import { SavedViewListFlyout } from './view_list_flyout';
-import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
+import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
 
 interface Props<ViewState> {
   viewType: string;
diff --git a/x-pack/legacy/plugins/infra/public/components/saved_views/view_list_flyout.tsx b/x-pack/plugins/infra/public/components/saved_views/view_list_flyout.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/saved_views/view_list_flyout.tsx
rename to x-pack/plugins/infra/public/components/saved_views/view_list_flyout.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/source_configuration/add_log_column_popover.tsx b/x-pack/plugins/infra/public/components/source_configuration/add_log_column_popover.tsx
similarity index 98%
rename from x-pack/legacy/plugins/infra/public/components/source_configuration/add_log_column_popover.tsx
rename to x-pack/plugins/infra/public/components/source_configuration/add_log_column_popover.tsx
index fc8407c5298e6..0835a904585ed 100644
--- a/x-pack/legacy/plugins/infra/public/components/source_configuration/add_log_column_popover.tsx
+++ b/x-pack/plugins/infra/public/components/source_configuration/add_log_column_popover.tsx
@@ -12,7 +12,7 @@ import { v4 as uuidv4 } from 'uuid';
 
 import { LogColumnConfiguration } from '../../utils/source_configuration';
 import { useVisibilityState } from '../../utils/use_visibility_state';
-import { euiStyled } from '../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../observability/public';
 
 interface SelectableColumnOption {
   optionProps: Option;
diff --git a/x-pack/legacy/plugins/infra/public/components/source_configuration/fields_configuration_panel.tsx b/x-pack/plugins/infra/public/components/source_configuration/fields_configuration_panel.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/source_configuration/fields_configuration_panel.tsx
rename to x-pack/plugins/infra/public/components/source_configuration/fields_configuration_panel.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/source_configuration/index.ts b/x-pack/plugins/infra/public/components/source_configuration/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/source_configuration/index.ts
rename to x-pack/plugins/infra/public/components/source_configuration/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/components/source_configuration/indices_configuration_form_state.ts b/x-pack/plugins/infra/public/components/source_configuration/indices_configuration_form_state.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/source_configuration/indices_configuration_form_state.ts
rename to x-pack/plugins/infra/public/components/source_configuration/indices_configuration_form_state.ts
diff --git a/x-pack/legacy/plugins/infra/public/components/source_configuration/indices_configuration_panel.tsx b/x-pack/plugins/infra/public/components/source_configuration/indices_configuration_panel.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/source_configuration/indices_configuration_panel.tsx
rename to x-pack/plugins/infra/public/components/source_configuration/indices_configuration_panel.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/source_configuration/input_fields.tsx b/x-pack/plugins/infra/public/components/source_configuration/input_fields.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/source_configuration/input_fields.tsx
rename to x-pack/plugins/infra/public/components/source_configuration/input_fields.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/source_configuration/log_columns_configuration_form_state.tsx b/x-pack/plugins/infra/public/components/source_configuration/log_columns_configuration_form_state.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/source_configuration/log_columns_configuration_form_state.tsx
rename to x-pack/plugins/infra/public/components/source_configuration/log_columns_configuration_form_state.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/source_configuration/log_columns_configuration_panel.tsx b/x-pack/plugins/infra/public/components/source_configuration/log_columns_configuration_panel.tsx
similarity index 99%
rename from x-pack/legacy/plugins/infra/public/components/source_configuration/log_columns_configuration_panel.tsx
rename to x-pack/plugins/infra/public/components/source_configuration/log_columns_configuration_panel.tsx
index 90361a5c2b541..9ccd28f149ec7 100644
--- a/x-pack/legacy/plugins/infra/public/components/source_configuration/log_columns_configuration_panel.tsx
+++ b/x-pack/plugins/infra/public/components/source_configuration/log_columns_configuration_panel.tsx
@@ -22,7 +22,7 @@ import {
 import { i18n } from '@kbn/i18n';
 import { FormattedMessage } from '@kbn/i18n/react';
 import React, { useCallback } from 'react';
-import { DragHandleProps, DropResult } from '../../../../../common/eui_draggable';
+import { DragHandleProps, DropResult } from '../../../../observability/public';
 
 import { AddLogColumnButtonAndPopover } from './add_log_column_popover';
 import {
diff --git a/x-pack/legacy/plugins/infra/public/components/source_configuration/name_configuration_panel.tsx b/x-pack/plugins/infra/public/components/source_configuration/name_configuration_panel.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/source_configuration/name_configuration_panel.tsx
rename to x-pack/plugins/infra/public/components/source_configuration/name_configuration_panel.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/source_configuration/source_configuration_form_state.tsx b/x-pack/plugins/infra/public/components/source_configuration/source_configuration_form_state.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/source_configuration/source_configuration_form_state.tsx
rename to x-pack/plugins/infra/public/components/source_configuration/source_configuration_form_state.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/source_configuration/source_configuration_settings.tsx b/x-pack/plugins/infra/public/components/source_configuration/source_configuration_settings.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/source_configuration/source_configuration_settings.tsx
rename to x-pack/plugins/infra/public/components/source_configuration/source_configuration_settings.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/source_configuration/view_source_configuration_button.tsx b/x-pack/plugins/infra/public/components/source_configuration/view_source_configuration_button.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/source_configuration/view_source_configuration_button.tsx
rename to x-pack/plugins/infra/public/components/source_configuration/view_source_configuration_button.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/source_error_page.tsx b/x-pack/plugins/infra/public/components/source_error_page.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/source_error_page.tsx
rename to x-pack/plugins/infra/public/components/source_error_page.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/source_loading_page.tsx b/x-pack/plugins/infra/public/components/source_loading_page.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/source_loading_page.tsx
rename to x-pack/plugins/infra/public/components/source_loading_page.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/waffle/conditional_tooltip.tsx b/x-pack/plugins/infra/public/components/waffle/conditional_tooltip.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/waffle/conditional_tooltip.tsx
rename to x-pack/plugins/infra/public/components/waffle/conditional_tooltip.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/waffle/custom_field_panel.tsx b/x-pack/plugins/infra/public/components/waffle/custom_field_panel.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/waffle/custom_field_panel.tsx
rename to x-pack/plugins/infra/public/components/waffle/custom_field_panel.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/waffle/gradient_legend.tsx b/x-pack/plugins/infra/public/components/waffle/gradient_legend.tsx
similarity index 97%
rename from x-pack/legacy/plugins/infra/public/components/waffle/gradient_legend.tsx
rename to x-pack/plugins/infra/public/components/waffle/gradient_legend.tsx
index 3dcc40818b4d5..6b0c4bb41dc98 100644
--- a/x-pack/legacy/plugins/infra/public/components/waffle/gradient_legend.tsx
+++ b/x-pack/plugins/infra/public/components/waffle/gradient_legend.tsx
@@ -6,7 +6,7 @@
 
 import React from 'react';
 
-import euiStyled from '../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../observability/public';
 import {
   InfraFormatter,
   InfraWaffleMapBounds,
diff --git a/x-pack/legacy/plugins/infra/public/components/waffle/group_name.tsx b/x-pack/plugins/infra/public/components/waffle/group_name.tsx
similarity index 97%
rename from x-pack/legacy/plugins/infra/public/components/waffle/group_name.tsx
rename to x-pack/plugins/infra/public/components/waffle/group_name.tsx
index 731bcdd52a98e..01bd3600a1624 100644
--- a/x-pack/legacy/plugins/infra/public/components/waffle/group_name.tsx
+++ b/x-pack/plugins/infra/public/components/waffle/group_name.tsx
@@ -6,7 +6,7 @@
 import { EuiLink, EuiToolTip } from '@elastic/eui';
 import React from 'react';
 
-import euiStyled from '../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../observability/public';
 import { InfraWaffleMapGroup, InfraWaffleMapOptions } from '../../lib/lib';
 
 interface Props {
diff --git a/x-pack/legacy/plugins/infra/public/components/waffle/group_of_groups.tsx b/x-pack/plugins/infra/public/components/waffle/group_of_groups.tsx
similarity index 96%
rename from x-pack/legacy/plugins/infra/public/components/waffle/group_of_groups.tsx
rename to x-pack/plugins/infra/public/components/waffle/group_of_groups.tsx
index 60a117e3ed617..9634293587d49 100644
--- a/x-pack/legacy/plugins/infra/public/components/waffle/group_of_groups.tsx
+++ b/x-pack/plugins/infra/public/components/waffle/group_of_groups.tsx
@@ -6,7 +6,7 @@
 
 import React from 'react';
 
-import euiStyled from '../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../observability/public';
 import {
   InfraWaffleMapBounds,
   InfraWaffleMapGroupOfGroups,
diff --git a/x-pack/legacy/plugins/infra/public/components/waffle/group_of_nodes.tsx b/x-pack/plugins/infra/public/components/waffle/group_of_nodes.tsx
similarity index 96%
rename from x-pack/legacy/plugins/infra/public/components/waffle/group_of_nodes.tsx
rename to x-pack/plugins/infra/public/components/waffle/group_of_nodes.tsx
index b47b8f6a1bf39..6b82671617df7 100644
--- a/x-pack/legacy/plugins/infra/public/components/waffle/group_of_nodes.tsx
+++ b/x-pack/plugins/infra/public/components/waffle/group_of_nodes.tsx
@@ -6,7 +6,7 @@
 
 import React from 'react';
 
-import euiStyled from '../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../observability/public';
 import {
   InfraWaffleMapBounds,
   InfraWaffleMapGroupOfNodes,
diff --git a/x-pack/legacy/plugins/infra/public/components/waffle/legend.tsx b/x-pack/plugins/infra/public/components/waffle/legend.tsx
similarity index 96%
rename from x-pack/legacy/plugins/infra/public/components/waffle/legend.tsx
rename to x-pack/plugins/infra/public/components/waffle/legend.tsx
index c7f647449606e..de070efb35a1f 100644
--- a/x-pack/legacy/plugins/infra/public/components/waffle/legend.tsx
+++ b/x-pack/plugins/infra/public/components/waffle/legend.tsx
@@ -5,7 +5,7 @@
  */
 import React from 'react';
 
-import euiStyled from '../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../observability/public';
 import { WithWaffleOptions } from '../../containers/waffle/with_waffle_options';
 import { InfraFormatter, InfraWaffleMapBounds, InfraWaffleMapLegend } from '../../lib/lib';
 import { GradientLegend } from './gradient_legend';
diff --git a/x-pack/legacy/plugins/infra/public/components/waffle/legend_controls.tsx b/x-pack/plugins/infra/public/components/waffle/legend_controls.tsx
similarity index 98%
rename from x-pack/legacy/plugins/infra/public/components/waffle/legend_controls.tsx
rename to x-pack/plugins/infra/public/components/waffle/legend_controls.tsx
index f6e4a65eead24..26b5b1c0af727 100644
--- a/x-pack/legacy/plugins/infra/public/components/waffle/legend_controls.tsx
+++ b/x-pack/plugins/infra/public/components/waffle/legend_controls.tsx
@@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n';
 import { FormattedMessage } from '@kbn/i18n/react';
 import React, { SyntheticEvent, useState } from 'react';
 
-import euiStyled from '../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../observability/public';
 import { InfraWaffleMapBounds } from '../../lib/lib';
 
 interface Props {
diff --git a/x-pack/legacy/plugins/infra/public/components/waffle/lib/apply_wafflemap_layout.ts b/x-pack/plugins/infra/public/components/waffle/lib/apply_wafflemap_layout.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/waffle/lib/apply_wafflemap_layout.ts
rename to x-pack/plugins/infra/public/components/waffle/lib/apply_wafflemap_layout.ts
diff --git a/x-pack/legacy/plugins/infra/public/components/waffle/lib/color_from_value.ts b/x-pack/plugins/infra/public/components/waffle/lib/color_from_value.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/waffle/lib/color_from_value.ts
rename to x-pack/plugins/infra/public/components/waffle/lib/color_from_value.ts
diff --git a/x-pack/legacy/plugins/infra/public/components/waffle/lib/create_uptime_link.test.ts b/x-pack/plugins/infra/public/components/waffle/lib/create_uptime_link.test.ts
similarity index 86%
rename from x-pack/legacy/plugins/infra/public/components/waffle/lib/create_uptime_link.test.ts
rename to x-pack/plugins/infra/public/components/waffle/lib/create_uptime_link.test.ts
index 1a285ceefec0e..fb9791fae9b5e 100644
--- a/x-pack/legacy/plugins/infra/public/components/waffle/lib/create_uptime_link.test.ts
+++ b/x-pack/plugins/infra/public/components/waffle/lib/create_uptime_link.test.ts
@@ -46,9 +46,7 @@ describe('createUptimeLink()', () => {
         avg: 0.6,
       },
     };
-    expect(createUptimeLink(options, 'host', node)).toBe(
-      '../app/uptime#/?search=host.ip:"10.0.1.2"'
-    );
+    expect(createUptimeLink(options, 'host', node)).toBe('#/?search=host.ip:"10.0.1.2"');
   });
 
   it('should work for hosts without ip', () => {
@@ -64,9 +62,7 @@ describe('createUptimeLink()', () => {
         avg: 0.6,
       },
     };
-    expect(createUptimeLink(options, 'host', node)).toBe(
-      '../app/uptime#/?search=host.name:"host-01"'
-    );
+    expect(createUptimeLink(options, 'host', node)).toBe('#/?search=host.name:"host-01"');
   });
 
   it('should work for pods', () => {
@@ -83,7 +79,7 @@ describe('createUptimeLink()', () => {
       },
     };
     expect(createUptimeLink(options, 'pod', node)).toBe(
-      '../app/uptime#/?search=kubernetes.pod.uid:"29193-pod-02939"'
+      '#/?search=kubernetes.pod.uid:"29193-pod-02939"'
     );
   });
 
@@ -101,7 +97,7 @@ describe('createUptimeLink()', () => {
       },
     };
     expect(createUptimeLink(options, 'container', node)).toBe(
-      '../app/uptime#/?search=container.id:"docker-1234"'
+      '#/?search=container.id:"docker-1234"'
     );
   });
 });
diff --git a/x-pack/legacy/plugins/infra/public/components/waffle/lib/create_uptime_link.ts b/x-pack/plugins/infra/public/components/waffle/lib/create_uptime_link.ts
similarity index 81%
rename from x-pack/legacy/plugins/infra/public/components/waffle/lib/create_uptime_link.ts
rename to x-pack/plugins/infra/public/components/waffle/lib/create_uptime_link.ts
index 05b4b6d514cd0..023a1a4b6ecef 100644
--- a/x-pack/legacy/plugins/infra/public/components/waffle/lib/create_uptime_link.ts
+++ b/x-pack/plugins/infra/public/components/waffle/lib/create_uptime_link.ts
@@ -8,16 +8,14 @@ import { get } from 'lodash';
 import { InfraWaffleMapNode, InfraWaffleMapOptions } from '../../../lib/lib';
 import { InventoryItemType } from '../../../../common/inventory_models/types';
 
-const BASE_URL = '../app/uptime#/?search=';
-
 export const createUptimeLink = (
   options: InfraWaffleMapOptions,
   nodeType: InventoryItemType,
   node: InfraWaffleMapNode
 ) => {
   if (nodeType === 'host' && node.ip) {
-    return `${BASE_URL}host.ip:"${node.ip}"`;
+    return `#/?search=host.ip:"${node.ip}"`;
   }
   const field = get(options, ['fields', nodeType], '');
-  return `${BASE_URL}${field ? field + ':' : ''}"${node.id}"`;
+  return `#/?search=${field ? field + ':' : ''}"${node.id}"`;
 };
diff --git a/x-pack/legacy/plugins/infra/public/components/waffle/lib/field_to_display_name.ts b/x-pack/plugins/infra/public/components/waffle/lib/field_to_display_name.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/waffle/lib/field_to_display_name.ts
rename to x-pack/plugins/infra/public/components/waffle/lib/field_to_display_name.ts
diff --git a/x-pack/legacy/plugins/infra/public/components/waffle/lib/size_of_squares.ts b/x-pack/plugins/infra/public/components/waffle/lib/size_of_squares.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/waffle/lib/size_of_squares.ts
rename to x-pack/plugins/infra/public/components/waffle/lib/size_of_squares.ts
diff --git a/x-pack/legacy/plugins/infra/public/components/waffle/lib/type_guards.ts b/x-pack/plugins/infra/public/components/waffle/lib/type_guards.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/waffle/lib/type_guards.ts
rename to x-pack/plugins/infra/public/components/waffle/lib/type_guards.ts
diff --git a/x-pack/legacy/plugins/infra/public/components/waffle/map.tsx b/x-pack/plugins/infra/public/components/waffle/map.tsx
similarity index 98%
rename from x-pack/legacy/plugins/infra/public/components/waffle/map.tsx
rename to x-pack/plugins/infra/public/components/waffle/map.tsx
index 91fb84840f37f..7cab2307dacfa 100644
--- a/x-pack/legacy/plugins/infra/public/components/waffle/map.tsx
+++ b/x-pack/plugins/infra/public/components/waffle/map.tsx
@@ -5,7 +5,7 @@
  */
 import React from 'react';
 
-import euiStyled from '../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../observability/public';
 import { nodesToWaffleMap } from '../../containers/waffle/nodes_to_wafflemap';
 import {
   isWaffleMapGroupWithGroups,
diff --git a/x-pack/legacy/plugins/infra/public/components/waffle/node.tsx b/x-pack/plugins/infra/public/components/waffle/node.tsx
similarity index 98%
rename from x-pack/legacy/plugins/infra/public/components/waffle/node.tsx
rename to x-pack/plugins/infra/public/components/waffle/node.tsx
index 72e2a4c8f35d7..4eb5ccb8c1b4d 100644
--- a/x-pack/legacy/plugins/infra/public/components/waffle/node.tsx
+++ b/x-pack/plugins/infra/public/components/waffle/node.tsx
@@ -10,7 +10,7 @@ import React from 'react';
 import { i18n } from '@kbn/i18n';
 
 import { ConditionalToolTip } from './conditional_tooltip';
-import euiStyled from '../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../observability/public';
 import { InfraWaffleMapBounds, InfraWaffleMapNode, InfraWaffleMapOptions } from '../../lib/lib';
 import { colorFromValue } from './lib/color_from_value';
 import { NodeContextMenu } from './node_context_menu';
diff --git a/x-pack/legacy/plugins/infra/public/components/waffle/node_context_menu.tsx b/x-pack/plugins/infra/public/components/waffle/node_context_menu.tsx
similarity index 89%
rename from x-pack/legacy/plugins/infra/public/components/waffle/node_context_menu.tsx
rename to x-pack/plugins/infra/public/components/waffle/node_context_menu.tsx
index 97b85a7e6e17d..43d27bb8259b3 100644
--- a/x-pack/legacy/plugins/infra/public/components/waffle/node_context_menu.tsx
+++ b/x-pack/plugins/infra/public/components/waffle/node_context_menu.tsx
@@ -13,7 +13,7 @@ import { InfraWaffleMapNode, InfraWaffleMapOptions } from '../../lib/lib';
 import { getNodeDetailUrl, getNodeLogsUrl } from '../../pages/link_to';
 import { createUptimeLink } from './lib/create_uptime_link';
 import { findInventoryModel, findInventoryFields } from '../../../common/inventory_models';
-import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
+import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
 import { InventoryItemType } from '../../../common/inventory_models/types';
 import {
   Section,
@@ -23,7 +23,8 @@ import {
   SectionSubtitle,
   SectionLinks,
   SectionLink,
-} from '../../../../../../plugins/observability/public';
+} from '../../../../observability/public';
+import { usePrefixPathWithBasepath } from '../../hooks/use_prefix_path_with_basepath';
 
 interface Props {
   options: InfraWaffleMapOptions;
@@ -45,6 +46,7 @@ export const NodeContextMenu: React.FC<Props> = ({
   nodeType,
   popoverPosition,
 }) => {
+  const urlPrefixer = usePrefixPathWithBasepath();
   const uiCapabilities = useKibana().services.application?.capabilities;
   const inventoryModel = findInventoryModel(nodeType);
   const nodeDetailFrom = currentTime - inventoryModel.metrics.defaultTimeRangeInSeconds * 1000;
@@ -84,11 +86,14 @@ export const NodeContextMenu: React.FC<Props> = ({
       defaultMessage: '{inventoryName} logs',
       values: { inventoryName: inventoryModel.singularDisplayName },
     }),
-    href: getNodeLogsUrl({
-      nodeType,
-      nodeId: node.id,
-      time: currentTime,
-    }),
+    href: urlPrefixer(
+      'logs',
+      getNodeLogsUrl({
+        nodeType,
+        nodeId: node.id,
+        time: currentTime,
+      })
+    ),
     'data-test-subj': 'viewLogsContextMenuItem',
     isDisabled: !showLogsLink,
   };
@@ -98,12 +103,15 @@ export const NodeContextMenu: React.FC<Props> = ({
       defaultMessage: '{inventoryName} metrics',
       values: { inventoryName: inventoryModel.singularDisplayName },
     }),
-    href: getNodeDetailUrl({
-      nodeType,
-      nodeId: node.id,
-      from: nodeDetailFrom,
-      to: currentTime,
-    }),
+    href: urlPrefixer(
+      'metrics',
+      getNodeDetailUrl({
+        nodeType,
+        nodeId: node.id,
+        from: nodeDetailFrom,
+        to: currentTime,
+      })
+    ),
     isDisabled: !showDetail,
   };
 
@@ -112,7 +120,7 @@ export const NodeContextMenu: React.FC<Props> = ({
       defaultMessage: '{inventoryName} APM traces',
       values: { inventoryName: inventoryModel.singularDisplayName },
     }),
-    href: `../app/apm#/traces?_g=()&kuery=${apmField}:"${node.id}"`,
+    href: urlPrefixer('apm', `#traces?_g=()&kuery=${apmField}:"${node.id}"`),
     'data-test-subj': 'viewApmTracesContextMenuItem',
     isDisabled: !showAPMTraceLink,
   };
@@ -122,7 +130,7 @@ export const NodeContextMenu: React.FC<Props> = ({
       defaultMessage: '{inventoryName} in Uptime',
       values: { inventoryName: inventoryModel.singularDisplayName },
     }),
-    href: createUptimeLink(options, nodeType, node),
+    href: urlPrefixer('uptime', createUptimeLink(options, nodeType, node)),
     isDisabled: !showUptimeLink,
   };
 
diff --git a/x-pack/legacy/plugins/infra/public/components/waffle/steps_legend.tsx b/x-pack/plugins/infra/public/components/waffle/steps_legend.tsx
similarity index 96%
rename from x-pack/legacy/plugins/infra/public/components/waffle/steps_legend.tsx
rename to x-pack/plugins/infra/public/components/waffle/steps_legend.tsx
index e251720795074..d2079d53dad71 100644
--- a/x-pack/legacy/plugins/infra/public/components/waffle/steps_legend.tsx
+++ b/x-pack/plugins/infra/public/components/waffle/steps_legend.tsx
@@ -7,7 +7,7 @@
 import { darken } from 'polished';
 import React from 'react';
 
-import euiStyled from '../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../observability/public';
 import {
   InfraFormatter,
   InfraWaffleMapRuleOperator,
diff --git a/x-pack/legacy/plugins/infra/public/components/waffle/view_switcher.tsx b/x-pack/plugins/infra/public/components/waffle/view_switcher.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/waffle/view_switcher.tsx
rename to x-pack/plugins/infra/public/components/waffle/view_switcher.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/waffle/waffle_accounts_controls.tsx b/x-pack/plugins/infra/public/components/waffle/waffle_accounts_controls.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/waffle/waffle_accounts_controls.tsx
rename to x-pack/plugins/infra/public/components/waffle/waffle_accounts_controls.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/waffle/waffle_group_by_controls.tsx b/x-pack/plugins/infra/public/components/waffle/waffle_group_by_controls.tsx
similarity index 98%
rename from x-pack/legacy/plugins/infra/public/components/waffle/waffle_group_by_controls.tsx
rename to x-pack/plugins/infra/public/components/waffle/waffle_group_by_controls.tsx
index 3e697dccabac5..81f82ec27b4a3 100644
--- a/x-pack/legacy/plugins/infra/public/components/waffle/waffle_group_by_controls.tsx
+++ b/x-pack/plugins/infra/public/components/waffle/waffle_group_by_controls.tsx
@@ -19,7 +19,7 @@ import React from 'react';
 import { IFieldType } from 'src/plugins/data/public';
 import { InfraGroupByOptions } from '../../lib/lib';
 import { CustomFieldPanel } from './custom_field_panel';
-import euiStyled from '../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../observability/public';
 import { InventoryItemType } from '../../../common/inventory_models/types';
 import { SnapshotGroupBy } from '../../../common/http_api/snapshot_api';
 
diff --git a/x-pack/legacy/plugins/infra/public/components/waffle/waffle_inventory_switcher.tsx b/x-pack/plugins/infra/public/components/waffle/waffle_inventory_switcher.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/waffle/waffle_inventory_switcher.tsx
rename to x-pack/plugins/infra/public/components/waffle/waffle_inventory_switcher.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/waffle/waffle_metric_controls.tsx b/x-pack/plugins/infra/public/components/waffle/waffle_metric_controls.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/waffle/waffle_metric_controls.tsx
rename to x-pack/plugins/infra/public/components/waffle/waffle_metric_controls.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/waffle/waffle_region_controls.tsx b/x-pack/plugins/infra/public/components/waffle/waffle_region_controls.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/waffle/waffle_region_controls.tsx
rename to x-pack/plugins/infra/public/components/waffle/waffle_region_controls.tsx
diff --git a/x-pack/legacy/plugins/infra/public/components/waffle/waffle_time_controls.tsx b/x-pack/plugins/infra/public/components/waffle/waffle_time_controls.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/components/waffle/waffle_time_controls.tsx
rename to x-pack/plugins/infra/public/components/waffle/waffle_time_controls.tsx
diff --git a/x-pack/legacy/plugins/infra/public/containers/inventory_metadata/use_inventory_meta.ts b/x-pack/plugins/infra/public/containers/inventory_metadata/use_inventory_meta.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/inventory_metadata/use_inventory_meta.ts
rename to x-pack/plugins/infra/public/containers/inventory_metadata/use_inventory_meta.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_api_types.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_api_types.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_api_types.ts
rename to x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_api_types.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_cleanup.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_cleanup.ts
similarity index 88%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_cleanup.ts
rename to x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_cleanup.ts
index 76e5f210ca238..21ae7804292dd 100644
--- a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_cleanup.ts
+++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_cleanup.ts
@@ -8,7 +8,7 @@ import * as rt from 'io-ts';
 import { pipe } from 'fp-ts/lib/pipeable';
 import { fold } from 'fp-ts/lib/Either';
 import { identity } from 'fp-ts/lib/function';
-import { npStart } from 'ui/new_platform';
+import { npStart } from '../../../../legacy_singletons';
 
 import { getDatafeedId, getJobId } from '../../../../../common/log_analysis';
 import { throwErrors, createPlainError } from '../../../../../common/runtime_types';
@@ -19,7 +19,7 @@ export const callDeleteJobs = async <JobType extends string>(
   jobTypes: JobType[]
 ) => {
   // NOTE: Deleting the jobs via this API will delete the datafeeds at the same time
-  const deleteJobsResponse = await npStart.core.http.fetch('/api/ml/jobs/delete_jobs', {
+  const deleteJobsResponse = await npStart.http.fetch('/api/ml/jobs/delete_jobs', {
     method: 'POST',
     body: JSON.stringify(
       deleteJobsRequestPayloadRT.encode({
@@ -35,9 +35,7 @@ export const callDeleteJobs = async <JobType extends string>(
 };
 
 export const callGetJobDeletionTasks = async () => {
-  const jobDeletionTasksResponse = await npStart.core.http.fetch(
-    '/api/ml/jobs/deleting_jobs_tasks'
-  );
+  const jobDeletionTasksResponse = await npStart.http.fetch('/api/ml/jobs/deleting_jobs_tasks');
 
   return pipe(
     getJobDeletionTasksResponsePayloadRT.decode(jobDeletionTasksResponse),
@@ -51,7 +49,7 @@ export const callStopDatafeeds = async <JobType extends string>(
   jobTypes: JobType[]
 ) => {
   // Stop datafeed due to https://github.com/elastic/kibana/issues/44652
-  const stopDatafeedResponse = await npStart.core.http.fetch('/api/ml/jobs/stop_datafeeds', {
+  const stopDatafeedResponse = await npStart.http.fetch('/api/ml/jobs/stop_datafeeds', {
     method: 'POST',
     body: JSON.stringify(
       stopDatafeedsRequestPayloadRT.encode({
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts
similarity index 93%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts
rename to x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts
index a067285026e33..ef463561a863f 100644
--- a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts
+++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts
@@ -8,7 +8,7 @@ import { pipe } from 'fp-ts/lib/pipeable';
 import { fold } from 'fp-ts/lib/Either';
 import { identity } from 'fp-ts/lib/function';
 import * as rt from 'io-ts';
-import { npStart } from 'ui/new_platform';
+import { npStart } from '../../../../legacy_singletons';
 import { jobCustomSettingsRT } from './ml_api_types';
 import { throwErrors, createPlainError } from '../../../../../common/runtime_types';
 import { getJobId } from '../../../../../common/log_analysis';
@@ -18,7 +18,7 @@ export const callJobsSummaryAPI = async <JobType extends string>(
   sourceId: string,
   jobTypes: JobType[]
 ) => {
-  const response = await npStart.core.http.fetch('/api/ml/jobs/jobs_summary', {
+  const response = await npStart.http.fetch('/api/ml/jobs/jobs_summary', {
     method: 'POST',
     body: JSON.stringify(
       fetchJobStatusRequestPayloadRT.encode({
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_get_module.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_module.ts
similarity index 88%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_get_module.ts
rename to x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_module.ts
index ae90c7bb84130..7c34eeca76a98 100644
--- a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_get_module.ts
+++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_module.ts
@@ -8,12 +8,12 @@ import { fold } from 'fp-ts/lib/Either';
 import { pipe } from 'fp-ts/lib/pipeable';
 import { identity } from 'fp-ts/lib/function';
 import * as rt from 'io-ts';
-import { npStart } from 'ui/new_platform';
+import { npStart } from '../../../../legacy_singletons';
 import { throwErrors, createPlainError } from '../../../../../common/runtime_types';
 import { jobCustomSettingsRT } from './ml_api_types';
 
 export const callGetMlModuleAPI = async (moduleId: string) => {
-  const response = await npStart.core.http.fetch(`/api/ml/modules/get_module/${moduleId}`, {
+  const response = await npStart.http.fetch(`/api/ml/modules/get_module/${moduleId}`, {
     method: 'GET',
   });
 
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_setup_module_api.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_setup_module_api.ts
similarity index 95%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_setup_module_api.ts
rename to x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_setup_module_api.ts
index 774a521ff1d34..d90bbab267871 100644
--- a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_setup_module_api.ts
+++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_setup_module_api.ts
@@ -8,7 +8,7 @@ import { fold } from 'fp-ts/lib/Either';
 import { pipe } from 'fp-ts/lib/pipeable';
 import { identity } from 'fp-ts/lib/function';
 import * as rt from 'io-ts';
-import { npStart } from 'ui/new_platform';
+import { npStart } from '../../../../legacy_singletons';
 import { throwErrors, createPlainError } from '../../../../../common/runtime_types';
 import { getJobIdPrefix } from '../../../../../common/log_analysis';
 
@@ -22,7 +22,7 @@ export const callSetupMlModuleAPI = async (
   jobOverrides: SetupMlModuleJobOverrides[] = [],
   datafeedOverrides: SetupMlModuleDatafeedOverrides[] = []
 ) => {
-  const response = await npStart.core.http.fetch(`/api/ml/modules/setup/${moduleId}`, {
+  const response = await npStart.http.fetch(`/api/ml/modules/setup/${moduleId}`, {
     method: 'POST',
     body: JSON.stringify(
       setupMlModuleRequestPayloadRT.encode({
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/validate_indices.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/validate_indices.ts
similarity index 88%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/validate_indices.ts
rename to x-pack/plugins/infra/public/containers/logs/log_analysis/api/validate_indices.ts
index 480e84ff53e43..bbef7d201045f 100644
--- a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/validate_indices.ts
+++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/validate_indices.ts
@@ -7,7 +7,7 @@
 import { fold } from 'fp-ts/lib/Either';
 import { pipe } from 'fp-ts/lib/pipeable';
 import { identity } from 'fp-ts/lib/function';
-import { npStart } from 'ui/new_platform';
+import { npStart } from '../../../../legacy_singletons';
 import {
   LOG_ANALYSIS_VALIDATE_INDICES_PATH,
   ValidationIndicesFieldSpecification,
@@ -21,7 +21,7 @@ export const callValidateIndicesAPI = async (
   indices: string[],
   fields: ValidationIndicesFieldSpecification[]
 ) => {
-  const response = await npStart.core.http.fetch(LOG_ANALYSIS_VALIDATE_INDICES_PATH, {
+  const response = await npStart.http.fetch(LOG_ANALYSIS_VALIDATE_INDICES_PATH, {
     method: 'POST',
     body: JSON.stringify(validationIndicesRequestPayloadRT.encode({ data: { indices, fields } })),
   });
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/index.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/index.ts
rename to x-pack/plugins/infra/public/containers/logs/log_analysis/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_capabilities.tsx b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_capabilities.tsx
similarity index 95%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_capabilities.tsx
rename to x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_capabilities.tsx
index 3c10ba805fad5..7bee10c3bfaa7 100644
--- a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_capabilities.tsx
+++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_capabilities.tsx
@@ -6,11 +6,11 @@
 
 import createContainer from 'constate';
 import { useMemo, useState, useEffect } from 'react';
-import { npStart } from 'ui/new_platform';
 import { fold } from 'fp-ts/lib/Either';
 import { pipe } from 'fp-ts/lib/pipeable';
 import { identity } from 'fp-ts/lib/function';
 import { useTrackedPromise } from '../../../utils/use_tracked_promise';
+import { npStart } from '../../../legacy_singletons';
 import {
   getMlCapabilitiesResponsePayloadRT,
   GetMlCapabilitiesResponsePayload,
@@ -26,7 +26,7 @@ export const useLogAnalysisCapabilities = () => {
     {
       cancelPreviousOn: 'resolution',
       createPromise: async () => {
-        const rawResponse = await npStart.core.http.fetch('/api/ml/ml_capabilities');
+        const rawResponse = await npStart.http.fetch('/api/ml/ml_capabilities');
 
         return pipe(
           getMlCapabilitiesResponsePayloadRT.decode(rawResponse),
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_cleanup.tsx b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_cleanup.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_cleanup.tsx
rename to x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_cleanup.tsx
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_module.tsx b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_module.tsx
rename to x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module.tsx
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_status.tsx b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_status.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_status.tsx
rename to x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_status.tsx
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_types.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_types.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_types.ts
rename to x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_types.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_setup_state.tsx b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_setup_state.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_setup_state.tsx
rename to x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_setup_state.tsx
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/api/fetch_log_entries_item.ts b/x-pack/plugins/infra/public/containers/logs/log_entries/api/fetch_log_entries_item.ts
similarity index 87%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_entries/api/fetch_log_entries_item.ts
rename to x-pack/plugins/infra/public/containers/logs/log_entries/api/fetch_log_entries_item.ts
index fd093f20dd490..5fde01e458e36 100644
--- a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/api/fetch_log_entries_item.ts
+++ b/x-pack/plugins/infra/public/containers/logs/log_entries/api/fetch_log_entries_item.ts
@@ -7,7 +7,7 @@
 import { fold } from 'fp-ts/lib/Either';
 import { pipe } from 'fp-ts/lib/pipeable';
 import { identity } from 'fp-ts/lib/function';
-import { kfetch } from 'ui/kfetch';
+import { npStart } from '../../../../legacy_singletons';
 
 import { throwErrors, createPlainError } from '../../../../../common/runtime_types';
 
@@ -19,9 +19,8 @@ import {
 } from '../../../../../common/http_api';
 
 export const fetchLogEntriesItem = async (requestArgs: LogEntriesItemRequest) => {
-  const response = await kfetch({
+  const response = await npStart.http.fetch(LOG_ENTRIES_ITEM_PATH, {
     method: 'POST',
-    pathname: LOG_ENTRIES_ITEM_PATH,
     body: JSON.stringify(logEntriesItemRequestRT.encode(requestArgs)),
   });
 
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/gql_queries.ts b/x-pack/plugins/infra/public/containers/logs/log_entries/gql_queries.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_entries/gql_queries.ts
rename to x-pack/plugins/infra/public/containers/logs/log_entries/gql_queries.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_entries/index.ts
rename to x-pack/plugins/infra/public/containers/logs/log_entries/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_entries/types.ts b/x-pack/plugins/infra/public/containers/logs/log_entries/types.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_entries/types.ts
rename to x-pack/plugins/infra/public/containers/logs/log_entries/types.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_filter/index.ts b/x-pack/plugins/infra/public/containers/logs/log_filter/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_filter/index.ts
rename to x-pack/plugins/infra/public/containers/logs/log_filter/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_filter/log_filter_state.ts b/x-pack/plugins/infra/public/containers/logs/log_filter/log_filter_state.ts
similarity index 97%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_filter/log_filter_state.ts
rename to x-pack/plugins/infra/public/containers/logs/log_filter/log_filter_state.ts
index 2911ee729638a..dd1ba585061fe 100644
--- a/x-pack/legacy/plugins/infra/public/containers/logs/log_filter/log_filter_state.ts
+++ b/x-pack/plugins/infra/public/containers/logs/log_filter/log_filter_state.ts
@@ -7,7 +7,7 @@
 import { useState, useMemo } from 'react';
 import createContainer from 'constate';
 import { IIndexPattern } from 'src/plugins/data/public';
-import { esKuery } from '../../../../../../../../src/plugins/data/public';
+import { esKuery } from '../../../../../../../src/plugins/data/public';
 import { convertKueryToElasticSearchQuery } from '../../../utils/kuery';
 
 export interface KueryFilterQuery {
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_filter/with_log_filter_url_state.tsx b/x-pack/plugins/infra/public/containers/logs/log_filter/with_log_filter_url_state.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_filter/with_log_filter_url_state.tsx
rename to x-pack/plugins/infra/public/containers/logs/log_filter/with_log_filter_url_state.tsx
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_flyout.tsx b/x-pack/plugins/infra/public/containers/logs/log_flyout.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_flyout.tsx
rename to x-pack/plugins/infra/public/containers/logs/log_flyout.tsx
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/api/fetch_log_summary_highlights.ts b/x-pack/plugins/infra/public/containers/logs/log_highlights/api/fetch_log_summary_highlights.ts
similarity index 87%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/api/fetch_log_summary_highlights.ts
rename to x-pack/plugins/infra/public/containers/logs/log_highlights/api/fetch_log_summary_highlights.ts
index 781e8401cf881..bda8f535549c7 100644
--- a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/api/fetch_log_summary_highlights.ts
+++ b/x-pack/plugins/infra/public/containers/logs/log_highlights/api/fetch_log_summary_highlights.ts
@@ -6,8 +6,7 @@
 import { fold } from 'fp-ts/lib/Either';
 import { pipe } from 'fp-ts/lib/pipeable';
 import { identity } from 'fp-ts/lib/function';
-import { kfetch } from 'ui/kfetch';
-
+import { npStart } from '../../../../legacy_singletons';
 import { throwErrors, createPlainError } from '../../../../../common/runtime_types';
 
 import {
@@ -20,9 +19,8 @@ import {
 export const fetchLogSummaryHighlights = async (
   requestArgs: LogEntriesSummaryHighlightsRequest
 ) => {
-  const response = await kfetch({
+  const response = await npStart.http.fetch(LOG_ENTRIES_SUMMARY_HIGHLIGHTS_PATH, {
     method: 'POST',
-    pathname: LOG_ENTRIES_SUMMARY_HIGHLIGHTS_PATH,
     body: JSON.stringify(logEntriesSummaryHighlightsRequestRT.encode(requestArgs)),
   });
 
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/index.ts b/x-pack/plugins/infra/public/containers/logs/log_highlights/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/index.ts
rename to x-pack/plugins/infra/public/containers/logs/log_highlights/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.gql_query.ts b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.gql_query.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.gql_query.ts
rename to x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.gql_query.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx
rename to x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx
rename to x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts
rename to x-pack/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/next_and_previous.tsx b/x-pack/plugins/infra/public/containers/logs/log_highlights/next_and_previous.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/next_and_previous.tsx
rename to x-pack/plugins/infra/public/containers/logs/log_highlights/next_and_previous.tsx
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/index.ts b/x-pack/plugins/infra/public/containers/logs/log_position/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_position/index.ts
rename to x-pack/plugins/infra/public/containers/logs/log_position/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts b/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_position/log_position_state.ts
rename to x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx b/x-pack/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx
rename to x-pack/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/api/fetch_log_summary.ts b/x-pack/plugins/infra/public/containers/logs/log_summary/api/fetch_log_summary.ts
similarity index 87%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_summary/api/fetch_log_summary.ts
rename to x-pack/plugins/infra/public/containers/logs/log_summary/api/fetch_log_summary.ts
index 5dabb8ebf0179..f74f0dc0e3117 100644
--- a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/api/fetch_log_summary.ts
+++ b/x-pack/plugins/infra/public/containers/logs/log_summary/api/fetch_log_summary.ts
@@ -7,8 +7,7 @@
 import { fold } from 'fp-ts/lib/Either';
 import { pipe } from 'fp-ts/lib/pipeable';
 import { identity } from 'fp-ts/lib/function';
-import { kfetch } from 'ui/kfetch';
-
+import { npStart } from '../../../../legacy_singletons';
 import { throwErrors, createPlainError } from '../../../../../common/runtime_types';
 
 import {
@@ -19,9 +18,8 @@ import {
 } from '../../../../../common/http_api';
 
 export const fetchLogSummary = async (requestArgs: LogEntriesSummaryRequest) => {
-  const response = await kfetch({
+  const response = await npStart.http.fetch(LOG_ENTRIES_SUMMARY_PATH, {
     method: 'POST',
-    pathname: LOG_ENTRIES_SUMMARY_PATH,
     body: JSON.stringify(logEntriesSummaryRequestRT.encode(requestArgs)),
   });
 
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/index.ts b/x-pack/plugins/infra/public/containers/logs/log_summary/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_summary/index.ts
rename to x-pack/plugins/infra/public/containers/logs/log_summary/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx b/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx
rename to x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/log_summary.tsx b/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_summary/log_summary.tsx
rename to x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.tsx
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/use_log_summary_buffer_interval.ts b/x-pack/plugins/infra/public/containers/logs/log_summary/use_log_summary_buffer_interval.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_summary/use_log_summary_buffer_interval.ts
rename to x-pack/plugins/infra/public/containers/logs/log_summary/use_log_summary_buffer_interval.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/with_summary.ts b/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_summary/with_summary.ts
rename to x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_view_configuration.test.tsx b/x-pack/plugins/infra/public/containers/logs/log_view_configuration.test.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_view_configuration.test.tsx
rename to x-pack/plugins/infra/public/containers/logs/log_view_configuration.test.tsx
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_view_configuration.tsx b/x-pack/plugins/infra/public/containers/logs/log_view_configuration.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/log_view_configuration.tsx
rename to x-pack/plugins/infra/public/containers/logs/log_view_configuration.tsx
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/with_log_minimap.tsx b/x-pack/plugins/infra/public/containers/logs/with_log_minimap.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/with_log_minimap.tsx
rename to x-pack/plugins/infra/public/containers/logs/with_log_minimap.tsx
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/with_log_textview.tsx b/x-pack/plugins/infra/public/containers/logs/with_log_textview.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/with_log_textview.tsx
rename to x-pack/plugins/infra/public/containers/logs/with_log_textview.tsx
diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/with_stream_items.ts b/x-pack/plugins/infra/public/containers/logs/with_stream_items.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/logs/with_stream_items.ts
rename to x-pack/plugins/infra/public/containers/logs/with_stream_items.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/metadata/lib/get_filtered_metrics.ts b/x-pack/plugins/infra/public/containers/metadata/lib/get_filtered_metrics.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/metadata/lib/get_filtered_metrics.ts
rename to x-pack/plugins/infra/public/containers/metadata/lib/get_filtered_metrics.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/metadata/use_metadata.ts b/x-pack/plugins/infra/public/containers/metadata/use_metadata.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/metadata/use_metadata.ts
rename to x-pack/plugins/infra/public/containers/metadata/use_metadata.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.test.tsx b/x-pack/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.test.tsx
similarity index 81%
rename from x-pack/legacy/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.test.tsx
rename to x-pack/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.test.tsx
index 4baa93f93753c..bbc8778545b4a 100644
--- a/x-pack/legacy/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.test.tsx
+++ b/x-pack/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.test.tsx
@@ -4,10 +4,11 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { fetch } from '../../utils/fetch';
+import React from 'react';
 import { useMetricsExplorerData } from './use_metrics_explorer_data';
 
 import { renderHook } from '@testing-library/react-hooks';
+import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
 
 import {
   options,
@@ -18,8 +19,18 @@ import {
   createSeries,
 } from '../../utils/fixtures/metrics_explorer';
 
-const renderUseMetricsExplorerDataHook = () =>
-  renderHook(
+const mockedFetch = jest.fn();
+
+const renderUseMetricsExplorerDataHook = () => {
+  const wrapper: React.FC = ({ children }) => {
+    const services = {
+      http: {
+        fetch: mockedFetch,
+      },
+    };
+    return <KibanaContextProvider services={services}>{children}</KibanaContextProvider>;
+  };
+  return renderHook(
     props =>
       useMetricsExplorerData(
         props.options,
@@ -38,14 +49,20 @@ const renderUseMetricsExplorerDataHook = () =>
         afterKey: null as string | null,
         signal: 1,
       },
+      wrapper,
     }
   );
+};
+
+jest.mock('../../utils/kuery', () => {
+  return {
+    convertKueryToElasticSearchQuery: (query: string) => query,
+  };
+});
 
-jest.mock('../../utils/fetch');
-const mockedFetch = fetch as jest.Mocked<typeof fetch>;
 describe('useMetricsExplorerData Hook', () => {
   it('should just work', async () => {
-    mockedFetch.post.mockResolvedValue({ data: resp } as any);
+    mockedFetch.mockResolvedValue(resp as any);
     const { result, waitForNextUpdate } = renderUseMetricsExplorerDataHook();
     expect(result.current.data).toBe(null);
     expect(result.current.loading).toBe(true);
@@ -58,7 +75,7 @@ describe('useMetricsExplorerData Hook', () => {
   });
 
   it('should paginate', async () => {
-    mockedFetch.post.mockResolvedValue({ data: resp } as any);
+    mockedFetch.mockResolvedValue(resp as any);
     const { result, waitForNextUpdate, rerender } = renderUseMetricsExplorerDataHook();
     expect(result.current.data).toBe(null);
     expect(result.current.loading).toBe(true);
@@ -68,11 +85,9 @@ describe('useMetricsExplorerData Hook', () => {
     const { series, pageInfo } = result.current.data!;
     expect(series).toBeDefined();
     expect(series.length).toBe(3);
-    mockedFetch.post.mockResolvedValue({
-      data: {
-        pageInfo: { total: 10, afterKey: 'host-06' },
-        series: [createSeries('host-04'), createSeries('host-05'), createSeries('host-06')],
-      },
+    mockedFetch.mockResolvedValue({
+      pageInfo: { total: 10, afterKey: 'host-06' },
+      series: [createSeries('host-04'), createSeries('host-05'), createSeries('host-06')],
     } as any);
     rerender({
       options,
@@ -92,7 +107,7 @@ describe('useMetricsExplorerData Hook', () => {
 
   it('should reset error upon recovery', async () => {
     const error = new Error('Network Error');
-    mockedFetch.post.mockRejectedValue(error);
+    mockedFetch.mockRejectedValue(error);
     const { result, waitForNextUpdate, rerender } = renderUseMetricsExplorerDataHook();
     expect(result.current.data).toBe(null);
     expect(result.current.error).toEqual(null);
@@ -101,7 +116,7 @@ describe('useMetricsExplorerData Hook', () => {
     expect(result.current.data).toEqual(null);
     expect(result.current.error).toEqual(error);
     expect(result.current.loading).toBe(false);
-    mockedFetch.post.mockResolvedValue({ data: resp } as any);
+    mockedFetch.mockResolvedValue(resp as any);
     rerender({
       options,
       source,
@@ -117,7 +132,7 @@ describe('useMetricsExplorerData Hook', () => {
   });
 
   it('should not paginate on option change', async () => {
-    mockedFetch.post.mockResolvedValue({ data: resp } as any);
+    mockedFetch.mockResolvedValue(resp as any);
     const { result, waitForNextUpdate, rerender } = renderUseMetricsExplorerDataHook();
     expect(result.current.data).toBe(null);
     expect(result.current.loading).toBe(true);
@@ -127,7 +142,7 @@ describe('useMetricsExplorerData Hook', () => {
     const { series, pageInfo } = result.current.data!;
     expect(series).toBeDefined();
     expect(series.length).toBe(3);
-    mockedFetch.post.mockResolvedValue({ data: resp } as any);
+    mockedFetch.mockResolvedValue(resp as any);
     rerender({
       options: {
         ...options,
@@ -147,7 +162,7 @@ describe('useMetricsExplorerData Hook', () => {
   });
 
   it('should not paginate on time change', async () => {
-    mockedFetch.post.mockResolvedValue({ data: resp } as any);
+    mockedFetch.mockResolvedValue(resp as any);
     const { result, waitForNextUpdate, rerender } = renderUseMetricsExplorerDataHook();
     expect(result.current.data).toBe(null);
     expect(result.current.loading).toBe(true);
@@ -157,7 +172,7 @@ describe('useMetricsExplorerData Hook', () => {
     const { series, pageInfo } = result.current.data!;
     expect(series).toBeDefined();
     expect(series.length).toBe(3);
-    mockedFetch.post.mockResolvedValue({ data: resp } as any);
+    mockedFetch.mockResolvedValue(resp as any);
     rerender({
       options,
       source,
diff --git a/x-pack/legacy/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.ts b/x-pack/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.ts
similarity index 59%
rename from x-pack/legacy/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.ts
rename to x-pack/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.ts
index 1bca733a6e02a..b32496fbf30a1 100644
--- a/x-pack/legacy/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.ts
+++ b/x-pack/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.ts
@@ -9,10 +9,14 @@ import { isEqual } from 'lodash';
 import { useEffect, useState } from 'react';
 import { IIndexPattern } from 'src/plugins/data/public';
 import { SourceQuery } from '../../../common/graphql/types';
-import { MetricsExplorerResponse } from '../../../server/routes/metrics_explorer/types';
-import { fetch } from '../../utils/fetch';
+import {
+  MetricsExplorerResponse,
+  metricsExplorerResponseRT,
+} from '../../../common/http_api/metrics_explorer';
 import { convertKueryToElasticSearchQuery } from '../../utils/kuery';
 import { MetricsExplorerOptions, MetricsExplorerTimeOptions } from './use_metrics_explorer_options';
+import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
+import { decodeOrThrow } from '../../../common/runtime_types';
 
 function isSameOptions(current: MetricsExplorerOptions, next: MetricsExplorerOptions) {
   return isEqual(current, next);
@@ -26,6 +30,7 @@ export function useMetricsExplorerData(
   afterKey: string | null,
   signal: any
 ) {
+  const fetch = useKibana().services.http?.fetch;
   const [error, setError] = useState<Error | null>(null);
   const [loading, setLoading] = useState<boolean>(true);
   const [data, setData] = useState<MetricsExplorerResponse | null>(null);
@@ -41,48 +46,54 @@ export function useMetricsExplorerData(
         if (!from || !to) {
           throw new Error('Unalble to parse timerange');
         }
-        const response = await fetch.post<MetricsExplorerResponse>(
-          '../api/infra/metrics_explorer',
-          {
-            metrics:
-              options.aggregation === 'count'
-                ? [{ aggregation: 'count' }]
-                : options.metrics.map(metric => ({
-                    aggregation: metric.aggregation,
-                    field: metric.field,
-                  })),
-            groupBy: options.groupBy,
-            afterKey,
-            limit: options.limit,
-            indexPattern: source.metricAlias,
-            filterQuery:
-              (options.filterQuery &&
-                convertKueryToElasticSearchQuery(options.filterQuery, derivedIndexPattern)) ||
-              void 0,
-            timerange: {
-              ...timerange,
-              field: source.fields.timestamp,
-              from: from.valueOf(),
-              to: to.valueOf(),
-            },
-          }
+        if (!fetch) {
+          throw new Error('HTTP service is unavailable');
+        }
+        const response = decodeOrThrow(metricsExplorerResponseRT)(
+          await fetch('/api/infra/metrics_explorer', {
+            method: 'POST',
+            body: JSON.stringify({
+              metrics:
+                options.aggregation === 'count'
+                  ? [{ aggregation: 'count' }]
+                  : options.metrics.map(metric => ({
+                      aggregation: metric.aggregation,
+                      field: metric.field,
+                    })),
+              groupBy: options.groupBy,
+              afterKey,
+              limit: options.limit,
+              indexPattern: source.metricAlias,
+              filterQuery:
+                (options.filterQuery &&
+                  convertKueryToElasticSearchQuery(options.filterQuery, derivedIndexPattern)) ||
+                void 0,
+              timerange: {
+                ...timerange,
+                field: source.fields.timestamp,
+                from: from.valueOf(),
+                to: to.valueOf(),
+              },
+            }),
+          })
         );
-        if (response.data) {
+
+        if (response) {
           if (
             data &&
             lastOptions &&
-            data.pageInfo.afterKey !== response.data.pageInfo.afterKey &&
+            data.pageInfo.afterKey !== response.pageInfo.afterKey &&
             isSameOptions(lastOptions, options) &&
             isEqual(timerange, lastTimerange) &&
             afterKey
           ) {
             const { series } = data;
             setData({
-              ...response.data,
-              series: [...series, ...response.data.series],
+              ...response,
+              series: [...series, ...response.series],
             });
           } else {
-            setData(response.data);
+            setData(response);
           }
           setLastOptions(options);
           setLastTimerange(timerange);
diff --git a/x-pack/legacy/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_options.test.tsx b/x-pack/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_options.test.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_options.test.tsx
rename to x-pack/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_options.test.tsx
diff --git a/x-pack/legacy/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_options.ts b/x-pack/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_options.ts
similarity index 98%
rename from x-pack/legacy/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_options.ts
rename to x-pack/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_options.ts
index 076a9159940e2..2b802af8e8c15 100644
--- a/x-pack/legacy/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_options.ts
+++ b/x-pack/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_options.ts
@@ -10,7 +10,7 @@ import { MetricsExplorerColor } from '../../../common/color_palette';
 import {
   MetricsExplorerAggregation,
   MetricsExplorerMetric,
-} from '../../../server/routes/metrics_explorer/types';
+} from '../../../common/http_api/metrics_explorer';
 
 export type MetricsExplorerOptionsMetric = MetricsExplorerMetric & {
   color?: MetricsExplorerColor;
diff --git a/x-pack/legacy/plugins/infra/public/containers/metrics_explorer/with_metrics_explorer_options_url_state.tsx b/x-pack/plugins/infra/public/containers/metrics_explorer/with_metrics_explorer_options_url_state.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/metrics_explorer/with_metrics_explorer_options_url_state.tsx
rename to x-pack/plugins/infra/public/containers/metrics_explorer/with_metrics_explorer_options_url_state.tsx
diff --git a/x-pack/legacy/plugins/infra/public/containers/node_details/use_node_details.ts b/x-pack/plugins/infra/public/containers/node_details/use_node_details.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/node_details/use_node_details.ts
rename to x-pack/plugins/infra/public/containers/node_details/use_node_details.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/source/create_source.gql_query.ts b/x-pack/plugins/infra/public/containers/source/create_source.gql_query.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/source/create_source.gql_query.ts
rename to x-pack/plugins/infra/public/containers/source/create_source.gql_query.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/source/index.ts b/x-pack/plugins/infra/public/containers/source/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/source/index.ts
rename to x-pack/plugins/infra/public/containers/source/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/source/query_source.gql_query.ts b/x-pack/plugins/infra/public/containers/source/query_source.gql_query.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/source/query_source.gql_query.ts
rename to x-pack/plugins/infra/public/containers/source/query_source.gql_query.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/source/source.tsx b/x-pack/plugins/infra/public/containers/source/source.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/source/source.tsx
rename to x-pack/plugins/infra/public/containers/source/source.tsx
diff --git a/x-pack/legacy/plugins/infra/public/containers/source/source_fields_fragment.gql_query.ts b/x-pack/plugins/infra/public/containers/source/source_fields_fragment.gql_query.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/source/source_fields_fragment.gql_query.ts
rename to x-pack/plugins/infra/public/containers/source/source_fields_fragment.gql_query.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/source/update_source.gql_query.ts b/x-pack/plugins/infra/public/containers/source/update_source.gql_query.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/source/update_source.gql_query.ts
rename to x-pack/plugins/infra/public/containers/source/update_source.gql_query.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/source_id/index.ts b/x-pack/plugins/infra/public/containers/source_id/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/source_id/index.ts
rename to x-pack/plugins/infra/public/containers/source_id/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/source_id/source_id.ts b/x-pack/plugins/infra/public/containers/source_id/source_id.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/source_id/source_id.ts
rename to x-pack/plugins/infra/public/containers/source_id/source_id.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/waffle/index.ts b/x-pack/plugins/infra/public/containers/waffle/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/waffle/index.ts
rename to x-pack/plugins/infra/public/containers/waffle/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/waffle/nodes_to_wafflemap.ts b/x-pack/plugins/infra/public/containers/waffle/nodes_to_wafflemap.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/waffle/nodes_to_wafflemap.ts
rename to x-pack/plugins/infra/public/containers/waffle/nodes_to_wafflemap.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/waffle/type_guards.ts b/x-pack/plugins/infra/public/containers/waffle/type_guards.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/waffle/type_guards.ts
rename to x-pack/plugins/infra/public/containers/waffle/type_guards.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/waffle/use_snaphot.ts b/x-pack/plugins/infra/public/containers/waffle/use_snaphot.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/waffle/use_snaphot.ts
rename to x-pack/plugins/infra/public/containers/waffle/use_snaphot.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/waffle/waffle_nodes.gql_query.ts b/x-pack/plugins/infra/public/containers/waffle/waffle_nodes.gql_query.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/waffle/waffle_nodes.gql_query.ts
rename to x-pack/plugins/infra/public/containers/waffle/waffle_nodes.gql_query.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/waffle/with_waffle_filters.tsx b/x-pack/plugins/infra/public/containers/waffle/with_waffle_filters.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/waffle/with_waffle_filters.tsx
rename to x-pack/plugins/infra/public/containers/waffle/with_waffle_filters.tsx
diff --git a/x-pack/legacy/plugins/infra/public/containers/waffle/with_waffle_options.tsx b/x-pack/plugins/infra/public/containers/waffle/with_waffle_options.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/waffle/with_waffle_options.tsx
rename to x-pack/plugins/infra/public/containers/waffle/with_waffle_options.tsx
diff --git a/x-pack/legacy/plugins/infra/public/containers/waffle/with_waffle_time.tsx b/x-pack/plugins/infra/public/containers/waffle/with_waffle_time.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/waffle/with_waffle_time.tsx
rename to x-pack/plugins/infra/public/containers/waffle/with_waffle_time.tsx
diff --git a/x-pack/legacy/plugins/infra/public/containers/waffle/with_waffle_view_state.tsx b/x-pack/plugins/infra/public/containers/waffle/with_waffle_view_state.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/waffle/with_waffle_view_state.tsx
rename to x-pack/plugins/infra/public/containers/waffle/with_waffle_view_state.tsx
diff --git a/x-pack/legacy/plugins/infra/public/containers/with_kuery_autocompletion.tsx b/x-pack/plugins/infra/public/containers/with_kuery_autocompletion.tsx
similarity index 77%
rename from x-pack/legacy/plugins/infra/public/containers/with_kuery_autocompletion.tsx
rename to x-pack/plugins/infra/public/containers/with_kuery_autocompletion.tsx
index 8188517ba7617..98e59227bc6f0 100644
--- a/x-pack/legacy/plugins/infra/public/containers/with_kuery_autocompletion.tsx
+++ b/x-pack/plugins/infra/public/containers/with_kuery_autocompletion.tsx
@@ -5,11 +5,16 @@
  */
 
 import React from 'react';
-import { npStart } from 'ui/new_platform';
-import { QuerySuggestion, IIndexPattern } from 'src/plugins/data/public';
+import { QuerySuggestion, IIndexPattern, DataPublicPluginStart } from 'src/plugins/data/public';
+import {
+  withKibana,
+  KibanaReactContextValue,
+  KibanaServices,
+} from '../../../../../src/plugins/kibana_react/public';
 import { RendererFunction } from '../utils/typed_react';
 
 interface WithKueryAutocompletionLifecycleProps {
+  kibana: KibanaReactContextValue<{ data: DataPublicPluginStart } & KibanaServices>;
   children: RendererFunction<{
     isLoadingSuggestions: boolean;
     loadSuggestions: (expression: string, cursorPosition: number, maxSuggestions?: number) => void;
@@ -28,7 +33,7 @@ interface WithKueryAutocompletionLifecycleState {
   suggestions: QuerySuggestion[];
 }
 
-export class WithKueryAutocompletion extends React.Component<
+class WithKueryAutocompletionComponent extends React.Component<
   WithKueryAutocompletionLifecycleProps,
   WithKueryAutocompletionLifecycleState
 > {
@@ -54,7 +59,9 @@ export class WithKueryAutocompletion extends React.Component<
   ) => {
     const { indexPattern } = this.props;
     const language = 'kuery';
-    const hasQuerySuggestions = npStart.plugins.data.autocomplete.hasQuerySuggestions(language);
+    const hasQuerySuggestions = this.props.kibana.services.data.autocomplete.hasQuerySuggestions(
+      language
+    );
 
     if (!hasQuerySuggestions) {
       return;
@@ -69,7 +76,7 @@ export class WithKueryAutocompletion extends React.Component<
     });
 
     const suggestions =
-      (await npStart.plugins.data.autocomplete.getQuerySuggestions({
+      (await this.props.kibana.services.data.autocomplete.getQuerySuggestions({
         language,
         query: expression,
         selectionStart: cursorPosition,
@@ -91,3 +98,7 @@ export class WithKueryAutocompletion extends React.Component<
     );
   };
 }
+
+export const WithKueryAutocompletion = withKibana<WithKueryAutocompletionLifecycleProps>(
+  WithKueryAutocompletionComponent
+);
diff --git a/x-pack/legacy/plugins/infra/public/containers/with_options.tsx b/x-pack/plugins/infra/public/containers/with_options.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/with_options.tsx
rename to x-pack/plugins/infra/public/containers/with_options.tsx
diff --git a/x-pack/legacy/plugins/infra/public/containers/with_source/index.ts b/x-pack/plugins/infra/public/containers/with_source/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/with_source/index.ts
rename to x-pack/plugins/infra/public/containers/with_source/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/containers/with_source/with_source.tsx b/x-pack/plugins/infra/public/containers/with_source/with_source.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/containers/with_source/with_source.tsx
rename to x-pack/plugins/infra/public/containers/with_source/with_source.tsx
diff --git a/x-pack/legacy/plugins/infra/public/containers/with_state_from_location.tsx b/x-pack/plugins/infra/public/containers/with_state_from_location.tsx
similarity index 96%
rename from x-pack/legacy/plugins/infra/public/containers/with_state_from_location.tsx
rename to x-pack/plugins/infra/public/containers/with_state_from_location.tsx
index 6f7baf6b98b62..2a9676046d451 100644
--- a/x-pack/legacy/plugins/infra/public/containers/with_state_from_location.tsx
+++ b/x-pack/plugins/infra/public/containers/with_state_from_location.tsx
@@ -6,7 +6,7 @@
 
 import { parse, stringify } from 'query-string';
 import { Location } from 'history';
-import omit from 'lodash/fp/omit';
+import { omit } from 'lodash';
 import React from 'react';
 import { RouteComponentProps, withRouter } from 'react-router-dom';
 // eslint-disable-next-line @typescript-eslint/camelcase
@@ -46,7 +46,7 @@ export const withStateFromLocation = <StateInLocation extends {}>({
 
       public render() {
         const { location } = this.props;
-        const otherProps = omit(['location', 'history', 'match', 'staticContext'], this.props);
+        const otherProps = omit(this.props, ['location', 'history', 'match', 'staticContext']);
 
         const stateFromLocation = mapLocationToState(location);
 
diff --git a/x-pack/legacy/plugins/infra/public/graphql/introspection.json b/x-pack/plugins/infra/public/graphql/introspection.json
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/graphql/introspection.json
rename to x-pack/plugins/infra/public/graphql/introspection.json
diff --git a/x-pack/legacy/plugins/infra/public/graphql/log_entries.gql_query.ts b/x-pack/plugins/infra/public/graphql/log_entries.gql_query.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/graphql/log_entries.gql_query.ts
rename to x-pack/plugins/infra/public/graphql/log_entries.gql_query.ts
diff --git a/x-pack/legacy/plugins/infra/public/graphql/types.ts b/x-pack/plugins/infra/public/graphql/types.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/graphql/types.ts
rename to x-pack/plugins/infra/public/graphql/types.ts
diff --git a/x-pack/legacy/plugins/infra/public/hooks/use_bulk_get_saved_object.tsx b/x-pack/plugins/infra/public/hooks/use_bulk_get_saved_object.tsx
similarity index 71%
rename from x-pack/legacy/plugins/infra/public/hooks/use_bulk_get_saved_object.tsx
rename to x-pack/plugins/infra/public/hooks/use_bulk_get_saved_object.tsx
index 934022d6e6bd0..80d1b67e59798 100644
--- a/x-pack/legacy/plugins/infra/public/hooks/use_bulk_get_saved_object.tsx
+++ b/x-pack/plugins/infra/public/hooks/use_bulk_get_saved_object.tsx
@@ -5,11 +5,11 @@
  */
 
 import { useState, useCallback } from 'react';
-
-import { npStart } from 'ui/new_platform';
 import { SavedObjectAttributes, SavedObjectsBatchResponse } from 'src/core/public';
+import { useKibana } from '../../../../../src/plugins/kibana_react/public';
 
 export const useBulkGetSavedObject = (type: string) => {
+  const kibana = useKibana();
   const [data, setData] = useState<SavedObjectsBatchResponse<SavedObjectAttributes> | null>(null);
   const [error, setError] = useState<string | null>(null);
   const [loading, setLoading] = useState<boolean>(false);
@@ -19,7 +19,11 @@ export const useBulkGetSavedObject = (type: string) => {
       setLoading(true);
       const fetchData = async () => {
         try {
-          const d = await npStart.core.savedObjects.client.bulkGet(ids.map(i => ({ type, id: i })));
+          const savedObjectsClient = kibana.services.savedObjects?.client;
+          if (!savedObjectsClient) {
+            throw new Error('Saved objects client is unavailable');
+          }
+          const d = await savedObjectsClient.bulkGet(ids.map(i => ({ type, id: i })));
           setError(null);
           setLoading(false);
           setData(d);
@@ -30,7 +34,7 @@ export const useBulkGetSavedObject = (type: string) => {
       };
       fetchData();
     },
-    [type]
+    [type, kibana.services.savedObjects]
   );
 
   return {
diff --git a/x-pack/legacy/plugins/infra/public/hooks/use_create_saved_object.tsx b/x-pack/plugins/infra/public/hooks/use_create_saved_object.tsx
similarity index 74%
rename from x-pack/legacy/plugins/infra/public/hooks/use_create_saved_object.tsx
rename to x-pack/plugins/infra/public/hooks/use_create_saved_object.tsx
index f03a198355bb8..8313d496a0651 100644
--- a/x-pack/legacy/plugins/infra/public/hooks/use_create_saved_object.tsx
+++ b/x-pack/plugins/infra/public/hooks/use_create_saved_object.tsx
@@ -5,15 +5,15 @@
  */
 
 import { useState, useCallback } from 'react';
-
-import { npStart } from 'ui/new_platform';
 import {
   SavedObjectAttributes,
   SavedObjectsCreateOptions,
   SimpleSavedObject,
 } from 'src/core/public';
+import { useKibana } from '../../../../../src/plugins/kibana_react/public';
 
 export const useCreateSavedObject = (type: string) => {
+  const kibana = useKibana();
   const [data, setData] = useState<SimpleSavedObject<SavedObjectAttributes> | null>(null);
   const [createdId, setCreatedId] = useState<string | null>(null);
   const [error, setError] = useState<string | null>(null);
@@ -24,7 +24,11 @@ export const useCreateSavedObject = (type: string) => {
       setLoading(true);
       const save = async () => {
         try {
-          const d = await npStart.core.savedObjects.client.create(type, attributes, options);
+          const savedObjectsClient = kibana.services.savedObjects?.client;
+          if (!savedObjectsClient) {
+            throw new Error('Saved objects client is unavailable');
+          }
+          const d = await savedObjectsClient.create(type, attributes, options);
           setCreatedId(d.id);
           setError(null);
           setData(d);
@@ -36,7 +40,7 @@ export const useCreateSavedObject = (type: string) => {
       };
       save();
     },
-    [type]
+    [type, kibana.services.savedObjects]
   );
 
   return {
diff --git a/x-pack/legacy/plugins/infra/public/hooks/use_delete_saved_object.tsx b/x-pack/plugins/infra/public/hooks/use_delete_saved_object.tsx
similarity index 81%
rename from x-pack/legacy/plugins/infra/public/hooks/use_delete_saved_object.tsx
rename to x-pack/plugins/infra/public/hooks/use_delete_saved_object.tsx
index cd04d82cc215b..3f2d15b3b86aa 100644
--- a/x-pack/legacy/plugins/infra/public/hooks/use_delete_saved_object.tsx
+++ b/x-pack/plugins/infra/public/hooks/use_delete_saved_object.tsx
@@ -5,9 +5,10 @@
  */
 
 import { useState, useCallback } from 'react';
-import { npStart } from 'ui/new_platform';
+import { useKibana } from '../../../../../src/plugins/kibana_react/public';
 
 export const useDeleteSavedObject = (type: string) => {
+  const kibana = useKibana();
   const [error, setError] = useState<string | null>(null);
   const [loading, setLoading] = useState<boolean>(false);
   const [deletedId, setDeletedId] = useState<string | null>(null);
@@ -17,7 +18,7 @@ export const useDeleteSavedObject = (type: string) => {
       setLoading(true);
       const dobj = async () => {
         try {
-          await npStart.core.savedObjects.client.delete(type, id);
+          await kibana.services.savedObjects?.client.delete(type, id);
           setError(null);
           setDeletedId(id);
           setLoading(false);
@@ -28,7 +29,7 @@ export const useDeleteSavedObject = (type: string) => {
       };
       dobj();
     },
-    [type]
+    [type, kibana.services.savedObjects]
   );
 
   return {
diff --git a/x-pack/legacy/plugins/infra/public/hooks/use_find_saved_object.tsx b/x-pack/plugins/infra/public/hooks/use_find_saved_object.tsx
similarity index 67%
rename from x-pack/legacy/plugins/infra/public/hooks/use_find_saved_object.tsx
rename to x-pack/plugins/infra/public/hooks/use_find_saved_object.tsx
index 2487d830266b1..2b704e7984415 100644
--- a/x-pack/legacy/plugins/infra/public/hooks/use_find_saved_object.tsx
+++ b/x-pack/plugins/infra/public/hooks/use_find_saved_object.tsx
@@ -5,11 +5,11 @@
  */
 
 import { useState, useCallback } from 'react';
-
-import { npStart } from 'ui/new_platform';
 import { SavedObjectAttributes, SavedObjectsBatchResponse } from 'src/core/public';
+import { useKibana } from '../../../../../src/plugins/kibana_react/public';
 
 export const useFindSavedObject = <SavedObjectType extends SavedObjectAttributes>(type: string) => {
+  const kibana = useKibana();
   const [data, setData] = useState<SavedObjectsBatchResponse<SavedObjectType> | null>(null);
   const [error, setError] = useState<string | null>(null);
   const [loading, setLoading] = useState<boolean>(false);
@@ -18,7 +18,11 @@ export const useFindSavedObject = <SavedObjectType extends SavedObjectAttributes
       setLoading(true);
       const fetchData = async () => {
         try {
-          const d = await npStart.core.savedObjects.client.find<SavedObjectType>({
+          const savedObjectsClient = kibana.services.savedObjects?.client;
+          if (!savedObjectsClient) {
+            throw new Error('Saved objects client is unavailable');
+          }
+          const d = await savedObjectsClient.find<SavedObjectType>({
             type,
             search: query,
             searchFields,
@@ -33,14 +37,17 @@ export const useFindSavedObject = <SavedObjectType extends SavedObjectAttributes
       };
       fetchData();
     },
-    [type]
+    [type, kibana.services.savedObjects]
   );
 
   const hasView = async (name: string) => {
-    const objects = await npStart.core.savedObjects.client.find<SavedObjectType>({
+    const savedObjectsClient = kibana.services.savedObjects?.client;
+    if (!savedObjectsClient) {
+      throw new Error('Saved objects client is unavailable');
+    }
+    const objects = await savedObjectsClient.find<SavedObjectType>({
       type,
     });
-
     return objects.savedObjects.filter(o => o.attributes.name === name).length > 0;
   };
 
diff --git a/x-pack/legacy/plugins/infra/public/hooks/use_http_request.tsx b/x-pack/plugins/infra/public/hooks/use_http_request.tsx
similarity index 97%
rename from x-pack/legacy/plugins/infra/public/hooks/use_http_request.tsx
rename to x-pack/plugins/infra/public/hooks/use_http_request.tsx
index 2078c4bdc151d..50f4a636b48a3 100644
--- a/x-pack/legacy/plugins/infra/public/hooks/use_http_request.tsx
+++ b/x-pack/plugins/infra/public/hooks/use_http_request.tsx
@@ -8,7 +8,7 @@ import React, { useMemo, useState } from 'react';
 import { IHttpFetchError } from 'src/core/public';
 import { i18n } from '@kbn/i18n';
 import { useTrackedPromise } from '../utils/use_tracked_promise';
-import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
+import { useKibana } from '../../../../../src/plugins/kibana_react/public';
 
 export function useHTTPRequest<Response>(
   pathname: string,
diff --git a/x-pack/legacy/plugins/infra/public/hooks/use_interval.ts b/x-pack/plugins/infra/public/hooks/use_interval.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/hooks/use_interval.ts
rename to x-pack/plugins/infra/public/hooks/use_interval.ts
diff --git a/x-pack/plugins/infra/public/hooks/use_prefix_path_with_basepath.tsx b/x-pack/plugins/infra/public/hooks/use_prefix_path_with_basepath.tsx
new file mode 100644
index 0000000000000..652a3fb478605
--- /dev/null
+++ b/x-pack/plugins/infra/public/hooks/use_prefix_path_with_basepath.tsx
@@ -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 { useMemo } from 'react';
+import { useKibana } from '../../../../../src/plugins/kibana_react/public';
+
+export const usePrefixPathWithBasepath = () => {
+  const getUrlForApp = useKibana().services.application?.getUrlForApp;
+  const prefixer = useMemo(() => {
+    if (!getUrlForApp) {
+      throw new Error('Application core service is unavailable');
+    }
+
+    return (app: string, path: string) => {
+      return getUrlForApp(app, { path });
+    };
+  }, [getUrlForApp]);
+  return prefixer;
+};
diff --git a/x-pack/legacy/plugins/infra/public/hooks/use_saved_view.ts b/x-pack/plugins/infra/public/hooks/use_saved_view.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/hooks/use_saved_view.ts
rename to x-pack/plugins/infra/public/hooks/use_saved_view.ts
diff --git a/x-pack/legacy/plugins/infra/public/images/docker.svg b/x-pack/plugins/infra/public/images/docker.svg
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/images/docker.svg
rename to x-pack/plugins/infra/public/images/docker.svg
diff --git a/x-pack/legacy/plugins/infra/public/images/hosts.svg b/x-pack/plugins/infra/public/images/hosts.svg
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/images/hosts.svg
rename to x-pack/plugins/infra/public/images/hosts.svg
diff --git a/x-pack/legacy/plugins/infra/public/images/infra_mono_white.svg b/x-pack/plugins/infra/public/images/infra_mono_white.svg
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/images/infra_mono_white.svg
rename to x-pack/plugins/infra/public/images/infra_mono_white.svg
diff --git a/x-pack/legacy/plugins/infra/public/images/k8.svg b/x-pack/plugins/infra/public/images/k8.svg
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/images/k8.svg
rename to x-pack/plugins/infra/public/images/k8.svg
diff --git a/x-pack/legacy/plugins/infra/public/images/logging_mono_white.svg b/x-pack/plugins/infra/public/images/logging_mono_white.svg
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/images/logging_mono_white.svg
rename to x-pack/plugins/infra/public/images/logging_mono_white.svg
diff --git a/x-pack/legacy/plugins/infra/public/images/services.svg b/x-pack/plugins/infra/public/images/services.svg
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/images/services.svg
rename to x-pack/plugins/infra/public/images/services.svg
diff --git a/x-pack/legacy/plugins/infra/public/index.scss b/x-pack/plugins/infra/public/index.scss
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/index.scss
rename to x-pack/plugins/infra/public/index.scss
diff --git a/x-pack/plugins/infra/public/index.ts b/x-pack/plugins/infra/public/index.ts
new file mode 100644
index 0000000000000..4465bde377c12
--- /dev/null
+++ b/x-pack/plugins/infra/public/index.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 { PluginInitializerContext, PluginInitializer } from 'kibana/public';
+import { Plugin, ClientSetup, ClientStart, ClientPluginsSetup, ClientPluginsStart } from './plugin';
+
+export const plugin: PluginInitializer<
+  ClientSetup,
+  ClientStart,
+  ClientPluginsSetup,
+  ClientPluginsStart
+> = (context: PluginInitializerContext) => {
+  return new Plugin(context);
+};
+
+export { FORMATTERS } from './utils/formatters';
+export { InfraFormatterType } from './lib/lib';
+
+export type InfraAppId = 'logs' | 'metrics';
diff --git a/x-pack/legacy/plugins/infra/public/new_platform_index.ts b/x-pack/plugins/infra/public/legacy_singletons.ts
similarity index 55%
rename from x-pack/legacy/plugins/infra/public/new_platform_index.ts
rename to x-pack/plugins/infra/public/legacy_singletons.ts
index 33b40da236145..f57047f21c281 100644
--- a/x-pack/legacy/plugins/infra/public/new_platform_index.ts
+++ b/x-pack/plugins/infra/public/legacy_singletons.ts
@@ -3,10 +3,12 @@
  * or more contributor license agreements. Licensed under the Elastic License;
  * you may not use this file except in compliance with the Elastic License.
  */
+import { CoreStart } from 'kibana/public';
 
-import { PluginInitializerContext } from 'kibana/public';
-import { Plugin } from './new_platform_plugin';
+let npStart: CoreStart;
 
-export function plugin(context: PluginInitializerContext) {
-  return new Plugin(context);
+export function registerStartSingleton(start: CoreStart) {
+  npStart = start;
 }
+
+export { npStart };
diff --git a/x-pack/legacy/plugins/infra/public/lib/adapters/observable_api/kibana_observable_api.ts b/x-pack/plugins/infra/public/lib/adapters/observable_api/kibana_observable_api.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/lib/adapters/observable_api/kibana_observable_api.ts
rename to x-pack/plugins/infra/public/lib/adapters/observable_api/kibana_observable_api.ts
diff --git a/x-pack/legacy/plugins/infra/public/lib/lib.ts b/x-pack/plugins/infra/public/lib/lib.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/lib/lib.ts
rename to x-pack/plugins/infra/public/lib/lib.ts
diff --git a/x-pack/legacy/plugins/infra/public/pages/404.tsx b/x-pack/plugins/infra/public/pages/404.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/404.tsx
rename to x-pack/plugins/infra/public/pages/404.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/error.tsx b/x-pack/plugins/infra/public/pages/error.tsx
similarity index 96%
rename from x-pack/legacy/plugins/infra/public/pages/error.tsx
rename to x-pack/plugins/infra/public/pages/error.tsx
index b525dd9b165ba..b8b598ba1a98b 100644
--- a/x-pack/legacy/plugins/infra/public/pages/error.tsx
+++ b/x-pack/plugins/infra/public/pages/error.tsx
@@ -15,7 +15,7 @@ import {
 import { FormattedMessage } from '@kbn/i18n/react';
 import React from 'react';
 
-import euiStyled from '../../../../common/eui_styled_components';
+import { euiStyled } from '../../../observability/public';
 import { Header } from '../components/header';
 import { ColumnarPage, PageContent } from '../components/page';
 
diff --git a/x-pack/legacy/plugins/infra/public/pages/infrastructure/index.tsx b/x-pack/plugins/infra/public/pages/infrastructure/index.tsx
similarity index 89%
rename from x-pack/legacy/plugins/infra/public/pages/infrastructure/index.tsx
rename to x-pack/plugins/infra/public/pages/infrastructure/index.tsx
index 5eaa2850aebdb..2271147c9d088 100644
--- a/x-pack/legacy/plugins/infra/public/pages/infrastructure/index.tsx
+++ b/x-pack/plugins/infra/public/pages/infrastructure/index.tsx
@@ -23,7 +23,7 @@ import { SnapshotPage } from './snapshot';
 import { MetricsSettingsPage } from './settings';
 import { AppNavigation } from '../../components/navigation/app_navigation';
 import { SourceLoadingPage } from '../../components/source_loading_page';
-import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
+import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
 
 export const InfrastructurePage = ({ match }: RouteComponentProps) => {
   const uiCapabilities = useKibana().services.application?.capabilities;
@@ -65,28 +65,28 @@ export const InfrastructurePage = ({ match }: RouteComponentProps) => {
                 title: i18n.translate('xpack.infra.homePage.inventoryTabTitle', {
                   defaultMessage: 'Inventory',
                 }),
-                path: `${match.path}/inventory`,
+                path: '/inventory',
               },
               {
                 title: i18n.translate('xpack.infra.homePage.metricsExplorerTabTitle', {
                   defaultMessage: 'Metrics Explorer',
                 }),
-                path: `${match.path}/metrics-explorer`,
+                path: '/explorer',
               },
               {
                 title: i18n.translate('xpack.infra.homePage.settingsTabTitle', {
                   defaultMessage: 'Settings',
                 }),
-                path: `${match.path}/settings`,
+                path: '/settings',
               },
             ]}
           />
         </AppNavigation>
 
         <Switch>
-          <Route path={`${match.path}/inventory`} component={SnapshotPage} />
+          <Route path={'/inventory'} component={SnapshotPage} />
           <Route
-            path={`${match.path}/metrics-explorer`}
+            path={'/explorer'}
             render={props => (
               <WithSource>
                 {({ configuration, createDerivedIndexPattern }) => (
@@ -106,7 +106,7 @@ export const InfrastructurePage = ({ match }: RouteComponentProps) => {
               </WithSource>
             )}
           />
-          <Route path={`${match.path}/settings`} component={MetricsSettingsPage} />
+          <Route path={'/settings'} component={MetricsSettingsPage} />
         </Switch>
       </ColumnarPage>
     </Source.Provider>
diff --git a/x-pack/legacy/plugins/infra/public/pages/infrastructure/metrics_explorer/index.tsx b/x-pack/plugins/infra/public/pages/infrastructure/metrics_explorer/index.tsx
similarity index 97%
rename from x-pack/legacy/plugins/infra/public/pages/infrastructure/metrics_explorer/index.tsx
rename to x-pack/plugins/infra/public/pages/infrastructure/metrics_explorer/index.tsx
index 4db4319b91d3c..0999cea59731c 100644
--- a/x-pack/legacy/plugins/infra/public/pages/infrastructure/metrics_explorer/index.tsx
+++ b/x-pack/plugins/infra/public/pages/infrastructure/metrics_explorer/index.tsx
@@ -14,7 +14,7 @@ import { MetricsExplorerToolbar } from '../../../components/metrics_explorer/too
 import { SourceQuery } from '../../../../common/graphql/types';
 import { NoData } from '../../../components/empty_states';
 import { useMetricsExplorerState } from './use_metric_explorer_state';
-import { useTrackPageview } from '../../../hooks/use_track_metric';
+import { useTrackPageview } from '../../../../../observability/public';
 
 interface MetricsExplorerPageProps {
   source: SourceQuery.Query['source']['configuration'];
diff --git a/x-pack/legacy/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.test.tsx b/x-pack/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.test.tsx
similarity index 68%
rename from x-pack/legacy/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.test.tsx
rename to x-pack/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.test.tsx
index 14533d46aaef8..874ac0987023b 100644
--- a/x-pack/legacy/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.test.tsx
+++ b/x-pack/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.test.tsx
@@ -4,8 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { fetch } from '../../../utils/fetch';
-import { renderHook } from '@testing-library/react-hooks';
+import { renderHook, act } from '@testing-library/react-hooks';
 import { useMetricsExplorerState } from './use_metric_explorer_state';
 import { MetricsExplorerOptionsContainer } from '../../../containers/metrics_explorer/use_metrics_explorer_options';
 import React from 'react';
@@ -26,8 +25,15 @@ const renderUseMetricsExplorerStateHook = () =>
     ),
   });
 
-jest.mock('../../../utils/fetch');
-const mockedFetch = fetch as jest.Mocked<typeof fetch>;
+const mockedUseMetricsExplorerData = jest.fn();
+
+jest.mock('../../../containers/metrics_explorer/use_metrics_explorer_data', () => {
+  return {
+    useMetricsExplorerData: () => {
+      return mockedUseMetricsExplorerData();
+    },
+  };
+});
 
 interface LocalStore {
   [key: string]: string;
@@ -54,33 +60,36 @@ Object.defineProperty(window, 'localStorage', {
 
 describe('useMetricsExplorerState', () => {
   beforeEach(() => {
-    mockedFetch.post.mockResolvedValue({ data: resp } as any);
+    mockedUseMetricsExplorerData.mockReturnValue({
+      loading: false,
+      error: null,
+      data: null,
+    });
     delete STORE.MetricsExplorerOptions;
     delete STORE.MetricsExplorerTimeRange;
   });
 
   it('should just work', async () => {
-    const { result, waitForNextUpdate } = renderUseMetricsExplorerStateHook();
-    expect(result.current.data).toBe(null);
+    mockedUseMetricsExplorerData.mockReturnValue({
+      loading: true,
+      error: null,
+      data: resp,
+    });
+    const { result } = renderUseMetricsExplorerStateHook();
+    expect(result.current.data).toEqual(resp);
     expect(result.current.error).toBe(null);
     expect(result.current.loading).toBe(true);
-    await waitForNextUpdate();
-    expect(result.current.data).toEqual(resp);
-    expect(result.current.loading).toBe(false);
-    const { series } = result.current.data!;
-    expect(series).toBeDefined();
-    expect(series.length).toBe(3);
   });
 
   describe('handleRefresh', () => {
     it('should trigger an addition request when handleRefresh is called', async () => {
-      const { result, waitForNextUpdate } = renderUseMetricsExplorerStateHook();
-      await waitForNextUpdate();
-      expect(mockedFetch.post.mock.calls.length).toBe(2);
-      const { handleRefresh } = result.current;
-      handleRefresh();
-      await waitForNextUpdate();
-      expect(mockedFetch.post.mock.calls.length).toBe(3);
+      const { result } = renderUseMetricsExplorerStateHook();
+      expect(result.current.refreshSignal).toBe(0);
+      act(() => {
+        result.current.handleRefresh();
+      });
+      expect(result.current.afterKey).toBe(null);
+      expect(result.current.refreshSignal).toBe(1);
     });
   });
 
@@ -88,7 +97,9 @@ describe('useMetricsExplorerState', () => {
     it('should change the metric', async () => {
       const { result } = renderUseMetricsExplorerStateHook();
       const { handleMetricsChange } = result.current;
-      handleMetricsChange([{ aggregation: 'max', field: 'system.load.1' }]);
+      act(() => {
+        handleMetricsChange([{ aggregation: 'max', field: 'system.load.1' }]);
+      });
       expect(result.current.options.metrics).toEqual([
         { aggregation: 'max', field: 'system.load.1' },
       ]);
@@ -99,7 +110,9 @@ describe('useMetricsExplorerState', () => {
     it('should change the metric', async () => {
       const { result } = renderUseMetricsExplorerStateHook();
       const { handleGroupByChange } = result.current;
-      handleGroupByChange('host.name');
+      act(() => {
+        handleGroupByChange('host.name');
+      });
       expect(result.current.options.groupBy).toBeDefined();
       expect(result.current.options.groupBy).toBe('host.name');
     });
@@ -109,7 +122,9 @@ describe('useMetricsExplorerState', () => {
     it('should change the time range', async () => {
       const { result } = renderUseMetricsExplorerStateHook();
       const { handleTimeChange } = result.current;
-      handleTimeChange('now-10m', 'now');
+      act(() => {
+        handleTimeChange('now-10m', 'now');
+      });
       expect(result.current.currentTimerange).toEqual({
         from: 'now-10m',
         to: 'now',
@@ -122,38 +137,44 @@ describe('useMetricsExplorerState', () => {
     it('should set the filter query', async () => {
       const { result } = renderUseMetricsExplorerStateHook();
       const { handleFilterQuerySubmit } = result.current;
-      handleFilterQuerySubmit('host.name: "example-host-01"');
+      act(() => {
+        handleFilterQuerySubmit('host.name: "example-host-01"');
+      });
       expect(result.current.options.filterQuery).toBe('host.name: "example-host-01"');
     });
   });
 
   describe('handleAggregationChange', () => {
     it('should set the metrics to only count when selecting count', async () => {
-      const { result, waitForNextUpdate } = renderUseMetricsExplorerStateHook();
+      const { result } = renderUseMetricsExplorerStateHook();
       const { handleMetricsChange } = result.current;
-      handleMetricsChange([{ aggregation: 'avg', field: 'system.load.1' }]);
+      act(() => {
+        handleMetricsChange([{ aggregation: 'avg', field: 'system.load.1' }]);
+      });
       expect(result.current.options.metrics).toEqual([
         { aggregation: 'avg', field: 'system.load.1' },
       ]);
-      await waitForNextUpdate();
       const { handleAggregationChange } = result.current;
-      handleAggregationChange('count');
-      await waitForNextUpdate();
+      act(() => {
+        handleAggregationChange('count');
+      });
       expect(result.current.options.aggregation).toBe('count');
       expect(result.current.options.metrics).toEqual([{ aggregation: 'count' }]);
     });
 
     it('should change aggregation for metrics', async () => {
-      const { result, waitForNextUpdate } = renderUseMetricsExplorerStateHook();
+      const { result } = renderUseMetricsExplorerStateHook();
       const { handleMetricsChange } = result.current;
-      handleMetricsChange([{ aggregation: 'avg', field: 'system.load.1' }]);
+      act(() => {
+        handleMetricsChange([{ aggregation: 'avg', field: 'system.load.1' }]);
+      });
       expect(result.current.options.metrics).toEqual([
         { aggregation: 'avg', field: 'system.load.1' },
       ]);
-      await waitForNextUpdate();
       const { handleAggregationChange } = result.current;
-      handleAggregationChange('max');
-      await waitForNextUpdate();
+      act(() => {
+        handleAggregationChange('max');
+      });
       expect(result.current.options.aggregation).toBe('max');
       expect(result.current.options.metrics).toEqual([
         { aggregation: 'max', field: 'system.load.1' },
@@ -163,30 +184,32 @@ describe('useMetricsExplorerState', () => {
 
   describe('handleLoadMore', () => {
     it('should load more based on the afterKey', async () => {
-      const { result, waitForNextUpdate, rerender } = renderUseMetricsExplorerStateHook();
+      const { result, rerender } = renderUseMetricsExplorerStateHook();
       expect(result.current.data).toBe(null);
-      expect(result.current.loading).toBe(true);
-      await waitForNextUpdate();
-      expect(result.current.data).toEqual(resp);
       expect(result.current.loading).toBe(false);
+      mockedUseMetricsExplorerData.mockReturnValue({
+        loading: false,
+        error: null,
+        data: resp,
+      });
+      await rerender();
       const { series, pageInfo } = result.current.data!;
       expect(series).toBeDefined();
       expect(series.length).toBe(3);
-      mockedFetch.post.mockResolvedValue({
+      mockedUseMetricsExplorerData.mockReturnValue({
+        loading: false,
+        error: null,
         data: {
           pageInfo: { total: 10, afterKey: 'host-06' },
           series: [createSeries('host-04'), createSeries('host-05'), createSeries('host-06')],
-        },
-      } as any);
-      const { handleLoadMore } = result.current;
-      handleLoadMore(pageInfo.afterKey!);
+        } as any,
+      });
       await rerender();
-      expect(result.current.loading).toBe(true);
-      await waitForNextUpdate();
-      expect(result.current.loading).toBe(false);
-      const { series: nextSeries } = result.current.data!;
-      expect(nextSeries).toBeDefined();
-      expect(nextSeries.length).toBe(6);
+      const { handleLoadMore } = result.current;
+      act(() => {
+        handleLoadMore(pageInfo.afterKey!);
+      });
+      expect(result.current.afterKey).toBe(pageInfo.afterKey);
     });
   });
 });
diff --git a/x-pack/legacy/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.ts b/x-pack/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.ts
similarity index 97%
rename from x-pack/legacy/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.ts
rename to x-pack/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.ts
index 22d18234197b6..88e6d9d800661 100644
--- a/x-pack/legacy/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.ts
+++ b/x-pack/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.ts
@@ -9,7 +9,7 @@ import { IIndexPattern } from 'src/plugins/data/public';
 import {
   MetricsExplorerMetric,
   MetricsExplorerAggregation,
-} from '../../../../server/routes/metrics_explorer/types';
+} from '../../../../common/http_api/metrics_explorer';
 import { useMetricsExplorerData } from '../../../containers/metrics_explorer/use_metrics_explorer_data';
 import {
   MetricsExplorerOptionsContainer,
@@ -144,5 +144,8 @@ export const useMetricsExplorerState = (
     handleLoadMore: setAfterKey,
     defaultViewState,
     onViewStateChange,
+
+    refreshSignal,
+    afterKey,
   };
 };
diff --git a/x-pack/legacy/plugins/infra/public/pages/infrastructure/settings.tsx b/x-pack/plugins/infra/public/pages/infrastructure/settings.tsx
similarity index 89%
rename from x-pack/legacy/plugins/infra/public/pages/infrastructure/settings.tsx
rename to x-pack/plugins/infra/public/pages/infrastructure/settings.tsx
index d75af7879d17a..9414eb7d3e564 100644
--- a/x-pack/legacy/plugins/infra/public/pages/infrastructure/settings.tsx
+++ b/x-pack/plugins/infra/public/pages/infrastructure/settings.tsx
@@ -6,7 +6,7 @@
 
 import React from 'react';
 import { SourceConfigurationSettings } from '../../components/source_configuration/source_configuration_settings';
-import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
+import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
 
 export const MetricsSettingsPage = () => {
   const uiCapabilities = useKibana().services.application?.capabilities;
diff --git a/x-pack/legacy/plugins/infra/public/pages/infrastructure/snapshot/index.tsx b/x-pack/plugins/infra/public/pages/infrastructure/snapshot/index.tsx
similarity index 96%
rename from x-pack/legacy/plugins/infra/public/pages/infrastructure/snapshot/index.tsx
rename to x-pack/plugins/infra/public/pages/infrastructure/snapshot/index.tsx
index 27d276a50c30d..7f3be965af8db 100644
--- a/x-pack/legacy/plugins/infra/public/pages/infrastructure/snapshot/index.tsx
+++ b/x-pack/plugins/infra/public/pages/infrastructure/snapshot/index.tsx
@@ -26,8 +26,8 @@ import { Source } from '../../../containers/source';
 import { WithWaffleFilterUrlState } from '../../../containers/waffle/with_waffle_filters';
 import { WithWaffleOptionsUrlState } from '../../../containers/waffle/with_waffle_options';
 import { WithWaffleTimeUrlState } from '../../../containers/waffle/with_waffle_time';
-import { useTrackPageview } from '../../../hooks/use_track_metric';
-import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public';
+import { useTrackPageview } from '../../../../../observability/public';
+import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
 
 export const SnapshotPage = () => {
   const uiCapabilities = useKibana().services.application?.capabilities;
diff --git a/x-pack/legacy/plugins/infra/public/pages/infrastructure/snapshot/page_content.tsx b/x-pack/plugins/infra/public/pages/infrastructure/snapshot/page_content.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/infrastructure/snapshot/page_content.tsx
rename to x-pack/plugins/infra/public/pages/infrastructure/snapshot/page_content.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/infrastructure/snapshot/toolbar.tsx b/x-pack/plugins/infra/public/pages/infrastructure/snapshot/toolbar.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/infrastructure/snapshot/toolbar.tsx
rename to x-pack/plugins/infra/public/pages/infrastructure/snapshot/toolbar.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/link_to/index.ts b/x-pack/plugins/infra/public/pages/link_to/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/link_to/index.ts
rename to x-pack/plugins/infra/public/pages/link_to/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/pages/link_to/link_to.tsx b/x-pack/plugins/infra/public/pages/link_to/link_to.tsx
similarity index 54%
rename from x-pack/legacy/plugins/infra/public/pages/link_to/link_to.tsx
rename to x-pack/plugins/infra/public/pages/link_to/link_to.tsx
index 7a7c9eb8408a3..ce62a0a3f4e33 100644
--- a/x-pack/legacy/plugins/infra/public/pages/link_to/link_to.tsx
+++ b/x-pack/plugins/infra/public/pages/link_to/link_to.tsx
@@ -19,21 +19,23 @@ interface LinkToPageProps {
 
 const ITEM_TYPES = inventoryModels.map(m => m.id).join('|');
 
-export const LinkToPage: React.FC<LinkToPageProps> = props => (
-  <Switch>
-    <Route
-      path={`${props.match.url}/:sourceId?/:nodeType(${ITEM_TYPES})-logs/:nodeId`}
-      component={RedirectToNodeLogs}
-    />
-    <Route
-      path={`${props.match.url}/:nodeType(${ITEM_TYPES})-detail/:nodeId`}
-      component={RedirectToNodeDetail}
-    />
-    <Route
-      path={`${props.match.url}/host-detail-via-ip/:hostIp`}
-      component={RedirectToHostDetailViaIP}
-    />
-    <Route path={`${props.match.url}/:sourceId?/logs`} component={RedirectToLogs} />
-    <Redirect to="/infrastructure" />
-  </Switch>
-);
+export const LinkToPage: React.FC<LinkToPageProps> = props => {
+  return (
+    <Switch>
+      <Route
+        path={`${props.match.url}/:sourceId?/:nodeType(${ITEM_TYPES})-logs/:nodeId`}
+        component={RedirectToNodeLogs}
+      />
+      <Route
+        path={`${props.match.url}/:nodeType(${ITEM_TYPES})-detail/:nodeId`}
+        component={RedirectToNodeDetail}
+      />
+      <Route
+        path={`${props.match.url}/host-detail-via-ip/:hostIp`}
+        component={RedirectToHostDetailViaIP}
+      />
+      <Route path={`${props.match.url}/:sourceId?/logs`} component={RedirectToLogs} />
+      <Redirect to="/infrastructure" />
+    </Switch>
+  );
+};
diff --git a/x-pack/legacy/plugins/infra/public/pages/link_to/query_params.ts b/x-pack/plugins/infra/public/pages/link_to/query_params.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/link_to/query_params.ts
rename to x-pack/plugins/infra/public/pages/link_to/query_params.ts
diff --git a/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_host_detail_via_ip.tsx b/x-pack/plugins/infra/public/pages/link_to/redirect_to_host_detail_via_ip.tsx
similarity index 96%
rename from x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_host_detail_via_ip.tsx
rename to x-pack/plugins/infra/public/pages/link_to/redirect_to_host_detail_via_ip.tsx
index c1b0814f550a3..01b02f1acbbf2 100644
--- a/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_host_detail_via_ip.tsx
+++ b/x-pack/plugins/infra/public/pages/link_to/redirect_to_host_detail_via_ip.tsx
@@ -49,7 +49,7 @@ export const RedirectToHostDetailViaIP = ({
   )('');
 
   if (name) {
-    return <Redirect to={`/metrics/host/${name}?${searchString}`} />;
+    return <Redirect to={`/detail/host/${name}?${searchString}`} />;
   }
 
   return (
diff --git a/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_logs.test.tsx b/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.test.tsx
similarity index 79%
rename from x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_logs.test.tsx
rename to x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.test.tsx
index e9ec053f8c609..3efefe93990bd 100644
--- a/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_logs.test.tsx
+++ b/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.test.tsx
@@ -19,7 +19,7 @@ describe('RedirectToLogs component', () => {
 
     expect(component).toMatchInlineSnapshot(`
       <Redirect
-        to="/logs/stream?sourceId=default&logPosition=(position:(tiebreaker:0,time:1550671089404),streamLive:!f)&logFilter=(expression:'',kind:kuery)"
+        to="/stream?sourceId=default&logPosition=(position:(tiebreaker:0,time:1550671089404),streamLive:!f)&logFilter=(expression:'',kind:kuery)"
       />
     `);
   });
@@ -33,7 +33,7 @@ describe('RedirectToLogs component', () => {
 
     expect(component).toMatchInlineSnapshot(`
       <Redirect
-        to="/logs/stream?sourceId=default&logPosition=(position:(tiebreaker:0,time:1550671089404),streamLive:!f)&logFilter=(expression:'FILTER_FIELD:FILTER_VALUE',kind:kuery)"
+        to="/stream?sourceId=default&logPosition=(position:(tiebreaker:0,time:1550671089404),streamLive:!f)&logFilter=(expression:'FILTER_FIELD:FILTER_VALUE',kind:kuery)"
       />
     `);
   });
@@ -45,7 +45,7 @@ describe('RedirectToLogs component', () => {
 
     expect(component).toMatchInlineSnapshot(`
       <Redirect
-        to="/logs/stream?sourceId=SOME-OTHER-SOURCE&logFilter=(expression:'',kind:kuery)"
+        to="/stream?sourceId=SOME-OTHER-SOURCE&logFilter=(expression:'',kind:kuery)"
       />
     `);
   });
diff --git a/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_logs.tsx b/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.tsx
similarity index 92%
rename from x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_logs.tsx
rename to x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.tsx
index b3fcaff75aafd..cc4b6967d34fb 100644
--- a/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_logs.tsx
+++ b/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.tsx
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import compose from 'lodash/fp/compose';
+import { compose } from 'lodash';
 import React from 'react';
 import { match as RouteMatch, Redirect, RouteComponentProps } from 'react-router-dom';
 
@@ -30,5 +30,5 @@ export const RedirectToLogs = ({ location, match }: RedirectToLogsProps) => {
     replaceSourceIdInQueryString(sourceId)
   )('');
 
-  return <Redirect to={`/logs/stream?${searchString}`} />;
+  return <Redirect to={`/stream?${searchString}`} />;
 };
diff --git a/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_node_detail.tsx b/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_detail.tsx
similarity index 88%
rename from x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_node_detail.tsx
rename to x-pack/plugins/infra/public/pages/link_to/redirect_to_node_detail.tsx
index ec1cc8ba45332..55dd15158b96f 100644
--- a/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_node_detail.tsx
+++ b/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_detail.tsx
@@ -27,7 +27,7 @@ export const RedirectToNodeDetail = ({
     getToFromLocation(location)
   )('');
 
-  return <Redirect to={`/infrastructure/metrics/${nodeType}/${nodeId}?${searchString}`} />;
+  return <Redirect to={`/detail/${nodeType}/${nodeId}?${searchString}`} />;
 };
 
 export const getNodeDetailUrl = ({
@@ -42,5 +42,5 @@ export const getNodeDetailUrl = ({
   from?: number;
 }) => {
   const args = to && from ? `?to=${to}&from=${from}` : '';
-  return `#/link-to/${nodeType}-detail/${nodeId}${args}`;
+  return `link-to/${nodeType}-detail/${nodeId}${args}`;
 };
diff --git a/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_node_logs.test.tsx b/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.test.tsx
similarity index 79%
rename from x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_node_logs.test.tsx
rename to x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.test.tsx
index 1e97072cac109..7c1cb9d7329ef 100644
--- a/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_node_logs.test.tsx
+++ b/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.test.tsx
@@ -35,7 +35,7 @@ describe('RedirectToNodeLogs component', () => {
 
     expect(component).toMatchInlineSnapshot(`
       <Redirect
-        to="/logs?sourceId=default&logFilter=(expression:'HOST_FIELD:%20HOST_NAME',kind:kuery)"
+        to="/?sourceId=default&logFilter=(expression:'HOST_FIELD:%20HOST_NAME',kind:kuery)"
       />
     `);
   });
@@ -47,7 +47,7 @@ describe('RedirectToNodeLogs component', () => {
 
     expect(component).toMatchInlineSnapshot(`
       <Redirect
-        to="/logs?sourceId=default&logFilter=(expression:'CONTAINER_FIELD:%20CONTAINER_ID',kind:kuery)"
+        to="/?sourceId=default&logFilter=(expression:'CONTAINER_FIELD:%20CONTAINER_ID',kind:kuery)"
       />
     `);
   });
@@ -59,7 +59,7 @@ describe('RedirectToNodeLogs component', () => {
 
     expect(component).toMatchInlineSnapshot(`
       <Redirect
-        to="/logs?sourceId=default&logFilter=(expression:'POD_FIELD:%20POD_ID',kind:kuery)"
+        to="/?sourceId=default&logFilter=(expression:'POD_FIELD:%20POD_ID',kind:kuery)"
       />
     `);
   });
@@ -73,7 +73,7 @@ describe('RedirectToNodeLogs component', () => {
 
     expect(component).toMatchInlineSnapshot(`
       <Redirect
-        to="/logs?sourceId=default&logPosition=(position:(tiebreaker:0,time:1550671089404),streamLive:!f)&logFilter=(expression:'HOST_FIELD:%20HOST_NAME',kind:kuery)"
+        to="/?sourceId=default&logPosition=(position:(tiebreaker:0,time:1550671089404),streamLive:!f)&logFilter=(expression:'HOST_FIELD:%20HOST_NAME',kind:kuery)"
       />
     `);
   });
@@ -89,7 +89,7 @@ describe('RedirectToNodeLogs component', () => {
 
     expect(component).toMatchInlineSnapshot(`
       <Redirect
-        to="/logs?sourceId=default&logPosition=(position:(tiebreaker:0,time:1550671089404),streamLive:!f)&logFilter=(expression:'(HOST_FIELD:%20HOST_NAME)%20and%20(FILTER_FIELD:FILTER_VALUE)',kind:kuery)"
+        to="/?sourceId=default&logPosition=(position:(tiebreaker:0,time:1550671089404),streamLive:!f)&logFilter=(expression:'(HOST_FIELD:%20HOST_NAME)%20and%20(FILTER_FIELD:FILTER_VALUE)',kind:kuery)"
       />
     `);
   });
@@ -103,7 +103,7 @@ describe('RedirectToNodeLogs component', () => {
 
     expect(component).toMatchInlineSnapshot(`
       <Redirect
-        to="/logs?sourceId=SOME-OTHER-SOURCE&logFilter=(expression:'HOST_FIELD:%20HOST_NAME',kind:kuery)"
+        to="/?sourceId=SOME-OTHER-SOURCE&logFilter=(expression:'HOST_FIELD:%20HOST_NAME',kind:kuery)"
       />
     `);
   });
diff --git a/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx b/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx
similarity index 93%
rename from x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx
rename to x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx
index 6f1fdd606dd73..9c998085400ca 100644
--- a/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx
+++ b/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx
@@ -6,7 +6,7 @@
 
 import { i18n } from '@kbn/i18n';
 
-import compose from 'lodash/fp/compose';
+import { compose } from 'lodash';
 import React from 'react';
 import { Redirect, RouteComponentProps } from 'react-router-dom';
 
@@ -70,7 +70,7 @@ export const RedirectToNodeLogs = ({
     replaceSourceIdInQueryString(sourceId)
   )('');
 
-  return <Redirect to={`/logs?${searchString}`} />;
+  return <Redirect to={`/?${searchString}`} />;
 };
 
 export const getNodeLogsUrl = ({
@@ -81,4 +81,6 @@ export const getNodeLogsUrl = ({
   nodeId: string;
   nodeType: InventoryItemType;
   time?: number;
-}) => [`#/link-to/${nodeType}-logs/`, nodeId, ...(time ? [`?time=${time}`] : [])].join('');
+}) => {
+  return [`link-to/${nodeType}-logs/`, nodeId, ...(time ? [`?time=${time}`] : [])].join('');
+};
diff --git a/x-pack/legacy/plugins/infra/public/pages/link_to/use_host_ip_to_name.test.ts b/x-pack/plugins/infra/public/pages/link_to/use_host_ip_to_name.test.ts
similarity index 81%
rename from x-pack/legacy/plugins/infra/public/pages/link_to/use_host_ip_to_name.test.ts
rename to x-pack/plugins/infra/public/pages/link_to/use_host_ip_to_name.test.ts
index 3b61181dfc6e0..739ea872021cf 100644
--- a/x-pack/legacy/plugins/infra/public/pages/link_to/use_host_ip_to_name.test.ts
+++ b/x-pack/plugins/infra/public/pages/link_to/use_host_ip_to_name.test.ts
@@ -5,7 +5,6 @@
  */
 
 import { useHostIpToName } from './use_host_ip_to_name';
-import { fetch } from '../../utils/fetch';
 import { renderHook } from '@testing-library/react-hooks';
 
 const renderUseHostIpToNameHook = () =>
@@ -13,12 +12,25 @@ const renderUseHostIpToNameHook = () =>
     initialProps: { ipAddress: '127.0.0.1', indexPattern: 'metricbest-*' },
   });
 
-jest.mock('../../utils/fetch');
-const mockedFetch = fetch as jest.Mocked<typeof fetch>;
+const mockedFetch = jest.fn();
+
+jest.mock('../../../../../../src/plugins/kibana_react/public', () => {
+  return {
+    useKibana: () => {
+      return {
+        services: {
+          http: {
+            fetch: mockedFetch,
+          },
+        },
+      };
+    },
+  };
+});
 
 describe('useHostIpToName Hook', () => {
   it('should basically work', async () => {
-    mockedFetch.post.mockResolvedValue({ data: { host: 'example-01' } } as any);
+    mockedFetch.mockResolvedValue({ host: 'example-01' } as any);
     const { result, waitForNextUpdate } = renderUseHostIpToNameHook();
     expect(result.current.name).toBe(null);
     expect(result.current.loading).toBe(true);
@@ -30,7 +42,7 @@ describe('useHostIpToName Hook', () => {
 
   it('should handle errors', async () => {
     const error = new Error('Host not found');
-    mockedFetch.post.mockRejectedValue(error);
+    mockedFetch.mockRejectedValue(error);
     const { result, waitForNextUpdate } = renderUseHostIpToNameHook();
     expect(result.current.name).toBe(null);
     expect(result.current.loading).toBe(true);
@@ -42,7 +54,7 @@ describe('useHostIpToName Hook', () => {
 
   it('should reset errors', async () => {
     const error = new Error('Host not found');
-    mockedFetch.post.mockRejectedValue(error);
+    mockedFetch.mockRejectedValue(error);
     const { result, waitForNextUpdate, rerender } = renderUseHostIpToNameHook();
     expect(result.current.name).toBe(null);
     expect(result.current.loading).toBe(true);
@@ -50,7 +62,7 @@ describe('useHostIpToName Hook', () => {
     expect(result.current.name).toBe(null);
     expect(result.current.loading).toBe(false);
     expect(result.current.error).toBe(error);
-    mockedFetch.post.mockResolvedValue({ data: { host: 'example-01' } } as any);
+    mockedFetch.mockResolvedValue({ host: 'example-01' } as any);
     rerender({ ipAddress: '192.168.1.2', indexPattern: 'metricbeat-*' });
     await waitForNextUpdate();
     expect(result.current.name).toBe('example-01');
diff --git a/x-pack/legacy/plugins/infra/public/pages/link_to/use_host_ip_to_name.ts b/x-pack/plugins/infra/public/pages/link_to/use_host_ip_to_name.ts
similarity index 61%
rename from x-pack/legacy/plugins/infra/public/pages/link_to/use_host_ip_to_name.ts
rename to x-pack/plugins/infra/public/pages/link_to/use_host_ip_to_name.ts
index 7b4b56f34a678..82236e152d6d0 100644
--- a/x-pack/legacy/plugins/infra/public/pages/link_to/use_host_ip_to_name.ts
+++ b/x-pack/plugins/infra/public/pages/link_to/use_host_ip_to_name.ts
@@ -5,10 +5,11 @@
  */
 
 import { useState, useEffect } from 'react';
-import { IpToHostResponse } from '../../../server/routes/ip_to_hostname';
-import { fetch } from '../../utils/fetch';
+import { IpToHostResponse } from '../../../common/http_api/ip_to_hostname';
+import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
 
 export const useHostIpToName = (ipAddress: string | null, indexPattern: string | null) => {
+  const fetch = useKibana().services.http?.fetch;
   const [error, setError] = useState<Error | null>(null);
   const [loading, setLoadingState] = useState<boolean>(true);
   const [data, setData] = useState<IpToHostResponse | null>(null);
@@ -18,19 +19,25 @@ export const useHostIpToName = (ipAddress: string | null, indexPattern: string |
       setLoadingState(true);
       setError(null);
       try {
+        if (!fetch) {
+          throw new Error('HTTP service is unavailable');
+        }
         if (ipAddress && indexPattern) {
-          const response = await fetch.post<IpToHostResponse>('../api/infra/ip_to_host', {
-            ip: ipAddress,
-            index_pattern: indexPattern,
+          const response = await fetch('/api/infra/ip_to_host', {
+            method: 'POST',
+            body: JSON.stringify({
+              ip: ipAddress,
+              index_pattern: indexPattern,
+            }),
           });
           setLoadingState(false);
-          setData(response.data);
+          setData(response);
         }
       } catch (err) {
         setLoadingState(false);
         setError(err);
       }
     })();
-  }, [ipAddress, indexPattern]);
+  }, [ipAddress, indexPattern, fetch]);
   return { name: (data && data.host) || null, loading, error };
 };
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/index.tsx b/x-pack/plugins/infra/public/pages/logs/index.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/index.tsx
rename to x-pack/plugins/infra/public/pages/logs/index.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/index.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/index.ts
rename to x-pack/plugins/infra/public/pages/logs/log_entry_categories/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/module_descriptor.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/module_descriptor.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/module_descriptor.ts
rename to x-pack/plugins/infra/public/pages/logs/log_entry_categories/module_descriptor.ts
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_categories/page.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page_content.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_content.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page_content.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_content.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page_providers.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_providers.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page_providers.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_providers.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx
similarity index 97%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx
index a810ce447d369..5826435878378 100644
--- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx
@@ -10,8 +10,8 @@ import { i18n } from '@kbn/i18n';
 import moment from 'moment';
 import React, { useCallback, useEffect, useMemo, useState } from 'react';
 
-import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public';
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
+import { euiStyled } from '../../../../../observability/public';
 import { TimeRange } from '../../../../common/http_api/shared/time_range';
 import {
   LogAnalysisJobProblemIndicator,
@@ -19,7 +19,7 @@ import {
 } from '../../../components/logging/log_analysis_job_status';
 import { FirstUseCallout } from '../../../components/logging/log_analysis_results';
 import { useInterval } from '../../../hooks/use_interval';
-import { useTrackPageview } from '../../../hooks/use_track_metric';
+import { useTrackPageview } from '../../../../../observability/public';
 import { TopCategoriesSection } from './sections/top_categories';
 import { useLogEntryCategoriesModuleContext } from './use_log_entry_categories_module';
 import { useLogEntryCategoriesResults } from './use_log_entry_categories_results';
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page_setup_content.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_setup_content.tsx
similarity index 97%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page_setup_content.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_setup_content.tsx
index ac902029f938e..a7ff98923a427 100644
--- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page_setup_content.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_setup_content.tsx
@@ -16,7 +16,7 @@ import {
   LogAnalysisSetupPageContent,
   LogAnalysisSetupPageHeader,
 } from '../../../components/logging/log_analysis_setup';
-import { useTrackPageview } from '../../../hooks/use_track_metric';
+import { useTrackPageview } from '../../../../../observability/public';
 import { useLogEntryCategoriesSetup } from './use_log_entry_categories_setup';
 
 export const LogEntryCategoriesSetupContent: React.FunctionComponent = () => {
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/anomaly_severity_indicator.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/anomaly_severity_indicator.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/anomaly_severity_indicator.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/anomaly_severity_indicator.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_expression.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_expression.tsx
similarity index 96%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_expression.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_expression.tsx
index 5c8b18528cae6..f6cf62421e2d5 100644
--- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_expression.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_expression.tsx
@@ -7,7 +7,7 @@
 import { i18n } from '@kbn/i18n';
 import React, { memo } from 'react';
 
-import euiStyled from '../../../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../../../observability/public';
 
 export const RegularExpressionRepresentation: React.FunctionComponent<{
   maximumSegmentCount?: number;
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/datasets_list.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/datasets_list.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/datasets_list.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/datasets_list.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/datasets_selector.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/datasets_selector.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/datasets_selector.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/datasets_selector.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/index.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/index.ts
rename to x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/log_entry_count_sparkline.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/log_entry_count_sparkline.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/log_entry_count_sparkline.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/log_entry_count_sparkline.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/single_metric_comparison.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/single_metric_comparison.tsx
similarity index 95%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/single_metric_comparison.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/single_metric_comparison.tsx
index 1352afb60a505..d73f9f33fe5db 100644
--- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/single_metric_comparison.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/single_metric_comparison.tsx
@@ -9,7 +9,7 @@ import numeral from '@elastic/numeral';
 import { i18n } from '@kbn/i18n';
 import React from 'react';
 
-import euiStyled from '../../../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../../../observability/public';
 
 export const SingleMetricComparison: React.FunctionComponent<{
   currentValue: number;
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/single_metric_sparkline.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/single_metric_sparkline.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/single_metric_sparkline.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/single_metric_sparkline.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_section.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_section.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_section.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_section.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_table.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_table.tsx
similarity index 97%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_table.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_table.tsx
index 3d20aef03ff15..a2bd2983092a0 100644
--- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_table.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_table.tsx
@@ -9,7 +9,7 @@ import numeral from '@elastic/numeral';
 import { i18n } from '@kbn/i18n';
 import React, { useMemo } from 'react';
 
-import euiStyled from '../../../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../../../observability/public';
 import {
   LogEntryCategory,
   LogEntryCategoryHistogram,
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_datasets.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_datasets.ts
similarity index 69%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_datasets.ts
rename to x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_datasets.ts
index 942ded4230e97..a8cd7854efb6b 100644
--- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_datasets.ts
+++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_datasets.ts
@@ -7,7 +7,7 @@
 import { fold } from 'fp-ts/lib/Either';
 import { pipe } from 'fp-ts/lib/pipeable';
 import { identity } from 'fp-ts/lib/function';
-import { npStart } from 'ui/new_platform';
+import { npStart } from '../../../../legacy_singletons';
 
 import {
   getLogEntryCategoryDatasetsRequestPayloadRT,
@@ -21,23 +21,20 @@ export const callGetLogEntryCategoryDatasetsAPI = async (
   startTime: number,
   endTime: number
 ) => {
-  const response = await npStart.core.http.fetch(
-    LOG_ANALYSIS_GET_LOG_ENTRY_CATEGORY_DATASETS_PATH,
-    {
-      method: 'POST',
-      body: JSON.stringify(
-        getLogEntryCategoryDatasetsRequestPayloadRT.encode({
-          data: {
-            sourceId,
-            timeRange: {
-              startTime,
-              endTime,
-            },
+  const response = await npStart.http.fetch(LOG_ANALYSIS_GET_LOG_ENTRY_CATEGORY_DATASETS_PATH, {
+    method: 'POST',
+    body: JSON.stringify(
+      getLogEntryCategoryDatasetsRequestPayloadRT.encode({
+        data: {
+          sourceId,
+          timeRange: {
+            startTime,
+            endTime,
           },
-        })
-      ),
-    }
-  );
+        },
+      })
+    ),
+  });
 
   return pipe(
     getLogEntryCategoryDatasetsSuccessReponsePayloadRT.decode(response),
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_top_log_entry_categories.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_top_log_entry_categories.ts
similarity index 92%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_top_log_entry_categories.ts
rename to x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_top_log_entry_categories.ts
index 35d6f1ec4f893..2ebcff4fd3ca5 100644
--- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_top_log_entry_categories.ts
+++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_top_log_entry_categories.ts
@@ -7,7 +7,7 @@
 import { fold } from 'fp-ts/lib/Either';
 import { pipe } from 'fp-ts/lib/pipeable';
 import { identity } from 'fp-ts/lib/function';
-import { npStart } from 'ui/new_platform';
+import { npStart } from '../../../../legacy_singletons';
 
 import {
   getLogEntryCategoriesRequestPayloadRT,
@@ -25,7 +25,7 @@ export const callGetTopLogEntryCategoriesAPI = async (
 ) => {
   const intervalDuration = endTime - startTime;
 
-  const response = await npStart.core.http.fetch(LOG_ANALYSIS_GET_LOG_ENTRY_CATEGORIES_PATH, {
+  const response = await npStart.http.fetch(LOG_ANALYSIS_GET_LOG_ENTRY_CATEGORIES_PATH, {
     method: 'POST',
     body: JSON.stringify(
       getLogEntryCategoriesRequestPayloadRT.encode({
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_module.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_module.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_module.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_module.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results.ts
rename to x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results.ts
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results_url_state.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results_url_state.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results_url_state.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results_url_state.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_setup.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_setup.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_setup.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_setup.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/index.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/index.ts
rename to x-pack/plugins/infra/public/pages/logs/log_entry_rate/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/module_descriptor.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/module_descriptor.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/module_descriptor.ts
rename to x-pack/plugins/infra/public/pages/logs/log_entry_rate/module_descriptor.ts
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_rate/page.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_content.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_content.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_content.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_content.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_providers.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_providers.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_providers.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_providers.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx
similarity index 98%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx
index fd77cc8dd7173..50d58865e9746 100644
--- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx
@@ -20,12 +20,12 @@ import { FormattedMessage } from '@kbn/i18n/react';
 import moment from 'moment';
 import React, { useCallback, useMemo, useState, useEffect } from 'react';
 
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 import { TimeRange } from '../../../../common/http_api/shared/time_range';
 import { bucketSpan } from '../../../../common/log_analysis';
 import { LoadingOverlayWrapper } from '../../../components/loading_overlay_wrapper';
 import { useInterval } from '../../../hooks/use_interval';
-import { useTrackPageview } from '../../../hooks/use_track_metric';
+import { useTrackPageview } from '../../../../../observability/public';
 import { useKibanaUiSetting } from '../../../utils/use_kibana_ui_setting';
 import { AnomaliesResults } from './sections/anomalies';
 import { LogRateResults } from './sections/log_rate';
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_setup_content.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_setup_content.tsx
similarity index 97%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_setup_content.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_setup_content.tsx
index 13574f589a111..a02dbfa941588 100644
--- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_setup_content.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_setup_content.tsx
@@ -16,7 +16,7 @@ import {
   LogAnalysisSetupPageContent,
   LogAnalysisSetupPageHeader,
 } from '../../../components/logging/log_analysis_setup';
-import { useTrackPageview } from '../../../hooks/use_track_metric';
+import { useTrackPageview } from '../../../../../observability/public';
 import { useLogEntryRateSetup } from './use_log_entry_rate_setup';
 
 export const LogEntryRateSetupContent: React.FunctionComponent = () => {
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/chart.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/chart.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/chart.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/chart.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/expanded_row.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/expanded_row.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/expanded_row.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/expanded_row.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/index.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/index.tsx
similarity index 99%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/index.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/index.tsx
index 0dc52d2762765..2551170c44f4e 100644
--- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/index.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/index.tsx
@@ -17,7 +17,7 @@ import numeral from '@elastic/numeral';
 import { i18n } from '@kbn/i18n';
 import React, { useMemo } from 'react';
 
-import euiStyled from '../../../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../../../observability/public';
 import { LogEntryRateResults } from '../../use_log_entry_rate_results';
 import { TimeRange } from '../../../../../../common/http_api/shared/time_range';
 import { formatAnomalyScore, JobStatus, SetupStatus } from '../../../../../../common/log_analysis';
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/table.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/table.tsx
similarity index 98%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/table.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/table.tsx
index 3e86b45fadfdd..39d76c4afcffe 100644
--- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/table.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/table.tsx
@@ -9,7 +9,6 @@ import { RIGHT_ALIGNMENT } from '@elastic/eui/lib/services';
 import { i18n } from '@kbn/i18n';
 import React, { useCallback, useMemo, useState } from 'react';
 
-import euiStyled from '../../../../../../../../common/eui_styled_components';
 import { TimeRange } from '../../../../../../common/http_api/shared/time_range';
 import {
   formatAnomalyScore,
@@ -17,6 +16,7 @@ import {
 } from '../../../../../../common/log_analysis';
 import { LogEntryRateResults } from '../../use_log_entry_rate_results';
 import { AnomaliesTableExpandedRow } from './expanded_row';
+import { euiStyled } from '../../../../../../../observability/public';
 
 interface TableItem {
   id: string;
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/helpers/data_formatters.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/helpers/data_formatters.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/helpers/data_formatters.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/helpers/data_formatters.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/log_rate/bar_chart.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/log_rate/bar_chart.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/log_rate/bar_chart.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/log_rate/bar_chart.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/log_rate/index.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/log_rate/index.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/log_rate/index.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/log_rate/index.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_rate.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_rate.ts
similarity index 89%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_rate.ts
rename to x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_rate.ts
index 1df7ef06b9d46..794139385f467 100644
--- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_rate.ts
+++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_rate.ts
@@ -7,7 +7,7 @@
 import { fold } from 'fp-ts/lib/Either';
 import { pipe } from 'fp-ts/lib/pipeable';
 import { identity } from 'fp-ts/lib/function';
-import { npStart } from 'ui/new_platform';
+import { npStart } from '../../../../legacy_singletons';
 import {
   getLogEntryRateRequestPayloadRT,
   getLogEntryRateSuccessReponsePayloadRT,
@@ -21,7 +21,7 @@ export const callGetLogEntryRateAPI = async (
   endTime: number,
   bucketDuration: number
 ) => {
-  const response = await npStart.core.http.fetch(LOG_ANALYSIS_GET_LOG_ENTRY_RATE_PATH, {
+  const response = await npStart.http.fetch(LOG_ANALYSIS_GET_LOG_ENTRY_RATE_PATH, {
     method: 'POST',
     body: JSON.stringify(
       getLogEntryRateRequestPayloadRT.encode({
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_module.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_module.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_module.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_module.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_results.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_results.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_results.ts
rename to x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_results.ts
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_results_url_state.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_results_url_state.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_results_url_state.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_results_url_state.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_setup.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_setup.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_setup.tsx
rename to x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_setup.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/page.tsx b/x-pack/plugins/infra/public/pages/logs/page.tsx
similarity index 91%
rename from x-pack/legacy/plugins/infra/public/pages/logs/page.tsx
rename to x-pack/plugins/infra/public/pages/logs/page.tsx
index 72826b156d7b4..08049183d0a18 100644
--- a/x-pack/legacy/plugins/infra/public/pages/logs/page.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/page.tsx
@@ -13,7 +13,7 @@ import { LogsPageProviders } from './page_providers';
 export const LogsPage: React.FunctionComponent<RouteComponentProps> = ({ match }) => {
   return (
     <LogsPageProviders>
-      <LogsPageContent logsPagePath={match.path} />
+      <LogsPageContent />
     </LogsPageProviders>
   );
 };
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/page_content.tsx b/x-pack/plugins/infra/public/pages/logs/page_content.tsx
similarity index 89%
rename from x-pack/legacy/plugins/infra/public/pages/logs/page_content.tsx
rename to x-pack/plugins/infra/public/pages/logs/page_content.tsx
index 41ef9987d1ad0..48ead15b2a232 100644
--- a/x-pack/legacy/plugins/infra/public/pages/logs/page_content.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/page_content.tsx
@@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n';
 import React from 'react';
 import { Route, Switch } from 'react-router-dom';
 
-import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
+import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
 import { DocumentTitle } from '../../components/document_title';
 import { Header } from '../../components/header';
 import { HelpCenterContent } from '../../components/help_center_content';
@@ -25,31 +25,29 @@ import { LogEntryRatePage } from './log_entry_rate';
 import { LogsSettingsPage } from './settings';
 import { StreamPage } from './stream';
 
-export const LogsPageContent: React.FunctionComponent<{
-  logsPagePath: string;
-}> = ({ logsPagePath }) => {
+export const LogsPageContent: React.FunctionComponent = () => {
   const uiCapabilities = useKibana().services.application?.capabilities;
   const source = useSourceContext();
   const logAnalysisCapabilities = useLogAnalysisCapabilitiesContext();
 
   const streamTab = {
     title: streamTabTitle,
-    path: `${logsPagePath}/stream`,
+    path: '/stream',
   };
 
   const logRateTab = {
     title: logRateTabTitle,
-    path: `${logsPagePath}/log-rate`,
+    path: '/log-rate',
   };
 
   const logCategoriesTab = {
     title: logCategoriesTabTitle,
-    path: `${logsPagePath}/log-categories`,
+    path: '/log-categories',
   };
 
   const settingsTab = {
     title: settingsTabTitle,
-    path: `${logsPagePath}/settings`,
+    path: '/settings',
   };
 
   return (
@@ -91,7 +89,7 @@ export const LogsPageContent: React.FunctionComponent<{
             <Route path={logRateTab.path} component={LogEntryRatePage} />
             <Route path={logCategoriesTab.path} component={LogEntryCategoriesPage} />
             <Route path={settingsTab.path} component={LogsSettingsPage} />
-            <RedirectWithQueryParams from={`${logsPagePath}/analysis`} to={logRateTab.path} exact />
+            <RedirectWithQueryParams from={'/analysis'} to={logRateTab.path} exact />
           </Switch>
         </>
       )}
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/page_providers.tsx b/x-pack/plugins/infra/public/pages/logs/page_providers.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/page_providers.tsx
rename to x-pack/plugins/infra/public/pages/logs/page_providers.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/settings.tsx b/x-pack/plugins/infra/public/pages/logs/settings.tsx
similarity index 88%
rename from x-pack/legacy/plugins/infra/public/pages/logs/settings.tsx
rename to x-pack/plugins/infra/public/pages/logs/settings.tsx
index 52ccf5d59e611..faee7a643085a 100644
--- a/x-pack/legacy/plugins/infra/public/pages/logs/settings.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/settings.tsx
@@ -6,7 +6,7 @@
 
 import React from 'react';
 import { SourceConfigurationSettings } from '../../components/source_configuration/source_configuration_settings';
-import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
+import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
 
 export const LogsSettingsPage = () => {
   const uiCapabilities = useKibana().services.application?.capabilities;
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/stream/index.ts b/x-pack/plugins/infra/public/pages/logs/stream/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/stream/index.ts
rename to x-pack/plugins/infra/public/pages/logs/stream/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page.tsx
similarity index 92%
rename from x-pack/legacy/plugins/infra/public/pages/logs/stream/page.tsx
rename to x-pack/plugins/infra/public/pages/logs/stream/page.tsx
index 9031a8cb4785d..3e07dcebf112d 100644
--- a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/stream/page.tsx
@@ -10,7 +10,7 @@ import { ColumnarPage } from '../../../components/page';
 import { StreamPageContent } from './page_content';
 import { StreamPageHeader } from './page_header';
 import { LogsPageProviders } from './page_providers';
-import { useTrackPageview } from '../../../hooks/use_track_metric';
+import { useTrackPageview } from '../../../../../observability/public';
 
 export const StreamPage = () => {
   useTrackPageview({ app: 'infra_logs', path: 'stream' });
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_content.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_content.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/stream/page_content.tsx
rename to x-pack/plugins/infra/public/pages/logs/stream/page_content.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_header.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_header.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/stream/page_header.tsx
rename to x-pack/plugins/infra/public/pages/logs/stream/page_header.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_logs_content.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx
similarity index 98%
rename from x-pack/legacy/plugins/infra/public/pages/logs/stream/page_logs_content.tsx
rename to x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx
index 4d2b99da9b412..c3c31b5d5bd0f 100644
--- a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_logs_content.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx
@@ -6,7 +6,7 @@
 
 import React, { useContext } from 'react';
 
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 import { AutoSizer } from '../../../components/auto_sizer';
 import { LogEntryFlyout } from '../../../components/logging/log_entry_flyout';
 import { LogMinimap } from '../../../components/logging/log_minimap';
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_no_indices_content.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_no_indices_content.tsx
similarity index 96%
rename from x-pack/legacy/plugins/infra/public/pages/logs/stream/page_no_indices_content.tsx
rename to x-pack/plugins/infra/public/pages/logs/stream/page_no_indices_content.tsx
index 7c697e8d22f14..1294007240027 100644
--- a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_no_indices_content.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/stream/page_no_indices_content.tsx
@@ -9,12 +9,12 @@ import { i18n } from '@kbn/i18n';
 import { identity } from 'fp-ts/lib/function';
 import React from 'react';
 
-import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public';
 import { NoIndices } from '../../../components/empty_states/no_indices';
 import {
   ViewSourceConfigurationButton,
   ViewSourceConfigurationButtonHrefBase,
 } from '../../../components/source_configuration';
+import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
 
 export const LogsPageNoIndicesContent = () => {
   const {
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx
rename to x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_toolbar.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/logs/stream/page_toolbar.tsx
rename to x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/components/chart_section_vis.tsx b/x-pack/plugins/infra/public/pages/metrics/components/chart_section_vis.tsx
similarity index 97%
rename from x-pack/legacy/plugins/infra/public/pages/metrics/components/chart_section_vis.tsx
rename to x-pack/plugins/infra/public/pages/metrics/components/chart_section_vis.tsx
index 1731e9aa62cda..49e95394deb97 100644
--- a/x-pack/legacy/plugins/infra/public/pages/metrics/components/chart_section_vis.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/components/chart_section_vis.tsx
@@ -20,7 +20,7 @@ import {
 } from './helpers';
 import { ErrorMessage } from './error_message';
 import { useKibanaUiSetting } from '../../../utils/use_kibana_ui_setting';
-import { useUiSetting } from '../../../../../../../../src/plugins/kibana_react/public';
+import { useUiSetting } from '../../../../../../../src/plugins/kibana_react/public';
 import { VisSectionProps } from '../types';
 
 export const ChartSectionVis = ({
diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/components/error_message.tsx b/x-pack/plugins/infra/public/pages/metrics/components/error_message.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/metrics/components/error_message.tsx
rename to x-pack/plugins/infra/public/pages/metrics/components/error_message.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/components/gauges_section_vis.tsx b/x-pack/plugins/infra/public/pages/metrics/components/gauges_section_vis.tsx
similarity index 97%
rename from x-pack/legacy/plugins/infra/public/pages/metrics/components/gauges_section_vis.tsx
rename to x-pack/plugins/infra/public/pages/metrics/components/gauges_section_vis.tsx
index 4c0c7faa464dd..e069b52be8be7 100644
--- a/x-pack/legacy/plugins/infra/public/pages/metrics/components/gauges_section_vis.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/components/gauges_section_vis.tsx
@@ -16,7 +16,7 @@ import {
 import { get, last, max } from 'lodash';
 import React, { ReactText } from 'react';
 
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 import { createFormatter } from '../../../utils/formatters';
 import { InventoryFormatterType } from '../../../../common/inventory_models/types';
 import { SeriesOverrides, VisSectionProps } from '../types';
diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/components/helpers.ts b/x-pack/plugins/infra/public/pages/metrics/components/helpers.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/metrics/components/helpers.ts
rename to x-pack/plugins/infra/public/pages/metrics/components/helpers.ts
diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/components/invalid_node.tsx b/x-pack/plugins/infra/public/pages/metrics/components/invalid_node.tsx
similarity index 93%
rename from x-pack/legacy/plugins/infra/public/pages/metrics/components/invalid_node.tsx
rename to x-pack/plugins/infra/public/pages/metrics/components/invalid_node.tsx
index 0e810d9815cf9..fde3b61de50b5 100644
--- a/x-pack/legacy/plugins/infra/public/pages/metrics/components/invalid_node.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/components/invalid_node.tsx
@@ -9,12 +9,12 @@ import { FormattedMessage } from '@kbn/i18n/react';
 import { identity } from 'fp-ts/lib/function';
 import React from 'react';
 
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 import {
   ViewSourceConfigurationButton,
   ViewSourceConfigurationButtonHrefBase,
 } from '../../../components/source_configuration';
-import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public';
+import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
 
 interface InvalidNodeErrorProps {
   nodeName: string;
diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/components/layout_content.tsx b/x-pack/plugins/infra/public/pages/metrics/components/layout_content.tsx
similarity index 83%
rename from x-pack/legacy/plugins/infra/public/pages/metrics/components/layout_content.tsx
rename to x-pack/plugins/infra/public/pages/metrics/components/layout_content.tsx
index a2bd9cdc13179..4e10f245acdcc 100644
--- a/x-pack/legacy/plugins/infra/public/pages/metrics/components/layout_content.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/components/layout_content.tsx
@@ -5,7 +5,7 @@
  */
 
 import { EuiPageContent } from '@elastic/eui';
-import { euiStyled } from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 
 export const LayoutContent = euiStyled(EuiPageContent)`
   position: relative;
diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/components/metadata_details.tsx b/x-pack/plugins/infra/public/pages/metrics/components/metadata_details.tsx
similarity index 98%
rename from x-pack/legacy/plugins/infra/public/pages/metrics/components/metadata_details.tsx
rename to x-pack/plugins/infra/public/pages/metrics/components/metadata_details.tsx
index c43f2d10d7163..3bf492d398f2c 100644
--- a/x-pack/legacy/plugins/infra/public/pages/metrics/components/metadata_details.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/components/metadata_details.tsx
@@ -9,7 +9,7 @@ import { EuiButtonIcon, EuiFlexGrid, EuiFlexItem, EuiTitle, EuiText } from '@ela
 import { i18n } from '@kbn/i18n';
 import { get } from 'lodash';
 import { InfraMetadata } from '../../../../common/http_api';
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 import { MetadataContext } from '../containers/metadata_context';
 
 interface FieldDef {
diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/components/node_details_page.tsx b/x-pack/plugins/infra/public/pages/metrics/components/node_details_page.tsx
similarity index 98%
rename from x-pack/legacy/plugins/infra/public/pages/metrics/components/node_details_page.tsx
rename to x-pack/plugins/infra/public/pages/metrics/components/node_details_page.tsx
index cc662f8fd5b57..ea91c53faf675 100644
--- a/x-pack/legacy/plugins/infra/public/pages/metrics/components/node_details_page.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/components/node_details_page.tsx
@@ -21,7 +21,7 @@ import { AutoSizer } from '../../../components/auto_sizer';
 import { MetricsTimeControls } from './time_controls';
 import { SideNavContext, NavItem } from '../lib/side_nav_context';
 import { PageBody } from './page_body';
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 import { MetricsTimeInput } from '../containers/with_metrics_time';
 import { InfraMetadata } from '../../../../common/http_api/metadata_api';
 import { PageError } from './page_error';
diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/components/page_body.tsx b/x-pack/plugins/infra/public/pages/metrics/components/page_body.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/metrics/components/page_body.tsx
rename to x-pack/plugins/infra/public/pages/metrics/components/page_body.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/components/page_error.tsx b/x-pack/plugins/infra/public/pages/metrics/components/page_error.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/metrics/components/page_error.tsx
rename to x-pack/plugins/infra/public/pages/metrics/components/page_error.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/components/section.tsx b/x-pack/plugins/infra/public/pages/metrics/components/section.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/metrics/components/section.tsx
rename to x-pack/plugins/infra/public/pages/metrics/components/section.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/components/series_chart.tsx b/x-pack/plugins/infra/public/pages/metrics/components/series_chart.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/metrics/components/series_chart.tsx
rename to x-pack/plugins/infra/public/pages/metrics/components/series_chart.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/components/side_nav.tsx b/x-pack/plugins/infra/public/pages/metrics/components/side_nav.tsx
similarity index 95%
rename from x-pack/legacy/plugins/infra/public/pages/metrics/components/side_nav.tsx
rename to x-pack/plugins/infra/public/pages/metrics/components/side_nav.tsx
index 8e922818222b4..94f97c7f45e61 100644
--- a/x-pack/legacy/plugins/infra/public/pages/metrics/components/side_nav.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/components/side_nav.tsx
@@ -6,7 +6,7 @@
 
 import { EuiHideFor, EuiPageSideBar, EuiShowFor, EuiSideNav } from '@elastic/eui';
 import React, { useState, useCallback } from 'react';
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 import { NavItem } from '../lib/side_nav_context';
 
 interface Props {
diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/components/sub_section.tsx b/x-pack/plugins/infra/public/pages/metrics/components/sub_section.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/metrics/components/sub_section.tsx
rename to x-pack/plugins/infra/public/pages/metrics/components/sub_section.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/components/time_controls.test.tsx b/x-pack/plugins/infra/public/pages/metrics/components/time_controls.test.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/metrics/components/time_controls.test.tsx
rename to x-pack/plugins/infra/public/pages/metrics/components/time_controls.test.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/components/time_controls.tsx b/x-pack/plugins/infra/public/pages/metrics/components/time_controls.tsx
similarity index 97%
rename from x-pack/legacy/plugins/infra/public/pages/metrics/components/time_controls.tsx
rename to x-pack/plugins/infra/public/pages/metrics/components/time_controls.tsx
index 1546966c10a1e..b1daaa0320fab 100644
--- a/x-pack/legacy/plugins/infra/public/pages/metrics/components/time_controls.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/components/time_controls.tsx
@@ -6,7 +6,7 @@
 
 import { EuiSuperDatePicker, OnRefreshChangeProps, OnTimeChangeProps } from '@elastic/eui';
 import React, { useCallback } from 'react';
-import euiStyled from '../../../../../../common/eui_styled_components';
+import { euiStyled } from '../../../../../observability/public';
 import { MetricsTimeInput } from '../containers/with_metrics_time';
 import { useKibanaUiSetting } from '../../../utils/use_kibana_ui_setting';
 import { mapKibanaQuickRangesToDatePickerRanges } from '../../../utils/map_timepicker_quickranges_to_datepicker_ranges';
diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/containers/metadata_context.ts b/x-pack/plugins/infra/public/pages/metrics/containers/metadata_context.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/metrics/containers/metadata_context.ts
rename to x-pack/plugins/infra/public/pages/metrics/containers/metadata_context.ts
diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/containers/metrics.gql_query.ts b/x-pack/plugins/infra/public/pages/metrics/containers/metrics.gql_query.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/metrics/containers/metrics.gql_query.ts
rename to x-pack/plugins/infra/public/pages/metrics/containers/metrics.gql_query.ts
diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/containers/metrics_time.test.tsx b/x-pack/plugins/infra/public/pages/metrics/containers/metrics_time.test.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/metrics/containers/metrics_time.test.tsx
rename to x-pack/plugins/infra/public/pages/metrics/containers/metrics_time.test.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/containers/with_metrics_time.tsx b/x-pack/plugins/infra/public/pages/metrics/containers/with_metrics_time.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/metrics/containers/with_metrics_time.tsx
rename to x-pack/plugins/infra/public/pages/metrics/containers/with_metrics_time.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/index.tsx b/x-pack/plugins/infra/public/pages/metrics/index.tsx
similarity index 96%
rename from x-pack/legacy/plugins/infra/public/pages/metrics/index.tsx
rename to x-pack/plugins/infra/public/pages/metrics/index.tsx
index 4c0d2d7c93d7a..533e6a7b9a8f3 100644
--- a/x-pack/legacy/plugins/infra/public/pages/metrics/index.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/index.tsx
@@ -5,7 +5,7 @@
  */
 import { i18n } from '@kbn/i18n';
 import React, { useContext, useState } from 'react';
-import euiStyled, { EuiTheme, withTheme } from '../../../../../common/eui_styled_components';
+import { euiStyled, EuiTheme, withTheme } from '../../../../observability/public';
 import { DocumentTitle } from '../../components/document_title';
 import { Header } from '../../components/header';
 import { ColumnarPage, PageContent } from '../../components/page';
@@ -17,7 +17,7 @@ import { InfraLoadingPanel } from '../../components/loading';
 import { findInventoryModel } from '../../../common/inventory_models';
 import { NavItem } from './lib/side_nav_context';
 import { NodeDetailsPage } from './components/node_details_page';
-import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
+import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
 import { InventoryItemType } from '../../../common/inventory_models/types';
 
 const DetailPageContent = euiStyled(PageContent)`
diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/lib/side_nav_context.ts b/x-pack/plugins/infra/public/pages/metrics/lib/side_nav_context.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/metrics/lib/side_nav_context.ts
rename to x-pack/plugins/infra/public/pages/metrics/lib/side_nav_context.ts
diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/page_providers.tsx b/x-pack/plugins/infra/public/pages/metrics/page_providers.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/pages/metrics/page_providers.tsx
rename to x-pack/plugins/infra/public/pages/metrics/page_providers.tsx
diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/types.ts b/x-pack/plugins/infra/public/pages/metrics/types.ts
similarity index 96%
rename from x-pack/legacy/plugins/infra/public/pages/metrics/types.ts
rename to x-pack/plugins/infra/public/pages/metrics/types.ts
index 4b9e32fddcc76..fd6243292ec07 100644
--- a/x-pack/legacy/plugins/infra/public/pages/metrics/types.ts
+++ b/x-pack/plugins/infra/public/pages/metrics/types.ts
@@ -5,7 +5,7 @@
  */
 
 import rt from 'io-ts';
-import { EuiTheme } from '../../../../../common/eui_styled_components';
+import { EuiTheme } from '../../../../observability/public';
 import { InventoryFormatterTypeRT } from '../../../common/inventory_models/types';
 import { MetricsTimeInput } from './containers/with_metrics_time';
 import { NodeDetailsMetricData } from '../../../common/http_api/node_details_api';
diff --git a/x-pack/plugins/infra/public/plugin.ts b/x-pack/plugins/infra/public/plugin.ts
new file mode 100644
index 0000000000000..5d529e1fda0dc
--- /dev/null
+++ b/x-pack/plugins/infra/public/plugin.ts
@@ -0,0 +1,209 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import { i18n } from '@kbn/i18n';
+import { merge } from 'lodash';
+import {
+  Plugin as PluginClass,
+  CoreSetup,
+  CoreStart,
+  PluginInitializerContext,
+  AppMountParameters,
+} from 'kibana/public';
+import { InMemoryCache, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
+import ApolloClient from 'apollo-client';
+import { ApolloLink } from 'apollo-link';
+import { createHttpLink } from 'apollo-link-http';
+import { withClientState } from 'apollo-link-state';
+import { HttpFetchOptions } from 'src/core/public';
+import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils';
+import { InfraFrontendLibs } from './lib/lib';
+import introspectionQueryResultData from './graphql/introspection.json';
+import { InfraKibanaObservableApiAdapter } from './lib/adapters/observable_api/kibana_observable_api';
+import { registerStartSingleton } from './legacy_singletons';
+import { registerFeatures } from './register_feature';
+import { HomePublicPluginSetup, HomePublicPluginStart } from '../../../../src/plugins/home/public';
+import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../../src/plugins/data/public';
+import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/public';
+import { DataEnhancedSetup, DataEnhancedStart } from '../../data_enhanced/public';
+import { LogsRouter, MetricsRouter } from './routers';
+
+export type ClientSetup = void;
+export type ClientStart = void;
+
+export interface ClientPluginsSetup {
+  home: HomePublicPluginSetup;
+  data: DataPublicPluginSetup;
+  usageCollection: UsageCollectionSetup;
+  dataEnhanced: DataEnhancedSetup;
+}
+
+export interface ClientPluginsStart {
+  home: HomePublicPluginStart;
+  data: DataPublicPluginStart;
+  dataEnhanced: DataEnhancedStart;
+}
+
+export type InfraPlugins = ClientPluginsSetup & ClientPluginsStart;
+
+const getMergedPlugins = (setup: ClientPluginsSetup, start: ClientPluginsStart): InfraPlugins => {
+  return merge({}, setup, start);
+};
+
+export class Plugin
+  implements PluginClass<ClientSetup, ClientStart, ClientPluginsSetup, ClientPluginsStart> {
+  constructor(context: PluginInitializerContext) {}
+
+  setup(core: CoreSetup, pluginsSetup: ClientPluginsSetup) {
+    registerFeatures(pluginsSetup.home);
+
+    core.application.register({
+      id: 'logs',
+      title: i18n.translate('xpack.infra.logs.pluginTitle', {
+        defaultMessage: 'Logs',
+      }),
+      euiIconType: 'logsApp',
+      order: 8001,
+      appRoute: '/app/logs',
+      category: DEFAULT_APP_CATEGORIES.observability,
+      mount: async (params: AppMountParameters) => {
+        const [coreStart, pluginsStart] = await core.getStartServices();
+        const plugins = getMergedPlugins(pluginsSetup, pluginsStart as ClientPluginsStart);
+        const { startApp } = await import('./apps/start_app');
+        return startApp(
+          this.composeLibs(coreStart, plugins),
+          coreStart,
+          plugins,
+          params,
+          LogsRouter
+        );
+      },
+    });
+
+    core.application.register({
+      id: 'metrics',
+      title: i18n.translate('xpack.infra.metrics.pluginTitle', {
+        defaultMessage: 'Metrics',
+      }),
+      euiIconType: 'metricsApp',
+      order: 8000,
+      appRoute: '/app/metrics',
+      category: DEFAULT_APP_CATEGORIES.observability,
+      mount: async (params: AppMountParameters) => {
+        const [coreStart, pluginsStart] = await core.getStartServices();
+        const plugins = getMergedPlugins(pluginsSetup, pluginsStart as ClientPluginsStart);
+        const { startApp } = await import('./apps/start_app');
+        return startApp(
+          this.composeLibs(coreStart, plugins),
+          coreStart,
+          plugins,
+          params,
+          MetricsRouter
+        );
+      },
+    });
+
+    /* This exists purely to facilitate URL redirects from the old App ID ("infra"),
+    to our new App IDs ("metrics" and "logs"). With version 8.0.0 we can remove this. */
+    core.application.register({
+      id: 'infra',
+      appRoute: '/app/infra',
+      title: 'infra',
+      navLinkStatus: 3,
+      mount: async (params: AppMountParameters) => {
+        const { startLegacyApp } = await import('./apps/start_legacy_app');
+        return startLegacyApp(params);
+      },
+    });
+  }
+
+  start(core: CoreStart, plugins: ClientPluginsStart) {
+    registerStartSingleton(core);
+  }
+
+  composeLibs(core: CoreStart, plugins: ClientPluginsStart) {
+    const cache = new InMemoryCache({
+      addTypename: false,
+      fragmentMatcher: new IntrospectionFragmentMatcher({
+        introspectionQueryResultData,
+      }),
+    });
+
+    const observableApi = new InfraKibanaObservableApiAdapter({
+      basePath: core.http.basePath.get(),
+    });
+
+    const wrappedFetch = (path: string, options: HttpFetchOptions) => {
+      return new Promise<Response>(async (resolve, reject) => {
+        // core.http.fetch isn't 100% compatible with the Fetch API and will
+        // throw Errors on 401s. This top level try / catch handles those scenarios.
+        try {
+          core.http
+            .fetch(path, {
+              ...options,
+              // Set headers to undefined due to this bug: https://github.com/apollographql/apollo-link/issues/249,
+              // Apollo will try to set a "content-type" header which will conflict with the "Content-Type" header that
+              // core.http.fetch correctly sets.
+              headers: undefined,
+              asResponse: true,
+            })
+            .then(res => {
+              if (!res.response) {
+                return reject();
+              }
+              // core.http.fetch will parse the Response and set a body before handing it back. As such .text() / .json()
+              // will have already been called on the Response instance. However, Apollo will also want to call
+              // .text() / .json() on the instance, as it expects the raw Response instance, rather than core's wrapper.
+              // .text() / .json() can only be called once, and an Error will be thrown if those methods are accessed again.
+              // This hacks around that by setting up a new .text() method that will restringify the JSON response we already have.
+              // This does result in an extra stringify / parse cycle, which isn't ideal, but as we only have a few endpoints left using
+              // GraphQL this shouldn't create excessive overhead.
+              // Ref: https://github.com/apollographql/apollo-link/blob/master/packages/apollo-link-http/src/httpLink.ts#L134
+              // and
+              // https://github.com/apollographql/apollo-link/blob/master/packages/apollo-link-http-common/src/index.ts#L125
+              return resolve({
+                ...res.response,
+                text: () => {
+                  return new Promise(async (resolveText, rejectText) => {
+                    if (res.body) {
+                      return resolveText(JSON.stringify(res.body));
+                    } else {
+                      return rejectText();
+                    }
+                  });
+                },
+              });
+            });
+        } catch (error) {
+          reject(error);
+        }
+      });
+    };
+
+    const HttpLink = createHttpLink({
+      fetch: wrappedFetch,
+      uri: `/api/infra/graphql`,
+    });
+
+    const graphQLOptions = {
+      cache,
+      link: ApolloLink.from([
+        withClientState({
+          cache,
+          resolvers: {},
+        }),
+        HttpLink,
+      ]),
+    };
+
+    const apolloClient = new ApolloClient(graphQLOptions);
+
+    const libs: InfraFrontendLibs = {
+      apolloClient,
+      observableApi,
+    };
+    return libs;
+  }
+}
diff --git a/x-pack/legacy/plugins/infra/public/feature_catalogue_entry.ts b/x-pack/plugins/infra/public/register_feature.ts
similarity index 75%
rename from x-pack/legacy/plugins/infra/public/feature_catalogue_entry.ts
rename to x-pack/plugins/infra/public/register_feature.ts
index 6442083234f2c..37217eaeaab2a 100644
--- a/x-pack/legacy/plugins/infra/public/feature_catalogue_entry.ts
+++ b/x-pack/plugins/infra/public/register_feature.ts
@@ -5,13 +5,14 @@
  */
 
 import { i18n } from '@kbn/i18n';
-import { FeatureCatalogueCategory } from '../../../../../src/plugins/home/public';
+import {
+  HomePublicPluginSetup,
+  FeatureCatalogueCategory,
+} from '../../../../src/plugins/home/public';
 
-const APP_ID = 'infra';
-
-export const featureCatalogueEntries = {
-  metrics: {
-    id: 'infraops',
+export const registerFeatures = (homePlugin: HomePublicPluginSetup) => {
+  homePlugin.featureCatalogue.register({
+    id: 'metrics',
     title: i18n.translate('xpack.infra.registerFeatures.infraOpsTitle', {
       defaultMessage: 'Metrics',
     }),
@@ -20,12 +21,13 @@ export const featureCatalogueEntries = {
         'Explore infrastructure metrics and logs for common servers, containers, and services.',
     }),
     icon: 'metricsApp',
-    path: `/app/${APP_ID}#infrastructure`,
+    path: `/app/metrics`,
     showOnHomePage: true,
     category: FeatureCatalogueCategory.DATA,
-  },
-  logs: {
-    id: 'infralogging',
+  });
+
+  homePlugin.featureCatalogue.register({
+    id: 'logs',
     title: i18n.translate('xpack.infra.registerFeatures.logsTitle', {
       defaultMessage: 'Logs',
     }),
@@ -34,8 +36,8 @@ export const featureCatalogueEntries = {
         'Stream logs in real time or scroll through historical views in a console-like experience.',
     }),
     icon: 'logsApp',
-    path: `/app/${APP_ID}#logs`,
+    path: `/app/logs`,
     showOnHomePage: true,
     category: FeatureCatalogueCategory.DATA,
-  },
+  });
 };
diff --git a/x-pack/plugins/infra/public/routers/index.ts b/x-pack/plugins/infra/public/routers/index.ts
new file mode 100644
index 0000000000000..71ab2613d8dc1
--- /dev/null
+++ b/x-pack/plugins/infra/public/routers/index.ts
@@ -0,0 +1,15 @@
+/*
+ * 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 { History } from 'history';
+
+export * from './logs_router';
+export * from './metrics_router';
+
+interface RouterProps {
+  history: History;
+}
+
+export type AppRouter = React.FC<RouterProps>;
diff --git a/x-pack/plugins/infra/public/routers/logs_router.tsx b/x-pack/plugins/infra/public/routers/logs_router.tsx
new file mode 100644
index 0000000000000..6700ed1fc8479
--- /dev/null
+++ b/x-pack/plugins/infra/public/routers/logs_router.tsx
@@ -0,0 +1,31 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { Route, Router, Switch } from 'react-router-dom';
+
+import { NotFoundPage } from '../pages/404';
+import { LinkToPage } from '../pages/link_to';
+import { LogsPage } from '../pages/logs';
+import { RedirectWithQueryParams } from '../utils/redirect_with_query_params';
+import { useKibana } from '../../../../../src/plugins/kibana_react/public';
+import { AppRouter } from './index';
+
+export const LogsRouter: AppRouter = ({ history }) => {
+  const uiCapabilities = useKibana().services.application?.capabilities;
+  return (
+    <Router history={history}>
+      <Switch>
+        <Route path="/link-to" component={LinkToPage} />
+        {uiCapabilities?.logs?.show && (
+          <RedirectWithQueryParams from="/" exact={true} to="/stream" />
+        )}
+        {uiCapabilities?.logs?.show && <Route path="/" component={LogsPage} />}
+        <Route component={NotFoundPage} />
+      </Switch>
+    </Router>
+  );
+};
diff --git a/x-pack/plugins/infra/public/routers/metrics_router.tsx b/x-pack/plugins/infra/public/routers/metrics_router.tsx
new file mode 100644
index 0000000000000..7c2ebed8463f1
--- /dev/null
+++ b/x-pack/plugins/infra/public/routers/metrics_router.tsx
@@ -0,0 +1,41 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { Route, Router, Switch } from 'react-router-dom';
+
+import { NotFoundPage } from '../pages/404';
+import { InfrastructurePage } from '../pages/infrastructure';
+import { LinkToPage } from '../pages/link_to';
+import { MetricDetail } from '../pages/metrics';
+import { RedirectWithQueryParams } from '../utils/redirect_with_query_params';
+import { useKibana } from '../../../../../src/plugins/kibana_react/public';
+import { AppRouter } from './index';
+
+export const MetricsRouter: AppRouter = ({ history }) => {
+  const uiCapabilities = useKibana().services.application?.capabilities;
+  return (
+    <Router history={history}>
+      <Switch>
+        <Route path="/link-to" component={LinkToPage} />
+        {uiCapabilities?.infrastructure?.show && (
+          <RedirectWithQueryParams from="/" exact={true} to="/inventory" />
+        )}
+        {uiCapabilities?.infrastructure?.show && (
+          <RedirectWithQueryParams from="/snapshot" exact={true} to="/inventory" />
+        )}
+        {uiCapabilities?.infrastructure?.show && (
+          <RedirectWithQueryParams from="/metrics-explorer" exact={true} to="/explorer" />
+        )}
+        {uiCapabilities?.infrastructure?.show && (
+          <Route path="/detail/:type/:node" component={MetricDetail} />
+        )}
+        {uiCapabilities?.infrastructure?.show && <Route path="/" component={InfrastructurePage} />}
+        <Route component={NotFoundPage} />
+      </Switch>
+    </Router>
+  );
+};
diff --git a/x-pack/legacy/plugins/infra/public/store/actions.ts b/x-pack/plugins/infra/public/store/actions.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/store/actions.ts
rename to x-pack/plugins/infra/public/store/actions.ts
diff --git a/x-pack/legacy/plugins/infra/public/store/epics.ts b/x-pack/plugins/infra/public/store/epics.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/store/epics.ts
rename to x-pack/plugins/infra/public/store/epics.ts
diff --git a/x-pack/legacy/plugins/infra/public/store/index.ts b/x-pack/plugins/infra/public/store/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/store/index.ts
rename to x-pack/plugins/infra/public/store/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/store/local/actions.ts b/x-pack/plugins/infra/public/store/local/actions.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/store/local/actions.ts
rename to x-pack/plugins/infra/public/store/local/actions.ts
diff --git a/x-pack/legacy/plugins/infra/public/store/local/epic.ts b/x-pack/plugins/infra/public/store/local/epic.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/store/local/epic.ts
rename to x-pack/plugins/infra/public/store/local/epic.ts
diff --git a/x-pack/legacy/plugins/infra/public/store/local/index.ts b/x-pack/plugins/infra/public/store/local/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/store/local/index.ts
rename to x-pack/plugins/infra/public/store/local/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/store/local/reducer.ts b/x-pack/plugins/infra/public/store/local/reducer.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/store/local/reducer.ts
rename to x-pack/plugins/infra/public/store/local/reducer.ts
diff --git a/x-pack/legacy/plugins/infra/public/store/local/selectors.ts b/x-pack/plugins/infra/public/store/local/selectors.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/store/local/selectors.ts
rename to x-pack/plugins/infra/public/store/local/selectors.ts
diff --git a/x-pack/legacy/plugins/infra/public/store/local/waffle_filter/actions.ts b/x-pack/plugins/infra/public/store/local/waffle_filter/actions.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/store/local/waffle_filter/actions.ts
rename to x-pack/plugins/infra/public/store/local/waffle_filter/actions.ts
diff --git a/x-pack/legacy/plugins/infra/public/store/local/waffle_filter/index.ts b/x-pack/plugins/infra/public/store/local/waffle_filter/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/store/local/waffle_filter/index.ts
rename to x-pack/plugins/infra/public/store/local/waffle_filter/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/store/local/waffle_filter/reducer.ts b/x-pack/plugins/infra/public/store/local/waffle_filter/reducer.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/store/local/waffle_filter/reducer.ts
rename to x-pack/plugins/infra/public/store/local/waffle_filter/reducer.ts
diff --git a/x-pack/legacy/plugins/infra/public/store/local/waffle_filter/selectors.ts b/x-pack/plugins/infra/public/store/local/waffle_filter/selectors.ts
similarity index 93%
rename from x-pack/legacy/plugins/infra/public/store/local/waffle_filter/selectors.ts
rename to x-pack/plugins/infra/public/store/local/waffle_filter/selectors.ts
index 0acce82950f77..047dabd3f0dd3 100644
--- a/x-pack/legacy/plugins/infra/public/store/local/waffle_filter/selectors.ts
+++ b/x-pack/plugins/infra/public/store/local/waffle_filter/selectors.ts
@@ -6,7 +6,7 @@
 
 import { createSelector } from 'reselect';
 
-import { esKuery } from '../../../../../../../../src/plugins/data/public';
+import { esKuery } from '../../../../../../../src/plugins/data/public';
 import { WaffleFilterState } from './reducer';
 
 export const selectWaffleFilterQuery = (state: WaffleFilterState) =>
diff --git a/x-pack/legacy/plugins/infra/public/store/local/waffle_options/actions.ts b/x-pack/plugins/infra/public/store/local/waffle_options/actions.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/store/local/waffle_options/actions.ts
rename to x-pack/plugins/infra/public/store/local/waffle_options/actions.ts
diff --git a/x-pack/legacy/plugins/infra/public/store/local/waffle_options/index.ts b/x-pack/plugins/infra/public/store/local/waffle_options/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/store/local/waffle_options/index.ts
rename to x-pack/plugins/infra/public/store/local/waffle_options/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/store/local/waffle_options/reducer.ts b/x-pack/plugins/infra/public/store/local/waffle_options/reducer.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/store/local/waffle_options/reducer.ts
rename to x-pack/plugins/infra/public/store/local/waffle_options/reducer.ts
diff --git a/x-pack/legacy/plugins/infra/public/store/local/waffle_options/selector.ts b/x-pack/plugins/infra/public/store/local/waffle_options/selector.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/store/local/waffle_options/selector.ts
rename to x-pack/plugins/infra/public/store/local/waffle_options/selector.ts
diff --git a/x-pack/legacy/plugins/infra/public/store/local/waffle_time/actions.ts b/x-pack/plugins/infra/public/store/local/waffle_time/actions.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/store/local/waffle_time/actions.ts
rename to x-pack/plugins/infra/public/store/local/waffle_time/actions.ts
diff --git a/x-pack/legacy/plugins/infra/public/store/local/waffle_time/epic.ts b/x-pack/plugins/infra/public/store/local/waffle_time/epic.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/store/local/waffle_time/epic.ts
rename to x-pack/plugins/infra/public/store/local/waffle_time/epic.ts
diff --git a/x-pack/legacy/plugins/infra/public/store/local/waffle_time/index.ts b/x-pack/plugins/infra/public/store/local/waffle_time/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/store/local/waffle_time/index.ts
rename to x-pack/plugins/infra/public/store/local/waffle_time/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/store/local/waffle_time/reducer.ts b/x-pack/plugins/infra/public/store/local/waffle_time/reducer.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/store/local/waffle_time/reducer.ts
rename to x-pack/plugins/infra/public/store/local/waffle_time/reducer.ts
diff --git a/x-pack/legacy/plugins/infra/public/store/local/waffle_time/selectors.ts b/x-pack/plugins/infra/public/store/local/waffle_time/selectors.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/store/local/waffle_time/selectors.ts
rename to x-pack/plugins/infra/public/store/local/waffle_time/selectors.ts
diff --git a/x-pack/legacy/plugins/infra/public/store/reducer.ts b/x-pack/plugins/infra/public/store/reducer.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/store/reducer.ts
rename to x-pack/plugins/infra/public/store/reducer.ts
diff --git a/x-pack/legacy/plugins/infra/public/store/selectors.ts b/x-pack/plugins/infra/public/store/selectors.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/store/selectors.ts
rename to x-pack/plugins/infra/public/store/selectors.ts
diff --git a/x-pack/legacy/plugins/infra/public/store/store.ts b/x-pack/plugins/infra/public/store/store.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/store/store.ts
rename to x-pack/plugins/infra/public/store/store.ts
diff --git a/x-pack/legacy/plugins/infra/public/utils/apollo_context.ts b/x-pack/plugins/infra/public/utils/apollo_context.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/apollo_context.ts
rename to x-pack/plugins/infra/public/utils/apollo_context.ts
diff --git a/x-pack/legacy/plugins/infra/public/utils/cancellable_effect.ts b/x-pack/plugins/infra/public/utils/cancellable_effect.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/cancellable_effect.ts
rename to x-pack/plugins/infra/public/utils/cancellable_effect.ts
diff --git a/x-pack/legacy/plugins/infra/public/utils/convert_interval_to_string.ts b/x-pack/plugins/infra/public/utils/convert_interval_to_string.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/convert_interval_to_string.ts
rename to x-pack/plugins/infra/public/utils/convert_interval_to_string.ts
diff --git a/x-pack/legacy/plugins/infra/public/utils/enzyme_helpers.tsx b/x-pack/plugins/infra/public/utils/enzyme_helpers.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/enzyme_helpers.tsx
rename to x-pack/plugins/infra/public/utils/enzyme_helpers.tsx
diff --git a/x-pack/legacy/plugins/infra/public/utils/fixtures/metrics_explorer.ts b/x-pack/plugins/infra/public/utils/fixtures/metrics_explorer.ts
similarity index 97%
rename from x-pack/legacy/plugins/infra/public/utils/fixtures/metrics_explorer.ts
rename to x-pack/plugins/infra/public/utils/fixtures/metrics_explorer.ts
index 3d5198f2d379b..e39d6f785d53e 100644
--- a/x-pack/legacy/plugins/infra/public/utils/fixtures/metrics_explorer.ts
+++ b/x-pack/plugins/infra/public/utils/fixtures/metrics_explorer.ts
@@ -7,7 +7,7 @@
 import {
   MetricsExplorerResponse,
   MetricsExplorerSeries,
-} from '../../../server/routes/metrics_explorer/types';
+} from '../../../common/http_api/metrics_explorer';
 import {
   MetricsExplorerOptions,
   MetricsExplorerTimeOptions,
diff --git a/x-pack/legacy/plugins/infra/public/utils/formatters/bytes.test.ts b/x-pack/plugins/infra/public/utils/formatters/bytes.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/formatters/bytes.test.ts
rename to x-pack/plugins/infra/public/utils/formatters/bytes.test.ts
diff --git a/x-pack/legacy/plugins/infra/public/utils/formatters/bytes.ts b/x-pack/plugins/infra/public/utils/formatters/bytes.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/formatters/bytes.ts
rename to x-pack/plugins/infra/public/utils/formatters/bytes.ts
diff --git a/x-pack/legacy/plugins/infra/public/utils/formatters/datetime.ts b/x-pack/plugins/infra/public/utils/formatters/datetime.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/formatters/datetime.ts
rename to x-pack/plugins/infra/public/utils/formatters/datetime.ts
diff --git a/x-pack/legacy/plugins/infra/public/utils/formatters/high_precision.ts b/x-pack/plugins/infra/public/utils/formatters/high_precision.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/formatters/high_precision.ts
rename to x-pack/plugins/infra/public/utils/formatters/high_precision.ts
diff --git a/x-pack/legacy/plugins/infra/public/utils/formatters/index.ts b/x-pack/plugins/infra/public/utils/formatters/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/formatters/index.ts
rename to x-pack/plugins/infra/public/utils/formatters/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/utils/formatters/number.ts b/x-pack/plugins/infra/public/utils/formatters/number.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/formatters/number.ts
rename to x-pack/plugins/infra/public/utils/formatters/number.ts
diff --git a/x-pack/legacy/plugins/infra/public/utils/formatters/percent.ts b/x-pack/plugins/infra/public/utils/formatters/percent.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/formatters/percent.ts
rename to x-pack/plugins/infra/public/utils/formatters/percent.ts
diff --git a/x-pack/legacy/plugins/infra/public/utils/handlers.ts b/x-pack/plugins/infra/public/utils/handlers.ts
similarity index 93%
rename from x-pack/legacy/plugins/infra/public/utils/handlers.ts
rename to x-pack/plugins/infra/public/utils/handlers.ts
index 4678718169b94..077355a9c5960 100644
--- a/x-pack/legacy/plugins/infra/public/utils/handlers.ts
+++ b/x-pack/plugins/infra/public/utils/handlers.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import isEqual from 'lodash/fp/isEqual';
+import { isEqual } from 'lodash';
 
 export function callWithoutRepeats<T>(
   func: (...args: any[]) => T,
diff --git a/x-pack/legacy/plugins/infra/public/utils/history_context.ts b/x-pack/plugins/infra/public/utils/history_context.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/history_context.ts
rename to x-pack/plugins/infra/public/utils/history_context.ts
diff --git a/x-pack/legacy/plugins/infra/public/utils/is_displayable.test.ts b/x-pack/plugins/infra/public/utils/is_displayable.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/is_displayable.test.ts
rename to x-pack/plugins/infra/public/utils/is_displayable.test.ts
diff --git a/x-pack/legacy/plugins/infra/public/utils/is_displayable.ts b/x-pack/plugins/infra/public/utils/is_displayable.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/is_displayable.ts
rename to x-pack/plugins/infra/public/utils/is_displayable.ts
diff --git a/x-pack/legacy/plugins/infra/public/utils/kuery.ts b/x-pack/plugins/infra/public/utils/kuery.ts
similarity index 87%
rename from x-pack/legacy/plugins/infra/public/utils/kuery.ts
rename to x-pack/plugins/infra/public/utils/kuery.ts
index 2292bf272b388..60d3f6454c546 100644
--- a/x-pack/legacy/plugins/infra/public/utils/kuery.ts
+++ b/x-pack/plugins/infra/public/utils/kuery.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { esKuery, IIndexPattern } from '../../../../../../src/plugins/data/public';
+import { esKuery, IIndexPattern } from '../../../../../src/plugins/data/public';
 
 export const convertKueryToElasticSearchQuery = (
   kueryExpression: string,
diff --git a/x-pack/legacy/plugins/infra/public/utils/loading_state/index.ts b/x-pack/plugins/infra/public/utils/loading_state/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/loading_state/index.ts
rename to x-pack/plugins/infra/public/utils/loading_state/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/utils/loading_state/loading_policy.ts b/x-pack/plugins/infra/public/utils/loading_state/loading_policy.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/loading_state/loading_policy.ts
rename to x-pack/plugins/infra/public/utils/loading_state/loading_policy.ts
diff --git a/x-pack/legacy/plugins/infra/public/utils/loading_state/loading_progress.ts b/x-pack/plugins/infra/public/utils/loading_state/loading_progress.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/loading_state/loading_progress.ts
rename to x-pack/plugins/infra/public/utils/loading_state/loading_progress.ts
diff --git a/x-pack/legacy/plugins/infra/public/utils/loading_state/loading_result.ts b/x-pack/plugins/infra/public/utils/loading_state/loading_result.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/loading_state/loading_result.ts
rename to x-pack/plugins/infra/public/utils/loading_state/loading_result.ts
diff --git a/x-pack/legacy/plugins/infra/public/utils/loading_state/loading_state.ts b/x-pack/plugins/infra/public/utils/loading_state/loading_state.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/loading_state/loading_state.ts
rename to x-pack/plugins/infra/public/utils/loading_state/loading_state.ts
diff --git a/x-pack/legacy/plugins/infra/public/utils/log_entry/index.ts b/x-pack/plugins/infra/public/utils/log_entry/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/log_entry/index.ts
rename to x-pack/plugins/infra/public/utils/log_entry/index.ts
diff --git a/x-pack/legacy/plugins/infra/public/utils/log_entry/log_entry.ts b/x-pack/plugins/infra/public/utils/log_entry/log_entry.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/log_entry/log_entry.ts
rename to x-pack/plugins/infra/public/utils/log_entry/log_entry.ts
diff --git a/x-pack/legacy/plugins/infra/public/utils/log_entry/log_entry_highlight.ts b/x-pack/plugins/infra/public/utils/log_entry/log_entry_highlight.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/log_entry/log_entry_highlight.ts
rename to x-pack/plugins/infra/public/utils/log_entry/log_entry_highlight.ts
diff --git a/x-pack/legacy/plugins/infra/public/utils/map_timepicker_quickranges_to_datepicker_ranges.ts b/x-pack/plugins/infra/public/utils/map_timepicker_quickranges_to_datepicker_ranges.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/map_timepicker_quickranges_to_datepicker_ranges.ts
rename to x-pack/plugins/infra/public/utils/map_timepicker_quickranges_to_datepicker_ranges.ts
diff --git a/x-pack/legacy/plugins/infra/public/utils/redirect_with_query_params.tsx b/x-pack/plugins/infra/public/utils/redirect_with_query_params.tsx
similarity index 59%
rename from x-pack/legacy/plugins/infra/public/utils/redirect_with_query_params.tsx
rename to x-pack/plugins/infra/public/utils/redirect_with_query_params.tsx
index 915b82860dd1b..7065ba7bf9b2e 100644
--- a/x-pack/legacy/plugins/infra/public/utils/redirect_with_query_params.tsx
+++ b/x-pack/plugins/infra/public/utils/redirect_with_query_params.tsx
@@ -18,27 +18,29 @@ export const RedirectWithQueryParams: React.FunctionComponent<RedirectWithQueryP
   from,
   to,
   ...rest
-}) => (
-  <Route
-    path={from}
-    render={({ location }: RouteProps) =>
-      location ? (
-        <Redirect
-          {...rest}
-          from={from}
-          to={
-            typeof to === 'string'
-              ? {
-                  ...location,
-                  pathname: to,
-                }
-              : {
-                  ...location,
-                  ...to,
-                }
-          }
-        />
-      ) : null
-    }
-  />
-);
+}) => {
+  return (
+    <Route
+      path={from}
+      render={({ location }: RouteProps) => {
+        return location ? (
+          <Redirect
+            {...rest}
+            from={from}
+            to={
+              typeof to === 'string'
+                ? {
+                    ...location,
+                    pathname: to,
+                  }
+                : {
+                    ...location,
+                    ...to,
+                  }
+            }
+          />
+        ) : null;
+      }}
+    />
+  );
+};
diff --git a/x-pack/legacy/plugins/infra/public/utils/redux_context.tsx b/x-pack/plugins/infra/public/utils/redux_context.tsx
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/redux_context.tsx
rename to x-pack/plugins/infra/public/utils/redux_context.tsx
diff --git a/x-pack/legacy/plugins/infra/public/utils/source_configuration.ts b/x-pack/plugins/infra/public/utils/source_configuration.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/source_configuration.ts
rename to x-pack/plugins/infra/public/utils/source_configuration.ts
diff --git a/x-pack/legacy/plugins/infra/public/utils/styles.ts b/x-pack/plugins/infra/public/utils/styles.ts
similarity index 94%
rename from x-pack/legacy/plugins/infra/public/utils/styles.ts
rename to x-pack/plugins/infra/public/utils/styles.ts
index 523f123666f6f..831996ddeca65 100644
--- a/x-pack/legacy/plugins/infra/public/utils/styles.ts
+++ b/x-pack/plugins/infra/public/utils/styles.ts
@@ -4,8 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import get from 'lodash/fp/get';
-import getOr from 'lodash/fp/getOr';
+import { get } from 'lodash';
 import { getLuminance, parseToHsl, parseToRgb, rgba, shade, tint } from 'polished';
 
 type PropReader = <Prop, Default = any>(props: object, defaultValue?: Default) => Prop;
@@ -16,7 +15,7 @@ const asPropReader = (reader: string | string[] | PropReader) =>
     : <Props extends object, Prop extends keyof Props, Default>(
         props: Props,
         defaultValue?: Default
-      ) => getOr(defaultValue, reader as Prop, props);
+      ) => get(props, reader as Prop, defaultValue);
 
 export const switchProp = Object.assign(
   (propName: string | string[] | PropReader, options: Map<any, any> | object) => (
@@ -26,7 +25,7 @@ export const switchProp = Object.assign(
     if (typeof propValue === 'undefined') {
       return;
     }
-    return options instanceof Map ? options.get(propValue) : get(propValue, options);
+    return options instanceof Map ? options.get(propValue) : get(options, propValue);
   },
   {
     default: Symbol('default'),
diff --git a/x-pack/legacy/plugins/infra/public/utils/typed_react.tsx b/x-pack/plugins/infra/public/utils/typed_react.tsx
similarity index 95%
rename from x-pack/legacy/plugins/infra/public/utils/typed_react.tsx
rename to x-pack/plugins/infra/public/utils/typed_react.tsx
index d057899ba18be..c997fb4ac983a 100644
--- a/x-pack/legacy/plugins/infra/public/utils/typed_react.tsx
+++ b/x-pack/plugins/infra/public/utils/typed_react.tsx
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import omit from 'lodash/fp/omit';
+import { omit } from 'lodash';
 import React from 'react';
 import { InferableComponentEnhancerWithProps, ConnectedComponent } from 'react-redux';
 
@@ -56,7 +56,7 @@ export const asChildFunctionRenderer = <InjectedProps extends {}, OwnProps>(
       }
 
       private getRendererArgs = () =>
-        omit(['children', 'initializeOnMount', 'resetOnUnmount'], this.props) as Pick<
+        omit(this.props, ['children', 'initializeOnMount', 'resetOnUnmount']) as Pick<
           ChildFunctionRendererProps<InjectedProps>,
           keyof InjectedProps
         >;
diff --git a/x-pack/legacy/plugins/infra/public/utils/typed_redux.ts b/x-pack/plugins/infra/public/utils/typed_redux.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/typed_redux.ts
rename to x-pack/plugins/infra/public/utils/typed_redux.ts
diff --git a/x-pack/legacy/plugins/infra/public/utils/url_state.tsx b/x-pack/plugins/infra/public/utils/url_state.tsx
similarity index 96%
rename from x-pack/legacy/plugins/infra/public/utils/url_state.tsx
rename to x-pack/plugins/infra/public/utils/url_state.tsx
index 58835715fe55c..bf4cfbaf05965 100644
--- a/x-pack/legacy/plugins/infra/public/utils/url_state.tsx
+++ b/x-pack/plugins/infra/public/utils/url_state.tsx
@@ -6,11 +6,11 @@
 
 import { parse, stringify } from 'query-string';
 import { History, Location } from 'history';
-import throttle from 'lodash/fp/throttle';
+import { throttle } from 'lodash';
 import React from 'react';
 import { Route, RouteProps } from 'react-router-dom';
 import { decode, encode, RisonValue } from 'rison-node';
-import { url } from '../../../../../../src/plugins/kibana_utils/public';
+import { url } from '../../../../../src/plugins/kibana_utils/public';
 
 interface UrlStateContainerProps<UrlState> {
   urlState: UrlState | undefined;
@@ -54,7 +54,7 @@ class UrlStateContainerLifecycle<UrlState> extends React.Component<
     this.handleInitialize(location);
   }
 
-  private replaceStateInLocation = throttle(1000, (urlState: UrlState | undefined) => {
+  private replaceStateInLocation = throttle((urlState: UrlState | undefined) => {
     const { history, location, urlStateKey } = this.props;
 
     const newLocation = replaceQueryStringInLocation(
@@ -65,7 +65,7 @@ class UrlStateContainerLifecycle<UrlState> extends React.Component<
     if (newLocation !== location) {
       history.replace(newLocation);
     }
-  });
+  }, 1000);
 
   private handleInitialize = (location: Location) => {
     const { onInitialize, mapToUrlState, urlStateKey, urlState } = this.props;
diff --git a/x-pack/legacy/plugins/infra/public/utils/use_kibana_space_id.ts b/x-pack/plugins/infra/public/utils/use_kibana_space_id.ts
similarity index 64%
rename from x-pack/legacy/plugins/infra/public/utils/use_kibana_space_id.ts
rename to x-pack/plugins/infra/public/utils/use_kibana_space_id.ts
index 52c896feef1a2..d229542ec940f 100644
--- a/x-pack/legacy/plugins/infra/public/utils/use_kibana_space_id.ts
+++ b/x-pack/plugins/infra/public/utils/use_kibana_space_id.ts
@@ -7,12 +7,13 @@
 import { fold } from 'fp-ts/lib/Either';
 import { pipe } from 'fp-ts/lib/pipeable';
 import * as rt from 'io-ts';
-
-import { useKibanaInjectedVar } from './use_kibana_injected_var';
+import { useKibana } from '../../../../../src/plugins/kibana_react/public';
 
 export const useKibanaSpaceId = (): string => {
-  // NOTICE: use of `activeSpace` is deprecated and will not be made available in the New Platform.
-  const activeSpace = useKibanaInjectedVar('activeSpace');
+  const kibana = useKibana();
+  // NOTE: The injectedMetadata service will be deprecated at some point. We should migrate
+  // this to the client side Spaces plugin when it becomes available.
+  const activeSpace = kibana.services.injectedMetadata?.getInjectedVar('activeSpace');
 
   return pipe(
     activeSpaceRT.decode(activeSpace),
diff --git a/x-pack/legacy/plugins/infra/public/utils/use_kibana_ui_setting.ts b/x-pack/plugins/infra/public/utils/use_kibana_ui_setting.ts
similarity index 72%
rename from x-pack/legacy/plugins/infra/public/utils/use_kibana_ui_setting.ts
rename to x-pack/plugins/infra/public/utils/use_kibana_ui_setting.ts
index b3697db81fb6e..aef75570e7db1 100644
--- a/x-pack/legacy/plugins/infra/public/utils/use_kibana_ui_setting.ts
+++ b/x-pack/plugins/infra/public/utils/use_kibana_ui_setting.ts
@@ -3,11 +3,8 @@
  * or more contributor license agreements. Licensed under the Elastic License;
  * you may not use this file except in compliance with the Elastic License.
  */
-
-import { useCallback, useMemo } from 'react';
-
-import { npSetup } from 'ui/new_platform';
-import useObservable from 'react-use/lib/useObservable';
+import { useMemo } from 'react';
+import { useUiSetting$ } from '../../../../../src/plugins/kibana_react/public';
 
 /**
  * This hook behaves like a `useState` hook in that it provides a requested
@@ -46,19 +43,9 @@ export function useKibanaUiSetting(
 ): [any, (key: string, value: any) => Promise<boolean>];
 
 export function useKibanaUiSetting(key: string, defaultValue?: any) {
-  const uiSettingsClient = npSetup.core.uiSettings;
-
-  const uiSetting$ = useMemo(() => uiSettingsClient.get$(key, defaultValue), [
-    defaultValue,
-    key,
-    uiSettingsClient,
-  ]);
-  const uiSetting = useObservable(uiSetting$);
-
-  const setUiSetting = useCallback((value: any) => uiSettingsClient.set(key, value), [
-    key,
-    uiSettingsClient,
-  ]);
-
-  return [uiSetting, setUiSetting];
+  const [uiSetting, setUiSetting] = useUiSetting$(key);
+  const uiSettingValue = useMemo(() => {
+    return uiSetting ? uiSetting : defaultValue;
+  }, [uiSetting, defaultValue]);
+  return [uiSettingValue, setUiSetting];
 }
diff --git a/x-pack/legacy/plugins/infra/public/utils/use_tracked_promise.ts b/x-pack/plugins/infra/public/utils/use_tracked_promise.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/use_tracked_promise.ts
rename to x-pack/plugins/infra/public/utils/use_tracked_promise.ts
diff --git a/x-pack/legacy/plugins/infra/public/utils/use_url_state.ts b/x-pack/plugins/infra/public/utils/use_url_state.ts
similarity index 98%
rename from x-pack/legacy/plugins/infra/public/utils/use_url_state.ts
rename to x-pack/plugins/infra/public/utils/use_url_state.ts
index 284af62e52fbb..7a63b48fa9a1a 100644
--- a/x-pack/legacy/plugins/infra/public/utils/use_url_state.ts
+++ b/x-pack/plugins/infra/public/utils/use_url_state.ts
@@ -8,7 +8,7 @@ import { parse, stringify } from 'query-string';
 import { Location } from 'history';
 import { useCallback, useEffect, useMemo, useState } from 'react';
 import { decode, encode, RisonValue } from 'rison-node';
-import { url } from '../../../../../../src/plugins/kibana_utils/public';
+import { url } from '../../../../../src/plugins/kibana_utils/public';
 
 import { useHistory } from './history_context';
 
diff --git a/x-pack/legacy/plugins/infra/public/utils/use_visibility_state.ts b/x-pack/plugins/infra/public/utils/use_visibility_state.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/public/utils/use_visibility_state.ts
rename to x-pack/plugins/infra/public/utils/use_visibility_state.ts
diff --git a/x-pack/legacy/plugins/infra/scripts/combined_schema.ts b/x-pack/plugins/infra/scripts/combined_schema.ts
similarity index 91%
rename from x-pack/legacy/plugins/infra/scripts/combined_schema.ts
rename to x-pack/plugins/infra/scripts/combined_schema.ts
index 625eb3a4a4755..4813a3b674b31 100644
--- a/x-pack/legacy/plugins/infra/scripts/combined_schema.ts
+++ b/x-pack/plugins/infra/scripts/combined_schema.ts
@@ -5,7 +5,7 @@
  */
 
 import { buildSchemaFromTypeDefinitions } from 'graphql-tools';
-
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { schemas as serverSchemas } from '../server/graphql';
 
 export const schemas = [...serverSchemas];
diff --git a/x-pack/legacy/plugins/infra/scripts/generate_types_from_graphql.js b/x-pack/plugins/infra/scripts/generate_types_from_graphql.js
similarity index 97%
rename from x-pack/legacy/plugins/infra/scripts/generate_types_from_graphql.js
rename to x-pack/plugins/infra/scripts/generate_types_from_graphql.js
index 9e2d3f4e05453..aec5ff6da99ce 100644
--- a/x-pack/legacy/plugins/infra/scripts/generate_types_from_graphql.js
+++ b/x-pack/plugins/infra/scripts/generate_types_from_graphql.js
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-require('../../../../../src/setup_node_env');
+require('../../../../src/setup_node_env');
 
 const { join, resolve } = require('path');
 // eslint-disable-next-line import/no-extraneous-dependencies, import/no-unresolved
diff --git a/x-pack/legacy/plugins/infra/scripts/gql_gen_client.json b/x-pack/plugins/infra/scripts/gql_gen_client.json
similarity index 100%
rename from x-pack/legacy/plugins/infra/scripts/gql_gen_client.json
rename to x-pack/plugins/infra/scripts/gql_gen_client.json
diff --git a/x-pack/legacy/plugins/infra/scripts/gql_gen_server.json b/x-pack/plugins/infra/scripts/gql_gen_server.json
similarity index 100%
rename from x-pack/legacy/plugins/infra/scripts/gql_gen_server.json
rename to x-pack/plugins/infra/scripts/gql_gen_server.json
diff --git a/x-pack/legacy/plugins/infra/scripts/storybook.js b/x-pack/plugins/infra/scripts/storybook.js
similarity index 100%
rename from x-pack/legacy/plugins/infra/scripts/storybook.js
rename to x-pack/plugins/infra/scripts/storybook.js
diff --git a/x-pack/legacy/plugins/infra/server/features.ts b/x-pack/plugins/infra/server/features.ts
similarity index 96%
rename from x-pack/legacy/plugins/infra/server/features.ts
rename to x-pack/plugins/infra/server/features.ts
index 02658002694d2..edf94beab43a7 100644
--- a/x-pack/legacy/plugins/infra/server/features.ts
+++ b/x-pack/plugins/infra/server/features.ts
@@ -12,7 +12,7 @@ export const METRICS_FEATURE = {
     defaultMessage: 'Metrics',
   }),
   icon: 'metricsApp',
-  navLinkId: 'infra:home',
+  navLinkId: 'metrics',
   app: ['infra', 'kibana'],
   catalogue: ['infraops'],
   privileges: {
@@ -41,7 +41,7 @@ export const LOGS_FEATURE = {
     defaultMessage: 'Logs',
   }),
   icon: 'logsApp',
-  navLinkId: 'infra:logs',
+  navLinkId: 'logs',
   app: ['infra', 'kibana'],
   catalogue: ['infralogging'],
   privileges: {
diff --git a/x-pack/legacy/plugins/infra/server/graphql/index.ts b/x-pack/plugins/infra/server/graphql/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/graphql/index.ts
rename to x-pack/plugins/infra/server/graphql/index.ts
diff --git a/x-pack/legacy/plugins/infra/server/graphql/log_entries/index.ts b/x-pack/plugins/infra/server/graphql/log_entries/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/graphql/log_entries/index.ts
rename to x-pack/plugins/infra/server/graphql/log_entries/index.ts
diff --git a/x-pack/legacy/plugins/infra/server/graphql/log_entries/resolvers.ts b/x-pack/plugins/infra/server/graphql/log_entries/resolvers.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/graphql/log_entries/resolvers.ts
rename to x-pack/plugins/infra/server/graphql/log_entries/resolvers.ts
diff --git a/x-pack/legacy/plugins/infra/server/graphql/log_entries/schema.gql.ts b/x-pack/plugins/infra/server/graphql/log_entries/schema.gql.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/graphql/log_entries/schema.gql.ts
rename to x-pack/plugins/infra/server/graphql/log_entries/schema.gql.ts
diff --git a/x-pack/legacy/plugins/infra/server/graphql/source_status/index.ts b/x-pack/plugins/infra/server/graphql/source_status/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/graphql/source_status/index.ts
rename to x-pack/plugins/infra/server/graphql/source_status/index.ts
diff --git a/x-pack/legacy/plugins/infra/server/graphql/source_status/resolvers.ts b/x-pack/plugins/infra/server/graphql/source_status/resolvers.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/graphql/source_status/resolvers.ts
rename to x-pack/plugins/infra/server/graphql/source_status/resolvers.ts
diff --git a/x-pack/legacy/plugins/infra/server/graphql/source_status/schema.gql.ts b/x-pack/plugins/infra/server/graphql/source_status/schema.gql.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/graphql/source_status/schema.gql.ts
rename to x-pack/plugins/infra/server/graphql/source_status/schema.gql.ts
diff --git a/x-pack/legacy/plugins/infra/server/graphql/sources/index.ts b/x-pack/plugins/infra/server/graphql/sources/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/graphql/sources/index.ts
rename to x-pack/plugins/infra/server/graphql/sources/index.ts
diff --git a/x-pack/legacy/plugins/infra/server/graphql/sources/resolvers.ts b/x-pack/plugins/infra/server/graphql/sources/resolvers.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/graphql/sources/resolvers.ts
rename to x-pack/plugins/infra/server/graphql/sources/resolvers.ts
diff --git a/x-pack/legacy/plugins/infra/server/graphql/sources/schema.gql.ts b/x-pack/plugins/infra/server/graphql/sources/schema.gql.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/graphql/sources/schema.gql.ts
rename to x-pack/plugins/infra/server/graphql/sources/schema.gql.ts
diff --git a/x-pack/legacy/plugins/infra/server/graphql/types.ts b/x-pack/plugins/infra/server/graphql/types.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/graphql/types.ts
rename to x-pack/plugins/infra/server/graphql/types.ts
diff --git a/x-pack/plugins/infra/server/index.ts b/x-pack/plugins/infra/server/index.ts
index b12f92c8c5a9d..54f858554a5a3 100644
--- a/x-pack/plugins/infra/server/index.ts
+++ b/x-pack/plugins/infra/server/index.ts
@@ -4,21 +4,12 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { schema, TypeOf } from '@kbn/config-schema';
 import { PluginInitializerContext } from 'src/core/server';
-import { InfraPlugin } from './plugin';
+import { config, InfraConfig, InfraServerPlugin } from './plugin';
+import { savedObjectMappings } from './saved_objects';
 
-export const config = {
-  schema: schema.object({
-    enabled: schema.maybe(schema.boolean()),
-    query: schema.object({
-      partitionSize: schema.maybe(schema.number()),
-      partitionFactor: schema.maybe(schema.number()),
-    }),
-  }),
-};
+export { config, InfraConfig, savedObjectMappings };
 
-export const plugin = (initContext: PluginInitializerContext) => new InfraPlugin(initContext);
-
-export type InfraConfig = TypeOf<typeof config.schema>;
-export { InfraSetup } from './plugin';
+export function plugin(context: PluginInitializerContext) {
+  return new InfraServerPlugin(context);
+}
diff --git a/x-pack/legacy/plugins/infra/server/infra_server.ts b/x-pack/plugins/infra/server/infra_server.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/infra_server.ts
rename to x-pack/plugins/infra/server/infra_server.ts
diff --git a/x-pack/legacy/plugins/infra/server/kibana.index.ts b/x-pack/plugins/infra/server/kibana.index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/kibana.index.ts
rename to x-pack/plugins/infra/server/kibana.index.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/adapters/fields/adapter_types.ts b/x-pack/plugins/infra/server/lib/adapters/fields/adapter_types.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/adapters/fields/adapter_types.ts
rename to x-pack/plugins/infra/server/lib/adapters/fields/adapter_types.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/adapters/fields/framework_fields_adapter.ts b/x-pack/plugins/infra/server/lib/adapters/fields/framework_fields_adapter.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/adapters/fields/framework_fields_adapter.ts
rename to x-pack/plugins/infra/server/lib/adapters/fields/framework_fields_adapter.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/adapters/fields/index.ts b/x-pack/plugins/infra/server/lib/adapters/fields/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/adapters/fields/index.ts
rename to x-pack/plugins/infra/server/lib/adapters/fields/index.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts
similarity index 89%
rename from x-pack/legacy/plugins/infra/server/lib/adapters/framework/adapter_types.ts
rename to x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts
index b14536275cec3..f8177365c9bdd 100644
--- a/x-pack/legacy/plugins/infra/server/lib/adapters/framework/adapter_types.ts
+++ b/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts
@@ -7,12 +7,12 @@
 import { SearchResponse, GenericParams } from 'elasticsearch';
 import { Lifecycle } from 'hapi';
 import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
-import { RouteMethod, RouteConfig } from '../../../../../../../../src/core/server';
-import { PluginSetupContract as FeaturesPluginSetup } from '../../../../../../../plugins/features/server';
-import { SpacesPluginSetup } from '../../../../../../../plugins/spaces/server';
-import { VisTypeTimeseriesSetup } from '../../../../../../../../src/plugins/vis_type_timeseries/server';
-import { APMPluginContract } from '../../../../../../../plugins/apm/server';
-import { HomeServerPluginSetup } from '../../../../../../../../src/plugins/home/server';
+import { RouteMethod, RouteConfig } from '../../../../../../../src/core/server';
+import { PluginSetupContract as FeaturesPluginSetup } from '../../../../../../plugins/features/server';
+import { SpacesPluginSetup } from '../../../../../../plugins/spaces/server';
+import { VisTypeTimeseriesSetup } from '../../../../../../../src/plugins/vis_type_timeseries/server';
+import { APMPluginContract } from '../../../../../../plugins/apm/server';
+import { HomeServerPluginSetup } from '../../../../../../../src/plugins/home/server';
 
 // NP_TODO: Compose real types from plugins we depend on, no "any"
 export interface InfraServerPluginDeps {
diff --git a/x-pack/legacy/plugins/infra/server/lib/adapters/framework/index.ts b/x-pack/plugins/infra/server/lib/adapters/framework/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/adapters/framework/index.ts
rename to x-pack/plugins/infra/server/lib/adapters/framework/index.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts b/x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts
similarity index 92%
rename from x-pack/legacy/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts
rename to x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts
index 4b1aa774a523f..7c12e23d7e903 100644
--- a/x-pack/legacy/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts
+++ b/x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts
@@ -6,7 +6,6 @@
 
 /* eslint-disable @typescript-eslint/array-type */
 
-import { GenericParams } from 'elasticsearch';
 import { GraphQLSchema } from 'graphql';
 import { Legacy } from 'kibana';
 import { runHttpQuery } from 'apollo-server-core';
@@ -30,9 +29,11 @@ import {
   RequestHandlerContext,
   KibanaResponseFactory,
   RouteMethod,
-} from '../../../../../../../../src/core/server';
-import { RequestHandler } from '../../../../../../../../src/core/server';
-import { InfraConfig } from '../../../../../../../plugins/infra/server';
+  APICaller,
+} from '../../../../../../../src/core/server';
+import { RequestHandler } from '../../../../../../../src/core/server';
+import { InfraConfig } from '../../../plugin';
+import { IndexPatternsFetcher } from '../../../../../../../src/plugins/data/server';
 
 export class KibanaFramework {
   public router: IRouter;
@@ -214,14 +215,10 @@ export class KibanaFramework {
   public getIndexPatternsService(
     requestContext: RequestHandlerContext
   ): Legacy.IndexPatternsService {
-    return this.plugins.indexPatterns.indexPatternsServiceFactory({
-      callCluster: async (method: string, args: [GenericParams], ...rest: any[]) => {
-        const fieldCaps = await this.callWithRequest(requestContext, method, {
-          ...args,
-          allowNoIndices: true,
-        } as GenericParams);
-        return fieldCaps;
-      },
+    return new IndexPatternsFetcher((...rest: Parameters<APICaller>) => {
+      rest[1] = rest[1] || {};
+      rest[1].allowNoIndices = true;
+      return requestContext.core.elasticsearch.adminClient.callAsCurrentUser(...rest);
     });
   }
 
diff --git a/x-pack/legacy/plugins/infra/server/lib/adapters/log_entries/adapter_types.ts b/x-pack/plugins/infra/server/lib/adapters/log_entries/adapter_types.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/adapters/log_entries/adapter_types.ts
rename to x-pack/plugins/infra/server/lib/adapters/log_entries/adapter_types.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/adapters/log_entries/index.ts b/x-pack/plugins/infra/server/lib/adapters/log_entries/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/adapters/log_entries/index.ts
rename to x-pack/plugins/infra/server/lib/adapters/log_entries/index.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts b/x-pack/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts
similarity index 97%
rename from x-pack/legacy/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts
rename to x-pack/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts
index b936d79a8edcd..f48c949329b04 100644
--- a/x-pack/legacy/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts
+++ b/x-pack/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts
@@ -8,11 +8,7 @@
 
 import { timeMilliseconds } from 'd3-time';
 import * as runtimeTypes from 'io-ts';
-import { compact } from 'lodash';
-import first from 'lodash/fp/first';
-import get from 'lodash/fp/get';
-import has from 'lodash/fp/has';
-import zip from 'lodash/fp/zip';
+import { compact, first, get, has, zip } from 'lodash';
 import { pipe } from 'fp-ts/lib/pipeable';
 import { map, fold } from 'fp-ts/lib/Either';
 import { identity, constant } from 'fp-ts/lib/function';
@@ -408,8 +404,8 @@ function mapHitsToLogEntryDocuments(
   return hits.map(hit => {
     const logFields = fields.reduce<{ [fieldName: string]: JsonValue }>(
       (flattenedFields, field) => {
-        if (has(field, hit._source)) {
-          flattenedFields[field] = get(field, hit._source);
+        if (has(hit._source, field)) {
+          flattenedFields[field] = get(hit._source, field);
         }
         return flattenedFields;
       },
@@ -434,13 +430,13 @@ const convertHitToLogEntryDocument = (fields: string[]) => (
   gid: hit._id,
   fields: fields.reduce(
     (flattenedFields, fieldName) =>
-      has(fieldName, hit._source)
+      has(hit._source, fieldName)
         ? {
             ...flattenedFields,
-            [fieldName]: get(fieldName, hit._source),
+            [fieldName]: get(hit._source, fieldName),
           }
         : flattenedFields,
-    {} as { [fieldName: string]: string | number | boolean | null }
+    {} as { [fieldName: string]: string | number | object | boolean | null }
   ),
   highlights: hit.highlight || {},
   key: {
diff --git a/x-pack/legacy/plugins/infra/server/lib/adapters/metrics/adapter_types.ts b/x-pack/plugins/infra/server/lib/adapters/metrics/adapter_types.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/adapters/metrics/adapter_types.ts
rename to x-pack/plugins/infra/server/lib/adapters/metrics/adapter_types.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/adapters/metrics/index.ts b/x-pack/plugins/infra/server/lib/adapters/metrics/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/adapters/metrics/index.ts
rename to x-pack/plugins/infra/server/lib/adapters/metrics/index.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts b/x-pack/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts
rename to x-pack/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/adapters/metrics/lib/check_valid_node.ts b/x-pack/plugins/infra/server/lib/adapters/metrics/lib/check_valid_node.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/adapters/metrics/lib/check_valid_node.ts
rename to x-pack/plugins/infra/server/lib/adapters/metrics/lib/check_valid_node.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/adapters/metrics/lib/errors.ts b/x-pack/plugins/infra/server/lib/adapters/metrics/lib/errors.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/adapters/metrics/lib/errors.ts
rename to x-pack/plugins/infra/server/lib/adapters/metrics/lib/errors.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/adapters/source_status/elasticsearch_source_status_adapter.ts b/x-pack/plugins/infra/server/lib/adapters/source_status/elasticsearch_source_status_adapter.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/adapters/source_status/elasticsearch_source_status_adapter.ts
rename to x-pack/plugins/infra/server/lib/adapters/source_status/elasticsearch_source_status_adapter.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/adapters/source_status/index.ts b/x-pack/plugins/infra/server/lib/adapters/source_status/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/adapters/source_status/index.ts
rename to x-pack/plugins/infra/server/lib/adapters/source_status/index.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/compose/kibana.ts b/x-pack/plugins/infra/server/lib/compose/kibana.ts
similarity index 94%
rename from x-pack/legacy/plugins/infra/server/lib/compose/kibana.ts
rename to x-pack/plugins/infra/server/lib/compose/kibana.ts
index d8a39a6b9c16f..f100726b5b92e 100644
--- a/x-pack/legacy/plugins/infra/server/lib/compose/kibana.ts
+++ b/x-pack/plugins/infra/server/lib/compose/kibana.ts
@@ -16,8 +16,8 @@ import { LogEntryCategoriesAnalysis, LogEntryRateAnalysis } from '../log_analysi
 import { InfraSnapshot } from '../snapshot';
 import { InfraSourceStatus } from '../source_status';
 import { InfraSources } from '../sources';
-import { InfraConfig } from '../../../../../../plugins/infra/server';
-import { CoreSetup } from '../../../../../../../src/core/server';
+import { InfraConfig } from '../../../server';
+import { CoreSetup } from '../../../../../../src/core/server';
 import { InfraServerPluginDeps } from '../adapters/framework/adapter_types';
 
 export function compose(core: CoreSetup, config: InfraConfig, plugins: InfraServerPluginDeps) {
diff --git a/x-pack/legacy/plugins/infra/server/lib/constants.ts b/x-pack/plugins/infra/server/lib/constants.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/constants.ts
rename to x-pack/plugins/infra/server/lib/constants.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/fields_domain.ts b/x-pack/plugins/infra/server/lib/domains/fields_domain.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/fields_domain.ts
rename to x-pack/plugins/infra/server/lib/domains/fields_domain.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_apache2.test.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_apache2.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_apache2.test.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_apache2.test.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_apache2.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_apache2.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_apache2.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_apache2.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_auditd.test.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_auditd.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_auditd.test.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_auditd.test.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_auditd.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_auditd.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_auditd.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_auditd.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_haproxy.test.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_haproxy.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_haproxy.test.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_haproxy.test.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_haproxy.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_haproxy.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_haproxy.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_haproxy.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_icinga.test.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_icinga.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_icinga.test.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_icinga.test.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_icinga.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_icinga.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_icinga.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_icinga.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_iis.test.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_iis.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_iis.test.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_iis.test.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_iis.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_iis.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_iis.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_iis.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_kafka.test.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_kafka.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_kafka.test.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_kafka.test.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_logstash.test.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_logstash.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_logstash.test.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_logstash.test.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_logstash.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_logstash.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_logstash.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_logstash.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_mongodb.test.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_mongodb.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_mongodb.test.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_mongodb.test.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_mongodb.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_mongodb.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_mongodb.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_mongodb.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_mysql.test.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_mysql.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_mysql.test.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_mysql.test.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_mysql.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_mysql.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_mysql.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_mysql.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_nginx.test.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_nginx.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_nginx.test.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_nginx.test.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_nginx.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_nginx.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_nginx.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_nginx.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_osquery.test.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_osquery.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_osquery.test.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_osquery.test.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_osquery.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_osquery.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_osquery.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_osquery.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_redis.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_redis.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_redis.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_redis.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_system.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_system.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_system.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_system.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_traefik.test.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_traefik.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_traefik.test.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_traefik.test.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_traefik.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_traefik.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_traefik.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/filebeat_traefik.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/generic.test.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/generic.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/generic.test.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/generic.test.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/generic.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/generic.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/generic.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/generic.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/generic_webserver.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/generic_webserver.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/generic_webserver.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/generic_webserver.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/helpers.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/helpers.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/helpers.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/helpers.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/index.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/index.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/builtin_rules/index.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/convert_document_source_to_log_item_fields.test.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/convert_document_source_to_log_item_fields.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/convert_document_source_to_log_item_fields.test.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/convert_document_source_to_log_item_fields.test.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/convert_document_source_to_log_item_fields.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/convert_document_source_to_log_item_fields.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/convert_document_source_to_log_item_fields.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/convert_document_source_to_log_item_fields.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/index.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/index.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/index.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/message.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/message.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/message.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/message.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/rule_types.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/rule_types.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/log_entries_domain/rule_types.ts
rename to x-pack/plugins/infra/server/lib/domains/log_entries_domain/rule_types.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/domains/metrics_domain.ts b/x-pack/plugins/infra/server/lib/domains/metrics_domain.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/domains/metrics_domain.ts
rename to x-pack/plugins/infra/server/lib/domains/metrics_domain.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/infra_types.ts b/x-pack/plugins/infra/server/lib/infra_types.ts
similarity index 92%
rename from x-pack/legacy/plugins/infra/server/lib/infra_types.ts
rename to x-pack/plugins/infra/server/lib/infra_types.ts
index d52416b39596b..51c433557f4fc 100644
--- a/x-pack/legacy/plugins/infra/server/lib/infra_types.ts
+++ b/x-pack/plugins/infra/server/lib/infra_types.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { InfraSourceConfiguration } from '../../public/graphql/types';
+import { InfraSourceConfiguration } from '../../common/graphql/types';
 import { InfraFieldsDomain } from './domains/fields_domain';
 import { InfraLogEntriesDomain } from './domains/log_entries_domain';
 import { InfraMetricsDomain } from './domains/metrics_domain';
@@ -12,7 +12,7 @@ import { LogEntryCategoriesAnalysis, LogEntryRateAnalysis } from './log_analysis
 import { InfraSnapshot } from './snapshot';
 import { InfraSources } from './sources';
 import { InfraSourceStatus } from './source_status';
-import { InfraConfig } from '../../../../../plugins/infra/server';
+import { InfraConfig } from '../plugin';
 import { KibanaFramework } from './adapters/framework/kibana_framework_adapter';
 
 // NP_TODO: We shouldn't need this context anymore but I am
diff --git a/x-pack/legacy/plugins/infra/server/lib/log_analysis/errors.ts b/x-pack/plugins/infra/server/lib/log_analysis/errors.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/log_analysis/errors.ts
rename to x-pack/plugins/infra/server/lib/log_analysis/errors.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/log_analysis/index.ts b/x-pack/plugins/infra/server/lib/log_analysis/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/log_analysis/index.ts
rename to x-pack/plugins/infra/server/lib/log_analysis/index.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/log_analysis/log_entry_categories_analysis.ts b/x-pack/plugins/infra/server/lib/log_analysis/log_entry_categories_analysis.ts
similarity index 99%
rename from x-pack/legacy/plugins/infra/server/lib/log_analysis/log_entry_categories_analysis.ts
rename to x-pack/plugins/infra/server/lib/log_analysis/log_entry_categories_analysis.ts
index f2b6c468df69f..2e270f16ca867 100644
--- a/x-pack/legacy/plugins/infra/server/lib/log_analysis/log_entry_categories_analysis.ts
+++ b/x-pack/plugins/infra/server/lib/log_analysis/log_entry_categories_analysis.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { KibanaRequest, RequestHandlerContext } from '../../../../../../../src/core/server';
+import { KibanaRequest, RequestHandlerContext } from 'src/core/server';
 import { getJobId, logEntryCategoriesJobTypes } from '../../../common/log_analysis';
 import { startTracingSpan, TracingSpan } from '../../../common/performance_tracing';
 import { decodeOrThrow } from '../../../common/runtime_types';
diff --git a/x-pack/legacy/plugins/infra/server/lib/log_analysis/log_entry_rate_analysis.ts b/x-pack/plugins/infra/server/lib/log_analysis/log_entry_rate_analysis.ts
similarity index 98%
rename from x-pack/legacy/plugins/infra/server/lib/log_analysis/log_entry_rate_analysis.ts
rename to x-pack/plugins/infra/server/lib/log_analysis/log_entry_rate_analysis.ts
index 515856fa6be8a..f60f758053c47 100644
--- a/x-pack/legacy/plugins/infra/server/lib/log_analysis/log_entry_rate_analysis.ts
+++ b/x-pack/plugins/infra/server/lib/log_analysis/log_entry_rate_analysis.ts
@@ -7,6 +7,7 @@
 import { pipe } from 'fp-ts/lib/pipeable';
 import { map, fold } from 'fp-ts/lib/Either';
 import { identity } from 'fp-ts/lib/function';
+import { RequestHandlerContext, KibanaRequest } from 'src/core/server';
 import { getJobId } from '../../../common/log_analysis';
 import { throwErrors, createPlainError } from '../../../common/runtime_types';
 import { KibanaFramework } from '../adapters/framework/kibana_framework_adapter';
@@ -17,7 +18,6 @@ import {
   LogRateModelPlotBucket,
   CompositeTimestampPartitionKey,
 } from './queries';
-import { RequestHandlerContext, KibanaRequest } from '../../../../../../../src/core/server';
 
 const COMPOSITE_AGGREGATION_BATCH_SIZE = 1000;
 
diff --git a/x-pack/legacy/plugins/infra/server/lib/log_analysis/queries/common.ts b/x-pack/plugins/infra/server/lib/log_analysis/queries/common.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/log_analysis/queries/common.ts
rename to x-pack/plugins/infra/server/lib/log_analysis/queries/common.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/log_analysis/queries/index.ts b/x-pack/plugins/infra/server/lib/log_analysis/queries/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/log_analysis/queries/index.ts
rename to x-pack/plugins/infra/server/lib/log_analysis/queries/index.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/log_analysis/queries/log_entry_categories.ts b/x-pack/plugins/infra/server/lib/log_analysis/queries/log_entry_categories.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/log_analysis/queries/log_entry_categories.ts
rename to x-pack/plugins/infra/server/lib/log_analysis/queries/log_entry_categories.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/log_analysis/queries/log_entry_category_histograms.ts b/x-pack/plugins/infra/server/lib/log_analysis/queries/log_entry_category_histograms.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/log_analysis/queries/log_entry_category_histograms.ts
rename to x-pack/plugins/infra/server/lib/log_analysis/queries/log_entry_category_histograms.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/log_analysis/queries/log_entry_data_sets.ts b/x-pack/plugins/infra/server/lib/log_analysis/queries/log_entry_data_sets.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/log_analysis/queries/log_entry_data_sets.ts
rename to x-pack/plugins/infra/server/lib/log_analysis/queries/log_entry_data_sets.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/log_analysis/queries/log_entry_rate.ts b/x-pack/plugins/infra/server/lib/log_analysis/queries/log_entry_rate.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/log_analysis/queries/log_entry_rate.ts
rename to x-pack/plugins/infra/server/lib/log_analysis/queries/log_entry_rate.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/log_analysis/queries/top_log_entry_categories.ts b/x-pack/plugins/infra/server/lib/log_analysis/queries/top_log_entry_categories.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/log_analysis/queries/top_log_entry_categories.ts
rename to x-pack/plugins/infra/server/lib/log_analysis/queries/top_log_entry_categories.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/snapshot/constants.ts b/x-pack/plugins/infra/server/lib/snapshot/constants.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/snapshot/constants.ts
rename to x-pack/plugins/infra/server/lib/snapshot/constants.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/snapshot/create_timerange_with_interval.ts b/x-pack/plugins/infra/server/lib/snapshot/create_timerange_with_interval.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/snapshot/create_timerange_with_interval.ts
rename to x-pack/plugins/infra/server/lib/snapshot/create_timerange_with_interval.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/snapshot/index.ts b/x-pack/plugins/infra/server/lib/snapshot/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/snapshot/index.ts
rename to x-pack/plugins/infra/server/lib/snapshot/index.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/snapshot/query_helpers.ts b/x-pack/plugins/infra/server/lib/snapshot/query_helpers.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/snapshot/query_helpers.ts
rename to x-pack/plugins/infra/server/lib/snapshot/query_helpers.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/snapshot/response_helpers.test.ts b/x-pack/plugins/infra/server/lib/snapshot/response_helpers.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/snapshot/response_helpers.test.ts
rename to x-pack/plugins/infra/server/lib/snapshot/response_helpers.test.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/snapshot/response_helpers.ts b/x-pack/plugins/infra/server/lib/snapshot/response_helpers.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/snapshot/response_helpers.ts
rename to x-pack/plugins/infra/server/lib/snapshot/response_helpers.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/snapshot/snapshot.ts b/x-pack/plugins/infra/server/lib/snapshot/snapshot.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/snapshot/snapshot.ts
rename to x-pack/plugins/infra/server/lib/snapshot/snapshot.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/snapshot/types.ts b/x-pack/plugins/infra/server/lib/snapshot/types.ts
similarity index 90%
rename from x-pack/legacy/plugins/infra/server/lib/snapshot/types.ts
rename to x-pack/plugins/infra/server/lib/snapshot/types.ts
index 3627747e327e0..7e17cb91c6a59 100644
--- a/x-pack/legacy/plugins/infra/server/lib/snapshot/types.ts
+++ b/x-pack/plugins/infra/server/lib/snapshot/types.ts
@@ -5,7 +5,7 @@
  */
 
 import { JsonObject } from '../../../common/typed_json';
-import { InfraSourceConfiguration } from '../../../public/graphql/types';
+import { InfraSourceConfiguration } from '../../../common/graphql/types';
 import { SnapshotRequest } from '../../../common/http_api/snapshot_api';
 
 export interface InfraSnapshotRequestOptions
diff --git a/x-pack/legacy/plugins/infra/server/lib/source_status.ts b/x-pack/plugins/infra/server/lib/source_status.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/source_status.ts
rename to x-pack/plugins/infra/server/lib/source_status.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/sources/defaults.ts b/x-pack/plugins/infra/server/lib/sources/defaults.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/sources/defaults.ts
rename to x-pack/plugins/infra/server/lib/sources/defaults.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/sources/errors.ts b/x-pack/plugins/infra/server/lib/sources/errors.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/sources/errors.ts
rename to x-pack/plugins/infra/server/lib/sources/errors.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/sources/index.ts b/x-pack/plugins/infra/server/lib/sources/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/sources/index.ts
rename to x-pack/plugins/infra/server/lib/sources/index.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/sources/saved_object_mappings.ts b/x-pack/plugins/infra/server/lib/sources/saved_object_mappings.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/sources/saved_object_mappings.ts
rename to x-pack/plugins/infra/server/lib/sources/saved_object_mappings.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/sources/sources.test.ts b/x-pack/plugins/infra/server/lib/sources/sources.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/sources/sources.test.ts
rename to x-pack/plugins/infra/server/lib/sources/sources.test.ts
diff --git a/x-pack/legacy/plugins/infra/server/lib/sources/sources.ts b/x-pack/plugins/infra/server/lib/sources/sources.ts
similarity index 99%
rename from x-pack/legacy/plugins/infra/server/lib/sources/sources.ts
rename to x-pack/plugins/infra/server/lib/sources/sources.ts
index 2b38d81e4a8d5..5b9207a88e6e4 100644
--- a/x-pack/legacy/plugins/infra/server/lib/sources/sources.ts
+++ b/x-pack/plugins/infra/server/lib/sources/sources.ts
@@ -21,7 +21,7 @@ import {
   SourceConfigurationSavedObjectRuntimeType,
   StaticSourceConfigurationRuntimeType,
 } from './types';
-import { InfraConfig } from '../../../../../../plugins/infra/server';
+import { InfraConfig } from '../../../server';
 
 interface Libs {
   config: InfraConfig;
diff --git a/x-pack/legacy/plugins/infra/server/lib/sources/types.ts b/x-pack/plugins/infra/server/lib/sources/types.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/lib/sources/types.ts
rename to x-pack/plugins/infra/server/lib/sources/types.ts
diff --git a/x-pack/plugins/infra/server/plugin.ts b/x-pack/plugins/infra/server/plugin.ts
index 0c763313fb973..ac45321e870e6 100644
--- a/x-pack/plugins/infra/server/plugin.ts
+++ b/x-pack/plugins/infra/server/plugin.ts
@@ -4,30 +4,158 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { Plugin, PluginInitializerContext } from 'src/core/server';
+import { CoreSetup, PluginInitializerContext } from 'src/core/server';
+import { Server } from 'hapi';
+import { Observable } from 'rxjs';
+import { schema, TypeOf } from '@kbn/config-schema';
+import { i18n } from '@kbn/i18n';
+import { initInfraServer } from './infra_server';
+import { InfraBackendLibs, InfraDomainLibs } from './lib/infra_types';
+import { FrameworkFieldsAdapter } from './lib/adapters/fields/framework_fields_adapter';
+import { KibanaFramework } from './lib/adapters/framework/kibana_framework_adapter';
+import { InfraKibanaLogEntriesAdapter } from './lib/adapters/log_entries/kibana_log_entries_adapter';
+import { KibanaMetricsAdapter } from './lib/adapters/metrics/kibana_metrics_adapter';
+import { InfraElasticsearchSourceStatusAdapter } from './lib/adapters/source_status';
+import { InfraFieldsDomain } from './lib/domains/fields_domain';
+import { InfraLogEntriesDomain } from './lib/domains/log_entries_domain';
+import { InfraMetricsDomain } from './lib/domains/metrics_domain';
+import { LogEntryCategoriesAnalysis, LogEntryRateAnalysis } from './lib/log_analysis';
+import { InfraSnapshot } from './lib/snapshot';
+import { InfraSourceStatus } from './lib/source_status';
+import { InfraSources } from './lib/sources';
+import { InfraServerPluginDeps } from './lib/adapters/framework';
+import { METRICS_FEATURE, LOGS_FEATURE } from './features';
+import { UsageCollector } from './usage/usage_collector';
+import { InfraStaticSourceConfiguration } from './lib/sources/types';
 
-export class InfraPlugin implements Plugin<InfraSetup> {
-  private readonly initContext: PluginInitializerContext;
+export const config = {
+  schema: schema.object({
+    query: schema.object({
+      partitionSize: schema.number({ defaultValue: 75 }),
+      partitionFactor: schema.number({ defaultValue: 1.2 }),
+    }),
+    sources: schema.maybe(
+      schema.object({
+        default: schema.maybe(
+          schema.object({
+            logAlias: schema.maybe(schema.string()),
+            metricAlias: schema.maybe(schema.string()),
+            fields: schema.maybe(
+              schema.object({
+                timestamp: schema.maybe(schema.string()),
+                message: schema.maybe(schema.arrayOf(schema.string())),
+                tiebreaker: schema.maybe(schema.string()),
+                host: schema.maybe(schema.string()),
+                container: schema.maybe(schema.string()),
+                pod: schema.maybe(schema.string()),
+              })
+            ),
+          })
+        ),
+      })
+    ),
+  }),
+};
 
-  constructor(initContext: PluginInitializerContext) {
-    this.initContext = initContext;
+export type InfraConfig = TypeOf<typeof config.schema>;
+
+export interface KbnServer extends Server {
+  usage: any;
+}
+
+const logsSampleDataLinkLabel = i18n.translate('xpack.infra.sampleDataLinkLabel', {
+  defaultMessage: 'Logs',
+});
+
+export interface InfraPluginSetup {
+  defineInternalSourceConfiguration: (
+    sourceId: string,
+    sourceProperties: InfraStaticSourceConfiguration
+  ) => void;
+}
+
+export class InfraServerPlugin {
+  private config$: Observable<InfraConfig>;
+  public config = {} as InfraConfig;
+  public libs: InfraBackendLibs | undefined;
+
+  constructor(context: PluginInitializerContext) {
+    this.config$ = context.config.create<InfraConfig>();
+  }
+
+  getLibs() {
+    if (!this.libs) {
+      throw new Error('libs not set up yet');
+    }
+    return this.libs;
   }
 
-  public setup() {
+  async setup(core: CoreSetup, plugins: InfraServerPluginDeps) {
+    await new Promise(resolve => {
+      this.config$.subscribe(configValue => {
+        this.config = configValue;
+        resolve();
+      });
+    });
+    const framework = new KibanaFramework(core, this.config, plugins);
+    const sources = new InfraSources({
+      config: this.config,
+    });
+    const sourceStatus = new InfraSourceStatus(
+      new InfraElasticsearchSourceStatusAdapter(framework),
+      {
+        sources,
+      }
+    );
+    const snapshot = new InfraSnapshot({ sources, framework });
+    const logEntryCategoriesAnalysis = new LogEntryCategoriesAnalysis({ framework });
+    const logEntryRateAnalysis = new LogEntryRateAnalysis({ framework });
+
+    // TODO: separate these out individually and do away with "domains" as a temporary group
+    const domainLibs: InfraDomainLibs = {
+      fields: new InfraFieldsDomain(new FrameworkFieldsAdapter(framework), {
+        sources,
+      }),
+      logEntries: new InfraLogEntriesDomain(new InfraKibanaLogEntriesAdapter(framework), {
+        sources,
+      }),
+      metrics: new InfraMetricsDomain(new KibanaMetricsAdapter(framework)),
+    };
+
+    this.libs = {
+      configuration: this.config,
+      framework,
+      logEntryCategoriesAnalysis,
+      logEntryRateAnalysis,
+      snapshot,
+      sources,
+      sourceStatus,
+      ...domainLibs,
+    };
+
+    plugins.features.registerFeature(METRICS_FEATURE);
+    plugins.features.registerFeature(LOGS_FEATURE);
+
+    plugins.home.sampleData.addAppLinksToSampleDataset('logs', [
+      {
+        path: `/app/logs`,
+        label: logsSampleDataLinkLabel,
+        icon: 'logsApp',
+      },
+    ]);
+
+    initInfraServer(this.libs);
+
+    // Telemetry
+    UsageCollector.registerUsageCollector(plugins.usageCollection);
+
     return {
-      __legacy: {
-        config: this.initContext.config,
+      defineInternalSourceConfiguration(sourceId, sourceProperties) {
+        sources.defineInternalSourceConfiguration(sourceId, sourceProperties);
       },
-    };
+    } as InfraPluginSetup;
   }
 
-  public start() {}
-  public stop() {}
-}
-
-export interface InfraSetup {
-  /** @deprecated */
-  __legacy: {
-    config: PluginInitializerContext['config'];
-  };
+  start() {}
+  stop() {}
 }
diff --git a/x-pack/legacy/plugins/infra/server/routes/inventory_metadata/index.ts b/x-pack/plugins/infra/server/routes/inventory_metadata/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/routes/inventory_metadata/index.ts
rename to x-pack/plugins/infra/server/routes/inventory_metadata/index.ts
diff --git a/x-pack/legacy/plugins/infra/server/routes/inventory_metadata/lib/get_cloud_metadata.ts b/x-pack/plugins/infra/server/routes/inventory_metadata/lib/get_cloud_metadata.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/routes/inventory_metadata/lib/get_cloud_metadata.ts
rename to x-pack/plugins/infra/server/routes/inventory_metadata/lib/get_cloud_metadata.ts
diff --git a/x-pack/legacy/plugins/infra/server/routes/ip_to_hostname.ts b/x-pack/plugins/infra/server/routes/ip_to_hostname.ts
similarity index 96%
rename from x-pack/legacy/plugins/infra/server/routes/ip_to_hostname.ts
rename to x-pack/plugins/infra/server/routes/ip_to_hostname.ts
index 5ad79b3d17a13..28b7777c1d688 100644
--- a/x-pack/legacy/plugins/infra/server/routes/ip_to_hostname.ts
+++ b/x-pack/plugins/infra/server/routes/ip_to_hostname.ts
@@ -7,10 +7,6 @@ import { first } from 'lodash';
 import { schema } from '@kbn/config-schema';
 import { InfraBackendLibs } from '../lib/infra_types';
 
-export interface IpToHostResponse {
-  host: string;
-}
-
 interface HostDoc {
   _source: {
     host: {
diff --git a/x-pack/legacy/plugins/infra/server/routes/log_analysis/index.ts b/x-pack/plugins/infra/server/routes/log_analysis/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/routes/log_analysis/index.ts
rename to x-pack/plugins/infra/server/routes/log_analysis/index.ts
diff --git a/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/index.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/routes/log_analysis/results/index.ts
rename to x-pack/plugins/infra/server/routes/log_analysis/results/index.ts
diff --git a/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_categories.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_categories.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_categories.ts
rename to x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_categories.ts
diff --git a/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets.ts
rename to x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets.ts
diff --git a/x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts
rename to x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts
diff --git a/x-pack/legacy/plugins/infra/server/routes/log_analysis/validation/index.ts b/x-pack/plugins/infra/server/routes/log_analysis/validation/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/routes/log_analysis/validation/index.ts
rename to x-pack/plugins/infra/server/routes/log_analysis/validation/index.ts
diff --git a/x-pack/legacy/plugins/infra/server/routes/log_analysis/validation/indices.ts b/x-pack/plugins/infra/server/routes/log_analysis/validation/indices.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/routes/log_analysis/validation/indices.ts
rename to x-pack/plugins/infra/server/routes/log_analysis/validation/indices.ts
diff --git a/x-pack/legacy/plugins/infra/server/routes/log_entries/entries.ts b/x-pack/plugins/infra/server/routes/log_entries/entries.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/routes/log_entries/entries.ts
rename to x-pack/plugins/infra/server/routes/log_entries/entries.ts
diff --git a/x-pack/legacy/plugins/infra/server/routes/log_entries/highlights.ts b/x-pack/plugins/infra/server/routes/log_entries/highlights.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/routes/log_entries/highlights.ts
rename to x-pack/plugins/infra/server/routes/log_entries/highlights.ts
diff --git a/x-pack/legacy/plugins/infra/server/routes/log_entries/index.ts b/x-pack/plugins/infra/server/routes/log_entries/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/routes/log_entries/index.ts
rename to x-pack/plugins/infra/server/routes/log_entries/index.ts
diff --git a/x-pack/legacy/plugins/infra/server/routes/log_entries/item.ts b/x-pack/plugins/infra/server/routes/log_entries/item.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/routes/log_entries/item.ts
rename to x-pack/plugins/infra/server/routes/log_entries/item.ts
diff --git a/x-pack/legacy/plugins/infra/server/routes/log_entries/summary.ts b/x-pack/plugins/infra/server/routes/log_entries/summary.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/routes/log_entries/summary.ts
rename to x-pack/plugins/infra/server/routes/log_entries/summary.ts
diff --git a/x-pack/legacy/plugins/infra/server/routes/log_entries/summary_highlights.ts b/x-pack/plugins/infra/server/routes/log_entries/summary_highlights.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/routes/log_entries/summary_highlights.ts
rename to x-pack/plugins/infra/server/routes/log_entries/summary_highlights.ts
diff --git a/x-pack/legacy/plugins/infra/server/routes/metadata/index.ts b/x-pack/plugins/infra/server/routes/metadata/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/routes/metadata/index.ts
rename to x-pack/plugins/infra/server/routes/metadata/index.ts
diff --git a/x-pack/legacy/plugins/infra/server/routes/metadata/lib/get_cloud_metric_metadata.ts b/x-pack/plugins/infra/server/routes/metadata/lib/get_cloud_metric_metadata.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/routes/metadata/lib/get_cloud_metric_metadata.ts
rename to x-pack/plugins/infra/server/routes/metadata/lib/get_cloud_metric_metadata.ts
diff --git a/x-pack/legacy/plugins/infra/server/routes/metadata/lib/get_metric_metadata.ts b/x-pack/plugins/infra/server/routes/metadata/lib/get_metric_metadata.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/routes/metadata/lib/get_metric_metadata.ts
rename to x-pack/plugins/infra/server/routes/metadata/lib/get_metric_metadata.ts
diff --git a/x-pack/legacy/plugins/infra/server/routes/metadata/lib/get_node_info.ts b/x-pack/plugins/infra/server/routes/metadata/lib/get_node_info.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/routes/metadata/lib/get_node_info.ts
rename to x-pack/plugins/infra/server/routes/metadata/lib/get_node_info.ts
diff --git a/x-pack/legacy/plugins/infra/server/routes/metadata/lib/get_pod_node_name.ts b/x-pack/plugins/infra/server/routes/metadata/lib/get_pod_node_name.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/routes/metadata/lib/get_pod_node_name.ts
rename to x-pack/plugins/infra/server/routes/metadata/lib/get_pod_node_name.ts
diff --git a/x-pack/legacy/plugins/infra/server/routes/metadata/lib/has_apm_data.ts b/x-pack/plugins/infra/server/routes/metadata/lib/has_apm_data.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/routes/metadata/lib/has_apm_data.ts
rename to x-pack/plugins/infra/server/routes/metadata/lib/has_apm_data.ts
diff --git a/x-pack/legacy/plugins/infra/server/routes/metadata/lib/pick_feature_name.ts b/x-pack/plugins/infra/server/routes/metadata/lib/pick_feature_name.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/routes/metadata/lib/pick_feature_name.ts
rename to x-pack/plugins/infra/server/routes/metadata/lib/pick_feature_name.ts
diff --git a/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/index.ts b/x-pack/plugins/infra/server/routes/metrics_explorer/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/routes/metrics_explorer/index.ts
rename to x-pack/plugins/infra/server/routes/metrics_explorer/index.ts
diff --git a/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/lib/create_metrics_model.ts b/x-pack/plugins/infra/server/routes/metrics_explorer/lib/create_metrics_model.ts
similarity index 96%
rename from x-pack/legacy/plugins/infra/server/routes/metrics_explorer/lib/create_metrics_model.ts
rename to x-pack/plugins/infra/server/routes/metrics_explorer/lib/create_metrics_model.ts
index 7e47f50c76088..c537ba0d5163e 100644
--- a/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/lib/create_metrics_model.ts
+++ b/x-pack/plugins/infra/server/routes/metrics_explorer/lib/create_metrics_model.ts
@@ -5,7 +5,7 @@
  */
 
 import { InfraMetricModelMetricType } from '../../../lib/adapters/metrics';
-import { MetricsExplorerRequestBody } from '../types';
+import { MetricsExplorerRequestBody } from '../../../../common/http_api/metrics_explorer';
 import { TSVBMetricModel } from '../../../../common/inventory_models/types';
 export const createMetricModel = (options: MetricsExplorerRequestBody): TSVBMetricModel => {
   return {
diff --git a/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/lib/get_dataset_for_field.ts b/x-pack/plugins/infra/server/routes/metrics_explorer/lib/get_dataset_for_field.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/routes/metrics_explorer/lib/get_dataset_for_field.ts
rename to x-pack/plugins/infra/server/routes/metrics_explorer/lib/get_dataset_for_field.ts
diff --git a/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/lib/get_groupings.ts b/x-pack/plugins/infra/server/routes/metrics_explorer/lib/get_groupings.ts
similarity index 96%
rename from x-pack/legacy/plugins/infra/server/routes/metrics_explorer/lib/get_groupings.ts
rename to x-pack/plugins/infra/server/routes/metrics_explorer/lib/get_groupings.ts
index 7111d3e7f8ca4..47dc58997a469 100644
--- a/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/lib/get_groupings.ts
+++ b/x-pack/plugins/infra/server/routes/metrics_explorer/lib/get_groupings.ts
@@ -6,7 +6,10 @@
 
 import { isObject, set } from 'lodash';
 import { InfraDatabaseSearchResponse } from '../../../lib/adapters/framework';
-import { MetricsExplorerRequestBody, MetricsExplorerResponse } from '../types';
+import {
+  MetricsExplorerRequestBody,
+  MetricsExplorerResponse,
+} from '../../../../common/http_api/metrics_explorer';
 
 interface GroupingAggregation {
   groupingsCount: {
diff --git a/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/lib/populate_series_with_tsvb_data.ts b/x-pack/plugins/infra/server/routes/metrics_explorer/lib/populate_series_with_tsvb_data.ts
similarity index 98%
rename from x-pack/legacy/plugins/infra/server/routes/metrics_explorer/lib/populate_series_with_tsvb_data.ts
rename to x-pack/plugins/infra/server/routes/metrics_explorer/lib/populate_series_with_tsvb_data.ts
index 347feca67aa99..3517800ea0dd1 100644
--- a/x-pack/legacy/plugins/infra/server/routes/metrics_explorer/lib/populate_series_with_tsvb_data.ts
+++ b/x-pack/plugins/infra/server/routes/metrics_explorer/lib/populate_series_with_tsvb_data.ts
@@ -12,7 +12,7 @@ import {
   MetricsExplorerSeries,
   MetricsExplorerRequestBody,
   MetricsExplorerColumn,
-} from '../types';
+} from '../../../../common/http_api/metrics_explorer';
 import { createMetricModel } from './create_metrics_model';
 import { JsonObject } from '../../../../common/typed_json';
 import { calculateMetricInterval } from '../../../utils/calculate_metric_interval';
diff --git a/x-pack/legacy/plugins/infra/server/routes/node_details/index.ts b/x-pack/plugins/infra/server/routes/node_details/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/routes/node_details/index.ts
rename to x-pack/plugins/infra/server/routes/node_details/index.ts
diff --git a/x-pack/legacy/plugins/infra/server/routes/snapshot/index.ts b/x-pack/plugins/infra/server/routes/snapshot/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/routes/snapshot/index.ts
rename to x-pack/plugins/infra/server/routes/snapshot/index.ts
diff --git a/x-pack/legacy/plugins/infra/server/saved_objects.ts b/x-pack/plugins/infra/server/saved_objects.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/saved_objects.ts
rename to x-pack/plugins/infra/server/saved_objects.ts
diff --git a/x-pack/legacy/plugins/infra/server/usage/usage_collector.ts b/x-pack/plugins/infra/server/usage/usage_collector.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/usage/usage_collector.ts
rename to x-pack/plugins/infra/server/usage/usage_collector.ts
diff --git a/x-pack/legacy/plugins/infra/server/utils/README.md b/x-pack/plugins/infra/server/utils/README.md
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/utils/README.md
rename to x-pack/plugins/infra/server/utils/README.md
diff --git a/x-pack/legacy/plugins/infra/server/utils/calculate_metric_interval.ts b/x-pack/plugins/infra/server/utils/calculate_metric_interval.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/utils/calculate_metric_interval.ts
rename to x-pack/plugins/infra/server/utils/calculate_metric_interval.ts
diff --git a/x-pack/legacy/plugins/infra/server/utils/create_afterkey_handler.ts b/x-pack/plugins/infra/server/utils/create_afterkey_handler.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/utils/create_afterkey_handler.ts
rename to x-pack/plugins/infra/server/utils/create_afterkey_handler.ts
diff --git a/x-pack/legacy/plugins/infra/server/utils/elasticsearch_runtime_types.ts b/x-pack/plugins/infra/server/utils/elasticsearch_runtime_types.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/utils/elasticsearch_runtime_types.ts
rename to x-pack/plugins/infra/server/utils/elasticsearch_runtime_types.ts
diff --git a/x-pack/legacy/plugins/infra/server/utils/get_all_composite_data.ts b/x-pack/plugins/infra/server/utils/get_all_composite_data.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/utils/get_all_composite_data.ts
rename to x-pack/plugins/infra/server/utils/get_all_composite_data.ts
diff --git a/x-pack/legacy/plugins/infra/server/utils/get_interval_in_seconds.ts b/x-pack/plugins/infra/server/utils/get_interval_in_seconds.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/utils/get_interval_in_seconds.ts
rename to x-pack/plugins/infra/server/utils/get_interval_in_seconds.ts
diff --git a/x-pack/legacy/plugins/infra/server/utils/serialized_query.ts b/x-pack/plugins/infra/server/utils/serialized_query.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/utils/serialized_query.ts
rename to x-pack/plugins/infra/server/utils/serialized_query.ts
diff --git a/x-pack/legacy/plugins/infra/server/utils/typed_elasticsearch_mappings.ts b/x-pack/plugins/infra/server/utils/typed_elasticsearch_mappings.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/utils/typed_elasticsearch_mappings.ts
rename to x-pack/plugins/infra/server/utils/typed_elasticsearch_mappings.ts
diff --git a/x-pack/legacy/plugins/infra/server/utils/typed_resolvers.ts b/x-pack/plugins/infra/server/utils/typed_resolvers.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/server/utils/typed_resolvers.ts
rename to x-pack/plugins/infra/server/utils/typed_resolvers.ts
diff --git a/x-pack/legacy/plugins/infra/types/eui.d.ts b/x-pack/plugins/infra/types/eui.d.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/types/eui.d.ts
rename to x-pack/plugins/infra/types/eui.d.ts
diff --git a/x-pack/legacy/plugins/infra/types/eui_experimental.d.ts b/x-pack/plugins/infra/types/eui_experimental.d.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/types/eui_experimental.d.ts
rename to x-pack/plugins/infra/types/eui_experimental.d.ts
diff --git a/x-pack/legacy/plugins/infra/types/graphql_fields.d.ts b/x-pack/plugins/infra/types/graphql_fields.d.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/types/graphql_fields.d.ts
rename to x-pack/plugins/infra/types/graphql_fields.d.ts
diff --git a/x-pack/legacy/plugins/infra/types/redux_observable.d.ts b/x-pack/plugins/infra/types/redux_observable.d.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/types/redux_observable.d.ts
rename to x-pack/plugins/infra/types/redux_observable.d.ts
diff --git a/x-pack/plugins/observability/public/hooks/use_track_metric.tsx b/x-pack/plugins/observability/public/hooks/use_track_metric.tsx
new file mode 100644
index 0000000000000..b146a80e89ea8
--- /dev/null
+++ b/x-pack/plugins/observability/public/hooks/use_track_metric.tsx
@@ -0,0 +1,92 @@
+/*
+ * 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 { useEffect, useMemo } from 'react';
+import { METRIC_TYPE, UiStatsMetricType } from '@kbn/analytics';
+import { UsageCollectionSetup } from 'src/plugins/usage_collection/public';
+import { useKibana } from '../../../../../src/plugins/kibana_react/public';
+
+/**
+ * Note: The usage_collection plugin will take care of sending this data to the telemetry server.
+ * You can find these metrics stored at:
+ * stack_stats.kibana.plugins.ui_metric.{app}.{metric}(__delayed_{n}ms)?
+ * which will be an array of objects each containing a key, representing the metric, and
+ * a value, which will be a counter
+ */
+
+type ObservabilityApp = 'infra_metrics' | 'infra_logs' | 'apm' | 'uptime';
+
+interface TrackOptions {
+  app?: ObservabilityApp;
+  metricType?: UiStatsMetricType;
+  delay?: number; // in ms
+}
+type EffectDeps = unknown[];
+
+interface ServiceDeps {
+  usageCollection: UsageCollectionSetup; // TODO: This should really be start. Looking into it.
+}
+
+export type TrackMetricOptions = TrackOptions & { metric: string };
+export type UiTracker = ReturnType<typeof useUiTracker>;
+
+export { METRIC_TYPE };
+
+export function useUiTracker<Services extends ServiceDeps>({
+  app: defaultApp,
+}: { app?: ObservabilityApp } = {}) {
+  const reportUiStats = useKibana<Services>().services?.usageCollection?.reportUiStats;
+  const trackEvent = useMemo(() => {
+    return ({ app = defaultApp, metric, metricType = METRIC_TYPE.COUNT }: TrackMetricOptions) => {
+      if (reportUiStats) {
+        reportUiStats(app as string, metricType, metric);
+      }
+    };
+  }, [defaultApp, reportUiStats]);
+  return trackEvent;
+}
+
+export function useTrackMetric<Services extends ServiceDeps>(
+  { app, metric, metricType = METRIC_TYPE.COUNT, delay = 0 }: TrackMetricOptions,
+  effectDependencies: EffectDeps = []
+) {
+  const reportUiStats = useKibana<Services>().services?.usageCollection?.reportUiStats;
+
+  useEffect(() => {
+    if (!reportUiStats) {
+      // eslint-disable-next-line no-console
+      console.log(
+        'usageCollection.reportUiStats is unavailable. Ensure this is setup via <KibanaContextProvider />.'
+      );
+    } else {
+      let decoratedMetric = metric;
+      if (delay > 0) {
+        decoratedMetric += `__delayed_${delay}ms`;
+      }
+      const id = setTimeout(
+        () => reportUiStats(app as string, metricType, decoratedMetric),
+        Math.max(delay, 0)
+      );
+      return () => clearTimeout(id);
+    }
+    // the dependencies are managed externally
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, effectDependencies);
+}
+
+/**
+ * useTrackPageview is a convenience wrapper for tracking a pageview
+ * Its metrics will be found at:
+ * stack_stats.kibana.plugins.ui_metric.{app}.pageview__{path}(__delayed_{n}ms)?
+ */
+type TrackPageviewProps = TrackOptions & { path: string };
+
+export function useTrackPageview<Services extends ServiceDeps>(
+  { path, ...rest }: TrackPageviewProps,
+  effectDependencies: EffectDeps = []
+) {
+  useTrackMetric<Services>({ ...rest, metric: `pageview__${path}` }, effectDependencies);
+}
diff --git a/x-pack/plugins/observability/public/index.ts b/x-pack/plugins/observability/public/index.ts
index c822edc3f4de8..dd8bca90cff4f 100644
--- a/x-pack/plugins/observability/public/index.ts
+++ b/x-pack/plugins/observability/public/index.ts
@@ -14,3 +14,13 @@ export const plugin: PluginInitializer<ClientSetup, ClientStart> = (
 };
 
 export * from './components/action_menu';
+
+export {
+  useTrackPageview,
+  useUiTracker,
+  UiTracker,
+  TrackMetricOptions,
+  METRIC_TYPE,
+} from './hooks/use_track_metric';
+
+export * from './typings';
diff --git a/x-pack/plugins/observability/public/typings/eui_draggable/index.ts b/x-pack/plugins/observability/public/typings/eui_draggable/index.ts
new file mode 100644
index 0000000000000..322966b3c982e
--- /dev/null
+++ b/x-pack/plugins/observability/public/typings/eui_draggable/index.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { EuiDraggable, EuiDragDropContext } from '@elastic/eui';
+
+type PropsOf<T> = T extends React.ComponentType<infer ComponentProps> ? ComponentProps : never;
+type FirstArgumentOf<Func> = Func extends (arg1: infer FirstArgument, ...rest: any[]) => any
+  ? FirstArgument
+  : never;
+export type DragHandleProps = FirstArgumentOf<
+  Exclude<PropsOf<typeof EuiDraggable>['children'], React.ReactElement>
+>['dragHandleProps'];
+export type DropResult = FirstArgumentOf<FirstArgumentOf<typeof EuiDragDropContext>['onDragEnd']>;
diff --git a/x-pack/plugins/observability/public/typings/eui_styled_components.tsx b/x-pack/plugins/observability/public/typings/eui_styled_components.tsx
new file mode 100644
index 0000000000000..aab16f9d79c4b
--- /dev/null
+++ b/x-pack/plugins/observability/public/typings/eui_styled_components.tsx
@@ -0,0 +1,45 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import * as styledComponents from 'styled-components';
+import { ThemedStyledComponentsModule, ThemeProvider, ThemeProviderProps } from 'styled-components';
+
+import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
+import euiLightVars from '@elastic/eui/dist/eui_theme_light.json';
+
+export interface EuiTheme {
+  eui: typeof euiLightVars | typeof euiDarkVars;
+  darkMode: boolean;
+}
+
+const EuiThemeProvider = <
+  OuterTheme extends styledComponents.DefaultTheme = styledComponents.DefaultTheme
+>({
+  darkMode = false,
+  ...otherProps
+}: Omit<ThemeProviderProps<OuterTheme, OuterTheme & EuiTheme>, 'theme'> & {
+  darkMode?: boolean;
+}) => (
+  <ThemeProvider
+    {...otherProps}
+    theme={(outerTheme?: OuterTheme) => ({
+      ...outerTheme,
+      eui: darkMode ? euiDarkVars : euiLightVars,
+      darkMode,
+    })}
+  />
+);
+
+const {
+  default: euiStyled,
+  css,
+  createGlobalStyle,
+  keyframes,
+  withTheme,
+} = (styledComponents as unknown) as ThemedStyledComponentsModule<EuiTheme>;
+
+export { css, euiStyled, EuiThemeProvider, createGlobalStyle, keyframes, withTheme };
diff --git a/x-pack/plugins/observability/public/typings/index.ts b/x-pack/plugins/observability/public/typings/index.ts
new file mode 100644
index 0000000000000..3da2febc73efd
--- /dev/null
+++ b/x-pack/plugins/observability/public/typings/index.ts
@@ -0,0 +1,8 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export * from './eui_draggable';
+export * from './eui_styled_components';
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index a59a6dbf12566..bb488c0a7b1fb 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -6385,10 +6385,8 @@
     "xpack.infra.homePage.noMetricsIndicesTitle": "メトリックインデックスがないようです。",
     "xpack.infra.homePage.settingsTabTitle": "設定",
     "xpack.infra.homePage.toolbar.kqlSearchFieldPlaceholder": "インフラストラクチャーデータを検索… (例: host.name:host-1)",
-    "xpack.infra.infrastructureDescription": "インフラストラクチャーを閲覧します",
     "xpack.infra.infrastructureMetricsExplorerPage.documentTitle": "{previousTitle} | メトリックエクスプローラー",
     "xpack.infra.infrastructureSnapshotPage.documentTitle": "{previousTitle} | インベントリ",
-    "xpack.infra.infrastructureTitle": "メトリック",
     "xpack.infra.kibanaMetrics.cloudIdMissingErrorMessage": "{metricId} のモデルには cloudId が必要ですが、{nodeId} に cloudId が指定されていません。",
     "xpack.infra.kibanaMetrics.invalidInfraMetricErrorMessage": "{id} は有効な InfraMetric ではありません",
     "xpack.infra.kibanaMetrics.nodeDoesNotExistErrorMessage": "{nodeId} が存在しません。",
@@ -6398,10 +6396,6 @@
     "xpack.infra.legendControls.maxLabel": "最高",
     "xpack.infra.legendControls.minLabel": "最低",
     "xpack.infra.legendControls.switchLabel": "自動計算範囲",
-    "xpack.infra.linkInfrastructureDescription": "インフラストラクチャーを閲覧します",
-    "xpack.infra.linkInfrastructureTitle": "メトリック",
-    "xpack.infra.linkLogsDescription": "ログを閲覧します",
-    "xpack.infra.linkLogsTitle": "ログ",
     "xpack.infra.linkTo.hostWithIp.error": "IP アドレス「{hostIp}」でホストが見つかりません.",
     "xpack.infra.linkTo.hostWithIp.loading": "IP アドレス「{hostIp}」のホストを読み込み中",
     "xpack.infra.logEntryActionsMenu.apmActionLabel": "APMで表示",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index dbc9cab4261d8..402ddd8288830 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -6384,10 +6384,8 @@
     "xpack.infra.homePage.noMetricsIndicesTitle": "似乎您没有任何指标索引。",
     "xpack.infra.homePage.settingsTabTitle": "设置",
     "xpack.infra.homePage.toolbar.kqlSearchFieldPlaceholder": "搜索基础设施数据……(例如 host.name:host-1)",
-    "xpack.infra.infrastructureDescription": "浏览您的基础设施",
     "xpack.infra.infrastructureMetricsExplorerPage.documentTitle": "{previousTitle} | 指标浏览器",
     "xpack.infra.infrastructureSnapshotPage.documentTitle": "{previousTitle} | 库存",
-    "xpack.infra.infrastructureTitle": "指标",
     "xpack.infra.kibanaMetrics.cloudIdMissingErrorMessage": "{metricId} 的模型需要云 ID,但没有为 {nodeId} 提供。",
     "xpack.infra.kibanaMetrics.invalidInfraMetricErrorMessage": "{id} 不是有效的 InfraMetric",
     "xpack.infra.kibanaMetrics.nodeDoesNotExistErrorMessage": "{nodeId} 不存在。",
@@ -6397,10 +6395,6 @@
     "xpack.infra.legendControls.maxLabel": "最大值",
     "xpack.infra.legendControls.minLabel": "最小值",
     "xpack.infra.legendControls.switchLabel": "自动计算范围",
-    "xpack.infra.linkInfrastructureDescription": "浏览您的基础设施",
-    "xpack.infra.linkInfrastructureTitle": "指标",
-    "xpack.infra.linkLogsDescription": "浏览您的日志",
-    "xpack.infra.linkLogsTitle": "鏃ュ織",
     "xpack.infra.linkTo.hostWithIp.error": "未找到 IP 地址为“{hostIp}”的主机。",
     "xpack.infra.linkTo.hostWithIp.loading": "正在加载 IP 地址为“{hostIp}”的主机。",
     "xpack.infra.logEntryActionsMenu.apmActionLabel": "在 APM 中查看",
diff --git a/x-pack/test/api_integration/apis/infra/ip_to_hostname.ts b/x-pack/test/api_integration/apis/infra/ip_to_hostname.ts
index 5d1a519efb5b9..3821e4b5aab0e 100644
--- a/x-pack/test/api_integration/apis/infra/ip_to_hostname.ts
+++ b/x-pack/test/api_integration/apis/infra/ip_to_hostname.ts
@@ -6,7 +6,6 @@
 
 import expect from '@kbn/expect';
 import { FtrProviderContext } from '../../ftr_provider_context';
-import { IpToHostResponse } from '../../../../legacy/plugins/infra/server/routes/ip_to_hostname';
 
 export default function ipToHostNameTest({ getService }: FtrProviderContext) {
   const supertest = getService('supertest');
@@ -27,8 +26,7 @@ export default function ipToHostNameTest({ getService }: FtrProviderContext) {
         .send(postBody)
         .expect(200);
 
-      const body: IpToHostResponse = response.body;
-      expect(body).to.have.property('host', 'demo-stack-mysql-01');
+      expect(response.body).to.have.property('host', 'demo-stack-mysql-01');
     });
 
     it('should return 404 for invalid ip', async () => {
diff --git a/x-pack/test/api_integration/apis/infra/log_analysis.ts b/x-pack/test/api_integration/apis/infra/log_analysis.ts
index 2e57678d1d4c2..2f27b5d5b7021 100644
--- a/x-pack/test/api_integration/apis/infra/log_analysis.ts
+++ b/x-pack/test/api_integration/apis/infra/log_analysis.ts
@@ -13,11 +13,8 @@ import {
   LOG_ANALYSIS_GET_LOG_ENTRY_RATE_PATH,
   getLogEntryRateRequestPayloadRT,
   getLogEntryRateSuccessReponsePayloadRT,
-} from '../../../../legacy/plugins/infra/common/http_api/log_analysis';
-import {
-  createPlainError,
-  throwErrors,
-} from '../../../../legacy/plugins/infra/common/runtime_types';
+} from '../../../../plugins/infra/common/http_api/log_analysis';
+import { createPlainError, throwErrors } from '../../../../plugins/infra/common/runtime_types';
 import { FtrProviderContext } from '../../ftr_provider_context';
 
 const TIME_BEFORE_START = 1569934800000;
diff --git a/x-pack/test/api_integration/apis/infra/log_entries.ts b/x-pack/test/api_integration/apis/infra/log_entries.ts
index 8db1426a219d4..75e7750058a87 100644
--- a/x-pack/test/api_integration/apis/infra/log_entries.ts
+++ b/x-pack/test/api_integration/apis/infra/log_entries.ts
@@ -13,19 +13,16 @@ import { pipe } from 'fp-ts/lib/pipeable';
 import { identity } from 'fp-ts/lib/function';
 import { fold } from 'fp-ts/lib/Either';
 
-import {
-  createPlainError,
-  throwErrors,
-} from '../../../../legacy/plugins/infra/common/runtime_types';
+import { createPlainError, throwErrors } from '../../../../plugins/infra/common/runtime_types';
 
 import {
   LOG_ENTRIES_PATH,
   logEntriesRequestRT,
   logEntriesResponseRT,
-} from '../../../../legacy/plugins/infra/common/http_api';
+} from '../../../../plugins/infra/common/http_api';
 
-import { sharedFragments } from '../../../../legacy/plugins/infra/common/graphql/shared';
-import { InfraTimeKey } from '../../../../legacy/plugins/infra/public/graphql/types';
+import { sharedFragments } from '../../../../plugins/infra/common/graphql/shared';
+import { InfraTimeKey } from '../../../../plugins/infra/public/graphql/types';
 import { FtrProviderContext } from '../../ftr_provider_context';
 
 const KEY_WITHIN_DATA_RANGE = {
diff --git a/x-pack/test/api_integration/apis/infra/log_entry_highlights.ts b/x-pack/test/api_integration/apis/infra/log_entry_highlights.ts
index 66701de2704a6..a34cd89eb3262 100644
--- a/x-pack/test/api_integration/apis/infra/log_entry_highlights.ts
+++ b/x-pack/test/api_integration/apis/infra/log_entry_highlights.ts
@@ -12,20 +12,17 @@ import { pipe } from 'fp-ts/lib/pipeable';
 import { identity } from 'fp-ts/lib/function';
 import { fold } from 'fp-ts/lib/Either';
 
-import {
-  createPlainError,
-  throwErrors,
-} from '../../../../legacy/plugins/infra/common/runtime_types';
+import { createPlainError, throwErrors } from '../../../../plugins/infra/common/runtime_types';
 
 import {
   LOG_ENTRIES_HIGHLIGHTS_PATH,
   logEntriesHighlightsRequestRT,
   logEntriesHighlightsResponseRT,
-} from '../../../../legacy/plugins/infra/common/http_api';
+} from '../../../../plugins/infra/common/http_api';
 
 import { FtrProviderContext } from '../../ftr_provider_context';
-import { sharedFragments } from '../../../../legacy/plugins/infra/common/graphql/shared';
-import { InfraTimeKey } from '../../../../legacy/plugins/infra/public/graphql/types';
+import { sharedFragments } from '../../../../plugins/infra/common/graphql/shared';
+import { InfraTimeKey } from '../../../../plugins/infra/public/graphql/types';
 
 const KEY_BEFORE_START = {
   time: new Date('2000-01-01T00:00:00.000Z').valueOf(),
diff --git a/x-pack/test/api_integration/apis/infra/log_item.ts b/x-pack/test/api_integration/apis/infra/log_item.ts
index bae2d0ebb891b..5a8e88c0aad62 100644
--- a/x-pack/test/api_integration/apis/infra/log_item.ts
+++ b/x-pack/test/api_integration/apis/infra/log_item.ts
@@ -10,7 +10,7 @@ import { FtrProviderContext } from '../../ftr_provider_context';
 import {
   LOG_ENTRIES_ITEM_PATH,
   logEntriesItemRequestRT,
-} from '../../../../legacy/plugins/infra/common/http_api';
+} from '../../../../plugins/infra/common/http_api';
 
 const COMMON_HEADERS = {
   'kbn-xsrf': 'some-xsrf-token',
diff --git a/x-pack/test/api_integration/apis/infra/log_summary.ts b/x-pack/test/api_integration/apis/infra/log_summary.ts
index 017b1c6f7ab45..15e503f7b4a5a 100644
--- a/x-pack/test/api_integration/apis/infra/log_summary.ts
+++ b/x-pack/test/api_integration/apis/infra/log_summary.ts
@@ -12,16 +12,13 @@ import { pipe } from 'fp-ts/lib/pipeable';
 import { identity } from 'fp-ts/lib/function';
 import { fold } from 'fp-ts/lib/Either';
 
-import {
-  createPlainError,
-  throwErrors,
-} from '../../../../legacy/plugins/infra/common/runtime_types';
+import { createPlainError, throwErrors } from '../../../../plugins/infra/common/runtime_types';
 
 import {
   LOG_ENTRIES_SUMMARY_PATH,
   logEntriesSummaryRequestRT,
   logEntriesSummaryResponseRT,
-} from '../../../../legacy/plugins/infra/common/http_api/log_entries';
+} from '../../../../plugins/infra/common/http_api/log_entries';
 
 import { FtrProviderContext } from '../../ftr_provider_context';
 
diff --git a/x-pack/test/api_integration/apis/infra/logs_without_millis.ts b/x-pack/test/api_integration/apis/infra/logs_without_millis.ts
index ce2319259985b..9295380cfbec1 100644
--- a/x-pack/test/api_integration/apis/infra/logs_without_millis.ts
+++ b/x-pack/test/api_integration/apis/infra/logs_without_millis.ts
@@ -12,19 +12,16 @@ import { pipe } from 'fp-ts/lib/pipeable';
 import { identity } from 'fp-ts/lib/function';
 import { fold } from 'fp-ts/lib/Either';
 
-import {
-  createPlainError,
-  throwErrors,
-} from '../../../../legacy/plugins/infra/common/runtime_types';
+import { createPlainError, throwErrors } from '../../../../plugins/infra/common/runtime_types';
 
 import { FtrProviderContext } from '../../ftr_provider_context';
-import { sharedFragments } from '../../../../legacy/plugins/infra/common/graphql/shared';
-import { InfraTimeKey } from '../../../../legacy/plugins/infra/public/graphql/types';
+import { sharedFragments } from '../../../../plugins/infra/common/graphql/shared';
+import { InfraTimeKey } from '../../../../plugins/infra/public/graphql/types';
 import {
   LOG_ENTRIES_SUMMARY_PATH,
   logEntriesSummaryRequestRT,
   logEntriesSummaryResponseRT,
-} from '../../../../legacy/plugins/infra/common/http_api/log_entries';
+} from '../../../../plugins/infra/common/http_api/log_entries';
 
 const COMMON_HEADERS = {
   'kbn-xsrf': 'some-xsrf-token',
diff --git a/x-pack/test/api_integration/apis/infra/metadata.ts b/x-pack/test/api_integration/apis/infra/metadata.ts
index ac3fa06d958dd..b693881abcdf7 100644
--- a/x-pack/test/api_integration/apis/infra/metadata.ts
+++ b/x-pack/test/api_integration/apis/infra/metadata.ts
@@ -5,11 +5,11 @@
  */
 
 import expect from '@kbn/expect';
-import { InfraNodeType } from '../../../../legacy/plugins/infra/server/graphql/types';
+import { InfraNodeType } from '../../../../plugins/infra/server/graphql/types';
 import {
   InfraMetadata,
   InfraMetadataRequest,
-} from '../../../../legacy/plugins/infra/common/http_api/metadata_api';
+} from '../../../../plugins/infra/common/http_api/metadata_api';
 import { FtrProviderContext } from '../../ftr_provider_context';
 
 export default function({ getService }: FtrProviderContext) {
diff --git a/x-pack/test/api_integration/apis/infra/metrics.ts b/x-pack/test/api_integration/apis/infra/metrics.ts
index 432b95163b4c1..f373bb788930d 100644
--- a/x-pack/test/api_integration/apis/infra/metrics.ts
+++ b/x-pack/test/api_integration/apis/infra/metrics.ts
@@ -7,16 +7,13 @@
 import expect from '@kbn/expect';
 import { first, last } from 'lodash';
 
-import { InventoryMetric } from '../../../../legacy/plugins/infra/common/inventory_models/types';
-import {
-  InfraNodeType,
-  InfraTimerangeInput,
-} from '../../../../legacy/plugins/infra/public/graphql/types';
+import { InventoryMetric } from '../../../../plugins/infra/common/inventory_models/types';
+import { InfraNodeType, InfraTimerangeInput } from '../../../../plugins/infra/public/graphql/types';
 import { FtrProviderContext } from '../../ftr_provider_context';
 
 import { DATES } from './constants';
 
-import { NodeDetailsMetricDataResponse } from '../../../../legacy/plugins/infra/common/http_api/node_details_api';
+import { NodeDetailsMetricDataResponse } from '../../../../plugins/infra/common/http_api/node_details_api';
 const { min, max } = DATES['7.0.0'].hosts;
 
 interface NodeDetailsRequest {
diff --git a/x-pack/test/api_integration/apis/infra/metrics_explorer.ts b/x-pack/test/api_integration/apis/infra/metrics_explorer.ts
index ee81b665adbe6..1563ae208867c 100644
--- a/x-pack/test/api_integration/apis/infra/metrics_explorer.ts
+++ b/x-pack/test/api_integration/apis/infra/metrics_explorer.ts
@@ -8,8 +8,9 @@ import expect from '@kbn/expect';
 import { first } from 'lodash';
 import moment from 'moment';
 import { DATES } from './constants';
-import { MetricsExplorerResponse } from '../../../../legacy/plugins/infra/server/routes/metrics_explorer/types';
 import { FtrProviderContext } from '../../ftr_provider_context';
+import { metricsExplorerResponseRT } from '../../../../plugins/infra/common/http_api/metrics_explorer';
+import { decodeOrThrow } from '../../../../plugins/infra/common/runtime_types';
 
 const { min, max } = DATES['7.0.0'].hosts;
 
@@ -48,20 +49,17 @@ export default function({ getService }: FtrProviderContext) {
           .set('kbn-xsrf', 'xxx')
           .send(postBody)
           .expect(200);
-        const body: MetricsExplorerResponse = response.body;
-        expect(body).to.have.property('series');
+        const body = decodeOrThrow(metricsExplorerResponseRT)(response.body);
         expect(body.series).length(1);
         const firstSeries = first(body.series);
         expect(firstSeries).to.have.property('id', 'ALL');
-        expect(firstSeries).to.have.property('columns');
-        expect(firstSeries).to.have.property('rows');
-        expect(firstSeries!.columns).to.eql([
+        expect(firstSeries.columns).to.eql([
           { name: 'timestamp', type: 'date' },
           { name: 'metric_0', type: 'number' },
           { name: 'metric_1', type: 'number' },
         ]);
-        expect(firstSeries!.rows).to.have.length(9);
-        expect(firstSeries!.rows![1]).to.eql({
+        expect(firstSeries.rows).to.have.length(9);
+        expect(firstSeries.rows![1]).to.eql({
           metric_0: 0.005333333333333333,
           metric_1: 131,
           timestamp: 1547571300000,
@@ -92,19 +90,16 @@ export default function({ getService }: FtrProviderContext) {
           .set('kbn-xsrf', 'xxx')
           .send(postBody)
           .expect(200);
-        const body: MetricsExplorerResponse = response.body;
-        expect(body).to.have.property('series');
+        const body = decodeOrThrow(metricsExplorerResponseRT)(response.body);
         expect(body.series).length(1);
         const firstSeries = first(body.series);
         expect(firstSeries).to.have.property('id', 'ALL');
-        expect(firstSeries).to.have.property('columns');
-        expect(firstSeries).to.have.property('rows');
-        expect(firstSeries!.columns).to.eql([
+        expect(firstSeries.columns).to.eql([
           { name: 'timestamp', type: 'date' },
           { name: 'metric_0', type: 'number' },
         ]);
-        expect(firstSeries!.rows).to.have.length(9);
-        expect(firstSeries!.rows![1]).to.eql({
+        expect(firstSeries.rows).to.have.length(9);
+        expect(firstSeries.rows![1]).to.eql({
           metric_0: 0.024,
           timestamp: 1547571300000,
         });
@@ -126,15 +121,12 @@ export default function({ getService }: FtrProviderContext) {
           .set('kbn-xsrf', 'xxx')
           .send(postBody)
           .expect(200);
-        const body: MetricsExplorerResponse = response.body;
-        expect(body).to.have.property('series');
+        const body = decodeOrThrow(metricsExplorerResponseRT)(response.body);
         expect(body.series).length(1);
         const firstSeries = first(body.series);
         expect(firstSeries).to.have.property('id', 'ALL');
-        expect(firstSeries).to.have.property('columns');
-        expect(firstSeries).to.have.property('rows');
-        expect(firstSeries!.columns).to.eql([]);
-        expect(firstSeries!.rows).to.have.length(0);
+        expect(firstSeries.columns).to.eql([]);
+        expect(firstSeries.rows).to.have.length(0);
       });
 
       it('should work with groupBy', async () => {
@@ -161,25 +153,21 @@ export default function({ getService }: FtrProviderContext) {
           .set('kbn-xsrf', 'xxx')
           .send(postBody)
           .expect(200);
-        const body: MetricsExplorerResponse = response.body;
-        expect(body).to.have.property('series');
+        const body = decodeOrThrow(metricsExplorerResponseRT)(response.body);
         expect(body.series).length(3);
         const firstSeries = first(body.series);
         expect(firstSeries).to.have.property('id', 'system.diskio');
-        expect(firstSeries).to.have.property('columns');
-        expect(firstSeries).to.have.property('rows');
-        expect(firstSeries!.columns).to.eql([
+        expect(firstSeries.columns).to.eql([
           { name: 'timestamp', type: 'date' },
           { name: 'metric_0', type: 'number' },
           { name: 'groupBy', type: 'string' },
         ]);
-        expect(firstSeries!.rows).to.have.length(9);
-        expect(firstSeries!.rows![1]).to.eql({
+        expect(firstSeries.rows).to.have.length(9);
+        expect(firstSeries.rows![1]).to.eql({
           groupBy: 'system.diskio',
           metric_0: 24,
           timestamp: 1547571300000,
         });
-        expect(body).to.have.property('pageInfo');
         expect(body.pageInfo).to.eql({
           afterKey: 'system.fsstat',
           total: 12,
@@ -212,12 +200,10 @@ export default function({ getService }: FtrProviderContext) {
           .set('kbn-xsrf', 'xxx')
           .send(postBody)
           .expect(200);
-        const body: MetricsExplorerResponse = response.body;
-        expect(body).to.have.property('series');
+        const body = decodeOrThrow(metricsExplorerResponseRT)(response.body);
         expect(body.series).length(1);
         expect(body.series[0]!).to.have.property('rows');
         expect(body.series[0]!.rows).length(0);
-        expect(body).to.have.property('pageInfo');
         expect(body.pageInfo).to.eql({
           afterKey: null,
           total: 0,
@@ -248,10 +234,8 @@ export default function({ getService }: FtrProviderContext) {
           .set('kbn-xsrf', 'xxx')
           .send(postBody)
           .expect(200);
-        const body: MetricsExplorerResponse = response.body;
-        expect(body).to.have.property('series');
+        const body = decodeOrThrow(metricsExplorerResponseRT)(response.body);
         expect(body.series).length(0);
-        expect(body).to.have.property('pageInfo');
         expect(body.pageInfo).to.eql({
           afterKey: null,
           total: 0,
diff --git a/x-pack/test/api_integration/apis/infra/sources.ts b/x-pack/test/api_integration/apis/infra/sources.ts
index 8e51610d208be..fb8ab1d0592a0 100644
--- a/x-pack/test/api_integration/apis/infra/sources.ts
+++ b/x-pack/test/api_integration/apis/infra/sources.ts
@@ -7,14 +7,14 @@
 import expect from '@kbn/expect';
 import gql from 'graphql-tag';
 
-import { sourceQuery } from '../../../../legacy/plugins/infra/public/containers/source/query_source.gql_query';
+import { sourceQuery } from '../../../../plugins/infra/public/containers/source/query_source.gql_query';
 import {
   sourceConfigurationFieldsFragment,
   sourceStatusFieldsFragment,
-} from '../../../../legacy/plugins/infra/public/containers/source/source_fields_fragment.gql_query';
-import { SourceQuery } from '../../../../legacy/plugins/infra/public/graphql/types';
+} from '../../../../plugins/infra/public/containers/source/source_fields_fragment.gql_query';
+import { SourceQuery } from '../../../../plugins/infra/public/graphql/types';
 import { FtrProviderContext } from '../../ftr_provider_context';
-import { sharedFragments } from '../../../../legacy/plugins/infra/common/graphql/shared';
+import { sharedFragments } from '../../../../plugins/infra/common/graphql/shared';
 
 export default function({ getService }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
diff --git a/x-pack/test/api_integration/apis/infra/waffle.ts b/x-pack/test/api_integration/apis/infra/waffle.ts
index 1f79ad4eee4e5..80fea1cdcd295 100644
--- a/x-pack/test/api_integration/apis/infra/waffle.ts
+++ b/x-pack/test/api_integration/apis/infra/waffle.ts
@@ -12,9 +12,9 @@ import {
   InfraNodeType,
   InfraTimerangeInput,
   InfraSnapshotGroupbyInput,
-} from '../../../../legacy/plugins/infra/server/graphql/types';
+} from '../../../../plugins/infra/server/graphql/types';
 import { FtrProviderContext } from '../../ftr_provider_context';
-import { SnapshotNodeResponse } from '../../../../legacy/plugins/infra/common/http_api/snapshot_api';
+import { SnapshotNodeResponse } from '../../../../plugins/infra/common/http_api/snapshot_api';
 import { DATES } from './constants';
 
 interface SnapshotRequest {
diff --git a/x-pack/test/api_integration/services/infraops_graphql_client.ts b/x-pack/test/api_integration/services/infraops_graphql_client.ts
index 33d1a59575c17..7236bbd82f3f0 100644
--- a/x-pack/test/api_integration/services/infraops_graphql_client.ts
+++ b/x-pack/test/api_integration/services/infraops_graphql_client.ts
@@ -12,7 +12,7 @@ import { HttpLink } from 'apollo-link-http';
 
 import { FtrProviderContext } from '../ftr_provider_context';
 
-import introspectionQueryResultData from '../../../legacy/plugins/infra/public/graphql/introspection.json';
+import introspectionQueryResultData from '../../../plugins/infra/public/graphql/introspection.json';
 
 export function InfraOpsGraphQLClientProvider(context: FtrProviderContext) {
   return InfraOpsGraphQLClientFactoryProvider(context)();
diff --git a/x-pack/test/api_integration/services/infraops_source_configuration.ts b/x-pack/test/api_integration/services/infraops_source_configuration.ts
index facfe485ab88a..728a58829d1ce 100644
--- a/x-pack/test/api_integration/services/infraops_source_configuration.ts
+++ b/x-pack/test/api_integration/services/infraops_source_configuration.ts
@@ -7,10 +7,7 @@
 import gql from 'graphql-tag';
 
 import { FtrProviderContext } from '../ftr_provider_context';
-import {
-  UpdateSourceInput,
-  UpdateSourceResult,
-} from '../../../legacy/plugins/infra/public/graphql/types';
+import { UpdateSourceInput, UpdateSourceResult } from '../../../plugins/infra/public/graphql/types';
 
 const createSourceMutation = gql`
   mutation createSource($sourceId: ID!, $sourceProperties: UpdateSourceInput!) {
diff --git a/x-pack/test/functional/apps/endpoint/feature_controls/endpoint_spaces.ts b/x-pack/test/functional/apps/endpoint/feature_controls/endpoint_spaces.ts
index bda336e73c4f8..287892903dd2b 100644
--- a/x-pack/test/functional/apps/endpoint/feature_controls/endpoint_spaces.ts
+++ b/x-pack/test/functional/apps/endpoint/feature_controls/endpoint_spaces.ts
@@ -42,11 +42,16 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
       });
 
       it(`endpoint management shows 'Manage Endpoints'`, async () => {
-        await pageObjects.common.navigateToUrlWithBrowserHistory('endpoint', '/management', {
-          basePath: '/s/custom_space',
-          ensureCurrentUrl: false,
-          shouldLoginIfPrompted: false,
-        });
+        await pageObjects.common.navigateToUrlWithBrowserHistory(
+          'endpoint',
+          '/management',
+          undefined,
+          {
+            basePath: '/s/custom_space',
+            ensureCurrentUrl: false,
+            shouldLoginIfPrompted: false,
+          }
+        );
         await testSubjects.existOrFail('managementViewTitle');
       });
     });
diff --git a/x-pack/test/functional/apps/infra/feature_controls/infrastructure_security.ts b/x-pack/test/functional/apps/infra/feature_controls/infrastructure_security.ts
index ac7bd66d3466f..ede77b7d9afa7 100644
--- a/x-pack/test/functional/apps/infra/feature_controls/infrastructure_security.ts
+++ b/x-pack/test/functional/apps/infra/feature_controls/infrastructure_security.ts
@@ -66,7 +66,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       describe('infrastructure landing page without data', () => {
         it(`shows 'Change source configuration' button`, async () => {
-          await PageObjects.common.navigateToActualUrl('infraOps', 'home', {
+          await PageObjects.common.navigateToUrlWithBrowserHistory('infraOps', '', undefined, {
             ensureCurrentUrl: true,
             shouldLoginIfPrompted: false,
           });
@@ -89,7 +89,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         });
 
         it(`shows Wafflemap`, async () => {
-          await PageObjects.common.navigateToActualUrl('infraOps', 'home', {
+          await PageObjects.common.navigateToUrlWithBrowserHistory('infraOps', '', undefined, {
             ensureCurrentUrl: true,
             shouldLoginIfPrompted: false,
           });
@@ -121,9 +121,10 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
       });
 
       it(`metrics page is visible`, async () => {
-        await PageObjects.common.navigateToActualUrl(
+        await PageObjects.common.navigateToUrlWithBrowserHistory(
           'infraOps',
-          '/infrastructure/metrics/host/demo-stack-redis-01',
+          '/detail/host/demo-stack-redis-01',
+          undefined,
           {
             ensureCurrentUrl: false,
             shouldLoginIfPrompted: false,
@@ -181,7 +182,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       describe('infrastructure landing page without data', () => {
         it(`doesn't show 'Change source configuration' button`, async () => {
-          await PageObjects.common.navigateToActualUrl('infraOps', 'home', {
+          await PageObjects.common.navigateToUrlWithBrowserHistory('infraOps', '', undefined, {
             ensureCurrentUrl: true,
             shouldLoginIfPrompted: false,
           });
@@ -204,7 +205,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         });
 
         it(`shows Wafflemap`, async () => {
-          await PageObjects.common.navigateToActualUrl('infraOps', 'home', {
+          await PageObjects.common.navigateToUrlWithBrowserHistory('infraOps', '', undefined, {
             ensureCurrentUrl: true,
             shouldLoginIfPrompted: false,
           });
@@ -236,9 +237,10 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
       });
 
       it(`metrics page is visible`, async () => {
-        await PageObjects.common.navigateToActualUrl(
+        await PageObjects.common.navigateToUrlWithBrowserHistory(
           'infraOps',
-          '/infrastructure/metrics/host/demo-stack-redis-01',
+          '/detail/host/demo-stack-redis-01',
+          undefined,
           {
             ensureCurrentUrl: false,
             shouldLoginIfPrompted: false,
@@ -300,7 +302,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         });
 
         it(`context menu allows user to view logs`, async () => {
-          await PageObjects.common.navigateToActualUrl('infraOps', 'home', {
+          await PageObjects.common.navigateToUrlWithBrowserHistory('infraOps', '', undefined, {
             ensureCurrentUrl: true,
             shouldLoginIfPrompted: false,
           });
@@ -366,7 +368,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         });
 
         it(`context menu allows user to view APM traces`, async () => {
-          await PageObjects.common.navigateToActualUrl('infraOps', 'home', {
+          await PageObjects.common.navigateToUrlWithBrowserHistory('infraOps', '', undefined, {
             ensureCurrentUrl: true,
             shouldLoginIfPrompted: false,
           });
@@ -421,48 +423,19 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         expect(navLinks).to.not.contain(['Metrics']);
       });
 
-      it(`infrastructure root renders not found page`, async () => {
-        await PageObjects.common.navigateToActualUrl('infraOps', '', {
-          ensureCurrentUrl: false,
-          shouldLoginIfPrompted: false,
-        });
-        await testSubjects.existOrFail('~infraNotFoundPage');
-      });
-
-      it(`infrastructure home page renders not found page`, async () => {
-        await PageObjects.common.navigateToActualUrl('infraOps', 'home', {
-          ensureCurrentUrl: false,
-          shouldLoginIfPrompted: false,
-        });
-        await testSubjects.existOrFail('~infraNotFoundPage');
-      });
-
-      it(`infrastructure landing page renders not found page`, async () => {
-        await PageObjects.common.navigateToActualUrl('infraOps', 'infrastructure', {
-          ensureCurrentUrl: false,
-          shouldLoginIfPrompted: false,
-        });
-        await testSubjects.existOrFail('~infraNotFoundPage');
-      });
-
-      it(`infrastructure snapshot page renders not found page`, async () => {
-        await PageObjects.common.navigateToActualUrl('infraOps', 'infrastructure/inventory', {
-          ensureCurrentUrl: false,
-          shouldLoginIfPrompted: false,
-        });
-        await testSubjects.existOrFail('~infraNotFoundPage');
-      });
-
-      it(`metrics page renders not found page`, async () => {
-        await PageObjects.common.navigateToActualUrl(
+      it(`metrics app is inaccessible and Application Not Found message is rendered`, async () => {
+        await PageObjects.common.navigateToApp('infraOps');
+        await testSubjects.existOrFail('~appNotFoundPageContent');
+        await PageObjects.common.navigateToUrlWithBrowserHistory(
           'infraOps',
-          '/metrics/host/demo-stack-redis-01',
+          '/inventory',
+          undefined,
           {
             ensureCurrentUrl: false,
             shouldLoginIfPrompted: false,
           }
         );
-        await testSubjects.existOrFail('~infraNotFoundPage');
+        await testSubjects.existOrFail('~appNotFoundPageContent');
       });
     });
   });
diff --git a/x-pack/test/functional/apps/infra/feature_controls/infrastructure_spaces.ts b/x-pack/test/functional/apps/infra/feature_controls/infrastructure_spaces.ts
index 1d7ef9bea81e6..3bbcc1aa7043c 100644
--- a/x-pack/test/functional/apps/infra/feature_controls/infrastructure_spaces.ts
+++ b/x-pack/test/functional/apps/infra/feature_controls/infrastructure_spaces.ts
@@ -5,9 +5,6 @@
  */
 import expect from '@kbn/expect';
 import { FtrProviderContext } from '../../../ftr_provider_context';
-import { DATES } from '../constants';
-
-const DATE_WITH_DATA = DATES.metricsAndLogs.hosts.withData;
 
 export default function({ getPageObjects, getService }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
@@ -21,22 +18,20 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
   ]);
   const testSubjects = getService('testSubjects');
   const appsMenu = getService('appsMenu');
-  const retry = getService('retry');
 
   describe('infrastructure spaces', () => {
     before(async () => {
-      await esArchiver.load('infra/metrics_and_logs');
+      await esArchiver.load('empty_kibana');
     });
 
     after(async () => {
-      await esArchiver.unload('infra/metrics_and_logs');
+      await esArchiver.unload('empty_kibana');
     });
 
     describe('space with no features disabled', () => {
       before(async () => {
         // we need to load the following in every situation as deleting
         // a space deletes all of the associated saved objects
-        await esArchiver.load('empty_kibana');
         await spacesService.create({
           id: 'custom_space',
           name: 'custom_space',
@@ -46,7 +41,6 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       after(async () => {
         await spacesService.delete('custom_space');
-        await esArchiver.unload('empty_kibana');
       });
 
       it('shows Metrics navlink', async () => {
@@ -58,29 +52,11 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         expect(navLinks).to.contain('Metrics');
       });
 
-      it(`landing page shows Wafflemap`, async () => {
-        await PageObjects.common.navigateToActualUrl('infraOps', 'home', {
+      it(`Metrics app is accessible`, async () => {
+        await PageObjects.common.navigateToApp('infraOps', {
           basePath: '/s/custom_space',
-          ensureCurrentUrl: true,
-        });
-        await PageObjects.infraHome.goToTime(DATE_WITH_DATA);
-        await testSubjects.existOrFail('~waffleMap');
-      });
-
-      describe('context menu', () => {
-        before(async () => {
-          await testSubjects.click('~nodeContainer');
-        });
-
-        it(`shows link to view logs`, async () => {
-          await retry.waitFor('context menu', () => testSubjects.exists('~nodeContextMenu'));
-          await testSubjects.existOrFail('~viewLogsContextMenuItem');
-        });
-
-        it(`shows link to view apm traces`, async () => {
-          await retry.waitFor('context menu', () => testSubjects.exists('~nodeContextMenu'));
-          await testSubjects.existOrFail('~viewApmTracesContextMenuItem');
         });
+        await testSubjects.existOrFail('~noMetricsIndicesPrompt');
       });
     });
 
@@ -109,52 +85,22 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         expect(navLinks).not.to.contain('Metrics');
       });
 
-      it(`infrastructure root renders not found page`, async () => {
-        await PageObjects.common.navigateToActualUrl('infraOps', '', {
-          basePath: '/s/custom_space',
-          ensureCurrentUrl: false,
-          shouldLoginIfPrompted: false,
-        });
-        await testSubjects.existOrFail('~infraNotFoundPage');
-      });
-
-      it(`infrastructure home page renders not found page`, async () => {
-        await PageObjects.common.navigateToActualUrl('infraOps', 'home', {
-          basePath: '/s/custom_space',
-          ensureCurrentUrl: false,
-          shouldLoginIfPrompted: false,
-        });
-        await testSubjects.existOrFail('~infraNotFoundPage');
-      });
-
-      it(`infrastructure landing page renders not found page`, async () => {
-        await PageObjects.common.navigateToActualUrl('infraOps', 'infrastructure', {
-          basePath: '/s/custom_space',
-          ensureCurrentUrl: false,
-          shouldLoginIfPrompted: false,
-        });
-        await testSubjects.existOrFail('~infraNotFoundPage');
-      });
-
-      it(`infrastructure snapshot page renders not found page`, async () => {
-        await PageObjects.common.navigateToActualUrl('infraOps', 'infrastructure/inventory', {
+      it(`metrics app is inaccessible and Application Not Found message is rendered`, async () => {
+        await PageObjects.common.navigateToApp('infraOps', {
           basePath: '/s/custom_space',
-          ensureCurrentUrl: false,
-          shouldLoginIfPrompted: false,
         });
-        await testSubjects.existOrFail('~infraNotFoundPage');
-      });
-
-      it(`metrics page renders not found page`, async () => {
-        await PageObjects.common.navigateToActualUrl(
+        await testSubjects.existOrFail('~appNotFoundPageContent');
+        await PageObjects.common.navigateToUrlWithBrowserHistory(
           'infraOps',
-          '/infrastructure/metrics/host/demo-stack-redis-01',
+          '/inventory',
+          undefined,
           {
             basePath: '/s/custom_space',
-            ensureCurrentUrl: true,
+            ensureCurrentUrl: false,
+            shouldLoginIfPrompted: false,
           }
         );
-        await testSubjects.existOrFail('~infraNotFoundPage');
+        await testSubjects.existOrFail('~appNotFoundPageContent');
       });
     });
 
@@ -175,30 +121,11 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         await esArchiver.unload('empty_kibana');
       });
 
-      it(`landing page shows Wafflemap`, async () => {
-        await PageObjects.common.navigateToActualUrl('infraOps', 'home', {
+      it(`Metrics app is accessible`, async () => {
+        await PageObjects.common.navigateToApp('infraOps', {
           basePath: '/s/custom_space',
-          ensureCurrentUrl: true,
-        });
-        await PageObjects.infraHome.goToTime(DATE_WITH_DATA);
-        await testSubjects.existOrFail('~waffleMap');
-      });
-
-      describe('context menu', () => {
-        before(async () => {
-          await testSubjects.click('~nodeContainer');
-        });
-
-        it(`doesn't show link to view logs`, async () => {
-          await retry.waitFor('context menu', () => testSubjects.exists('~nodeContextMenu'));
-          const link = await testSubjects.find('~viewLogsContextMenuItem');
-          expect(await link.isEnabled()).to.be(false);
-        });
-
-        it(`shows link to view apm traces`, async () => {
-          await retry.waitFor('context menu', () => testSubjects.exists('~nodeContextMenu'));
-          await testSubjects.existOrFail('~viewApmTracesContextMenuItem');
         });
+        await testSubjects.existOrFail('~noMetricsIndicesPrompt');
       });
     });
 
@@ -219,30 +146,11 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         await esArchiver.unload('empty_kibana');
       });
 
-      it(`landing page shows Wafflemap`, async () => {
-        await PageObjects.common.navigateToActualUrl('infraOps', 'home', {
+      it(`Metrics app is accessible`, async () => {
+        await PageObjects.common.navigateToApp('infraOps', {
           basePath: '/s/custom_space',
-          ensureCurrentUrl: true,
-        });
-        await PageObjects.infraHome.goToTime(DATE_WITH_DATA);
-        await testSubjects.existOrFail('~waffleMap');
-      });
-
-      describe('context menu', () => {
-        before(async () => {
-          await testSubjects.click('~nodeContainer');
-        });
-
-        it(`shows link to view logs`, async () => {
-          await retry.waitFor('context menu', () => testSubjects.exists('~nodeContextMenu'));
-          await testSubjects.existOrFail('~viewLogsContextMenuItem');
-        });
-
-        it(`doesn't show link to view apm traces`, async () => {
-          await retry.waitFor('context menu', () => testSubjects.exists('~nodeContextMenu'));
-          const link = await testSubjects.find('~viewApmTracesContextMenuItem');
-          expect(await link.isEnabled()).to.be(false);
         });
+        await testSubjects.existOrFail('~noMetricsIndicesPrompt');
       });
     });
   });
diff --git a/x-pack/test/functional/apps/infra/feature_controls/logs_security.ts b/x-pack/test/functional/apps/infra/feature_controls/logs_security.ts
index 5008f93feeb01..48ad4e90fd413 100644
--- a/x-pack/test/functional/apps/infra/feature_controls/logs_security.ts
+++ b/x-pack/test/functional/apps/infra/feature_controls/logs_security.ts
@@ -63,7 +63,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       describe('logs landing page without data', () => {
         it(`shows 'Change source configuration' button`, async () => {
-          await PageObjects.common.navigateToActualUrl('infraOps', 'logs', {
+          await PageObjects.common.navigateToUrlWithBrowserHistory('infraLogs', '', undefined, {
             ensureCurrentUrl: true,
             shouldLoginIfPrompted: false,
           });
@@ -126,7 +126,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       describe('logs landing page without data', () => {
         it(`doesn't show 'Change source configuration' button`, async () => {
-          await PageObjects.common.navigateToActualUrl('infraOps', 'logs', {
+          await PageObjects.common.navigateToUrlWithBrowserHistory('infraLogs', '', undefined, {
             ensureCurrentUrl: true,
             shouldLoginIfPrompted: false,
           });
@@ -187,12 +187,19 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         expect(navLinks).to.not.contain('Logs');
       });
 
-      it('logs landing page renders not found page', async () => {
-        await PageObjects.common.navigateToActualUrl('infraOps', 'logs', {
-          ensureCurrentUrl: true,
-          shouldLoginIfPrompted: false,
-        });
-        await testSubjects.existOrFail('~infraNotFoundPage');
+      it(`logs app is inaccessible and Application Not Found message is rendered`, async () => {
+        await PageObjects.common.navigateToApp('infraLogs');
+        await testSubjects.existOrFail('~appNotFoundPageContent');
+        await PageObjects.common.navigateToUrlWithBrowserHistory(
+          'infraLogs',
+          '/stream',
+          undefined,
+          {
+            ensureCurrentUrl: false,
+            shouldLoginIfPrompted: false,
+          }
+        );
+        await testSubjects.existOrFail('~appNotFoundPageContent');
       });
     });
   });
diff --git a/x-pack/test/functional/apps/infra/feature_controls/logs_spaces.ts b/x-pack/test/functional/apps/infra/feature_controls/logs_spaces.ts
index 61a57e09f96c5..0094d227514c0 100644
--- a/x-pack/test/functional/apps/infra/feature_controls/logs_spaces.ts
+++ b/x-pack/test/functional/apps/infra/feature_controls/logs_spaces.ts
@@ -49,7 +49,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       describe('logs landing page without data', () => {
         it(`shows 'Change source configuration' button`, async () => {
-          await PageObjects.common.navigateToActualUrl('infraOps', 'logs', {
+          await PageObjects.common.navigateToUrlWithBrowserHistory('infraLogs', '', undefined, {
             basePath: '/s/custom_space',
             ensureCurrentUrl: true,
             shouldLoginIfPrompted: false,
@@ -86,13 +86,22 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         expect(navLinks).to.not.contain('Logs');
       });
 
-      it('logs landing page renders not found page', async () => {
-        await PageObjects.common.navigateToActualUrl('infraOps', 'logs', {
+      it(`logs app is inaccessible and Application Not Found message is rendered`, async () => {
+        await PageObjects.common.navigateToApp('infraLogs', {
           basePath: '/s/custom_space',
-          ensureCurrentUrl: true,
-          shouldLoginIfPrompted: false,
         });
-        await testSubjects.existOrFail('~infraNotFoundPage');
+        await testSubjects.existOrFail('~appNotFoundPageContent');
+        await PageObjects.common.navigateToUrlWithBrowserHistory(
+          'infraLogs',
+          '/stream',
+          undefined,
+          {
+            basePath: '/s/custom_space',
+            ensureCurrentUrl: false,
+            shouldLoginIfPrompted: false,
+          }
+        );
+        await testSubjects.existOrFail('~appNotFoundPageContent');
       });
     });
   });
diff --git a/x-pack/test/functional/apps/infra/link_to.ts b/x-pack/test/functional/apps/infra/link_to.ts
index 738dc7efd8fd9..7f803d9c3d0c1 100644
--- a/x-pack/test/functional/apps/infra/link_to.ts
+++ b/x-pack/test/functional/apps/infra/link_to.ts
@@ -18,23 +18,26 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
       const location = {
         hash: '',
         pathname: '/link-to/logs',
-        search: '?time=1565707203194&filter=trace.id:433b4651687e18be2c6c8e3b11f53d09',
+        search: 'time=1565707203194&filter=trace.id:433b4651687e18be2c6c8e3b11f53d09',
         state: undefined,
       };
       const expectedSearchString =
         "sourceId=default&logPosition=(position:(tiebreaker:0,time:1565707203194),streamLive:!f)&logFilter=(expression:'trace.id:433b4651687e18be2c6c8e3b11f53d09',kind:kuery)";
       const expectedRedirectPath = '/logs/stream?';
 
-      await pageObjects.common.navigateToActualUrl(
-        'infraOps',
-        `${location.pathname}${location.search}`
+      await pageObjects.common.navigateToUrlWithBrowserHistory(
+        'infraLogs',
+        location.pathname,
+        location.search,
+        {
+          ensureCurrentUrl: false,
+        }
       );
       await retry.tryForTime(5000, async () => {
         const currentUrl = await browser.getCurrentUrl();
-        const [, currentHash] = decodeURIComponent(currentUrl).split('#');
-        // Account for unpredictable location of the g parameter in the search string
-        expect(currentHash.slice(0, expectedRedirectPath.length)).to.be(expectedRedirectPath);
-        expect(currentHash.slice(expectedRedirectPath.length)).to.contain(expectedSearchString);
+        const decodedUrl = decodeURIComponent(currentUrl);
+        expect(decodedUrl).to.contain(expectedRedirectPath);
+        expect(decodedUrl).to.contain(expectedSearchString);
       });
     });
   });
diff --git a/x-pack/test/functional/apps/infra/metrics_source_configuration.ts b/x-pack/test/functional/apps/infra/metrics_source_configuration.ts
index 8f5c765cec6ad..d334fa7956be4 100644
--- a/x-pack/test/functional/apps/infra/metrics_source_configuration.ts
+++ b/x-pack/test/functional/apps/infra/metrics_source_configuration.ts
@@ -38,7 +38,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
       });
 
       it('can change the metric indices to a pattern that matches nothing', async () => {
-        await pageObjects.common.navigateToActualUrl('infraOps', 'infrastructure/settings');
+        await pageObjects.common.navigateToUrlWithBrowserHistory('infraOps', '/settings');
 
         const nameInput = await infraSourceConfigurationForm.getNameInput();
         await nameInput.clearValueWithKeyboard({ charByChar: true });
@@ -57,7 +57,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
       });
 
       it('can change the metric indices back to a pattern that matches something', async () => {
-        await pageObjects.common.navigateToActualUrl('infraOps', 'infrastructure/settings');
+        await pageObjects.common.navigateToUrlWithBrowserHistory('infraOps', '/settings');
 
         const metricIndicesInput = await infraSourceConfigurationForm.getMetricIndicesInput();
         await metricIndicesInput.clearValueWithKeyboard({ charByChar: true });
diff --git a/x-pack/test/functional/config.js b/x-pack/test/functional/config.js
index 913a8ea0c9dac..b589451c76165 100644
--- a/x-pack/test/functional/config.js
+++ b/x-pack/test/functional/config.js
@@ -133,11 +133,10 @@ export default async function({ readConfigFile }) {
         pathname: '/',
       },
       infraOps: {
-        pathname: '/app/infra',
+        pathname: '/app/metrics',
       },
       infraLogs: {
-        pathname: '/app/infra',
-        hash: '/logs',
+        pathname: '/app/logs',
       },
       canvas: {
         pathname: '/app/canvas',
diff --git a/x-pack/test/functional/page_objects/infra_logs_page.ts b/x-pack/test/functional/page_objects/infra_logs_page.ts
index 1c58f8dc41eba..8f554729328bb 100644
--- a/x-pack/test/functional/page_objects/infra_logs_page.ts
+++ b/x-pack/test/functional/page_objects/infra_logs_page.ts
@@ -19,7 +19,7 @@ export function InfraLogsPageProvider({ getPageObjects, getService }: FtrProvide
     },
 
     async navigateToTab(logsUiTab: LogsUiTab) {
-      await pageObjects.common.navigateToActualUrl('infraLogs', `/logs/${logsUiTab}`);
+      await pageObjects.common.navigateToUrlWithBrowserHistory('infraLogs', `/${logsUiTab}`);
     },
 
     async getLogStream() {
diff --git a/x-pack/legacy/plugins/infra/types/rison_node.d.ts b/x-pack/typings/rison_node.d.ts
similarity index 100%
rename from x-pack/legacy/plugins/infra/types/rison_node.d.ts
rename to x-pack/typings/rison_node.d.ts
diff --git a/yarn.lock b/yarn.lock
index 0e830540ccdb2..c4a2b05888d43 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4223,11 +4223,6 @@
   resolved "https://registry.yarnpkg.com/@types/boom/-/boom-7.2.0.tgz#19c36cbb5811a7493f0f2e37f31d42b28df1abc1"
   integrity sha512-HonbGsHFbskh9zRAzA6tabcw18mCOsSEOL2ibGAuVqk6e7nElcRmWO5L4UfIHpDbWBWw+eZYFdsQ1+MEGgpcVA==
 
-"@types/boom@7.2.1":
-  version "7.2.1"
-  resolved "https://registry.yarnpkg.com/@types/boom/-/boom-7.2.1.tgz#a21e21ba08cc49d17b26baef98e1a77ee4d6cdb0"
-  integrity sha512-kOiap+kSa4DPoookJXQGQyKy1rjZ55tgfKAh9F0m1NUdukkcwVzpSnXPMH42a5L+U++ugdQlh/xFJu/WAdr1aw==
-
 "@types/browserslist-useragent@^3.0.0":
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/@types/browserslist-useragent/-/browserslist-useragent-3.0.0.tgz#d425c9818182ce71ce53866798cee9c7d41d6e53"
@@ -8017,13 +8012,6 @@ boom@5.x.x:
   dependencies:
     hoek "4.x.x"
 
-boom@7.3.0:
-  version "7.3.0"
-  resolved "https://registry.yarnpkg.com/boom/-/boom-7.3.0.tgz#733a6d956d33b0b1999da3fe6c12996950d017b9"
-  integrity sha512-Swpoyi2t5+GhOEGw8rEsKvTxFLIDiiKoUc2gsoV6Lyr43LHBIzch3k2MvYUs8RTROrIkVJ3Al0TkaOGjnb+B6A==
-  dependencies:
-    hoek "6.x.x"
-
 boom@7.x.x, boom@^7.1.0, boom@^7.2.0:
   version "7.2.2"
   resolved "https://registry.yarnpkg.com/boom/-/boom-7.2.2.tgz#ac92101451aa5cea901aed07d881dd32b4f08345"

From 7cf33c14dd8d752ae1411f7a07e72d36e77f9565 Mon Sep 17 00:00:00 2001
From: Spencer <email@spalger.com>
Date: Tue, 18 Feb 2020 12:43:55 -0700
Subject: [PATCH 038/174] [@kbn/optimizer] rewrite url(ui/*) in legacy scss
 files (#57869)

* [@kbn/optimizer] rewrite url(ui/*) in legacy scss files

* update kbn/pm dist
---
 packages/kbn-optimizer/package.json           |   1 +
 .../src/worker/webpack.config.ts              |  21 ++++
 packages/kbn-pm/dist/index.js                 |  31 +++--
 yarn.lock                                     | 119 +++++++++++++++---
 4 files changed, 142 insertions(+), 30 deletions(-)

diff --git a/packages/kbn-optimizer/package.json b/packages/kbn-optimizer/package.json
index e8bb31f1e365d..219b9aaa7f8f2 100644
--- a/packages/kbn-optimizer/package.json
+++ b/packages/kbn-optimizer/package.json
@@ -31,6 +31,7 @@
     "node-sass": "^4.13.0",
     "postcss-loader": "^3.0.0",
     "raw-loader": "^3.1.0",
+    "resolve-url-loader": "^3.1.1",
     "rxjs": "^6.5.3",
     "sass-loader": "^8.0.2",
     "style-loader": "^1.1.3",
diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts
index 1e87b8a5a7f7b..22b927d4638d7 100644
--- a/packages/kbn-optimizer/src/worker/webpack.config.ts
+++ b/packages/kbn-optimizer/src/worker/webpack.config.ts
@@ -126,6 +126,27 @@ export function getWebpackConfig(bundle: Bundle, worker: WorkerConfig) {
                     },
                   },
                 },
+                {
+                  loader: 'resolve-url-loader',
+                  options: {
+                    join: (_: string, __: any) => (uri: string, base?: string) => {
+                      if (!base) {
+                        return null;
+                      }
+
+                      // manually force ui/* urls in legacy styles to resolve to ui/legacy/public
+                      if (uri.startsWith('ui/') && base.split(Path.sep).includes('legacy')) {
+                        return Path.resolve(
+                          worker.repoRoot,
+                          'src/legacy/ui/public',
+                          uri.replace('ui/', '')
+                        );
+                      }
+
+                      return null;
+                    },
+                  },
+                },
                 {
                   loader: 'sass-loader',
                   options: {
diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js
index 2ccff3e9c3df2..451db9750ada7 100644
--- a/packages/kbn-pm/dist/index.js
+++ b/packages/kbn-pm/dist/index.js
@@ -43046,21 +43046,28 @@ module.exports = require("tty");
 const os = __webpack_require__(11);
 const hasFlag = __webpack_require__(12);
 
-const env = process.env;
+const {env} = process;
 
 let forceColor;
 if (hasFlag('no-color') ||
 	hasFlag('no-colors') ||
-	hasFlag('color=false')) {
-	forceColor = false;
+	hasFlag('color=false') ||
+	hasFlag('color=never')) {
+	forceColor = 0;
 } else if (hasFlag('color') ||
 	hasFlag('colors') ||
 	hasFlag('color=true') ||
 	hasFlag('color=always')) {
-	forceColor = true;
+	forceColor = 1;
 }
 if ('FORCE_COLOR' in env) {
-	forceColor = env.FORCE_COLOR.length === 0 || parseInt(env.FORCE_COLOR, 10) !== 0;
+	if (env.FORCE_COLOR === true || env.FORCE_COLOR === 'true') {
+		forceColor = 1;
+	} else if (env.FORCE_COLOR === false || env.FORCE_COLOR === 'false') {
+		forceColor = 0;
+	} else {
+		forceColor = env.FORCE_COLOR.length === 0 ? 1 : Math.min(parseInt(env.FORCE_COLOR, 10), 3);
+	}
 }
 
 function translateLevel(level) {
@@ -43077,7 +43084,7 @@ function translateLevel(level) {
 }
 
 function supportsColor(stream) {
-	if (forceColor === false) {
+	if (forceColor === 0) {
 		return 0;
 	}
 
@@ -43091,11 +43098,15 @@ function supportsColor(stream) {
 		return 2;
 	}
 
-	if (stream && !stream.isTTY && forceColor !== true) {
+	if (stream && !stream.isTTY && forceColor === undefined) {
 		return 0;
 	}
 
-	const min = forceColor ? 1 : 0;
+	const min = forceColor || 0;
+
+	if (env.TERM === 'dumb') {
+		return min;
+	}
 
 	if (process.platform === 'win32') {
 		// Node.js 7.5.0 is the first version of Node.js to include a patch to
@@ -43156,10 +43167,6 @@ function supportsColor(stream) {
 		return 1;
 	}
 
-	if (env.TERM === 'dumb') {
-		return min;
-	}
-
 	return min;
 }
 
diff --git a/yarn.lock b/yarn.lock
index c4a2b05888d43..74daf198c44d5 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5885,6 +5885,17 @@ address@^1.0.1:
   resolved "https://registry.yarnpkg.com/address/-/address-1.0.3.tgz#b5f50631f8d6cec8bd20c963963afb55e06cbce9"
   integrity sha512-z55ocwKBRLryBs394Sm3ushTtBeg6VAeuku7utSoSnsJKvKcnXFIyC6vh27n3rXyxSgkJBBCAvyOn7gSUcTYjg==
 
+adjust-sourcemap-loader@2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/adjust-sourcemap-loader/-/adjust-sourcemap-loader-2.0.0.tgz#6471143af75ec02334b219f54bc7970c52fb29a4"
+  integrity sha512-4hFsTsn58+YjrU9qKzML2JSSDqKvN8mUGQ0nNIrfPi8hmIONT4L3uUaT6MKdMsZ9AjsU6D2xDkZxCkbQPxChrA==
+  dependencies:
+    assert "1.4.1"
+    camelcase "5.0.0"
+    loader-utils "1.2.3"
+    object-path "0.11.4"
+    regex-parser "2.2.10"
+
 adm-zip@0.4.11:
   version "0.4.11"
   resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.11.tgz#2aa54c84c4b01a9d0fb89bb11982a51f13e3d62a"
@@ -6640,6 +6651,11 @@ aria-query@3.0.0, aria-query@^3.0.0:
     ast-types-flow "0.0.7"
     commander "^2.11.0"
 
+arity-n@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/arity-n/-/arity-n-1.0.4.tgz#d9e76b11733e08569c0847ae7b39b2860b30b745"
+  integrity sha1-2edrEXM+CFacCEeuezmyhgswt0U=
+
 arr-diff@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
@@ -6874,7 +6890,7 @@ assert-plus@^0.2.0:
   resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234"
   integrity sha1-104bh+ev/A24qttwIfP+SBAasjQ=
 
-assert@^1.1.1:
+assert@1.4.1, assert@^1.1.1:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91"
   integrity sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=
@@ -8639,6 +8655,16 @@ camelcase-keys@^4.0.0:
     map-obj "^2.0.0"
     quick-lru "^1.0.0"
 
+camelcase@5.0.0, camelcase@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42"
+  integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==
+
+camelcase@5.3.1, camelcase@^5.3.1:
+  version "5.3.1"
+  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
+  integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
+
 camelcase@^1.0.2:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39"
@@ -8659,16 +8685,6 @@ camelcase@^4.0.0, camelcase@^4.1.0:
   resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
   integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=
 
-camelcase@^5.0.0:
-  version "5.0.0"
-  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42"
-  integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==
-
-camelcase@^5.3.1:
-  version "5.3.1"
-  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
-  integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
-
 camelize@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b"
@@ -9655,6 +9671,13 @@ component-inherit@0.0.3:
   resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143"
   integrity sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=
 
+compose-function@3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/compose-function/-/compose-function-3.0.3.tgz#9ed675f13cc54501d30950a486ff6a7ba3ab185f"
+  integrity sha1-ntZ18TzFRQHTCVCkhv9qe6OrGF8=
+  dependencies:
+    arity-n "^1.0.4"
+
 compress-commons@^2.1.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-2.1.1.tgz#9410d9a534cf8435e3fbbb7c6ce48de2dc2f0610"
@@ -9902,11 +9925,23 @@ contour_plot@^0.0.1:
   resolved "https://registry.yarnpkg.com/contour_plot/-/contour_plot-0.0.1.tgz#475870f032b8e338412aa5fc507880f0bf495c77"
   integrity sha1-R1hw8DK44zhBKqX8UHiA8L9JXHc=
 
+convert-source-map@1.7.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
+  integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==
+  dependencies:
+    safe-buffer "~5.1.1"
+
 convert-source-map@1.X, convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.5.0:
   version "1.5.1"
   resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5"
   integrity sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=
 
+convert-source-map@^0.3.3:
+  version "0.3.5"
+  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-0.3.5.tgz#f1d802950af7dd2631a1febe0596550c86ab3190"
+  integrity sha1-8dgClQr33SYxof6+BZZVDIarMZA=
+
 convert-source-map@^1.5.1, convert-source-map@^1.6.0:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20"
@@ -10513,7 +10548,7 @@ css.escape@^1.5.1:
   resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb"
   integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=
 
-css@2.X, css@^2.2.1, css@^2.2.3, css@^2.2.4:
+css@2.X, css@^2.0.0, css@^2.2.1, css@^2.2.3, css@^2.2.4:
   version "2.2.4"
   resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929"
   integrity sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==
@@ -12403,7 +12438,7 @@ es6-error@^4.0.1:
   resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d"
   integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==
 
-es6-iterator@^2.0.1, es6-iterator@~2.0.1, es6-iterator@~2.0.3:
+es6-iterator@2.0.3, es6-iterator@^2.0.1, es6-iterator@~2.0.1, es6-iterator@~2.0.3:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
   integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c=
@@ -21865,6 +21900,11 @@ object-path-immutable@^3.1.1:
   dependencies:
     is-plain-object "3.0.0"
 
+object-path@0.11.4:
+  version "0.11.4"
+  resolved "https://registry.yarnpkg.com/object-path/-/object-path-0.11.4.tgz#370ae752fbf37de3ea70a861c23bba8915691949"
+  integrity sha1-NwrnUvvzfePqcKhhwju6iRVpGUk=
+
 object-values@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/object-values/-/object-values-1.0.0.tgz#72af839630119e5b98c3b02bb8c27e3237158105"
@@ -23291,6 +23331,15 @@ postcss-values-parser@^1.5.0:
     indexes-of "^1.0.1"
     uniq "^1.0.1"
 
+postcss@7.0.21:
+  version "7.0.21"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.21.tgz#06bb07824c19c2021c5d056d5b10c35b989f7e17"
+  integrity sha512-uIFtJElxJo29QC753JzhidoAhvp/e/Exezkdhfmt8AymWT6/5B7W1WmponYWkHk2eg6sONyTch0A3nkMPun3SQ==
+  dependencies:
+    chalk "^2.4.2"
+    source-map "^0.6.1"
+    supports-color "^6.1.0"
+
 postcss@^7.0.0, postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.2, postcss@^7.0.23, postcss@^7.0.26, postcss@^7.0.5, postcss@^7.0.6:
   version "7.0.26"
   resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.26.tgz#5ed615cfcab35ba9bbb82414a4fa88ea10429587"
@@ -25434,6 +25483,11 @@ regex-not@^1.0.2:
     extend-shallow "^3.0.2"
     safe-regex "^1.1.0"
 
+regex-parser@2.2.10:
+  version "2.2.10"
+  resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.10.tgz#9e66a8f73d89a107616e63b39d4deddfee912b37"
+  integrity sha512-8t6074A68gHfU8Neftl0Le6KTDwfGAj7IyjPIMSfikI2wJUTHDMaIq42bUsfVnj8mhx0R+45rdUXHGpN164avA==
+
 regex-regex@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/regex-regex/-/regex-regex-1.0.0.tgz#9048a1eaeb870f4d480dabc76fc42cdcc0bc3a72"
@@ -25990,6 +26044,22 @@ resolve-protobuf-schema@^2.0.0:
   dependencies:
     protocol-buffers-schema "^3.3.1"
 
+resolve-url-loader@^3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-3.1.1.tgz#28931895fa1eab9be0647d3b2958c100ae3c0bf0"
+  integrity sha512-K1N5xUjj7v0l2j/3Sgs5b8CjrrgtC70SmdCuZiJ8tSyb5J+uk3FoeZ4b7yTnH6j7ngI+Bc5bldHJIa8hYdu2gQ==
+  dependencies:
+    adjust-sourcemap-loader "2.0.0"
+    camelcase "5.3.1"
+    compose-function "3.0.3"
+    convert-source-map "1.7.0"
+    es6-iterator "2.0.3"
+    loader-utils "1.2.3"
+    postcss "7.0.21"
+    rework "1.0.1"
+    rework-visit "1.0.0"
+    source-map "0.6.1"
+
 resolve-url@^0.2.1:
   version "0.2.1"
   resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
@@ -26128,6 +26198,19 @@ reusify@^1.0.0:
   resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
   integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
 
+rework-visit@1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/rework-visit/-/rework-visit-1.0.0.tgz#9945b2803f219e2f7aca00adb8bc9f640f842c9a"
+  integrity sha1-mUWygD8hni96ygCtuLyfZA+ELJo=
+
+rework@1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/rework/-/rework-1.0.1.tgz#30806a841342b54510aa4110850cd48534144aa7"
+  integrity sha1-MIBqhBNCtUUQqkEQhQzUhTQUSqc=
+  dependencies:
+    convert-source-map "^0.3.3"
+    css "^2.0.0"
+
 rfdc@^1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.1.2.tgz#e6e72d74f5dc39de8f538f65e00c36c18018e349"
@@ -27312,6 +27395,11 @@ source-map@0.5.6:
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
   integrity sha1-dc449SvwczxafwwRjYEzSiu19BI=
 
+source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+  integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
 source-map@^0.4.2:
   version "0.4.4"
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
@@ -27324,11 +27412,6 @@ source-map@^0.5.0, source-map@^0.5.1, source-map@^0.5.3, source-map@^0.5.6, sour
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
   integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
 
-source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
-  version "0.6.1"
-  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
-  integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
-
 source-map@~0.1.30:
   version "0.1.43"
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346"

From 7763a6055a598a6ed6845682a948e7a6283e01b2 Mon Sep 17 00:00:00 2001
From: Brandon Kobel <brandon.kobel@elastic.co>
Date: Tue, 18 Feb 2020 11:47:01 -0800
Subject: [PATCH 039/174] Handling a 404 when the space's telemetry collector
 runs (#55921)

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../server/lib/spaces_usage_collector.test.ts | 32 +++++++++++++
 .../server/lib/spaces_usage_collector.ts      | 47 +++++++++++--------
 2 files changed, 60 insertions(+), 19 deletions(-)

diff --git a/x-pack/plugins/spaces/server/lib/spaces_usage_collector.test.ts b/x-pack/plugins/spaces/server/lib/spaces_usage_collector.test.ts
index b343bac9343a3..c0a6a152c8322 100644
--- a/x-pack/plugins/spaces/server/lib/spaces_usage_collector.test.ts
+++ b/x-pack/plugins/spaces/server/lib/spaces_usage_collector.test.ts
@@ -66,6 +66,38 @@ const defaultCallClusterMock = jest.fn().mockResolvedValue({
   },
 });
 
+describe('error handling', () => {
+  it('handles a 404 when searching for space usage', async () => {
+    const { features, licensing, usageCollecion } = setup({
+      license: { isAvailable: true, type: 'basic' },
+    });
+    const { fetch: getSpacesUsage } = getSpacesUsageCollector(usageCollecion as any, {
+      kibanaIndex: '.kibana',
+      features,
+      licensing,
+    });
+
+    await getSpacesUsage(jest.fn().mockRejectedValue({ status: 404 }));
+  });
+
+  it('throws error for a non-404', async () => {
+    const { features, licensing, usageCollecion } = setup({
+      license: { isAvailable: true, type: 'basic' },
+    });
+    const { fetch: getSpacesUsage } = getSpacesUsageCollector(usageCollecion as any, {
+      kibanaIndex: '.kibana',
+      features,
+      licensing,
+    });
+
+    const statusCodes = [401, 402, 403, 500];
+    for (const statusCode of statusCodes) {
+      const error = { status: statusCode };
+      await expect(getSpacesUsage(jest.fn().mockRejectedValue(error))).rejects.toBe(error);
+    }
+  });
+});
+
 describe('with a basic license', () => {
   let usageStats: UsageStats;
   beforeAll(async () => {
diff --git a/x-pack/plugins/spaces/server/lib/spaces_usage_collector.ts b/x-pack/plugins/spaces/server/lib/spaces_usage_collector.ts
index eb6843cfe4538..af77f2d3a72ba 100644
--- a/x-pack/plugins/spaces/server/lib/spaces_usage_collector.ts
+++ b/x-pack/plugins/spaces/server/lib/spaces_usage_collector.ts
@@ -50,31 +50,40 @@ async function getSpacesUsage(
 
   const knownFeatureIds = features.getFeatures().map(feature => feature.id);
 
-  const resp = await callCluster<SpacesAggregationResponse>('search', {
-    index: kibanaIndex,
-    body: {
-      track_total_hits: true,
-      query: {
-        term: {
-          type: {
-            value: 'space',
+  let resp: SpacesAggregationResponse | undefined;
+  try {
+    resp = await callCluster<SpacesAggregationResponse>('search', {
+      index: kibanaIndex,
+      body: {
+        track_total_hits: true,
+        query: {
+          term: {
+            type: {
+              value: 'space',
+            },
           },
         },
-      },
-      aggs: {
-        disabledFeatures: {
-          terms: {
-            field: 'space.disabledFeatures',
-            include: knownFeatureIds,
-            size: knownFeatureIds.length,
+        aggs: {
+          disabledFeatures: {
+            terms: {
+              field: 'space.disabledFeatures',
+              include: knownFeatureIds,
+              size: knownFeatureIds.length,
+            },
           },
         },
+        size: 0,
       },
-      size: 0,
-    },
-  });
+    });
+  } catch (err) {
+    if (err.status === 404) {
+      return {} as UsageStats;
+    }
+
+    throw err;
+  }
 
-  const { hits, aggregations } = resp;
+  const { hits, aggregations } = resp!;
 
   const count = get(hits, 'total.value', 0);
   const disabledFeatureBuckets = get(aggregations, 'disabledFeatures.buckets', []);

From 6436862dc52942a060246af30e63e0b4d2db17bf Mon Sep 17 00:00:00 2001
From: Vadim Dalecky <streamich@users.noreply.github.com>
Date: Tue, 18 Feb 2020 20:52:03 +0100
Subject: [PATCH 040/174] Types (#57801)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat: 🎸 add Values<T>

* feat: 🎸 add UnionToIntersection<T> type

* refactor: 💡 use UnionToIntersection<T> in Canvas from @kbn/

* feat: 🎸 add PublicKeys<T> and PublicContract<T> types

* style: 💄 fix ESLint errors
---
 packages/kbn-utility-types/README.md          |  4 ++
 packages/kbn-utility-types/index.ts           | 28 ++++++++++++++
 .../test-d/public_contract.ts                 | 33 ++++++++++++++++
 .../kbn-utility-types/test-d/public_keys.ts   | 31 +++++++++++++++
 .../test-d/union_to_intersection.ts           | 28 ++++++++++++++
 packages/kbn-utility-types/test-d/values.ts   | 38 +++++++++++++++++++
 .../canvas/i18n/functions/function_help.ts    |  3 +-
 .../legacy/plugins/canvas/types/functions.ts  | 16 --------
 8 files changed, 164 insertions(+), 17 deletions(-)
 create mode 100644 packages/kbn-utility-types/test-d/public_contract.ts
 create mode 100644 packages/kbn-utility-types/test-d/public_keys.ts
 create mode 100644 packages/kbn-utility-types/test-d/union_to_intersection.ts
 create mode 100644 packages/kbn-utility-types/test-d/values.ts

diff --git a/packages/kbn-utility-types/README.md b/packages/kbn-utility-types/README.md
index aafae4d3a5134..829fd21e14366 100644
--- a/packages/kbn-utility-types/README.md
+++ b/packages/kbn-utility-types/README.md
@@ -20,8 +20,12 @@ type B = UnwrapPromise<A>; // string
 
 - `Ensure<T, X>` &mdash; Makes sure `T` is of type `X`.
 - `ObservableLike<T>` &mdash; Minimal interface for an object resembling an `Observable`.
+- `PublicContract<T>` &mdash; Returns an object with public keys only.
+- `PublicKeys<T>` &mdash; Returns public keys of an object.
 - `RecursiveReadonly<T>` &mdash; Like `Readonly<T>`, but freezes object recursively.
 - `ShallowPromise<T>` &mdash; Same as `Promise` type, but it flat maps the wrapped type.
+- `UnionToIntersection<T>` &mdash; Converts a union of types into an intersection.
 - `UnwrapObservable<T>` &mdash; Returns wrapped type of an observable.
 - `UnwrapPromise<T>` &mdash; Returns wrapped type of a promise.
 - `UnwrapPromiseOrReturn<T>` &mdash; Returns wrapped type of a promise or the type itself, if it isn't a promise.
+- `Values<T>` &mdash; Returns object or array value types.
diff --git a/packages/kbn-utility-types/index.ts b/packages/kbn-utility-types/index.ts
index ec81f7347b481..808935ed4cb5b 100644
--- a/packages/kbn-utility-types/index.ts
+++ b/packages/kbn-utility-types/index.ts
@@ -69,3 +69,31 @@ export type RecursiveReadonly<T> = T extends (...args: any) => any
   : T extends object
   ? Readonly<{ [K in keyof T]: RecursiveReadonly<T[K]> }>
   : T;
+
+/**
+ * Returns types or array or object values.
+ */
+export type Values<T> = T extends any[] ? T[number] : T extends object ? T[keyof T] : never;
+
+/**
+ * Utility type for converting a union of types into an intersection.
+ *
+ * This is a bit of "black magic" that will interpret a Union type as an Intersection
+ * type.  This is necessary in the case of distinguishing one collection from
+ * another.
+ */
+export type UnionToIntersection<U> = (U extends any
+? (k: U) => void
+: never) extends (k: infer I) => void
+  ? I
+  : never;
+
+/**
+ * Returns public keys of an object.
+ */
+export type PublicKeys<T> = keyof T;
+
+/**
+ * Returns an object with public keys only.
+ */
+export type PublicContract<T> = Pick<T, PublicKeys<T>>;
diff --git a/packages/kbn-utility-types/test-d/public_contract.ts b/packages/kbn-utility-types/test-d/public_contract.ts
new file mode 100644
index 0000000000000..b4429a60c758c
--- /dev/null
+++ b/packages/kbn-utility-types/test-d/public_contract.ts
@@ -0,0 +1,33 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { expectType } from 'tsd';
+import { PublicContract } from '../index';
+
+class Test {
+  public str: string = '';
+  // @ts-ignore
+  private num: number = 0;
+}
+
+type CONTRACT = PublicContract<Test>;
+
+expectType<CONTRACT>({
+  str: 'foo',
+});
diff --git a/packages/kbn-utility-types/test-d/public_keys.ts b/packages/kbn-utility-types/test-d/public_keys.ts
new file mode 100644
index 0000000000000..66bf250a91547
--- /dev/null
+++ b/packages/kbn-utility-types/test-d/public_keys.ts
@@ -0,0 +1,31 @@
+/*
+ * 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 { expectType } from 'tsd';
+import { PublicKeys } from '../index';
+
+class Test {
+  public str: string = '';
+  // @ts-ignore
+  private num: number = 0;
+}
+
+type KEYS = PublicKeys<Test>;
+
+expectType<KEYS>('str');
diff --git a/packages/kbn-utility-types/test-d/union_to_intersection.ts b/packages/kbn-utility-types/test-d/union_to_intersection.ts
new file mode 100644
index 0000000000000..ba385268475e7
--- /dev/null
+++ b/packages/kbn-utility-types/test-d/union_to_intersection.ts
@@ -0,0 +1,28 @@
+/*
+ * 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 { expectType } from 'tsd';
+import { UnionToIntersection } from '../index';
+
+type INTERSECTED = UnionToIntersection<{ foo: 'bar' } | { baz: 'qux' }>;
+
+expectType<INTERSECTED>({
+  foo: 'bar',
+  baz: 'qux',
+});
diff --git a/packages/kbn-utility-types/test-d/values.ts b/packages/kbn-utility-types/test-d/values.ts
new file mode 100644
index 0000000000000..9e50cfebde1db
--- /dev/null
+++ b/packages/kbn-utility-types/test-d/values.ts
@@ -0,0 +1,38 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { expectType } from 'tsd';
+import { Values } from '../index';
+
+// Arrays
+type STRING = Values<string[]>;
+type ASDF_FOO = Values<Array<'asdf' | 'foo'>>;
+
+expectType<STRING>('adf');
+expectType<ASDF_FOO>('asdf');
+expectType<ASDF_FOO>('foo');
+
+// Objects
+type STRING2 = Values<Record<number, string>>;
+type FOO = Values<Record<number, 'foo'>>;
+type BAR = Values<{ foo: 'bar' }>;
+
+expectType<STRING2>('adf');
+expectType<FOO>('foo');
+expectType<BAR>('bar');
diff --git a/x-pack/legacy/plugins/canvas/i18n/functions/function_help.ts b/x-pack/legacy/plugins/canvas/i18n/functions/function_help.ts
index 565cfa251e126..dbdadd09df67f 100644
--- a/x-pack/legacy/plugins/canvas/i18n/functions/function_help.ts
+++ b/x-pack/legacy/plugins/canvas/i18n/functions/function_help.ts
@@ -5,7 +5,8 @@
  */
 
 import { ExpressionFunctionDefinition } from 'src/plugins/expressions';
-import { UnionToIntersection, CanvasFunction } from '../../types';
+import { UnionToIntersection } from '@kbn/utility-types';
+import { CanvasFunction } from '../../types';
 
 import { help as all } from './dict/all';
 import { help as alterColumn } from './dict/alter_column';
diff --git a/x-pack/legacy/plugins/canvas/types/functions.ts b/x-pack/legacy/plugins/canvas/types/functions.ts
index 27b2a04ebd6e3..3344f9b3ae9f2 100644
--- a/x-pack/legacy/plugins/canvas/types/functions.ts
+++ b/x-pack/legacy/plugins/canvas/types/functions.ts
@@ -10,22 +10,6 @@ import { functions as browserFunctions } from '../canvas_plugin_src/functions/br
 import { functions as serverFunctions } from '../canvas_plugin_src/functions/server';
 import { clientFunctions } from '../public/functions';
 
-/**
- * Utility type for converting a union of types into an intersection.
- *
- * This is a bit of "black magic" that will interpret a Union type as an Intersection
- * type.  This is necessary in this case of distiguishing one collection from
- * another in `FunctionError` and `FunctionStrings`.
- */
-// prettier-ignore
-export type UnionToIntersection<U> = 
-  (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
-
-/**
- * Utility type: gathers values of a collection as a type for use as a type.
- */
-export type ValuesOf<T extends any[]> = T[number];
-
 /**
  * A `ExpressionFunctionFactory` is a powerful type used for any function that produces
  * an `ExpressionFunction`. If it does not meet the signature for such a function,

From 5c2e6d93821f0ee71da9e5bc7f28686267ccd313 Mon Sep 17 00:00:00 2001
From: gchaps <33642766+gchaps@users.noreply.github.com>
Date: Tue, 18 Feb 2020 12:19:44 -0800
Subject: [PATCH 041/174] [DOCS] Removes IRC from Contributing.md (#57397)

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 CONTRIBUTING.md | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9cba1ee909f6d..e559177586f48 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -55,11 +55,9 @@ Granted that you share your thoughts, we might even be able to come up with crea
 
 First of all, **sorry about that!** We want you to have a great time with Kibana.
 
-Hosting meaningful discussions on GitHub can be challenging. For that reason, we'll sometimes ask that you join us on IRC _([#kibana](https://kiwiirc.com/client/irc.freenode.net/?#kibana) on freenode)_ to chat about your issues. You may also experience **faster response times** when engaging us via IRC.
-
 There's hundreds of open issues and prioritizing what to work on is an important aspect of our daily jobs. We prioritize issues according to impact and difficulty, so some issues can be neglected while we work on more pressing issues.
 
-Feel free to bump your issues if you think they've been neglected for a prolonged period, or just jump on IRC and let us have it!
+Feel free to bump your issues if you think they've been neglected for a prolonged period.
 
 ### "I want to help!"
 

From f6dc67470d5796ed99df1013c4bf007ab3492167 Mon Sep 17 00:00:00 2001
From: Davis Plumlee <56367316+dplumlee@users.noreply.github.com>
Date: Tue, 18 Feb 2020 15:21:10 -0500
Subject: [PATCH 042/174] Alert list frontend pagination (#57142)

---
 .../public/applications/endpoint/index.tsx    |  55 ++++---
 .../endpoint/store/alerts/action.ts           |   5 +-
 .../endpoint/store/alerts/alert_list.test.ts  |  85 +++++++++++
 .../alerts/alert_list_pagination.test.ts      |  98 +++++++++++++
 .../endpoint/store/alerts/middleware.ts       |  18 +--
 .../endpoint/store/alerts/reducer.ts          |   6 +
 .../endpoint/store/alerts/selectors.ts        |  70 +++++++++
 .../applications/endpoint/store/index.ts      |   5 +-
 .../endpoint/store/routing/action.ts          |  10 +-
 .../applications/endpoint/store/selectors.ts  |  31 ----
 .../public/applications/endpoint/types.ts     |  17 ++-
 .../view/alerts/hooks/use_alerts_selector.ts  |  12 ++
 .../endpoint/view/alerts/index.tsx            | 136 +++++++++++++-----
 .../functional/apps/endpoint/alert_list.ts    |  25 ++++
 x-pack/test/functional/apps/endpoint/index.ts |   1 +
 15 files changed, 469 insertions(+), 105 deletions(-)
 create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/alert_list.test.ts
 create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/alert_list_pagination.test.ts
 delete mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/store/selectors.ts
 create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/hooks/use_alerts_selector.ts
 create mode 100644 x-pack/test/functional/apps/endpoint/alert_list.ts

diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx
index 7bb3b13525914..8530d6206d398 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx
@@ -8,20 +8,22 @@ import * as React from 'react';
 import ReactDOM from 'react-dom';
 import { CoreStart, AppMountParameters } from 'kibana/public';
 import { I18nProvider, FormattedMessage } from '@kbn/i18n/react';
-import { Route, BrowserRouter, Switch } from 'react-router-dom';
-import { Provider } from 'react-redux';
+import { Route, Switch, BrowserRouter, useLocation } from 'react-router-dom';
+import { Provider, useDispatch } from 'react-redux';
 import { Store } from 'redux';
+import { memo } from 'react';
 import { appStoreFactory } from './store';
 import { AlertIndex } from './view/alerts';
 import { ManagementList } from './view/managing';
 import { PolicyList } from './view/policy';
+import { AppAction } from './store/action';
+import { EndpointAppLocation } from './types';
 
 /**
  * This module will be loaded asynchronously to reduce the bundle size of your plugin's main bundle.
  */
 export function renderApp(coreStart: CoreStart, { appBasePath, element }: AppMountParameters) {
   coreStart.http.get('/api/endpoint/hello-world');
-
   const store = appStoreFactory(coreStart);
 
   ReactDOM.render(<AppRoot basename={appBasePath} store={store} />, element);
@@ -31,6 +33,13 @@ export function renderApp(coreStart: CoreStart, { appBasePath, element }: AppMou
   };
 }
 
+const RouteCapture = memo(({ children }) => {
+  const location: EndpointAppLocation = useLocation();
+  const dispatch: (action: AppAction) => unknown = useDispatch();
+  dispatch({ type: 'userChangedUrl', payload: location });
+  return <>{children}</>;
+});
+
 interface RouterProps {
   basename: string;
   store: Store;
@@ -40,25 +49,27 @@ const AppRoot: React.FunctionComponent<RouterProps> = React.memo(({ basename, st
   <Provider store={store}>
     <I18nProvider>
       <BrowserRouter basename={basename}>
-        <Switch>
-          <Route
-            exact
-            path="/"
-            render={() => (
-              <h1 data-test-subj="welcomeTitle">
-                <FormattedMessage id="xpack.endpoint.welcomeTitle" defaultMessage="Hello World" />
-              </h1>
-            )}
-          />
-          <Route path="/management" component={ManagementList} />
-          <Route path="/alerts" component={AlertIndex} />
-          <Route path="/policy" exact component={PolicyList} />
-          <Route
-            render={() => (
-              <FormattedMessage id="xpack.endpoint.notFound" defaultMessage="Page Not Found" />
-            )}
-          />
-        </Switch>
+        <RouteCapture>
+          <Switch>
+            <Route
+              exact
+              path="/"
+              render={() => (
+                <h1 data-test-subj="welcomeTitle">
+                  <FormattedMessage id="xpack.endpoint.welcomeTitle" defaultMessage="Hello World" />
+                </h1>
+              )}
+            />
+            <Route path="/management" component={ManagementList} />
+            <Route path="/alerts" render={() => <AlertIndex />} />
+            <Route path="/policy" exact component={PolicyList} />
+            <Route
+              render={() => (
+                <FormattedMessage id="xpack.endpoint.notFound" defaultMessage="Page Not Found" />
+              )}
+            />
+          </Switch>
+        </RouteCapture>
       </BrowserRouter>
     </I18nProvider>
   </Provider>
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/action.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/action.ts
index 464a04eff5ebd..a628a95003a7f 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/action.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/action.ts
@@ -4,11 +4,12 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+import { Immutable } from '../../../../../common/types';
 import { AlertListData } from '../../types';
 
 interface ServerReturnedAlertsData {
-  type: 'serverReturnedAlertsData';
-  payload: AlertListData;
+  readonly type: 'serverReturnedAlertsData';
+  readonly payload: Immutable<AlertListData>;
 }
 
 export type AlertAction = ServerReturnedAlertsData;
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/alert_list.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/alert_list.test.ts
new file mode 100644
index 0000000000000..6ba7a34ae81d1
--- /dev/null
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/alert_list.test.ts
@@ -0,0 +1,85 @@
+/*
+ * 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 { Store, createStore, applyMiddleware } from 'redux';
+import { History } from 'history';
+import { alertListReducer } from './reducer';
+import { AlertListState } from '../../types';
+import { alertMiddlewareFactory } from './middleware';
+import { AppAction } from '../action';
+import { coreMock } from 'src/core/public/mocks';
+import { AlertResultList } from '../../../../../common/types';
+import { isOnAlertPage } from './selectors';
+import { createBrowserHistory } from 'history';
+
+describe('alert list tests', () => {
+  let store: Store<AlertListState, AppAction>;
+  let coreStart: ReturnType<typeof coreMock.createStart>;
+  let history: History<never>;
+  beforeEach(() => {
+    coreStart = coreMock.createStart();
+    history = createBrowserHistory();
+    const middleware = alertMiddlewareFactory(coreStart);
+    store = createStore(alertListReducer, applyMiddleware(middleware));
+  });
+  describe('when the user navigates to the alert list page', () => {
+    beforeEach(() => {
+      coreStart.http.get.mockImplementation(async () => {
+        const response: AlertResultList = {
+          alerts: [
+            {
+              '@timestamp': new Date(1542341895000),
+              agent: {
+                id: 'ced9c68e-b94a-4d66-bb4c-6106514f0a2f',
+                version: '3.0.0',
+              },
+              event: {
+                action: 'open',
+              },
+              file_classification: {
+                malware_classification: {
+                  score: 3,
+                },
+              },
+              host: {
+                hostname: 'HD-c15-bc09190a',
+                ip: '10.179.244.14',
+                os: {
+                  name: 'Windows',
+                },
+              },
+              thread: {},
+            },
+          ],
+          total: 1,
+          request_page_size: 10,
+          request_page_index: 0,
+          result_from_index: 0,
+        };
+        return response;
+      });
+
+      // Simulates user navigating to the /alerts page
+      store.dispatch({
+        type: 'userChangedUrl',
+        payload: {
+          ...history.location,
+          pathname: '/alerts',
+        },
+      });
+    });
+
+    it("should recognize it's on the alert list page", () => {
+      const actual = isOnAlertPage(store.getState());
+      expect(actual).toBe(true);
+    });
+
+    it('should return alertListData', () => {
+      const actualResponseLength = store.getState().alerts.length;
+      expect(actualResponseLength).toEqual(1);
+    });
+  });
+});
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/alert_list_pagination.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/alert_list_pagination.test.ts
new file mode 100644
index 0000000000000..77708a3c77e2b
--- /dev/null
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/alert_list_pagination.test.ts
@@ -0,0 +1,98 @@
+/*
+ * 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 { Store, createStore, applyMiddleware } from 'redux';
+import { History } from 'history';
+import { alertListReducer } from './reducer';
+import { AlertListState } from '../../types';
+import { alertMiddlewareFactory } from './middleware';
+import { AppAction } from '../action';
+import { coreMock } from 'src/core/public/mocks';
+import { createBrowserHistory } from 'history';
+import {
+  urlFromNewPageSizeParam,
+  paginationDataFromUrl,
+  urlFromNewPageIndexParam,
+} from './selectors';
+
+describe('alert list pagination', () => {
+  let store: Store<AlertListState, AppAction>;
+  let coreStart: ReturnType<typeof coreMock.createStart>;
+  let history: History<never>;
+  beforeEach(() => {
+    coreStart = coreMock.createStart();
+    history = createBrowserHistory();
+    const middleware = alertMiddlewareFactory(coreStart);
+    store = createStore(alertListReducer, applyMiddleware(middleware));
+  });
+  describe('when the user navigates to the alert list page', () => {
+    describe('when a new page size is passed', () => {
+      beforeEach(() => {
+        const urlPageSizeSelector = urlFromNewPageSizeParam(store.getState());
+        history.push(urlPageSizeSelector(1));
+        store.dispatch({ type: 'userChangedUrl', payload: history.location });
+      });
+      it('should modify the url correctly', () => {
+        const actualPaginationQuery = paginationDataFromUrl(store.getState());
+        expect(actualPaginationQuery).toMatchInlineSnapshot(`
+          Object {
+            "page_size": "1",
+          }
+        `);
+      });
+
+      describe('and then a new page index is passed', () => {
+        beforeEach(() => {
+          const urlPageIndexSelector = urlFromNewPageIndexParam(store.getState());
+          history.push(urlPageIndexSelector(1));
+          store.dispatch({ type: 'userChangedUrl', payload: history.location });
+        });
+        it('should modify the url in the correct order', () => {
+          const actualPaginationQuery = paginationDataFromUrl(store.getState());
+          expect(actualPaginationQuery).toMatchInlineSnapshot(`
+            Object {
+              "page_index": "1",
+              "page_size": "1",
+            }
+          `);
+        });
+      });
+    });
+
+    describe('when a new page index is passed', () => {
+      beforeEach(() => {
+        const urlPageIndexSelector = urlFromNewPageIndexParam(store.getState());
+        history.push(urlPageIndexSelector(1));
+        store.dispatch({ type: 'userChangedUrl', payload: history.location });
+      });
+      it('should modify the url correctly', () => {
+        const actualPaginationQuery = paginationDataFromUrl(store.getState());
+        expect(actualPaginationQuery).toMatchInlineSnapshot(`
+          Object {
+            "page_index": "1",
+          }
+        `);
+      });
+
+      describe('and then a new page size is passed', () => {
+        beforeEach(() => {
+          const urlPageSizeSelector = urlFromNewPageSizeParam(store.getState());
+          history.push(urlPageSizeSelector(1));
+          store.dispatch({ type: 'userChangedUrl', payload: history.location });
+        });
+        it('should modify the url correctly and reset index to `0`', () => {
+          const actualPaginationQuery = paginationDataFromUrl(store.getState());
+          expect(actualPaginationQuery).toMatchInlineSnapshot(`
+            Object {
+              "page_index": "0",
+              "page_size": "1",
+            }
+          `);
+        });
+      });
+    });
+  });
+});
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts
index 4a7fac147852b..059507c8f0658 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts
@@ -4,19 +4,19 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { parse } from 'query-string';
-import { HttpFetchQuery } from 'src/core/public';
+import { HttpFetchQuery } from 'kibana/public';
+import { AlertResultList } from '../../../../../common/types';
 import { AppAction } from '../action';
-import { MiddlewareFactory, AlertListData } from '../../types';
-
-export const alertMiddlewareFactory: MiddlewareFactory = coreStart => {
-  const qp = parse(window.location.search.slice(1), { sort: false });
+import { MiddlewareFactory, AlertListState } from '../../types';
+import { isOnAlertPage, paginationDataFromUrl } from './selectors';
 
+export const alertMiddlewareFactory: MiddlewareFactory<AlertListState> = coreStart => {
   return api => next => async (action: AppAction) => {
     next(action);
-    if (action.type === 'userNavigatedToPage' && action.payload === 'alertsPage') {
-      const response: AlertListData = await coreStart.http.get('/api/endpoint/alerts', {
-        query: qp as HttpFetchQuery,
+    const state = api.getState();
+    if (action.type === 'userChangedUrl' && isOnAlertPage(state)) {
+      const response: AlertResultList = await coreStart.http.get(`/api/endpoint/alerts`, {
+        query: paginationDataFromUrl(state) as HttpFetchQuery,
       });
       api.dispatch({ type: 'serverReturnedAlertsData', payload: response });
     }
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/reducer.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/reducer.ts
index de79476245d29..6369bb9fb2d0d 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/reducer.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/reducer.ts
@@ -15,6 +15,7 @@ const initialState = (): AlertListState => {
     request_page_index: 0,
     result_from_index: 0,
     total: 0,
+    location: undefined,
   };
 };
 
@@ -27,6 +28,11 @@ export const alertListReducer: Reducer<AlertListState, AppAction> = (
       ...state,
       ...action.payload,
     };
+  } else if (action.type === 'userChangedUrl') {
+    return {
+      ...state,
+      location: action.payload,
+    };
   }
 
   return state;
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts
index 51903a0a641e8..6ad053888729c 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts
@@ -4,6 +4,76 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+import qs from 'querystring';
 import { AlertListState } from '../../types';
 
+/**
+ * Returns the Alert Data array from state
+ */
 export const alertListData = (state: AlertListState) => state.alerts;
+
+/**
+ * Returns the alert list pagination data from state
+ */
+export const alertListPagination = (state: AlertListState) => {
+  return {
+    pageIndex: state.request_page_index,
+    pageSize: state.request_page_size,
+    resultFromIndex: state.result_from_index,
+    total: state.total,
+  };
+};
+
+/**
+ * Returns a boolean based on whether or not the user is on the alerts page
+ */
+export const isOnAlertPage = (state: AlertListState): boolean => {
+  return state.location ? state.location.pathname === '/alerts' : false;
+};
+
+/**
+ * Returns the query object received from parsing the URL query params
+ */
+export const paginationDataFromUrl = (state: AlertListState): qs.ParsedUrlQuery => {
+  if (state.location) {
+    // Removes the `?` from the beginning of query string if it exists
+    const query = qs.parse(state.location.search.slice(1));
+    return {
+      ...(query.page_size ? { page_size: query.page_size } : {}),
+      ...(query.page_index ? { page_index: query.page_index } : {}),
+    };
+  } else {
+    return {};
+  }
+};
+
+/**
+ * Returns a function that takes in a new page size and returns a new query param string
+ */
+export const urlFromNewPageSizeParam: (
+  state: AlertListState
+) => (newPageSize: number) => string = state => {
+  return newPageSize => {
+    const urlPaginationData = paginationDataFromUrl(state);
+    urlPaginationData.page_size = newPageSize.toString();
+
+    // Only set the url back to page zero if the user has changed the page index already
+    if (urlPaginationData.page_index !== undefined) {
+      urlPaginationData.page_index = '0';
+    }
+    return '?' + qs.stringify(urlPaginationData);
+  };
+};
+
+/**
+ * Returns a function that takes in a new page index and returns a new query param string
+ */
+export const urlFromNewPageIndexParam: (
+  state: AlertListState
+) => (newPageIndex: number) => string = state => {
+  return newPageIndex => {
+    const urlPaginationData = paginationDataFromUrl(state);
+    urlPaginationData.page_index = newPageIndex.toString();
+    return '?' + qs.stringify(urlPaginationData);
+  };
+};
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts
index 8fe61ae01d319..3aeeeaf1c09e2 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts
@@ -53,7 +53,6 @@ export const appStoreFactory = (coreStart: CoreStart): Store => {
     appReducer,
     composeWithReduxDevTools(
       applyMiddleware(
-        alertMiddlewareFactory(coreStart),
         substateMiddlewareFactory(
           globalState => globalState.managementList,
           managementMiddlewareFactory(coreStart)
@@ -61,6 +60,10 @@ export const appStoreFactory = (coreStart: CoreStart): Store => {
         substateMiddlewareFactory(
           globalState => globalState.policyList,
           policyListMiddlewareFactory(coreStart)
+        ),
+        substateMiddlewareFactory(
+          globalState => globalState.alertList,
+          alertMiddlewareFactory(coreStart)
         )
       )
     )
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/routing/action.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/routing/action.ts
index 9080af8c91817..c7e9970e58c30 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/routing/action.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/routing/action.ts
@@ -4,7 +4,8 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { PageId } from '../../../../../common/types';
+import { PageId, Immutable } from '../../../../../common/types';
+import { EndpointAppLocation } from '../alerts';
 
 interface UserNavigatedToPage {
   readonly type: 'userNavigatedToPage';
@@ -16,4 +17,9 @@ interface UserNavigatedFromPage {
   readonly payload: PageId;
 }
 
-export type RoutingAction = UserNavigatedToPage | UserNavigatedFromPage;
+interface UserChangedUrl {
+  readonly type: 'userChangedUrl';
+  readonly payload: Immutable<EndpointAppLocation>;
+}
+
+export type RoutingAction = UserNavigatedToPage | UserNavigatedFromPage | UserChangedUrl;
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/selectors.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/selectors.ts
deleted file mode 100644
index 2766707271cde..0000000000000
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/selectors.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { GlobalState } from '../types';
-import * as alertListSelectors from './alerts/selectors';
-
-export const alertListData = composeSelectors(
-  alertListStateSelector,
-  alertListSelectors.alertListData
-);
-
-/**
- * Returns the alert list state from within Global State
- */
-function alertListStateSelector(state: GlobalState) {
-  return state.alertList;
-}
-
-/**
- * Calls the `secondSelector` with the result of the `selector`. Use this when re-exporting a
- * concern-specific selector. `selector` should return the concern-specific state.
- */
-function composeSelectors<OuterState, InnerState, ReturnValue>(
-  selector: (state: OuterState) => InnerState,
-  secondSelector: (state: InnerState) => ReturnValue
-): (state: OuterState) => ReturnValue {
-  return state => secondSelector(selector(state));
-}
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/types.ts b/x-pack/plugins/endpoint/public/applications/endpoint/types.ts
index 6b20012592fd9..d07521d09a119 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/types.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/types.ts
@@ -8,7 +8,7 @@ import { Dispatch, MiddlewareAPI } from 'redux';
 import { CoreStart } from 'kibana/public';
 import { EndpointMetadata } from '../../../common/types';
 import { AppAction } from './store/action';
-import { AlertResultList } from '../../../common/types';
+import { AlertResultList, Immutable } from '../../../common/types';
 
 export type MiddlewareFactory<S = GlobalState> = (
   coreStart: CoreStart
@@ -63,8 +63,6 @@ export interface GlobalState {
   readonly policyList: PolicyListState;
 }
 
-export type AlertListData = AlertResultList;
-export type AlertListState = AlertResultList;
 export type CreateStructuredSelector = <
   SelectorMap extends { [key: string]: (...args: never[]) => unknown }
 >(
@@ -74,3 +72,16 @@ export type CreateStructuredSelector = <
 ) => {
   [Key in keyof SelectorMap]: ReturnType<SelectorMap[Key]>;
 };
+
+export interface EndpointAppLocation {
+  pathname: string;
+  search: string;
+  state: never;
+  hash: string;
+  key?: string;
+}
+
+export type AlertListData = AlertResultList;
+export type AlertListState = Immutable<AlertResultList> & {
+  readonly location?: Immutable<EndpointAppLocation>;
+};
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/hooks/use_alerts_selector.ts b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/hooks/use_alerts_selector.ts
new file mode 100644
index 0000000000000..d3962f908757c
--- /dev/null
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/hooks/use_alerts_selector.ts
@@ -0,0 +1,12 @@
+/*
+ * 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 { useSelector } from 'react-redux';
+import { GlobalState, AlertListState } from '../../../types';
+
+export function useAlertListSelector<TSelected>(selector: (state: AlertListState) => TSelected) {
+  return useSelector((state: GlobalState) => selector(state.alertList));
+}
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx
index 8c32426dcc868..045b82200b11b 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx
@@ -4,41 +4,94 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { memo, useState, useMemo } from 'react';
+import { memo, useState, useMemo, useCallback } from 'react';
 import React from 'react';
-import { EuiDataGrid } from '@elastic/eui';
-import { useSelector } from 'react-redux';
+import { EuiDataGrid, EuiDataGridColumn, EuiPage, EuiPageBody, EuiPageContent } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
-import * as selectors from '../../store/selectors';
-import { usePageId } from '../use_page_id';
+import { useHistory } from 'react-router-dom';
+import * as selectors from '../../store/alerts/selectors';
+import { useAlertListSelector } from './hooks/use_alerts_selector';
 
 export const AlertIndex = memo(() => {
-  usePageId('alertsPage');
+  const history = useHistory();
 
-  const columns: Array<{ id: string }> = useMemo(() => {
+  const columns: EuiDataGridColumn[] = useMemo(() => {
     return [
-      { id: 'alert_type' },
-      { id: 'event_type' },
-      { id: 'os' },
-      { id: 'ip_address' },
-      { id: 'host_name' },
-      { id: 'timestamp' },
-      { id: 'archived' },
-      { id: 'malware_score' },
+      {
+        id: 'alert_type',
+        display: i18n.translate('xpack.endpoint.application.endpoint.alerts.alertType', {
+          defaultMessage: 'Alert Type',
+        }),
+      },
+      {
+        id: 'event_type',
+        display: i18n.translate('xpack.endpoint.application.endpoint.alerts.eventType', {
+          defaultMessage: 'Event Type',
+        }),
+      },
+      {
+        id: 'os',
+        display: i18n.translate('xpack.endpoint.application.endpoint.alerts.os', {
+          defaultMessage: 'OS',
+        }),
+      },
+      {
+        id: 'ip_address',
+        display: i18n.translate('xpack.endpoint.application.endpoint.alerts.ipAddress', {
+          defaultMessage: 'IP Address',
+        }),
+      },
+      {
+        id: 'host_name',
+        display: i18n.translate('xpack.endpoint.application.endpoint.alerts.hostName', {
+          defaultMessage: 'Host Name',
+        }),
+      },
+      {
+        id: 'timestamp',
+        display: i18n.translate('xpack.endpoint.application.endpoint.alerts.timestamp', {
+          defaultMessage: 'Timestamp',
+        }),
+      },
+      {
+        id: 'archived',
+        display: i18n.translate('xpack.endpoint.application.endpoint.alerts.archived', {
+          defaultMessage: 'Archived',
+        }),
+      },
+      {
+        id: 'malware_score',
+        display: i18n.translate('xpack.endpoint.application.endpoint.alerts.malwareScore', {
+          defaultMessage: 'Malware Score',
+        }),
+      },
     ];
   }, []);
 
-  const [visibleColumns, setVisibleColumns] = useState(() => columns.map(({ id }) => id));
+  const { pageIndex, pageSize, total } = useAlertListSelector(selectors.alertListPagination);
+  const urlFromNewPageSizeParam = useAlertListSelector(selectors.urlFromNewPageSizeParam);
+  const urlFromNewPageIndexParam = useAlertListSelector(selectors.urlFromNewPageIndexParam);
+  const alertListData = useAlertListSelector(selectors.alertListData);
+
+  const onChangeItemsPerPage = useCallback(
+    newPageSize => history.push(urlFromNewPageSizeParam(newPageSize)),
+    [history, urlFromNewPageSizeParam]
+  );
+
+  const onChangePage = useCallback(
+    newPageIndex => history.push(urlFromNewPageIndexParam(newPageIndex)),
+    [history, urlFromNewPageIndexParam]
+  );
 
-  const json = useSelector(selectors.alertListData);
+  const [visibleColumns, setVisibleColumns] = useState(() => columns.map(({ id }) => id));
 
   const renderCellValue = useMemo(() => {
     return ({ rowIndex, columnId }: { rowIndex: number; columnId: string }) => {
-      if (rowIndex > json.length) {
+      if (rowIndex > total) {
         return null;
       }
 
-      const row = json[rowIndex];
+      const row = alertListData[rowIndex % pageSize];
 
       if (columnId === 'alert_type') {
         return i18n.translate(
@@ -64,23 +117,36 @@ export const AlertIndex = memo(() => {
       }
       return null;
     };
-  }, [json]);
+  }, [alertListData, pageSize, total]);
+
+  const pagination = useMemo(() => {
+    return {
+      pageIndex,
+      pageSize,
+      pageSizeOptions: [10, 20, 50],
+      onChangeItemsPerPage,
+      onChangePage,
+    };
+  }, [onChangeItemsPerPage, onChangePage, pageIndex, pageSize]);
 
   return (
-    <EuiDataGrid
-      aria-label="Alert List"
-      rowCount={json.length}
-      // Required. Sets up three columns, the last of which has a custom schema we later define down below.
-      // The second column B won't allow clicking in to see the content in a popup.
-      // The first column defines an starting width of 150px and prevents the user from resizing it
-      columns={columns}
-      // This allows you to initially hide columns. Users can still turn them on.
-      columnVisibility={{
-        visibleColumns,
-        setVisibleColumns,
-      }}
-      // Often used in combination with useEffect() to dynamically change the render.
-      renderCellValue={renderCellValue}
-    />
+    <EuiPage data-test-subj="alertListPage">
+      <EuiPageBody>
+        <EuiPageContent>
+          <EuiDataGrid
+            aria-label="Alert List"
+            rowCount={total}
+            columns={columns}
+            columnVisibility={{
+              visibleColumns,
+              setVisibleColumns,
+            }}
+            renderCellValue={renderCellValue}
+            pagination={pagination}
+            data-test-subj="alertListGrid"
+          />
+        </EuiPageContent>
+      </EuiPageBody>
+    </EuiPage>
   );
 });
diff --git a/x-pack/test/functional/apps/endpoint/alert_list.ts b/x-pack/test/functional/apps/endpoint/alert_list.ts
new file mode 100644
index 0000000000000..089fa487ef1b8
--- /dev/null
+++ b/x-pack/test/functional/apps/endpoint/alert_list.ts
@@ -0,0 +1,25 @@
+/*
+ * 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 { FtrProviderContext } from '../../ftr_provider_context';
+
+export default function({ getPageObjects, getService }: FtrProviderContext) {
+  const pageObjects = getPageObjects(['common', 'endpoint']);
+  const testSubjects = getService('testSubjects');
+
+  describe('Endpoint Alert List', function() {
+    this.tags(['ciGroup7']);
+    before(async () => {
+      await pageObjects.common.navigateToUrlWithBrowserHistory('endpoint', '/alerts');
+    });
+
+    it('loads the Alert List Page', async () => {
+      await testSubjects.existOrFail('alertListPage');
+    });
+    it('includes Alert list data grid', async () => {
+      await testSubjects.existOrFail('alertListGrid');
+    });
+  });
+}
diff --git a/x-pack/test/functional/apps/endpoint/index.ts b/x-pack/test/functional/apps/endpoint/index.ts
index 0ea9344a67aba..818c040f824d9 100644
--- a/x-pack/test/functional/apps/endpoint/index.ts
+++ b/x-pack/test/functional/apps/endpoint/index.ts
@@ -13,5 +13,6 @@ export default function({ loadTestFile }: FtrProviderContext) {
     loadTestFile(require.resolve('./landing_page'));
     loadTestFile(require.resolve('./management'));
     loadTestFile(require.resolve('./policy_list'));
+    loadTestFile(require.resolve('./alert_list'));
   });
 }

From 911981d8b5761f56dd4f157b45d67da69cca4f37 Mon Sep 17 00:00:00 2001
From: Matthew Kime <matt@mattki.me>
Date: Tue, 18 Feb 2020 15:41:10 -0600
Subject: [PATCH 043/174] Index pattern management - typescript components
 (#56987)

* Index pattern management - typescript components
---
 .../create_index_pattern_wizard.test.js.snap  |   0
 .../empty_state.test.tsx.snap}                |   0
 ...pty_state.test.js => empty_state.test.tsx} |   8 +-
 .../{empty_state.js => empty_state.tsx}       |   7 +-
 .../empty_state/{index.js => index.ts}        |   0
 .../header.test.tsx.snap}                     |   4 +-
 .../header.test.js => header.test.tsx}        |  15 ++-
 .../header/{header.js => header.tsx}          |  11 +-
 .../components/header/{index.js => index.ts}  |   0
 .../loading_state.test.tsx.snap}              |   0
 .../loading_state/{index.js => index.ts}      |   0
 ...g_state.test.js => loading_state.test.tsx} |   0
 .../{loading_state.js => loading_state.tsx}   |   0
 .../step_index_pattern.test.tsx.snap}         |   0
 .../header.test.tsx.snap}                     |   0
 .../header.test.js => header.test.tsx}        |   4 +-
 .../header/{header.js => header.tsx}          |  12 +-
 .../components/header/{index.js => index.ts}  |   0
 .../indices_list.test.tsx.snap}               |   0
 .../indices_list/{index.js => index.ts}       |   0
 ...ces_list.test.js => indices_list.test.tsx} |   4 +-
 .../{indices_list.js => indices_list.tsx}     |  37 +++---
 .../loading_indices.test.tsx.snap}            |   0
 .../loading_indices/{index.js => index.ts}    |   0
 ...dices.test.js => loading_indices.test.tsx} |   0
 ...loading_indices.js => loading_indices.tsx} |   0
 .../status_message.test.tsx.snap}             |   0
 .../status_message/{index.js => index.ts}     |   0
 ...essage.test.js => status_message.test.tsx} |  71 ++++++++--
 .../{status_message.js => status_message.tsx} |  20 ++-
 .../step_index_pattern/{index.js => index.ts} |   0
 ...rn.test.js => step_index_pattern.test.tsx} |  74 ++++++-----
 ...ndex_pattern.js => step_index_pattern.tsx} | 124 +++++++++++-------
 .../step_time_field.test.tsx.snap}            |  45 +------
 .../{action_buttons.js => action_buttons.tsx} |  10 +-
 .../action_buttons/{index.js => index.ts}     |   0
 .../advanced_options.test.js.snap             |  37 ------
 .../advanced_options.test.tsx.snap            |  69 ++++++++++
 ...ions.test.js => advanced_options.test.tsx} |   4 +-
 ...vanced_options.js => advanced_options.tsx} |   9 +-
 .../advanced_options/{index.js => index.ts}   |   0
 .../header.test.tsx.snap}                     |   2 +-
 .../header.test.js => header.test.tsx}        |   2 +-
 .../header/{header.js => header.tsx}          |   7 +-
 .../components/header/{index.js => index.ts}  |   0
 .../time_field.test.tsx.snap}                 |   0
 .../time_field/{index.js => index.ts}         |   0
 ...time_field.test.js => time_field.test.tsx} |   0
 .../{time_field.js => time_field.tsx}         |  11 +-
 .../step_time_field/{index.js => index.ts}    |   0
 ...field.test.js => step_time_field.test.tsx} |  52 ++++----
 ...step_time_field.js => step_time_field.tsx} | 115 +++++++++-------
 .../create_index_pattern_wizard.js            |   1 +
 .../create_index_pattern_wizard.test.js       |  14 +-
 .../create_index_pattern_wizard/index.js      |   1 +
 .../lib/get_matched_indices.test.ts           |   3 +-
 .../{__jest__ => }/render.test.js             |   2 +-
 .../create_index_pattern_wizard/types.ts      |   7 +-
 .../creation/config.ts                        |   3 +-
 .../management_app/components/field/field.tsx |   1 -
 .../components/search/search.test.tsx         |   1 +
 .../index_patterns/index_pattern.ts           |   2 +-
 src/plugins/data/public/mocks.ts              |   8 +-
 .../query_string_input.test.tsx.snap          |  24 +++-
 64 files changed, 509 insertions(+), 312 deletions(-)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/{__jest__ => }/__snapshots__/create_index_pattern_wizard.test.js.snap (100%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/empty_state/{__jest__/__snapshots__/empty_state.test.js.snap => __snapshots__/empty_state.test.tsx.snap} (100%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/empty_state/{__jest__/empty_state.test.js => empty_state.test.tsx} (84%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/empty_state/{empty_state.js => empty_state.tsx} (94%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/empty_state/{index.js => index.ts} (100%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/header/{__jest__/__snapshots__/header.test.js.snap => __snapshots__/header.test.tsx.snap} (96%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/header/{__jest__/header.test.js => header.test.tsx} (79%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/header/{header.js => header.tsx} (92%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/header/{index.js => index.ts} (100%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/loading_state/{__jest__/__snapshots__/loading_state.test.js.snap => __snapshots__/loading_state.test.tsx.snap} (100%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/loading_state/{index.js => index.ts} (100%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/loading_state/{__jest__/loading_state.test.js => loading_state.test.tsx} (100%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/loading_state/{loading_state.js => loading_state.tsx} (100%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/{__jest__/__snapshots__/step_index_pattern.test.js.snap => __snapshots__/step_index_pattern.test.tsx.snap} (100%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/{__jest__/__snapshots__/header.test.js.snap => __snapshots__/header.test.tsx.snap} (100%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/{__jest__/header.test.js => header.test.tsx} (96%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/{header.js => header.tsx} (92%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/{index.js => index.ts} (100%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/indices_list/{__jest__/__snapshots__/indices_list.test.js.snap => __snapshots__/indices_list.test.tsx.snap} (100%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/indices_list/{index.js => index.ts} (100%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/indices_list/{__jest__/indices_list.test.js => indices_list.test.tsx} (95%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/indices_list/{indices_list.js => indices_list.tsx} (87%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/{__jest__/__snapshots__/loading_indices.test.js.snap => __snapshots__/loading_indices.test.tsx.snap} (100%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/{index.js => index.ts} (100%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/{__jest__/loading_indices.test.js => loading_indices.test.tsx} (100%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/{loading_indices.js => loading_indices.tsx} (100%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/status_message/{__jest__/__snapshots__/status_message.test.js.snap => __snapshots__/status_message.test.tsx.snap} (100%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/status_message/{index.js => index.ts} (100%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/status_message/{__jest__/status_message.test.js => status_message.test.tsx} (53%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/status_message/{status_message.js => status_message.tsx} (91%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/{index.js => index.ts} (100%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/{__jest__/step_index_pattern.test.js => step_index_pattern.test.tsx} (67%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/{step_index_pattern.js => step_index_pattern.tsx} (74%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/{__jest__/__snapshots__/step_time_field.test.js.snap => __snapshots__/step_time_field.test.tsx.snap} (92%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/action_buttons/{action_buttons.js => action_buttons.tsx} (89%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/action_buttons/{index.js => index.ts} (100%)
 delete mode 100644 src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/advanced_options/__jest__/__snapshots__/advanced_options.test.js.snap
 create mode 100644 src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/advanced_options/__snapshots__/advanced_options.test.tsx.snap
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/advanced_options/{__jest__/advanced_options.test.js => advanced_options.test.tsx} (95%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/advanced_options/{advanced_options.js => advanced_options.tsx} (90%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/advanced_options/{index.js => index.ts} (100%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/header/{__jest__/__snapshots__/header.test.js.snap => __snapshots__/header.test.tsx.snap} (94%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/header/{__jest__/header.test.js => header.test.tsx} (92%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/header/{header.js => header.tsx} (90%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/header/{index.js => index.ts} (100%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/time_field/{__jest__/__snapshots__/time_field.test.js.snap => __snapshots__/time_field.test.tsx.snap} (100%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/time_field/{index.js => index.ts} (100%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/time_field/{__jest__/time_field.test.js => time_field.test.tsx} (100%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/time_field/{time_field.js => time_field.tsx} (92%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/{index.js => index.ts} (100%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/{__jest__/step_time_field.test.js => step_time_field.test.tsx} (83%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/{step_time_field.js => step_time_field.tsx} (71%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/{__jest__ => }/create_index_pattern_wizard.test.js (91%)
 rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/{__jest__ => }/render.test.js (98%)

diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/__jest__/__snapshots__/create_index_pattern_wizard.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/__snapshots__/create_index_pattern_wizard.test.js.snap
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/__jest__/__snapshots__/create_index_pattern_wizard.test.js.snap
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/__snapshots__/create_index_pattern_wizard.test.js.snap
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/empty_state/__jest__/__snapshots__/empty_state.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/empty_state/__snapshots__/empty_state.test.tsx.snap
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/empty_state/__jest__/__snapshots__/empty_state.test.js.snap
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/empty_state/__snapshots__/empty_state.test.tsx.snap
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/empty_state/__jest__/empty_state.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/empty_state/empty_state.test.tsx
similarity index 84%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/empty_state/__jest__/empty_state.test.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/empty_state/empty_state.test.tsx
index 92b54a4000e46..5b1f9f546e250 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/empty_state/__jest__/empty_state.test.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/empty_state/empty_state.test.tsx
@@ -24,9 +24,7 @@ import sinon from 'sinon';
 
 describe('EmptyState', () => {
   it('should render normally', () => {
-    const component = shallow(
-      <EmptyState loadingDataDocUrl="http://www.elastic.co" onRefresh={() => {}} />
-    );
+    const component = shallow(<EmptyState onRefresh={() => {}} />);
 
     expect(component).toMatchSnapshot();
   });
@@ -36,9 +34,7 @@ describe('EmptyState', () => {
       it('is called when refresh button is clicked', () => {
         const onRefreshHandler = sinon.stub();
 
-        const component = shallow(
-          <EmptyState loadingDataDocUrl="http://www.elastic.co" onRefresh={onRefreshHandler} />
-        );
+        const component = shallow(<EmptyState onRefresh={onRefreshHandler} />);
 
         component.find('[data-test-subj="refreshIndicesButton"]').simulate('click');
 
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/empty_state/empty_state.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/empty_state/empty_state.tsx
similarity index 94%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/empty_state/empty_state.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/empty_state/empty_state.tsx
index 1f81fb7361686..676f4d38f409b 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/empty_state/empty_state.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/empty_state/empty_state.tsx
@@ -18,13 +18,12 @@
  */
 
 import React from 'react';
-import PropTypes from 'prop-types';
 
 import { EuiCallOut, EuiTextColor, EuiLink, EuiButton } from '@elastic/eui';
 
 import { FormattedMessage } from '@kbn/i18n/react';
 
-export const EmptyState = ({ onRefresh }) => (
+export const EmptyState = ({ onRefresh }: { onRefresh: () => void }) => (
   <div>
     <EuiCallOut
       color="warning"
@@ -82,7 +81,3 @@ export const EmptyState = ({ onRefresh }) => (
     </EuiCallOut>
   </div>
 );
-
-EmptyState.propTypes = {
-  onRefresh: PropTypes.func.isRequired,
-};
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/empty_state/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/empty_state/index.ts
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/empty_state/index.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/empty_state/index.ts
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/header/__jest__/__snapshots__/header.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/header/__snapshots__/header.test.tsx.snap
similarity index 96%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/header/__jest__/__snapshots__/header.test.js.snap
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/header/__snapshots__/header.test.tsx.snap
index a4998fc975372..a6b22ebf32a43 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/header/__jest__/__snapshots__/header.test.js.snap
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/header/__snapshots__/header.test.tsx.snap
@@ -64,7 +64,7 @@ exports[`Header should render normally 1`] = `
         id="kbn.management.createIndexPatternHeader"
         values={
           Object {
-            "indexPatternName": undefined,
+            "indexPatternName": "test index pattern",
           }
         }
       />
@@ -109,7 +109,7 @@ exports[`Header should render without including system indices 1`] = `
         id="kbn.management.createIndexPatternHeader"
         values={
           Object {
-            "indexPatternName": undefined,
+            "indexPatternName": "test index pattern",
           }
         }
       />
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/header/__jest__/header.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/header/header.test.tsx
similarity index 79%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/header/__jest__/header.test.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/header/header.test.tsx
index c7cb3d4631b94..06b8b38e2d8bc 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/header/__jest__/header.test.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/header/header.test.tsx
@@ -22,9 +22,14 @@ import { Header } from '../header';
 import { shallowWithI18nProvider } from 'test_utils/enzyme_helpers';
 
 describe('Header', () => {
+  const indexPatternName = 'test index pattern';
   it('should render normally', () => {
     const component = shallowWithI18nProvider(
-      <Header isIncludingSystemIndices={true} onChangeIncludingSystemIndices={() => {}} />
+      <Header
+        indexPatternName={indexPatternName}
+        isIncludingSystemIndices={true}
+        onChangeIncludingSystemIndices={() => {}}
+      />
     );
 
     expect(component).toMatchSnapshot();
@@ -32,7 +37,11 @@ describe('Header', () => {
 
   it('should render without including system indices', () => {
     const component = shallowWithI18nProvider(
-      <Header isIncludingSystemIndices={false} onChangeIncludingSystemIndices={() => {}} />
+      <Header
+        indexPatternName={indexPatternName}
+        isIncludingSystemIndices={false}
+        onChangeIncludingSystemIndices={() => {}}
+      />
     );
 
     expect(component).toMatchSnapshot();
@@ -44,7 +53,7 @@ describe('Header', () => {
         isIncludingSystemIndices={false}
         onChangeIncludingSystemIndices={() => {}}
         prompt={<div>Test prompt</div>}
-        indexPatternName="test index pattern"
+        indexPatternName={indexPatternName}
         isBeta={true}
       />
     );
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/header/header.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/header/header.tsx
similarity index 92%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/header/header.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/header/header.tsx
index 3352996368669..928c1ef2b6299 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/header/header.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/header/header.tsx
@@ -36,10 +36,17 @@ import { FormattedMessage } from '@kbn/i18n/react';
 export const Header = ({
   prompt,
   indexPatternName,
-  showSystemIndices,
+  showSystemIndices = false,
   isIncludingSystemIndices,
   onChangeIncludingSystemIndices,
-  isBeta,
+  isBeta = false,
+}: {
+  prompt?: React.ReactNode;
+  indexPatternName: string;
+  showSystemIndices?: boolean;
+  isIncludingSystemIndices: boolean;
+  onChangeIncludingSystemIndices: () => void;
+  isBeta?: boolean;
 }) => (
   <div>
     <EuiTitle>
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/header/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/header/index.ts
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/header/index.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/header/index.ts
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/loading_state/__jest__/__snapshots__/loading_state.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/loading_state/__snapshots__/loading_state.test.tsx.snap
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/loading_state/__jest__/__snapshots__/loading_state.test.js.snap
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/loading_state/__snapshots__/loading_state.test.tsx.snap
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/loading_state/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/loading_state/index.ts
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/loading_state/index.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/loading_state/index.ts
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/loading_state/__jest__/loading_state.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/loading_state/loading_state.test.tsx
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/loading_state/__jest__/loading_state.test.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/loading_state/loading_state.test.tsx
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/loading_state/loading_state.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/loading_state/loading_state.tsx
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/loading_state/loading_state.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/loading_state/loading_state.tsx
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/__jest__/__snapshots__/step_index_pattern.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/__snapshots__/step_index_pattern.test.tsx.snap
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/__jest__/__snapshots__/step_index_pattern.test.js.snap
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/__snapshots__/step_index_pattern.test.tsx.snap
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/__jest__/__snapshots__/header.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/__snapshots__/header.test.tsx.snap
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/__jest__/__snapshots__/header.test.js.snap
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/__snapshots__/header.test.tsx.snap
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/__jest__/header.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/header.test.tsx
similarity index 96%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/__jest__/header.test.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/header.test.tsx
index 534c46ab73ded..f56340d0009be 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/__jest__/header.test.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/header.test.tsx
@@ -27,7 +27,7 @@ describe('Header', () => {
       <Header
         isInputInvalid={false}
         errors={[]}
-        characterList={['%']}
+        characterList={'%'}
         query={'k'}
         onQueryChanged={() => {}}
         goToNextStep={() => {}}
@@ -43,7 +43,7 @@ describe('Header', () => {
       <Header
         isInputInvalid={true}
         errors={['Input is invalid']}
-        characterList={['%']}
+        characterList={'%'}
         query={'%'}
         onQueryChanged={() => {}}
         goToNextStep={() => {}}
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/header.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/header.tsx
similarity index 92%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/header.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/header.tsx
index 8fc039fc4b2cc..f9d86d5d9d53d 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/header.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/header.tsx
@@ -33,7 +33,17 @@ import {
 import { i18n } from '@kbn/i18n';
 import { FormattedMessage } from '@kbn/i18n/react';
 
-export const Header = ({
+interface HeaderProps {
+  isInputInvalid: boolean;
+  errors: any;
+  characterList: string;
+  query: string;
+  onQueryChanged: (e: React.ChangeEvent<HTMLInputElement>) => void;
+  goToNextStep: (query: string) => void;
+  isNextStepDisabled: boolean;
+}
+
+export const Header: React.FC<HeaderProps> = ({
   isInputInvalid,
   errors,
   characterList,
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/index.ts
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/index.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/index.ts
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/indices_list/__jest__/__snapshots__/indices_list.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/indices_list/__snapshots__/indices_list.test.tsx.snap
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/indices_list/__jest__/__snapshots__/indices_list.test.js.snap
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/indices_list/__snapshots__/indices_list.test.tsx.snap
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/indices_list/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/indices_list/index.ts
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/indices_list/index.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/indices_list/index.ts
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/indices_list/__jest__/indices_list.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/indices_list/indices_list.test.tsx
similarity index 95%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/indices_list/__jest__/indices_list.test.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/indices_list/indices_list.test.tsx
index 00ae40c9d513b..d8a1d1a0ab72f 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/indices_list/__jest__/indices_list.test.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/indices_list/indices_list.test.tsx
@@ -36,7 +36,7 @@ describe('IndicesList', () => {
   it('should change pages', () => {
     const component = shallow(<IndicesList indices={indices} query="" />);
 
-    const instance = component.instance();
+    const instance = component.instance() as IndicesList;
 
     component.setState({ perPage: 1 });
     instance.onChangePage(1);
@@ -48,7 +48,7 @@ describe('IndicesList', () => {
   it('should change per page', () => {
     const component = shallow(<IndicesList indices={indices} query="" />);
 
-    const instance = component.instance();
+    const instance = component.instance() as IndicesList;
     instance.onChangePerPage(1);
     component.update();
 
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/indices_list/indices_list.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/indices_list/indices_list.tsx
similarity index 87%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/indices_list/indices_list.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/indices_list/indices_list.tsx
index 6ac086842f246..921a5692d00aa 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/indices_list/indices_list.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/indices_list/indices_list.tsx
@@ -17,9 +17,7 @@
  * under the License.
  */
 
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import { PER_PAGE_INCREMENTS } from '../../../../constants';
+import React from 'react';
 
 import {
   EuiBadge,
@@ -37,17 +35,26 @@ import {
   EuiPopover,
 } from '@elastic/eui';
 
-import { Pager } from '@elastic/eui/lib/services';
+import { Pager } from '@elastic/eui';
 
 import { FormattedMessage } from '@kbn/i18n/react';
+import { PER_PAGE_INCREMENTS } from '../../../../constants';
+import { MatchedIndex, Tag } from '../../../../types';
 
-export class IndicesList extends Component {
-  static propTypes = {
-    indices: PropTypes.array.isRequired,
-    query: PropTypes.string.isRequired,
-  };
+interface IndicesListProps {
+  indices: MatchedIndex[];
+  query: string;
+}
+
+interface IndicesListState {
+  page: number;
+  perPage: number;
+  isPerPageControlOpen: boolean;
+}
 
-  constructor(props) {
+export class IndicesList extends React.Component<IndicesListProps, IndicesListState> {
+  pager: Pager;
+  constructor(props: IndicesListProps) {
     super(props);
 
     this.state = {
@@ -59,7 +66,7 @@ export class IndicesList extends Component {
     this.pager = new Pager(props.indices.length, this.state.perPage, this.state.page);
   }
 
-  UNSAFE_componentWillReceiveProps(nextProps) {
+  UNSAFE_componentWillReceiveProps(nextProps: IndicesListProps) {
     if (nextProps.indices.length !== this.props.indices.length) {
       this.pager.setTotalItems(nextProps.indices.length);
       this.resetPageTo0();
@@ -68,12 +75,12 @@ export class IndicesList extends Component {
 
   resetPageTo0 = () => this.onChangePage(0);
 
-  onChangePage = page => {
+  onChangePage = (page: number) => {
     this.pager.goToPageIndex(page);
     this.setState({ page });
   };
 
-  onChangePerPage = perPage => {
+  onChangePerPage = (perPage: number) => {
     this.pager.setItemsPerPage(perPage);
     this.setState({ perPage });
     this.resetPageTo0();
@@ -147,7 +154,7 @@ export class IndicesList extends Component {
     );
   }
 
-  highlightIndexName(indexName, query) {
+  highlightIndexName(indexName: string, query: string) {
     const queryIdx = indexName.indexOf(query);
     if (!query || queryIdx === -1) {
       return indexName;
@@ -178,7 +185,7 @@ export class IndicesList extends Component {
             {this.highlightIndexName(index.name, queryWithoutWildcard)}
           </EuiTableRowCell>
           <EuiTableRowCell>
-            {index.tags.map(tag => {
+            {index.tags.map((tag: Tag) => {
               return (
                 <EuiBadge key={`index_${key}_tag_${tag.key}`} color="primary">
                   {tag.name}
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/__jest__/__snapshots__/loading_indices.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/__snapshots__/loading_indices.test.tsx.snap
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/__jest__/__snapshots__/loading_indices.test.js.snap
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/__snapshots__/loading_indices.test.tsx.snap
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/index.ts
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/index.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/index.ts
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/__jest__/loading_indices.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/loading_indices.test.tsx
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/__jest__/loading_indices.test.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/loading_indices.test.tsx
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/loading_indices.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/loading_indices.tsx
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/loading_indices.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/loading_indices.tsx
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/status_message/__jest__/__snapshots__/status_message.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/status_message/__snapshots__/status_message.test.tsx.snap
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/status_message/__jest__/__snapshots__/status_message.test.js.snap
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/status_message/__snapshots__/status_message.test.tsx.snap
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/status_message/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/status_message/index.ts
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/status_message/index.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/status_message/index.ts
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/status_message/__jest__/status_message.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/status_message/status_message.test.tsx
similarity index 53%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/status_message/__jest__/status_message.test.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/status_message/status_message.test.tsx
index 301b230b71494..899c21d59c5bc 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/status_message/__jest__/status_message.test.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/status_message/status_message.test.tsx
@@ -21,15 +21,29 @@ import React from 'react';
 import { StatusMessage } from '../status_message';
 import { shallow } from 'enzyme';
 
+const tagsPartial = {
+  tags: [],
+};
+
 const matchedIndices = {
-  allIndices: [{ name: 'kibana' }, { name: 'es' }],
+  allIndices: [
+    { name: 'kibana', ...tagsPartial },
+    { name: 'es', ...tagsPartial },
+  ],
   exactMatchedIndices: [],
-  partialMatchedIndices: [{ name: 'kibana' }],
+  partialMatchedIndices: [{ name: 'kibana', ...tagsPartial }],
 };
 
 describe('StatusMessage', () => {
   it('should render without a query', () => {
-    const component = shallow(<StatusMessage matchedIndices={matchedIndices} query={''} />);
+    const component = shallow(
+      <StatusMessage
+        matchedIndices={matchedIndices}
+        query={''}
+        isIncludingSystemIndices={false}
+        showSystemIndices={false}
+      />
+    );
 
     expect(component).toMatchSnapshot();
   });
@@ -37,16 +51,30 @@ describe('StatusMessage', () => {
   it('should render with exact matches', () => {
     const localMatchedIndices = {
       ...matchedIndices,
-      exactMatchedIndices: [{ name: 'kibana' }],
+      exactMatchedIndices: [{ name: 'kibana', ...tagsPartial }],
     };
 
-    const component = shallow(<StatusMessage matchedIndices={localMatchedIndices} query={'k*'} />);
+    const component = shallow(
+      <StatusMessage
+        matchedIndices={localMatchedIndices}
+        query={'k*'}
+        isIncludingSystemIndices={false}
+        showSystemIndices={false}
+      />
+    );
 
     expect(component).toMatchSnapshot();
   });
 
   it('should render with partial matches', () => {
-    const component = shallow(<StatusMessage matchedIndices={matchedIndices} query={'k'} />);
+    const component = shallow(
+      <StatusMessage
+        matchedIndices={matchedIndices}
+        query={'k'}
+        isIncludingSystemIndices={false}
+        showSystemIndices={false}
+      />
+    );
 
     expect(component).toMatchSnapshot();
   });
@@ -57,14 +85,30 @@ describe('StatusMessage', () => {
       partialMatchedIndices: [],
     };
 
-    const component = shallow(<StatusMessage matchedIndices={localMatchedIndices} query={'k'} />);
+    const component = shallow(
+      <StatusMessage
+        matchedIndices={localMatchedIndices}
+        query={'k'}
+        isIncludingSystemIndices={false}
+        showSystemIndices={false}
+      />
+    );
 
     expect(component).toMatchSnapshot();
   });
 
   it('should show that system indices exist', () => {
     const component = shallow(
-      <StatusMessage matchedIndices={[]} isIncludingSystemIndices={false} query={''} />
+      <StatusMessage
+        matchedIndices={{
+          allIndices: [],
+          exactMatchedIndices: [],
+          partialMatchedIndices: [],
+        }}
+        isIncludingSystemIndices={false}
+        query={''}
+        showSystemIndices={false}
+      />
     );
 
     expect(component).toMatchSnapshot();
@@ -72,7 +116,16 @@ describe('StatusMessage', () => {
 
   it('should show that no indices exist', () => {
     const component = shallow(
-      <StatusMessage matchedIndices={[]} isIncludingSystemIndices={true} query={''} />
+      <StatusMessage
+        matchedIndices={{
+          allIndices: [],
+          exactMatchedIndices: [],
+          partialMatchedIndices: [],
+        }}
+        isIncludingSystemIndices={true}
+        query={''}
+        showSystemIndices={false}
+      />
     );
 
     expect(component).toMatchSnapshot();
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/status_message/status_message.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/status_message/status_message.tsx
similarity index 91%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/status_message/status_message.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/status_message/status_message.tsx
index f78bedf988cd7..ad7f58fa4e879 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/status_message/status_message.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/status_message/status_message.tsx
@@ -22,16 +22,28 @@ import React from 'react';
 import { EuiText, EuiTextColor, EuiIcon } from '@elastic/eui';
 
 import { FormattedMessage } from '@kbn/i18n/react';
+import { MatchedIndex } from '../../../../types';
 
-export const StatusMessage = ({
+interface StatusMessageProps {
+  matchedIndices: {
+    allIndices: MatchedIndex[];
+    exactMatchedIndices: MatchedIndex[];
+    partialMatchedIndices: MatchedIndex[];
+  };
+  isIncludingSystemIndices: boolean;
+  query: string;
+  showSystemIndices: boolean;
+}
+
+export const StatusMessage: React.FC<StatusMessageProps> = ({
   matchedIndices: { allIndices = [], exactMatchedIndices = [], partialMatchedIndices = [] },
   isIncludingSystemIndices,
   query,
-  showSystemIndicies,
+  showSystemIndices,
 }) => {
   let statusIcon;
   let statusMessage;
-  let statusColor;
+  let statusColor: 'default' | 'secondary' | undefined;
 
   const allIndicesLength = allIndices.length;
 
@@ -49,7 +61,7 @@ export const StatusMessage = ({
           />
         </span>
       );
-    } else if (!isIncludingSystemIndices && showSystemIndicies) {
+    } else if (!isIncludingSystemIndices && showSystemIndices) {
       statusMessage = (
         <span>
           <FormattedMessage
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/index.ts
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/index.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/index.ts
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/__jest__/step_index_pattern.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.test.tsx
similarity index 67%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/__jest__/step_index_pattern.test.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.test.tsx
index 85e610bbbf993..25bd36829b6d0 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/__jest__/step_index_pattern.test.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.test.tsx
@@ -20,28 +20,24 @@
 import React from 'react';
 import { StepIndexPattern } from '../step_index_pattern';
 import { shallowWithI18nProvider } from 'test_utils/enzyme_helpers';
-import { Header } from '../components/header';
-
-jest.mock('../../../lib/ensure_minimum_time', () => ({
-  ensureMinimumTime: async promises =>
+import { Header } from './components/header';
+import { IndexPatternCreationConfig } from '../../../../../../../../management/public';
+import { coreMock } from '../../../../../../../../../../core/public/mocks';
+import { dataPluginMock } from '../../../../../../../../../../plugins/data/public/mocks';
+import { SavedObjectsFindResponsePublic } from '../../../../../../../../../../core/public';
+
+jest.mock('../../lib/ensure_minimum_time', () => ({
+  ensureMinimumTime: async (promises: Array<Promise<any>>) =>
     Array.isArray(promises) ? await Promise.all(promises) : await promises,
 }));
-const mockIndexPatternCreationType = {
-  getIndexPatternType: () => 'default',
-  getIndexPatternName: () => 'name',
-  checkIndicesForErrors: () => false,
-  getShowSystemIndices: () => false,
-};
 
-jest.mock('ui/chrome', () => ({
-  getUiSettingsClient: () => ({
-    get: () => '',
-  }),
-  addBasePath: () => {},
-}));
+const mockIndexPatternCreationType = new IndexPatternCreationConfig({
+  type: 'default',
+  name: 'name',
+});
 
-jest.mock('../../../lib/get_indices', () => ({
-  getIndices: (service, indexPatternCreationType, query) => {
+jest.mock('../../lib/get_indices', () => ({
+  getIndices: ({}, {}, query: string) => {
     if (query.startsWith('e')) {
       return [{ name: 'es' }];
     }
@@ -50,22 +46,30 @@ jest.mock('../../../lib/get_indices', () => ({
   },
 }));
 
-const allIndices = [{ name: 'kibana' }, { name: 'es' }];
-const esService = {};
-const savedObjectsClient = {
-  find: () => ({ savedObjects: [] }),
-};
+const allIndices = [
+  { name: 'kibana', tags: [] },
+  { name: 'es', tags: [] },
+];
+
 const goToNextStep = () => {};
 
-const createComponent = props => {
+const savedObjectClient = coreMock.createStart().savedObjects.client;
+savedObjectClient.find = () =>
+  new Promise<SavedObjectsFindResponsePublic<any>>(() => ({ savedObjects: [] }));
+
+const uiSettings = coreMock.createSetup().uiSettings;
+uiSettings.get.mockReturnValue('');
+
+const createComponent = (props?: Record<string, any>) => {
   return shallowWithI18nProvider(
     <StepIndexPattern
       allIndices={allIndices}
       isIncludingSystemIndices={false}
-      esService={esService}
-      savedObjectsClient={savedObjectsClient}
+      esService={dataPluginMock.createStartContract().search.__LEGACY.esClient}
+      savedObjectsClient={savedObjectClient as any}
       goToNextStep={goToNextStep}
       indexPatternCreationType={mockIndexPatternCreationType}
+      uiSettings={uiSettings}
       {...props}
     />
   );
@@ -93,8 +97,8 @@ describe('StepIndexPattern', () => {
 
   it('renders errors when input is invalid', async () => {
     const component = createComponent();
-    const instance = component.instance();
-    instance.onQueryChanged({ target: { value: '?' } });
+    const instance = component.instance() as StepIndexPattern;
+    instance.onQueryChanged({ target: { value: '?' } } as React.ChangeEvent<HTMLInputElement>);
 
     // Ensure all promises resolve
     await new Promise(resolve => process.nextTick(resolve));
@@ -107,8 +111,8 @@ describe('StepIndexPattern', () => {
 
   it('renders matching indices when input is valid', async () => {
     const component = createComponent();
-    const instance = component.instance();
-    instance.onQueryChanged({ target: { value: 'k' } });
+    const instance = component.instance() as StepIndexPattern;
+    instance.onQueryChanged({ target: { value: 'k' } } as React.ChangeEvent<HTMLInputElement>);
 
     // Ensure all promises resolve
     await new Promise(resolve => process.nextTick(resolve));
@@ -122,8 +126,8 @@ describe('StepIndexPattern', () => {
 
   it('appends a wildcard automatically to queries', async () => {
     const component = createComponent();
-    const instance = component.instance();
-    instance.onQueryChanged({ target: { value: 'k' } });
+    const instance = component.instance() as StepIndexPattern;
+    instance.onQueryChanged({ target: { value: 'k' } } as React.ChangeEvent<HTMLInputElement>);
     expect(component.state('query')).toBe('k*');
   });
 
@@ -135,8 +139,8 @@ describe('StepIndexPattern', () => {
 
   it('ensures the response of the latest request is persisted', async () => {
     const component = createComponent();
-    const instance = component.instance();
-    instance.onQueryChanged({ target: { value: 'e' } });
+    const instance = component.instance() as StepIndexPattern;
+    instance.onQueryChanged({ target: { value: 'e' } } as React.ChangeEvent<HTMLInputElement>);
     instance.lastQuery = 'k';
     await new Promise(resolve => process.nextTick(resolve));
 
@@ -149,7 +153,7 @@ describe('StepIndexPattern', () => {
     // Ensure it works in the other code flow too (the other early return)
 
     // Provide `es` so we do not auto append * and enter our other code flow
-    instance.onQueryChanged({ target: { value: 'es' } });
+    instance.onQueryChanged({ target: { value: 'es' } } as React.ChangeEvent<HTMLInputElement>);
     instance.lastQuery = 'k';
     await new Promise(resolve => process.nextTick(resolve));
     expect(component.state('exactMatchedIndices')).toEqual([]);
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx
similarity index 74%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx
index e10f033ed8165..7cfc7c4dbc81c 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx
@@ -18,8 +18,14 @@
  */
 
 import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import { indexPatterns } from '../../../../../../../../../../plugins/data/public';
+import { EuiPanel, EuiSpacer, EuiCallOut } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import {
+  indexPatterns,
+  DataPublicPluginStart,
+} from '../../../../../../../../../../plugins/data/public';
+import { SavedObjectsClient, IUiSettingsClient } from '../../../../../../../../../../core/public';
 import { MAX_SEARCH_SIZE } from '../../constants';
 import {
   getIndices,
@@ -32,47 +38,53 @@ import { LoadingIndices } from './components/loading_indices';
 import { StatusMessage } from './components/status_message';
 import { IndicesList } from './components/indices_list';
 import { Header } from './components/header';
+import { IndexPatternCreationConfig } from '../../../../../../../../management/public';
+import { MatchedIndex } from '../../types';
+
+interface StepIndexPatternProps {
+  allIndices: MatchedIndex[];
+  isIncludingSystemIndices: boolean;
+  esService: DataPublicPluginStart['search']['__LEGACY']['esClient'];
+  savedObjectsClient: SavedObjectsClient;
+  indexPatternCreationType: IndexPatternCreationConfig;
+  goToNextStep: () => void;
+  initialQuery?: string;
+  uiSettings: IUiSettingsClient;
+}
 
-import { EuiPanel, EuiSpacer, EuiCallOut } from '@elastic/eui';
-
-import { i18n } from '@kbn/i18n';
-import { FormattedMessage } from '@kbn/i18n/react';
-import chrome from 'ui/chrome';
-
-const uiSettings = chrome.getUiSettingsClient();
-
-export class StepIndexPattern extends Component {
-  static propTypes = {
-    allIndices: PropTypes.array.isRequired,
-    isIncludingSystemIndices: PropTypes.bool.isRequired,
-    esService: PropTypes.object.isRequired,
-    savedObjectsClient: PropTypes.object.isRequired,
-    indexPatternCreationType: PropTypes.object.isRequired,
-    goToNextStep: PropTypes.func.isRequired,
-    initialQuery: PropTypes.string,
-  };
+interface StepIndexPatternState {
+  partialMatchedIndices: MatchedIndex[];
+  exactMatchedIndices: MatchedIndex[];
+  isLoadingIndices: boolean;
+  existingIndexPatterns: string[];
+  indexPatternExists: boolean;
+  query: string;
+  appendedWildcard: boolean;
+  showingIndexPatternQueryErrors: boolean;
+  indexPatternName: string;
+}
 
-  static defaultProps = {
-    initialQuery: uiSettings.get('indexPattern:placeholder'),
+export class StepIndexPattern extends Component<StepIndexPatternProps, StepIndexPatternState> {
+  state = {
+    partialMatchedIndices: [],
+    exactMatchedIndices: [],
+    isLoadingIndices: false,
+    existingIndexPatterns: [],
+    indexPatternExists: false,
+    query: '',
+    appendedWildcard: false,
+    showingIndexPatternQueryErrors: false,
+    indexPatternName: '',
   };
+  ILLEGAL_CHARACTERS = [...indexPatterns.ILLEGAL_CHARACTERS];
+  lastQuery: string | undefined;
 
-  constructor(props) {
+  constructor(props: StepIndexPatternProps) {
     super(props);
-    const { indexPatternCreationType } = this.props;
-    this.state = {
-      partialMatchedIndices: [],
-      exactMatchedIndices: [],
-      isLoadingIndices: false,
-      existingIndexPatterns: [],
-      indexPatternExists: false,
-      query: props.initialQuery,
-      appendedWildcard: false,
-      showingIndexPatternQueryErrors: false,
-      indexPatternName: indexPatternCreationType.getIndexPatternName(),
-    };
-
-    this.ILLEGAL_CHARACTERS = [...indexPatterns.ILLEGAL_CHARACTERS];
-    this.lastQuery = null;
+    const { indexPatternCreationType, initialQuery } = this.props;
+
+    this.state.query = initialQuery || props.uiSettings.get('indexPattern:placeholder');
+    this.state.indexPatternName = indexPatternCreationType.getIndexPatternName();
   }
 
   async UNSAFE_componentWillMount() {
@@ -89,17 +101,19 @@ export class StepIndexPattern extends Component {
       fields: ['title'],
       perPage: 10000,
     });
+
     const existingIndexPatterns = savedObjects.map(obj =>
       obj && obj.attributes ? obj.attributes.title : ''
-    );
+    ) as string[];
+
     this.setState({ existingIndexPatterns });
   };
 
-  fetchIndices = async query => {
+  fetchIndices = async (query: string) => {
     const { esService, indexPatternCreationType } = this.props;
     const { existingIndexPatterns } = this.state;
 
-    if (existingIndexPatterns.includes(query)) {
+    if ((existingIndexPatterns as string[]).includes(query)) {
       this.setState({ indexPatternExists: true });
       return;
     }
@@ -135,7 +149,7 @@ export class StepIndexPattern extends Component {
     });
   };
 
-  onQueryChanged = e => {
+  onQueryChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
     const { appendedWildcard } = this.state;
     const { target } = e;
 
@@ -166,9 +180,13 @@ export class StepIndexPattern extends Component {
     return <LoadingIndices data-test-subj="createIndexPatternStep1Loading" />;
   }
 
-  renderStatusMessage(matchedIndices) {
-    const { indexPatternCreationType } = this.props;
-    const { query, isLoadingIndices, indexPatternExists, isIncludingSystemIndices } = this.state;
+  renderStatusMessage(matchedIndices: {
+    allIndices: MatchedIndex[];
+    exactMatchedIndices: MatchedIndex[];
+    partialMatchedIndices: MatchedIndex[];
+  }) {
+    const { indexPatternCreationType, isIncludingSystemIndices } = this.props;
+    const { query, isLoadingIndices, indexPatternExists } = this.state;
 
     if (isLoadingIndices || indexPatternExists) {
       return null;
@@ -184,7 +202,13 @@ export class StepIndexPattern extends Component {
     );
   }
 
-  renderList({ visibleIndices, allIndices }) {
+  renderList({
+    visibleIndices,
+    allIndices,
+  }: {
+    visibleIndices: MatchedIndex[];
+    allIndices: MatchedIndex[];
+  }) {
     const { query, isLoadingIndices, indexPatternExists } = this.state;
 
     if (isLoadingIndices || indexPatternExists) {
@@ -192,7 +216,6 @@ export class StepIndexPattern extends Component {
     }
 
     const indicesToList = query.length ? visibleIndices : allIndices;
-
     return (
       <IndicesList
         data-test-subj="createIndexPatternStep1IndicesList"
@@ -224,7 +247,7 @@ export class StepIndexPattern extends Component {
     );
   }
 
-  renderHeader({ exactMatchedIndices: indices }) {
+  renderHeader({ exactMatchedIndices: indices }: { exactMatchedIndices: MatchedIndex[] }) {
     const { goToNextStep, indexPatternCreationType } = this.props;
     const {
       query,
@@ -238,6 +261,7 @@ export class StepIndexPattern extends Component {
     const characterList = this.ILLEGAL_CHARACTERS.slice(0, this.ILLEGAL_CHARACTERS.length - 1).join(
       ', '
     );
+
     const checkIndices = indexPatternCreationType.checkIndicesForErrors(indices);
 
     if (!query || !query.length || query === '.' || query === '..') {
@@ -256,7 +280,7 @@ export class StepIndexPattern extends Component {
       errors.push(errorMessage);
       containsErrors = true;
     } else if (checkIndices) {
-      errors.push(...checkIndices);
+      errors.push(...(checkIndices as string[]));
       containsErrors = true;
     }
 
@@ -292,7 +316,7 @@ export class StepIndexPattern extends Component {
       <EuiPanel paddingSize="l">
         {this.renderHeader(matchedIndices)}
         <EuiSpacer size="s" />
-        {this.renderLoadingState(matchedIndices)}
+        {this.renderLoadingState()}
         {this.renderIndexPatternExists()}
         {this.renderStatusMessage(matchedIndices)}
         <EuiSpacer size="s" />
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/__jest__/__snapshots__/step_time_field.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/__snapshots__/step_time_field.test.tsx.snap
similarity index 92%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/__jest__/__snapshots__/step_time_field.test.js.snap
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/__snapshots__/step_time_field.test.tsx.snap
index 04a8a5070b21f..8ca307b3fc2a8 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/__jest__/__snapshots__/step_time_field.test.js.snap
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/__snapshots__/step_time_field.test.tsx.snap
@@ -32,14 +32,7 @@ exports[`StepTimeField should render "Custom index pattern ID already exists" wh
     isLoading={false}
     isVisible={false}
     onTimeFieldChanged={[Function]}
-    timeFieldOptions={
-      Array [
-        Object {
-          "text": "",
-          "value": "",
-        },
-      ]
-    }
+    timeFieldOptions={Array []}
   />
   <EuiSpacer
     size="s"
@@ -180,14 +173,7 @@ exports[`StepTimeField should render advanced options 1`] = `
     isLoading={false}
     isVisible={false}
     onTimeFieldChanged={[Function]}
-    timeFieldOptions={
-      Array [
-        Object {
-          "text": "",
-          "value": "",
-        },
-      ]
-    }
+    timeFieldOptions={Array []}
   />
   <EuiSpacer
     size="s"
@@ -225,14 +211,7 @@ exports[`StepTimeField should render advanced options with an index pattern id 1
     isLoading={false}
     isVisible={false}
     onTimeFieldChanged={[Function]}
-    timeFieldOptions={
-      Array [
-        Object {
-          "text": "",
-          "value": "",
-        },
-      ]
-    }
+    timeFieldOptions={Array []}
   />
   <EuiSpacer
     size="s"
@@ -270,14 +249,7 @@ exports[`StepTimeField should render any error message 1`] = `
     isLoading={false}
     isVisible={false}
     onTimeFieldChanged={[Function]}
-    timeFieldOptions={
-      Array [
-        Object {
-          "text": "",
-          "value": "",
-        },
-      ]
-    }
+    timeFieldOptions={Array []}
   />
   <EuiSpacer
     size="s"
@@ -333,14 +305,7 @@ exports[`StepTimeField should render normally 1`] = `
     isLoading={false}
     isVisible={false}
     onTimeFieldChanged={[Function]}
-    timeFieldOptions={
-      Array [
-        Object {
-          "text": "",
-          "value": "",
-        },
-      ]
-    }
+    timeFieldOptions={Array []}
   />
   <EuiSpacer
     size="s"
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/action_buttons/action_buttons.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/action_buttons/action_buttons.tsx
similarity index 89%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/action_buttons/action_buttons.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/action_buttons/action_buttons.tsx
index 23fbe0642fc2c..342876712eb28 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/action_buttons/action_buttons.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/action_buttons/action_buttons.tsx
@@ -23,7 +23,15 @@ import { EuiFlexGroup, EuiFlexItem, EuiButton, EuiButtonEmpty } from '@elastic/e
 
 import { FormattedMessage } from '@kbn/i18n/react';
 
-export const ActionButtons = ({ goToPreviousStep, submittable, createIndexPattern }) => (
+export const ActionButtons = ({
+  goToPreviousStep,
+  submittable,
+  createIndexPattern,
+}: {
+  goToPreviousStep: () => void;
+  submittable: boolean;
+  createIndexPattern: () => void;
+}) => (
   <EuiFlexGroup justifyContent="flexEnd">
     <EuiFlexItem grow={false}>
       <EuiButtonEmpty iconType="arrowLeft" onClick={goToPreviousStep}>
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/action_buttons/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/action_buttons/index.ts
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/action_buttons/index.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/action_buttons/index.ts
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/advanced_options/__jest__/__snapshots__/advanced_options.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/advanced_options/__jest__/__snapshots__/advanced_options.test.js.snap
deleted file mode 100644
index 9dbc052d143f3..0000000000000
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/advanced_options/__jest__/__snapshots__/advanced_options.test.js.snap
+++ /dev/null
@@ -1,37 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`AdvancedOptions should hide if not showing 1`] = `
-<div>
-  <EuiButtonEmpty
-    iconType="arrowRight"
-    onClick={[Function]}
-  >
-    <FormattedMessage
-      defaultMessage="Show advanced options"
-      id="kbn.management.createIndexPattern.stepTime.options.showButton"
-      values={Object {}}
-    />
-  </EuiButtonEmpty>
-  <EuiSpacer
-    size="xs"
-  />
-</div>
-`;
-
-exports[`AdvancedOptions should render normally 1`] = `
-<div>
-  <EuiButtonEmpty
-    iconType="arrowRight"
-    onClick={[Function]}
-  >
-    <FormattedMessage
-      defaultMessage="Show advanced options"
-      id="kbn.management.createIndexPattern.stepTime.options.showButton"
-      values={Object {}}
-    />
-  </EuiButtonEmpty>
-  <EuiSpacer
-    size="xs"
-  />
-</div>
-`;
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/advanced_options/__snapshots__/advanced_options.test.tsx.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/advanced_options/__snapshots__/advanced_options.test.tsx.snap
new file mode 100644
index 0000000000000..02ceed3454a00
--- /dev/null
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/advanced_options/__snapshots__/advanced_options.test.tsx.snap
@@ -0,0 +1,69 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`AdvancedOptions should hide if not showing 1`] = `
+<div>
+  <EuiButtonEmpty
+    iconType="arrowRight"
+    onClick={[Function]}
+  >
+    <FormattedMessage
+      defaultMessage="Show advanced options"
+      id="kbn.management.createIndexPattern.stepTime.options.showButton"
+      values={Object {}}
+    />
+  </EuiButtonEmpty>
+  <EuiSpacer
+    size="xs"
+  />
+</div>
+`;
+
+exports[`AdvancedOptions should render normally 1`] = `
+<div>
+  <EuiButtonEmpty
+    iconType="arrowDown"
+    onClick={[Function]}
+  >
+    <FormattedMessage
+      defaultMessage="Hide advanced options"
+      id="kbn.management.createIndexPattern.stepTime.options.hideButton"
+      values={Object {}}
+    />
+  </EuiButtonEmpty>
+  <EuiSpacer
+    size="xs"
+  />
+  <EuiForm>
+    <EuiFormRow
+      describedByIds={Array []}
+      display="row"
+      fullWidth={false}
+      hasChildLabel={true}
+      hasEmptyLabelSpace={false}
+      helpText={
+        <FormattedMessage
+          defaultMessage="Kibana will provide a unique identifier for each index pattern. If you do not want to use this unique ID, enter a custom one."
+          id="kbn.management.createIndexPattern.stepTime.options.patternLabel"
+          values={Object {}}
+        />
+      }
+      label={
+        <FormattedMessage
+          defaultMessage="Custom index pattern ID"
+          id="kbn.management.createIndexPattern.stepTime.options.patternHeader"
+          values={Object {}}
+        />
+      }
+      labelType="label"
+    >
+      <EuiFieldText
+        data-test-subj="createIndexPatternIdInput"
+        name="indexPatternId"
+        onChange={[Function]}
+        placeholder="custom-index-pattern-id"
+        value="foobar"
+      />
+    </EuiFormRow>
+  </EuiForm>
+</div>
+`;
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/advanced_options/__jest__/advanced_options.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/advanced_options/advanced_options.test.tsx
similarity index 95%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/advanced_options/__jest__/advanced_options.test.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/advanced_options/advanced_options.test.tsx
index 6dde1a597fc2d..ffb3779d47c89 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/advanced_options/__jest__/advanced_options.test.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/advanced_options/advanced_options.test.tsx
@@ -25,7 +25,7 @@ describe('AdvancedOptions', () => {
   it('should render normally', () => {
     const component = shallowWithI18nProvider(
       <AdvancedOptions
-        showingAdvancedOptions={true}
+        isVisible={true}
         indexPatternId={'foobar'}
         toggleAdvancedOptions={() => {}}
         onChangeIndexPatternId={() => {}}
@@ -38,7 +38,7 @@ describe('AdvancedOptions', () => {
   it('should hide if not showing', () => {
     const component = shallowWithI18nProvider(
       <AdvancedOptions
-        showingAdvancedOptions={false}
+        isVisible={false}
         indexPatternId={'foobar'}
         toggleAdvancedOptions={() => {}}
         onChangeIndexPatternId={() => {}}
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/advanced_options/advanced_options.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/advanced_options/advanced_options.tsx
similarity index 90%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/advanced_options/advanced_options.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/advanced_options/advanced_options.tsx
index 9082df6affa09..fd2c1db1eacd7 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/advanced_options/advanced_options.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/advanced_options/advanced_options.tsx
@@ -24,7 +24,14 @@ import { EuiForm, EuiFormRow, EuiFieldText, EuiButtonEmpty, EuiSpacer } from '@e
 import { i18n } from '@kbn/i18n';
 import { FormattedMessage } from '@kbn/i18n/react';
 
-export const AdvancedOptions = ({
+interface AdvancedOptionsProps {
+  isVisible: boolean;
+  indexPatternId: string;
+  toggleAdvancedOptions: (e: React.FormEvent<HTMLButtonElement>) => void;
+  onChangeIndexPatternId: (e: React.ChangeEvent<HTMLInputElement>) => void;
+}
+
+export const AdvancedOptions: React.FC<AdvancedOptionsProps> = ({
   isVisible,
   indexPatternId,
   toggleAdvancedOptions,
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/advanced_options/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/advanced_options/index.ts
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/advanced_options/index.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/advanced_options/index.ts
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/header/__jest__/__snapshots__/header.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/header/__snapshots__/header.test.tsx.snap
similarity index 94%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/header/__jest__/__snapshots__/header.test.js.snap
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/header/__snapshots__/header.test.tsx.snap
index dabe9577596a0..5c53558286b0d 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/header/__jest__/__snapshots__/header.test.js.snap
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/header/__snapshots__/header.test.tsx.snap
@@ -27,7 +27,7 @@ exports[`Header should render normally 1`] = `
           "indexPattern": <strong>
             ki*
           </strong>,
-          "indexPatternName": undefined,
+          "indexPatternName": "ki*",
         }
       }
     />
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/header/__jest__/header.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/header/header.test.tsx
similarity index 92%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/header/__jest__/header.test.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/header/header.test.tsx
index 29c4b849070b8..8a37eefc82ce2 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/header/__jest__/header.test.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/header/header.test.tsx
@@ -23,7 +23,7 @@ import { shallow } from 'enzyme';
 
 describe('Header', () => {
   it('should render normally', () => {
-    const component = shallow(<Header indexPattern="ki*" />);
+    const component = shallow(<Header indexPattern="ki*" indexPatternName="ki*" />);
 
     expect(component).toMatchSnapshot();
   });
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/header/header.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/header/header.tsx
similarity index 90%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/header/header.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/header/header.tsx
index 63718e411f712..5c2f184e8038b 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/header/header.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/header/header.tsx
@@ -23,7 +23,12 @@ import { EuiTitle, EuiSpacer, EuiText } from '@elastic/eui';
 
 import { FormattedMessage } from '@kbn/i18n/react';
 
-export const Header = ({ indexPattern, indexPatternName }) => (
+interface HeaderProps {
+  indexPattern: string;
+  indexPatternName: string;
+}
+
+export const Header: React.FC<HeaderProps> = ({ indexPattern, indexPatternName }) => (
   <div>
     <EuiTitle size="s">
       <h2>
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/header/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/header/index.ts
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/header/index.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/header/index.ts
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/time_field/__jest__/__snapshots__/time_field.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/time_field/__snapshots__/time_field.test.tsx.snap
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/time_field/__jest__/__snapshots__/time_field.test.js.snap
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/time_field/__snapshots__/time_field.test.tsx.snap
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/time_field/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/time_field/index.ts
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/time_field/index.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/time_field/index.ts
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/time_field/__jest__/time_field.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/time_field/time_field.test.tsx
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/time_field/__jest__/time_field.test.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/time_field/time_field.test.tsx
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/time_field/time_field.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/time_field/time_field.tsx
similarity index 92%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/time_field/time_field.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/time_field/time_field.tsx
index 595552568b461..876a3b79a8812 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/time_field/time_field.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/components/time_field/time_field.tsx
@@ -35,7 +35,16 @@ import {
 import { i18n } from '@kbn/i18n';
 import { FormattedMessage } from '@kbn/i18n/react';
 
-export const TimeField = ({
+interface TimeFieldProps {
+  isVisible: boolean;
+  fetchTimeFields: () => void;
+  timeFieldOptions: Array<{ text: string; value?: string }>;
+  isLoading: boolean;
+  selectedTimeField?: string;
+  onTimeFieldChanged: (e: React.ChangeEvent<HTMLSelectElement>) => void;
+}
+
+export const TimeField: React.FC<TimeFieldProps> = ({
   isVisible,
   fetchTimeFields,
   timeFieldOptions,
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/index.ts
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/index.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/index.ts
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/__jest__/step_time_field.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/step_time_field.test.tsx
similarity index 83%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/__jest__/step_time_field.test.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/step_time_field.test.tsx
index 1f9d80440f6cb..f37dc088ac78e 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/__jest__/step_time_field.test.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/step_time_field.test.tsx
@@ -19,34 +19,30 @@
 
 import React from 'react';
 import { shallowWithI18nProvider } from 'test_utils/enzyme_helpers';
+import { IndexPatternCreationConfig } from '../../../../../../../../management/public';
+import { IFieldType } from '../../../../../../../../../../plugins/data/public';
+import { dataPluginMock } from '../../../../../../../../../../plugins/data/public/mocks';
 
 import { StepTimeField } from '../step_time_field';
 
-jest.mock('../components/header', () => ({ Header: 'Header' }));
-jest.mock('../components/time_field', () => ({ TimeField: 'TimeField' }));
-jest.mock('../components/advanced_options', () => ({ AdvancedOptions: 'AdvancedOptions' }));
-jest.mock('../components/action_buttons', () => ({ ActionButtons: 'ActionButtons' }));
-jest.mock('../../../lib/extract_time_fields', () => ({
-  extractTimeFields: fields => fields,
+jest.mock('./components/header', () => ({ Header: 'Header' }));
+jest.mock('./components/time_field', () => ({ TimeField: 'TimeField' }));
+jest.mock('./components/advanced_options', () => ({ AdvancedOptions: 'AdvancedOptions' }));
+jest.mock('./components/action_buttons', () => ({ ActionButtons: 'ActionButtons' }));
+jest.mock('./../../lib/extract_time_fields', () => ({
+  extractTimeFields: (fields: IFieldType) => fields,
 }));
 jest.mock('ui/chrome', () => ({
   addBasePath: () => {},
 }));
 
-const mockIndexPatternCreationType = {
-  getIndexPatternType: () => 'default',
-  getIndexPatternName: () => 'name',
-  getFetchForWildcardOptions: () => {},
-};
+const mockIndexPatternCreationType = new IndexPatternCreationConfig({
+  type: 'default',
+  name: 'name',
+});
+
 const noop = () => {};
-const indexPatternsService = {
-  make: async () => ({
-    fieldsFetcher: {
-      fetch: noop,
-      fetchForWildcard: noop,
-    },
-  }),
-};
+const indexPatternsService = dataPluginMock.createStartContract().indexPatterns;
 
 describe('StepTimeField', () => {
   it('should render normally', () => {
@@ -127,12 +123,16 @@ describe('StepTimeField', () => {
 
     // If the value is undefined, that means the user selected the
     // `I don't want to use a Time filter` option
-    component.instance().onTimeFieldChanged({ target: { value: undefined } });
+    (component.instance() as StepTimeField).onTimeFieldChanged(({
+      target: { value: undefined },
+    } as unknown) as React.ChangeEvent<HTMLSelectElement>);
     expect(component.state('timeFieldSet')).toBe(true);
 
     // If the value is an empty string, that means the user selected
     // an invalid selection (like the empty selection or the `-----`)
-    component.instance().onTimeFieldChanged({ target: { value: '' } });
+    (component.instance() as StepTimeField).onTimeFieldChanged(({
+      target: { value: '' },
+    } as unknown) as React.ChangeEvent<HTMLSelectElement>);
     expect(component.state('timeFieldSet')).toBe(false);
   });
 
@@ -154,7 +154,9 @@ describe('StepTimeField', () => {
       ],
     });
 
-    component.instance().onTimeFieldChanged({ target: { value: '' } });
+    (component.instance() as StepTimeField).onTimeFieldChanged(({
+      target: { value: '' },
+    } as unknown) as React.ChangeEvent<HTMLSelectElement>);
     component.update();
 
     expect(component.find('ActionButtons')).toMatchSnapshot();
@@ -178,7 +180,9 @@ describe('StepTimeField', () => {
       ],
     });
 
-    component.instance().onTimeFieldChanged({ target: { value: undefined } });
+    (component.instance() as StepTimeField).onTimeFieldChanged(({
+      target: { value: undefined },
+    } as unknown) as React.ChangeEvent<HTMLSelectElement>);
     component.update();
 
     expect(component.find('ActionButtons')).toMatchSnapshot();
@@ -281,7 +285,7 @@ describe('StepTimeField', () => {
       />
     );
 
-    await component.instance().createIndexPattern();
+    await (component.instance() as StepTimeField).createIndexPattern();
     component.update();
 
     expect(component.instance().state).toMatchObject({
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/step_time_field.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/step_time_field.tsx
similarity index 71%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/step_time_field.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/step_time_field.tsx
index d69717eead231..dff2a07e460e2 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/step_time_field.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/step_time_field.tsx
@@ -18,14 +18,6 @@
  */
 
 import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import { ensureMinimumTime, extractTimeFields } from '../../lib';
-
-import { Header } from './components/header';
-import { TimeField } from './components/time_field';
-import { AdvancedOptions } from './components/advanced_options';
-import { ActionButtons } from './components/action_buttons';
-
 import {
   EuiCallOut,
   EuiFlexGroup,
@@ -35,33 +27,61 @@ import {
   EuiSpacer,
   EuiLoadingSpinner,
 } from '@elastic/eui';
-
 import { FormattedMessage } from '@kbn/i18n/react';
+import { ensureMinimumTime, extractTimeFields } from '../../lib';
+
+import { Header } from './components/header';
+import { TimeField } from './components/time_field';
+import { AdvancedOptions } from './components/advanced_options';
+import { ActionButtons } from './components/action_buttons';
+import { IndexPatternCreationConfig } from '../../../../../../../../management/public';
+import { DataPublicPluginStart } from '../../../../../../../../../../plugins/data/public';
 
-export class StepTimeField extends Component {
-  static propTypes = {
-    indexPattern: PropTypes.string.isRequired,
-    indexPatternsService: PropTypes.object.isRequired,
-    goToPreviousStep: PropTypes.func.isRequired,
-    createIndexPattern: PropTypes.func.isRequired,
-    indexPatternCreationType: PropTypes.object.isRequired,
+interface StepTimeFieldProps {
+  indexPattern: string;
+  indexPatternsService: DataPublicPluginStart['indexPatterns'];
+  goToPreviousStep: () => void;
+  createIndexPattern: (selectedTimeField: string, indexPatternId: string) => void;
+  indexPatternCreationType: IndexPatternCreationConfig;
+}
+
+interface StepTimeFieldState {
+  error: string;
+  timeFields: TimeFieldConfig[];
+  selectedTimeField?: string;
+  timeFieldSet: boolean;
+  isAdvancedOptionsVisible: boolean;
+  isFetchingTimeFields: boolean;
+  isCreating: boolean;
+  indexPatternId: string;
+  indexPatternType: string;
+  indexPatternName: string;
+}
+
+interface TimeFieldConfig {
+  display: string;
+  fieldName?: string;
+  isDisabled?: boolean;
+}
+
+export class StepTimeField extends Component<StepTimeFieldProps, StepTimeFieldState> {
+  state = {
+    error: '',
+    timeFields: [],
+    selectedTimeField: undefined,
+    timeFieldSet: false,
+    isAdvancedOptionsVisible: false,
+    isFetchingTimeFields: false,
+    isCreating: false,
+    indexPatternId: '',
+    indexPatternType: '',
+    indexPatternName: '',
   };
 
-  constructor(props) {
+  constructor(props: StepTimeFieldProps) {
     super(props);
-
-    this.state = {
-      error: '',
-      timeFields: [],
-      selectedTimeField: undefined,
-      timeFieldSet: false,
-      isAdvancedOptionsVisible: false,
-      isFetchingTimeFields: false,
-      isCreating: false,
-      indexPatternId: '',
-      indexPatternType: props.indexPatternCreationType.getIndexPatternType(),
-      indexPatternName: props.indexPatternCreationType.getIndexPatternName(),
-    };
+    this.state.indexPatternType = props.indexPatternCreationType.getIndexPatternType() || '';
+    this.state.indexPatternName = props.indexPatternCreationType.getIndexPatternName();
   }
 
   mounted = false;
@@ -91,22 +111,24 @@ export class StepTimeField extends Component {
     this.setState({ timeFields, isFetchingTimeFields: false });
   };
 
-  onTimeFieldChanged = e => {
+  onTimeFieldChanged = (e: React.ChangeEvent<HTMLSelectElement>) => {
     const value = e.target.value;
 
     // Find the time field based on the selected value
-    const timeField = this.state.timeFields.find(timeField => timeField.fieldName === value);
+    const timeField = this.state.timeFields.find(
+      (timeFld: TimeFieldConfig) => timeFld.fieldName === value
+    );
 
     // If the value is an empty string, it's not a valid selection
     const validSelection = value !== '';
 
     this.setState({
-      selectedTimeField: timeField ? timeField.fieldName : undefined,
+      selectedTimeField: timeField ? (timeField as TimeFieldConfig).fieldName : undefined,
       timeFieldSet: validSelection,
     });
   };
 
-  onChangeIndexPatternId = e => {
+  onChangeIndexPatternId = (e: React.ChangeEvent<HTMLInputElement>) => {
     this.setState({ indexPatternId: e.target.value });
   };
 
@@ -121,7 +143,7 @@ export class StepTimeField extends Component {
     const { selectedTimeField, indexPatternId } = this.state;
     this.setState({ isCreating: true });
     try {
-      await createIndexPattern(selectedTimeField, indexPatternId);
+      await createIndexPattern(selectedTimeField || '', indexPatternId);
     } catch (error) {
       if (!this.mounted) return;
       this.setState({
@@ -131,7 +153,7 @@ export class StepTimeField extends Component {
     }
   };
 
-  formatErrorMessage(message) {
+  formatErrorMessage(message: string) {
     // `createIndexPattern` throws "Conflict" when index pattern ID already exists.
     return message === 'Conflict' ? (
       <FormattedMessage
@@ -177,16 +199,17 @@ export class StepTimeField extends Component {
 
     const { indexPattern, goToPreviousStep } = this.props;
 
-    const timeFieldOptions = timeFields
-      ? [
-          { text: '', value: '' },
-          ...timeFields.map(timeField => ({
-            text: timeField.display,
-            value: timeField.fieldName,
-            disabled: timeFields.isDisabled,
-          })),
-        ]
-      : [];
+    const timeFieldOptions =
+      timeFields.length > 0
+        ? [
+            { text: '', value: '' },
+            ...timeFields.map((timeField: TimeFieldConfig) => ({
+              text: timeField.display,
+              value: timeField.fieldName,
+              disabled: ((timeFields as unknown) as TimeFieldConfig).isDisabled,
+            })),
+          ]
+        : [];
 
     const showTimeField = !timeFields || timeFields.length > 1;
     const submittable = !showTimeField || timeFieldSet;
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.js
index b5c6000eb2fe1..1a93188edd6cc 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.js
@@ -224,6 +224,7 @@ export class CreateIndexPatternWizard extends Component {
           savedObjectsClient={services.savedObjectsClient}
           indexPatternCreationType={this.indexPatternCreationType}
           goToNextStep={this.goToTimeFieldStep}
+          uiSettings={services.uiSettings}
         />
       );
     }
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/__jest__/create_index_pattern_wizard.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.test.js
similarity index 91%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/__jest__/create_index_pattern_wizard.test.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.test.js
index bcf28725c91da..941f87d4d9fd2 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/__jest__/create_index_pattern_wizard.test.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.test.js
@@ -20,7 +20,7 @@
 import React from 'react';
 import { shallow } from 'enzyme';
 
-import { CreateIndexPatternWizard } from '../create_index_pattern_wizard';
+import { CreateIndexPatternWizard } from './create_index_pattern_wizard';
 const mockIndexPatternCreationType = {
   getIndexPatternType: () => 'default',
   getIndexPatternName: () => 'name',
@@ -32,12 +32,12 @@ const mockIndexPatternCreationType = {
     return {};
   },
 };
-jest.mock('../components/step_index_pattern', () => ({ StepIndexPattern: 'StepIndexPattern' }));
-jest.mock('../components/step_time_field', () => ({ StepTimeField: 'StepTimeField' }));
-jest.mock('../components/header', () => ({ Header: 'Header' }));
-jest.mock('../components/loading_state', () => ({ LoadingState: 'LoadingState' }));
-jest.mock('../components/empty_state', () => ({ EmptyState: 'EmptyState' }));
-jest.mock('../lib/get_indices', () => ({
+jest.mock('./components/step_index_pattern', () => ({ StepIndexPattern: 'StepIndexPattern' }));
+jest.mock('./components/step_time_field', () => ({ StepTimeField: 'StepTimeField' }));
+jest.mock('./components/header', () => ({ Header: 'Header' }));
+jest.mock('./components/loading_state', () => ({ LoadingState: 'LoadingState' }));
+jest.mock('./components/empty_state', () => ({ EmptyState: 'EmptyState' }));
+jest.mock('./lib/get_indices', () => ({
   getIndices: () => {
     return [{ name: 'kibana' }];
   },
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js
index d06bc8784de51..50c5a58d35db3 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js
@@ -47,6 +47,7 @@ uiRoutes.when('/management/kibana/index_pattern', {
           $scope.$evalAsync(() => kbnUrl.changePath(url));
         },
         openConfirm: npStart.core.overlays.openConfirm,
+        uiSettings: npStart.core.uiSettings,
       };
 
       const initialQuery = $routeParams.id ? decodeURIComponent($routeParams.id) : undefined;
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_matched_indices.test.ts b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_matched_indices.test.ts
index 7aba50a7ca12b..65840aa64046d 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_matched_indices.test.ts
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_matched_indices.test.ts
@@ -18,12 +18,13 @@
  */
 
 import { getMatchedIndices } from './get_matched_indices';
+import { Tag } from '../types';
 
 jest.mock('./../constants', () => ({
   MAX_NUMBER_OF_MATCHING_INDICES: 6,
 }));
 
-const tags: string[] = [];
+const tags: Tag[] = [];
 const indices = [
   { name: 'kibana', tags },
   { name: 'es', tags },
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/__jest__/render.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/render.test.js
similarity index 98%
rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/__jest__/render.test.js
rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/render.test.js
index 375e80028f317..af580547b11ed 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/__jest__/render.test.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/render.test.js
@@ -33,7 +33,7 @@ jest.mock('ui/i18n', () => ({
   I18nContext: () => {},
 }));
 
-const { renderCreateIndexPatternWizard, destroyCreateIndexPatternWizard } = require('../render');
+const { renderCreateIndexPatternWizard, destroyCreateIndexPatternWizard } = require('./render');
 
 describe('CreateIndexPatternWizardRender', () => {
   beforeEach(() => {
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/types.ts b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/types.ts
index 93bb6920c6981..634bbd856ea86 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/types.ts
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/types.ts
@@ -19,5 +19,10 @@
 
 export interface MatchedIndex {
   name: string;
-  tags: string[];
+  tags: Tag[];
+}
+
+export interface Tag {
+  name: string;
+  key: string;
 }
diff --git a/src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/creation/config.ts b/src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/creation/config.ts
index b68b2e40aad9e..5714fa3338962 100644
--- a/src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/creation/config.ts
+++ b/src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/creation/config.ts
@@ -18,6 +18,7 @@
  */
 
 import { i18n } from '@kbn/i18n';
+import { MatchedIndex } from '../../../../../../kibana/public/management/sections/index_patterns/create_index_pattern_wizard/types';
 
 const indexPatternTypeName = i18n.translate(
   'management.editIndexPattern.createIndex.defaultTypeName',
@@ -106,7 +107,7 @@ export class IndexPatternCreationConfig {
     return [];
   }
 
-  public checkIndicesForErrors() {
+  public checkIndicesForErrors(indices: MatchedIndex[]) {
     return undefined;
   }
 
diff --git a/src/plugins/advanced_settings/public/management_app/components/field/field.tsx b/src/plugins/advanced_settings/public/management_app/components/field/field.tsx
index 5cf5ea99faa80..7158e3d5e7b3e 100644
--- a/src/plugins/advanced_settings/public/management_app/components/field/field.tsx
+++ b/src/plugins/advanced_settings/public/management_app/components/field/field.tsx
@@ -31,7 +31,6 @@ import {
   EuiCodeBlock,
   // @ts-ignore
   EuiCodeEditor,
-  // @ts-ignore
   EuiDescribedFormGroup,
   EuiFieldNumber,
   EuiFieldText,
diff --git a/src/plugins/advanced_settings/public/management_app/components/search/search.test.tsx b/src/plugins/advanced_settings/public/management_app/components/search/search.test.tsx
index 8e7bac5129ae9..0e3fbb3cf97fa 100644
--- a/src/plugins/advanced_settings/public/management_app/components/search/search.test.tsx
+++ b/src/plugins/advanced_settings/public/management_app/components/search/search.test.tsx
@@ -19,6 +19,7 @@
 
 import React from 'react';
 import { shallowWithI18nProvider, mountWithI18nProvider } from 'test_utils/enzyme_helpers';
+
 // @ts-ignore
 import { findTestSubject } from '@elastic/eui/lib/test';
 
diff --git a/src/plugins/data/public/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/public/index_patterns/index_patterns/index_pattern.ts
index 06d4a881447dc..768029136879d 100644
--- a/src/plugins/data/public/index_patterns/index_patterns/index_pattern.ts
+++ b/src/plugins/data/public/index_patterns/index_patterns/index_pattern.ts
@@ -64,7 +64,7 @@ export class IndexPattern implements IIndexPattern {
   private getConfig: any;
   private sourceFilters?: [];
   private originalBody: { [key: string]: any } = {};
-  private fieldsFetcher: any;
+  public fieldsFetcher: any; // probably want to factor out any direct usage and change to private
   private shortDotsEnable: boolean = false;
 
   private mapping: MappingObject = expandShorthand({
diff --git a/src/plugins/data/public/mocks.ts b/src/plugins/data/public/mocks.ts
index c723c34cd0a22..2d5cc72597ec4 100644
--- a/src/plugins/data/public/mocks.ts
+++ b/src/plugins/data/public/mocks.ts
@@ -97,7 +97,13 @@ const createStartContract = (): Start => {
         msearch: jest.fn(),
       },
     },
-    indexPatterns: {} as IndexPatternsContract,
+    indexPatterns: ({
+      make: () => ({
+        fieldsFetcher: {
+          fetchForWildcard: jest.fn(),
+        },
+      }),
+    } as unknown) as IndexPatternsContract,
   };
   return startContract;
 };
diff --git a/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap b/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap
index 990386687bade..06e56aaf3eb0a 100644
--- a/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap
+++ b/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap
@@ -189,7 +189,9 @@ exports[`QueryStringInput Should disable autoFocus on EuiFieldText when disableA
                 "register": [MockFunction],
               },
               "getSuggestions": [MockFunction],
-              "indexPatterns": Object {},
+              "indexPatterns": Object {
+                "make": [Function],
+              },
               "query": Object {
                 "filterManager": [MockFunction],
                 "savedQueries": [MockFunction],
@@ -842,7 +844,9 @@ exports[`QueryStringInput Should disable autoFocus on EuiFieldText when disableA
                         "register": [MockFunction],
                       },
                       "getSuggestions": [MockFunction],
-                      "indexPatterns": Object {},
+                      "indexPatterns": Object {
+                        "make": [Function],
+                      },
                       "query": Object {
                         "filterManager": [MockFunction],
                         "savedQueries": [MockFunction],
@@ -1477,7 +1481,9 @@ exports[`QueryStringInput Should pass the query language to the language switche
                 "register": [MockFunction],
               },
               "getSuggestions": [MockFunction],
-              "indexPatterns": Object {},
+              "indexPatterns": Object {
+                "make": [Function],
+              },
               "query": Object {
                 "filterManager": [MockFunction],
                 "savedQueries": [MockFunction],
@@ -2127,7 +2133,9 @@ exports[`QueryStringInput Should pass the query language to the language switche
                         "register": [MockFunction],
                       },
                       "getSuggestions": [MockFunction],
-                      "indexPatterns": Object {},
+                      "indexPatterns": Object {
+                        "make": [Function],
+                      },
                       "query": Object {
                         "filterManager": [MockFunction],
                         "savedQueries": [MockFunction],
@@ -2762,7 +2770,9 @@ exports[`QueryStringInput Should render the given query 1`] = `
                 "register": [MockFunction],
               },
               "getSuggestions": [MockFunction],
-              "indexPatterns": Object {},
+              "indexPatterns": Object {
+                "make": [Function],
+              },
               "query": Object {
                 "filterManager": [MockFunction],
                 "savedQueries": [MockFunction],
@@ -3412,7 +3422,9 @@ exports[`QueryStringInput Should render the given query 1`] = `
                         "register": [MockFunction],
                       },
                       "getSuggestions": [MockFunction],
-                      "indexPatterns": Object {},
+                      "indexPatterns": Object {
+                        "make": [Function],
+                      },
                       "query": Object {
                         "filterManager": [MockFunction],
                         "savedQueries": [MockFunction],

From 76d475a64c02ab4bd44cf7bbb5e12b609b321902 Mon Sep 17 00:00:00 2001
From: Jonathan Budzenski <jon@budzenski.me>
Date: Tue, 18 Feb 2020 16:21:36 -0600
Subject: [PATCH 044/174] Refactor test entry by runner (#44679)

Over the last few years we've increased the number of test runners.
Entry points by test type have become too lage of a category for unique
names, so this moves top level test scripts under yarn and replaces test
types with the runner name.

e.g. `yarn test:browser` -> `yarn test:karma`

Closes #41133

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 CONTRIBUTING.md                               | 44 ++++++-------------
 package.json                                  | 12 +++--
 packages/kbn-plugin-generator/README.md       |  2 +-
 .../integration_tests/generate_plugin.test.js |  4 +-
 .../sao_template/template/README.md           |  1 -
 packages/kbn-plugin-helpers/README.md         |  4 +-
 packages/kbn-plugin-helpers/cli.js            | 12 ++---
 packages/kbn-plugin-helpers/lib/index.d.ts    |  2 +-
 packages/kbn-plugin-helpers/lib/tasks.js      |  8 ++--
 .../tasks/test/all/README.md                  |  4 +-
 .../tasks/test/all/test_all_action.js         |  4 +-
 .../tasks/test/{browser => karma}/README.md   |  8 ++--
 .../tasks/test/{server => karma}/index.js     |  2 +-
 .../test_karma_action.js}                     |  4 +-
 .../tasks/test/{server => mocha}/README.md    |  6 +--
 .../tasks/test/{browser => mocha}/index.js    |  2 +-
 .../test_mocha_action.js}                     |  0
 tasks/config/karma.js                         |  4 +-
 tasks/config/run.js                           | 22 +++++-----
 tasks/jenkins.js                              |  2 +-
 tasks/test.js                                 | 12 ++---
 test/scripts/jenkins_xpack.sh                 |  2 +-
 x-pack/README.md                              |  6 +--
 x-pack/gulpfile.js                            |  6 +--
 .../plugins/canvas/scripts/test_browser.js    |  2 +-
 .../legacy/plugins/canvas/scripts/test_dev.js |  2 +-
 .../plugins/canvas/scripts/test_server.js     |  2 +-
 x-pack/package.json                           |  4 +-
 x-pack/tasks/helpers/flags.ts                 |  2 +-
 x-pack/tasks/test.ts                          | 10 ++---
 30 files changed, 87 insertions(+), 108 deletions(-)
 rename packages/kbn-plugin-helpers/tasks/test/{browser => karma}/README.md (88%)
 rename packages/kbn-plugin-helpers/tasks/test/{server => karma}/index.js (94%)
 rename packages/kbn-plugin-helpers/tasks/test/{browser/test_browser_action.js => karma/test_karma_action.js} (91%)
 rename packages/kbn-plugin-helpers/tasks/test/{server => mocha}/README.md (85%)
 rename packages/kbn-plugin-helpers/tasks/test/{browser => mocha}/index.js (94%)
 rename packages/kbn-plugin-helpers/tasks/test/{server/test_server_action.js => mocha/test_mocha_action.js} (100%)

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e559177586f48..bd7868adb511e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -469,11 +469,11 @@ The following table outlines possible test file locations and how to invoke them
 
 | Test runner        | Test location                                                                                                                                           | Runner command (working directory is kibana root)                                       |
 | -----------------  | ------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
-| Jest               | `src/**/*.test.js`<br>`src/**/*.test.ts`                                                                                                                | `node scripts/jest -t regexp [test path]`                                               |
-| Jest (integration) | `**/integration_tests/**/*.test.js`                                                                                                                     | `node scripts/jest_integration -t regexp [test path]`                                   |
+| Jest               | `src/**/*.test.js`<br>`src/**/*.test.ts`                                                                                                                | `yarn test:jest -t regexp [test path]`                                               |
+| Jest (integration) | `**/integration_tests/**/*.test.js`                                                                                                                     | `yarn test:jest_integration -t regexp [test path]`                                   |
 | Mocha              | `src/**/__tests__/**/*.js`<br>`!src/**/public/__tests__/*.js`<br>`packages/kbn-datemath/test/**/*.js`<br>`packages/kbn-dev-utils/src/**/__tests__/**/*.js`<br>`tasks/**/__tests__/**/*.js` | `node scripts/mocha --grep=regexp [test path]`       |
-| Functional         | `test/*integration/**/config.js`<br>`test/*functional/**/config.js`<br>`test/accessibility/config.js`                                                                                    | `node scripts/functional_tests_server --config test/[directory]/config.js`<br>`node scripts/functional_test_runner --config test/[directory]/config.js --grep=regexp`       |
-| Karma              | `src/**/public/__tests__/*.js`                                                                                                                          | `npm run test:dev`                                                                      |
+| Functional         | `test/*integration/**/config.js`<br>`test/*functional/**/config.js`<br>`test/accessibility/config.js`                                                                                    | `yarn test:ftr:server --config test/[directory]/config.js`<br>`yarn test:ftr:runner --config test/[directory]/config.js --grep=regexp`       |
+| Karma              | `src/**/public/__tests__/*.js`                                                                                                                          | `yarn test:karma:debug`                                                                      |
 
 For X-Pack tests located in `x-pack/` see [X-Pack Testing](x-pack/README.md#testing)
 
@@ -484,56 +484,38 @@ Test runner arguments:
  Examples:
   - Run the entire elasticsearch_service test suite:
     ```
-    node scripts/jest src/core/server/elasticsearch/elasticsearch_service.test.ts
+    yarn test:jest src/core/server/elasticsearch/elasticsearch_service.test.ts
     ```
   - Run the jest test case whose description matches `stops both admin and data clients`:
     ```
-    node scripts/jest -t 'stops both admin and data clients' src/core/server/elasticsearch/elasticsearch_service.test.ts
+    yarn test:jest -t 'stops both admin and data clients' src/core/server/elasticsearch/elasticsearch_service.test.ts
     ```
   - Run the api integration test case whose description matches the given string:
     ```
-    node scripts/functional_tests_server --config test/api_integration/config.js
-    node scripts/functional_test_runner --config test/api_integration/config.js --grep='should return 404 if id does not match any sample data sets'
+    yarn test:ftr:server --config test/api_integration/config.js
+    yarn test:ftr:runner --config test/api_integration/config.js --grep='should return 404 if id does not match any sample data sets'
     ```
 
 ### Debugging Unit Tests
 
 The standard `yarn test` task runs several sub tasks and can take several minutes to complete, making debugging failures pretty painful. In order to ease the pain specialized tasks provide alternate methods for running the tests.
 
-To execute both server and browser tests, but skip linting, use `yarn test:quick`.
-
-```bash
-yarn test:quick
-```
-
-Use `yarn test:mocha` when you want to run the mocha tests.
-
-```bash
-yarn test:mocha
-```
-
-When you'd like to execute individual server-side test files, you can use the command below. Note that this command takes care of configuring Mocha with Babel compilation for you, and you'll be better off avoiding a globally installed `mocha` package. This command is great for development and for quickly identifying bugs.
-
-```bash
-node scripts/mocha <file>
-```
-
 You could also add the `--debug` option so that `node` is run using the `--debug-brk` flag. You'll need to connect a remote debugger such as [`node-inspector`](https://github.com/node-inspector/node-inspector) to proceed in this mode.
 
 ```bash
 node scripts/mocha --debug <file>
 ```
 
-With `yarn test:browser`, you can run only the browser tests. Coverage reports are available for browser tests by running `yarn test:coverage`. You can find the results under the `coverage/` directory that will be created upon completion.
+With `yarn test:karma`, you can run only the browser tests. Coverage reports are available for browser tests by running `yarn test:coverage`. You can find the results under the `coverage/` directory that will be created upon completion.
 
 ```bash
-yarn test:browser
+yarn test:karma
 ```
 
-Using `yarn test:dev` initializes an environment for debugging the browser tests. Includes an dedicated instance of the kibana server for building the test bundle, and a karma server. When running this task the build is optimized for the first time and then a karma-owned instance of the browser is opened. Click the "debug" button to open a new tab that executes the unit tests.
+Using `yarn test:karma:debug` initializes an environment for debugging the browser tests. Includes an dedicated instance of the kibana server for building the test bundle, and a karma server. When running this task the build is optimized for the first time and then a karma-owned instance of the browser is opened. Click the "debug" button to open a new tab that executes the unit tests.
 
 ```bash
-yarn test:dev
+yarn test:karma:debug
 ```
 
 In the screenshot below, you'll notice the URL is `localhost:9876/debug.html`. You can append a `grep` query parameter to this URL and set it to a string value which will be used to exclude tests which don't match. For example, if you changed the URL to `localhost:9876/debug.html?query=my test` and then refreshed the browser, you'd only see tests run which contain "my test" in the test description.
@@ -549,7 +531,7 @@ To run the tests for just your particular plugin run the following command from
 
 ```bash
 yarn test:mocha
-yarn test:browser --dev # remove the --dev flag to run them once and close
+yarn test:karma:debug # remove the debug flag to run them once and close
 ```
 
 ### Automated Accessibility Testing
diff --git a/package.json b/package.json
index 04dc5edb6fd99..b11234b6312e9 100644
--- a/package.json
+++ b/package.json
@@ -40,15 +40,14 @@
     "kbn": "node scripts/kbn",
     "es": "node scripts/es",
     "test": "grunt test",
-    "test:dev": "grunt test:dev",
-    "test:quick": "grunt test:quick",
-    "test:browser": "grunt test:browser",
+    "test:karma": "grunt test:karma",
+    "test:karma:debug": "grunt test:karmaDebug",
     "test:jest": "node scripts/jest",
     "test:mocha": "node scripts/mocha",
     "test:mocha:coverage": "grunt test:mochaCoverage",
-    "test:ui": "node scripts/functional_tests",
-    "test:ui:server": "node scripts/functional_tests_server",
-    "test:ui:runner": "node scripts/functional_test_runner",
+    "test:ftr": "node scripts/functional_tests",
+    "test:ftr:server": "node scripts/functional_tests_server",
+    "test:ftr:runner": "node scripts/functional_test_runner",
     "test:coverage": "grunt test:coverage",
     "typespec": "typings-tester --config x-pack/legacy/plugins/canvas/public/lib/aeroelastic/tsconfig.json x-pack/legacy/plugins/canvas/public/lib/aeroelastic/__fixtures__/typescript/typespec_tests.ts",
     "checkLicenses": "node scripts/check_licenses --dev",
@@ -56,7 +55,6 @@
     "start": "node --trace-warnings --throw-deprecation scripts/kibana --dev",
     "debug": "node --nolazy --inspect scripts/kibana --dev",
     "debug-break": "node --nolazy --inspect-brk scripts/kibana --dev",
-    "karma": "karma start",
     "lint": "yarn run lint:es && yarn run lint:sass",
     "lint:es": "node scripts/eslint",
     "lint:sass": "node scripts/sasslint",
diff --git a/packages/kbn-plugin-generator/README.md b/packages/kbn-plugin-generator/README.md
index 86871978aedeb..95de0e93fd075 100644
--- a/packages/kbn-plugin-generator/README.md
+++ b/packages/kbn-plugin-generator/README.md
@@ -71,7 +71,7 @@ Generated plugins receive a handful of scripts that can be used during developme
 
     Build a distributable archive of your plugin.
 
-  - `yarn test:browser`
+  - `yarn test:karma`
 
     Run the browser tests in a real web browser.
 
diff --git a/packages/kbn-plugin-generator/integration_tests/generate_plugin.test.js b/packages/kbn-plugin-generator/integration_tests/generate_plugin.test.js
index 51a404379fedb..771bf43c4020a 100644
--- a/packages/kbn-plugin-generator/integration_tests/generate_plugin.test.js
+++ b/packages/kbn-plugin-generator/integration_tests/generate_plugin.test.js
@@ -70,8 +70,8 @@ describe(`running the plugin-generator via 'node scripts/generate_plugin.js plug
   });
 
   describe(`then running`, () => {
-    it(`'yarn test:browser' should exit 0`, async () => {
-      await execa('yarn', ['test:browser'], {
+    it(`'yarn test:karma' should exit 0`, async () => {
+      await execa('yarn', ['test:karma'], {
         cwd: generatedPath,
         env: {
           DISABLE_JUNIT_REPORTER: '1',
diff --git a/packages/kbn-plugin-generator/sao_template/template/README.md b/packages/kbn-plugin-generator/sao_template/template/README.md
index 1e0139428fcbc..008d500abbbf5 100755
--- a/packages/kbn-plugin-generator/sao_template/template/README.md
+++ b/packages/kbn-plugin-generator/sao_template/template/README.md
@@ -9,4 +9,3 @@
 ## Development
 
 See the [kibana contributing guide](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md) for instructions setting up your development environment.
-
diff --git a/packages/kbn-plugin-helpers/README.md b/packages/kbn-plugin-helpers/README.md
index b1fe525a9c116..4c648fd9bde8c 100644
--- a/packages/kbn-plugin-helpers/README.md
+++ b/packages/kbn-plugin-helpers/README.md
@@ -30,8 +30,8 @@ $ plugin-helpers help
     start                       Start kibana and have it include this plugin
     build [options] [files...]  Build a distributable archive
     test                        Run the server and browser tests
-    test:browser [options]      Run the browser tests in a real web browser
-    test:server [files...]      Run the server tests using mocha
+    test:karma [options]      Run the browser tests in a real web browser
+    test:mocha [files...]      Run the server tests using mocha
 
   Options:
 
diff --git a/packages/kbn-plugin-helpers/cli.js b/packages/kbn-plugin-helpers/cli.js
index da146ae387945..c6fc48bc5be9a 100644
--- a/packages/kbn-plugin-helpers/cli.js
+++ b/packages/kbn-plugin-helpers/cli.js
@@ -66,24 +66,24 @@ program
   .action(createCommanderAction('testAll'));
 
 program
-  .command('test:browser')
+  .command('test:karma')
   .description('Run the browser tests in a real web browser')
   .option('--dev', 'Enable dev mode, keeps the test server running')
   .option('-p, --plugins <plugin-ids>', "Manually specify which plugins' test bundles to run")
-  .on('--help', docs('test/browser'))
+  .on('--help', docs('test/karma'))
   .action(
-    createCommanderAction('testBrowser', command => ({
+    createCommanderAction('testKarma', command => ({
       dev: Boolean(command.dev),
       plugins: command.plugins,
     }))
   );
 
 program
-  .command('test:server [files...]')
+  .command('test:mocha [files...]')
   .description('Run the server tests using mocha')
-  .on('--help', docs('test/server'))
+  .on('--help', docs('test/mocha'))
   .action(
-    createCommanderAction('testServer', (command, files) => ({
+    createCommanderAction('testMocha', (command, files) => ({
       files: files,
     }))
   );
diff --git a/packages/kbn-plugin-helpers/lib/index.d.ts b/packages/kbn-plugin-helpers/lib/index.d.ts
index 1515bf6b84bfb..6f1eb73505b05 100644
--- a/packages/kbn-plugin-helpers/lib/index.d.ts
+++ b/packages/kbn-plugin-helpers/lib/index.d.ts
@@ -21,6 +21,6 @@ export function babelRegister(): void;
 export function resolveKibanaPath(path: string): string;
 export function readFtrConfigFile(path: string): any;
 export function run(
-  task: 'build' | 'start' | 'testAll' | 'testBrowser' | 'testServer' | 'postinstall',
+  task: 'build' | 'start' | 'testAll' | 'testKarma' | 'testMocha' | 'postinstall',
   options: any
 ): Promise<void>;
diff --git a/packages/kbn-plugin-helpers/lib/tasks.js b/packages/kbn-plugin-helpers/lib/tasks.js
index 608a1617ffdb2..0e33e2086d9c4 100644
--- a/packages/kbn-plugin-helpers/lib/tasks.js
+++ b/packages/kbn-plugin-helpers/lib/tasks.js
@@ -20,15 +20,15 @@
 const buildTask = require('../tasks/build');
 const startTask = require('../tasks/start');
 const testAllTask = require('../tasks/test/all');
-const testBrowserTask = require('../tasks/test/browser');
-const testServerTask = require('../tasks/test/server');
+const testKarmaTask = require('../tasks/test/karma');
+const testMochaTask = require('../tasks/test/mocha');
 const postinstallTask = require('../tasks/postinstall');
 
 module.exports = {
   build: buildTask,
   start: startTask,
   testAll: testAllTask,
-  testBrowser: testBrowserTask,
-  testServer: testServerTask,
+  testKarma: testKarmaTask,
+  testMocha: testMochaTask,
   postinstall: postinstallTask,
 };
diff --git a/packages/kbn-plugin-helpers/tasks/test/all/README.md b/packages/kbn-plugin-helpers/tasks/test/all/README.md
index cf34b1dce2869..4f5a72ac0d523 100644
--- a/packages/kbn-plugin-helpers/tasks/test/all/README.md
+++ b/packages/kbn-plugin-helpers/tasks/test/all/README.md
@@ -1,3 +1,3 @@
-Runs both the server and browser tests, in that order. 
+Runs both the mocha and karma tests, in that order.
 
-This is just a simple caller to both `test/server` and `test/browser`
\ No newline at end of file
+This is just a simple caller to both `test/mocha` and `test/karma`
\ No newline at end of file
diff --git a/packages/kbn-plugin-helpers/tasks/test/all/test_all_action.js b/packages/kbn-plugin-helpers/tasks/test/all/test_all_action.js
index da0b46e47a3d3..f16e391b2b211 100644
--- a/packages/kbn-plugin-helpers/tasks/test/all/test_all_action.js
+++ b/packages/kbn-plugin-helpers/tasks/test/all/test_all_action.js
@@ -18,6 +18,6 @@
  */
 
 module.exports = function testAllAction(plugin, run) {
-  run('testServer');
-  run('testBrowser');
+  run('testMocha');
+  run('testKarma');
 };
diff --git a/packages/kbn-plugin-helpers/tasks/test/browser/README.md b/packages/kbn-plugin-helpers/tasks/test/karma/README.md
similarity index 88%
rename from packages/kbn-plugin-helpers/tasks/test/browser/README.md
rename to packages/kbn-plugin-helpers/tasks/test/karma/README.md
index 29e748396b6ec..8d921e8312344 100644
--- a/packages/kbn-plugin-helpers/tasks/test/browser/README.md
+++ b/packages/kbn-plugin-helpers/tasks/test/karma/README.md
@@ -21,12 +21,12 @@ Browser tests are written just like server tests, they are just executed differe
 starting the test runner
 ========================
 
-Under the covers this command uses the `test:browser` task from kibana. This will execute
+Under the covers this command uses the `test:karma` task from kibana. This will execute
 your tasks once and exit when complete.
 
-When run with the `--dev` option, the command uses the `test:dev` task from kibana. 
-This task sets-up a test runner that will watch your code for changes and rebuild your 
-tests when necessary. You access the test runner through a browser that it starts itself 
+When run with the `--dev` option, the command uses the `test:karma:debug` task from kibana.
+This task sets-up a test runner that will watch your code for changes and rebuild your
+tests when necessary. You access the test runner through a browser that it starts itself
 (via Karma).
 
 If your plugin consists of a number of internal plugins, you may wish to keep the tests
diff --git a/packages/kbn-plugin-helpers/tasks/test/server/index.js b/packages/kbn-plugin-helpers/tasks/test/karma/index.js
similarity index 94%
rename from packages/kbn-plugin-helpers/tasks/test/server/index.js
rename to packages/kbn-plugin-helpers/tasks/test/karma/index.js
index b95d0321b04fe..0472207948c3d 100644
--- a/packages/kbn-plugin-helpers/tasks/test/server/index.js
+++ b/packages/kbn-plugin-helpers/tasks/test/karma/index.js
@@ -17,4 +17,4 @@
  * under the License.
  */
 
-module.exports = require('./test_server_action');
+module.exports = require('./test_karma_action');
diff --git a/packages/kbn-plugin-helpers/tasks/test/browser/test_browser_action.js b/packages/kbn-plugin-helpers/tasks/test/karma/test_karma_action.js
similarity index 91%
rename from packages/kbn-plugin-helpers/tasks/test/browser/test_browser_action.js
rename to packages/kbn-plugin-helpers/tasks/test/karma/test_karma_action.js
index 2fa89f0d84349..e1ba8caee7f9a 100644
--- a/packages/kbn-plugin-helpers/tasks/test/browser/test_browser_action.js
+++ b/packages/kbn-plugin-helpers/tasks/test/karma/test_karma_action.js
@@ -20,7 +20,7 @@
 const execFileSync = require('child_process').execFileSync;
 const winCmd = require('../../../lib/win_cmd');
 
-module.exports = function testBrowserAction(plugin, run, options) {
+module.exports = function testKarmaAction(plugin, run, options) {
   options = options || {};
 
   const kbnServerArgs = ['--kbnServer.plugin-path=' + plugin.root];
@@ -31,7 +31,7 @@ module.exports = function testBrowserAction(plugin, run, options) {
     kbnServerArgs.push('--kbnServer.tests_bundle.pluginId=' + plugin.id);
   }
 
-  const task = options.dev ? 'test:dev' : 'test:browser';
+  const task = options.dev ? 'test:karma:debug' : 'test:karma';
   const args = [task].concat(kbnServerArgs);
   execFileSync(winCmd('yarn'), args, {
     cwd: plugin.kibanaRoot,
diff --git a/packages/kbn-plugin-helpers/tasks/test/server/README.md b/packages/kbn-plugin-helpers/tasks/test/mocha/README.md
similarity index 85%
rename from packages/kbn-plugin-helpers/tasks/test/server/README.md
rename to packages/kbn-plugin-helpers/tasks/test/mocha/README.md
index 4ceb855d165c5..6f5997a40a23b 100644
--- a/packages/kbn-plugin-helpers/tasks/test/server/README.md
+++ b/packages/kbn-plugin-helpers/tasks/test/mocha/README.md
@@ -20,14 +20,14 @@ Server tests are written just like browser tests, they are just executed differe
 running the tests
 =================
 
-Running the server tests is simple, just execute `yarn test:server` in your terminal
+Running the server tests is simple, just execute `yarn test:mocha` in your terminal
 and all of the tests in your server will be run.
 
 By default, the runner will look for tests in `server/**/__tests__/**/*.js`. If you'd prefer to
-use a different collection of globs and files, you can specify them after the `yarn test:server`
+use a different collection of globs and files, you can specify them after the `yarn test:mocha`
 task, like so:
 
-`yarn test:server 'plugins/myplugins/server/__tests__/**/*.js'`
+`yarn test:mocha 'plugins/myplugins/server/__tests__/**/*.js'`
 
 NOTE: quoting the glob pattern is not required, but helps to avoid issues with globbing expansion
 in your shell.
diff --git a/packages/kbn-plugin-helpers/tasks/test/browser/index.js b/packages/kbn-plugin-helpers/tasks/test/mocha/index.js
similarity index 94%
rename from packages/kbn-plugin-helpers/tasks/test/browser/index.js
rename to packages/kbn-plugin-helpers/tasks/test/mocha/index.js
index 972da4eca4518..1ab022db588f9 100644
--- a/packages/kbn-plugin-helpers/tasks/test/browser/index.js
+++ b/packages/kbn-plugin-helpers/tasks/test/mocha/index.js
@@ -17,4 +17,4 @@
  * under the License.
  */
 
-module.exports = require('./test_browser_action');
+module.exports = require('./test_mocha_action');
diff --git a/packages/kbn-plugin-helpers/tasks/test/server/test_server_action.js b/packages/kbn-plugin-helpers/tasks/test/mocha/test_mocha_action.js
similarity index 100%
rename from packages/kbn-plugin-helpers/tasks/test/server/test_server_action.js
rename to packages/kbn-plugin-helpers/tasks/test/mocha/test_mocha_action.js
diff --git a/tasks/config/karma.js b/tasks/config/karma.js
index ec37277cae0f8..9992dafed41c5 100644
--- a/tasks/config/karma.js
+++ b/tasks/config/karma.js
@@ -176,11 +176,11 @@ module.exports = function(grunt) {
    *  (&shard_num=Y), are added to the testing bundle url and read by the
    *  test_harness/setup_test_sharding[1] module. This allows us to use a
    *  different number of shards in different scenarios (ie. running
-   *  `yarn test:browser` runs the tests in a single shard, effectively
+   *  `yarn test:karma` runs the tests in a single shard, effectively
    *  disabling sharding)
    *
    *  These same parameters can also be defined in the URL/query string of the
-   *  karma debug page (started when you run `yarn test:dev`).
+   *  karma debug page (started when you run `yarn test:karma:debug`).
    *
    *  ## debugging
    *
diff --git a/tasks/config/run.js b/tasks/config/run.js
index 4ba450129e93c..a47634a93ef14 100644
--- a/tasks/config/run.js
+++ b/tasks/config/run.js
@@ -54,7 +54,7 @@ module.exports = function(grunt) {
     };
   }
 
-  const browserTestServerFlags = [
+  const karmaTestServerFlags = [
     '--env.name=development',
     '--plugins.initialize=false',
     '--optimize.bundleFilter=tests',
@@ -170,27 +170,27 @@ module.exports = function(grunt) {
       ],
     }),
 
-    // used by the test:browser task
+    // used by the test:karma task
     //    runs the kibana server to serve the browser test bundle
-    browserTestServer: createKbnServerTask({
-      flags: [...browserTestServerFlags],
+    karmaTestServer: createKbnServerTask({
+      flags: [...karmaTestServerFlags],
     }),
     browserSCSS: createKbnServerTask({
-      flags: [...browserTestServerFlags, '--optimize', '--optimize.enabled=false'],
+      flags: [...karmaTestServerFlags, '--optimize', '--optimize.enabled=false'],
     }),
 
     // used by the test:coverage task
     //    runs the kibana server to serve the instrumented version of the browser test bundle
-    browserTestCoverageServer: createKbnServerTask({
-      flags: [...browserTestServerFlags, '--tests_bundle.instrument=true'],
+    karmaTestCoverageServer: createKbnServerTask({
+      flags: [...karmaTestServerFlags, '--tests_bundle.instrument=true'],
     }),
 
-    // used by the test:dev task
+    // used by the test:karma:debug task
     //    runs the kibana server to serve the browser test bundle, but listens for changes
     //    to the public/browser code and rebuilds the test bundle on changes
-    devBrowserTestServer: createKbnServerTask({
+    karmaTestDebugServer: createKbnServerTask({
       flags: [
-        ...browserTestServerFlags,
+        ...karmaTestServerFlags,
         '--dev',
         '--no-dev-config',
         '--no-watch',
@@ -304,7 +304,7 @@ module.exports = function(grunt) {
       'test:jest_integration'
     ),
     test_projects: gruntTaskWithGithubChecks('Project tests', 'test:projects'),
-    test_browser_ci: gruntTaskWithGithubChecks('Browser tests', 'test:browser-ci'),
+    test_karma_ci: gruntTaskWithGithubChecks('Browser tests', 'test:karma-ci'),
 
     ...getFunctionalTestGroupRunConfigs({
       kibanaInstallDir: KIBANA_INSTALL_DIR,
diff --git a/tasks/jenkins.js b/tasks/jenkins.js
index 733d665630019..b40908c9b56c3 100644
--- a/tasks/jenkins.js
+++ b/tasks/jenkins.js
@@ -36,7 +36,7 @@ module.exports = function(grunt) {
     'run:test_jest',
     'run:test_jest_integration',
     'run:test_projects',
-    'run:test_browser_ci',
+    'run:test_karma_ci',
     'run:apiIntegrationTests',
   ]);
 };
diff --git a/tasks/test.js b/tasks/test.js
index 73257b881c38d..c995502836378 100644
--- a/tasks/test.js
+++ b/tasks/test.js
@@ -31,21 +31,21 @@ module.exports = function(grunt) {
     }
   );
 
-  grunt.registerTask('test:browser', [
+  grunt.registerTask('test:karma', [
     'checkPlugins',
     'run:browserSCSS',
-    'run:browserTestServer',
+    'run:karmaTestServer',
     'karma:unit',
   ]);
 
-  grunt.registerTask('test:browser-ci', () => {
+  grunt.registerTask('test:karma-ci', () => {
     const ciShardTasks = keys(grunt.config.get('karma'))
       .filter(key => key.startsWith('ciShard-'))
       .map(key => `karma:${key}`);
 
     grunt.log.ok(`Running UI tests in ${ciShardTasks.length} shards`);
     grunt.task.run(['run:browserSCSS']);
-    grunt.task.run(['run:browserTestServer', ...ciShardTasks]);
+    grunt.task.run(['run:karmaTestServer', ...ciShardTasks]);
   });
 
   grunt.registerTask('test:coverage', ['run:testCoverageServer', 'karma:coverage']);
@@ -57,11 +57,11 @@ module.exports = function(grunt) {
     'test:jest',
     'test:jest_integration',
     'test:projects',
-    'test:browser',
+    'test:karma',
     'run:apiIntegrationTests',
   ]);
 
-  grunt.registerTask('test:dev', ['checkPlugins', 'run:devBrowserTestServer', 'karma:dev']);
+  grunt.registerTask('test:karmaDebug', ['checkPlugins', 'run:karmaDebugServer', 'karma:dev']);
   grunt.registerTask('test:mochaCoverage', ['run:mochaCoverage']);
 
   grunt.registerTask('test', subTask => {
diff --git a/test/scripts/jenkins_xpack.sh b/test/scripts/jenkins_xpack.sh
index e076bd43e1c6e..3d30496ecb582 100755
--- a/test/scripts/jenkins_xpack.sh
+++ b/test/scripts/jenkins_xpack.sh
@@ -5,7 +5,7 @@ source test/scripts/jenkins_test_setup.sh
 if [[ -z "$CODE_COVERAGE" ]] ; then
   echo " -> Running mocha tests"
   cd "$XPACK_DIR"
-  checks-reporter-with-killswitch "X-Pack Karma Tests" yarn test:browser
+  checks-reporter-with-killswitch "X-Pack Karma Tests" yarn test:karma
   echo ""
   echo ""
 
diff --git a/x-pack/README.md b/x-pack/README.md
index e92d6849538bb..42e54aa2f50f9 100644
--- a/x-pack/README.md
+++ b/x-pack/README.md
@@ -46,17 +46,17 @@ yarn test --plugins <plugin>[,<plugin>]*    # where <plugin> is "reporting", etc
 
 #### Debugging browser tests
 ```
-yarn test:browser:dev
+yarn test:karma:debug
 ```
 Initializes an environment for debugging the browser tests. Includes an dedicated instance of the kibana server for building the test bundle, and a karma server. When running this task the build is optimized for the first time and then a karma-owned instance of the browser is opened. Click the "debug" button to open a new tab that executes the unit tests.
 
 Run single tests by appending `grep` parameter to the end of the URL. For example `http://localhost:9876/debug.html?grep=ML%20-%20Explorer%20Controller` will only run tests with 'ML - Explorer Controller' in the describe block.
 
 #### Running server unit tests
-You can run server-side unit tests by running:
+You can run mocha unit tests by running:
 
 ```
-yarn test:server
+yarn test:mocha
 ```
 
 #### Running functional tests
diff --git a/x-pack/gulpfile.js b/x-pack/gulpfile.js
index d3f93c29e3df8..0118d178f54e5 100644
--- a/x-pack/gulpfile.js
+++ b/x-pack/gulpfile.js
@@ -8,7 +8,7 @@ require('../src/setup_node_env');
 
 const { buildTask } = require('./tasks/build');
 const { devTask } = require('./tasks/dev');
-const { testTask, testBrowserTask, testBrowserDevTask } = require('./tasks/test');
+const { testTask, testKarmaTask, testKarmaDebugTask } = require('./tasks/test');
 const { prepareTask } = require('./tasks/prepare');
 
 // export the tasks that are runnable from the CLI
@@ -17,6 +17,6 @@ module.exports = {
   dev: devTask,
   prepare: prepareTask,
   test: testTask,
-  testbrowser: testBrowserTask,
-  'testbrowser-dev': testBrowserDevTask,
+  'test:karma': testKarmaTask,
+  'test:karma:debug': testKarmaDebugTask,
 };
diff --git a/x-pack/legacy/plugins/canvas/scripts/test_browser.js b/x-pack/legacy/plugins/canvas/scripts/test_browser.js
index 971a04d9d97c2..e04fac0615284 100644
--- a/x-pack/legacy/plugins/canvas/scripts/test_browser.js
+++ b/x-pack/legacy/plugins/canvas/scripts/test_browser.js
@@ -4,4 +4,4 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-require('./_helpers').runGulpTask('canvas:test:browser');
+require('./_helpers').runGulpTask('canvas:test:karma');
diff --git a/x-pack/legacy/plugins/canvas/scripts/test_dev.js b/x-pack/legacy/plugins/canvas/scripts/test_dev.js
index 656168b23af4b..8b03d7930d473 100644
--- a/x-pack/legacy/plugins/canvas/scripts/test_dev.js
+++ b/x-pack/legacy/plugins/canvas/scripts/test_dev.js
@@ -4,4 +4,4 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-require('./_helpers').runGulpTask('canvas:test:dev');
+require('./_helpers').runGulpTask('canvas:karma:debug');
diff --git a/x-pack/legacy/plugins/canvas/scripts/test_server.js b/x-pack/legacy/plugins/canvas/scripts/test_server.js
index a05994c4d8606..4eb72b566f988 100644
--- a/x-pack/legacy/plugins/canvas/scripts/test_server.js
+++ b/x-pack/legacy/plugins/canvas/scripts/test_server.js
@@ -4,4 +4,4 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-require('./_helpers').runGulpTask('canvas:test:server');
+require('./_helpers').runGulpTask('canvas:test:mocha');
diff --git a/x-pack/package.json b/x-pack/package.json
index 305aaa1d9457b..9d6b5d76a58e7 100644
--- a/x-pack/package.json
+++ b/x-pack/package.json
@@ -11,8 +11,8 @@
     "build": "gulp build",
     "testonly": "echo 'Deprecated, use `yarn test`' && gulp test",
     "test": "gulp test",
-    "test:browser:dev": "gulp testbrowser-dev",
-    "test:browser": "gulp testbrowser",
+    "test:karma:debug": "gulp test:karma:debug",
+    "test:karma": "gulp test:karma",
     "test:jest": "node scripts/jest",
     "test:mocha": "node scripts/mocha"
   },
diff --git a/x-pack/tasks/helpers/flags.ts b/x-pack/tasks/helpers/flags.ts
index 62a382b02ed22..6c6cdf0d4abb0 100644
--- a/x-pack/tasks/helpers/flags.ts
+++ b/x-pack/tasks/helpers/flags.ts
@@ -19,7 +19,7 @@ import { findPluginSpecs } from '../../../src/legacy/plugin_discovery';
     One of more plugins can be specified, and each one should be command separated, like so:
       gulp testserver --plugins monitoring,reporting
     If using with yarn:
-      yarn test:server --plugins graph
+      yarn test:mocha --plugins graph
 */
 
 const opts = Object.freeze(
diff --git a/x-pack/tasks/test.ts b/x-pack/tasks/test.ts
index 0767d7479724a..78a04413daae7 100644
--- a/x-pack/tasks/test.ts
+++ b/x-pack/tasks/test.ts
@@ -13,19 +13,19 @@ export const testServerTask = async () => {
   throw new Error('server mocha tests are now included in the `node scripts/mocha` script');
 };
 
-export const testBrowserTask = async () => {
+export const testKarmaTask = async () => {
   const plugins = await getEnabledPlugins();
-  await pluginHelpers.run('testBrowser', {
+  await pluginHelpers.run('testKarma', {
     plugins: plugins.join(','),
   });
 };
 
-export const testBrowserDevTask = async () => {
+export const testKarmaDebugTask = async () => {
   const plugins = await getEnabledPlugins();
-  await pluginHelpers.run('testBrowser', {
+  await pluginHelpers.run('testKarma', {
     dev: true,
     plugins: plugins.join(','),
   });
 };
 
-export const testTask = gulp.series(testBrowserTask, testServerTask);
+export const testTask = gulp.series(testKarmaTask, testServerTask);

From 0160aa42118da48d9258859bcb214279708e7aac Mon Sep 17 00:00:00 2001
From: Michail Yasonik <michail.yasonik@elastic.co>
Date: Tue, 18 Feb 2020 17:44:25 -0500
Subject: [PATCH 045/174] Fixing console a11y failures (#57520)

---
 .../application/components/editor_example.tsx | 26 +++++++--
 .../editor/legacy/console_editor/editor.tsx   | 58 ++++++++++---------
 .../legacy/console_editor/editor_output.tsx   | 33 ++++++-----
 src/plugins/console/public/styles/_app.scss   |  1 -
 test/accessibility/config.ts                  |  2 +-
 5 files changed, 70 insertions(+), 50 deletions(-)

diff --git a/src/plugins/console/public/application/components/editor_example.tsx b/src/plugins/console/public/application/components/editor_example.tsx
index ea08a06a9e39b..e9e252e4ebb17 100644
--- a/src/plugins/console/public/application/components/editor_example.tsx
+++ b/src/plugins/console/public/application/components/editor_example.tsx
@@ -16,10 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
-import React, { useEffect } from 'react';
+import { EuiScreenReaderOnly } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
 // @ts-ignore
 import exampleText from 'raw-loader!../constants/help_example.txt';
+import React, { useEffect } from 'react';
 import { createReadOnlyAceEditor } from '../models/legacy_core_editor';
 
 interface EditorExampleProps {
@@ -28,16 +29,31 @@ interface EditorExampleProps {
 
 export function EditorExample(props: EditorExampleProps) {
   const elemId = `help-example-${props.panel}`;
+  const inputId = `help-example-${props.panel}-input`;
 
   useEffect(() => {
-    const el = document.querySelector<HTMLElement>(`#${elemId}`)!;
+    const el = document.getElementById(elemId)!;
     el.textContent = exampleText.trim();
     const editor = createReadOnlyAceEditor(el);
+    const textarea = el.querySelector('textarea')!;
+    textarea.setAttribute('id', inputId);
+    textarea.setAttribute('readonly', 'true');
 
     return () => {
       editor.destroy();
     };
-  }, [elemId]);
+  }, [elemId, inputId]);
 
-  return <div id={elemId} className="conHelp__example" />;
+  return (
+    <>
+      <EuiScreenReaderOnly>
+        <label htmlFor={inputId}>
+          {i18n.translate('console.exampleOutputTextarea', {
+            defaultMessage: 'Dev Tools Console editor example',
+          })}
+        </label>
+      </EuiScreenReaderOnly>
+      <div id={elemId} className="conHelp__example" />
+    </>
+  );
 }
diff --git a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx
index bfa74392c14fb..daf88e28c6440 100644
--- a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx
+++ b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx
@@ -17,31 +17,26 @@
  * under the License.
  */
 
-import React, { CSSProperties, useCallback, useEffect, useRef, useState } from 'react';
-import { EuiToolTip } from '@elastic/eui';
+import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiScreenReaderOnly, EuiToolTip } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
 import { debounce } from 'lodash';
 import { parse } from 'query-string';
-import { EuiIcon, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
-import { useServicesContext, useEditorReadContext } from '../../../../contexts';
-import { useUIAceKeyboardMode } from '../use_ui_ace_keyboard_mode';
+import React, { CSSProperties, useCallback, useEffect, useRef, useState } from 'react';
+// @ts-ignore
+import mappings from '../../../../../lib/mappings/mappings';
 import { ConsoleMenu } from '../../../../components';
-
-import { autoIndent, getDocumentation } from '../console_menu_actions';
-import { registerCommands } from './keyboard_shortcuts';
-import { applyCurrentSettings } from './apply_editor_settings';
-
+import { useEditorReadContext, useServicesContext } from '../../../../contexts';
 import {
+  useSaveCurrentTextObject,
   useSendCurrentRequestToES,
   useSetInputEditor,
-  useSaveCurrentTextObject,
 } from '../../../../hooks';
-
 import * as senseEditor from '../../../../models/sense_editor';
-// @ts-ignore
-import mappings from '../../../../../lib/mappings/mappings';
-
+import { autoIndent, getDocumentation } from '../console_menu_actions';
 import { subscribeResizeChecker } from '../subscribe_console_resize_checker';
+import { useUIAceKeyboardMode } from '../use_ui_ace_keyboard_mode';
+import { applyCurrentSettings } from './apply_editor_settings';
+import { registerCommands } from './keyboard_shortcuts';
 
 export interface EditorProps {
   initialTextValue: string;
@@ -66,6 +61,8 @@ const DEFAULT_INPUT_VALUE = `GET _search
   }
 }`;
 
+const inputId = 'ConAppInputTextarea';
+
 function EditorUI({ initialTextValue }: EditorProps) {
   const {
     services: { history, notifications },
@@ -95,6 +92,11 @@ function EditorUI({ initialTextValue }: EditorProps) {
   useEffect(() => {
     editorInstanceRef.current = senseEditor.create(editorRef.current!);
     const editor = editorInstanceRef.current;
+    const textareaElement = editorRef.current!.querySelector('textarea');
+
+    if (textareaElement) {
+      textareaElement.setAttribute('id', inputId);
+    }
 
     const readQueryParams = () => {
       const [, queryString] = (window.location.hash || '').split('?');
@@ -244,19 +246,19 @@ function EditorUI({ initialTextValue }: EditorProps) {
           </EuiFlexItem>
         </EuiFlexGroup>
 
-        {/* Axe complains about Ace's textarea element missing a label, which interferes with our
-        automated a11y tests per #52136. This wrapper does nothing to address a11y but it does
-        satisfy Axe. */}
-
-        {/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
-        <label className="conApp__textAreaLabelHack">
-          <div
-            ref={editorRef}
-            id="ConAppEditor"
-            className="conApp__editorContent"
-            data-test-subj="request-editor"
-          />
-        </label>
+        <EuiScreenReaderOnly>
+          <label htmlFor={inputId}>
+            {i18n.translate('console.inputTextarea', {
+              defaultMessage: 'Dev Tools Console',
+            })}
+          </label>
+        </EuiScreenReaderOnly>
+        <div
+          ref={editorRef}
+          id="ConAppEditor"
+          className="conApp__editorContent"
+          data-test-subj="request-editor"
+        />
       </div>
     </div>
   );
diff --git a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor_output.tsx b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor_output.tsx
index ca468f85275a8..a8f456d22e726 100644
--- a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor_output.tsx
+++ b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor_output.tsx
@@ -17,17 +17,16 @@
  * under the License.
  */
 
+import { EuiScreenReaderOnly } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
 import React, { useEffect, useRef } from 'react';
-
-import { createReadOnlyAceEditor, CustomAceEditor } from '../../../../models/legacy_core_editor';
+import { expandLiteralStrings } from '../../../../../../../es_ui_shared/console_lang/lib';
 import {
-  useServicesContext,
   useEditorReadContext,
   useRequestReadContext,
+  useServicesContext,
 } from '../../../../contexts';
-
-import { expandLiteralStrings } from '../../../../../../../es_ui_shared/console_lang/lib';
-
+import { createReadOnlyAceEditor, CustomAceEditor } from '../../../../models/legacy_core_editor';
 import { subscribeResizeChecker } from '../subscribe_console_resize_checker';
 import { applyCurrentSettings } from './apply_editor_settings';
 
@@ -44,18 +43,22 @@ function EditorOutputUI() {
   const editorRef = useRef<null | HTMLDivElement>(null);
   const editorInstanceRef = useRef<null | CustomAceEditor>(null);
   const { services } = useServicesContext();
-
   const { settings: readOnlySettings } = useEditorReadContext();
   const {
     lastResult: { data, error },
   } = useRequestReadContext();
+  const inputId = 'ConAppOutputTextarea';
 
   useEffect(() => {
     editorInstanceRef.current = createReadOnlyAceEditor(editorRef.current!);
     const unsubscribe = subscribeResizeChecker(editorRef.current!, editorInstanceRef.current);
+    const textarea = editorRef.current!.querySelector('textarea')!;
+    textarea.setAttribute('id', inputId);
+    textarea.setAttribute('readonly', 'true');
 
     return () => {
       unsubscribe();
+      editorInstanceRef.current!.destroy();
     };
   }, [services.settings]);
 
@@ -84,15 +87,15 @@ function EditorOutputUI() {
 
   return (
     <>
-      <div ref={editorRef} className="conApp__output" data-test-subj="response-editor">
-        {/* Axe complains about Ace's textarea element missing a label, which interferes with our
-      automated a11y tests per #52136. This wrapper does nothing to address a11y but it does
-      satisfy Axe. */}
-
-        {/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
-        <label className="conApp__textAreaLabelHack">
-          <div className="conApp__outputContent" id="ConAppOutput" />
+      <EuiScreenReaderOnly>
+        <label htmlFor={inputId}>
+          {i18n.translate('console.outputTextarea', {
+            defaultMessage: 'Dev Tools Console output',
+          })}
         </label>
+      </EuiScreenReaderOnly>
+      <div ref={editorRef} className="conApp__output" data-test-subj="response-editor">
+        <div className="conApp__outputContent" id="ConAppOutput" />
       </div>
     </>
   );
diff --git a/src/plugins/console/public/styles/_app.scss b/src/plugins/console/public/styles/_app.scss
index 865a4fc7fafb0..047db6e3ad877 100644
--- a/src/plugins/console/public/styles/_app.scss
+++ b/src/plugins/console/public/styles/_app.scss
@@ -37,7 +37,6 @@
   flex: 1 1 1px;
 }
 
-.conApp__textAreaLabelHack,
 .conApp__editorContent,
 .conApp__outputContent {
   height: 100%;
diff --git a/test/accessibility/config.ts b/test/accessibility/config.ts
index 48fe97e2d0e38..dd8c59c1be835 100644
--- a/test/accessibility/config.ts
+++ b/test/accessibility/config.ts
@@ -32,7 +32,7 @@ export default async function({ readConfigFile }: FtrConfigProviderContext) {
       require.resolve('./apps/dashboard'),
       require.resolve('./apps/visualize'),
       require.resolve('./apps/management'),
-      // require.resolve('./apps/console'),
+      require.resolve('./apps/console'),
       require.resolve('./apps/home'),
     ],
     pageObjects,

From e8994abebe7f2f0693713fad40ed5e9899a11497 Mon Sep 17 00:00:00 2001
From: Jonathan Buttner <56361221+jonathan-buttner@users.noreply.github.com>
Date: Tue, 18 Feb 2020 18:15:45 -0500
Subject: [PATCH 046/174] [Endpoint] Adding the endpoint teams for relevant
 directories (#57920)

* Adding the endpoint teams for relevant directories

* Using combined team
---
 .github/CODEOWNERS | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 50d157a1da3dc..33fbddbd1faf8 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -177,3 +177,9 @@
 /x-pack/plugins/searchprofiler/  @elastic/es-ui
 /x-pack/legacy/plugins/snapshot_restore/  @elastic/es-ui
 /x-pack/plugins/watcher/  @elastic/es-ui
+
+# Endpoint
+/x-pack/plugins/endpoint/ @elastic/endpoint-app-team
+/x-pack/test/api_integration/apis/endpoint/ @elastic/endpoint-app-team
+/x-pack/test/functional/apps/endpoint/ @elastic/endpoint-app-team
+/x-pack/test/functional/es_archives/endpoint/ @elastic/endpoint-app-team

From c47612f240b1204323d1e06382c948bc1a1dc21a Mon Sep 17 00:00:00 2001
From: Tim Sullivan <tsullivan@users.noreply.github.com>
Date: Tue, 18 Feb 2020 16:20:53 -0700
Subject: [PATCH 047/174] [Reporting] Unit test for screenshot observable
 (#57638)

[Reporting] test the screenshot observable
---
 .../common/layouts/print_layout.ts            |   4 +-
 .../common/lib/screenshots/check_for_toast.ts |  19 +-
 .../common/lib/screenshots/constants.ts       |  14 ++
 .../screenshots/get_element_position_data.ts  |  73 ++++---
 .../lib/screenshots/get_number_of_items.ts    |  29 +--
 .../common/lib/screenshots/get_screenshots.ts |   2 +-
 .../common/lib/screenshots/get_time_range.ts  |  33 +--
 .../common/lib/screenshots/index.ts           |  90 +-------
 .../common/lib/screenshots/inject_css.ts      |  23 ++-
 .../common/lib/screenshots/observable.test.ts | 194 ++++++++++++++++++
 .../common/lib/screenshots/observable.ts      |  96 +++++++++
 .../common/lib/screenshots/open_url.ts        |   2 +-
 .../common/lib/screenshots/scan_page.ts       |   2 +-
 .../common/lib/screenshots/skip_telemetry.ts  |  27 ++-
 .../lib/screenshots/wait_for_dom_elements.ts  |  19 +-
 .../common/lib/screenshots/wait_for_render.ts |  75 +++----
 .../chromium/driver/chromium_driver.ts        |  53 +++--
 .../reporting/server/browsers/index.ts        |   3 +
 .../plugins/reporting/server/types.d.ts       |   9 +-
 .../create_mock_browserdriverfactory.ts       | 132 ++++++++++++
 .../create_mock_layoutinstance.ts             |  25 +++
 .../plugins/reporting/test_helpers/index.ts   |   2 +
 x-pack/legacy/plugins/reporting/types.d.ts    |   5 +-
 23 files changed, 684 insertions(+), 247 deletions(-)
 create mode 100644 x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/constants.ts
 create mode 100644 x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/observable.test.ts
 create mode 100644 x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/observable.ts
 create mode 100644 x-pack/legacy/plugins/reporting/test_helpers/create_mock_browserdriverfactory.ts
 create mode 100644 x-pack/legacy/plugins/reporting/test_helpers/create_mock_layoutinstance.ts

diff --git a/x-pack/legacy/plugins/reporting/export_types/common/layouts/print_layout.ts b/x-pack/legacy/plugins/reporting/export_types/common/layouts/print_layout.ts
index 44361181e3262..6007c2960057a 100644
--- a/x-pack/legacy/plugins/reporting/export_types/common/layouts/print_layout.ts
+++ b/x-pack/legacy/plugins/reporting/export_types/common/layouts/print_layout.ts
@@ -6,7 +6,7 @@
 import path from 'path';
 import { EvaluateFn, SerializableOrJSHandle } from 'puppeteer';
 import { LevelLogger } from '../../../server/lib';
-import { HeadlessChromiumDriver } from '../../../server/browsers/chromium/driver';
+import { HeadlessChromiumDriver } from '../../../server/browsers';
 import { ServerFacade } from '../../../types';
 import { LayoutTypes } from '../constants';
 import { getDefaultLayoutSelectors, Layout, LayoutSelectorDictionary, Size } from './layout';
@@ -75,7 +75,7 @@ export class PrintLayout extends Layout {
       args: [this.selectors.screenshot, elementSize.height, elementSize.width],
     };
 
-    await browser.evaluate(evalOptions);
+    await browser.evaluate(evalOptions, { context: 'PositionElements' }, logger);
   }
 
   public getPdfImageSize() {
diff --git a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/check_for_toast.ts b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/check_for_toast.ts
index 50599a927ec67..c888870bd2bc3 100644
--- a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/check_for_toast.ts
+++ b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/check_for_toast.ts
@@ -6,9 +6,10 @@
 
 import { i18n } from '@kbn/i18n';
 import { ElementHandle } from 'puppeteer';
-import { HeadlessChromiumDriver as HeadlessBrowser } from '../../../../server/browsers/chromium/driver';
+import { HeadlessChromiumDriver as HeadlessBrowser } from '../../../../server/browsers';
 import { LevelLogger } from '../../../../server/lib';
 import { LayoutInstance } from '../../layouts/layout';
+import { CONTEXT_CHECKFORTOASTMESSAGE } from './constants';
 
 export const checkForToastMessage = async (
   browser: HeadlessBrowser,
@@ -20,13 +21,17 @@ export const checkForToastMessage = async (
     .then(async () => {
       // Check for a toast message on the page. If there is one, capture the
       // message and throw an error, to fail the screenshot.
-      const toastHeaderText: string = await browser.evaluate({
-        fn: selector => {
-          const nodeList = document.querySelectorAll(selector);
-          return nodeList.item(0).innerText;
+      const toastHeaderText: string = await browser.evaluate(
+        {
+          fn: selector => {
+            const nodeList = document.querySelectorAll(selector);
+            return nodeList.item(0).innerText;
+          },
+          args: [layout.selectors.toastHeader],
         },
-        args: [layout.selectors.toastHeader],
-      });
+        { context: CONTEXT_CHECKFORTOASTMESSAGE },
+        logger
+      );
 
       // Log an error to track the event in kibana server logs
       logger.error(
diff --git a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/constants.ts b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/constants.ts
new file mode 100644
index 0000000000000..bbc97ca57940c
--- /dev/null
+++ b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/constants.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.
+ */
+
+export const CONTEXT_GETNUMBEROFITEMS = 'GetNumberOfItems';
+export const CONTEXT_INJECTCSS = 'InjectCss';
+export const CONTEXT_WAITFORRENDER = 'WaitForRender';
+export const CONTEXT_GETTIMERANGE = 'GetTimeRange';
+export const CONTEXT_ELEMENTATTRIBUTES = 'ElementPositionAndAttributes';
+export const CONTEXT_CHECKFORTOASTMESSAGE = 'CheckForToastMessage';
+export const CONTEXT_WAITFORELEMENTSTOBEINDOM = 'WaitForElementsToBeInDOM';
+export const CONTEXT_SKIPTELEMETRY = 'SkipTelemetry';
diff --git a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/get_element_position_data.ts b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/get_element_position_data.ts
index ce51dc2317c79..4302f4c631e3c 100644
--- a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/get_element_position_data.ts
+++ b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/get_element_position_data.ts
@@ -4,48 +4,55 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { HeadlessChromiumDriver as HeadlessBrowser } from '../../../../server/browsers/chromium/driver';
+import { HeadlessChromiumDriver as HeadlessBrowser } from '../../../../server/browsers';
 import { LayoutInstance } from '../../layouts/layout';
 import { AttributesMap, ElementsPositionAndAttribute } from './types';
+import { Logger } from '../../../../types';
+import { CONTEXT_ELEMENTATTRIBUTES } from './constants';
 
 export const getElementPositionAndAttributes = async (
   browser: HeadlessBrowser,
-  layout: LayoutInstance
+  layout: LayoutInstance,
+  logger: Logger
 ): Promise<ElementsPositionAndAttribute[]> => {
-  const elementsPositionAndAttributes: ElementsPositionAndAttribute[] = await browser.evaluate({
-    fn: (selector, attributes) => {
-      const elements: NodeListOf<Element> = document.querySelectorAll(selector);
+  const elementsPositionAndAttributes: ElementsPositionAndAttribute[] = await browser.evaluate(
+    {
+      fn: (selector: string, attributes: any) => {
+        const elements: NodeListOf<Element> = document.querySelectorAll(selector);
 
-      // NodeList isn't an array, just an iterator, unable to use .map/.forEach
-      const results: ElementsPositionAndAttribute[] = [];
-      for (let i = 0; i < elements.length; i++) {
-        const element = elements[i];
-        const boundingClientRect = element.getBoundingClientRect() as DOMRect;
-        results.push({
-          position: {
-            boundingClientRect: {
-              // modern browsers support x/y, but older ones don't
-              top: boundingClientRect.y || boundingClientRect.top,
-              left: boundingClientRect.x || boundingClientRect.left,
-              width: boundingClientRect.width,
-              height: boundingClientRect.height,
+        // NodeList isn't an array, just an iterator, unable to use .map/.forEach
+        const results: ElementsPositionAndAttribute[] = [];
+        for (let i = 0; i < elements.length; i++) {
+          const element = elements[i];
+          const boundingClientRect = element.getBoundingClientRect() as DOMRect;
+          results.push({
+            position: {
+              boundingClientRect: {
+                // modern browsers support x/y, but older ones don't
+                top: boundingClientRect.y || boundingClientRect.top,
+                left: boundingClientRect.x || boundingClientRect.left,
+                width: boundingClientRect.width,
+                height: boundingClientRect.height,
+              },
+              scroll: {
+                x: window.scrollX,
+                y: window.scrollY,
+              },
             },
-            scroll: {
-              x: window.scrollX,
-              y: window.scrollY,
-            },
-          },
-          attributes: Object.keys(attributes).reduce((result: AttributesMap, key) => {
-            const attribute = attributes[key];
-            result[key] = element.getAttribute(attribute);
-            return result;
-          }, {}),
-        });
-      }
-      return results;
+            attributes: Object.keys(attributes).reduce((result: AttributesMap, key) => {
+              const attribute = attributes[key];
+              (result as any)[key] = element.getAttribute(attribute);
+              return result;
+            }, {} as AttributesMap),
+          });
+        }
+        return results;
+      },
+      args: [layout.selectors.screenshot, { title: 'data-title', description: 'data-description' }],
     },
-    args: [layout.selectors.screenshot, { title: 'data-title', description: 'data-description' }],
-  });
+    { context: CONTEXT_ELEMENTATTRIBUTES },
+    logger
+  );
 
   if (elementsPositionAndAttributes.length === 0) {
     throw new Error(
diff --git a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/get_number_of_items.ts b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/get_number_of_items.ts
index 166d57f972a5c..1beae719cd6b0 100644
--- a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/get_number_of_items.ts
+++ b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/get_number_of_items.ts
@@ -4,9 +4,10 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { HeadlessChromiumDriver as HeadlessBrowser } from '../../../../server/browsers/chromium/driver';
+import { HeadlessChromiumDriver as HeadlessBrowser } from '../../../../server/browsers';
 import { LevelLogger } from '../../../../server/lib';
 import { LayoutInstance } from '../../layouts/layout';
+import { CONTEXT_GETNUMBEROFITEMS } from './constants';
 
 export const getNumberOfItems = async (
   browser: HeadlessBrowser,
@@ -17,20 +18,24 @@ export const getNumberOfItems = async (
 
   // returns the value of the `itemsCountAttribute` if it's there, otherwise
   // we just count the number of `itemSelector`
-  const itemsCount: number = await browser.evaluate({
-    fn: (selector, countAttribute) => {
-      const elementWithCount = document.querySelector(`[${countAttribute}]`);
-      if (elementWithCount && elementWithCount != null) {
-        const count = elementWithCount.getAttribute(countAttribute);
-        if (count && count != null) {
-          return parseInt(count, 10);
+  const itemsCount: number = await browser.evaluate(
+    {
+      fn: (selector, countAttribute) => {
+        const elementWithCount = document.querySelector(`[${countAttribute}]`);
+        if (elementWithCount && elementWithCount != null) {
+          const count = elementWithCount.getAttribute(countAttribute);
+          if (count && count != null) {
+            return parseInt(count, 10);
+          }
         }
-      }
 
-      return document.querySelectorAll(selector).length;
+        return document.querySelectorAll(selector).length;
+      },
+      args: [layout.selectors.renderComplete, layout.selectors.itemsCountAttribute],
     },
-    args: [layout.selectors.renderComplete, layout.selectors.itemsCountAttribute],
-  });
+    { context: CONTEXT_GETNUMBEROFITEMS },
+    logger
+  );
 
   return itemsCount;
 };
diff --git a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/get_screenshots.ts b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/get_screenshots.ts
index ff9f4549c0d4f..b21d1e752ba3f 100644
--- a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/get_screenshots.ts
+++ b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/get_screenshots.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { HeadlessChromiumDriver as HeadlessBrowser } from '../../../../server/browsers/chromium/driver';
+import { HeadlessChromiumDriver as HeadlessBrowser } from '../../../../server/browsers';
 import { LevelLogger } from '../../../../server/lib';
 import { Screenshot, ElementsPositionAndAttribute } from './types';
 
diff --git a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/get_time_range.ts b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/get_time_range.ts
index db63748c534d5..c1c43ed452594 100644
--- a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/get_time_range.ts
+++ b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/get_time_range.ts
@@ -4,9 +4,10 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { HeadlessChromiumDriver as HeadlessBrowser } from '../../../../server/browsers/chromium/driver';
+import { HeadlessChromiumDriver as HeadlessBrowser } from '../../../../server/browsers';
 import { LevelLogger } from '../../../../server/lib';
 import { LayoutInstance } from '../../layouts/layout';
+import { CONTEXT_GETTIMERANGE } from './constants';
 import { TimeRange } from './types';
 
 export const getTimeRange = async (
@@ -16,23 +17,27 @@ export const getTimeRange = async (
 ): Promise<TimeRange | null> => {
   logger.debug('getting timeRange');
 
-  const timeRange: TimeRange | null = await browser.evaluate({
-    fn: durationAttribute => {
-      const durationElement = document.querySelector(`[${durationAttribute}]`);
+  const timeRange: TimeRange | null = await browser.evaluate(
+    {
+      fn: durationAttribute => {
+        const durationElement = document.querySelector(`[${durationAttribute}]`);
 
-      if (!durationElement) {
-        return null;
-      }
+        if (!durationElement) {
+          return null;
+        }
 
-      const duration = durationElement.getAttribute(durationAttribute);
-      if (!duration) {
-        return null;
-      }
+        const duration = durationElement.getAttribute(durationAttribute);
+        if (!duration) {
+          return null;
+        }
 
-      return { duration };
+        return { duration };
+      },
+      args: [layout.selectors.timefilterDurationAttribute],
     },
-    args: [layout.selectors.timefilterDurationAttribute],
-  });
+    { context: CONTEXT_GETTIMERANGE },
+    logger
+  );
 
   if (timeRange) {
     logger.info(`timeRange: ${timeRange.duration}`);
diff --git a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts
index 62b5e29e88ecf..5a04f1a497abf 100644
--- a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts
+++ b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts
@@ -4,92 +4,4 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import * as Rx from 'rxjs';
-import { concatMap, first, mergeMap, take, toArray } from 'rxjs/operators';
-import { CaptureConfig, HeadlessChromiumDriverFactory, ServerFacade } from '../../../../types';
-import { getElementPositionAndAttributes } from './get_element_position_data';
-import { getNumberOfItems } from './get_number_of_items';
-import { getScreenshots } from './get_screenshots';
-import { getTimeRange } from './get_time_range';
-import { injectCustomCss } from './inject_css';
-import { openUrl } from './open_url';
-import { scanPage } from './scan_page';
-import { skipTelemetry } from './skip_telemetry';
-import { ScreenshotObservableOpts, ScreenshotResults } from './types';
-import { waitForElementsToBeInDOM } from './wait_for_dom_elements';
-import { waitForRenderComplete } from './wait_for_render';
-
-export function screenshotsObservableFactory(
-  server: ServerFacade,
-  browserDriverFactory: HeadlessChromiumDriverFactory
-) {
-  const config = server.config();
-  const captureConfig: CaptureConfig = config.get('xpack.reporting.capture');
-
-  return function screenshotsObservable({
-    logger,
-    urls,
-    conditionalHeaders,
-    layout,
-    browserTimezone,
-  }: ScreenshotObservableOpts): Rx.Observable<ScreenshotResults[]> {
-    const create$ = browserDriverFactory.createPage(
-      { viewport: layout.getBrowserViewport(), browserTimezone },
-      logger
-    );
-    return Rx.from(urls).pipe(
-      concatMap(url => {
-        return create$.pipe(
-          mergeMap(({ driver, exit$ }) => {
-            const screenshot$ = Rx.of(1).pipe(
-              mergeMap(() => openUrl(driver, url, conditionalHeaders, logger)),
-              mergeMap(() => skipTelemetry(driver, logger)),
-              mergeMap(() => scanPage(driver, layout, logger)),
-              mergeMap(() => getNumberOfItems(driver, layout, logger)),
-              mergeMap(async itemsCount => {
-                const viewport = layout.getViewport(itemsCount);
-                await Promise.all([
-                  driver.setViewport(viewport, logger),
-                  waitForElementsToBeInDOM(driver, itemsCount, layout, logger),
-                ]);
-              }),
-              mergeMap(async () => {
-                // Waiting till _after_ elements have rendered before injecting our CSS
-                // allows for them to be displayed properly in many cases
-                await injectCustomCss(driver, layout, logger);
-
-                if (layout.positionElements) {
-                  // position panel elements for print layout
-                  await layout.positionElements(driver, logger);
-                }
-
-                await waitForRenderComplete(captureConfig, driver, layout, logger);
-              }),
-              mergeMap(() => getTimeRange(driver, layout, logger)),
-              mergeMap(
-                async (timeRange): Promise<ScreenshotResults> => {
-                  const elementsPositionAndAttributes = await getElementPositionAndAttributes(
-                    driver,
-                    layout
-                  );
-                  const screenshots = await getScreenshots({
-                    browser: driver,
-                    elementsPositionAndAttributes,
-                    logger,
-                  });
-
-                  return { timeRange, screenshots };
-                }
-              )
-            );
-
-            return Rx.race(screenshot$, exit$);
-          }),
-          first()
-        );
-      }),
-      take(urls.length),
-      toArray()
-    );
-  };
-}
+export { screenshotsObservableFactory } from './observable';
diff --git a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/inject_css.ts b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/inject_css.ts
index d27b6d0752cf9..40204804a276f 100644
--- a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/inject_css.ts
+++ b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/inject_css.ts
@@ -7,8 +7,9 @@
 import fs from 'fs';
 import { promisify } from 'util';
 import { LevelLogger } from '../../../../server/lib';
-import { HeadlessChromiumDriver as HeadlessBrowser } from '../../../../server/browsers/chromium/driver';
+import { HeadlessChromiumDriver as HeadlessBrowser } from '../../../../server/browsers';
 import { Layout } from '../../layouts/layout';
+import { CONTEXT_INJECTCSS } from './constants';
 
 const fsp = { readFile: promisify(fs.readFile) };
 
@@ -21,13 +22,17 @@ export const injectCustomCss = async (
 
   const filePath = layout.getCssOverridesPath();
   const buffer = await fsp.readFile(filePath);
-  await browser.evaluate({
-    fn: css => {
-      const node = document.createElement('style');
-      node.type = 'text/css';
-      node.innerHTML = css; // eslint-disable-line no-unsanitized/property
-      document.getElementsByTagName('head')[0].appendChild(node);
+  await browser.evaluate(
+    {
+      fn: css => {
+        const node = document.createElement('style');
+        node.type = 'text/css';
+        node.innerHTML = css; // eslint-disable-line no-unsanitized/property
+        document.getElementsByTagName('head')[0].appendChild(node);
+      },
+      args: [buffer.toString()],
     },
-    args: [buffer.toString()],
-  });
+    { context: CONTEXT_INJECTCSS },
+    logger
+  );
 };
diff --git a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/observable.test.ts b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/observable.test.ts
new file mode 100644
index 0000000000000..9f8e218f4f614
--- /dev/null
+++ b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/observable.test.ts
@@ -0,0 +1,194 @@
+/*
+ * 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.
+ */
+
+jest.mock('../../../../server/browsers/chromium/puppeteer', () => ({
+  puppeteerLaunch: () => ({
+    // Fixme needs event emitters
+    newPage: () => ({
+      setDefaultTimeout: jest.fn(),
+    }),
+    process: jest.fn(),
+    close: jest.fn(),
+  }),
+}));
+
+import * as Rx from 'rxjs';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { loggingServiceMock } from '../../../../../../../../src/core/server/mocks';
+import { LevelLogger } from '../../../../server/lib';
+import {
+  createMockBrowserDriverFactory,
+  createMockLayoutInstance,
+  createMockServer,
+  mockSelectors,
+} from '../../../../test_helpers';
+import { ConditionalHeaders, HeadlessChromiumDriver } from '../../../../types';
+import { screenshotsObservableFactory } from './observable';
+import { ElementsPositionAndAttribute } from './types';
+
+/*
+ * Mocks
+ */
+const mockLogger = jest.fn(loggingServiceMock.create);
+const logger = new LevelLogger(mockLogger());
+
+const __LEGACY = createMockServer({ settings: { 'xpack.reporting.capture': { loadDelay: 13 } } });
+const mockLayout = createMockLayoutInstance(__LEGACY);
+
+/*
+ * Tests
+ */
+describe('Screenshot Observable Pipeline', () => {
+  let mockBrowserDriverFactory: any;
+
+  beforeEach(async () => {
+    mockBrowserDriverFactory = await createMockBrowserDriverFactory(logger, {});
+  });
+
+  it('pipelines a single url into screenshot and timeRange', async () => {
+    const getScreenshots$ = screenshotsObservableFactory(__LEGACY, mockBrowserDriverFactory);
+    const result = await getScreenshots$({
+      logger,
+      urls: ['/welcome/home/start/index.htm'],
+      conditionalHeaders: {} as ConditionalHeaders,
+      layout: mockLayout,
+      browserTimezone: 'UTC',
+    }).toPromise();
+
+    expect(result).toMatchInlineSnapshot(`
+      Array [
+        Object {
+          "screenshots": Array [
+            Object {
+              "base64EncodedData": "allyourBase64 of boundingClientRect,scroll",
+              "description": "Default ",
+              "title": "Default Mock Title",
+            },
+          ],
+          "timeRange": "Default GetTimeRange Result",
+        },
+      ]
+    `);
+  });
+
+  it('pipelines multiple urls into', async () => {
+    // mock implementations
+    const mockScreenshot = jest.fn().mockImplementation((item: ElementsPositionAndAttribute) => {
+      return Promise.resolve(`allyourBase64 screenshots`);
+    });
+
+    // mocks
+    mockBrowserDriverFactory = await createMockBrowserDriverFactory(logger, {
+      screenshot: mockScreenshot,
+    });
+
+    // test
+    const getScreenshots$ = screenshotsObservableFactory(__LEGACY, mockBrowserDriverFactory);
+    const result = await getScreenshots$({
+      logger,
+      urls: ['/welcome/home/start/index2.htm', '/welcome/home/start/index.php3?page=./home.php'],
+      conditionalHeaders: {} as ConditionalHeaders,
+      layout: mockLayout,
+      browserTimezone: 'UTC',
+    }).toPromise();
+
+    expect(result).toMatchInlineSnapshot(`
+      Array [
+        Object {
+          "screenshots": Array [
+            Object {
+              "base64EncodedData": "allyourBase64 screenshots",
+              "description": "Default ",
+              "title": "Default Mock Title",
+            },
+          ],
+          "timeRange": "Default GetTimeRange Result",
+        },
+        Object {
+          "screenshots": Array [
+            Object {
+              "base64EncodedData": "allyourBase64 screenshots",
+              "description": "Default ",
+              "title": "Default Mock Title",
+            },
+          ],
+          "timeRange": "Default GetTimeRange Result",
+        },
+      ]
+    `);
+  });
+
+  describe('error handling', () => {
+    it('fails if error toast message is found', async () => {
+      // mock implementations
+      const mockWaitForSelector = jest.fn().mockImplementation((selectorArg: string) => {
+        const { toastHeader } = mockSelectors;
+        if (selectorArg === toastHeader) {
+          return Promise.resolve(true);
+        }
+        // make the error toast message get found before anything else
+        return Rx.interval(100).toPromise();
+      });
+
+      // mocks
+      mockBrowserDriverFactory = await createMockBrowserDriverFactory(logger, {
+        waitForSelector: mockWaitForSelector,
+      });
+
+      // test
+      const getScreenshots$ = screenshotsObservableFactory(__LEGACY, mockBrowserDriverFactory);
+      const getScreenshot = async () => {
+        return await getScreenshots$({
+          logger,
+          urls: [
+            '/welcome/home/start/index2.htm',
+            '/welcome/home/start/index.php3?page=./home.php3',
+          ],
+          conditionalHeaders: {} as ConditionalHeaders,
+          layout: mockLayout,
+          browserTimezone: 'UTC',
+        }).toPromise();
+      };
+
+      await expect(getScreenshot()).rejects.toMatchInlineSnapshot(
+        `[Error: Encountered an unexpected message on the page: Toast Message]`
+      );
+    });
+
+    it('fails if exit$ fires a timeout or error signal', async () => {
+      // mocks
+      const mockGetCreatePage = (driver: HeadlessChromiumDriver) =>
+        jest
+          .fn()
+          .mockImplementation(() =>
+            Rx.of({ driver, exit$: Rx.throwError('Instant timeout has fired!') })
+          );
+
+      const mockWaitForSelector = jest.fn().mockImplementation((selectorArg: string) => {
+        return Rx.never().toPromise();
+      });
+
+      mockBrowserDriverFactory = await createMockBrowserDriverFactory(logger, {
+        getCreatePage: mockGetCreatePage,
+        waitForSelector: mockWaitForSelector,
+      });
+
+      // test
+      const getScreenshots$ = screenshotsObservableFactory(__LEGACY, mockBrowserDriverFactory);
+      const getScreenshot = async () => {
+        return await getScreenshots$({
+          logger,
+          urls: ['/welcome/home/start/index.php3?page=./home.php3'],
+          conditionalHeaders: {} as ConditionalHeaders,
+          layout: mockLayout,
+          browserTimezone: 'UTC',
+        }).toPromise();
+      };
+
+      await expect(getScreenshot()).rejects.toMatchInlineSnapshot(`"Instant timeout has fired!"`);
+    });
+  });
+});
diff --git a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/observable.ts b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/observable.ts
new file mode 100644
index 0000000000000..d429931602951
--- /dev/null
+++ b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/observable.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 * as Rx from 'rxjs';
+import { concatMap, first, mergeMap, take, toArray } from 'rxjs/operators';
+import { CaptureConfig, HeadlessChromiumDriverFactory, ServerFacade } from '../../../../types';
+import { getElementPositionAndAttributes } from './get_element_position_data';
+import { getNumberOfItems } from './get_number_of_items';
+import { getScreenshots } from './get_screenshots';
+import { getTimeRange } from './get_time_range';
+import { injectCustomCss } from './inject_css';
+import { openUrl } from './open_url';
+import { scanPage } from './scan_page';
+import { ScreenshotObservableOpts, ScreenshotResults } from './types';
+import { waitForElementsToBeInDOM } from './wait_for_dom_elements';
+import { waitForRenderComplete } from './wait_for_render';
+import { skipTelemetry } from './skip_telemetry';
+
+export function screenshotsObservableFactory(
+  server: ServerFacade,
+  browserDriverFactory: HeadlessChromiumDriverFactory
+) {
+  const config = server.config();
+  const captureConfig: CaptureConfig = config.get('xpack.reporting.capture');
+
+  return function screenshotsObservable({
+    logger,
+    urls,
+    conditionalHeaders,
+    layout,
+    browserTimezone,
+  }: ScreenshotObservableOpts): Rx.Observable<ScreenshotResults[]> {
+    const create$ = browserDriverFactory.createPage(
+      { viewport: layout.getBrowserViewport(), browserTimezone },
+      logger
+    );
+    return Rx.from(urls).pipe(
+      concatMap(url => {
+        return create$.pipe(
+          mergeMap(({ driver, exit$ }) => {
+            const screenshot$ = Rx.of(1).pipe(
+              mergeMap(() => openUrl(driver, url, conditionalHeaders, logger)),
+              mergeMap(() => skipTelemetry(driver, logger)),
+              mergeMap(() => scanPage(driver, layout, logger)),
+              mergeMap(() => getNumberOfItems(driver, layout, logger)),
+              mergeMap(async itemsCount => {
+                const viewport = layout.getViewport(itemsCount);
+                await Promise.all([
+                  driver.setViewport(viewport, logger),
+                  waitForElementsToBeInDOM(driver, itemsCount, layout, logger),
+                ]);
+              }),
+              mergeMap(async () => {
+                // Waiting till _after_ elements have rendered before injecting our CSS
+                // allows for them to be displayed properly in many cases
+                await injectCustomCss(driver, layout, logger);
+
+                if (layout.positionElements) {
+                  // position panel elements for print layout
+                  await layout.positionElements(driver, logger);
+                }
+
+                await waitForRenderComplete(captureConfig, driver, layout, logger);
+              }),
+              mergeMap(() => getTimeRange(driver, layout, logger)),
+              mergeMap(
+                async (timeRange): Promise<ScreenshotResults> => {
+                  const elementsPositionAndAttributes = await getElementPositionAndAttributes(
+                    driver,
+                    layout,
+                    logger
+                  );
+                  const screenshots = await getScreenshots({
+                    browser: driver,
+                    elementsPositionAndAttributes,
+                    logger,
+                  });
+
+                  return { timeRange, screenshots };
+                }
+              )
+            );
+
+            return Rx.race(screenshot$, exit$);
+          }),
+          first()
+        );
+      }),
+      take(urls.length),
+      toArray()
+    );
+  };
+}
diff --git a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/open_url.ts b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/open_url.ts
index 288e8b81acdc9..e465499f839f9 100644
--- a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/open_url.ts
+++ b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/open_url.ts
@@ -6,7 +6,7 @@
 
 import { ConditionalHeaders } from '../../../../types';
 import { LevelLogger } from '../../../../server/lib';
-import { HeadlessChromiumDriver as HeadlessBrowser } from '../../../../server/browsers/chromium/driver';
+import { HeadlessChromiumDriver as HeadlessBrowser } from '../../../../server/browsers';
 import { WAITFOR_SELECTOR } from '../../constants';
 
 export const openUrl = async (
diff --git a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/scan_page.ts b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/scan_page.ts
index 81ff01bb204b8..010ffe8f23afc 100644
--- a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/scan_page.ts
+++ b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/scan_page.ts
@@ -5,7 +5,7 @@
  */
 
 import * as Rx from 'rxjs';
-import { HeadlessChromiumDriver } from '../../../../server/browsers/chromium/driver';
+import { HeadlessChromiumDriver } from '../../../../server/browsers';
 import { LevelLogger } from '../../../../server/lib';
 import { LayoutInstance } from '../../layouts/layout';
 import { checkForToastMessage } from './check_for_toast';
diff --git a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/skip_telemetry.ts b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/skip_telemetry.ts
index 367354032a843..1762a78f22720 100644
--- a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/skip_telemetry.ts
+++ b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/skip_telemetry.ts
@@ -6,24 +6,29 @@
 
 import { HeadlessChromiumDriver as HeadlessBrowser } from '../../../../server/browsers/chromium/driver';
 import { LevelLogger } from '../../../../server/lib';
+import { CONTEXT_SKIPTELEMETRY } from './constants';
 
 const LAST_REPORT_STORAGE_KEY = 'xpack.data';
 
 export async function skipTelemetry(browser: HeadlessBrowser, logger: LevelLogger) {
-  const storageData = await browser.evaluate({
-    fn: storageKey => {
-      // set something
-      const optOutJSON = JSON.stringify({ lastReport: Date.now() });
-      localStorage.setItem(storageKey, optOutJSON);
+  const storageData = await browser.evaluate(
+    {
+      fn: storageKey => {
+        // set something
+        const optOutJSON = JSON.stringify({ lastReport: Date.now() });
+        localStorage.setItem(storageKey, optOutJSON);
 
-      // get it
-      const session = localStorage.getItem(storageKey);
+        // get it
+        const session = localStorage.getItem(storageKey);
 
-      // return it
-      return session;
+        // return it
+        return session;
+      },
+      args: [LAST_REPORT_STORAGE_KEY],
     },
-    args: [LAST_REPORT_STORAGE_KEY],
-  });
+    { context: CONTEXT_SKIPTELEMETRY },
+    logger
+  );
 
   logger.debug(`added data to localStorage to skip telmetry: ${storageData}`);
 }
diff --git a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/wait_for_dom_elements.ts b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/wait_for_dom_elements.ts
index 3e9498179e407..c958585f78e0d 100644
--- a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/wait_for_dom_elements.ts
+++ b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/wait_for_dom_elements.ts
@@ -4,9 +4,10 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { HeadlessChromiumDriver as HeadlessBrowser } from '../../../../server/browsers/chromium/driver';
+import { HeadlessChromiumDriver as HeadlessBrowser } from '../../../../server/browsers';
 import { LevelLogger } from '../../../../server/lib';
 import { LayoutInstance } from '../../layouts/layout';
+import { CONTEXT_WAITFORELEMENTSTOBEINDOM } from './constants';
 
 export const waitForElementsToBeInDOM = async (
   browser: HeadlessBrowser,
@@ -16,13 +17,17 @@ export const waitForElementsToBeInDOM = async (
 ): Promise<number> => {
   logger.debug(`waiting for ${itemsCount} rendered elements to be in the DOM`);
 
-  await browser.waitFor({
-    fn: selector => {
-      return document.querySelectorAll(selector).length;
+  await browser.waitFor(
+    {
+      fn: selector => {
+        return document.querySelectorAll(selector).length;
+      },
+      args: [layout.selectors.renderComplete],
+      toEqual: itemsCount,
     },
-    args: [layout.selectors.renderComplete],
-    toEqual: itemsCount,
-  });
+    { context: CONTEXT_WAITFORELEMENTSTOBEINDOM },
+    logger
+  );
 
   logger.info(`found ${itemsCount} rendered elements in the DOM`);
   return itemsCount;
diff --git a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/wait_for_render.ts b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/wait_for_render.ts
index df0d591ff913c..632f008ca63bc 100644
--- a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/wait_for_render.ts
+++ b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/wait_for_render.ts
@@ -5,9 +5,10 @@
  */
 
 import { CaptureConfig } from '../../../../types';
-import { HeadlessChromiumDriver as HeadlessBrowser } from '../../../../server/browsers/chromium/driver';
+import { HeadlessChromiumDriver as HeadlessBrowser } from '../../../../server/browsers';
 import { LevelLogger } from '../../../../server/lib';
 import { LayoutInstance } from '../../layouts/layout';
+import { CONTEXT_WAITFORRENDER } from './constants';
 
 export const waitForRenderComplete = async (
   captureConfig: CaptureConfig,
@@ -18,48 +19,52 @@ export const waitForRenderComplete = async (
   logger.debug('waiting for rendering to complete');
 
   return await browser
-    .evaluate({
-      fn: (selector, visLoadDelay) => {
-        // wait for visualizations to finish loading
-        const visualizations: NodeListOf<Element> = document.querySelectorAll(selector);
-        const visCount = visualizations.length;
-        const renderedTasks = [];
+    .evaluate(
+      {
+        fn: (selector, visLoadDelay) => {
+          // wait for visualizations to finish loading
+          const visualizations: NodeListOf<Element> = document.querySelectorAll(selector);
+          const visCount = visualizations.length;
+          const renderedTasks = [];
 
-        function waitForRender(visualization: Element) {
-          return new Promise(resolve => {
-            visualization.addEventListener('renderComplete', () => resolve());
-          });
-        }
+          function waitForRender(visualization: Element) {
+            return new Promise(resolve => {
+              visualization.addEventListener('renderComplete', () => resolve());
+            });
+          }
 
-        function waitForRenderDelay() {
-          return new Promise(resolve => {
-            setTimeout(resolve, visLoadDelay);
-          });
-        }
+          function waitForRenderDelay() {
+            return new Promise(resolve => {
+              setTimeout(resolve, visLoadDelay);
+            });
+          }
 
-        for (let i = 0; i < visCount; i++) {
-          const visualization = visualizations[i];
-          const isRendered = visualization.getAttribute('data-render-complete');
+          for (let i = 0; i < visCount; i++) {
+            const visualization = visualizations[i];
+            const isRendered = visualization.getAttribute('data-render-complete');
 
-          if (isRendered === 'disabled') {
-            renderedTasks.push(waitForRenderDelay());
-          } else if (isRendered === 'false') {
-            renderedTasks.push(waitForRender(visualization));
+            if (isRendered === 'disabled') {
+              renderedTasks.push(waitForRenderDelay());
+            } else if (isRendered === 'false') {
+              renderedTasks.push(waitForRender(visualization));
+            }
           }
-        }
 
-        // The renderComplete fires before the visualizations are in the DOM, so
-        // we wait for the event loop to flush before telling reporting to continue. This
-        // seems to correct a timing issue that was causing reporting to occasionally
-        // capture the first visualization before it was actually in the DOM.
-        // Note: 100 proved too short, see https://github.com/elastic/kibana/issues/22581,
-        // bumping to 250.
-        const hackyWaitForVisualizations = () => new Promise(r => setTimeout(r, 250));
+          // The renderComplete fires before the visualizations are in the DOM, so
+          // we wait for the event loop to flush before telling reporting to continue. This
+          // seems to correct a timing issue that was causing reporting to occasionally
+          // capture the first visualization before it was actually in the DOM.
+          // Note: 100 proved too short, see https://github.com/elastic/kibana/issues/22581,
+          // bumping to 250.
+          const hackyWaitForVisualizations = () => new Promise(r => setTimeout(r, 250));
 
-        return Promise.all(renderedTasks).then(hackyWaitForVisualizations);
+          return Promise.all(renderedTasks).then(hackyWaitForVisualizations);
+        },
+        args: [layout.selectors.renderComplete, captureConfig.loadDelay],
       },
-      args: [layout.selectors.renderComplete, captureConfig.loadDelay],
-    })
+      { context: CONTEXT_WAITFORRENDER },
+      logger
+    )
     .then(() => {
       logger.debug('rendering is complete');
     });
diff --git a/x-pack/legacy/plugins/reporting/server/browsers/chromium/driver/chromium_driver.ts b/x-pack/legacy/plugins/reporting/server/browsers/chromium/driver/chromium_driver.ts
index de8449ff29132..0592124b9897b 100644
--- a/x-pack/legacy/plugins/reporting/server/browsers/chromium/driver/chromium_driver.ts
+++ b/x-pack/legacy/plugins/reporting/server/browsers/chromium/driver/chromium_driver.ts
@@ -28,6 +28,15 @@ interface WaitForSelectorOpts {
   silent?: boolean;
 }
 
+interface EvaluateOpts {
+  fn: EvaluateFn;
+  args: SerializableOrJSHandle[];
+}
+
+interface EvaluateMetaOpts {
+  context: string;
+}
+
 const WAIT_FOR_DELAY_MS: number = 100;
 
 export class HeadlessChromiumDriver {
@@ -158,11 +167,15 @@ export class HeadlessChromiumDriver {
     return screenshot.toString('base64');
   }
 
-  public async evaluate({ fn, args = [] }: { fn: EvaluateFn; args: SerializableOrJSHandle[] }) {
+  public async evaluate(
+    { fn, args = [] }: EvaluateOpts,
+    meta: EvaluateMetaOpts,
+    logger: LevelLogger
+  ) {
+    logger.debug(`evaluate ${meta.context}`);
     const result = await this.page.evaluate(fn, ...args);
     return result;
   }
-
   public async waitForSelector(
     selector: string,
     opts: WaitForSelectorOpts = {},
@@ -179,10 +192,14 @@ export class HeadlessChromiumDriver {
         // Provide some troubleshooting info to see if we're on the login page,
         // "Kibana could not load correctly", etc
         logger.error(`waitForSelector ${selector} failed on ${this.page.url()}`);
-        const pageText = await this.evaluate({
-          fn: () => document.querySelector('body')!.innerText,
-          args: [],
-        });
+        const pageText = await this.evaluate(
+          {
+            fn: () => document.querySelector('body')!.innerText,
+            args: [],
+          },
+          { context: `waitForSelector${selector}` },
+          logger
+        );
         logger.debug(`Page plain text: ${pageText.replace(/\n/g, '\\n')}`); // replace newline with escaped for single log line
       }
       throw err;
@@ -192,17 +209,21 @@ export class HeadlessChromiumDriver {
     return resp;
   }
 
-  public async waitFor<T>({
-    fn,
-    args,
-    toEqual,
-  }: {
-    fn: EvaluateFn;
-    args: SerializableOrJSHandle[];
-    toEqual: T;
-  }) {
+  public async waitFor<T>(
+    {
+      fn,
+      args,
+      toEqual,
+    }: {
+      fn: EvaluateFn;
+      args: SerializableOrJSHandle[];
+      toEqual: T;
+    },
+    context: EvaluateMetaOpts,
+    logger: LevelLogger
+  ) {
     while (true) {
-      const result = await this.evaluate({ fn, args });
+      const result = await this.evaluate({ fn, args }, context, logger);
       if (result === toEqual) {
         return;
       }
diff --git a/x-pack/legacy/plugins/reporting/server/browsers/index.ts b/x-pack/legacy/plugins/reporting/server/browsers/index.ts
index 402fabea56c84..1e42e2736962e 100644
--- a/x-pack/legacy/plugins/reporting/server/browsers/index.ts
+++ b/x-pack/legacy/plugins/reporting/server/browsers/index.ts
@@ -10,6 +10,9 @@ export { ensureAllBrowsersDownloaded } from './download';
 export { createBrowserDriverFactory } from './create_browser_driver_factory';
 export { getDefaultChromiumSandboxDisabled } from './default_chromium_sandbox_disabled';
 
+export { HeadlessChromiumDriver } from './chromium/driver';
+export { HeadlessChromiumDriverFactory } from './chromium/driver_factory';
+
 export const chromium = {
   paths: chromiumDefinition.paths,
   createDriverFactory: chromiumDefinition.createDriverFactory,
diff --git a/x-pack/legacy/plugins/reporting/server/types.d.ts b/x-pack/legacy/plugins/reporting/server/types.d.ts
index 20673423aa448..59b7bc2020ad9 100644
--- a/x-pack/legacy/plugins/reporting/server/types.d.ts
+++ b/x-pack/legacy/plugins/reporting/server/types.d.ts
@@ -5,17 +5,12 @@
  */
 
 import { Legacy } from 'kibana';
-import {
-  ElasticsearchServiceSetup,
-  SavedObjectsServiceStart,
-  UiSettingsServiceStart,
-} from 'src/core/server';
+import { ElasticsearchServiceSetup } from 'src/core/server';
 import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
 import { PluginStart as DataPluginStart } from '../../../../../src/plugins/data/server';
 import { SecurityPluginSetup } from '../../../../plugins/security/server';
 import { XPackMainPlugin } from '../../xpack_main/server/xpack_main';
-import { EnqueueJobFn, ESQueueInstance, ReportingPluginSpecOptions } from '../types';
-import { HeadlessChromiumDriverFactory } from './browsers/chromium/driver_factory';
+import { ReportingPluginSpecOptions } from '../types';
 
 export interface ReportingSetupDeps {
   elasticsearch: ElasticsearchServiceSetup;
diff --git a/x-pack/legacy/plugins/reporting/test_helpers/create_mock_browserdriverfactory.ts b/x-pack/legacy/plugins/reporting/test_helpers/create_mock_browserdriverfactory.ts
new file mode 100644
index 0000000000000..6d9ae2153255f
--- /dev/null
+++ b/x-pack/legacy/plugins/reporting/test_helpers/create_mock_browserdriverfactory.ts
@@ -0,0 +1,132 @@
+/*
+ * 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 { Page } from 'puppeteer';
+import * as Rx from 'rxjs';
+import * as contexts from '../export_types/common/lib/screenshots/constants';
+import { ElementsPositionAndAttribute } from '../export_types/common/lib/screenshots/types';
+import { HeadlessChromiumDriver, HeadlessChromiumDriverFactory } from '../server/browsers';
+import { createDriverFactory } from '../server/browsers/chromium';
+import { BrowserConfig, Logger, NetworkPolicy } from '../types';
+
+interface CreateMockBrowserDriverFactoryOpts {
+  evaluate: jest.Mock<Promise<any>, any[]>;
+  waitForSelector: jest.Mock<Promise<any>, any[]>;
+  screenshot: jest.Mock<Promise<any>, any[]>;
+  getCreatePage: (driver: HeadlessChromiumDriver) => jest.Mock<any, any>;
+}
+
+export const mockSelectors = {
+  renderComplete: 'renderedSelector',
+  itemsCountAttribute: 'itemsSelector',
+  screenshot: 'screenshotSelector',
+  timefilterDurationAttribute: 'timefilterDurationSelector',
+  toastHeader: 'toastHeaderSelector',
+};
+
+const getMockElementsPositionAndAttributes = (
+  title: string,
+  description: string
+): ElementsPositionAndAttribute[] => [
+  {
+    position: {
+      boundingClientRect: { top: 0, left: 0, width: 10, height: 11 },
+      scroll: { x: 0, y: 0 },
+    },
+    attributes: { title, description },
+  },
+];
+
+const mockWaitForSelector = jest.fn();
+mockWaitForSelector.mockImplementation((selectorArg: string) => {
+  const { renderComplete, itemsCountAttribute, toastHeader } = mockSelectors;
+  if (selectorArg === `${renderComplete},[${itemsCountAttribute}]`) {
+    return Promise.resolve(true);
+  } else if (selectorArg === toastHeader) {
+    return Rx.never().toPromise();
+  }
+  throw new Error(selectorArg);
+});
+const mockBrowserEvaluate = jest.fn();
+mockBrowserEvaluate.mockImplementation(() => {
+  const lastCallIndex = mockBrowserEvaluate.mock.calls.length - 1;
+  const { context: mockCall } = mockBrowserEvaluate.mock.calls[lastCallIndex][1];
+
+  if (mockCall === contexts.CONTEXT_SKIPTELEMETRY) {
+    return Promise.resolve();
+  }
+  if (mockCall === contexts.CONTEXT_GETNUMBEROFITEMS) {
+    return Promise.resolve(1);
+  }
+  if (mockCall === contexts.CONTEXT_INJECTCSS) {
+    return Promise.resolve();
+  }
+  if (mockCall === contexts.CONTEXT_WAITFORRENDER) {
+    return Promise.resolve();
+  }
+  if (mockCall === contexts.CONTEXT_GETTIMERANGE) {
+    return Promise.resolve('Default GetTimeRange Result');
+  }
+  if (mockCall === contexts.CONTEXT_ELEMENTATTRIBUTES) {
+    return Promise.resolve(getMockElementsPositionAndAttributes('Default Mock Title', 'Default '));
+  }
+  if (mockCall === contexts.CONTEXT_CHECKFORTOASTMESSAGE) {
+    return Promise.resolve('Toast Message');
+  }
+  throw new Error(mockCall);
+});
+const mockScreenshot = jest.fn();
+mockScreenshot.mockImplementation((item: ElementsPositionAndAttribute) => {
+  return Promise.resolve(`allyourBase64 of ${Object.keys(item)}`);
+});
+const getCreatePage = (driver: HeadlessChromiumDriver) =>
+  jest.fn().mockImplementation(() => Rx.of({ driver, exit$: Rx.never() }));
+
+const defaultOpts: CreateMockBrowserDriverFactoryOpts = {
+  evaluate: mockBrowserEvaluate,
+  waitForSelector: mockWaitForSelector,
+  screenshot: mockScreenshot,
+  getCreatePage,
+};
+
+export const createMockBrowserDriverFactory = async (
+  logger: Logger,
+  opts: Partial<CreateMockBrowserDriverFactoryOpts>
+): Promise<HeadlessChromiumDriverFactory> => {
+  const browserConfig = {
+    inspect: true,
+    userDataDir: '/usr/data/dir',
+    viewport: { width: 12, height: 12 },
+    disableSandbox: false,
+    proxy: { enabled: false },
+  } as BrowserConfig;
+
+  const binaryPath = '/usr/local/share/common/secure/';
+  const queueTimeout = 55;
+  const networkPolicy = {} as NetworkPolicy;
+
+  const mockBrowserDriverFactory = await createDriverFactory(
+    binaryPath,
+    logger,
+    browserConfig,
+    queueTimeout,
+    networkPolicy
+  );
+
+  const mockPage = {} as Page;
+  const mockBrowserDriver = new HeadlessChromiumDriver(mockPage, { inspect: true, networkPolicy });
+
+  // mock the driver methods as either default mocks or passed-in
+  mockBrowserDriver.waitForSelector = opts.waitForSelector ? opts.waitForSelector : defaultOpts.waitForSelector; // prettier-ignore
+  mockBrowserDriver.evaluate = opts.evaluate ? opts.evaluate : defaultOpts.evaluate;
+  mockBrowserDriver.screenshot = opts.screenshot ? opts.screenshot : defaultOpts.screenshot;
+
+  mockBrowserDriverFactory.createPage = opts.getCreatePage
+    ? opts.getCreatePage(mockBrowserDriver)
+    : getCreatePage(mockBrowserDriver);
+
+  return mockBrowserDriverFactory;
+};
diff --git a/x-pack/legacy/plugins/reporting/test_helpers/create_mock_layoutinstance.ts b/x-pack/legacy/plugins/reporting/test_helpers/create_mock_layoutinstance.ts
new file mode 100644
index 0000000000000..a2eb03c3fe300
--- /dev/null
+++ b/x-pack/legacy/plugins/reporting/test_helpers/create_mock_layoutinstance.ts
@@ -0,0 +1,25 @@
+/*
+ * 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 { createLayout } from '../export_types/common/layouts';
+import { LayoutTypes } from '../export_types/common/constants';
+import { LayoutInstance } from '../export_types/common/layouts/layout';
+import { ServerFacade } from '../types';
+
+export const createMockLayoutInstance = (__LEGACY: ServerFacade) => {
+  const mockLayout = createLayout(__LEGACY, {
+    id: LayoutTypes.PRESERVE_LAYOUT,
+    dimensions: { height: 12, width: 12 },
+  }) as LayoutInstance;
+  mockLayout.selectors = {
+    renderComplete: 'renderedSelector',
+    itemsCountAttribute: 'itemsSelector',
+    screenshot: 'screenshotSelector',
+    timefilterDurationAttribute: 'timefilterDurationSelector',
+    toastHeader: 'toastHeaderSelector',
+  };
+  return mockLayout;
+};
diff --git a/x-pack/legacy/plugins/reporting/test_helpers/index.ts b/x-pack/legacy/plugins/reporting/test_helpers/index.ts
index 7fbc5661d5211..91c348ba1db3d 100644
--- a/x-pack/legacy/plugins/reporting/test_helpers/index.ts
+++ b/x-pack/legacy/plugins/reporting/test_helpers/index.ts
@@ -6,3 +6,5 @@
 
 export { createMockServer } from './create_mock_server';
 export { createMockReportingCore } from './create_mock_reportingplugin';
+export { createMockBrowserDriverFactory, mockSelectors } from './create_mock_browserdriverfactory';
+export { createMockLayoutInstance } from './create_mock_layoutinstance';
diff --git a/x-pack/legacy/plugins/reporting/types.d.ts b/x-pack/legacy/plugins/reporting/types.d.ts
index 1549c173b3d6e..38406186c8173 100644
--- a/x-pack/legacy/plugins/reporting/types.d.ts
+++ b/x-pack/legacy/plugins/reporting/types.d.ts
@@ -311,8 +311,9 @@ export interface ExportTypeDefinition<
 }
 
 export { CancellationToken } from './common/cancellation_token';
-export { HeadlessChromiumDriver } from './server/browsers/chromium/driver';
-export { HeadlessChromiumDriverFactory } from './server/browsers/chromium/driver_factory';
+
+export { HeadlessChromiumDriver, HeadlessChromiumDriverFactory } from './server/browsers';
+
 export { ExportTypesRegistry } from './server/lib/export_types_registry';
 // Prefer to import this type using: `import { LevelLogger } from 'relative/path/server/lib';`
 export { LevelLogger as Logger };

From 05947ade7680b868d540f6c3458b2833ef1450f7 Mon Sep 17 00:00:00 2001
From: Garrett Spong <spong@users.noreply.github.com>
Date: Tue, 18 Feb 2020 17:05:32 -0700
Subject: [PATCH 048/174] [SIEM] [Detection Engine] Fixes Signals Table bulk
 selection issues (#56825)

## Summary

This PR fixes regressions around the bulk selection action, including:

* Incorrect total when opening/closing signals
* Selection total persisting after opening/closing signals
* `Select All` checkbox remaining selected after opening/closing signals
* Bulk action not being enabled after opening/closing via single action while others are selected

##### Incorrect total / persisted selection total
<details><summary>Before</summary>
<p>

![selection_persisted_wrong_count](https://user-images.githubusercontent.com/2946766/73814700-0d7faf80-47a1-11ea-9ec1-c6cb6a3d30c3.gif)

</p>
</details>

<details><summary>After</summary>
<p>

![selection_persisted_wrong_count_fix](https://user-images.githubusercontent.com/2946766/73814704-107aa000-47a1-11ea-9eb9-4f56d3d3f8c2.gif)
</p>
</details>


##### Bulk action not being enabled
<details><summary>Before</summary>
<p>

![selection_disabled_bulk_action](https://user-images.githubusercontent.com/2946766/73814695-09ec2880-47a1-11ea-8a37-ae35a19979ab.gif)
</p>
</details>

<details><summary>After</summary>
<p>

![selection_disabled_bulk_action_fixed](https://user-images.githubusercontent.com/2946766/73814701-0f497300-47a1-11ea-8c73-3a131bda59f6.gif)
</p>
</details>







### Checklist

Delete any items that are not applicable to this PR.

- [ ] ~Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/master/packages/kbn-i18n/README.md)~
- [ ] ~[Documentation](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#writing-documentation) was added for features that require explanation or tutorials~
- [ ] ~[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~
- [ ] ~This was checked for [keyboard-only and screenreader accessibility](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility#Accessibility_testing_checklist)~
- [ ] ~This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)~
- [ ] ~This was checked for cross-browser compatibility, [including a check against IE11](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility)~

### For maintainers

- [ ] ~This was checked for breaking API changes and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process)~
---
 .../events_viewer/events_viewer.tsx           |  1 -
 .../siem/public/containers/query_template.tsx | 30 +++++++++++++++++--
 .../siem/public/containers/timeline/index.tsx | 29 +++++++++++++++---
 .../components/signals/index.tsx              |  2 +-
 .../signals/signals_utility_bar/index.tsx     |  1 +
 .../siem/public/store/timeline/helpers.ts     | 11 +++++++
 6 files changed, 65 insertions(+), 9 deletions(-)

diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx
index b05529f9a497f..8e2c5bdd12d68 100644
--- a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx
@@ -157,7 +157,6 @@ const EventsViewerComponent: React.FC<Props> = ({
                 totalCountMinusDeleted
               ) ?? i18n.UNIT(totalCountMinusDeleted)}`;
 
-              // TODO: Reset eventDeletedIds/eventLoadingIds on refresh/loadmore (getUpdatedAt)
               return (
                 <>
                   <HeaderSection
diff --git a/x-pack/legacy/plugins/siem/public/containers/query_template.tsx b/x-pack/legacy/plugins/siem/public/containers/query_template.tsx
index b51eac492c48a..dfb452c24b86e 100644
--- a/x-pack/legacy/plugins/siem/public/containers/query_template.tsx
+++ b/x-pack/legacy/plugins/siem/public/containers/query_template.tsx
@@ -19,7 +19,7 @@ export interface QueryTemplateProps {
   startDate?: number;
 }
 // eslint-disable-next-line @typescript-eslint/no-explicit-any
-type FetchMoreOptionsArgs<TData, TVariables> = FetchMoreQueryOptions<any, any> &
+export type FetchMoreOptionsArgs<TData, TVariables> = FetchMoreQueryOptions<any, any> &
   FetchMoreOptions<TData, TVariables>;
 
 // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -40,6 +40,19 @@ export class QueryTemplate<
     tiebreaker?: string
   ) => FetchMoreOptionsArgs<TData, TVariables>;
 
+  private refetch!: (variables?: TVariables) => Promise<ApolloQueryResult<TData>>;
+
+  private executeBeforeFetchMore!: ({ id }: { id?: string }) => void;
+
+  private executeBeforeRefetch!: ({ id }: { id?: string }) => void;
+
+  public setExecuteBeforeFetchMore = (val: ({ id }: { id?: string }) => void) => {
+    this.executeBeforeFetchMore = val;
+  };
+  public setExecuteBeforeRefetch = (val: ({ id }: { id?: string }) => void) => {
+    this.executeBeforeRefetch = val;
+  };
+
   public setFetchMore = (
     val: (fetchMoreOptions: FetchMoreOptionsArgs<TData, TVariables>) => PromiseApolloQueryResult
   ) => {
@@ -52,6 +65,17 @@ export class QueryTemplate<
     this.fetchMoreOptions = val;
   };
 
-  public wrappedLoadMore = (newCursor: string, tiebreaker?: string) =>
-    this.fetchMore(this.fetchMoreOptions(newCursor, tiebreaker));
+  public setRefetch = (val: (variables?: TVariables) => Promise<ApolloQueryResult<TData>>) => {
+    this.refetch = val;
+  };
+
+  public wrappedLoadMore = (newCursor: string, tiebreaker?: string) => {
+    this.executeBeforeFetchMore({ id: this.props.id });
+    return this.fetchMore(this.fetchMoreOptions(newCursor, tiebreaker));
+  };
+
+  public wrappedRefetch = (variables?: TVariables) => {
+    this.executeBeforeRefetch({ id: this.props.id });
+    return this.refetch(variables);
+  };
 }
diff --git a/x-pack/legacy/plugins/siem/public/containers/timeline/index.tsx b/x-pack/legacy/plugins/siem/public/containers/timeline/index.tsx
index 68d87ef565fb7..ccd8babd41e68 100644
--- a/x-pack/legacy/plugins/siem/public/containers/timeline/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/containers/timeline/index.tsx
@@ -8,7 +8,7 @@ import { getOr } from 'lodash/fp';
 import memoizeOne from 'memoize-one';
 import React from 'react';
 import { Query } from 'react-apollo';
-import { compose } from 'redux';
+import { compose, Dispatch } from 'redux';
 import { connect, ConnectedProps } from 'react-redux';
 
 import { IIndexPattern } from '../../../../../../../src/plugins/data/common/index_patterns';
@@ -26,6 +26,8 @@ import { createFilter } from '../helpers';
 import { QueryTemplate, QueryTemplateProps } from '../query_template';
 import { EventType } from '../../store/timeline/model';
 import { timelineQuery } from './index.gql_query';
+import { timelineActions } from '../../store/timeline';
+import { SIGNALS_PAGE_TIMELINE_ID } from '../../pages/detection_engine/components/signals';
 
 export interface TimelineArgs {
   events: TimelineItem[];
@@ -39,6 +41,10 @@ export interface TimelineArgs {
   getUpdatedAt: () => number;
 }
 
+export interface CustomReduxProps {
+  clearSignalsState: ({ id }: { id?: string }) => void;
+}
+
 export interface OwnProps extends QueryTemplateProps {
   children?: (args: TimelineArgs) => React.ReactNode;
   eventType?: EventType;
@@ -50,7 +56,7 @@ export interface OwnProps extends QueryTemplateProps {
   fields: string[];
 }
 
-type TimelineQueryProps = OwnProps & PropsFromRedux & WithKibanaProps;
+type TimelineQueryProps = OwnProps & PropsFromRedux & WithKibanaProps & CustomReduxProps;
 
 class TimelineQueryComponent extends QueryTemplate<
   TimelineQueryProps,
@@ -68,6 +74,7 @@ class TimelineQueryComponent extends QueryTemplate<
   public render() {
     const {
       children,
+      clearSignalsState,
       eventType = 'raw',
       id,
       indexPattern,
@@ -97,6 +104,7 @@ class TimelineQueryComponent extends QueryTemplate<
       defaultIndex,
       inspect: isInspected,
     };
+
     return (
       <Query<GetTimelineQuery.Query, GetTimelineQuery.Variables>
         query={timelineQuery}
@@ -105,6 +113,10 @@ class TimelineQueryComponent extends QueryTemplate<
         variables={variables}
       >
         {({ data, loading, fetchMore, refetch }) => {
+          this.setRefetch(refetch);
+          this.setExecuteBeforeRefetch(clearSignalsState);
+          this.setExecuteBeforeFetchMore(clearSignalsState);
+
           const timelineEdges = getOr([], 'source.Timeline.edges', data);
           this.setFetchMore(fetchMore);
           this.setFetchMoreOptions((newCursor: string, tiebreaker?: string) => ({
@@ -138,7 +150,7 @@ class TimelineQueryComponent extends QueryTemplate<
           return children!({
             id,
             inspect: getOr(null, 'source.Timeline.inspect', data),
-            refetch,
+            refetch: this.wrappedRefetch,
             loading,
             totalCount: getOr(0, 'source.Timeline.totalCount', data),
             pageInfo: getOr({}, 'source.Timeline.pageInfo', data),
@@ -168,7 +180,16 @@ const makeMapStateToProps = () => {
   return mapStateToProps;
 };
 
-const connector = connect(makeMapStateToProps);
+const mapDispatchToProps = (dispatch: Dispatch) => ({
+  clearSignalsState: ({ id }: { id?: string }) => {
+    if (id != null && id === SIGNALS_PAGE_TIMELINE_ID) {
+      dispatch(timelineActions.clearEventsLoading({ id }));
+      dispatch(timelineActions.clearEventsDeleted({ id }));
+    }
+  },
+});
+
+const connector = connect(makeMapStateToProps, mapDispatchToProps);
 
 type PropsFromRedux = ConnectedProps<typeof connector>;
 
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx
index 7cd26ac0cc41b..75f19218d9b38 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx
@@ -47,7 +47,7 @@ import {
 } from './types';
 import { dispatchUpdateTimeline } from '../../../../components/open_timeline/helpers';
 
-const SIGNALS_PAGE_TIMELINE_ID = 'signals-page';
+export const SIGNALS_PAGE_TIMELINE_ID = 'signals-page';
 
 interface OwnProps {
   canUserCRUD: boolean;
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_utility_bar/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_utility_bar/index.tsx
index 13d77385c53d4..86772eb0e155d 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_utility_bar/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_utility_bar/index.tsx
@@ -114,6 +114,7 @@ const SignalsUtilityBarComponent: React.FC<SignalsUtilityBarProps> = ({
 export const SignalsUtilityBar = React.memo(
   SignalsUtilityBarComponent,
   (prevProps, nextProps) =>
+    prevProps.areEventsLoading === nextProps.areEventsLoading &&
     prevProps.selectedEventIds === nextProps.selectedEventIds &&
     prevProps.totalCount === nextProps.totalCount &&
     prevProps.showClearSelection === nextProps.showClearSelection
diff --git a/x-pack/legacy/plugins/siem/public/store/timeline/helpers.ts b/x-pack/legacy/plugins/siem/public/store/timeline/helpers.ts
index f56bbc34ce165..fa70c1b04608d 100644
--- a/x-pack/legacy/plugins/siem/public/store/timeline/helpers.ts
+++ b/x-pack/legacy/plugins/siem/public/store/timeline/helpers.ts
@@ -1161,11 +1161,22 @@ export const setDeletedTimelineEvents = ({
     ? union(timeline.deletedEventIds, eventIds)
     : timeline.deletedEventIds.filter(currentEventId => !eventIds.includes(currentEventId));
 
+  const selectedEventIds = Object.fromEntries(
+    Object.entries(timeline.selectedEventIds).filter(
+      ([selectedEventId]) => !deletedEventIds.includes(selectedEventId)
+    )
+  );
+
+  const isSelectAllChecked =
+    Object.keys(selectedEventIds).length > 0 ? timeline.isSelectAllChecked : false;
+
   return {
     ...timelineById,
     [id]: {
       ...timeline,
       deletedEventIds,
+      selectedEventIds,
+      isSelectAllChecked,
     },
   };
 };

From 1319b6513572870bb99c412f7790d13ca4867fb4 Mon Sep 17 00:00:00 2001
From: Liza Katz <liza.katz@elastic.co>
Date: Wed, 19 Feb 2020 00:17:23 +0000
Subject: [PATCH 049/174] Search platform context cleanup (#57448)

* Initial commit - cleanup

* cleanup

* tsing

* ts fixes

* Fix jest test

* Code review fxes

* Remove empty file reference

Remove empty file reference

* code review

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../public/demo_search_strategy.ts            |  22 ++--
 examples/demo_search/public/index.ts          |   6 +-
 examples/demo_search/public/plugin.ts         |   4 +-
 .../search_explorer/public/application.tsx    |  21 ++--
 .../search_explorer/public/es_strategy.tsx    |   7 +-
 examples/search_explorer/public/plugin.tsx    |  16 +--
 .../search_explorer/public/search_api.tsx     |  18 ---
 .../search_explorer/public/types.ts           |  13 +-
 .../ui_actions_explorer/public/plugin.tsx     |   7 --
 src/plugins/data/public/plugin.ts             |   2 +-
 .../create_app_mount_context_search.test.ts   |  71 -----------
 .../search/create_app_mount_context_search.ts |  53 ---------
 .../es_search/es_search_service.test.ts       |  42 -------
 .../search/es_search/es_search_service.ts     |  41 -------
 .../es_search/es_search_strategy.test.ts      |  22 ++--
 .../search/es_search/es_search_strategy.ts    |  11 +-
 .../public/search/es_search/index.test.ts     |  25 ----
 .../data/public/search/es_search/index.ts     |  25 ----
 .../data/public/search/i_search_context.ts    |  23 ----
 .../data/public/search/i_search_setup.ts      |  40 -------
 src/plugins/data/public/search/index.ts       |  15 ++-
 .../data/public/search/search_service.test.ts |   3 +-
 .../data/public/search/search_service.ts      | 111 +++++-------------
 .../search/sync_search_strategy.test.ts       |  21 ++--
 .../public/search/sync_search_strategy.ts     |   3 +-
 .../search/{i_search_strategy.ts => types.ts} |  37 ++++--
 26 files changed, 139 insertions(+), 520 deletions(-)
 rename src/plugins/data/public/search/i_search_app_mount_context.ts => examples/search_explorer/public/types.ts (70%)
 delete mode 100644 src/plugins/data/public/search/create_app_mount_context_search.test.ts
 delete mode 100644 src/plugins/data/public/search/create_app_mount_context_search.ts
 delete mode 100644 src/plugins/data/public/search/es_search/es_search_service.test.ts
 delete mode 100644 src/plugins/data/public/search/es_search/es_search_service.ts
 delete mode 100644 src/plugins/data/public/search/es_search/index.test.ts
 delete mode 100644 src/plugins/data/public/search/es_search/index.ts
 delete mode 100644 src/plugins/data/public/search/i_search_context.ts
 delete mode 100644 src/plugins/data/public/search/i_search_setup.ts
 rename src/plugins/data/public/search/{i_search_strategy.ts => types.ts} (73%)

diff --git a/examples/demo_search/public/demo_search_strategy.ts b/examples/demo_search/public/demo_search_strategy.ts
index d2854151e14c8..cb2480c8a5f19 100644
--- a/examples/demo_search/public/demo_search_strategy.ts
+++ b/examples/demo_search/public/demo_search_strategy.ts
@@ -18,11 +18,7 @@
  */
 
 import { Observable } from 'rxjs';
-import {
-  ISearchContext,
-  SYNC_SEARCH_STRATEGY,
-  ISearchGeneric,
-} from '../../../src/plugins/data/public';
+import { ISearchContext, SYNC_SEARCH_STRATEGY } from '../../../src/plugins/data/public';
 import { TSearchStrategyProvider, ISearchStrategy } from '../../../src/plugins/data/public';
 
 import { DEMO_SEARCH_STRATEGY, IDemoResponse } from '../common';
@@ -54,15 +50,15 @@ import { DEMO_SEARCH_STRATEGY, IDemoResponse } from '../common';
  * @param search - a search function to access other strategies that have already been registered.
  */
 export const demoClientSearchStrategyProvider: TSearchStrategyProvider<typeof DEMO_SEARCH_STRATEGY> = (
-  context: ISearchContext,
-  search: ISearchGeneric
+  context: ISearchContext
 ): ISearchStrategy<typeof DEMO_SEARCH_STRATEGY> => {
+  const syncStrategyProvider = context.getSearchStrategy(SYNC_SEARCH_STRATEGY);
+  const { search } = syncStrategyProvider(context);
   return {
-    search: (request, options) =>
-      search(
-        { ...request, serverStrategy: DEMO_SEARCH_STRATEGY },
-        options,
-        SYNC_SEARCH_STRATEGY
-      ) as Observable<IDemoResponse>,
+    search: (request, options) => {
+      return search({ ...request, serverStrategy: DEMO_SEARCH_STRATEGY }, options) as Observable<
+        IDemoResponse
+      >;
+    },
   };
 };
diff --git a/examples/demo_search/public/index.ts b/examples/demo_search/public/index.ts
index 7790c2950ac22..0a97ac6b72ea7 100644
--- a/examples/demo_search/public/index.ts
+++ b/examples/demo_search/public/index.ts
@@ -17,12 +17,10 @@
  * under the License.
  */
 
-import { PluginInitializer, PluginInitializerContext } from 'kibana/public';
+import { PluginInitializer } from 'kibana/public';
 
 import { DemoDataPlugin } from './plugin';
 
 export { DEMO_SEARCH_STRATEGY } from '../common';
 
-export const plugin: PluginInitializer<void, void> = (
-  initializerContext: PluginInitializerContext
-) => new DemoDataPlugin(initializerContext);
+export const plugin: PluginInitializer<void, void> = () => new DemoDataPlugin();
diff --git a/examples/demo_search/public/plugin.ts b/examples/demo_search/public/plugin.ts
index 81ba585b99190..62c912716e627 100644
--- a/examples/demo_search/public/plugin.ts
+++ b/examples/demo_search/public/plugin.ts
@@ -18,7 +18,7 @@
  */
 
 import { DataPublicPluginSetup } from '../../../src/plugins/data/public';
-import { Plugin, CoreSetup, PluginInitializerContext } from '../../../src/core/public';
+import { Plugin, CoreSetup } from '../../../src/core/public';
 import { DEMO_SEARCH_STRATEGY } from '../common';
 import { demoClientSearchStrategyProvider } from './demo_search_strategy';
 import { IDemoRequest, IDemoResponse } from '../common';
@@ -47,10 +47,8 @@ declare module '../../../src/plugins/data/public' {
 }
 
 export class DemoDataPlugin implements Plugin {
-  constructor(private initializerContext: PluginInitializerContext) {}
   public setup(core: CoreSetup, deps: DemoDataSearchSetupDependencies) {
     deps.data.search.registerSearchStrategyProvider(
-      this.initializerContext.opaqueId,
       DEMO_SEARCH_STRATEGY,
       demoClientSearchStrategyProvider
     );
diff --git a/examples/search_explorer/public/application.tsx b/examples/search_explorer/public/application.tsx
index 801a3c615ac61..7d921adc1d29b 100644
--- a/examples/search_explorer/public/application.tsx
+++ b/examples/search_explorer/public/application.tsx
@@ -28,12 +28,13 @@ import {
   EuiSideNav,
 } from '@elastic/eui';
 
-import { AppMountContext, AppMountParameters } from '../../../src/core/public';
+import { AppMountParameters, CoreStart } from '../../../src/core/public';
 import { EsSearchTest } from './es_strategy';
 import { Page } from './page';
 import { DemoStrategy } from './demo_strategy';
 import { DocumentationPage } from './documentation';
 import { SearchApiPage } from './search_api';
+import { AppPluginStartDependencies, SearchBarComponentParams } from './types';
 
 const Home = () => <DocumentationPage />;
 
@@ -44,7 +45,7 @@ interface PageDef {
 }
 
 type NavProps = RouteComponentProps & {
-  navigateToApp: AppMountContext['core']['application']['navigateToApp'];
+  navigateToApp: CoreStart['application']['navigateToApp'];
   pages: PageDef[];
 };
 
@@ -71,7 +72,7 @@ const Nav = withRouter(({ history, navigateToApp, pages }: NavProps) => {
 
 const buildPage = (page: PageDef) => <Page title={page.title}>{page.component}</Page>;
 
-const SearchApp = ({ basename, context }: { basename: string; context: AppMountContext }) => {
+const SearchApp = ({ basename, data, application }: SearchBarComponentParams) => {
   const pages: PageDef[] = [
     {
       id: 'home',
@@ -86,12 +87,12 @@ const SearchApp = ({ basename, context }: { basename: string; context: AppMountC
     {
       title: 'ES search strategy',
       id: 'esSearch',
-      component: <EsSearchTest search={context.search!.search} />,
+      component: <EsSearchTest search={data.search.search} />,
     },
     {
       title: 'Demo search strategy',
       id: 'demoSearch',
-      component: <DemoStrategy search={context.search!.search} />,
+      component: <DemoStrategy search={data.search.search} />,
     },
   ];
 
@@ -103,7 +104,7 @@ const SearchApp = ({ basename, context }: { basename: string; context: AppMountC
     <Router basename={basename}>
       <EuiPage>
         <EuiPageSideBar>
-          <Nav navigateToApp={context.core.application.navigateToApp} pages={pages} />
+          <Nav navigateToApp={application.navigateToApp} pages={pages} />
         </EuiPageSideBar>
         <Route path="/" exact component={Home} />
         {routes}
@@ -113,10 +114,14 @@ const SearchApp = ({ basename, context }: { basename: string; context: AppMountC
 };
 
 export const renderApp = (
-  context: AppMountContext,
+  coreStart: CoreStart,
+  deps: AppPluginStartDependencies,
   { appBasePath, element }: AppMountParameters
 ) => {
-  ReactDOM.render(<SearchApp basename={appBasePath} context={context} />, element);
+  ReactDOM.render(
+    <SearchApp basename={appBasePath} data={deps.data} application={coreStart.application} />,
+    element
+  );
 
   return () => ReactDOM.unmountComponentAtNode(element);
 };
diff --git a/examples/search_explorer/public/es_strategy.tsx b/examples/search_explorer/public/es_strategy.tsx
index e26c11a646669..5d2617e64a79e 100644
--- a/examples/search_explorer/public/es_strategy.tsx
+++ b/examples/search_explorer/public/es_strategy.tsx
@@ -38,8 +38,6 @@ import serverPlugin from '!!raw-loader!./../../../src/plugins/data/server/search
 // @ts-ignore
 import serverStrategy from '!!raw-loader!./../../../src/plugins/data/server/search/es_search/es_search_strategy';
 
-// @ts-ignore
-import publicPlugin from '!!raw-loader!./../../../src/plugins/data/public/search/es_search/es_search_service';
 // @ts-ignore
 import publicStrategy from '!!raw-loader!./../../../src/plugins/data/public/search/es_search/es_search_strategy';
 
@@ -125,10 +123,7 @@ export class EsSearchTest extends React.Component<Props, State> {
           codeSections={[
             {
               title: 'Public',
-              code: [
-                { description: 'es_search_service.ts', snippet: publicPlugin },
-                { description: 'es_search_strategy.ts', snippet: publicStrategy },
-              ],
+              code: [{ description: 'es_search_strategy.ts', snippet: publicStrategy }],
             },
             {
               title: 'Server',
diff --git a/examples/search_explorer/public/plugin.tsx b/examples/search_explorer/public/plugin.tsx
index a7a6fd11341a4..29b236e82bf46 100644
--- a/examples/search_explorer/public/plugin.tsx
+++ b/examples/search_explorer/public/plugin.tsx
@@ -17,22 +17,18 @@
  * under the License.
  */
 
-import { Plugin, CoreSetup } from 'kibana/public';
-import { ISearchAppMountContext } from '../../../src/plugins/data/public';
+import { Plugin, CoreSetup, AppMountParameters } from 'kibana/public';
+import { AppPluginStartDependencies } from './types';
 
-declare module 'kibana/public' {
-  interface AppMountContext {
-    search?: ISearchAppMountContext;
-  }
-}
 export class SearchExplorerPlugin implements Plugin {
-  public setup(core: CoreSetup) {
+  public setup(core: CoreSetup<AppPluginStartDependencies>) {
     core.application.register({
       id: 'searchExplorer',
       title: 'Search Explorer',
-      async mount(context, params) {
+      async mount(params: AppMountParameters) {
+        const [coreStart, depsStart] = await core.getStartServices();
         const { renderApp } = await import('./application');
-        return renderApp(context, params);
+        return renderApp(coreStart, depsStart, params);
       },
     });
   }
diff --git a/examples/search_explorer/public/search_api.tsx b/examples/search_explorer/public/search_api.tsx
index fc68571e4ef68..c77ec725c6890 100644
--- a/examples/search_explorer/public/search_api.tsx
+++ b/examples/search_explorer/public/search_api.tsx
@@ -19,10 +19,6 @@
 import React from 'react';
 import { GuideSection } from './guide_section';
 
-// @ts-ignore
-import publicSetupContract from '!!raw-loader!./../../../src/plugins/data/public/search/i_search_setup';
-// @ts-ignore
-import publicSearchStrategy from '!!raw-loader!./../../../src/plugins/data/public/search/i_search_strategy';
 // @ts-ignore
 import publicSearch from '!!raw-loader!./../../../src/plugins/data/public/search/i_search';
 // @ts-ignore
@@ -31,8 +27,6 @@ import publicPlugin from '!!raw-loader!./../../../src/plugins/data/public/search
 // @ts-ignore
 import serverSetupContract from '!!raw-loader!./../../../src/plugins/data/server/search/i_search_setup';
 // @ts-ignore
-import serverSearchStrategy from '!!raw-loader!./../../../src/plugins/data/server/search/i_search_strategy';
-// @ts-ignore
 import serverSearch from '!!raw-loader!./../../../src/plugins/data/server/search/i_search';
 // @ts-ignore
 import serverPlugin from '!!raw-loader!./../../../src/plugins/data/server/search/search_service';
@@ -47,18 +41,10 @@ export const SearchApiPage = () => (
             description: 'search_service.ts',
             snippet: publicPlugin,
           },
-          {
-            description: `i_search_setup.ts`,
-            snippet: publicSetupContract,
-          },
           {
             description: 'i_search',
             snippet: publicSearch,
           },
-          {
-            description: 'i_search_strategy',
-            snippet: publicSearchStrategy,
-          },
         ],
       },
       {
@@ -76,10 +62,6 @@ export const SearchApiPage = () => (
             description: 'i_search',
             snippet: serverSearch,
           },
-          {
-            description: 'i_search_strategy',
-            snippet: serverSearchStrategy,
-          },
         ],
       },
     ]}
diff --git a/src/plugins/data/public/search/i_search_app_mount_context.ts b/examples/search_explorer/public/types.ts
similarity index 70%
rename from src/plugins/data/public/search/i_search_app_mount_context.ts
rename to examples/search_explorer/public/types.ts
index 36cae0083ce56..af520d84f82ea 100644
--- a/src/plugins/data/public/search/i_search_app_mount_context.ts
+++ b/examples/search_explorer/public/types.ts
@@ -17,8 +17,15 @@
  * under the License.
  */
 
-import { ISearchGeneric } from './i_search';
+import { CoreStart } from 'kibana/public';
+import { DataPublicPluginStart } from '../../../src/plugins/data/public';
 
-export interface ISearchAppMountContext {
-  search: ISearchGeneric;
+export interface AppPluginStartDependencies {
+  data: DataPublicPluginStart;
+}
+
+export interface SearchBarComponentParams {
+  application: CoreStart['application'];
+  basename: string;
+  data: DataPublicPluginStart;
 }
diff --git a/examples/ui_actions_explorer/public/plugin.tsx b/examples/ui_actions_explorer/public/plugin.tsx
index 981ad97a31b46..953bfd3f52692 100644
--- a/examples/ui_actions_explorer/public/plugin.tsx
+++ b/examples/ui_actions_explorer/public/plugin.tsx
@@ -19,7 +19,6 @@
 
 import { Plugin, CoreSetup, AppMountParameters } from 'kibana/public';
 import { UiActionsStart, UiActionsSetup } from 'src/plugins/ui_actions/public';
-import { ISearchAppMountContext } from '../../../src/plugins/data/public';
 import {
   PHONE_TRIGGER,
   USER_TRIGGER,
@@ -38,12 +37,6 @@ import {
   SHOWCASE_PLUGGABILITY_ACTION,
 } from './actions/actions';
 
-declare module 'kibana/public' {
-  interface AppMountContext {
-    search?: ISearchAppMountContext;
-  }
-}
-
 interface StartDeps {
   uiActions: UiActionsStart;
 }
diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts
index 560f415eb082a..8ce379547ead5 100644
--- a/src/plugins/data/public/plugin.ts
+++ b/src/plugins/data/public/plugin.ts
@@ -57,7 +57,7 @@ export class DataPublicPlugin implements Plugin<DataPublicPluginSetup, DataPubli
   private readonly packageInfo: PackageInfo;
 
   constructor(initializerContext: PluginInitializerContext) {
-    this.searchService = new SearchService(initializerContext);
+    this.searchService = new SearchService();
     this.queryService = new QueryService();
     this.fieldFormatsService = new FieldFormatsService();
     this.storage = new Storage(window.localStorage);
diff --git a/src/plugins/data/public/search/create_app_mount_context_search.test.ts b/src/plugins/data/public/search/create_app_mount_context_search.test.ts
deleted file mode 100644
index fa7cdbcda3082..0000000000000
--- a/src/plugins/data/public/search/create_app_mount_context_search.test.ts
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { createAppMountSearchContext } from './create_app_mount_context_search';
-import { from } from 'rxjs';
-
-describe('Create app mount search context', () => {
-  it('Returns search fn when there are no strategies', () => {
-    const context = createAppMountSearchContext({});
-    expect(context.search).toBeDefined();
-  });
-
-  it(`Search throws an error when the strategy doesn't exist`, () => {
-    const context = createAppMountSearchContext({});
-    expect(() => context.search({}, {}, 'noexist').toPromise()).toThrowErrorMatchingInlineSnapshot(
-      `"Strategy with name noexist does not exist"`
-    );
-  });
-
-  it(`Search fn is called on appropriate strategy name`, done => {
-    const context = createAppMountSearchContext({
-      mysearch: search =>
-        Promise.resolve({
-          search: () => from(Promise.resolve({ percentComplete: 98 })),
-        }),
-      anothersearch: search =>
-        Promise.resolve({
-          search: () => from(Promise.resolve({ percentComplete: 0 })),
-        }),
-    });
-
-    context.search({}, {}, 'mysearch').subscribe(response => {
-      expect(response).toEqual({ percentComplete: 98 });
-      done();
-    });
-  });
-
-  it(`Search fn is called with the passed in request object`, done => {
-    const context = createAppMountSearchContext({
-      mysearch: search => {
-        return Promise.resolve({
-          search: request => {
-            expect(request).toEqual({ greeting: 'hi' });
-            return from(Promise.resolve({}));
-          },
-        });
-      },
-    });
-    context.search({ greeting: 'hi' } as any, {}, 'mysearch').subscribe(
-      response => {},
-      () => {},
-      done
-    );
-  });
-});
diff --git a/src/plugins/data/public/search/create_app_mount_context_search.ts b/src/plugins/data/public/search/create_app_mount_context_search.ts
deleted file mode 100644
index 7a617e0bab837..0000000000000
--- a/src/plugins/data/public/search/create_app_mount_context_search.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { mergeMap } from 'rxjs/operators';
-import { from } from 'rxjs';
-import { ISearchAppMountContext } from './i_search_app_mount_context';
-import { ISearchGeneric } from './i_search';
-import {
-  TSearchStrategiesMap,
-  ISearchStrategy,
-  TSearchStrategyProviderEnhanced,
-} from './i_search_strategy';
-import { TStrategyTypes } from './strategy_types';
-import { DEFAULT_SEARCH_STRATEGY } from '../../common/search';
-
-export const createAppMountSearchContext = (
-  searchStrategies: TSearchStrategiesMap
-): ISearchAppMountContext => {
-  const getSearchStrategy = <K extends TStrategyTypes = typeof DEFAULT_SEARCH_STRATEGY>(
-    strategyName?: K
-  ): Promise<ISearchStrategy<K>> => {
-    const strategyProvider = searchStrategies[
-      strategyName ? strategyName : DEFAULT_SEARCH_STRATEGY
-    ] as TSearchStrategyProviderEnhanced<K> | undefined;
-    if (!strategyProvider) {
-      throw new Error(`Strategy with name ${strategyName} does not exist`);
-    }
-    return strategyProvider(search);
-  };
-
-  const search: ISearchGeneric = (request, options, strategyName) => {
-    const strategyPromise = getSearchStrategy(strategyName);
-    return from(strategyPromise).pipe(mergeMap(strategy => strategy.search(request, options)));
-  };
-
-  return { search };
-};
diff --git a/src/plugins/data/public/search/es_search/es_search_service.test.ts b/src/plugins/data/public/search/es_search/es_search_service.test.ts
deleted file mode 100644
index d756b54a5cce6..0000000000000
--- a/src/plugins/data/public/search/es_search/es_search_service.test.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { coreMock } from '../../../../../core/public/mocks';
-import { EsSearchService } from './es_search_service';
-import { CoreSetup } from '../../../../../core/public';
-import { searchSetupMock } from '../mocks';
-
-describe('ES search strategy service', () => {
-  let service: EsSearchService;
-  let mockCoreSetup: MockedKeys<CoreSetup>;
-
-  beforeEach(() => {
-    service = new EsSearchService(coreMock.createPluginInitializerContext());
-    mockCoreSetup = coreMock.createSetup();
-  });
-
-  describe('setup()', () => {
-    it('registers the ES search strategy', async () => {
-      service.setup(mockCoreSetup, {
-        search: searchSetupMock,
-      });
-      expect(searchSetupMock.registerSearchStrategyProvider).toBeCalled();
-    });
-  });
-});
diff --git a/src/plugins/data/public/search/es_search/es_search_service.ts b/src/plugins/data/public/search/es_search/es_search_service.ts
deleted file mode 100644
index fe74292d9f8fa..0000000000000
--- a/src/plugins/data/public/search/es_search/es_search_service.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { Plugin, CoreSetup, PluginInitializerContext } from '../../../../../core/public';
-import { ES_SEARCH_STRATEGY } from '../../../common/search/es_search';
-import { esSearchStrategyProvider } from './es_search_strategy';
-import { ISearchSetup } from '../i_search_setup';
-
-export interface IEsSearchSetupDependencies {
-  search: ISearchSetup;
-}
-
-export class EsSearchService implements Plugin {
-  constructor(private initializerContext: PluginInitializerContext) {}
-  public setup(core: CoreSetup, deps: IEsSearchSetupDependencies) {
-    deps.search.registerSearchStrategyProvider(
-      this.initializerContext.opaqueId,
-      ES_SEARCH_STRATEGY,
-      esSearchStrategyProvider
-    );
-  }
-
-  public start() {}
-  public stop() {}
-}
diff --git a/src/plugins/data/public/search/es_search/es_search_strategy.test.ts b/src/plugins/data/public/search/es_search/es_search_strategy.test.ts
index 915a67412e519..de1835f09f3bf 100644
--- a/src/plugins/data/public/search/es_search/es_search_strategy.test.ts
+++ b/src/plugins/data/public/search/es_search/es_search_strategy.test.ts
@@ -19,15 +19,15 @@
 
 import { coreMock } from '../../../../../core/public/mocks';
 import { esSearchStrategyProvider } from './es_search_strategy';
-import { CoreSetup } from 'kibana/public';
+import { CoreStart } from 'kibana/public';
 import { ES_SEARCH_STRATEGY } from '../../../common/search/es_search';
 
 describe('ES search strategy', () => {
-  let mockCoreSetup: MockedKeys<CoreSetup>;
+  let mockCoreStart: MockedKeys<CoreStart>;
   const mockSearch = jest.fn();
 
   beforeEach(() => {
-    mockCoreSetup = coreMock.createSetup();
+    mockCoreStart = coreMock.createStart();
     mockSearch.mockClear();
   });
 
@@ -35,12 +35,16 @@ describe('ES search strategy', () => {
     const request = { params: {} };
     const options = {};
 
-    const esSearch = esSearchStrategyProvider(
-      {
-        core: mockCoreSetup,
-      },
-      mockSearch
-    );
+    const esSearch = esSearchStrategyProvider({
+      core: mockCoreStart,
+      getSearchStrategy: jest.fn().mockImplementation(() => {
+        return () => {
+          return {
+            search: mockSearch,
+          };
+        };
+      }),
+    });
     esSearch.search(request, options);
 
     expect(mockSearch.mock.calls[0][0]).toEqual({
diff --git a/src/plugins/data/public/search/es_search/es_search_strategy.ts b/src/plugins/data/public/search/es_search/es_search_strategy.ts
index d29f3b6882b26..a5eab20a89f53 100644
--- a/src/plugins/data/public/search/es_search/es_search_strategy.ts
+++ b/src/plugins/data/public/search/es_search/es_search_strategy.ts
@@ -21,11 +21,10 @@ import { Observable } from 'rxjs';
 import { ES_SEARCH_STRATEGY, IEsSearchResponse } from '../../../common/search';
 import { SYNC_SEARCH_STRATEGY } from '../sync_search_strategy';
 import { getEsPreference } from './get_es_preference';
-import { TSearchStrategyProvider, ISearchStrategy, ISearchGeneric, ISearchContext } from '..';
+import { ISearchContext, TSearchStrategyProvider, ISearchStrategy } from '../types';
 
 export const esSearchStrategyProvider: TSearchStrategyProvider<typeof ES_SEARCH_STRATEGY> = (
-  context: ISearchContext,
-  search: ISearchGeneric
+  context: ISearchContext
 ): ISearchStrategy<typeof ES_SEARCH_STRATEGY> => {
   return {
     search: (request, options) => {
@@ -34,10 +33,10 @@ export const esSearchStrategyProvider: TSearchStrategyProvider<typeof ES_SEARCH_
         const customPreference = context.core.uiSettings.get('courier:customRequestPreference');
         request.params.preference = getEsPreference(setPreference, customPreference);
       }
-      return search(
+      const syncStrategyProvider = context.getSearchStrategy(SYNC_SEARCH_STRATEGY);
+      return syncStrategyProvider(context).search(
         { ...request, serverStrategy: ES_SEARCH_STRATEGY },
-        options,
-        SYNC_SEARCH_STRATEGY
+        options
       ) as Observable<IEsSearchResponse>;
     },
   };
diff --git a/src/plugins/data/public/search/es_search/index.test.ts b/src/plugins/data/public/search/es_search/index.test.ts
deleted file mode 100644
index ecceffc752e9a..0000000000000
--- a/src/plugins/data/public/search/es_search/index.test.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-import { coreMock } from '../../../../../../src/core/public/mocks';
-import { esSearchService } from '.';
-
-it('es search service is instantiated', () => {
-  const esSearch = esSearchService(coreMock.createPluginInitializerContext());
-  expect(esSearch).toBeDefined();
-});
diff --git a/src/plugins/data/public/search/es_search/index.ts b/src/plugins/data/public/search/es_search/index.ts
deleted file mode 100644
index 31952f14c63c5..0000000000000
--- a/src/plugins/data/public/search/es_search/index.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { PluginInitializer, PluginInitializerContext } from 'kibana/public';
-import { EsSearchService } from './es_search_service';
-
-export const esSearchService: PluginInitializer<void, void> = (
-  initializerContext: PluginInitializerContext
-) => new EsSearchService(initializerContext);
diff --git a/src/plugins/data/public/search/i_search_context.ts b/src/plugins/data/public/search/i_search_context.ts
deleted file mode 100644
index da94988027ff6..0000000000000
--- a/src/plugins/data/public/search/i_search_context.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-import { CoreSetup } from '../../../../core/public';
-
-export interface ISearchContext {
-  core: CoreSetup;
-}
diff --git a/src/plugins/data/public/search/i_search_setup.ts b/src/plugins/data/public/search/i_search_setup.ts
deleted file mode 100644
index a59c62e2e0d9b..0000000000000
--- a/src/plugins/data/public/search/i_search_setup.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { IContextProvider } from 'kibana/public';
-import { ISearchContext } from './i_search_context';
-import { TRegisterSearchStrategyProvider, TSearchStrategyProvider } from './i_search_strategy';
-
-/**
- * The setup contract exposed by the Search plugin exposes the search strategy extension
- * point.
- */
-export interface ISearchSetup {
-  registerSearchStrategyContext: <TContextName extends keyof ISearchContext>(
-    pluginId: symbol,
-    contextName: TContextName,
-    provider: IContextProvider<TSearchStrategyProvider<any>, TContextName>
-  ) => void;
-
-  /**
-   * Extension point exposed for other plugins to register their own search
-   * strategies.
-   */
-  registerSearchStrategyProvider: TRegisterSearchStrategyProvider;
-}
diff --git a/src/plugins/data/public/search/index.ts b/src/plugins/data/public/search/index.ts
index 9d6e5072d657f..762c278a05640 100644
--- a/src/plugins/data/public/search/index.ts
+++ b/src/plugins/data/public/search/index.ts
@@ -17,12 +17,13 @@
  * under the License.
  */
 
-export { ISearchAppMountContext } from './i_search_app_mount_context';
-
-export { ISearchSetup } from './i_search_setup';
-export { ISearchStart } from './search_service';
-
-export { ISearchContext } from './i_search_context';
+export {
+  ISearchSetup,
+  ISearchStart,
+  ISearchContext,
+  TSearchStrategyProvider,
+  ISearchStrategy,
+} from './types';
 
 export {
   ISearch,
@@ -32,8 +33,6 @@ export {
   ISearchGeneric,
 } from './i_search';
 
-export { TSearchStrategyProvider, ISearchStrategy } from './i_search_strategy';
-
 export { IEsSearchResponse, IEsSearchRequest, ES_SEARCH_STRATEGY } from '../../common/search';
 
 export { SYNC_SEARCH_STRATEGY } from './sync_search_strategy';
diff --git a/src/plugins/data/public/search/search_service.test.ts b/src/plugins/data/public/search/search_service.test.ts
index 9ed02e1d61018..4bf027b07b833 100644
--- a/src/plugins/data/public/search/search_service.test.ts
+++ b/src/plugins/data/public/search/search_service.test.ts
@@ -27,7 +27,7 @@ describe('Search service', () => {
   let mockCoreSetup: MockedKeys<CoreSetup>;
 
   beforeEach(() => {
-    searchService = new SearchService(coreMock.createPluginInitializerContext());
+    searchService = new SearchService();
     mockCoreSetup = coreMock.createSetup();
   });
 
@@ -36,7 +36,6 @@ describe('Search service', () => {
       const setup = searchService.setup(mockCoreSetup, {
         version: '8',
       } as any);
-      expect(setup).toHaveProperty('registerSearchStrategyContext');
       expect(setup).toHaveProperty('registerSearchStrategyProvider');
     });
   });
diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts
index dd67c04f1d7bd..559b27a98cd64 100644
--- a/src/plugins/data/public/search/search_service.ts
+++ b/src/plugins/data/public/search/search_service.ts
@@ -16,46 +16,15 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import {
-  Plugin,
-  CoreSetup,
-  PluginInitializerContext,
-  CoreStart,
-  IContextContainer,
-  PluginOpaqueId,
-  PackageInfo,
-} from '../../../../core/public';
 
-import { ISearchAppMountContext } from './i_search_app_mount_context';
-import { ISearchSetup } from './i_search_setup';
-import { createAppMountSearchContext } from './create_app_mount_context_search';
+import { Plugin, CoreSetup, CoreStart, PackageInfo } from '../../../../core/public';
+
 import { SYNC_SEARCH_STRATEGY, syncSearchStrategyProvider } from './sync_search_strategy';
-import {
-  TSearchStrategyProvider,
-  TRegisterSearchStrategyProvider,
-  TSearchStrategiesMap,
-} from './i_search_strategy';
+import { ISearchSetup, ISearchStart, TSearchStrategyProvider, TSearchStrategiesMap } from './types';
 import { TStrategyTypes } from './strategy_types';
-import { esSearchService } from './es_search';
-import { ISearchGeneric } from './i_search';
 import { getEsClient, LegacyApiCaller } from './es_client';
-
-/**
- * Extends the AppMountContext so other plugins have access
- * to search functionality in their applications.
- */
-declare module 'kibana/public' {
-  interface AppMountContext {
-    search?: ISearchAppMountContext;
-  }
-}
-
-export interface ISearchStart {
-  search: ISearchGeneric;
-  __LEGACY: {
-    esClient: LegacyApiCaller;
-  };
-}
+import { ES_SEARCH_STRATEGY, DEFAULT_SEARCH_STRATEGY } from '../../common/search';
+import { esSearchStrategyProvider } from './es_search/es_search_strategy';
 
 /**
  * The search plugin exposes two registration methods for other plugins:
@@ -73,63 +42,43 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
    */
   private searchStrategies: TSearchStrategiesMap = {};
 
-  /**
-   * Exposes context to the search strategies.
-   */
-  private contextContainer?: IContextContainer<TSearchStrategyProvider<any>>;
   private esClient?: LegacyApiCaller;
-  private search?: ISearchGeneric;
 
-  constructor(private initializerContext: PluginInitializerContext) {}
+  private registerSearchStrategyProvider = <T extends TStrategyTypes>(
+    name: T,
+    strategyProvider: TSearchStrategyProvider<T>
+  ) => {
+    this.searchStrategies[name] = strategyProvider;
+  };
+
+  private getSearchStrategy = <T extends TStrategyTypes>(name: T): TSearchStrategyProvider<T> => {
+    const strategyProvider = this.searchStrategies[name];
+    if (!strategyProvider) throw new Error(`Search strategy ${name} not found`);
+    return strategyProvider;
+  };
 
   public setup(core: CoreSetup, packageInfo: PackageInfo): ISearchSetup {
-    const search = (this.search = createAppMountSearchContext(this.searchStrategies).search);
-    core.application.registerMountContext<'search'>('search', () => {
-      return { search };
-    });
-
-    this.contextContainer = core.context.createContextContainer();
     this.esClient = getEsClient(core.injectedMetadata, core.http, packageInfo);
 
-    const registerSearchStrategyProvider: TRegisterSearchStrategyProvider = <
-      T extends TStrategyTypes
-    >(
-      plugin: PluginOpaqueId,
-      name: T,
-      strategyProvider: TSearchStrategyProvider<T>
-    ) => {
-      this.searchStrategies[name] = this.contextContainer!.createHandler(plugin, strategyProvider);
-    };
-
-    const api = {
-      registerSearchStrategyContext: this.contextContainer!.registerContext,
-      registerSearchStrategyProvider,
-      __LEGACY: {
-        esClient: this.esClient,
-      },
-    };
-
-    api.registerSearchStrategyContext(this.initializerContext.opaqueId, 'core', () => core);
-    api.registerSearchStrategyProvider(
-      this.initializerContext.opaqueId,
-      SYNC_SEARCH_STRATEGY,
-      syncSearchStrategyProvider
-    );
+    this.registerSearchStrategyProvider(SYNC_SEARCH_STRATEGY, syncSearchStrategyProvider);
 
-    // ES search capabilities are written in a way that it could easily be a separate plugin,
-    // however these two plugins are tightly coupled due to the default search strategy using
-    // es search types.
-    esSearchService(this.initializerContext).setup(core, { search: api });
+    this.registerSearchStrategyProvider(ES_SEARCH_STRATEGY, esSearchStrategyProvider);
 
-    return api;
+    return {
+      registerSearchStrategyProvider: this.registerSearchStrategyProvider,
+    };
   }
 
   public start(core: CoreStart): ISearchStart {
-    if (!this.search) {
-      throw new Error('Search should always be defined');
-    }
     return {
-      search: this.search,
+      search: (request, options, strategyName) => {
+        const strategyProvider = this.getSearchStrategy(strategyName || DEFAULT_SEARCH_STRATEGY);
+        const { search } = strategyProvider({
+          core,
+          getSearchStrategy: this.getSearchStrategy,
+        });
+        return search(request as any, options);
+      },
       __LEGACY: {
         esClient: this.esClient!,
       },
diff --git a/src/plugins/data/public/search/sync_search_strategy.test.ts b/src/plugins/data/public/search/sync_search_strategy.test.ts
index cd19c4d84dce1..9378e5833f8d7 100644
--- a/src/plugins/data/public/search/sync_search_strategy.test.ts
+++ b/src/plugins/data/public/search/sync_search_strategy.test.ts
@@ -19,32 +19,29 @@
 
 import { coreMock } from '../../../../core/public/mocks';
 import { SYNC_SEARCH_STRATEGY, syncSearchStrategyProvider } from './sync_search_strategy';
-import { CoreSetup } from '../../../../core/public';
+import { CoreStart } from 'kibana/public';
 
 describe('Sync search strategy', () => {
-  let mockCoreSetup: MockedKeys<CoreSetup>;
-  const mockSearch = jest.fn();
+  let mockCoreStart: MockedKeys<CoreStart>;
 
   beforeEach(() => {
-    mockCoreSetup = coreMock.createSetup();
+    mockCoreStart = coreMock.createStart();
   });
 
   it('returns a strategy with `search` that calls the backend API', () => {
-    mockCoreSetup.http.fetch.mockImplementationOnce(() => Promise.resolve());
+    mockCoreStart.http.fetch.mockImplementationOnce(() => Promise.resolve());
 
-    const syncSearch = syncSearchStrategyProvider(
-      {
-        core: mockCoreSetup,
-      },
-      mockSearch
-    );
+    const syncSearch = syncSearchStrategyProvider({
+      core: mockCoreStart,
+      getSearchStrategy: jest.fn(),
+    });
     syncSearch.search(
       {
         serverStrategy: SYNC_SEARCH_STRATEGY,
       },
       {}
     );
-    expect(mockCoreSetup.http.fetch.mock.calls[0][0]).toEqual({
+    expect(mockCoreStart.http.fetch.mock.calls[0][0]).toEqual({
       path: `/internal/search/${SYNC_SEARCH_STRATEGY}`,
       body: JSON.stringify({
         serverStrategy: 'SYNC_SEARCH_STRATEGY',
diff --git a/src/plugins/data/public/search/sync_search_strategy.ts b/src/plugins/data/public/search/sync_search_strategy.ts
index b895a177d8608..c2cc180af546e 100644
--- a/src/plugins/data/public/search/sync_search_strategy.ts
+++ b/src/plugins/data/public/search/sync_search_strategy.ts
@@ -19,9 +19,8 @@
 
 import { BehaviorSubject, from } from 'rxjs';
 import { IKibanaSearchRequest, IKibanaSearchResponse } from '../../common/search';
-import { ISearchContext } from './i_search_context';
 import { ISearch, ISearchOptions } from './i_search';
-import { TSearchStrategyProvider, ISearchStrategy } from './i_search_strategy';
+import { TSearchStrategyProvider, ISearchStrategy, ISearchContext } from './types';
 
 export const SYNC_SEARCH_STRATEGY = 'SYNC_SEARCH_STRATEGY';
 
diff --git a/src/plugins/data/public/search/i_search_strategy.ts b/src/plugins/data/public/search/types.ts
similarity index 73%
rename from src/plugins/data/public/search/i_search_strategy.ts
rename to src/plugins/data/public/search/types.ts
index bd67409d5054a..1f888d8b5b607 100644
--- a/src/plugins/data/public/search/i_search_strategy.ts
+++ b/src/plugins/data/public/search/types.ts
@@ -17,9 +17,15 @@
  * under the License.
  */
 
+import { CoreStart } from 'kibana/public';
 import { ISearch, ISearchGeneric } from './i_search';
 import { TStrategyTypes } from './strategy_types';
-import { ISearchContext } from './i_search_context';
+import { LegacyApiCaller } from './es_client';
+
+export interface ISearchContext {
+  core: CoreStart;
+  getSearchStrategy: <T extends TStrategyTypes>(name: T) => TSearchStrategyProvider<T>;
+}
 
 /**
  * Search strategy interface contains a search method that takes in
@@ -38,14 +44,17 @@ export type TSearchStrategyProviderEnhanced<T extends TStrategyTypes> = (
   search: ISearchGeneric
 ) => Promise<ISearchStrategy<T>>;
 
+export type TSearchStrategiesMap = {
+  [K in TStrategyTypes]?: TSearchStrategyProvider<any>;
+};
+
 /**
  * Search strategy provider creates an instance of a search strategy with the request
  * handler context bound to it. This way every search strategy can use
  * whatever information they require from the request context.
  */
 export type TSearchStrategyProvider<T extends TStrategyTypes> = (
-  context: ISearchContext,
-  search: ISearchGeneric
+  context: ISearchContext
 ) => ISearchStrategy<T>;
 
 /**
@@ -53,11 +62,25 @@ export type TSearchStrategyProvider<T extends TStrategyTypes> = (
  * strategies.
  */
 export type TRegisterSearchStrategyProvider = <T extends TStrategyTypes>(
-  opaqueId: symbol,
   name: T,
   searchStrategyProvider: TSearchStrategyProvider<T>
 ) => void;
 
-export type TSearchStrategiesMap = {
-  [K in TStrategyTypes]?: TSearchStrategyProviderEnhanced<K>;
-};
+/**
+ * The setup contract exposed by the Search plugin exposes the search strategy extension
+ * point.
+ */
+export interface ISearchSetup {
+  /**
+   * Extension point exposed for other plugins to register their own search
+   * strategies.
+   */
+  registerSearchStrategyProvider: TRegisterSearchStrategyProvider;
+}
+
+export interface ISearchStart {
+  search: ISearchGeneric;
+  __LEGACY: {
+    esClient: LegacyApiCaller;
+  };
+}

From 8f37f6e10c283ddd3e36fdafb82da2048c111967 Mon Sep 17 00:00:00 2001
From: Spencer <email@spalger.com>
Date: Tue, 18 Feb 2020 19:11:05 -0700
Subject: [PATCH 050/174] Unskip flaky tests (#57715)

* Revert "Skip flaky test (#57675)"

This reverts commit c965a9efa8490539327a04bb005987c72fb1e58d.

* Revert "disable firefox smoke tests so we can fix flakiness out of band"

This reverts commit fe3864282a0e3c26affc1bf3f34e235833f58041.

* Revert "skip flaky tests (#57643)"

This reverts commit b22045433ec5dee20353624b0b23d28979796871.

* Revert "skip flaky suite (#50018)"

This reverts commit b058dc2fe73abc2e00321687f27a7c15b63534c0.

* Revert "skip settings tests (#57608)"

This reverts commit 64625b282cc0042fcec96459bec6376c00db4451.

* Revert "skip failing suite (#44631)"

This reverts commit 8aa718d11e68e0ac422271e5e8a91b6a46504e1b.

* Revert "skip flaky suite (#44631)"

This reverts commit 6e4efdfa7cccab3c3053414334b90a3feff4e3c3.

* Revert "skip flaky test (#57377)"

This reverts commit 59672ab5da6d616fb77f564bd4bcff74fd0a5fa1.

* Revert "Skip save query tests (#57589)"

This reverts commit 431a1e9c894d0251da968a2dee65ac76da2e7b92.

* remove hard coded timeouts

* Revert "Revert "disable firefox smoke tests so we can fix flakiness out of band""

This reverts commit 271f8814d58e6c64be2df02d5caff3132d8edfc6.

* Revert "remove hard coded timeouts"

This reverts commit 8b843d0aa6d59c12ff179bec99006391f89639b1.

* wait for managementHome to exist, don't bail early if it's missing

* Revert "skip flaky suite (#45244)"

This reverts commit 0cee1a4adb0940ed911daf2ddf50ff15f40fa539.

* extend timeouts for common existsOrFail() calls

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 test/functional/page_objects/common_page.ts   |  2 +-
 .../advanced_settings_security.ts             |  6 ++--
 .../advanced_settings_spaces.ts               | 11 +++---
 .../feature_controls/dashboard_security.ts    | 30 ++++++++++------
 .../feature_controls/dashboard_spaces.ts      | 19 ++++++----
 .../feature_controls/dev_tools_security.ts    |  7 ++--
 .../feature_controls/dev_tools_spaces.ts      |  7 ++--
 .../feature_controls/discover_security.ts     |  6 ++--
 .../feature_controls/discover_spaces.ts       |  7 ++--
 .../index_patterns_security.ts                |  3 +-
 .../feature_controls/index_patterns_spaces.ts |  6 ++--
 .../test/functional/apps/rollup_job/tsvb.js   |  3 +-
 .../feature_controls/visualize_security.ts    | 36 +++++++++++++------
 .../feature_controls/visualize_spaces.ts      | 14 ++++----
 .../apps/triggers_actions_ui/details.ts       |  2 +-
 15 files changed, 99 insertions(+), 60 deletions(-)

diff --git a/test/functional/page_objects/common_page.ts b/test/functional/page_objects/common_page.ts
index f909852b0c936..ee3e24bc8be62 100644
--- a/test/functional/page_objects/common_page.ts
+++ b/test/functional/page_objects/common_page.ts
@@ -114,7 +114,7 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo
         );
         await find.byCssSelector(
           '[data-test-subj="kibanaChrome"] nav:not(.ng-hide)',
-          2 * defaultFindTimeout
+          6 * defaultFindTimeout
         );
         await browser.get(appUrl);
         currentUrl = await browser.getCurrentUrl();
diff --git a/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_security.ts b/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_security.ts
index 8f902471cf6cd..261a853387619 100644
--- a/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_security.ts
+++ b/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_security.ts
@@ -10,6 +10,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
   const kibanaServer = getService('kibanaServer');
   const security = getService('security');
+  const config = getService('config');
   const PageObjects = getPageObjects(['common', 'settings', 'security', 'spaceSelector']);
   const appsMenu = getService('appsMenu');
   const testSubjects = getService('testSubjects');
@@ -178,14 +179,13 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         expect(navLinks).to.eql(['Discover', 'Stack Management']);
       });
 
-      // https://github.com/elastic/kibana/issues/57377
-      it.skip(`does not allow navigation to advanced settings; redirects to management home`, async () => {
+      it(`does not allow navigation to advanced settings; redirects to management home`, async () => {
         await PageObjects.common.navigateToActualUrl('kibana', 'management/kibana/settings', {
           ensureCurrentUrl: false,
           shouldLoginIfPrompted: false,
         });
         await testSubjects.existOrFail('managementHome', {
-          timeout: 10000,
+          timeout: config.get('timeouts.waitFor'),
         });
       });
     });
diff --git a/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_spaces.ts b/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_spaces.ts
index aceebf7219c3f..53202089e8961 100644
--- a/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_spaces.ts
+++ b/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_spaces.ts
@@ -12,9 +12,9 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
   const PageObjects = getPageObjects(['common', 'settings', 'security', 'spaceSelector']);
   const testSubjects = getService('testSubjects');
   const appsMenu = getService('appsMenu');
+  const config = getService('config');
 
-  // FLAKY: https://github.com/elastic/kibana/issues/57377
-  describe.skip('spaces feature controls', () => {
+  describe('spaces feature controls', () => {
     before(async () => {
       await esArchiver.loadIfNeeded('logstash_functional');
     });
@@ -58,8 +58,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
       });
     });
 
-    // https://github.com/elastic/kibana/issues/57413
-    describe.skip('space with Advanced Settings disabled', () => {
+    describe('space with Advanced Settings disabled', () => {
       before(async () => {
         // we need to load the following in every situation as deleting
         // a space deletes all of the associated saved objects
@@ -82,7 +81,9 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
           ensureCurrentUrl: false,
           shouldLoginIfPrompted: false,
         });
-        await testSubjects.existOrFail('managementHome');
+        await testSubjects.existOrFail('managementHome', {
+          timeout: config.get('timeouts.waitFor'),
+        });
       });
     });
   });
diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts
index ad09bc5c89143..98035d2558b09 100644
--- a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts
+++ b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts
@@ -13,6 +13,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
 export default function({ getPageObjects, getService }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
   const security = getService('security');
+  const config = getService('config');
   const PageObjects = getPageObjects(['common', 'dashboard', 'security', 'spaceSelector', 'share']);
   const appsMenu = getService('appsMenu');
   const panelActions = getService('dashboardPanelActions');
@@ -21,8 +22,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
   const queryBar = getService('queryBar');
   const savedQueryManagementComponent = getService('savedQueryManagementComponent');
 
-  // FLAKY: https://github.com/elastic/kibana/issues/44631
-  describe.skip('dashboard security', () => {
+  describe('dashboard security', () => {
     before(async () => {
       await esArchiver.load('dashboard/feature_controls/security');
       await esArchiver.loadIfNeeded('logstash_functional');
@@ -88,7 +88,9 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
             shouldLoginIfPrompted: false,
           }
         );
-        await testSubjects.existOrFail('dashboardLandingPage', { timeout: 10000 });
+        await testSubjects.existOrFail('dashboardLandingPage', {
+          timeout: config.get('timeouts.waitFor'),
+        });
         await testSubjects.existOrFail('newItemButton');
       });
 
@@ -105,7 +107,9 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
             shouldLoginIfPrompted: false,
           }
         );
-        await testSubjects.existOrFail('emptyDashboardWidget', { timeout: 10000 });
+        await testSubjects.existOrFail('emptyDashboardWidget', {
+          timeout: config.get('timeouts.waitFor'),
+        });
       });
 
       it(`can view existing Dashboard`, async () => {
@@ -113,7 +117,9 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
           ensureCurrentUrl: false,
           shouldLoginIfPrompted: false,
         });
-        await testSubjects.existOrFail('embeddablePanelHeading-APie', { timeout: 10000 });
+        await testSubjects.existOrFail('embeddablePanelHeading-APie', {
+          timeout: config.get('timeouts.waitFor'),
+        });
       });
 
       it(`does not allow a visualization to be edited`, async () => {
@@ -266,7 +272,9 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
             shouldLoginIfPrompted: false,
           }
         );
-        await testSubjects.existOrFail('dashboardLandingPage', { timeout: 10000 });
+        await testSubjects.existOrFail('dashboardLandingPage', {
+          timeout: config.get('timeouts.waitFor'),
+        });
         await testSubjects.missingOrFail('newItemButton');
       });
 
@@ -291,7 +299,9 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
           ensureCurrentUrl: false,
           shouldLoginIfPrompted: false,
         });
-        await testSubjects.existOrFail('embeddablePanelHeading-APie', { timeout: 10000 });
+        await testSubjects.existOrFail('embeddablePanelHeading-APie', {
+          timeout: config.get('timeouts.waitFor'),
+        });
       });
 
       it(`Permalinks doesn't show create short-url button`, async () => {
@@ -377,7 +387,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
             shouldLoginIfPrompted: false,
           }
         );
-        await testSubjects.existOrFail('homeApp', { timeout: 10000 });
+        await testSubjects.existOrFail('homeApp', { timeout: config.get('timeouts.waitFor') });
       });
 
       it(`create new dashboard redirects to the home page`, async () => {
@@ -401,7 +411,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
             shouldLoginIfPrompted: false,
           }
         );
-        await testSubjects.existOrFail('homeApp', { timeout: 10000 });
+        await testSubjects.existOrFail('homeApp', { timeout: config.get('timeouts.waitFor') });
       });
 
       it(`edit dashboard for object which exists redirects to the home page`, async () => {
@@ -409,7 +419,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
           ensureCurrentUrl: false,
           shouldLoginIfPrompted: false,
         });
-        await testSubjects.existOrFail('homeApp', { timeout: 10000 });
+        await testSubjects.existOrFail('homeApp', { timeout: config.get('timeouts.waitFor') });
       });
     });
   });
diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_spaces.ts b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_spaces.ts
index 002ae627c488d..1f4f0f33a061e 100644
--- a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_spaces.ts
+++ b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_spaces.ts
@@ -12,6 +12,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
 
 export default function({ getPageObjects, getService }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
+  const config = getService('config');
   const spacesService = getService('spaces');
   const PageObjects = getPageObjects([
     'common',
@@ -64,7 +65,9 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
             shouldLoginIfPrompted: false,
           }
         );
-        await testSubjects.existOrFail('dashboardLandingPage', { timeout: 10000 });
+        await testSubjects.existOrFail('dashboardLandingPage', {
+          timeout: config.get('timeouts.waitFor'),
+        });
         await testSubjects.existOrFail('newItemButton');
       });
 
@@ -78,7 +81,9 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
             shouldLoginIfPrompted: false,
           }
         );
-        await testSubjects.existOrFail('emptyDashboardWidget', { timeout: 10000 });
+        await testSubjects.existOrFail('emptyDashboardWidget', {
+          timeout: config.get('timeouts.waitFor'),
+        });
       });
 
       it(`can view existing Dashboard`, async () => {
@@ -87,7 +92,9 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
           ensureCurrentUrl: false,
           shouldLoginIfPrompted: false,
         });
-        await testSubjects.existOrFail('embeddablePanelHeading-APie', { timeout: 10000 });
+        await testSubjects.existOrFail('embeddablePanelHeading-APie', {
+          timeout: config.get('timeouts.waitFor'),
+        });
       });
     });
 
@@ -126,7 +133,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
             shouldLoginIfPrompted: false,
           }
         );
-        await testSubjects.existOrFail('homeApp', { timeout: 10000 });
+        await testSubjects.existOrFail('homeApp', { timeout: config.get('timeouts.waitFor') });
       });
 
       it(`edit dashboard for object which doesn't exist redirects to the home page`, async () => {
@@ -139,7 +146,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
             shouldLoginIfPrompted: false,
           }
         );
-        await testSubjects.existOrFail('homeApp', { timeout: 10000 });
+        await testSubjects.existOrFail('homeApp', { timeout: config.get('timeouts.waitFor') });
       });
 
       it(`edit dashboard for object which exists redirects to the home page`, async () => {
@@ -148,7 +155,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
           ensureCurrentUrl: false,
           shouldLoginIfPrompted: false,
         });
-        await testSubjects.existOrFail('homeApp', { timeout: 10000 });
+        await testSubjects.existOrFail('homeApp', { timeout: config.get('timeouts.waitFor') });
       });
     });
   });
diff --git a/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_security.ts b/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_security.ts
index 9db9a913e9a4b..162bf23c29490 100644
--- a/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_security.ts
+++ b/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_security.ts
@@ -10,6 +10,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
   const security = getService('security');
   const PageObjects = getPageObjects(['common', 'console', 'security']);
+  const config = getService('config');
   const appsMenu = getService('appsMenu');
   const testSubjects = getService('testSubjects');
   const grokDebugger = getService('grokDebugger');
@@ -232,21 +233,21 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         await PageObjects.common.navigateToUrl('console', '', {
           ensureCurrentUrl: false,
         });
-        await testSubjects.existOrFail('homeApp', { timeout: 10000 });
+        await testSubjects.existOrFail('homeApp', { timeout: config.get('timeouts.waitFor') });
       });
 
       it(`navigating to search profiler redirect to homepage`, async () => {
         await PageObjects.common.navigateToUrl('searchProfiler', '', {
           ensureCurrentUrl: false,
         });
-        await testSubjects.existOrFail('homeApp', { timeout: 10000 });
+        await testSubjects.existOrFail('homeApp', { timeout: config.get('timeouts.waitFor') });
       });
 
       it(`navigating to grok debugger redirect to homepage`, async () => {
         await PageObjects.common.navigateToUrl('grokDebugger', '', {
           ensureCurrentUrl: false,
         });
-        await testSubjects.existOrFail('homeApp', { timeout: 10000 });
+        await testSubjects.existOrFail('homeApp', { timeout: config.get('timeouts.waitFor') });
       });
     });
   });
diff --git a/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_spaces.ts b/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_spaces.ts
index f917792eea027..561b7f64eb77d 100644
--- a/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_spaces.ts
+++ b/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_spaces.ts
@@ -8,6 +8,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
 
 export default function({ getPageObjects, getService }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
+  const config = getService('config');
   const spacesService = getService('spaces');
   const PageObjects = getPageObjects([
     'common',
@@ -92,21 +93,21 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         await PageObjects.common.navigateToUrl('console', '', {
           ensureCurrentUrl: false,
         });
-        await testSubjects.existOrFail('homeApp', { timeout: 10000 });
+        await testSubjects.existOrFail('homeApp', { timeout: config.get('timeouts.waitFor') });
       });
 
       it(`navigating to search profiler redirect to homepage`, async () => {
         await PageObjects.common.navigateToUrl('searchProfiler', '', {
           ensureCurrentUrl: false,
         });
-        await testSubjects.existOrFail('homeApp', { timeout: 10000 });
+        await testSubjects.existOrFail('homeApp', { timeout: config.get('timeouts.waitFor') });
       });
 
       it(`navigating to grok debugger redirect to homepage`, async () => {
         await PageObjects.common.navigateToUrl('grokDebugger', '', {
           ensureCurrentUrl: false,
         });
-        await testSubjects.existOrFail('homeApp', { timeout: 10000 });
+        await testSubjects.existOrFail('homeApp', { timeout: config.get('timeouts.waitFor') });
       });
     });
   });
diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts
index 8669577f202a7..a1d6bfefdea64 100644
--- a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts
+++ b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts
@@ -10,6 +10,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
   const security = getService('security');
   const globalNav = getService('globalNav');
+  const config = getService('config');
   const PageObjects = getPageObjects([
     'common',
     'discover',
@@ -27,8 +28,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
     await PageObjects.timePicker.setDefaultAbsoluteRange();
   }
 
-  // FLAKY: https://github.com/elastic/kibana/issues/45348
-  describe.skip('security', () => {
+  describe('security', () => {
     before(async () => {
       await esArchiver.load('discover/feature_controls/security');
       await esArchiver.loadIfNeeded('logstash_functional');
@@ -305,7 +305,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         await PageObjects.common.navigateToUrl('discover', '', {
           ensureCurrentUrl: false,
         });
-        await testSubjects.existOrFail('homeApp', { timeout: 10000 });
+        await testSubjects.existOrFail('homeApp', { timeout: config.get('timeouts.waitFor') });
       });
     });
   });
diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_spaces.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_spaces.ts
index c38dda536f253..ba7d4077b2740 100644
--- a/x-pack/test/functional/apps/discover/feature_controls/discover_spaces.ts
+++ b/x-pack/test/functional/apps/discover/feature_controls/discover_spaces.ts
@@ -8,6 +8,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
 
 export default function({ getPageObjects, getService }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
+  const config = getService('config');
   const spacesService = getService('spaces');
   const PageObjects = getPageObjects([
     'common',
@@ -59,7 +60,9 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         await PageObjects.common.navigateToApp('discover', {
           basePath: '/s/custom_space',
         });
-        await testSubjects.existOrFail('discoverSaveButton', { timeout: 10000 });
+        await testSubjects.existOrFail('discoverSaveButton', {
+          timeout: config.get('timeouts.waitFor'),
+        });
       });
 
       it('shows "visualize" field button', async () => {
@@ -156,7 +159,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
           basePath: '/s/custom_space',
           ensureCurrentUrl: false,
         });
-        await testSubjects.existOrFail('homeApp', { timeout: 10000 });
+        await testSubjects.existOrFail('homeApp', { timeout: config.get('timeouts.waitFor') });
       });
     });
   });
diff --git a/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_security.ts b/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_security.ts
index ed25816e68712..0783767d8f152 100644
--- a/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_security.ts
+++ b/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_security.ts
@@ -10,6 +10,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
   const kibanaServer = getService('kibanaServer');
   const security = getService('security');
+  const config = getService('config');
   const PageObjects = getPageObjects(['common', 'settings', 'security']);
   const appsMenu = getService('appsMenu');
   const testSubjects = getService('testSubjects');
@@ -190,7 +191,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
           ensureCurrentUrl: false,
           shouldLoginIfPrompted: false,
         });
-        await testSubjects.existOrFail('homeApp', { timeout: 10000 });
+        await testSubjects.existOrFail('homeApp', { timeout: config.get('timeouts.waitFor') });
       });
     });
   });
diff --git a/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_spaces.ts b/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_spaces.ts
index b303ad23f8977..d4422e94d10cf 100644
--- a/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_spaces.ts
+++ b/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_spaces.ts
@@ -8,6 +8,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
 
 export default function({ getPageObjects, getService }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
+  const config = getService('config');
   const spacesService = getService('spaces');
   const PageObjects = getPageObjects(['common', 'settings', 'security']);
   const testSubjects = getService('testSubjects');
@@ -52,8 +53,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
       });
     });
 
-    // https://github.com/elastic/kibana/issues/57601
-    describe.skip('space with Index Patterns disabled', () => {
+    describe('space with Index Patterns disabled', () => {
       before(async () => {
         // we need to load the following in every situation as deleting
         // a space deletes all of the associated saved objects
@@ -76,7 +76,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
           ensureCurrentUrl: false,
           shouldLoginIfPrompted: false,
         });
-        await testSubjects.existOrFail('homeApp', { timeout: 10000 });
+        await testSubjects.existOrFail('homeApp', { timeout: config.get('timeouts.waitFor') });
       });
     });
   });
diff --git a/x-pack/test/functional/apps/rollup_job/tsvb.js b/x-pack/test/functional/apps/rollup_job/tsvb.js
index b697e751ef550..f3782c4c91644 100644
--- a/x-pack/test/functional/apps/rollup_job/tsvb.js
+++ b/x-pack/test/functional/apps/rollup_job/tsvb.js
@@ -21,8 +21,7 @@ export default function({ getService, getPageObjects }) {
     'timePicker',
   ]);
 
-  // https://github.com/elastic/kibana/issues/56816
-  describe.skip('tsvb integration', function() {
+  describe('tsvb integration', function() {
     //Since rollups can only be created once with the same name (even if you delete it),
     //we add the Date.now() to avoid name collision if you run the tests locally back to back.
     const rollupJobName = `tsvb-test-rollup-job-${Date.now()}`;
diff --git a/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts b/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts
index 6db8ad28deccb..1876f46038326 100644
--- a/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts
+++ b/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts
@@ -9,6 +9,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
 export default function({ getPageObjects, getService }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
   const security = getService('security');
+  const config = getService('config');
   const PageObjects = getPageObjects([
     'common',
     'visualize',
@@ -24,8 +25,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
   const queryBar = getService('queryBar');
   const savedQueryManagementComponent = getService('savedQueryManagementComponent');
 
-  // FLAKY: https://github.com/elastic/kibana/issues/50018
-  describe.skip('feature controls security', () => {
+  describe('feature controls security', () => {
     before(async () => {
       await esArchiver.load('visualize/default');
       await esArchiver.loadIfNeeded('logstash_functional');
@@ -81,7 +81,9 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it(`landing page shows "Create new Visualization" button`, async () => {
         await PageObjects.visualize.gotoVisualizationLandingPage();
-        await testSubjects.existOrFail('visualizeLandingPage', { timeout: 10000 });
+        await testSubjects.existOrFail('visualizeLandingPage', {
+          timeout: config.get('timeouts.waitFor'),
+        });
         await testSubjects.existOrFail('newItemButton');
       });
 
@@ -94,7 +96,9 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
           ensureCurrentUrl: false,
           shouldLoginIfPrompted: false,
         });
-        await testSubjects.existOrFail('visualizationLoader', { timeout: 10000 });
+        await testSubjects.existOrFail('visualizationLoader', {
+          timeout: config.get('timeouts.waitFor'),
+        });
       });
 
       it('can save existing Visualization', async () => {
@@ -102,7 +106,9 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
           ensureCurrentUrl: false,
           shouldLoginIfPrompted: false,
         });
-        await testSubjects.existOrFail('visualizeSaveButton', { timeout: 10000 });
+        await testSubjects.existOrFail('visualizeSaveButton', {
+          timeout: config.get('timeouts.waitFor'),
+        });
       });
 
       it('Embed code shows create short-url button', async () => {
@@ -199,7 +205,9 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it(`landing page shows "Create new Visualization" button`, async () => {
         await PageObjects.visualize.gotoVisualizationLandingPage();
-        await testSubjects.existOrFail('visualizeLandingPage', { timeout: 10000 });
+        await testSubjects.existOrFail('visualizeLandingPage', {
+          timeout: config.get('timeouts.waitFor'),
+        });
         await testSubjects.existOrFail('newItemButton');
       });
 
@@ -212,7 +220,9 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
           ensureCurrentUrl: false,
           shouldLoginIfPrompted: false,
         });
-        await testSubjects.existOrFail('visualizationLoader', { timeout: 10000 });
+        await testSubjects.existOrFail('visualizationLoader', {
+          timeout: config.get('timeouts.waitFor'),
+        });
       });
 
       it(`can't save existing Visualization`, async () => {
@@ -220,8 +230,12 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
           ensureCurrentUrl: false,
           shouldLoginIfPrompted: false,
         });
-        await testSubjects.existOrFail('shareTopNavButton', { timeout: 10000 });
-        await testSubjects.missingOrFail('visualizeSaveButton', { timeout: 10000 });
+        await testSubjects.existOrFail('shareTopNavButton', {
+          timeout: config.get('timeouts.waitFor'),
+        });
+        await testSubjects.missingOrFail('visualizeSaveButton', {
+          timeout: config.get('timeouts.waitFor'),
+        });
       });
 
       it(`Embed Code doesn't show create short-url button`, async () => {
@@ -305,7 +319,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
           ensureCurrentUrl: false,
           shouldLoginIfPrompted: false,
         });
-        await testSubjects.existOrFail('homeApp', { timeout: 10000 });
+        await testSubjects.existOrFail('homeApp', { timeout: config.get('timeouts.waitFor') });
       });
 
       it(`edit page redirects to home page`, async () => {
@@ -313,7 +327,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
           ensureCurrentUrl: false,
           shouldLoginIfPrompted: false,
         });
-        await testSubjects.existOrFail('homeApp', { timeout: 10000 });
+        await testSubjects.existOrFail('homeApp', { timeout: config.get('timeouts.waitFor') });
       });
     });
   });
diff --git a/x-pack/test/functional/apps/visualize/feature_controls/visualize_spaces.ts b/x-pack/test/functional/apps/visualize/feature_controls/visualize_spaces.ts
index b24c537d25085..b1cb156caad90 100644
--- a/x-pack/test/functional/apps/visualize/feature_controls/visualize_spaces.ts
+++ b/x-pack/test/functional/apps/visualize/feature_controls/visualize_spaces.ts
@@ -9,6 +9,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
 
 export default function({ getPageObjects, getService }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
+  const config = getService('config');
   const spacesService = getService('spaces');
   const PageObjects = getPageObjects([
     'common',
@@ -20,8 +21,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
   const testSubjects = getService('testSubjects');
   const appsMenu = getService('appsMenu');
 
-  // FLAKY: https://github.com/elastic/kibana/issues/45244
-  describe.skip('visualize', () => {
+  describe('visualize', () => {
     before(async () => {
       await esArchiver.loadIfNeeded('logstash_functional');
     });
@@ -62,7 +62,9 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
             shouldLoginIfPrompted: false,
           }
         );
-        await testSubjects.existOrFail('visualizationLoader', { timeout: 10000 });
+        await testSubjects.existOrFail('visualizationLoader', {
+          timeout: config.get('timeouts.waitFor'),
+        });
       });
     });
 
@@ -97,7 +99,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
           ensureCurrentUrl: false,
           shouldLoginIfPrompted: false,
         });
-        await testSubjects.existOrFail('homeApp', { timeout: 10000 });
+        await testSubjects.existOrFail('homeApp', { timeout: config.get('timeouts.waitFor') });
       });
 
       it(`edit visualization for object which doesn't exist redirects to the home page`, async () => {
@@ -110,7 +112,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
             shouldLoginIfPrompted: false,
           }
         );
-        await testSubjects.existOrFail('homeApp', { timeout: 10000 });
+        await testSubjects.existOrFail('homeApp', { timeout: config.get('timeouts.waitFor') });
       });
 
       it(`edit visualization for object which exists redirects to the home page`, async () => {
@@ -123,7 +125,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
             shouldLoginIfPrompted: false,
           }
         );
-        await testSubjects.existOrFail('homeApp', { timeout: 10000 });
+        await testSubjects.existOrFail('homeApp', { timeout: config.get('timeouts.waitFor') });
       });
     });
   });
diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts
index 6d83e0bbf1df7..95371b5b501f5 100644
--- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts
+++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts
@@ -204,7 +204,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
         });
       });
 
-      it.skip('renders the active alert instances', async () => {
+      it('renders the active alert instances', async () => {
         const testBeganAt = moment().utc();
 
         // Verify content

From 5a99642cfbebc0611b15666244aa5c91ec2935f9 Mon Sep 17 00:00:00 2001
From: John Schulz <john.schulz@elastic.co>
Date: Tue, 18 Feb 2020 21:30:38 -0500
Subject: [PATCH 051/174] Add Ingest Manager plugin to master (#56567)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* [Fleet] Add permissions checks (#48143)

* Add permissions check to API

* Add permissions check to UI

* Undo changes

* Update index.tsx

* Update enroll.ts

* add API tests for permissions

* add fleet to trial license check

* Agent enrollment flyout (#48173)

* Fix agent table empty prompt

* First pass at agent enrollment flyout with placeholders

* Move enrollment commands into separate file and enable templated vars

* Use default double braces for templating

* [Fleet] unenroll agent from the details page (#48286)

* [Fleet] add API to unenroll agents
* [Fleet] return a 403 for inactive agent
* [Fleet] UI to unenroll an agent from the detail page

* [Fleet] Filter inactive agent from the listing (#48466)

* [Fleet] Expose policy change method from fleet plugins (#48562)

* [IM] add screenshot detail view and image endpoint (#48149)

* add api endpoint to handle images

* #47978 add screenshots component with single image

* update padding around screenshot

* import existing API_ROOT

* move ImageRequestParams interface and fix type

* pass the content-type through and add test

* fix ie11 issues with nested flex items, change radius to use eui variable

* use eui variables for padding

* add aria label and image description

* [IM] Use EPM in variables & types (#48453)

* IntegrationsManager -> EPM

* integrationsManager -> epm

* [iI]ntegration -> [pP]ackage. Update tests.

* Don't rename integrations registry URL.

* Update i18n key in x-pack/.i18nrc.json

* Update path to functional test config

* Add epm to recently re-enabled privileges test

* Update two values recently added in main feature branch

* Move/update screenshot tests

* [Fleet] Allow to edit metadata (#48682)

* [Fleet] Add a toggle to show Inactive users (#48917)

* Multiselect to unenroll agents (#48852)

* Initial pass at multiselect to unenroll agents

* Adjust loading state button text

* Fix types; use unenroll provider in agent details

* Update select all agents kuery

* Prevent inactive agents from being selected

* [Fleet] Fix privilege tests after merge of master (#49118)

* [Fleet] Temporary use the elastic user as kibana user to be able to create API keys (#49037)

* [EPM] Fix package version requirement structure (#49172)

* [EPM] Fix package version requirement structure

In https://github.com/elastic/integrations-registry/pull/134 the API structure changes. This PR should adjust to the new structure.

* Update x-pack/legacy/plugins/integrations_manager/common/types.ts

Co-Authored-By: John Schulz <github.com@jfsiii.org>

* Update x-pack/legacy/plugins/integrations_manager/common/types.ts

Co-Authored-By: John Schulz <github.com@jfsiii.org>

* Update x-pack/legacy/plugins/integrations_manager/common/types.ts

Co-Authored-By: John Schulz <github.com@jfsiii.org>

* Update x-pack/legacy/plugins/integrations_manager/common/types.ts

Co-Authored-By: John Schulz <github.com@jfsiii.org>

* [EPM] 48799 markdown into readme (#49180)

* add markdown component and fetch markdown

* add package.json file with react-markdown dependency

* add markdown renderers, use readme path

* remove description and rename to readme

* remove dropshadow on images

* add loading component for readme request

* comment on loading components

* Add command to spin up a mock server for ingest endpoints (#49259)

* 48654 files from registry (#49580)

* Replace image path/handler with generic file one

* Incorporate tests from #48696

* [EPM] Make screenshot image captions optional (#48912)

* Make screenshot image captions optional

* fix typos, type, rename variable

* [EPM] update to layout and and spacing (#49413)

* fix height and color issues

* make container full height

* remove panels per design update and make white bg color

* adjust spacing

* check for empty response

* fix eslint issue

* fix layout in safari

* remove unused component

* [EPM] Rename registry url to new path (#49598)

The integrations-registry repository was renamed to package-registry and with it also all the assets inside. The url under which the service is served will also be changed (not done yet). As soon as this is done, this PR should be merged.

* [EPM] rewrite relative image paths (#49637)

* rewrite relative image paths

* remove EuiImage, use transformImageUri, clean up

* [EPM] Use NP registerFeature (#49625)

* Use NP registerFeature

* Added features plugin to deps. Used it in setup.

Moved `registerFeature` from `init` to plugin setup.

Moved rest of init into `createSetupShim`. `init` is now

```ts
init(server: Legacy.Server) {
    const { initializerContext, coreSetup, pluginsSetup } = createSetupShim(server);

    new Plugin(initializerContext).setup(coreSetup, pluginsSetup);
  },
```

Moved feature (argument for `registerFeature`) to separate file.

Renamed plugin-specific `CoreSetup` to `EPMCoreSetup`

* [Fleet] Filter agents list table by policy name (#49968)

* Add policies lib and adapters

* Fix mock server headers

* Initial pass at policy filter dropdown by manipulating kuery string

* Adjust spec return values

* Use separate state for policy filter; fix typings and disable some eslint rules

* Fix react-related eslint errors

/Users/jfsiii/work/kibana/x-pack/legacy/plugins/integrations_manager/public/hooks/use_links.tsx
  26:20  error  React Hook "useCore" is called in function "addBasePath" which is neither a React function component or a custom React Hook function  react-hooks/rules-of-hooks

/Users/jfsiii/work/kibana/x-pack/legacy/plugins/integrations_manager/public/screens/detail/readme.tsx
  26:35  error  React Hook "useLinks" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function  react-hooks/rules-of-hooks
  39:6   error  React Hook useEffect has a missing dependency: 'readmePath'. Either include it or remove the dependency array                                       react-hooks/exhaustive-deps

/Users/jfsiii/work/kibana/x-pack/legacy/plugins/integrations_manager/public/screens/home/search_results.tsx
  27:42  error  `"` can be escaped with `&quot;`, `&ldquo;`, `&#34;`, `&rdquo;`  react/no-unescaped-entities
  27:49  error  `"` can be escaped with `&quot;`, `&ldquo;`, `&#34;`, `&rdquo;`  react/no-unescaped-entities

✖ 5 problems (5 errors, 0 warnings)
  1 error and 0 warnings potentially fixable with the `--fix` option.

* Remove ui/* imports (#50094)

* [Fleet] Use ES api keys for agent authentication (#49639)

* add collapsible read me component (#49990)

* add collapsible read me component

* Update x-pack/legacy/plugins/integrations_manager/public/components/content_collapse.tsx

Co-Authored-By: John Schulz <github.com@jfsiii.org>

* add empty array as second argument to useLayoutEffect

* [EPM] Make API consistent for package installation and removal (#48745)

* Adjust type names

* Install package in one go.

* Integration -> Package

* Really install all known assets

* Remove ingest pipelines on package deletion.

* [EPM] Replace image paths/handlers with generic ones for file (#48688)

* Replace image path/handler with generic file one

* Incorporate tests from #48696

* Fix merge error.

* Type tuning

* Adjust types

* Remove asset types from routes altogether

* Add timelion-sheet back to types.

* Respond with full package info to install/delete

* Be specific in return type.

* Keep installAssets() as separate step

* [EPM] Add unit tests for pathParts (#50279)

* [EPM] Add unit tests for pathParts

This adds unit tests for the pathParts function. I initially wanted to enhance the function to also support paths like `iptables-1.0.4/dataset/log/elasticsearch/ingest-pipeline/pipeline.json`. But without unit tests it was hard for me to make the changes.

This adds a unit test driven by a table so we can extend it later. Adding these tests also helps me to better understand the code.

As a note for myself, the command to run these tests is:

```
node scripts/jest --watch ./legacy/plugins/integrations_manager/server/registry/index.test.ts
```

* Update docs for merging master into feature branch (#50396)

* [Fleet] Remove in memory repository (#50431)

* [EPM] Documentation of HTTP routes & TS types for Ingest (#48798)

* Add beginning models and two routes for Ingest

* Update types & models per discussion w/Ruflin
Also reviewed data structures listed at https://docs.google.com/document/d/1IBR3f9dpHqJmXYEdg06WV34KSMd3g5k4aMGa4jde_Eg/edit#

* Update: /policies always returns array. /policy returns single policy

* Add pagination for /policy & /datasources. Uses per_page & page params

* Add API metadata. Standardize policy_id param name.

* Update descriptions to match Google Doc. Move use case to Policy.

Disabled the '@typescript-eslint/array-type' rule because it was going around in circles. It didn't like Datasource[] or Array<Datasource>

* Return to initial TS annotation for Arrays

Remove the line disabling @typescript-eslint/array-type now that it's behaving normally again :shrug:

* [EPM] Add directory structure for server/lib. (#50469)

* Add directory structure for server/lib.

* 'tests' seems to be more common than 'test'

* Make CI happy

* [EPM] Add basic documentation directory (#50478)

* [EPM] Add basic documentation directory

Having the doc directory around allows us to easily add docs from here on to document how EPM works.

To run the docs build, use the following command from the kibana directory:

```
../docs/build_docs --doc docs/epm/index.asciidoc --open
```

The above assumes that docs (https://github.com/elastic/docs) are checked out in the same directory as Kibana.

With this change, the EPM docs build is not included yet in the overall docs build. For this adjustments to https://github.com/elastic/docs/blob/master/conf.yaml must be made.

* [EPM] Add basic index template (#50471)

This PR adds the very basic index template we will use for the packages. It contains all the basic settings and some examples. The examples will be remove as soon as we have an actual implementation with packages but for now is convenient to see if it is a valid package.

This code is put into the lib directory as it does not tie directly into any handlers.

It also adds an functional tests for loading a template. This means we have a way to check if a template is valid in Elasticsearch. Based on this we can check in the future all our generated templates for validity with Elasticsearch.

To run the functional test, go to the Kibana x-pack directory. Start the first command:

```
node scripts/functional_tests_server.js --config test/epm_api_integration/config.ts
```

Keep the above running and switch to an other Terminal. Now run:

```
node scripts/functional_test_runner.js --config x-pack/test/epm_api_integration/config.ts
```

* 40752 rewrite ingest pipeline (#50627)

* Add directory structure for server/lib.

* 'tests' seems to be more common than 'test'

* Make CI happy

* Implement pipeline rewriting.

* Add more testcases

* For posterity (comment change)

* Allow beats-style template delimiters

* Be more succinct

* Document better

* Replace AssetType enum with union type (#50696)

See https://github.com/elastic/kibana/pull/50609#discussion_r346080439

Discussed in Slack and agree to revert for now. Can track down the issues & restore later

* Remove unnecessary await if we can return the promise (#50329)

* Fix whitespace per figma comments. Closes #47348 (#47350)

* Add fix & comment for TS 3.7.2 regression

* [EPM] cleanup assets, filter assets for those currently supported (#50609)

* cleanup assets, filter assets for those currently supported

* removed unused type

* fix type

* add comment for better type

* change type name to be more descriptive

* hardcode image width for ie11 (#49796)

* hardcode image width for ie11

* eslint

* improve comment

* add maxWidth

* [EPM] useKibana hook & render in plugin.start (#50110)

* plugin.start now does reactdom.render vs returning react element

export plugin function from public/index

* Move setClient call from plugin.start to plugin.setup

* Use `useUiSetting$` from `useKibana` hooks

* Fix broken app due to bad hooks usage

Can't use useKibana outside a React component.

Reverting to prior approach since it's still NP. Can revisit context usage in a followup PR

* [EPM] Install package from detail view on button click (#50735)

* Support basic "click button -> show spinner -> installed" install flow

* Remove incorrect comments. Add TS return types to data functions.

* [EPM] Use NP feature_catalogue.register (#50108)

* Use NP feature_catalogue.register

* Use type from NP plugin

* fix linting

* fix types

* fix headers in Fleet

* skipping test due to ES param change

* Revert "skipping test due to ES param change"

This reverts commit d05f20decfbfc4d91069816a6f8dfde26b5bd6bc.

* remove type field

* remove unused import

* [EPM] Final(?) update from integrations_manager -> EPM (#50976)

* Update (all remaining?) references from integrations_manager to EPM

* Update path in i18n file

* [EPM] update compatibility section (#50975)

* change min max version to single range value

* add elastic stack icon and change text

* remove badge from version and use code font

* remove euistyled

* add back version component lost in merge

* remove euiStyled

* remove old file

* Restore RequirementVersionRange type

* Disable test for elasticsearch username.

Temporary work around until we know how the stack wants to add the permissions we need. Either adding to the kibana user or creating a new user.

* Revert "Disable test for elasticsearch username."

This reverts commit f1020e4eab2ada5d854eacc44cdb6d5bd23c267f.

* Disable test for elasticsearch username.

    Temporary work around until we know how the stack wants to add the permissions we need. Either adding to the kibana user or creating a new user.

* Fix EPM typing issues in register feature

* Fix typings after master merge

* [EPM] CI fixes (#51284)

* Initialize es in test.

* Add it(), no es.init()

* Clean up.

* [EPM] add confirmation modal (#51172)

* add confirmation modal, move install state to Header

* update callout to use title

* move components only used in detail view to detail dir

* use better variable names

* update to more descriptive  variable names

* Restore prior response vaulues for install & delete package (#51252)

Discussed this with @skh https://github.com/elastic/kibana/pull/51112#commitcomment-35961413 & https://github.com/elastic/kibana/pull/51112#commitcomment-35970664 as well as in a video call

Also added some TS type annotations for data fetching functions to make the contracts more explicit

* [EPM] /package API only lists installable assets. Restore enums. (#51414)

* API only shows installable assets. Server types to own file. Restore enums

* Fix type imports

* Only return installable asset types (kibana for now)

* server/types now only has code from hapi which shouldn't go to client

* Add more restricted TS types to DisplayAssets object

* Flip order of arguments to Extract

In these cases it still works the same, but looking at https://www.typescriptlang.org/docs/handbook/advanced-types.html the signature is

`Extract<T, U>` - Extract from `T` those types that are assignable to `U`

so the larger set should be first

* [Fleet] Enrollment api key UI (#51495)

* Make button pretty in dark mode as well. (#51610)

* [EPM] Add docs entry about registryUrl config (#51697)

This documentation is at the moment mainly for internal use. I found myself searching for this URL several times in the code or PRs so I thought I rather add it to the docs for now.

* [EPM] Remove encoding of Kibana objects as not needed anymore

* [Fleet] Move agent status server side and API to get aggregated status for a policy (#51673)

* [EPM] Add basic docs around install/delete API endpoint (#51728)

This is mainly for internal usage at the moment to look up.

* Ingest/policy (#51741)

* wip policy

* tweaks

* tweaks

* FIX TYPOS

* WIP move policy => agent config conversion to fleet, WIP policy changed method

* fix tests and bugs

* updates tests and snaps

* more fixes

* use AGENT_POLLING_INTERVAL

* cleanup and fix some formatting

* Update x-pack/legacy/plugins/ingest/server/libs/datasources.ts

Co-Authored-By: John Schulz <github.com@jfsiii.org>

* Update x-pack/legacy/plugins/ingest/server/libs/datasources.ts

Co-Authored-By: John Schulz <github.com@jfsiii.org>

* Update x-pack/legacy/plugins/ingest/server/libs/outputs.ts

Co-Authored-By: John Schulz <github.com@jfsiii.org>

* Update x-pack/legacy/plugins/fleet/server/libs/policy.ts

Co-Authored-By: John Schulz <github.com@jfsiii.org>

* fix things broken by PR review suggestions

* remove unused field

* fix types

* fix mappings

* add datasource mappings

* Fix mappings and remove get full policy from checkin

* Fix ingest api integration tests

* run es-lint to fix fleet

* Fix typescript issues

* [EPM] Track package install state and add toast notification (#51734)

* add notifications from core to plugin

* add package install state hook

* fix type error

* use toMountPoint helper to add jsx to notification

* add warning notification to failed install

* make notifications dependency explicit prop

* move PackageInstall provider lower

* add comment about InstallStatus type overlapping InstallationStatus

* use InstallStatus type in InstallationButton component

* fix type

* [Ingest] Adds support for a working default output (#51841)

* aadding config

* add working settings to the default output

* remove default username and password

* update libs

* [EPM] Add basics for creating the ILM setup (#50474)

This contains the basic objects to setup ILM

* Create index and alias with a write index
* Get the policy

The code does not contain any functional tests yet as it is still open on how to do it best. I suggest to get this in as a foundation and then iterate on top of it.

* [EPM] Add datasource (ingest pipeline) from package (#51851)

## Summary

This mixes a few concerns but I think it's worth it to show the parts working together.

Take a look at the individual commits for a better separation of features.

This adds
 - the `/datasource/install/{pkgkey}` endpoint which installs ingest pipelines from a package into ES and saves a reference to them in the EPM state Saved Object
 - Connects the "Add datasource" button in the successful installation Toast to the new API
 - Adds a toast notification to inform the user the datasource was added correctly
 - Adds a "Delete Package" button on the details page so we can uninstall a package while we're waiting for the separate view which allows deletes
 - b99eda6 Pushes logic that was in the detail view into `InstallationButton`. This consolidates the logic in one component (or one component & the existing hook) and, iiic, means we can put `<InstallationButton package={...} />` on any view and get the same behavior

I'm marking this as a normal PR so people can merge if they wish

![add-datasource-delete-package-small](https://user-images.githubusercontent.com/57655/69775686-7fb39280-1167-11ea-8d41-e2b8a02252a1.gif)

* [EPM] Add basic processing of fields.yml file (#51148)

The fields.yml is used to generate the Elasticsearch template and Kibana index pattern. This PR adds a very basic implementation of processing the fields.yml and then create an Elasticsearch template out of it. The only fields that are supported at the moment are keyword fields, more will be added as a follow up.

The testing was implemented with a golden file. The output from the method is compared to a json file. If the input is changed or the method is changed, it is possible to regenerate the files with the `-generate` flag as following:

```
node scripts/jest  ./legacy/plugins/epm/server/lib/template/template.test.ts -generate
```

This will allow us to quickly test many inputs / outputs in the future, make adjustments to the existing files and generate the new outputs. We then can compare it in the diff it the changes make sense.

* [EPM] Create basic implementation to merge input template and dataset manifest (#51803)

* [EPM] Create basic implementation to merge input template and dataset manifest

With this code it is possible to take an input template for the agent and merge it with the config variables from the dataset manifest file. Currently only the name and the default value are merged. Later on we must implement to be able to pass user configured variables to it and make decision based on OS selection.

Closing https://github.com/elastic/kibana/issues/51794

* [EPM] Refactoring of lib structure (#51885)

This refactors the structure of lib. As so far all the lib parts are related to assets in the package, it is organised the same way with the same structure. Each directory has its own tests directory if it needs one. This makes it possible to (almost) not need relative paths for tests.

* [EPM] Allow to read files from fields directory (#51958)

This change allows to also extract files from the `fields` directory. Previously this was not possible because it always assumed a service must be there.

* [EPM] Install Elasticsearch Index Template for data source (#51878)

This installs the Elasticsearch index template for each dataset in a package. For now the names are hardcoded based on package key and dataset name but will be more dynamic later on when we pass the full dataset information.

The dataset extractions is a bit "hacky" at the moment and we should get a full implementation of dataset at a later stage and replace this code.

* [Fleet] Policy list, details, create, edit UIs (#51950)

* Set up simple policies list view

* Adjust spec to return single policy

* Set up simple policy details page

* Add demo stats/chart to policy details

* Add description string

* Initial setup of policy form and create policy UI

* Policy create/edit form; integrate policy list api

* Integrate create policy api

* Integrate policy detail, agent status, and policy edit APIs; adjust policy list api integration in agent enrollment

* Fix edit policy mock meta

* Fix policy list search bar

* PR and linting fixes; use typings from ingest plugin

* Fix i18n

* [EPM] Add datasource saved object type (#51871)

## Summary

This PR makes a few assumptions, and contains a lot of refactoring. It might be beneficial to look at the resulting directory structure under `server` first to get the (new) big picture.

Assumptions:
- our API deals with several concerns, for now these are packages and datasources
- we manage our own HTTP API endpoints for these concerns (in particular, don't use the ingest plugin for that)
- we manage (for now) the Kibana saved object in which datasources are saved. importing and calling methods from the ingest plugin to do that down the road will (hopefully) be a manageable change

This led to the following decisions:
- the code is separated into subdirectories by concern, containing all the route handlers and tightly coupled code
- for now, these directories are in `server/packages` and `server/datasources`. I'm tempted to move them into `server/api/{packages,datasources}` but wanted to limit the amount of refactoring in one PR
- shared code lives in `server/lib`
- some code from `server/packages` has been almost duplicated to handle saving to Datasource saved objects, some has been refactored and is used from both places. The deduplication needs further improvement
- maybe `server/registry` should also move under `server/lib` (but see above, I'm trying to not move everything around all at once)

Testing:

* Please note that this is a breaking change because the saved object type for package information
has also been renamed. You'll need to start with a fresh `.kibana-*` index. Restarting `yarn es snapshot` (withouth specifying a data directory) should do the trick.

* Package installation should still work, e.g. with a GET request to `http://localhost:5601/api/epm/package/coredns-1.0.1`. The saved objects for packages can be inspected with a GET request to `http://localhost:5601/api/saved_objects/epm-package/$PKG_KEY`, e.g. `http://localhost:5601/api/saved_objects/epm-package/coredns-1.0.1`
* Datasource creation should still work, e.g. with a GET request to `http://localhost:5601/api/epm/datasource/install/coredns-1.0.1`. The saved objects for datasources can be inspected with a GET to `http://localhost:5601/api/saved_objects/epm-datasource/$PKG_KEY`, e.g. `http://localhost:5601/api/saved_objects/epm-datasource/coredns-1.0.1`

* [Fleet] Expose policy during agent checkin (#51968)

* [EPM] Add /epr prefix to the tar.gz download path (#51881)

The registry slightly changed the .tar.gz path because of download stats reason. This adjusts for it. See https://github.com/elastic/package-registry/pull/169

* [EPM] Move template installation to lib and add asset helper  (#52049)

* [EPM] Move template installation to lib and add asset helper

All the logic related to the installation of the templates for a package should be inside the template library folder. This moves the logic into this folder.

A few refactorings were made to simplify installation:

* Introduction of DataSet interface: This interface is needed to extract the data sets inside a package and install one template per data set.
* Pass package instead of package key to installation process: Passing the package instead of the package key means fetching of package information is decoupled from the installation process and abstracted. This separates the two concerns and should simplify testing.
* getAsssets method: The getAssets methods works on top of the package object to extract asset paths. It is inspired by get_objects methods but supports passing a package and a dataset.

Currently one problem with testing that exists is that to fetch the content of an asset is not decoupled yet.

* [EPM] Reduce data source to one type (#52061)

Between Fleet / Ingest / EPM there had been several interface definitions of Datasource and the related types. This reduces it to one place for the definition. The same applies to the policy definition.

The goal of this is that from now on we all rely on the same definition. If we make changes, we make them in all parts of the code.

In this PR is only the minimal change needed to get us all on one interface. Further changes will be needed that we all rely on the same saved objects etc.

* add export command

* revert 2 more files to rely on export

* revert imports

* Fix types for Datasource Saved Object

* merge in master

* fix type check

* Run VSCode's organize imports on EPM files (#52234)

Learned about it on Slack from https://twitter.com/ryanchenkie/status/1201883268527927301

Blog at https://code.visualstudio.com/updates/v1_23#_run-code-actions-on-save

Basically does the order we've been loosely following (3rd party, then relative) & alphabetic by location and variable name.

It's not customizable but it's reasonable and, afaict, consistent.

* [EPM] More realistic datasource SO. Error if package not installed. (#52229)

* Move cache 'hack' into getAssetsData

* p -> pkg. package is reserved. pkgkey is used in many places

* Remove unnecessary type cast

* Clarify reasons behind asset path manipulation

* Return the Datasource; not the Saved Object.

* Use real values from package in fake datasource SO

* Error if /datasource/install before /package/install

```
> curl --user elastic:changeme localhost:5601/api/epm/datasource/install/coredns-1.0.1
{
  "statusCode": 403,
  "error": "Forbidden",
  "message": "coredns-1.0.1 is not installed"
}

> curl --user elastic:changeme localhost:5601/api/epm/install/coredns-1.0.1
[
  {
    "id": "53aa1f70-443e-11e9-8548-ab7fbe04f038",
    "type": "dashboard"
  },
  {
    "id": "Metricbeat-CoreDNS-Dashboard-ecs",
    "type": "dashboard"
  },
  {
    "id": "75743f70-443c-11e9-8548-ab7fbe04f038",
    "type": "visualization"
  },
  {
    "id": "36e08510-53c4-11e9-b466-9be470bbd327-ecs",
    "type": "visualization"
  },
  {
    "id": "277fc650-67a9-11e9-a534-715561d0bf42",
    "type": "visualization"
  },
  {
    "id": "cfde7fb0-443d-11e9-8548-ab7fbe04f038",
    "type": "visualization"
  },
  {
    "id": "a19df590-53c4-11e9-b466-9be470bbd327-ecs",
    "type": "visualization"
  },
  {
    "id": "a58345f0-7298-11e9-b0d0-414c3011ddbb",
    "type": "visualization"
  },
  {
    "id": "9dc640e0-4432-11e9-8548-ab7fbe04f038",
    "type": "visualization"
  },
  {
    "id": "3ad75810-4429-11e9-8548-ab7fbe04f038",
    "type": "visualization"
  },
  {
    "id": "57c74300-7308-11e9-b0d0-414c3011ddbb",
    "type": "visualization"
  },
  {
    "id": "27da53f0-53d5-11e9-b466-9be470bbd327-ecs",
    "type": "visualization"
  },
  {
    "id": "86177430-728d-11e9-b0d0-414c3011ddbb",
    "type": "visualization"
  },
  {
    "id": "4804eaa0-7315-11e9-b0d0-414c3011ddbb",
    "type": "visualization"
  }
]

> curl --user elastic:changeme localhost:5601/api/epm/datasource/install/coredns-1.0.1
[
  {
    "id": "coredns_1_0_1_dataset_log_elasticsearch_ingest_pipeline_pipeline_plaintext_json",
    "type": "ingest-pipeline"
  },
  {
    "id": "coredns_1_0_1_dataset_log_elasticsearch_ingest_pipeline_pipeline_json_json",
    "type": "ingest-pipeline"
  },
  {
    "id": "coredns_1_0_1_dataset_log_elasticsearch_ingest_pipeline_pipeline_entry_json",
    "type": "ingest-pipeline"
  }
]
```

* fix duplicated imports

* [EPM] Move golden files generation over to jest snapshot (#52203)

* [EPM] Move golden files generation over to jest snapshot

I initially used my own implementation to write the generated files. It runs out jest has a feature to write snapshots which simplifies the code a lot.

I added a loop with an additional test file so in the future we can just keep adding test files without having to modify the test code.

To updated the snapshots, the param `-u` has to be used:

```
node scripts/jest legacy/plugins/epm/server/lib/fields/field.test.ts -u
```

* [EPM] Create metrics-* and logs-* Kibana index pattern (#52277)

This creates the very basic Kibana index patterns metrics-* and logs-* for Kibana. At the moment it is overwritten every time. We need to change this in the future to take the fields from all installed data sources and regenerate it.

* [EPM] Create helper for elasticsearch asset names (#52265)

Most of the Elasticsearch assets have the same base name. This creates a helper to get the base name for the assets. In case we decide to change the base name in the future, we can change it in one place.

* fix tests and destructing

* [EPM] Update Registry types. Prevent errors installing certain datasources. (#52285)

* Update RegistryPackage type

* Use download key from EPR to fetch archive

* Fix errors caused by correcting the Registry types.

The issues were largely that some Registry types like `title, `datasets` and `assets` where marked as required, but are actually optional. This highlighted area in the code were we relied on them always being present.

We added to the issue by wrapping Registry types in `Required` which made those items which were correctly listed as optional, required for EPM code. Updated EPM types to reflect the largely pass-through nature of the EPM types.

There are two properties which we ensure are in every EPM response, those were put into their own (unexported) type.

Confirm by trying to add a datasource to a package which has no datasources, like apache-1.0.1 or system-2.0.1. In `feature-ingest` and the earlier version of this PR, the `/datasource/install` call returns a 500. In this PR it succeeds.

* [EPM] Add setup of default ILM policies (#52272)

This creates two ILM policies: logs-default and metrics-default. These are the default ILM policies used. Currently the policy content is hardcoded in the code but should be fetched from the base package in the future. The setup happens as part of the datasource installation. When a data source is installed it is a good time to check if the assets are there but we might extract this to a better place in the future.

* [EPM] 52075 add data source first page (#52320)

* Update RegistryPackage type

* add first page of add data source

* fix for ie11 flex min width bug

* remove toDetailViewRelative

* remove unneeded spread

* Update TS type names for EPR search results (#52512)


 * `RegistryList -> RegistrySearchResults`
 * `RegistryListItem -> RegistrySearchResult`

* Restore import sort order from #52234 (#52548)

Many of the changes from #52234 were lost. Presumably due to PR(s) merging which were based on branches which had the previous unsorted order.

* [EPM] Replace wildcard export (#52554)


 * PackageNotInstalledError -> packages/index.ts
 * pkgToPkgKey -> registry/index.ts (will convert existing `${name}-${version}` instances later)

* Replace export * from packages.

There's an argument that the import sites should be updated to import from `packages/get`, `packages/install`, etc but that can wait for a later PR.

* [EPM] Reduce usage of epm-package SavedObject (#52576)

* Delete existing Installation type. Rename InstallationAttributes to Installation

* Reduce usage of EPM SO. Add getInstallation().

Replaced two calls of getInstallationObject() with getInstallation().

Two less places with knowledge of SO internals.

Lots of potential improvements for EPM TS types remain (refactoring/removing Installable, etc), but this is a good incremental step, IMO

* [EPM] Fix missing export link (#52628)

Without it, things break. I am surprised CI did not catch this.

* [EPM] Cleanup ILM loading (#52632)

Before the check for the ILM policy to exist triggered an exception. With this change it is a normal response also if the policy does not exist yet.

A follow up issue will be created in Elasticsearch to get a HEAD request for this available.

* [EPM] Switch to staging URL for registry (#52626)

The old cluster with the registry will be removed as soon as this is merged.

* [EPM] Use Dataset interface to generate template (#52255)

This will make sure we have to pass much feature params and can fully rely on the datasource object to create names for assets.

* [Fleet] Use agent events to compute agent health (#52513)

* [EPM] Data source integration tests (#52542)

* Add fixtures for data source integration test.

* Move test setup to beforeEach

* Add test for datasource creation

* Handle pipelines in yml format.

* Make integration test for adding a data source pass.

* Use EPR staging URL with CDN. (#52776)

See https://github.com/elastic/kibana/pull/52626#pullrequestreview-330622868

* [EPM] Add Data Source page updates (#52705)

* remove dupe type RegistryPackage

* change switches to checkboxes, use datasets to create checkboxes, add some local form state

* update types

* [EPM] redirect after package install (#52771)

* add callback after successful installation and redirect

* add temp data sources tab content to access add data source page

* remove assets tab for mvp

* hide data sources link and redirect from data sources tab if package not installed

* change callback name

* remove commented out assets logic

* add redirect to hook

* fix type

* Use ingest datasource api (#52964)

Incremental change. Uses HTTP API for datasource creation. Will do follow-up PR which uses JS function instead

* Remove duplicate fetchInfo & installTemplates

I think this was from a bad merge, but pretty sure we don't want these functions called twice in the same function

* WIP. Pushing so others can see

* Improve correctness/flexibility of absolute URL

* Disable datasource test & template installation

* [Ingest] Data source APIs (#52448)

* Clean up ingest imports and remove unneeded mock_spec files

* Initial pass at datasources lib and API endpoints

* Add add/remove datasource to/from policy API endpoints

* Add datasource contract tests and related policy contract tests; update snapshots

* Fix tests

* Fix tests again

* Fix tests 3

* Adjust routes, PR feedback

* modify epm createDatasource endpoint to use user data (#52971)

* change epm/datasource/install/{pkg} to POST, send user data to endpoint, install pipelines and templates based on user selected datasets

* change test to post for installing a datasource

* change some names and types around

* delete request.headers['transfer-encoding'] being passed through from epm request

* [EPM] Don't share CreateFakeDatasource type (#53068)

It's not shared between client & server so it doesn't need to be in common. Also, it imports server code which would try to bring server types to the client. It's types so they're compiled away but it's important to keep common to what's truly common. Breaking this separation is why we thought enums broke the client. A lint rule just landed in master to prevent this.

* [EPM] Index template generation fixes (#53104)

* Only add keyword type field to mappings.

* Index template installation

* Handle empty fields definition files
* Re-enable index template installation

* [Fleet] Assign/Unassign data source from policy UI (#53058)

* Add index files to export various modules; normalize imports

* Clean up unused files; extract datasources table component from policy details page

* Expose http client to frontend libs; remove unused types; import ES UI's useRequest lib

* Adjust shape of rest api adapter interface to better match with rest of kibana; remove unused node adapter; change per_page param to perPage in agent events route

* Initial pass at assign data sources flyout

* Initial pass at unassigning data sources from policy

* Make data sources table searchable by package values

* Fix enrollment key lib for rest adapter param changes

* Fix imports and types

* `yarn.lock` changes after bootstrapping

* [EPM] Implement getConfig for dataset (#53261)

* [EPM] Implement getConfig for dataset

* Implements a getConfig method on a dataset object.
* Build the configuration for each dataset in a package.
* construct and save streams into datasource saved object

* [EPM] Fix template installation (#53272)

As dataset.package was not set, the installed templates contained undefined in the template name. This changes fixes this.

* [EM] Refactor ingest pipeline installation (#53309)

* Refactor ingest pipeline installation

* Only install index templates for requested datasets

* Add index.default_pipeline to index template

* Hook up pipeline rewriting

* Add correct types.

* change POST create datasources path (#53165)

* change POST create datasources path

* remove pkgkey from params

* Fix creation of a data source with a custom ID (#53537)

* [Ingest] Return associated policy IDs in data source info (#53350)

* Return number of policies from data source, surface in assign data source UI

* Update snapshots

* [EPM]: Assign data source to policy in UI (#53597)

* Let ES generate source ids. Refactor along the way.
* Datasource.id isn't optional. It's just missing before we send to Ingest
* Delete EPM's mapping of datasources saved object. Ingest handles that.
* Keep datasource object-related work in constructDatasource
* Move asset installation into own function. Keep entry point high-level.
* More descriptive (less ambiguous) names for these two functions
* Use enum values from Ingest instead of plain strings
* Limit the 'type' key of references to known asset types.
* Update variable names to clarify that we're merging arrays of references
* Use [].flat instead .reduce + .concat to avoid error on empty arrays.
* Pass PackageInfo value directly to component vs pulling off n properties
* Name handlers/options based on the data, not the UI element
* Populate policy combo box based on values from Ingest policy API
* Mark Dataset.vars as optional.
* Add TODOs

* Add commands to run API tests to README (#53847)

* Limit functions to 3 params max. Update those which used more (#53848)

* [EPM] Code in 'common' directories shouldn't import server code (#53854)

* [Fleet] Code in 'common' directories shouldn't import server code (#53938)

* [Fleet] Remove server code from common folder in fleet
* [Fleet] Fix typescript issues after master merge
* [EPM] Fix typescript issues after master merge

* Fix eslint issues

* Fix typescript issues after merge

* Fix merge master missing line

* Fix merge conflict

* [Fleet] Fix registration of Ingest management section (#54065)

* Fix registration of Ingest management section

* Fix i18n key

* [Fleet] Remove server code from common folder in ingest (#53969)

* [Fleet] Connect fleet to policy change update (#53201)

* [Fleet] Send created event when a policy is created
* [Fleet] updated created event when a policy is created
* [Fleet] Send deleted event when a policy is deleted

* [Fleet] Rename output.url => output.hosts (#54258)

* [Ingest] Remove policies UI (#54308)

* Remove meta field UI from policy add/edit form

* Initial pass at policy bulk+single delete UI and API

* Adjust policy links from agent list and detail pages so that links are only active if policy exists

* Add delete policy UI to policy detail page

* Disable policy delete button for default policy

* Commit updated kbn-pm artifact.

CI is failing with messages like
14:52:28  ERROR: 'yarn kbn run build -i @kbn/pm' caused changes to the following files:
14:52:28
14:52:28  packages/kbn-pm/dist/index.js

Following advice from https://elastic.slack.com/archives/C0D8P2XK5/p1570032166063400 and running/committing build

* Update kbn/pm package

Signed-off-by: Tyler Smalley <tyler.smalley@elastic.co>

* [EPM] create logs metrics index patterns (#54037)

* fixes bug in for loop returning too early and not looping through all yaml files creating incomplete index template, move loading yaml files to own function, other cleanup

* use reduce in place of for loop

* basic functionality for creating index patterns

* separate logs and metrics index patterns

* dedupe fields

* adjust flattenFields to rename nested fields with parent name path

* some tests

* use yml files for tests

* add awaits as part of installing the package

* optimize loading of yaml files

* fix typo

* change type packageName to package

* update tests to use all files from the beginning

* fix type errors

* fix test

* Use dataset.package from registry

https://github.com/elastic/kibana/pull/54037#pullrequestreview-340362812

* Form validation on add datasource page. (#53920)

* [Ingest] Add support for policy `label` field (#54413)

* Allow `label` field in policy APIs, update UIs to support `label` field

* PR review changes, typo fixes, update tests

* [Fleet] Fix api key creation (#54498)

* [Fleet] Move agent acks to his own endpoint (#54401)

* [Ingest] Fix MaxListenersExceededWarning during kibana boot (#54745)

* [Fleet] Create a default api key for the default output (#54658)

* [EPM] update index pattern fields (#54862)

* add DatasetType type

* move loadFieldsFromYaml to fields

* add logic to determine field values

* update tests

* add back accidentally removed readFromDocValues

* remove DatasetType and add IndexPatternType

* rename dedup to dedupe

* group tests

* use null coalescing operator

* Fix typing issues

* [Fleet] Support agent event subtype STOPPING (#55231)

* [EPM] Prevent double submit when creating data sources

Update installationRequested state after submit (#55100)

* [EPM] handle alias fields when creating kibana index pattern (#55254)

* add alias support, update flattenFields to handle alias copying, update and add tests

* update snapshot

* update findFieldByPath to return undefined if not leaf node

* remove temporary alias type from map

* [EPM] handle multi fields when creating index patterns (#55554)

* handle mult_fields

* move nested function out

* [Fleet] Fleet/spec docs (#55619)

* [EPM] Start to document definitions (#55361)

This is a first stab at creating a place where we define the terms we use across ingest management. This is not complete but defines a place where we can add all the future defintions.

* [EPM] Indexing strategy docs (#55301)

* [EPM] Indexing strategy docs

This documentation is to start documenting our new indexing strategy in a public place and have it versioned. This will allow us to share the current state of the indexing strategy more easily in a single place, track it when updated and also already have it ready for our future users to look it up and understand the benefits of it.

* update typos

* fix one more typo

* apply review feedback

* skip fields that are disabled (#55735)

* [Fleet] Fix fleet typing issues after merging master

* [EPM] Create fieldFormatMap in kibana index pattern (#55892)

* add support for fieldFormatMap

* output params must be camel case

* fix case

* add url_template param

* [Fleet] Use user from saved object to create apiKeys (#55458)

* [EPM] Document package upgrade behaviour (#56138)

This PR adds more detailed documentation on what should happen when a package is upgraded.

* Remove some explicit typing to pass type checks.

This abstraction will be removed in the single plugin going into master.

* remove readFromDocValues (#56227)

* Add symlink to yarn.lock to fix(?) CI

https://github.com/elastic/kibana/pull/56443/checks?check_run_id=418123781 failed saying EPM directory "MUST have a 'yarn.lock' symlink"

Seems to have originated with https://github.com/elastic/kibana/pull/55440

Following example from other legacy/plugins/* in that PR

* Like 441d9ed, but correct

* [Ingest] Convert `ingest` plugin to new platform `ingest_manager` plugin (#56262)

* Seed Ingest Manager as a new NP plugin

* Add contexts for core, deps, and config. Begin routing and nav UI

* Export NP ready request from top-level es_ui_shared/public/

* Add nav styling w/ theming, add useRequest hook

* Set up license and config server-side services; add test routes

* Move most types and constants into /common

* Initial pass at:
* data stream and agent config models
* data stream routes and schemas

* Initial pass at agent config api route handlers

* Change plugin id to camel case

* Fix circular schema dependency, add security as optional plugin

* Create appContext service, use request user info in agent config routes + libs

* Create default agent config

* Add default output host config, output typings, and create default output and its api key

* Move saved object mapping to new plugin

* Change data streams -> datasources

* Add legacy plugin to bootstrap mappings

* Adjust fleet's ingest dependencies

* Disable policies UI in Fleet

* Adjust EPM's ingest dependencies

* Adjust ingest manager base API route

* Adjust fleet's client side ingest dependencies

* Remove more ingest dependencies from fleet

* REMOVE MOST OF LEGACY INGEST PLUGIN

* Add section for agent configs in UI nav

* Allow useRequest and sendRequest consumers to specify typing for response

* Initial pass at porting over agent config list UI

* Port over agent config creation

* Port over delete agent config functionality

* Fix app routing

* Port over fleet setup routes

* Adjust fleet's ingest dependencies

* Make fleet happy path work, skip some tests (MESSY! :))

* Remove policy list UI code from fleet

* Change useRequestResponse error type

* Add missing agent config schemas and hooks

* Fix type check issues

* Register IM under management

* Fix type issues as a result of changes to use/sendRequest interfaces

* Make all ingest saved objects *not* space-aware

* Fix i18n path

* Fix app categories import

* Fix datasource package assets schema (array of asset objects)

* Seed Ingest Manager privileges to fix tests

* Change `features` to optional plugin instead of required

* Fix security privileges tests

* Fix feature test

* Fix duplicate enrollment key created for default agent config

* Fix fleet agent enrollment by catching agent config 404

* PR feedback

* First pass at deleting everything related to EPM & Fleet plugins

* Remove EPM-specific react-markdown

* Remove some unwanted differences from master.
Removed more EPM and Fleet changes

* React.FC -> React.FunctionComponent to match the pattern in master

* Selectively enable features based on config values

* Fix typing and(?) CI

* Enable xpack.ingestManger during tests

Follow example from xpack.endpoint flags

* Don't create BASE_URL value based on PLUGIN_ID

* Don't add optional routes. Use config for registry url

* Add README with some skeleton docs on tests, etc

* Undo changes to some files based on PR feedback

* Commit the result of 'yarn kbn run build -i @kbn/pm'

* Commit the result of 'yarn kbn run build -i @kbn/pm'

* WIP. But add some passing api integration tests

 └-: apis
   └-> "before all" hook
   └-: Ingest Manager plugin
     └-> "before all" hook
     └-: /agent_configs
       └-> "before all" hook
       └-> should get agent configs
         └-> "before each" hook: global before each
         └- ✓ pass  (42ms) "apis Ingest Manager plugin /agent_configs should get agent configs"
       └-> should create agent config
         └-> "before each" hook: global before each
         └- ✓ pass  (63ms) "apis Ingest Manager plugin /agent_configs should create agent config"
       └-> should have new agent config
         └-> "before each" hook: global before each
         └- ✓ pass  (41ms) "apis Ingest Manager plugin /agent_configs should have new agent config"
       └-> should delete that new config
         └-> "before each" hook: global before each
         └- ✓ pass  (1.0s) "apis Ingest Manager plugin /agent_configs should delete that new config"
       └-> should get datasources
         └-> "before each" hook: global before each
         └- ✓ pass  (26ms) "apis Ingest Manager plugin /agent_configs should get datasources"
       └-> should 404 from EPM package api
         └-> "before each" hook: global before each
         └- ✓ pass  (41ms) "apis Ingest Manager plugin /agent_configs should 404 from EPM package api"
       └-> "after all" hook
     └-> "after all" hook
   └-> "after all" hook

6 passing (1.2s)

* WIP

* Fix TS type errors.

Types are created tests. Should they come from test project?

* Add route to delete datasources (mirrors agent_configs api)

* Remove old ESArchiver files

* Add integration tests for each /agent_config & /datasources route

* Update README

Remove bash integration test scripts

* More updates to README

* Remove xpack.kueryAutocomplete from x-pack/.i18n.json

* Use default appRoute. We can't use /app/ingest (at least for now).

Looking at the app_router it seems like any route starting with `/app` must be `/app/:appId`.

I also don't think appRoute is fully supported. I'll follow up with Platform.

* Use appBasePath from app mount params for React Router basename

* Remove out-of-date comments

* Use the ES client from core

* Move License & Config services WIP. Pushing to share

* Move config & license services into App Context service.

* Remove FTR tests for Ingest Manager

* Remove license-related propperties

* Remove clusterClient properties

* Don't set this.security if not in dependencies

* WIP (failing) integration_test

* No more unknown key errors. Timeouts now

* Test routes with default (disabled) and manager-only flags

* Add tests to cover all permutations of flags (A, AB, AC, ABC)

* Use TS types vs config-schema in common & public

Move all @kbn/config-schema usage to server

Co-authored-by: Matt Apperson <me@mattapperson.com>
Co-authored-by: Jen Huang <its.jenetic@gmail.com>
Co-authored-by: Nicolas Chaulet <n.chaulet@gmail.com>
Co-authored-by: Sandra Gonzales <neptunian@users.noreply.github.com>
Co-authored-by: Nicolas Ruflin <spam@ruflin.com>
Co-authored-by: Sonja Krause-Harder <krauseha@gmail.com>
Co-authored-by: Brian Seeders <seeders@gmail.com>
Co-authored-by: Tyler Smalley <tylersmalley@me.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .github/CODEOWNERS                            |   3 +-
 src/dev/typescript/projects.ts                |   1 -
 src/plugins/es_ui_shared/public/index.ts      |   9 +
 .../public/request/np_ready_request.ts        |  20 +-
 x-pack/.i18nrc.json                           |   1 +
 x-pack/index.js                               |   2 +
 x-pack/legacy/plugins/ingest_manager/index.ts |  39 +++
 x-pack/plugins/ingest_manager/README.md       |  20 ++
 .../common/constants/agent_config.ts          |  18 ++
 .../common/constants/datasource.ts            |   7 +
 .../ingest_manager/common/constants/index.ts  |  11 +
 .../ingest_manager/common/constants/output.ts |  18 ++
 .../ingest_manager/common/constants/plugin.ts |   7 +
 .../ingest_manager/common/constants/routes.ts |  44 +++
 x-pack/plugins/ingest_manager/common/index.ts |   8 +
 .../ingest_manager/common/services/index.ts   |   6 +
 .../ingest_manager/common/services/routes.ts  |  77 +++++
 .../ingest_manager/common/types/index.ts      |  20 ++
 .../common/types/models/agent_config.ts       |  32 ++
 .../common/types/models/datasource.ts         |  43 +++
 .../common/types/models/index.ts              |   8 +
 .../common/types/models/output.ts             |  33 +++
 .../common/types/rest_spec/agent_config.ts    |  59 ++++
 .../common/types/rest_spec/common.ts          |  13 +
 .../common/types/rest_spec/datasource.ts      |  36 +++
 .../common/types/rest_spec/fleet_setup.ts     |  19 ++
 .../common/types/rest_spec/index.ts           |   9 +
 x-pack/plugins/ingest_manager/kibana.json     |   9 +
 .../ingest_manager/components/index.ts        |   6 +
 .../ingest_manager/components/loading.tsx     |  15 +
 .../ingest_manager/constants/index.ts         |  18 ++
 .../ingest_manager/hooks/index.ts             |  13 +
 .../ingest_manager/hooks/use_config.ts        |  18 ++
 .../ingest_manager/hooks/use_core.ts          |  18 ++
 .../ingest_manager/hooks/use_debounce.tsx     |  23 ++
 .../ingest_manager/hooks/use_deps.ts          |  18 ++
 .../ingest_manager/hooks/use_link.ts          |  13 +
 .../ingest_manager/hooks/use_pagination.tsx   |  25 ++
 .../hooks/use_request/agent_config.ts         |  61 ++++
 .../ingest_manager/hooks/use_request/index.ts |   7 +
 .../hooks/use_request/use_request.ts          |  35 +++
 .../applications/ingest_manager/index.tsx     | 108 +++++++
 .../ingest_manager/layouts/default.tsx        |  90 ++++++
 .../ingest_manager/layouts/index.tsx          |   6 +
 .../components/config_delete_provider.tsx     | 217 ++++++++++++++
 .../agent_config/components/config_form.tsx   |  96 ++++++
 .../sections/agent_config/components/index.ts |   8 +
 .../sections/agent_config/index.tsx           |  18 ++
 .../list_page/components/create_config.tsx    | 143 +++++++++
 .../list_page/components/index.ts             |   6 +
 .../sections/agent_config/list_page/index.tsx | 280 ++++++++++++++++++
 .../ingest_manager/sections/epm/index.tsx     |  13 +
 .../ingest_manager/sections/fleet/index.tsx   |  12 +
 .../ingest_manager/sections/index.tsx         |  11 +
 .../sections/overview/index.tsx               |  10 +
 .../ingest_manager/services/index.ts          |   7 +
 .../ingest_manager/types/index.ts             |  19 ++
 x-pack/plugins/ingest_manager/public/index.ts |  11 +
 .../plugins/ingest_manager/public/plugin.ts   |  57 ++++
 .../ingest_manager/server/constants/index.ts  |  22 ++
 x-pack/plugins/ingest_manager/server/index.ts |  43 +++
 .../server/integration_tests/router.test.ts   | 190 ++++++++++++
 .../plugins/ingest_manager/server/plugin.ts   | 102 +++++++
 .../server/routes/agent_config/handlers.ts    | 144 +++++++++
 .../server/routes/agent_config/index.ts       |  73 +++++
 .../server/routes/datasource/handlers.ts      | 131 ++++++++
 .../server/routes/datasource/index.ts         |  73 +++++
 .../ingest_manager/server/routes/epm/index.ts |  75 +++++
 .../server/routes/fleet_setup/handlers.ts     |  55 ++++
 .../server/routes/fleet_setup/index.ts        |  31 ++
 .../ingest_manager/server/routes/index.ts     |   9 +
 .../ingest_manager/server/saved_objects.ts    |  78 +++++
 .../server/services/agent_config.ts           | 258 ++++++++++++++++
 .../server/services/app_context.ts            |  50 ++++
 .../server/services/datasource.ts             | 132 +++++++++
 .../ingest_manager/server/services/index.ts   |  11 +
 .../ingest_manager/server/services/output.ts  | 113 +++++++
 .../ingest_manager/server/types/index.tsx     |  12 +
 .../server/types/models/agent_config.ts       |  38 +++
 .../server/types/models/datasource.ts         |  55 ++++
 .../server/types/models/index.ts              |   8 +
 .../server/types/models/output.ts             |  37 +++
 .../server/types/rest_spec/agent_config.ts    |  61 ++++
 .../server/types/rest_spec/common.ts          |  14 +
 .../server/types/rest_spec/datasource.ts      |  38 +++
 .../server/types/rest_spec/fleet_setup.ts     |  19 ++
 .../server/types/rest_spec/index.ts           |   9 +
 .../watch_visualization.tsx                   |   2 +-
 .../watch_list/components/watch_list.tsx      |   2 +-
 .../watch_status/components/watch_history.tsx |   4 +-
 90 files changed, 3827 insertions(+), 16 deletions(-)
 create mode 100644 x-pack/legacy/plugins/ingest_manager/index.ts
 create mode 100644 x-pack/plugins/ingest_manager/README.md
 create mode 100644 x-pack/plugins/ingest_manager/common/constants/agent_config.ts
 create mode 100644 x-pack/plugins/ingest_manager/common/constants/datasource.ts
 create mode 100644 x-pack/plugins/ingest_manager/common/constants/index.ts
 create mode 100644 x-pack/plugins/ingest_manager/common/constants/output.ts
 create mode 100644 x-pack/plugins/ingest_manager/common/constants/plugin.ts
 create mode 100644 x-pack/plugins/ingest_manager/common/constants/routes.ts
 create mode 100644 x-pack/plugins/ingest_manager/common/index.ts
 create mode 100644 x-pack/plugins/ingest_manager/common/services/index.ts
 create mode 100644 x-pack/plugins/ingest_manager/common/services/routes.ts
 create mode 100644 x-pack/plugins/ingest_manager/common/types/index.ts
 create mode 100644 x-pack/plugins/ingest_manager/common/types/models/agent_config.ts
 create mode 100644 x-pack/plugins/ingest_manager/common/types/models/datasource.ts
 create mode 100644 x-pack/plugins/ingest_manager/common/types/models/index.ts
 create mode 100644 x-pack/plugins/ingest_manager/common/types/models/output.ts
 create mode 100644 x-pack/plugins/ingest_manager/common/types/rest_spec/agent_config.ts
 create mode 100644 x-pack/plugins/ingest_manager/common/types/rest_spec/common.ts
 create mode 100644 x-pack/plugins/ingest_manager/common/types/rest_spec/datasource.ts
 create mode 100644 x-pack/plugins/ingest_manager/common/types/rest_spec/fleet_setup.ts
 create mode 100644 x-pack/plugins/ingest_manager/common/types/rest_spec/index.ts
 create mode 100644 x-pack/plugins/ingest_manager/kibana.json
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/index.ts
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/loading.tsx
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/constants/index.ts
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/index.ts
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_config.ts
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_core.ts
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_debounce.tsx
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_deps.ts
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_link.ts
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_pagination.tsx
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agent_config.ts
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/index.ts
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/use_request.ts
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/index.tsx
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_delete_provider.tsx
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_form.tsx
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/index.ts
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/index.tsx
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/components/create_config.tsx
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/components/index.ts
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/index.tsx
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/index.tsx
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/index.tsx
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/index.tsx
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/services/index.ts
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts
 create mode 100644 x-pack/plugins/ingest_manager/public/index.ts
 create mode 100644 x-pack/plugins/ingest_manager/public/plugin.ts
 create mode 100644 x-pack/plugins/ingest_manager/server/constants/index.ts
 create mode 100644 x-pack/plugins/ingest_manager/server/index.ts
 create mode 100644 x-pack/plugins/ingest_manager/server/integration_tests/router.test.ts
 create mode 100644 x-pack/plugins/ingest_manager/server/plugin.ts
 create mode 100644 x-pack/plugins/ingest_manager/server/routes/agent_config/handlers.ts
 create mode 100644 x-pack/plugins/ingest_manager/server/routes/agent_config/index.ts
 create mode 100644 x-pack/plugins/ingest_manager/server/routes/datasource/handlers.ts
 create mode 100644 x-pack/plugins/ingest_manager/server/routes/datasource/index.ts
 create mode 100644 x-pack/plugins/ingest_manager/server/routes/epm/index.ts
 create mode 100644 x-pack/plugins/ingest_manager/server/routes/fleet_setup/handlers.ts
 create mode 100644 x-pack/plugins/ingest_manager/server/routes/fleet_setup/index.ts
 create mode 100644 x-pack/plugins/ingest_manager/server/routes/index.ts
 create mode 100644 x-pack/plugins/ingest_manager/server/saved_objects.ts
 create mode 100644 x-pack/plugins/ingest_manager/server/services/agent_config.ts
 create mode 100644 x-pack/plugins/ingest_manager/server/services/app_context.ts
 create mode 100644 x-pack/plugins/ingest_manager/server/services/datasource.ts
 create mode 100644 x-pack/plugins/ingest_manager/server/services/index.ts
 create mode 100644 x-pack/plugins/ingest_manager/server/services/output.ts
 create mode 100644 x-pack/plugins/ingest_manager/server/types/index.tsx
 create mode 100644 x-pack/plugins/ingest_manager/server/types/models/agent_config.ts
 create mode 100644 x-pack/plugins/ingest_manager/server/types/models/datasource.ts
 create mode 100644 x-pack/plugins/ingest_manager/server/types/models/index.ts
 create mode 100644 x-pack/plugins/ingest_manager/server/types/models/output.ts
 create mode 100644 x-pack/plugins/ingest_manager/server/types/rest_spec/agent_config.ts
 create mode 100644 x-pack/plugins/ingest_manager/server/types/rest_spec/common.ts
 create mode 100644 x-pack/plugins/ingest_manager/server/types/rest_spec/datasource.ts
 create mode 100644 x-pack/plugins/ingest_manager/server/types/rest_spec/fleet_setup.ts
 create mode 100644 x-pack/plugins/ingest_manager/server/types/rest_spec/index.ts

diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 33fbddbd1faf8..56db8d3793f57 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -72,7 +72,8 @@
 # Observability UIs
 /x-pack/legacy/plugins/infra/ @elastic/logs-metrics-ui
 /x-pack/plugins/infra/ @elastic/logs-metrics-ui
-/x-pack/legacy/plugins/integrations_manager/ @elastic/epm
+/x-pack/plugins/ingest_manager/ @elastic/ingest
+/x-pack/legacy/plugins/ingest_manager/ @elastic/ingest
 /x-pack/plugins/observability/ @elastic/logs-metrics-ui @elastic/apm-ui @elastic/uptime @elastic/ingest
 
 # Machine Learning
diff --git a/src/dev/typescript/projects.ts b/src/dev/typescript/projects.ts
index b6353e44989ee..fb35e5ce526ed 100644
--- a/src/dev/typescript/projects.ts
+++ b/src/dev/typescript/projects.ts
@@ -19,7 +19,6 @@
 
 import glob from 'glob';
 import { resolve } from 'path';
-
 import { REPO_ROOT } from '../constants';
 import { Project } from './project';
 
diff --git a/src/plugins/es_ui_shared/public/index.ts b/src/plugins/es_ui_shared/public/index.ts
index 67e3617a85115..2925e5e16458e 100644
--- a/src/plugins/es_ui_shared/public/index.ts
+++ b/src/plugins/es_ui_shared/public/index.ts
@@ -18,3 +18,12 @@
  */
 
 export { JsonEditor, OnJsonEditorUpdateHandler } from './components/json_editor';
+
+export {
+  SendRequestConfig,
+  SendRequestResponse,
+  UseRequestConfig,
+  UseRequestResponse,
+  sendRequest,
+  useRequest,
+} from './request/np_ready_request';
diff --git a/src/plugins/es_ui_shared/public/request/np_ready_request.ts b/src/plugins/es_ui_shared/public/request/np_ready_request.ts
index 790e29b6d3655..d7532553f45f9 100644
--- a/src/plugins/es_ui_shared/public/request/np_ready_request.ts
+++ b/src/plugins/es_ui_shared/public/request/np_ready_request.ts
@@ -28,8 +28,8 @@ export interface SendRequestConfig {
   body?: any;
 }
 
-export interface SendRequestResponse {
-  data: any;
+export interface SendRequestResponse<D = any> {
+  data: D | null;
   error: Error | null;
 }
 
@@ -39,18 +39,18 @@ export interface UseRequestConfig extends SendRequestConfig {
   deserializer?: (data: any) => any;
 }
 
-export interface UseRequestResponse {
+export interface UseRequestResponse<D = any> {
   isInitialRequest: boolean;
   isLoading: boolean;
-  error: null | unknown;
-  data: any;
-  sendRequest: (...args: any[]) => Promise<SendRequestResponse>;
+  error: Error | null;
+  data: D | null;
+  sendRequest: (...args: any[]) => Promise<SendRequestResponse<D>>;
 }
 
-export const sendRequest = async (
+export const sendRequest = async <D = any>(
   httpClient: HttpSetup,
   { path, method, body, query }: SendRequestConfig
-): Promise<SendRequestResponse> => {
+): Promise<SendRequestResponse<D>> => {
   try {
     const response = await httpClient[method](path, { body, query });
 
@@ -66,7 +66,7 @@ export const sendRequest = async (
   }
 };
 
-export const useRequest = (
+export const useRequest = <D = any>(
   httpClient: HttpSetup,
   {
     path,
@@ -77,7 +77,7 @@ export const useRequest = (
     initialData,
     deserializer = (data: any): any => data,
   }: UseRequestConfig
-): UseRequestResponse => {
+): UseRequestResponse<D> => {
   // Main states for tracking request status and data
   const [error, setError] = useState<null | any>(null);
   const [isLoading, setIsLoading] = useState<boolean>(true);
diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json
index 775c661dd75e0..f0b590f7ffd6c 100644
--- a/x-pack/.i18nrc.json
+++ b/x-pack/.i18nrc.json
@@ -18,6 +18,7 @@
     "xpack.idxMgmt": "legacy/plugins/index_management",
     "xpack.indexLifecycleMgmt": "legacy/plugins/index_lifecycle_management",
     "xpack.infra": "plugins/infra",
+    "xpack.ingestManager": "plugins/ingest_manager",
     "xpack.data": "plugins/data_enhanced",
     "xpack.lens": "legacy/plugins/lens",
     "xpack.licenseMgmt": "legacy/plugins/license_management",
diff --git a/x-pack/index.js b/x-pack/index.js
index 858c3e8b68d18..f3f569e021070 100644
--- a/x-pack/index.js
+++ b/x-pack/index.js
@@ -37,6 +37,7 @@ import { transform } from './legacy/plugins/transform';
 import { actions } from './legacy/plugins/actions';
 import { alerting } from './legacy/plugins/alerting';
 import { lens } from './legacy/plugins/lens';
+import { ingestManager } from './legacy/plugins/ingest_manager';
 import { triggersActionsUI } from './legacy/plugins/triggers_actions_ui';
 
 module.exports = function(kibana) {
@@ -74,6 +75,7 @@ module.exports = function(kibana) {
     snapshotRestore(kibana),
     actions(kibana),
     alerting(kibana),
+    ingestManager(kibana),
     triggersActionsUI(kibana),
   ];
 };
diff --git a/x-pack/legacy/plugins/ingest_manager/index.ts b/x-pack/legacy/plugins/ingest_manager/index.ts
new file mode 100644
index 0000000000000..c20cc7225d780
--- /dev/null
+++ b/x-pack/legacy/plugins/ingest_manager/index.ts
@@ -0,0 +1,39 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import {
+  savedObjectMappings,
+  OUTPUT_SAVED_OBJECT_TYPE,
+  AGENT_CONFIG_SAVED_OBJECT_TYPE,
+  DATASOURCE_SAVED_OBJECT_TYPE,
+} from '../../../plugins/ingest_manager/server';
+
+// TODO https://github.com/elastic/kibana/issues/46373
+// const INDEX_NAMES = {
+//   INGEST: '.kibana',
+// };
+
+export function ingestManager(kibana: any) {
+  return new kibana.Plugin({
+    id: 'ingestManager',
+    uiExports: {
+      savedObjectSchemas: {
+        [AGENT_CONFIG_SAVED_OBJECT_TYPE]: {
+          isNamespaceAgnostic: true,
+          // indexPattern: INDEX_NAMES.INGEST,
+        },
+        [OUTPUT_SAVED_OBJECT_TYPE]: {
+          isNamespaceAgnostic: true,
+          // indexPattern: INDEX_NAMES.INGEST,
+        },
+        [DATASOURCE_SAVED_OBJECT_TYPE]: {
+          isNamespaceAgnostic: true,
+          // indexPattern: INDEX_NAMES.INGEST,
+        },
+      },
+      mappings: savedObjectMappings,
+    },
+  });
+}
diff --git a/x-pack/plugins/ingest_manager/README.md b/x-pack/plugins/ingest_manager/README.md
new file mode 100644
index 0000000000000..60c2a457a2806
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/README.md
@@ -0,0 +1,20 @@
+# Ingest Manager
+
+## Getting started
+See the Kibana docs for [how to set up your dev environment](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#setting-up-your-development-environment), [run Elasticsearch](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#running-elasticsearch), and [start Kibana](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#running-kibana).
+
+One common workflow is:
+
+ 1. `yarn es snapshot`
+ 1. In another shell: `yarn start --xpack.ingestManager.enabled=true` (or set in `config.yml`)
+
+## HTTP API
+  1. Nothing by default. If `xpack.ingestManager.enabled=true`, it adds the `DATASOURCE_API_ROUTES` and `AGENT_CONFIG_API_ROUTES` values in [`common/constants/routes.ts`](./common/constants/routes.ts)
+  1. [Integration tests](../../test/api_integration/apis/ingest_manager/endpoints.ts)
+  1. In later versions the EPM and Fleet routes will be added when their flags are enabled. See the [currently disabled logic to add those routes](https://github.com/jfsiii/kibana/blob/feature-ingest-manager/x-pack/plugins/ingest_manager/server/plugin.ts#L86-L90).
+
+## Plugin architecture
+Follows the `common`, `server`, `public` structure from the [Architecture Style Guide
+](https://github.com/elastic/kibana/blob/master/style_guides/architecture_style_guide.md#file-and-folder-structure).
+
+We use New Platform approach (structure, APIs, etc) where possible. There's a `kibana.json` manifest, and the server uses the `server/{index,plugin}.ts` approach from [`MIGRATION.md`](https://github.com/elastic/kibana/blob/master/src/core/MIGRATION.md#architecture). 
\ No newline at end of file
diff --git a/x-pack/plugins/ingest_manager/common/constants/agent_config.ts b/x-pack/plugins/ingest_manager/common/constants/agent_config.ts
new file mode 100644
index 0000000000000..d0854d6ffeec7
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/common/constants/agent_config.ts
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import { AgentConfigStatus } from '../types';
+
+export const AGENT_CONFIG_SAVED_OBJECT_TYPE = 'agent_configs';
+
+export const DEFAULT_AGENT_CONFIG_ID = 'default';
+
+export const DEFAULT_AGENT_CONFIG = {
+  name: 'Default config',
+  namespace: 'default',
+  description: 'Default agent configuration created by Kibana',
+  status: AgentConfigStatus.Active,
+  datasources: [],
+};
diff --git a/x-pack/plugins/ingest_manager/common/constants/datasource.ts b/x-pack/plugins/ingest_manager/common/constants/datasource.ts
new file mode 100644
index 0000000000000..0ff472b2afeb0
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/common/constants/datasource.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 const DATASOURCE_SAVED_OBJECT_TYPE = 'datasources';
diff --git a/x-pack/plugins/ingest_manager/common/constants/index.ts b/x-pack/plugins/ingest_manager/common/constants/index.ts
new file mode 100644
index 0000000000000..aa3b204be4889
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/common/constants/index.ts
@@ -0,0 +1,11 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+export * from './plugin';
+export * from './routes';
+
+export * from './agent_config';
+export * from './datasource';
+export * from './output';
diff --git a/x-pack/plugins/ingest_manager/common/constants/output.ts b/x-pack/plugins/ingest_manager/common/constants/output.ts
new file mode 100644
index 0000000000000..e0262d0ca811c
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/common/constants/output.ts
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import { OutputType } from '../types';
+
+export const OUTPUT_SAVED_OBJECT_TYPE = 'outputs';
+
+export const DEFAULT_OUTPUT_ID = 'default';
+
+export const DEFAULT_OUTPUT = {
+  name: DEFAULT_OUTPUT_ID,
+  type: OutputType.Elasticsearch,
+  hosts: [''],
+  ingest_pipeline: DEFAULT_OUTPUT_ID,
+  api_key: '',
+};
diff --git a/x-pack/plugins/ingest_manager/common/constants/plugin.ts b/x-pack/plugins/ingest_manager/common/constants/plugin.ts
new file mode 100644
index 0000000000000..7922e6cadfa28
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/common/constants/plugin.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 const PLUGIN_ID = 'ingestManager';
diff --git a/x-pack/plugins/ingest_manager/common/constants/routes.ts b/x-pack/plugins/ingest_manager/common/constants/routes.ts
new file mode 100644
index 0000000000000..efd6ef17ba05b
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/common/constants/routes.ts
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+// Base API paths
+export const API_ROOT = `/api/ingest_manager`;
+export const DATASOURCE_API_ROOT = `${API_ROOT}/datasources`;
+export const AGENT_CONFIG_API_ROOT = `${API_ROOT}/agent_configs`;
+export const EPM_API_ROOT = `${API_ROOT}/epm`;
+export const FLEET_API_ROOT = `${API_ROOT}/fleet`;
+
+// EPM API routes
+export const EPM_API_ROUTES = {
+  LIST_PATTERN: `${EPM_API_ROOT}/list`,
+  INFO_PATTERN: `${EPM_API_ROOT}/package/{pkgkey}`,
+  INSTALL_PATTERN: `${EPM_API_ROOT}/install/{pkgkey}`,
+  DELETE_PATTERN: `${EPM_API_ROOT}/delete/{pkgkey}`,
+  CATEGORIES_PATTERN: `${EPM_API_ROOT}/categories`,
+};
+
+// Datasource API routes
+export const DATASOURCE_API_ROUTES = {
+  LIST_PATTERN: `${DATASOURCE_API_ROOT}`,
+  INFO_PATTERN: `${DATASOURCE_API_ROOT}/{datasourceId}`,
+  CREATE_PATTERN: `${DATASOURCE_API_ROOT}`,
+  UPDATE_PATTERN: `${DATASOURCE_API_ROOT}/{datasourceId}`,
+  DELETE_PATTERN: `${DATASOURCE_API_ROOT}/delete`,
+};
+
+// Agent config API routes
+export const AGENT_CONFIG_API_ROUTES = {
+  LIST_PATTERN: `${AGENT_CONFIG_API_ROOT}`,
+  INFO_PATTERN: `${AGENT_CONFIG_API_ROOT}/{agentConfigId}`,
+  CREATE_PATTERN: `${AGENT_CONFIG_API_ROOT}`,
+  UPDATE_PATTERN: `${AGENT_CONFIG_API_ROOT}/{agentConfigId}`,
+  DELETE_PATTERN: `${AGENT_CONFIG_API_ROOT}/delete`,
+};
+
+// Fleet setup API routes
+export const FLEET_SETUP_API_ROUTES = {
+  INFO_PATTERN: `${FLEET_API_ROOT}/setup`,
+  CREATE_PATTERN: `${FLEET_API_ROOT}/setup`,
+};
diff --git a/x-pack/plugins/ingest_manager/common/index.ts b/x-pack/plugins/ingest_manager/common/index.ts
new file mode 100644
index 0000000000000..3d1c70ba2635e
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/common/index.ts
@@ -0,0 +1,8 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+export * from './constants';
+export * from './services';
+export * from './types';
diff --git a/x-pack/plugins/ingest_manager/common/services/index.ts b/x-pack/plugins/ingest_manager/common/services/index.ts
new file mode 100644
index 0000000000000..1b3ae4706e3a7
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/common/services/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 './routes';
diff --git a/x-pack/plugins/ingest_manager/common/services/routes.ts b/x-pack/plugins/ingest_manager/common/services/routes.ts
new file mode 100644
index 0000000000000..bcd1646fe1f0c
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/common/services/routes.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 {
+  EPM_API_ROOT,
+  EPM_API_ROUTES,
+  DATASOURCE_API_ROUTES,
+  AGENT_CONFIG_API_ROUTES,
+} from '../constants';
+
+export const epmRouteService = {
+  getCategoriesPath: () => {
+    return EPM_API_ROUTES.CATEGORIES_PATTERN;
+  },
+
+  getListPath: () => {
+    return EPM_API_ROUTES.LIST_PATTERN;
+  },
+
+  getInfoPath: (pkgkey: string) => {
+    return EPM_API_ROUTES.INFO_PATTERN.replace('{pkgkey}', pkgkey);
+  },
+
+  getFilePath: (filePath: string) => {
+    return `${EPM_API_ROOT}${filePath}`;
+  },
+
+  getInstallPath: (pkgkey: string) => {
+    return EPM_API_ROUTES.INSTALL_PATTERN.replace('{pkgkey}', pkgkey).replace(/\/$/, ''); // trim trailing slash
+  },
+
+  getRemovePath: (pkgkey: string) => {
+    return EPM_API_ROUTES.DELETE_PATTERN.replace('{pkgkey}', pkgkey).replace(/\/$/, ''); // trim trailing slash
+  },
+};
+
+export const datasourceRouteService = {
+  getListPath: () => {
+    return DATASOURCE_API_ROUTES.LIST_PATTERN;
+  },
+
+  getInfoPath: (datasourceId: string) => {
+    return DATASOURCE_API_ROUTES.INFO_PATTERN.replace('{datasourceId}', datasourceId);
+  },
+
+  getCreatePath: () => {
+    return DATASOURCE_API_ROUTES.CREATE_PATTERN;
+  },
+
+  getUpdatePath: (datasourceId: string) => {
+    return DATASOURCE_API_ROUTES.UPDATE_PATTERN.replace('{datasourceId}', datasourceId);
+  },
+};
+
+export const agentConfigRouteService = {
+  getListPath: () => {
+    return AGENT_CONFIG_API_ROUTES.LIST_PATTERN;
+  },
+
+  getInfoPath: (agentConfigId: string) => {
+    return AGENT_CONFIG_API_ROUTES.INFO_PATTERN.replace('{agentConfigId}', agentConfigId);
+  },
+
+  getCreatePath: () => {
+    return AGENT_CONFIG_API_ROUTES.CREATE_PATTERN;
+  },
+
+  getUpdatePath: (agentConfigId: string) => {
+    return AGENT_CONFIG_API_ROUTES.UPDATE_PATTERN.replace('{agentConfigId}', agentConfigId);
+  },
+
+  getDeletePath: () => {
+    return AGENT_CONFIG_API_ROUTES.DELETE_PATTERN;
+  },
+};
diff --git a/x-pack/plugins/ingest_manager/common/types/index.ts b/x-pack/plugins/ingest_manager/common/types/index.ts
new file mode 100644
index 0000000000000..4abb1b659f036
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/common/types/index.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.
+ */
+
+export * from './models';
+export * from './rest_spec';
+
+export interface IngestManagerConfigType {
+  enabled: boolean;
+  epm: {
+    enabled: boolean;
+    registryUrl: string;
+  };
+  fleet: {
+    enabled: boolean;
+    defaultOutputHost: string;
+  };
+}
diff --git a/x-pack/plugins/ingest_manager/common/types/models/agent_config.ts b/x-pack/plugins/ingest_manager/common/types/models/agent_config.ts
new file mode 100644
index 0000000000000..1cc8b32afe3c1
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/common/types/models/agent_config.ts
@@ -0,0 +1,32 @@
+/*
+ * 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 { DatasourceSchema } from './datasource';
+
+export enum AgentConfigStatus {
+  Active = 'active',
+  Inactive = 'inactive',
+}
+
+interface AgentConfigBaseSchema {
+  name: string;
+  namespace: string;
+  description?: string;
+}
+
+export type NewAgentConfigSchema = AgentConfigBaseSchema;
+
+export type AgentConfigSchema = AgentConfigBaseSchema & {
+  id: string;
+  status: AgentConfigStatus;
+  datasources: Array<string | DatasourceSchema>;
+  updated_on: string;
+  updated_by: string;
+};
+
+export type NewAgentConfig = NewAgentConfigSchema;
+
+export type AgentConfig = AgentConfigSchema;
diff --git a/x-pack/plugins/ingest_manager/common/types/models/datasource.ts b/x-pack/plugins/ingest_manager/common/types/models/datasource.ts
new file mode 100644
index 0000000000000..f28037845c7f7
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/common/types/models/datasource.ts
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+interface DatasourceBaseSchema {
+  name: string;
+  namespace: string;
+  read_alias: string;
+  agent_config_id: string;
+  package: {
+    assets: Array<{
+      id: string;
+      type: string;
+    }>;
+    description: string;
+    name: string;
+    title: string;
+    version: string;
+  };
+  streams: Array<{
+    config: Record<string, any>;
+    input: {
+      type: string;
+      config: Record<string, any>;
+      fields: Array<Record<string, any>>;
+      ilm_policy: string;
+      index_template: string;
+      ingest_pipelines: string[];
+    };
+    output_id: string;
+    processors: string[];
+  }>;
+}
+
+export type NewDatasourceSchema = DatasourceBaseSchema;
+
+export type DatasourceSchema = DatasourceBaseSchema & { id: string };
+
+export type NewDatasource = NewDatasourceSchema;
+
+export type Datasource = DatasourceSchema;
diff --git a/x-pack/plugins/ingest_manager/common/types/models/index.ts b/x-pack/plugins/ingest_manager/common/types/models/index.ts
new file mode 100644
index 0000000000000..959dfe1d937b9
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/common/types/models/index.ts
@@ -0,0 +1,8 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+export * from './agent_config';
+export * from './datasource';
+export * from './output';
diff --git a/x-pack/plugins/ingest_manager/common/types/models/output.ts b/x-pack/plugins/ingest_manager/common/types/models/output.ts
new file mode 100644
index 0000000000000..5f96fe33b5e16
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/common/types/models/output.ts
@@ -0,0 +1,33 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export enum OutputType {
+  Elasticsearch = 'elasticsearch',
+}
+
+interface OutputBaseSchema {
+  name: string;
+  type: OutputType;
+  username?: string;
+  password?: string;
+  index_name?: string;
+  ingest_pipeline?: string;
+  hosts?: string[];
+  api_key?: string;
+  admin_username?: string;
+  admin_password?: string;
+  config?: Record<string, any>;
+}
+
+export type NewOutputSchema = OutputBaseSchema;
+
+export type OutputSchema = OutputBaseSchema & {
+  id: string;
+};
+
+export type NewOutput = NewOutputSchema;
+
+export type Output = OutputSchema;
diff --git a/x-pack/plugins/ingest_manager/common/types/rest_spec/agent_config.ts b/x-pack/plugins/ingest_manager/common/types/rest_spec/agent_config.ts
new file mode 100644
index 0000000000000..5d281b03260db
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/common/types/rest_spec/agent_config.ts
@@ -0,0 +1,59 @@
+/*
+ * 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 { AgentConfig, NewAgentConfigSchema } from '../models';
+import { ListWithKuerySchema } from './common';
+
+export interface GetAgentConfigsRequestSchema {
+  query: ListWithKuerySchema;
+}
+
+export interface GetAgentConfigsResponse {
+  items: AgentConfig[];
+  total: number;
+  page: number;
+  perPage: number;
+  success: boolean;
+}
+
+export interface GetOneAgentConfigRequestSchema {
+  params: {
+    agentConfigId: string;
+  };
+}
+
+export interface GetOneAgentConfigResponse {
+  item: AgentConfig;
+  success: boolean;
+}
+
+export interface CreateAgentConfigRequestSchema {
+  body: NewAgentConfigSchema;
+}
+
+export interface CreateAgentConfigResponse {
+  item: AgentConfig;
+  success: boolean;
+}
+
+export type UpdateAgentConfigRequestSchema = GetOneAgentConfigRequestSchema & {
+  body: NewAgentConfigSchema;
+};
+
+export interface UpdateAgentConfigResponse {
+  item: AgentConfig;
+  success: boolean;
+}
+
+export interface DeleteAgentConfigsRequestSchema {
+  body: {
+    agentConfigIds: string[];
+  };
+}
+
+export type DeleteAgentConfigsResponse = Array<{
+  id: string;
+  success: boolean;
+}>;
diff --git a/x-pack/plugins/ingest_manager/common/types/rest_spec/common.ts b/x-pack/plugins/ingest_manager/common/types/rest_spec/common.ts
new file mode 100644
index 0000000000000..d247933d4011f
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/common/types/rest_spec/common.ts
@@ -0,0 +1,13 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export interface ListWithKuerySchema {
+  page: number;
+  perPage: number;
+  kuery?: string;
+}
+
+export type ListWithKuery = ListWithKuerySchema;
diff --git a/x-pack/plugins/ingest_manager/common/types/rest_spec/datasource.ts b/x-pack/plugins/ingest_manager/common/types/rest_spec/datasource.ts
new file mode 100644
index 0000000000000..78859f2008005
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/common/types/rest_spec/datasource.ts
@@ -0,0 +1,36 @@
+/*
+ * 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 { NewDatasourceSchema } from '../models';
+import { ListWithKuerySchema } from './common';
+
+export interface GetDatasourcesRequestSchema {
+  query: ListWithKuerySchema;
+}
+
+export interface GetOneDatasourceRequestSchema {
+  params: {
+    datasourceId: string;
+  };
+}
+
+export interface CreateDatasourceRequestSchema {
+  body: NewDatasourceSchema;
+}
+
+export type UpdateDatasourceRequestSchema = GetOneDatasourceRequestSchema & {
+  body: NewDatasourceSchema;
+};
+
+export interface DeleteDatasourcesRequestSchema {
+  body: {
+    datasourceIds: string[];
+  };
+}
+
+export type DeleteDatasourcesResponse = Array<{
+  id: string;
+  success: boolean;
+}>;
diff --git a/x-pack/plugins/ingest_manager/common/types/rest_spec/fleet_setup.ts b/x-pack/plugins/ingest_manager/common/types/rest_spec/fleet_setup.ts
new file mode 100644
index 0000000000000..926021baab0ef
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/common/types/rest_spec/fleet_setup.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.
+ */
+
+// eslint-disable-next-line @typescript-eslint/no-empty-interface
+export interface GetFleetSetupRequestSchema {}
+
+export interface CreateFleetSetupRequestSchema {
+  body: {
+    admin_username: string;
+    admin_password: string;
+  };
+}
+
+export interface CreateFleetSetupResponse {
+  isInitialized: boolean;
+}
diff --git a/x-pack/plugins/ingest_manager/common/types/rest_spec/index.ts b/x-pack/plugins/ingest_manager/common/types/rest_spec/index.ts
new file mode 100644
index 0000000000000..7d0d7e67f2db0
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/common/types/rest_spec/index.ts
@@ -0,0 +1,9 @@
+/*
+ * 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 './common';
+export * from './datasource';
+export * from './agent_config';
+export * from './fleet_setup';
diff --git a/x-pack/plugins/ingest_manager/kibana.json b/x-pack/plugins/ingest_manager/kibana.json
new file mode 100644
index 0000000000000..cef1a293c104b
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/kibana.json
@@ -0,0 +1,9 @@
+{
+  "id": "ingestManager",
+  "version": "kibana",
+  "server": true,
+  "ui": true,
+  "configPath": ["xpack", "ingestManager"],
+  "requiredPlugins": ["licensing", "data", "encryptedSavedObjects"],
+  "optionalPlugins": ["security", "features"]
+}
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/index.ts
new file mode 100644
index 0000000000000..5133d82588494
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/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 { Loading } from './loading';
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/loading.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/loading.tsx
new file mode 100644
index 0000000000000..c1fae19c5dab0
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/loading.tsx
@@ -0,0 +1,15 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import React from 'react';
+import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui';
+
+export const Loading: React.FunctionComponent<{}> = () => (
+  <EuiFlexGroup justifyContent="spaceAround">
+    <EuiFlexItem grow={false}>
+      <EuiLoadingSpinner size="xl" />
+    </EuiFlexItem>
+  </EuiFlexGroup>
+);
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/constants/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/constants/index.ts
new file mode 100644
index 0000000000000..1af39a60455e0
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/constants/index.ts
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export {
+  PLUGIN_ID,
+  EPM_API_ROUTES,
+  DEFAULT_AGENT_CONFIG_ID,
+  AGENT_CONFIG_SAVED_OBJECT_TYPE,
+} from '../../../../common';
+
+export const BASE_PATH = '/app/ingestManager';
+export const EPM_PATH = '/epm';
+export const AGENT_CONFIG_PATH = '/configs';
+export const AGENT_CONFIG_DETAILS_PATH = `${AGENT_CONFIG_PATH}/`;
+export const FLEET_PATH = '/fleet';
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/index.ts
new file mode 100644
index 0000000000000..a224b599c13af
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/index.ts
@@ -0,0 +1,13 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export { useCore, CoreContext } from './use_core';
+export { useConfig, ConfigContext } from './use_config';
+export { useDeps, DepsContext } from './use_deps';
+export { useLink } from './use_link';
+export { usePagination } from './use_pagination';
+export { useDebounce } from './use_debounce';
+export * from './use_request';
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_config.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_config.ts
new file mode 100644
index 0000000000000..d3f27a180cfd0
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_config.ts
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { useContext } from 'react';
+import { IngestManagerConfigType } from '../../../plugin';
+
+export const ConfigContext = React.createContext<IngestManagerConfigType | null>(null);
+
+export function useConfig() {
+  const config = useContext(ConfigContext);
+  if (config === null) {
+    throw new Error('ConfigContext not initialized');
+  }
+  return config;
+}
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_core.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_core.ts
new file mode 100644
index 0000000000000..c6e91444d21f5
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_core.ts
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { useContext } from 'react';
+import { CoreStart } from 'kibana/public';
+
+export const CoreContext = React.createContext<CoreStart | null>(null);
+
+export function useCore() {
+  const core = useContext(CoreContext);
+  if (core === null) {
+    throw new Error('CoreContext not initialized');
+  }
+  return core;
+}
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_debounce.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_debounce.tsx
new file mode 100644
index 0000000000000..f701ebeaadbe5
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_debounce.tsx
@@ -0,0 +1,23 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { useState, useEffect } from 'react';
+
+export function useDebounce<T>(value: T, delay: number) {
+  const [debouncedValue, setDebouncedValue] = useState(value);
+
+  useEffect(() => {
+    const handler = setTimeout(() => {
+      setDebouncedValue(value);
+    }, delay);
+
+    return () => {
+      clearTimeout(handler);
+    };
+  }, [value, delay]);
+
+  return debouncedValue;
+}
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_deps.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_deps.ts
new file mode 100644
index 0000000000000..a2e2f278930e3
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_deps.ts
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { useContext } from 'react';
+import { IngestManagerSetupDeps } from '../../../plugin';
+
+export const DepsContext = React.createContext<IngestManagerSetupDeps | null>(null);
+
+export function useDeps() {
+  const deps = useContext(DepsContext);
+  if (deps === null) {
+    throw new Error('DepsContext not initialized');
+  }
+  return deps;
+}
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_link.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_link.ts
new file mode 100644
index 0000000000000..333606cec8028
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_link.ts
@@ -0,0 +1,13 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { BASE_PATH } from '../constants';
+import { useCore } from './';
+
+export function useLink(path: string = '/') {
+  const core = useCore();
+  return core.http.basePath.prepend(`${BASE_PATH}#${path}`);
+}
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_pagination.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_pagination.tsx
new file mode 100644
index 0000000000000..ae0352a33b2ff
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_pagination.tsx
@@ -0,0 +1,25 @@
+/*
+ * 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 { useState } from 'react';
+
+export interface Pagination {
+  currentPage: number;
+  pageSize: number;
+}
+
+export function usePagination() {
+  const [pagination, setPagination] = useState<Pagination>({
+    currentPage: 1,
+    pageSize: 20,
+  });
+
+  return {
+    pagination,
+    setPagination,
+    pageSizeOptions: 20,
+  };
+}
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agent_config.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agent_config.ts
new file mode 100644
index 0000000000000..389909e1d99ef
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agent_config.ts
@@ -0,0 +1,61 @@
+/*
+ * 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 { HttpFetchQuery } from 'kibana/public';
+import { useRequest, sendRequest } from './use_request';
+import { agentConfigRouteService } from '../../services';
+import {
+  GetAgentConfigsResponse,
+  GetOneAgentConfigResponse,
+  CreateAgentConfigRequestSchema,
+  CreateAgentConfigResponse,
+  UpdateAgentConfigRequestSchema,
+  UpdateAgentConfigResponse,
+  DeleteAgentConfigsRequestSchema,
+  DeleteAgentConfigsResponse,
+} from '../../types';
+
+export const useGetAgentConfigs = (query: HttpFetchQuery = {}) => {
+  return useRequest<GetAgentConfigsResponse>({
+    path: agentConfigRouteService.getListPath(),
+    method: 'get',
+    query,
+  });
+};
+
+export const useGetOneAgentConfig = (agentConfigId: string) => {
+  return useRequest<GetOneAgentConfigResponse>({
+    path: agentConfigRouteService.getInfoPath(agentConfigId),
+    method: 'get',
+  });
+};
+
+export const sendCreateAgentConfig = (body: CreateAgentConfigRequestSchema['body']) => {
+  return sendRequest<CreateAgentConfigResponse>({
+    path: agentConfigRouteService.getCreatePath(),
+    method: 'post',
+    body: JSON.stringify(body),
+  });
+};
+
+export const sendUpdateAgentConfig = (
+  agentConfigId: string,
+  body: UpdateAgentConfigRequestSchema['body']
+) => {
+  return sendRequest<UpdateAgentConfigResponse>({
+    path: agentConfigRouteService.getUpdatePath(agentConfigId),
+    method: 'put',
+    body: JSON.stringify(body),
+  });
+};
+
+export const sendDeleteAgentConfigs = (body: DeleteAgentConfigsRequestSchema['body']) => {
+  return sendRequest<DeleteAgentConfigsResponse>({
+    path: agentConfigRouteService.getDeletePath(),
+    method: 'post',
+    body: JSON.stringify(body),
+  });
+};
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/index.ts
new file mode 100644
index 0000000000000..68d67080d90ba
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/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 { setHttpClient, sendRequest } from './use_request';
+export * from './agent_config';
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/use_request.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/use_request.ts
new file mode 100644
index 0000000000000..12b4d0bdf7df6
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/use_request.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.
+ */
+import { HttpSetup } from 'kibana/public';
+import {
+  SendRequestConfig,
+  SendRequestResponse,
+  UseRequestConfig,
+  sendRequest as _sendRequest,
+  useRequest as _useRequest,
+} from '../../../../../../../../src/plugins/es_ui_shared/public';
+
+let httpClient: HttpSetup;
+
+export const setHttpClient = (client: HttpSetup) => {
+  httpClient = client;
+};
+
+export const sendRequest = <D = any>(
+  config: SendRequestConfig
+): Promise<SendRequestResponse<D>> => {
+  if (!httpClient) {
+    throw new Error('sendRequest has no http client set');
+  }
+  return _sendRequest<D>(httpClient, config);
+};
+
+export const useRequest = <D = any>(config: UseRequestConfig) => {
+  if (!httpClient) {
+    throw new Error('sendRequest has no http client set');
+  }
+  return _useRequest<D>(httpClient, config);
+};
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx
new file mode 100644
index 0000000000000..935eb42d0347e
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx
@@ -0,0 +1,108 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { useObservable } from 'react-use';
+import { HashRouter as Router, Redirect, Switch, Route, RouteProps } from 'react-router-dom';
+import { CoreStart, AppMountParameters } from 'kibana/public';
+import { EuiErrorBoundary } from '@elastic/eui';
+import { EuiThemeProvider } from '../../../../../legacy/common/eui_styled_components';
+import { IngestManagerSetupDeps, IngestManagerConfigType } from '../../plugin';
+import { EPM_PATH, FLEET_PATH, AGENT_CONFIG_PATH } from './constants';
+import { DefaultLayout } from './layouts';
+import { IngestManagerOverview, EPMApp, AgentConfigApp, FleetApp } from './sections';
+import { CoreContext, DepsContext, ConfigContext, setHttpClient, useConfig } from './hooks';
+
+export interface ProtectedRouteProps extends RouteProps {
+  isAllowed?: boolean;
+  restrictedPath?: string;
+}
+
+export const ProtectedRoute: React.FunctionComponent<ProtectedRouteProps> = ({
+  isAllowed = false,
+  restrictedPath = '/',
+  ...routeProps
+}: ProtectedRouteProps) => {
+  return isAllowed ? <Route {...routeProps} /> : <Redirect to={{ pathname: restrictedPath }} />;
+};
+
+const IngestManagerRoutes = ({ ...rest }) => {
+  const { epm, fleet } = useConfig();
+
+  return (
+    <EuiErrorBoundary>
+      <Router {...rest}>
+        <Switch>
+          <ProtectedRoute path={EPM_PATH} isAllowed={epm.enabled}>
+            <DefaultLayout section="epm">
+              <EPMApp />
+            </DefaultLayout>
+          </ProtectedRoute>
+          <Route path={AGENT_CONFIG_PATH}>
+            <DefaultLayout section="agent_config">
+              <AgentConfigApp />
+            </DefaultLayout>
+          </Route>
+          <ProtectedRoute path={FLEET_PATH} isAllowed={fleet.enabled}>
+            <DefaultLayout section="fleet">
+              <FleetApp />
+            </DefaultLayout>
+          </ProtectedRoute>
+          <Route exact path="/">
+            <DefaultLayout section="overview">
+              <IngestManagerOverview />
+            </DefaultLayout>
+          </Route>
+          <Redirect to="/" />
+        </Switch>
+      </Router>
+    </EuiErrorBoundary>
+  );
+};
+
+const IngestManagerApp = ({
+  basepath,
+  coreStart,
+  deps,
+  config,
+}: {
+  basepath: string;
+  coreStart: CoreStart;
+  deps: IngestManagerSetupDeps;
+  config: IngestManagerConfigType;
+}) => {
+  const isDarkMode = useObservable<boolean>(coreStart.uiSettings.get$('theme:darkMode'));
+  return (
+    <coreStart.i18n.Context>
+      <CoreContext.Provider value={coreStart}>
+        <DepsContext.Provider value={deps}>
+          <ConfigContext.Provider value={config}>
+            <EuiThemeProvider darkMode={isDarkMode}>
+              <IngestManagerRoutes basepath={basepath} />
+            </EuiThemeProvider>
+          </ConfigContext.Provider>
+        </DepsContext.Provider>
+      </CoreContext.Provider>
+    </coreStart.i18n.Context>
+  );
+};
+
+export function renderApp(
+  coreStart: CoreStart,
+  { element, appBasePath }: AppMountParameters,
+  deps: IngestManagerSetupDeps,
+  config: IngestManagerConfigType
+) {
+  setHttpClient(coreStart.http);
+  ReactDOM.render(
+    <IngestManagerApp basepath={appBasePath} coreStart={coreStart} deps={deps} config={config} />,
+    element
+  );
+
+  return () => {
+    ReactDOM.unmountComponentAtNode(element);
+  };
+}
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx
new file mode 100644
index 0000000000000..eaf49fed3d933
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx
@@ -0,0 +1,90 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import React from 'react';
+import {
+  EuiPage,
+  EuiPageBody,
+  EuiTabs,
+  EuiTab,
+  EuiFlexGroup,
+  EuiFlexItem,
+  EuiIcon,
+} from '@elastic/eui';
+import { FormattedMessage } from '@kbn/i18n/react';
+import euiStyled from '../../../../../../legacy/common/eui_styled_components';
+import { Section } from '../sections';
+import { useLink, useConfig } from '../hooks';
+import { EPM_PATH, FLEET_PATH, AGENT_CONFIG_PATH } from '../constants';
+
+interface Props {
+  section: Section;
+  children?: React.ReactNode;
+}
+
+const Nav = euiStyled.nav`
+  background: ${props => props.theme.eui.euiColorEmptyShade};
+  border-bottom: ${props => props.theme.eui.euiBorderThin};
+  padding: ${props =>
+    `${props.theme.eui.euiSize} ${props.theme.eui.euiSizeL} ${props.theme.eui.euiSize} ${props.theme.eui.euiSizeL}`};
+  .euiTabs {
+    padding-left: 3px;
+    margin-left: -3px;
+  };
+`;
+
+export const DefaultLayout: React.FunctionComponent<Props> = ({ section, children }) => {
+  const { epm, fleet } = useConfig();
+  return (
+    <div>
+      <Nav>
+        <EuiFlexGroup gutterSize="l" alignItems="center">
+          <EuiFlexItem grow={false}>
+            <EuiIcon type="savedObjectsApp" size="l" />
+          </EuiFlexItem>
+          <EuiFlexItem>
+            <EuiTabs display="condensed">
+              <EuiTab isSelected={!section || section === 'overview'} href={useLink()}>
+                <FormattedMessage
+                  id="xpack.ingestManager.appNavigation.overviewLinkText"
+                  defaultMessage="Overview"
+                />
+              </EuiTab>
+              <EuiTab
+                isSelected={section === 'epm'}
+                href={useLink(EPM_PATH)}
+                disabled={!epm?.enabled}
+              >
+                <FormattedMessage
+                  id="xpack.ingestManager.appNavigation.packagesLinkText"
+                  defaultMessage="Packages"
+                />
+              </EuiTab>
+              <EuiTab isSelected={section === 'agent_config'} href={useLink(AGENT_CONFIG_PATH)}>
+                <FormattedMessage
+                  id="xpack.ingestManager.appNavigation.configurationsLinkText"
+                  defaultMessage="Configurations"
+                />
+              </EuiTab>
+              <EuiTab
+                isSelected={section === 'fleet'}
+                href={useLink(FLEET_PATH)}
+                disabled={!fleet?.enabled}
+              >
+                <FormattedMessage
+                  id="xpack.ingestManager.appNavigation.fleetLinkText"
+                  defaultMessage="Fleet"
+                />
+              </EuiTab>
+            </EuiTabs>
+          </EuiFlexItem>
+        </EuiFlexGroup>
+      </Nav>
+      <EuiPage>
+        <EuiPageBody>{children}</EuiPageBody>
+      </EuiPage>
+    </div>
+  );
+};
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/index.tsx
new file mode 100644
index 0000000000000..858951bd0d38f
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/index.tsx
@@ -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 { DefaultLayout } from './default';
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_delete_provider.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_delete_provider.tsx
new file mode 100644
index 0000000000000..6f51415a562a3
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_delete_provider.tsx
@@ -0,0 +1,217 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { Fragment, useRef, useState } from 'react';
+import { EuiConfirmModal, EuiOverlayMask } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { sendDeleteAgentConfigs, useCore, sendRequest } from '../../../hooks';
+
+interface Props {
+  children: (deleteAgentConfigs: deleteAgentConfigs) => React.ReactElement;
+}
+
+export type deleteAgentConfigs = (agentConfigs: string[], onSuccess?: OnSuccessCallback) => void;
+
+type OnSuccessCallback = (agentConfigsUnenrolled: string[]) => void;
+
+export const AgentConfigDeleteProvider: React.FunctionComponent<Props> = ({ children }) => {
+  const { notifications } = useCore();
+  const [agentConfigs, setAgentConfigs] = useState<string[]>([]);
+  const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
+  const [isLoadingAgentsCount, setIsLoadingAgentsCount] = useState<boolean>(false);
+  const [agentsCount, setAgentsCount] = useState<number>(0);
+  const [isLoading, setIsLoading] = useState<boolean>(false);
+  const onSuccessCallback = useRef<OnSuccessCallback | null>(null);
+
+  const deleteAgentConfigsPrompt: deleteAgentConfigs = (
+    agentConfigsToDelete,
+    onSuccess = () => undefined
+  ) => {
+    if (
+      agentConfigsToDelete === undefined ||
+      (Array.isArray(agentConfigsToDelete) && agentConfigsToDelete.length === 0)
+    ) {
+      throw new Error('No agent configs specified for deletion');
+    }
+    setIsModalOpen(true);
+    setAgentConfigs(agentConfigsToDelete);
+    fetchAgentsCount(agentConfigsToDelete);
+    onSuccessCallback.current = onSuccess;
+  };
+
+  const closeModal = () => {
+    setAgentConfigs([]);
+    setIsLoading(false);
+    setIsLoadingAgentsCount(false);
+    setIsModalOpen(false);
+  };
+
+  const deleteAgentConfigs = async () => {
+    setIsLoading(true);
+
+    try {
+      const { data } = await sendDeleteAgentConfigs({
+        agentConfigIds: agentConfigs,
+      });
+      const successfulResults = data?.filter(result => result.success) || [];
+      const failedResults = data?.filter(result => !result.success) || [];
+
+      if (successfulResults.length) {
+        const hasMultipleSuccesses = successfulResults.length > 1;
+        const successMessage = hasMultipleSuccesses
+          ? i18n.translate(
+              'xpack.ingestManager.deleteAgentConfigs.successMultipleNotificationTitle',
+              {
+                defaultMessage: 'Deleted {count} agent configs',
+                values: { count: successfulResults.length },
+              }
+            )
+          : i18n.translate(
+              'xpack.ingestManager.deleteAgentConfigs.successSingleNotificationTitle',
+              {
+                defaultMessage: "Deleted agent config '{id}'",
+                values: { id: successfulResults[0].id },
+              }
+            );
+        notifications.toasts.addSuccess(successMessage);
+      }
+
+      if (failedResults.length) {
+        const hasMultipleFailures = failedResults.length > 1;
+        const failureMessage = hasMultipleFailures
+          ? i18n.translate(
+              'xpack.ingestManager.deleteAgentConfigs.failureMultipleNotificationTitle',
+              {
+                defaultMessage: 'Error deleting {count} agent configs',
+                values: { count: failedResults.length },
+              }
+            )
+          : i18n.translate(
+              'xpack.ingestManager.deleteAgentConfigs.failureSingleNotificationTitle',
+              {
+                defaultMessage: "Error deleting agent config '{id}'",
+                values: { id: failedResults[0].id },
+              }
+            );
+        notifications.toasts.addDanger(failureMessage);
+      }
+
+      if (onSuccessCallback.current) {
+        onSuccessCallback.current(successfulResults.map(result => result.id));
+      }
+    } catch (e) {
+      notifications.toasts.addDanger(
+        i18n.translate('xpack.ingestManager.deleteAgentConfigs.fatalErrorNotificationTitle', {
+          defaultMessage: 'Error deleting agent configs',
+        })
+      );
+    }
+    closeModal();
+  };
+
+  const fetchAgentsCount = async (agentConfigsToCheck: string[]) => {
+    if (isLoadingAgentsCount) {
+      return;
+    }
+    setIsLoadingAgentsCount(true);
+    const { data } = await sendRequest<{ total: number }>({
+      path: `/api/fleet/agents`,
+      method: 'get',
+      query: {
+        kuery: `agents.policy_id : (${agentConfigsToCheck.join(' or ')})`,
+      },
+    });
+    setAgentsCount(data?.total || 0);
+    setIsLoadingAgentsCount(false);
+  };
+
+  const renderModal = () => {
+    if (!isModalOpen) {
+      return null;
+    }
+
+    return (
+      <EuiOverlayMask>
+        <EuiConfirmModal
+          title={
+            <FormattedMessage
+              id="xpack.ingestManager.deleteAgentConfigs.confirmModal.deleteMultipleTitle"
+              defaultMessage="Delete {count, plural, one {this agent config} other {# agent configs}}?"
+              values={{ count: agentConfigs.length }}
+            />
+          }
+          onCancel={closeModal}
+          onConfirm={deleteAgentConfigs}
+          cancelButtonText={
+            <FormattedMessage
+              id="xpack.ingestManager.deleteAgentConfigs.confirmModal.cancelButtonLabel"
+              defaultMessage="Cancel"
+            />
+          }
+          confirmButtonText={
+            isLoading || isLoadingAgentsCount ? (
+              <FormattedMessage
+                id="xpack.ingestManager.deleteAgentConfigs.confirmModal.loadingButtonLabel"
+                defaultMessage="Loading…"
+              />
+            ) : agentsCount ? (
+              <FormattedMessage
+                id="xpack.ingestManager.deleteAgentConfigs.confirmModal.confirmAndReassignButtonLabel"
+                defaultMessage="Delete {agentConfigsCount, plural, one {agent config} other {agent configs}} and unenroll {agentsCount, plural, one {agent} other {agents}}"
+                values={{
+                  agentsCount,
+                  agentConfigsCount: agentConfigs.length,
+                }}
+              />
+            ) : (
+              <FormattedMessage
+                id="xpack.ingestManager.deleteAgentConfigs.confirmModal.confirmButtonLabel"
+                defaultMessage="Delete {agentConfigsCount, plural, one {agent config} other {agent configs}}"
+                values={{
+                  agentConfigsCount: agentConfigs.length,
+                }}
+              />
+            )
+          }
+          buttonColor="danger"
+          confirmButtonDisabled={isLoading || isLoadingAgentsCount}
+        >
+          {isLoadingAgentsCount ? (
+            <FormattedMessage
+              id="xpack.ingestManager.deleteAgentConfigs.confirmModal.loadingAgentsCountMessage"
+              defaultMessage="Checking amount of affected agents…"
+            />
+          ) : agentsCount ? (
+            <FormattedMessage
+              id="xpack.ingestManager.deleteAgentConfigs.confirmModal.affectedAgentsMessage"
+              defaultMessage="{agentsCount, plural, one {# agent is} other {# agents are}} assigned {agentConfigsCount, plural, one {to this agent config} other {across these agentConfigs}}. {agentsCount, plural, one {This agent} other {These agents}} will be unenrolled."
+              values={{
+                agentsCount,
+                agentConfigsCount: agentConfigs.length,
+              }}
+            />
+          ) : (
+            <FormattedMessage
+              id="xpack.ingestManager.deleteAgentConfigs.confirmModal.noAffectedAgentsMessage"
+              defaultMessage="There are no agents assigned to {agentConfigsCount, plural, one {this agent config} other {these agentConfigs}}."
+              values={{
+                agentConfigsCount: agentConfigs.length,
+              }}
+            />
+          )}
+        </EuiConfirmModal>
+      </EuiOverlayMask>
+    );
+  };
+
+  return (
+    <Fragment>
+      {children(deleteAgentConfigsPrompt)}
+      {renderModal()}
+    </Fragment>
+  );
+};
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_form.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_form.tsx
new file mode 100644
index 0000000000000..5a25dc8bc92b5
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_form.tsx
@@ -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 React, { useState } from 'react';
+import { EuiFieldText, EuiForm, EuiFormRow } from '@elastic/eui';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { NewAgentConfig } from '../../../types';
+
+interface ValidationResults {
+  [key: string]: JSX.Element[];
+}
+
+export const agentConfigFormValidation = (
+  agentConfig: Partial<NewAgentConfig>
+): ValidationResults => {
+  const errors: ValidationResults = {};
+
+  if (!agentConfig.name?.trim()) {
+    errors.name = [
+      <FormattedMessage
+        id="xpack.ingestManager.agentConfigForm.nameRequiredErrorMessage"
+        defaultMessage="Agent config name is required"
+      />,
+    ];
+  }
+
+  return errors;
+};
+
+interface Props {
+  agentConfig: Partial<NewAgentConfig>;
+  updateAgentConfig: (u: Partial<NewAgentConfig>) => void;
+  validation: ValidationResults;
+}
+
+export const AgentConfigForm: React.FunctionComponent<Props> = ({
+  agentConfig,
+  updateAgentConfig,
+  validation,
+}) => {
+  const [touchedFields, setTouchedFields] = useState<{ [key: string]: boolean }>({});
+  const fields: Array<{ name: 'name' | 'description' | 'namespace'; label: JSX.Element }> = [
+    {
+      name: 'name',
+      label: (
+        <FormattedMessage
+          id="xpack.ingestManager.agentConfigForm.nameFieldLabel"
+          defaultMessage="Name"
+        />
+      ),
+    },
+    {
+      name: 'description',
+      label: (
+        <FormattedMessage
+          id="xpack.ingestManager.agentConfigForm.descriptionFieldLabel"
+          defaultMessage="Description"
+        />
+      ),
+    },
+    {
+      name: 'namespace',
+      label: (
+        <FormattedMessage
+          id="xpack.ingestManager.agentConfigForm.namespaceFieldLabel"
+          defaultMessage="Namespace"
+        />
+      ),
+    },
+  ];
+
+  return (
+    <EuiForm>
+      {fields.map(({ name, label }) => {
+        return (
+          <EuiFormRow
+            key={name}
+            label={label}
+            error={touchedFields[name] && validation[name] ? validation[name] : null}
+            isInvalid={Boolean(touchedFields[name] && validation[name])}
+          >
+            <EuiFieldText
+              value={agentConfig[name]}
+              onChange={e => updateAgentConfig({ [name]: e.target.value })}
+              isInvalid={Boolean(touchedFields[name] && validation[name])}
+              onBlur={() => setTouchedFields({ ...touchedFields, [name]: true })}
+            />
+          </EuiFormRow>
+        );
+      })}
+    </EuiForm>
+  );
+};
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/index.ts
new file mode 100644
index 0000000000000..d838221cd844e
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/index.ts
@@ -0,0 +1,8 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export { AgentConfigForm, agentConfigFormValidation } from './config_form';
+export { AgentConfigDeleteProvider } from './config_delete_provider';
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/index.tsx
new file mode 100644
index 0000000000000..c80c4496198be
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/index.tsx
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import React from 'react';
+import { HashRouter as Router, Switch, Route } from 'react-router-dom';
+import { AgentConfigListPage } from './list_page';
+
+export const AgentConfigApp: React.FunctionComponent = () => (
+  <Router>
+    <Switch>
+      <Route path="/">
+        <AgentConfigListPage />
+      </Route>
+    </Switch>
+  </Router>
+);
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/components/create_config.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/components/create_config.tsx
new file mode 100644
index 0000000000000..c6fea7b22bcd1
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/components/create_config.tsx
@@ -0,0 +1,143 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import React, { useState } from 'react';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import {
+  EuiFlyout,
+  EuiFlyoutHeader,
+  EuiTitle,
+  EuiFlyoutBody,
+  EuiFlyoutFooter,
+  EuiFlexGroup,
+  EuiFlexItem,
+  EuiButtonEmpty,
+  EuiButton,
+} from '@elastic/eui';
+import { NewAgentConfig } from '../../../../types';
+import { useCore, sendCreateAgentConfig } from '../../../../hooks';
+import { AgentConfigForm, agentConfigFormValidation } from '../../components';
+
+interface Props {
+  onClose: () => void;
+}
+
+export const CreateAgentConfigFlyout: React.FunctionComponent<Props> = ({ onClose }) => {
+  const { notifications } = useCore();
+
+  const [agentConfig, setAgentConfig] = useState<NewAgentConfig>({
+    name: '',
+    description: '',
+    namespace: '',
+  });
+  const [isLoading, setIsLoading] = useState<boolean>(false);
+  const validation = agentConfigFormValidation(agentConfig);
+
+  const updateAgentConfig = (updatedFields: Partial<NewAgentConfig>) => {
+    setAgentConfig({
+      ...agentConfig,
+      ...updatedFields,
+    });
+  };
+
+  const createAgentConfig = async () => {
+    return await sendCreateAgentConfig(agentConfig);
+  };
+
+  const header = (
+    <EuiFlyoutHeader hasBorder aria-labelledby="CreateAgentConfigFlyoutTitle">
+      <EuiTitle size="m">
+        <h2 id="CreateAgentConfigFlyoutTitle">
+          <FormattedMessage
+            id="xpack.ingestManager.createAgentConfig.flyoutTitle"
+            defaultMessage="Create new agent config"
+          />
+        </h2>
+      </EuiTitle>
+    </EuiFlyoutHeader>
+  );
+
+  const body = (
+    <EuiFlyoutBody>
+      <AgentConfigForm
+        agentConfig={agentConfig}
+        updateAgentConfig={updateAgentConfig}
+        validation={validation}
+      />
+    </EuiFlyoutBody>
+  );
+
+  const footer = (
+    <EuiFlyoutFooter>
+      <EuiFlexGroup justifyContent="spaceBetween">
+        <EuiFlexItem grow={false}>
+          <EuiButtonEmpty iconType="cross" onClick={onClose} flush="left">
+            <FormattedMessage
+              id="xpack.ingestManager.createAgentConfig.cancelButtonLabel"
+              defaultMessage="Cancel"
+            />
+          </EuiButtonEmpty>
+        </EuiFlexItem>
+        <EuiFlexItem grow={false}>
+          <EuiButton
+            fill
+            isLoading={isLoading}
+            disabled={isLoading || Object.keys(validation).length > 0}
+            onClick={async () => {
+              setIsLoading(true);
+              try {
+                const { data, error } = await createAgentConfig();
+                if (data?.success) {
+                  notifications.toasts.addSuccess(
+                    i18n.translate(
+                      'xpack.ingestManager.createAgentConfig.successNotificationTitle',
+                      {
+                        defaultMessage: "Agent config '{name}' created",
+                        values: { name: agentConfig.name },
+                      }
+                    )
+                  );
+                } else {
+                  notifications.toasts.addDanger(
+                    error
+                      ? error.message
+                      : i18n.translate(
+                          'xpack.ingestManager.createAgentConfig.errorNotificationTitle',
+                          {
+                            defaultMessage: 'Unable to create agent config',
+                          }
+                        )
+                  );
+                }
+              } catch (e) {
+                notifications.toasts.addDanger(
+                  i18n.translate('xpack.ingestManager.createAgentConfig.errorNotificationTitle', {
+                    defaultMessage: 'Unable to create agent config',
+                  })
+                );
+              }
+              setIsLoading(false);
+              onClose();
+            }}
+          >
+            <FormattedMessage
+              id="xpack.ingestManager.createAgentConfig.submitButtonLabel"
+              defaultMessage="Continue"
+            />
+          </EuiButton>
+        </EuiFlexItem>
+      </EuiFlexGroup>
+    </EuiFlyoutFooter>
+  );
+
+  return (
+    <EuiFlyout onClose={onClose} size="m" maxWidth={400}>
+      {header}
+      {body}
+      {footer}
+    </EuiFlyout>
+  );
+};
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/components/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/components/index.ts
new file mode 100644
index 0000000000000..43668b4ffb804
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/components/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 { CreateAgentConfigFlyout } from './create_config';
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/index.tsx
new file mode 100644
index 0000000000000..ca9fb195166f6
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/index.tsx
@@ -0,0 +1,280 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import React, { useState } from 'react';
+import {
+  EuiPageBody,
+  EuiPageContent,
+  EuiTitle,
+  EuiSpacer,
+  EuiText,
+  EuiFlexGroup,
+  EuiFlexItem,
+  EuiButton,
+  EuiEmptyPrompt,
+  // @ts-ignore
+  EuiSearchBar,
+  EuiBasicTable,
+  EuiLink,
+  EuiBadge,
+} from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { AgentConfig } from '../../../types';
+import { DEFAULT_AGENT_CONFIG_ID, AGENT_CONFIG_DETAILS_PATH } from '../../../constants';
+// import { SearchBar } from '../../../components';
+import { useGetAgentConfigs, usePagination, useLink } from '../../../hooks';
+import { AgentConfigDeleteProvider } from '../components';
+import { CreateAgentConfigFlyout } from './components';
+
+export const AgentConfigListPage: React.FunctionComponent<{}> = () => {
+  // Create agent config flyout state
+  const [isCreateAgentConfigFlyoutOpen, setIsCreateAgentConfigFlyoutOpen] = useState<boolean>(
+    false
+  );
+
+  // Table and search states
+  const [search, setSearch] = useState<string>('');
+  const { pagination, setPagination } = usePagination();
+  const [selectedAgentConfigs, setSelectedAgentConfigs] = useState<AgentConfig[]>([]);
+
+  // Fetch agent configs
+  const { isLoading, data: agentConfigData, sendRequest } = useGetAgentConfigs();
+
+  // Base path for config details
+  const DETAILS_URI = useLink(AGENT_CONFIG_DETAILS_PATH);
+
+  // Some configs retrieved, set up table props
+  const columns = [
+    {
+      field: 'name',
+      name: i18n.translate('xpack.ingestManager.agentConfigList.nameColumnTitle', {
+        defaultMessage: 'Name',
+      }),
+      render: (name: string, agentConfig: AgentConfig) => name || agentConfig.id,
+    },
+    {
+      field: 'namespace',
+      name: i18n.translate('xpack.ingestManager.agentConfigList.namespaceColumnTitle', {
+        defaultMessage: 'Namespace',
+      }),
+      render: (namespace: string) => (namespace ? <EuiBadge>{namespace}</EuiBadge> : null),
+    },
+    {
+      field: 'description',
+      name: i18n.translate('xpack.ingestManager.agentConfigList.descriptionColumnTitle', {
+        defaultMessage: 'Description',
+      }),
+    },
+    {
+      field: 'datasources',
+      name: i18n.translate('xpack.ingestManager.agentConfigList.datasourcesCountColumnTitle', {
+        defaultMessage: 'Datasources',
+      }),
+      render: (datasources: AgentConfig['datasources']) => (datasources ? datasources.length : 0),
+    },
+    {
+      name: i18n.translate('xpack.ingestManager.agentConfigList.actionsColumnTitle', {
+        defaultMessage: 'Actions',
+      }),
+      actions: [
+        {
+          render: ({ id }: AgentConfig) => {
+            return (
+              <EuiLink href={`${DETAILS_URI}${id}`}>
+                <FormattedMessage
+                  id="xpack.ingestManager.agentConfigList.viewActionLinkText"
+                  defaultMessage="view"
+                />
+              </EuiLink>
+            );
+          },
+        },
+      ],
+      width: '100px',
+    },
+  ];
+
+  const emptyPrompt = (
+    <EuiEmptyPrompt
+      title={
+        <h2>
+          <FormattedMessage
+            id="xpack.ingestManager.agentConfigList.noAgentConfigsPrompt"
+            defaultMessage="No agent configurations"
+          />
+        </h2>
+      }
+      actions={
+        <EuiButton
+          fill
+          iconType="plusInCircle"
+          onClick={() => setIsCreateAgentConfigFlyoutOpen(true)}
+        >
+          <FormattedMessage
+            id="xpack.ingestManager.agentConfigList.addButton"
+            defaultMessage="Create new agent configuration"
+          />
+        </EuiButton>
+      }
+    />
+  );
+
+  return (
+    <EuiPageBody>
+      <EuiPageContent>
+        {isCreateAgentConfigFlyoutOpen ? (
+          <CreateAgentConfigFlyout
+            onClose={() => {
+              setIsCreateAgentConfigFlyoutOpen(false);
+              sendRequest();
+            }}
+          />
+        ) : null}
+
+        <EuiTitle size="l">
+          <h1>
+            <FormattedMessage
+              id="xpack.ingestManager.agentConfigList.pageTitle"
+              defaultMessage="Agent configurations"
+            />
+          </h1>
+        </EuiTitle>
+        <EuiSpacer size="s" />
+        <EuiFlexGroup alignItems={'center'} justifyContent={'spaceBetween'}>
+          <EuiFlexItem grow={false}>
+            <EuiTitle size="s">
+              <EuiText color="subdued">
+                <FormattedMessage
+                  id="xpack.ingestManager.agentConfigList.pageDescription"
+                  defaultMessage="Lorem ipsum"
+                />
+              </EuiText>
+            </EuiTitle>
+          </EuiFlexItem>
+        </EuiFlexGroup>
+        <EuiSpacer size="m" />
+
+        <EuiFlexGroup alignItems={'center'} gutterSize="m">
+          {selectedAgentConfigs.length ? (
+            <EuiFlexItem>
+              <AgentConfigDeleteProvider>
+                {deleteAgentConfigsPrompt => (
+                  <EuiButton
+                    color="danger"
+                    onClick={() => {
+                      deleteAgentConfigsPrompt(
+                        selectedAgentConfigs.map(agentConfig => agentConfig.id),
+                        () => {
+                          sendRequest();
+                          setSelectedAgentConfigs([]);
+                        }
+                      );
+                    }}
+                  >
+                    <FormattedMessage
+                      id="xpack.ingestManager.agentConfigList.deleteButton"
+                      defaultMessage="Delete {count, plural, one {# agent config} other {# agent configs}}"
+                      values={{
+                        count: selectedAgentConfigs.length,
+                      }}
+                    />
+                  </EuiButton>
+                )}
+              </AgentConfigDeleteProvider>
+            </EuiFlexItem>
+          ) : null}
+          <EuiFlexItem grow={4}>
+            {/* <SearchBar
+              value={search}
+              onChange={newSearch => {
+                setPagination({
+                  ...pagination,
+                  currentPage: 1,
+                });
+                setSearch(newSearch);
+              }}
+              fieldPrefix={AGENT_CONFIG_SAVED_OBJECT_TYPE}
+            /> */}
+          </EuiFlexItem>
+          <EuiFlexItem grow={false}>
+            <EuiButton color="secondary" iconType="refresh" onClick={() => sendRequest()}>
+              <FormattedMessage
+                id="xpack.ingestManager.agentConfigList.reloadAgentConfigsButtonText"
+                defaultMessage="Reload"
+              />
+            </EuiButton>
+          </EuiFlexItem>
+          <EuiFlexItem grow={false}>
+            <EuiButton
+              fill
+              iconType="plusInCircle"
+              onClick={() => setIsCreateAgentConfigFlyoutOpen(true)}
+            >
+              <FormattedMessage
+                id="xpack.ingestManager.agentConfigList.addButton"
+                defaultMessage="Create new agent configuration"
+              />
+            </EuiButton>
+          </EuiFlexItem>
+        </EuiFlexGroup>
+
+        <EuiSpacer size="m" />
+        <EuiBasicTable
+          loading={isLoading}
+          noItemsMessage={
+            isLoading ? (
+              <FormattedMessage
+                id="xpack.ingestManager.agentConfigList.loadingAgentConfigsMessage"
+                defaultMessage="Loading agent configurations…"
+              />
+            ) : !search.trim() && agentConfigData?.total === 0 ? (
+              emptyPrompt
+            ) : (
+              <FormattedMessage
+                id="xpack.ingestManager.agentConfigList.noFilteredAgentConfigsPrompt"
+                defaultMessage="No agent configurations found. {clearFiltersLink}"
+                values={{
+                  clearFiltersLink: (
+                    <EuiLink onClick={() => setSearch('')}>
+                      <FormattedMessage
+                        id="xpack.ingestManager.agentConfigList.clearFiltersLinkText"
+                        defaultMessage="Clear filters"
+                      />
+                    </EuiLink>
+                  ),
+                }}
+              />
+            )
+          }
+          items={agentConfigData ? agentConfigData.items : []}
+          itemId="id"
+          columns={columns}
+          isSelectable={true}
+          selection={{
+            selectable: (agentConfig: AgentConfig) => agentConfig.id !== DEFAULT_AGENT_CONFIG_ID,
+            onSelectionChange: (newSelectedAgentConfigs: AgentConfig[]) => {
+              setSelectedAgentConfigs(newSelectedAgentConfigs);
+            },
+          }}
+          pagination={{
+            pageIndex: pagination.currentPage - 1,
+            pageSize: pagination.pageSize,
+            totalItemCount: agentConfigData ? agentConfigData.total : 0,
+          }}
+          onChange={({ page }: { page: { index: number; size: number } }) => {
+            const newPagination = {
+              ...pagination,
+              currentPage: page.index + 1,
+              pageSize: page.size,
+            };
+            setPagination(newPagination);
+            sendRequest(); // todo: fix this to send pagination options
+          }}
+        />
+      </EuiPageContent>
+    </EuiPageBody>
+  );
+};
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/index.tsx
new file mode 100644
index 0000000000000..ca8c22be9c34c
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/index.tsx
@@ -0,0 +1,13 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { useConfig } from '../../hooks';
+
+export const EPMApp: React.FunctionComponent = () => {
+  const { epm } = useConfig();
+  return epm.enabled ? <div>hello world - epm app</div> : null;
+};
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/index.tsx
new file mode 100644
index 0000000000000..978414769004d
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/index.tsx
@@ -0,0 +1,12 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import React from 'react';
+import { useConfig } from '../../hooks';
+
+export const FleetApp: React.FunctionComponent = () => {
+  const { fleet } = useConfig();
+  return fleet.enabled ? <div>hello world - fleet app</div> : null;
+};
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/index.tsx
new file mode 100644
index 0000000000000..c691bb609d435
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/index.tsx
@@ -0,0 +1,11 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+export { IngestManagerOverview } from './overview';
+export { EPMApp } from './epm';
+export { AgentConfigApp } from './agent_config';
+export { FleetApp } from './fleet';
+
+export type Section = 'overview' | 'epm' | 'agent_config' | 'fleet';
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx
new file mode 100644
index 0000000000000..da4a78a39e2fe
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx
@@ -0,0 +1,10 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import React from 'react';
+
+export const IngestManagerOverview: React.FunctionComponent = () => {
+  return <div>Ingest manager overview page</div>;
+};
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/services/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/services/index.ts
new file mode 100644
index 0000000000000..6502b0fff7123
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/services/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 { agentConfigRouteService } from '../../../../common';
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts
new file mode 100644
index 0000000000000..8597d6fd59323
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.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.
+ */
+export {
+  // Object types
+  AgentConfig,
+  NewAgentConfig,
+  // API schemas
+  GetAgentConfigsResponse,
+  GetOneAgentConfigResponse,
+  CreateAgentConfigRequestSchema,
+  CreateAgentConfigResponse,
+  UpdateAgentConfigRequestSchema,
+  UpdateAgentConfigResponse,
+  DeleteAgentConfigsRequestSchema,
+  DeleteAgentConfigsResponse,
+} from '../../../../common';
diff --git a/x-pack/plugins/ingest_manager/public/index.ts b/x-pack/plugins/ingest_manager/public/index.ts
new file mode 100644
index 0000000000000..a9e40a2a42302
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/index.ts
@@ -0,0 +1,11 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import { PluginInitializerContext } from 'kibana/public';
+import { IngestManagerPlugin } from './plugin';
+
+export const plugin = (initializerContext: PluginInitializerContext) => {
+  return new IngestManagerPlugin(initializerContext);
+};
diff --git a/x-pack/plugins/ingest_manager/public/plugin.ts b/x-pack/plugins/ingest_manager/public/plugin.ts
new file mode 100644
index 0000000000000..ae244e7ebec3d
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/plugin.ts
@@ -0,0 +1,57 @@
+/*
+ * 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 {
+  AppMountParameters,
+  CoreSetup,
+  CoreStart,
+  Plugin,
+  PluginInitializerContext,
+} from 'kibana/public';
+import { i18n } from '@kbn/i18n';
+import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils';
+import { DataPublicPluginSetup } from '../../../../src/plugins/data/public';
+import { LicensingPluginSetup } from '../../licensing/public';
+import { PLUGIN_ID } from '../common/constants';
+import { IngestManagerConfigType } from '../common/types';
+
+export { IngestManagerConfigType } from '../common/types';
+
+export type IngestManagerSetup = void;
+export type IngestManagerStart = void;
+
+export interface IngestManagerSetupDeps {
+  licensing: LicensingPluginSetup;
+  data: DataPublicPluginSetup;
+}
+
+export class IngestManagerPlugin implements Plugin {
+  private config: IngestManagerConfigType;
+
+  constructor(private readonly initializerContext: PluginInitializerContext) {
+    this.config = this.initializerContext.config.get<IngestManagerConfigType>();
+  }
+
+  public setup(core: CoreSetup, deps: IngestManagerSetupDeps) {
+    const config = this.config;
+
+    // Register main Ingest Manager app
+    core.application.register({
+      id: PLUGIN_ID,
+      category: DEFAULT_APP_CATEGORIES.management,
+      title: i18n.translate('xpack.ingestManager.appTitle', { defaultMessage: 'Ingest Manager' }),
+      euiIconType: 'savedObjectsApp',
+      async mount(params: AppMountParameters) {
+        const [coreStart] = await core.getStartServices();
+        const { renderApp } = await import('./applications/ingest_manager');
+        return renderApp(coreStart, params, deps, config);
+      },
+    });
+  }
+
+  public start(core: CoreStart) {}
+
+  public stop() {}
+}
diff --git a/x-pack/plugins/ingest_manager/server/constants/index.ts b/x-pack/plugins/ingest_manager/server/constants/index.ts
new file mode 100644
index 0000000000000..6b54afa1d81cb
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/constants/index.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.
+ */
+export {
+  // Routes
+  PLUGIN_ID,
+  EPM_API_ROUTES,
+  DATASOURCE_API_ROUTES,
+  AGENT_CONFIG_API_ROUTES,
+  FLEET_SETUP_API_ROUTES,
+  // Saved object types
+  AGENT_CONFIG_SAVED_OBJECT_TYPE,
+  DATASOURCE_SAVED_OBJECT_TYPE,
+  OUTPUT_SAVED_OBJECT_TYPE,
+  // Defaults
+  DEFAULT_AGENT_CONFIG_ID,
+  DEFAULT_AGENT_CONFIG,
+  DEFAULT_OUTPUT_ID,
+  DEFAULT_OUTPUT,
+} from '../../common';
diff --git a/x-pack/plugins/ingest_manager/server/index.ts b/x-pack/plugins/ingest_manager/server/index.ts
new file mode 100644
index 0000000000000..5228f1e0e3469
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/index.ts
@@ -0,0 +1,43 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { schema } from '@kbn/config-schema';
+import { PluginInitializerContext } from 'kibana/server';
+import { IngestManagerPlugin } from './plugin';
+
+export const config = {
+  exposeToBrowser: {
+    epm: true,
+    fleet: true,
+  },
+  schema: schema.object({
+    enabled: schema.boolean({ defaultValue: false }),
+    epm: schema.object({
+      enabled: schema.boolean({ defaultValue: false }),
+      registryUrl: schema.uri({ defaultValue: 'https://epr-staging.elastic.co' }),
+    }),
+    fleet: schema.object({
+      enabled: schema.boolean({ defaultValue: false }),
+      defaultOutputHost: schema.string({ defaultValue: 'http://localhost:9200' }),
+    }),
+  }),
+};
+
+export const plugin = (initializerContext: PluginInitializerContext) => {
+  return new IngestManagerPlugin(initializerContext);
+};
+
+// Saved object information bootstrapped by legacy `ingest_manager` plugin
+// TODO: Remove once saved object mappings can be done from NP
+export { savedObjectMappings } from './saved_objects';
+export {
+  OUTPUT_SAVED_OBJECT_TYPE,
+  AGENT_CONFIG_SAVED_OBJECT_TYPE,
+  DATASOURCE_SAVED_OBJECT_TYPE,
+} from './constants';
+
+// TODO: Temporary exports for Fleet dependencies, remove once Fleet moved into this plugin
+export { agentConfigService, outputService } from './services';
diff --git a/x-pack/plugins/ingest_manager/server/integration_tests/router.test.ts b/x-pack/plugins/ingest_manager/server/integration_tests/router.test.ts
new file mode 100644
index 0000000000000..38976957173f4
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/integration_tests/router.test.ts
@@ -0,0 +1,190 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { resolve } from 'path';
+import * as kbnTestServer from '../../../../../src/test_utils/kbn_server';
+
+function createXPackRoot(config: {} = {}) {
+  return kbnTestServer.createRoot({
+    plugins: {
+      scanDirs: [],
+      paths: [
+        resolve(__dirname, '../../../../../x-pack/plugins/encrypted_saved_objects'),
+        resolve(__dirname, '../../../../../x-pack/plugins/ingest_manager'),
+        resolve(__dirname, '../../../../../x-pack/plugins/licensing'),
+      ],
+    },
+    migrations: { skip: true },
+    xpack: config,
+  });
+}
+
+describe('ingestManager', () => {
+  describe('default. manager, EPM, and Fleet all disabled', () => {
+    let root: ReturnType<typeof kbnTestServer.createRoot>;
+    beforeAll(async () => {
+      root = createXPackRoot();
+      await root.setup();
+      await root.start();
+    }, 30000);
+
+    afterAll(async () => await root.shutdown());
+
+    it('does not have agent config api', async () => {
+      await kbnTestServer.request.get(root, '/api/ingest_manager/agent_configs').expect(404);
+    });
+
+    it('does not have datasources api', async () => {
+      await kbnTestServer.request.get(root, '/api/ingest_manager/datasources').expect(404);
+    });
+
+    it('does not have EPM api', async () => {
+      await kbnTestServer.request.get(root, '/api/ingest_manager/epm').expect(404);
+    });
+
+    it('does not have Fleet api', async () => {
+      await kbnTestServer.request.get(root, '/api/ingest_manager/fleet').expect(404);
+    });
+  });
+
+  describe('manager only (no EPM, no Fleet)', () => {
+    let root: ReturnType<typeof kbnTestServer.createRoot>;
+    beforeAll(async () => {
+      const ingestManagerConfig = {
+        enabled: true,
+      };
+      root = createXPackRoot({
+        ingestManager: ingestManagerConfig,
+      });
+      await root.setup();
+      await root.start();
+    }, 30000);
+
+    afterAll(async () => await root.shutdown());
+
+    it('has agent config api', async () => {
+      await kbnTestServer.request.get(root, '/api/ingest_manager/agent_configs').expect(200);
+    });
+
+    it('has datasources api', async () => {
+      await kbnTestServer.request.get(root, '/api/ingest_manager/datasources').expect(200);
+    });
+
+    it('does not have EPM api', async () => {
+      await kbnTestServer.request.get(root, '/api/ingest_manager/epm/list').expect(404);
+    });
+
+    it('does not have Fleet api', async () => {
+      await kbnTestServer.request.get(root, '/api/ingest_manager/fleet/setup').expect(404);
+    });
+  });
+
+  // For now, only the manager routes (/agent_configs & /datasources) are added
+  // EPM and ingest will be conditionally added when we enable these lines
+  // https://github.com/jfsiii/kibana/blob/f73b54ebb7e0f6fc00efd8a6800a01eb2d9fb772/x-pack/plugins/ingest_manager/server/plugin.ts#L84
+  // adding tests to confirm the Fleet & EPM routes are never added
+
+  describe('manager and EPM; no Fleet', () => {
+    let root: ReturnType<typeof kbnTestServer.createRoot>;
+    beforeAll(async () => {
+      const ingestManagerConfig = {
+        enabled: true,
+        epm: { enabled: true },
+      };
+      root = createXPackRoot({
+        ingestManager: ingestManagerConfig,
+      });
+      await root.setup();
+      await root.start();
+    }, 30000);
+
+    afterAll(async () => await root.shutdown());
+
+    it('has agent config api', async () => {
+      await kbnTestServer.request.get(root, '/api/ingest_manager/agent_configs').expect(200);
+    });
+
+    it('has datasources api', async () => {
+      await kbnTestServer.request.get(root, '/api/ingest_manager/datasources').expect(200);
+    });
+
+    it('does not have EPM api', async () => {
+      await kbnTestServer.request.get(root, '/api/ingest_manager/epm/list').expect(404);
+    });
+
+    it('does not have Fleet api', async () => {
+      await kbnTestServer.request.get(root, '/api/ingest_manager/fleet/setup').expect(404);
+    });
+  });
+
+  describe('manager and Fleet; no EPM)', () => {
+    let root: ReturnType<typeof kbnTestServer.createRoot>;
+    beforeAll(async () => {
+      const ingestManagerConfig = {
+        enabled: true,
+        epm: { enabled: true },
+        fleet: { enabled: true },
+      };
+      root = createXPackRoot({
+        ingestManager: ingestManagerConfig,
+      });
+      await root.setup();
+      await root.start();
+    }, 30000);
+
+    afterAll(async () => await root.shutdown());
+
+    it('has agent config api', async () => {
+      await kbnTestServer.request.get(root, '/api/ingest_manager/agent_configs').expect(200);
+    });
+
+    it('has datasources api', async () => {
+      await kbnTestServer.request.get(root, '/api/ingest_manager/datasources').expect(200);
+    });
+
+    it('does not have EPM api', async () => {
+      await kbnTestServer.request.get(root, '/api/ingest_manager/epm/list').expect(404);
+    });
+
+    it('does not have Fleet api', async () => {
+      await kbnTestServer.request.get(root, '/api/ingest_manager/fleet/setup').expect(404);
+    });
+  });
+
+  describe('all flags enabled: manager, EPM, and Fleet)', () => {
+    let root: ReturnType<typeof kbnTestServer.createRoot>;
+    beforeAll(async () => {
+      const ingestManagerConfig = {
+        enabled: true,
+        epm: { enabled: true },
+        fleet: { enabled: true },
+      };
+      root = createXPackRoot({
+        ingestManager: ingestManagerConfig,
+      });
+      await root.setup();
+      await root.start();
+    }, 30000);
+
+    afterAll(async () => await root.shutdown());
+
+    it('has agent config api', async () => {
+      await kbnTestServer.request.get(root, '/api/ingest_manager/agent_configs').expect(200);
+    });
+
+    it('has datasources api', async () => {
+      await kbnTestServer.request.get(root, '/api/ingest_manager/datasources').expect(200);
+    });
+
+    it('does not have EPM api', async () => {
+      await kbnTestServer.request.get(root, '/api/ingest_manager/epm/list').expect(404);
+    });
+
+    it('does not have Fleet api', async () => {
+      await kbnTestServer.request.get(root, '/api/ingest_manager/fleet/setup').expect(404);
+    });
+  });
+});
diff --git a/x-pack/plugins/ingest_manager/server/plugin.ts b/x-pack/plugins/ingest_manager/server/plugin.ts
new file mode 100644
index 0000000000000..4f30a171ab0c0
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/plugin.ts
@@ -0,0 +1,102 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import { Observable } from 'rxjs';
+import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'kibana/server';
+import { LicensingPluginSetup } from '../../licensing/server';
+import { EncryptedSavedObjectsPluginStart } from '../../encrypted_saved_objects/server';
+import { SecurityPluginSetup } from '../../security/server';
+import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server';
+import { PLUGIN_ID } from './constants';
+import { appContextService } from './services';
+import { registerDatasourceRoutes, registerAgentConfigRoutes } from './routes';
+import { IngestManagerConfigType } from '../common';
+
+export interface IngestManagerSetupDeps {
+  licensing: LicensingPluginSetup;
+  security?: SecurityPluginSetup;
+  features?: FeaturesPluginSetup;
+}
+
+export interface IngestManagerAppContext {
+  encryptedSavedObjects: EncryptedSavedObjectsPluginStart;
+  security?: SecurityPluginSetup;
+  config$?: Observable<IngestManagerConfigType>;
+}
+
+export class IngestManagerPlugin implements Plugin {
+  private config$: Observable<IngestManagerConfigType>;
+  private security: SecurityPluginSetup | undefined;
+
+  constructor(private readonly initializerContext: PluginInitializerContext) {
+    this.config$ = this.initializerContext.config.create<IngestManagerConfigType>();
+  }
+
+  public async setup(core: CoreSetup, deps: IngestManagerSetupDeps) {
+    if (deps.security) {
+      this.security = deps.security;
+    }
+
+    // Register feature
+    // TODO: Flesh out privileges
+    if (deps.features) {
+      deps.features.registerFeature({
+        id: PLUGIN_ID,
+        name: 'Ingest Manager',
+        icon: 'savedObjectsApp',
+        navLinkId: PLUGIN_ID,
+        app: [PLUGIN_ID, 'kibana'],
+        privileges: {
+          all: {
+            api: [PLUGIN_ID],
+            savedObject: {
+              all: [],
+              read: [],
+            },
+            ui: ['show'],
+          },
+          read: {
+            api: [PLUGIN_ID],
+            savedObject: {
+              all: [],
+              read: [],
+            },
+            ui: ['show'],
+          },
+        },
+      });
+    }
+
+    // Create router
+    const router = core.http.createRouter();
+
+    // Register routes
+    registerAgentConfigRoutes(router);
+    registerDatasourceRoutes(router);
+
+    // Optional route registration depending on Kibana config
+    // restore when EPM & Fleet features are added
+    // const config = await this.config$.pipe(first()).toPromise();
+    // if (config.epm.enabled) registerEPMRoutes(router);
+    // if (config.fleet.enabled) registerFleetSetupRoutes(router);
+  }
+
+  public async start(
+    core: CoreStart,
+    plugins: {
+      encryptedSavedObjects: EncryptedSavedObjectsPluginStart;
+    }
+  ) {
+    appContextService.start({
+      encryptedSavedObjects: plugins.encryptedSavedObjects,
+      security: this.security,
+      config$: this.config$,
+    });
+  }
+
+  public async stop() {
+    appContextService.stop();
+  }
+}
diff --git a/x-pack/plugins/ingest_manager/server/routes/agent_config/handlers.ts b/x-pack/plugins/ingest_manager/server/routes/agent_config/handlers.ts
new file mode 100644
index 0000000000000..67da6a4cf2f1d
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/routes/agent_config/handlers.ts
@@ -0,0 +1,144 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import { TypeOf } from '@kbn/config-schema';
+import { RequestHandler } from 'kibana/server';
+import { appContextService, agentConfigService } from '../../services';
+import {
+  GetAgentConfigsRequestSchema,
+  GetAgentConfigsResponse,
+  GetOneAgentConfigRequestSchema,
+  GetOneAgentConfigResponse,
+  CreateAgentConfigRequestSchema,
+  CreateAgentConfigResponse,
+  UpdateAgentConfigRequestSchema,
+  UpdateAgentConfigResponse,
+  DeleteAgentConfigsRequestSchema,
+  DeleteAgentConfigsResponse,
+} from '../../types';
+
+export const getAgentConfigsHandler: RequestHandler<
+  undefined,
+  TypeOf<typeof GetAgentConfigsRequestSchema.query>
+> = async (context, request, response) => {
+  const soClient = context.core.savedObjects.client;
+  try {
+    const { items, total, page, perPage } = await agentConfigService.list(soClient, request.query);
+    const body: GetAgentConfigsResponse = {
+      items,
+      total,
+      page,
+      perPage,
+      success: true,
+    };
+    return response.ok({ body });
+  } catch (e) {
+    return response.customError({
+      statusCode: 500,
+      body: { message: e.message },
+    });
+  }
+};
+
+export const getOneAgentConfigHandler: RequestHandler<TypeOf<
+  typeof GetOneAgentConfigRequestSchema.params
+>> = async (context, request, response) => {
+  const soClient = context.core.savedObjects.client;
+  try {
+    const agentConfig = await agentConfigService.get(soClient, request.params.agentConfigId);
+    if (agentConfig) {
+      const body: GetOneAgentConfigResponse = {
+        item: agentConfig,
+        success: true,
+      };
+      return response.ok({
+        body,
+      });
+    } else {
+      return response.customError({
+        statusCode: 404,
+        body: { message: 'Agent config not found' },
+      });
+    }
+  } catch (e) {
+    return response.customError({
+      statusCode: 500,
+      body: { message: e.message },
+    });
+  }
+};
+
+export const createAgentConfigHandler: RequestHandler<
+  undefined,
+  undefined,
+  TypeOf<typeof CreateAgentConfigRequestSchema.body>
+> = async (context, request, response) => {
+  const soClient = context.core.savedObjects.client;
+  const user = await appContextService.getSecurity()?.authc.getCurrentUser(request);
+  try {
+    const agentConfig = await agentConfigService.create(soClient, request.body, {
+      user: user || undefined,
+    });
+    const body: CreateAgentConfigResponse = { item: agentConfig, success: true };
+    return response.ok({
+      body,
+    });
+  } catch (e) {
+    return response.customError({
+      statusCode: 500,
+      body: { message: e.message },
+    });
+  }
+};
+
+export const updateAgentConfigHandler: RequestHandler<
+  TypeOf<typeof UpdateAgentConfigRequestSchema.params>,
+  unknown,
+  TypeOf<typeof UpdateAgentConfigRequestSchema.body>
+> = async (context, request, response) => {
+  const soClient = context.core.savedObjects.client;
+  const user = await appContextService.getSecurity()?.authc.getCurrentUser(request);
+  try {
+    const agentConfig = await agentConfigService.update(
+      soClient,
+      request.params.agentConfigId,
+      request.body,
+      {
+        user: user || undefined,
+      }
+    );
+    const body: UpdateAgentConfigResponse = { item: agentConfig, success: true };
+    return response.ok({
+      body,
+    });
+  } catch (e) {
+    return response.customError({
+      statusCode: 500,
+      body: { message: e.message },
+    });
+  }
+};
+
+export const deleteAgentConfigsHandler: RequestHandler<
+  unknown,
+  unknown,
+  TypeOf<typeof DeleteAgentConfigsRequestSchema.body>
+> = async (context, request, response) => {
+  const soClient = context.core.savedObjects.client;
+  try {
+    const body: DeleteAgentConfigsResponse = await agentConfigService.delete(
+      soClient,
+      request.body.agentConfigIds
+    );
+    return response.ok({
+      body,
+    });
+  } catch (e) {
+    return response.customError({
+      statusCode: 500,
+      body: { message: e.message },
+    });
+  }
+};
diff --git a/x-pack/plugins/ingest_manager/server/routes/agent_config/index.ts b/x-pack/plugins/ingest_manager/server/routes/agent_config/index.ts
new file mode 100644
index 0000000000000..67ad915b71e45
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/routes/agent_config/index.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 { PLUGIN_ID, AGENT_CONFIG_API_ROUTES } from '../../constants';
+import {
+  GetAgentConfigsRequestSchema,
+  GetOneAgentConfigRequestSchema,
+  CreateAgentConfigRequestSchema,
+  UpdateAgentConfigRequestSchema,
+  DeleteAgentConfigsRequestSchema,
+} from '../../types';
+import {
+  getAgentConfigsHandler,
+  getOneAgentConfigHandler,
+  createAgentConfigHandler,
+  updateAgentConfigHandler,
+  deleteAgentConfigsHandler,
+} from './handlers';
+
+export const registerRoutes = (router: IRouter) => {
+  // List
+  router.get(
+    {
+      path: AGENT_CONFIG_API_ROUTES.LIST_PATTERN,
+      validate: GetAgentConfigsRequestSchema,
+      options: { tags: [`access:${PLUGIN_ID}`] },
+    },
+    getAgentConfigsHandler
+  );
+
+  // Get one
+  router.get(
+    {
+      path: AGENT_CONFIG_API_ROUTES.INFO_PATTERN,
+      validate: GetOneAgentConfigRequestSchema,
+      options: { tags: [`access:${PLUGIN_ID}`] },
+    },
+    getOneAgentConfigHandler
+  );
+
+  // Create
+  router.post(
+    {
+      path: AGENT_CONFIG_API_ROUTES.CREATE_PATTERN,
+      validate: CreateAgentConfigRequestSchema,
+      options: { tags: [`access:${PLUGIN_ID}`] },
+    },
+    createAgentConfigHandler
+  );
+
+  // Update
+  router.put(
+    {
+      path: AGENT_CONFIG_API_ROUTES.UPDATE_PATTERN,
+      validate: UpdateAgentConfigRequestSchema,
+      options: { tags: [`access:${PLUGIN_ID}`] },
+    },
+    updateAgentConfigHandler
+  );
+
+  // Delete
+  router.post(
+    {
+      path: AGENT_CONFIG_API_ROUTES.DELETE_PATTERN,
+      validate: DeleteAgentConfigsRequestSchema,
+      options: { tags: [`access:${PLUGIN_ID}`] },
+    },
+    deleteAgentConfigsHandler
+  );
+};
diff --git a/x-pack/plugins/ingest_manager/server/routes/datasource/handlers.ts b/x-pack/plugins/ingest_manager/server/routes/datasource/handlers.ts
new file mode 100644
index 0000000000000..78cad2e21c5fa
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/routes/datasource/handlers.ts
@@ -0,0 +1,131 @@
+/*
+ * 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 { TypeOf } from '@kbn/config-schema';
+import { RequestHandler } from 'kibana/server';
+import { datasourceService } from '../../services';
+import {
+  GetDatasourcesRequestSchema,
+  GetOneDatasourceRequestSchema,
+  CreateDatasourceRequestSchema,
+  UpdateDatasourceRequestSchema,
+  DeleteDatasourcesRequestSchema,
+  DeleteDatasourcesResponse,
+} from '../../types';
+
+export const getDatasourcesHandler: RequestHandler<
+  undefined,
+  TypeOf<typeof GetDatasourcesRequestSchema.query>
+> = async (context, request, response) => {
+  const soClient = context.core.savedObjects.client;
+  try {
+    const { items, total, page, perPage } = await datasourceService.list(soClient, request.query);
+    return response.ok({
+      body: {
+        items,
+        total,
+        page,
+        perPage,
+        success: true,
+      },
+    });
+  } catch (e) {
+    return response.customError({
+      statusCode: 500,
+      body: { message: e.message },
+    });
+  }
+};
+
+export const getOneDatasourceHandler: RequestHandler<TypeOf<
+  typeof GetOneDatasourceRequestSchema.params
+>> = async (context, request, response) => {
+  const soClient = context.core.savedObjects.client;
+  try {
+    const datasource = await datasourceService.get(soClient, request.params.datasourceId);
+    if (datasource) {
+      return response.ok({
+        body: {
+          item: datasource,
+          success: true,
+        },
+      });
+    } else {
+      return response.customError({
+        statusCode: 404,
+        body: { message: 'Datasource not found' },
+      });
+    }
+  } catch (e) {
+    return response.customError({
+      statusCode: 500,
+      body: { message: e.message },
+    });
+  }
+};
+
+export const createDatasourceHandler: RequestHandler<
+  undefined,
+  undefined,
+  TypeOf<typeof CreateDatasourceRequestSchema.body>
+> = async (context, request, response) => {
+  const soClient = context.core.savedObjects.client;
+  try {
+    const datasource = await datasourceService.create(soClient, request.body);
+    return response.ok({
+      body: { item: datasource, success: true },
+    });
+  } catch (e) {
+    return response.customError({
+      statusCode: 500,
+      body: { message: e.message },
+    });
+  }
+};
+
+export const updateDatasourceHandler: RequestHandler<
+  TypeOf<typeof UpdateDatasourceRequestSchema.params>,
+  unknown,
+  TypeOf<typeof UpdateDatasourceRequestSchema.body>
+> = async (context, request, response) => {
+  const soClient = context.core.savedObjects.client;
+  try {
+    const datasource = await datasourceService.update(
+      soClient,
+      request.params.datasourceId,
+      request.body
+    );
+    return response.ok({
+      body: { item: datasource, success: true },
+    });
+  } catch (e) {
+    return response.customError({
+      statusCode: 500,
+      body: { message: e.message },
+    });
+  }
+};
+
+export const deleteDatasourcesHandler: RequestHandler<
+  unknown,
+  unknown,
+  TypeOf<typeof DeleteDatasourcesRequestSchema.body>
+> = async (context, request, response) => {
+  const soClient = context.core.savedObjects.client;
+  try {
+    const body: DeleteDatasourcesResponse = await datasourceService.delete(
+      soClient,
+      request.body.datasourceIds
+    );
+    return response.ok({
+      body,
+    });
+  } catch (e) {
+    return response.customError({
+      statusCode: 500,
+      body: { message: e.message },
+    });
+  }
+};
diff --git a/x-pack/plugins/ingest_manager/server/routes/datasource/index.ts b/x-pack/plugins/ingest_manager/server/routes/datasource/index.ts
new file mode 100644
index 0000000000000..d9e3ba9de8838
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/routes/datasource/index.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 { PLUGIN_ID, DATASOURCE_API_ROUTES } from '../../constants';
+import {
+  GetDatasourcesRequestSchema,
+  GetOneDatasourceRequestSchema,
+  CreateDatasourceRequestSchema,
+  UpdateDatasourceRequestSchema,
+  DeleteDatasourcesRequestSchema,
+} from '../../types';
+import {
+  getDatasourcesHandler,
+  getOneDatasourceHandler,
+  createDatasourceHandler,
+  updateDatasourceHandler,
+  deleteDatasourcesHandler,
+} from './handlers';
+
+export const registerRoutes = (router: IRouter) => {
+  // List
+  router.get(
+    {
+      path: DATASOURCE_API_ROUTES.LIST_PATTERN,
+      validate: GetDatasourcesRequestSchema,
+      options: { tags: [`access:${PLUGIN_ID}`] },
+    },
+    getDatasourcesHandler
+  );
+
+  // Get one
+  router.get(
+    {
+      path: DATASOURCE_API_ROUTES.INFO_PATTERN,
+      validate: GetOneDatasourceRequestSchema,
+      options: { tags: [`access:${PLUGIN_ID}`] },
+    },
+    getOneDatasourceHandler
+  );
+
+  // Create
+  router.post(
+    {
+      path: DATASOURCE_API_ROUTES.CREATE_PATTERN,
+      validate: CreateDatasourceRequestSchema,
+      options: { tags: [`access:${PLUGIN_ID}`] },
+    },
+    createDatasourceHandler
+  );
+
+  // Update
+  router.put(
+    {
+      path: DATASOURCE_API_ROUTES.UPDATE_PATTERN,
+      validate: UpdateDatasourceRequestSchema,
+      options: { tags: [`access:${PLUGIN_ID}`] },
+    },
+    updateDatasourceHandler
+  );
+
+  // Delete
+  router.post(
+    {
+      path: DATASOURCE_API_ROUTES.DELETE_PATTERN,
+      validate: DeleteDatasourcesRequestSchema,
+      options: { tags: [`access:${PLUGIN_ID}`] },
+    },
+    deleteDatasourcesHandler
+  );
+};
diff --git a/x-pack/plugins/ingest_manager/server/routes/epm/index.ts b/x-pack/plugins/ingest_manager/server/routes/epm/index.ts
new file mode 100644
index 0000000000000..7bdcafe633843
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/routes/epm/index.ts
@@ -0,0 +1,75 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import { IRouter } from 'kibana/server';
+import { PLUGIN_ID, EPM_API_ROUTES } from '../../constants';
+
+export const registerRoutes = (router: IRouter) => {
+  router.get(
+    {
+      path: EPM_API_ROUTES.CATEGORIES_PATTERN,
+      validate: false,
+      options: { tags: [`access:${PLUGIN_ID}`] },
+    },
+    async (context, req, res) => {
+      return res.ok({ body: { hello: 'world' } });
+    }
+  );
+
+  router.get(
+    {
+      path: EPM_API_ROUTES.LIST_PATTERN,
+      validate: false,
+      options: { tags: [`access:${PLUGIN_ID}`] },
+    },
+    async (context, req, res) => {
+      return res.ok({ body: { hello: 'world' } });
+    }
+  );
+
+  router.get(
+    {
+      path: `${EPM_API_ROUTES.INFO_PATTERN}/{filePath*}`,
+      validate: false,
+      options: { tags: [`access:${PLUGIN_ID}`] },
+    },
+    async (context, req, res) => {
+      return res.ok({ body: { hello: 'world' } });
+    }
+  );
+
+  router.get(
+    {
+      path: EPM_API_ROUTES.INFO_PATTERN,
+      validate: false,
+      options: { tags: [`access:${PLUGIN_ID}`] },
+    },
+    async (context, req, res) => {
+      return res.ok({ body: { hello: 'world' } });
+    }
+  );
+
+  router.get(
+    {
+      path: EPM_API_ROUTES.INSTALL_PATTERN,
+      validate: false,
+      options: { tags: [`access:${PLUGIN_ID}`] },
+    },
+    async (context, req, res) => {
+      return res.ok({ body: { hello: 'world' } });
+    }
+  );
+
+  router.get(
+    {
+      path: EPM_API_ROUTES.DELETE_PATTERN,
+      validate: false,
+      options: { tags: [`access:${PLUGIN_ID}`] },
+    },
+    async (context, req, res) => {
+      return res.ok({ body: { hello: 'world' } });
+    }
+  );
+};
diff --git a/x-pack/plugins/ingest_manager/server/routes/fleet_setup/handlers.ts b/x-pack/plugins/ingest_manager/server/routes/fleet_setup/handlers.ts
new file mode 100644
index 0000000000000..72fe34eb23c5f
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/routes/fleet_setup/handlers.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.
+ */
+import { TypeOf } from '@kbn/config-schema';
+import { RequestHandler } from 'kibana/server';
+import { DEFAULT_OUTPUT_ID } from '../../constants';
+import { outputService, agentConfigService } from '../../services';
+import { CreateFleetSetupRequestSchema, CreateFleetSetupResponse } from '../../types';
+
+export const getFleetSetupHandler: RequestHandler = async (context, request, response) => {
+  const soClient = context.core.savedObjects.client;
+  const successBody: CreateFleetSetupResponse = { isInitialized: true };
+  const failureBody: CreateFleetSetupResponse = { isInitialized: false };
+  try {
+    const output = await outputService.get(soClient, DEFAULT_OUTPUT_ID);
+    if (output) {
+      return response.ok({
+        body: successBody,
+      });
+    } else {
+      return response.ok({
+        body: failureBody,
+      });
+    }
+  } catch (e) {
+    return response.ok({
+      body: failureBody,
+    });
+  }
+};
+
+export const createFleetSetupHandler: RequestHandler<
+  undefined,
+  undefined,
+  TypeOf<typeof CreateFleetSetupRequestSchema.body>
+> = async (context, request, response) => {
+  const soClient = context.core.savedObjects.client;
+  try {
+    await outputService.createDefaultOutput(soClient, {
+      username: request.body.admin_username,
+      password: request.body.admin_password,
+    });
+    await agentConfigService.ensureDefaultAgentConfig(soClient);
+    return response.ok({
+      body: { isInitialized: true },
+    });
+  } catch (e) {
+    return response.customError({
+      statusCode: 500,
+      body: { message: e.message },
+    });
+  }
+};
diff --git a/x-pack/plugins/ingest_manager/server/routes/fleet_setup/index.ts b/x-pack/plugins/ingest_manager/server/routes/fleet_setup/index.ts
new file mode 100644
index 0000000000000..c23164db6b6eb
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/routes/fleet_setup/index.ts
@@ -0,0 +1,31 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import { IRouter } from 'kibana/server';
+import { PLUGIN_ID, FLEET_SETUP_API_ROUTES } from '../../constants';
+import { GetFleetSetupRequestSchema, CreateFleetSetupRequestSchema } from '../../types';
+import { getFleetSetupHandler, createFleetSetupHandler } from './handlers';
+
+export const registerRoutes = (router: IRouter) => {
+  // Get
+  router.get(
+    {
+      path: FLEET_SETUP_API_ROUTES.INFO_PATTERN,
+      validate: GetFleetSetupRequestSchema,
+      options: { tags: [`access:${PLUGIN_ID}`] },
+    },
+    getFleetSetupHandler
+  );
+
+  // Create
+  router.post(
+    {
+      path: FLEET_SETUP_API_ROUTES.CREATE_PATTERN,
+      validate: CreateFleetSetupRequestSchema,
+      options: { tags: [`access:${PLUGIN_ID}`] },
+    },
+    createFleetSetupHandler
+  );
+};
diff --git a/x-pack/plugins/ingest_manager/server/routes/index.ts b/x-pack/plugins/ingest_manager/server/routes/index.ts
new file mode 100644
index 0000000000000..b458ef31dee45
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/routes/index.ts
@@ -0,0 +1,9 @@
+/*
+ * 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 { registerRoutes as registerAgentConfigRoutes } from './agent_config';
+export { registerRoutes as registerDatasourceRoutes } from './datasource';
+export { registerRoutes as registerEPMRoutes } from './epm';
+export { registerRoutes as registerFleetSetupRoutes } from './fleet_setup';
diff --git a/x-pack/plugins/ingest_manager/server/saved_objects.ts b/x-pack/plugins/ingest_manager/server/saved_objects.ts
new file mode 100644
index 0000000000000..976556f388acf
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/saved_objects.ts
@@ -0,0 +1,78 @@
+/*
+ * 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 {
+  OUTPUT_SAVED_OBJECT_TYPE,
+  AGENT_CONFIG_SAVED_OBJECT_TYPE,
+  DATASOURCE_SAVED_OBJECT_TYPE,
+} from './constants';
+
+/*
+ * Saved object mappings
+ *
+ * Please update typings in `/common/types` if mappings are updated.
+ */
+export const savedObjectMappings = {
+  [AGENT_CONFIG_SAVED_OBJECT_TYPE]: {
+    properties: {
+      id: { type: 'keyword' },
+      name: { type: 'text' },
+      namespace: { type: 'keyword' },
+      description: { type: 'text' },
+      status: { type: 'keyword' },
+      datasources: { type: 'keyword' },
+      updated_on: { type: 'keyword' },
+      updated_by: { type: 'keyword' },
+    },
+  },
+  [OUTPUT_SAVED_OBJECT_TYPE]: {
+    properties: {
+      id: { type: 'keyword' },
+      name: { type: 'keyword' },
+      type: { type: 'keyword' },
+      username: { type: 'keyword' },
+      password: { type: 'keyword' },
+      index_name: { type: 'keyword' },
+      ingest_pipeline: { type: 'keyword' },
+      hosts: { type: 'keyword' },
+      api_key: { type: 'keyword' },
+      admin_username: { type: 'binary' },
+      admin_password: { type: 'binary' },
+      config: { type: 'flattened' },
+    },
+  },
+  [DATASOURCE_SAVED_OBJECT_TYPE]: {
+    properties: {
+      id: { type: 'keyword' },
+      name: { type: 'keyword' },
+      namespace: { type: 'keyword' },
+      read_alias: { type: 'keyword' },
+      agent_config_id: { type: 'keyword' },
+      package: {
+        properties: {
+          assets: {
+            properties: {
+              id: { type: 'keyword' },
+              type: { type: 'keyword' },
+            },
+          },
+          description: { type: 'keyword' },
+          name: { type: 'keyword' },
+          title: { type: 'keyword' },
+          version: { type: 'keyword' },
+        },
+      },
+      streams: {
+        properties: {
+          config: { type: 'flattened' },
+          id: { type: 'keyword' },
+          input: { type: 'flattened' },
+          output_id: { type: 'keyword' },
+          processors: { type: 'keyword' },
+        },
+      },
+    },
+  },
+};
diff --git a/x-pack/plugins/ingest_manager/server/services/agent_config.ts b/x-pack/plugins/ingest_manager/server/services/agent_config.ts
new file mode 100644
index 0000000000000..0690e115ca862
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/services/agent_config.ts
@@ -0,0 +1,258 @@
+/*
+ * 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 { AuthenticatedUser } from '../../../security/server';
+import {
+  DEFAULT_AGENT_CONFIG_ID,
+  DEFAULT_AGENT_CONFIG,
+  AGENT_CONFIG_SAVED_OBJECT_TYPE,
+} from '../constants';
+import {
+  NewAgentConfig,
+  AgentConfig,
+  AgentConfigStatus,
+  AgentConfigUpdateHandler,
+  ListWithKuery,
+  DeleteAgentConfigsResponse,
+} from '../types';
+import { datasourceService } from './datasource';
+
+const SAVED_OBJECT_TYPE = AGENT_CONFIG_SAVED_OBJECT_TYPE;
+
+class AgentConfigService {
+  private eventsHandler: AgentConfigUpdateHandler[] = [];
+
+  public registerAgentConfigUpdateHandler(handler: AgentConfigUpdateHandler) {
+    this.eventsHandler.push(handler);
+  }
+
+  public triggerAgentConfigUpdatedEvent: AgentConfigUpdateHandler = async (
+    action,
+    agentConfigId
+  ) => {
+    for (const handler of this.eventsHandler) {
+      await handler(action, agentConfigId);
+    }
+  };
+
+  private async _update(
+    soClient: SavedObjectsClientContract,
+    id: string,
+    agentConfig: Partial<AgentConfig>,
+    user?: AuthenticatedUser
+  ): Promise<AgentConfig> {
+    await soClient.update<AgentConfig>(SAVED_OBJECT_TYPE, id, {
+      ...agentConfig,
+      updated_on: new Date().toString(),
+      updated_by: user ? user.username : 'system',
+    });
+
+    await this.triggerAgentConfigUpdatedEvent('updated', id);
+
+    return (await this.get(soClient, id)) as AgentConfig;
+  }
+
+  public async ensureDefaultAgentConfig(soClient: SavedObjectsClientContract) {
+    let defaultAgentConfig;
+
+    try {
+      defaultAgentConfig = await this.get(soClient, DEFAULT_AGENT_CONFIG_ID);
+    } catch (err) {
+      if (!err.isBoom || err.output.statusCode !== 404) {
+        throw err;
+      }
+    }
+
+    if (!defaultAgentConfig) {
+      const newDefaultAgentConfig: NewAgentConfig = {
+        ...DEFAULT_AGENT_CONFIG,
+      };
+
+      await this.create(soClient, newDefaultAgentConfig, {
+        id: DEFAULT_AGENT_CONFIG_ID,
+      });
+    }
+  }
+
+  public async create(
+    soClient: SavedObjectsClientContract,
+    agentConfig: NewAgentConfig,
+    options?: { id?: string; user?: AuthenticatedUser }
+  ): Promise<AgentConfig> {
+    const newSo = await soClient.create<AgentConfig>(
+      SAVED_OBJECT_TYPE,
+      {
+        ...agentConfig,
+        updated_on: new Date().toISOString(),
+        updated_by: options?.user?.username || 'system',
+      } as AgentConfig,
+      options
+    );
+
+    await this.triggerAgentConfigUpdatedEvent('created', newSo.id);
+
+    return {
+      id: newSo.id,
+      ...newSo.attributes,
+    };
+  }
+
+  public async get(soClient: SavedObjectsClientContract, id: string): Promise<AgentConfig | null> {
+    const agentConfigSO = await soClient.get<AgentConfig>(SAVED_OBJECT_TYPE, id);
+    if (!agentConfigSO) {
+      return null;
+    }
+
+    if (agentConfigSO.error) {
+      throw new Error(agentConfigSO.error.message);
+    }
+
+    return {
+      id: agentConfigSO.id,
+      ...agentConfigSO.attributes,
+      datasources:
+        (await datasourceService.getByIDs(
+          soClient,
+          (agentConfigSO.attributes.datasources as string[]) || []
+        )) || [],
+    };
+  }
+
+  public async list(
+    soClient: SavedObjectsClientContract,
+    options: ListWithKuery
+  ): Promise<{ items: AgentConfig[]; total: number; page: number; perPage: number }> {
+    const { page = 1, perPage = 20, kuery } = options;
+
+    const agentConfigs = await soClient.find<AgentConfig>({
+      type: SAVED_OBJECT_TYPE,
+      page,
+      perPage,
+      // To ensure users don't need to know about SO data structure...
+      filter: kuery
+        ? kuery.replace(
+            new RegExp(`${SAVED_OBJECT_TYPE}\.`, 'g'),
+            `${SAVED_OBJECT_TYPE}.attributes.`
+          )
+        : undefined,
+    });
+
+    return {
+      items: agentConfigs.saved_objects.map<AgentConfig>(agentConfigSO => {
+        return {
+          id: agentConfigSO.id,
+          ...agentConfigSO.attributes,
+        };
+      }),
+      total: agentConfigs.total,
+      page,
+      perPage,
+    };
+  }
+
+  public async update(
+    soClient: SavedObjectsClientContract,
+    id: string,
+    agentConfig: Partial<AgentConfig>,
+    options?: { user?: AuthenticatedUser }
+  ): Promise<AgentConfig> {
+    const oldAgentConfig = await this.get(soClient, id);
+
+    if (!oldAgentConfig) {
+      throw new Error('Agent config not found');
+    }
+
+    if (
+      oldAgentConfig.status === AgentConfigStatus.Inactive &&
+      agentConfig.status !== AgentConfigStatus.Active
+    ) {
+      throw new Error(
+        `Agent config ${id} cannot be updated because it is ${oldAgentConfig.status}`
+      );
+    }
+
+    return this._update(soClient, id, agentConfig, options?.user);
+  }
+
+  public async assignDatasources(
+    soClient: SavedObjectsClientContract,
+    id: string,
+    datasourceIds: string[],
+    options?: { user?: AuthenticatedUser }
+  ): Promise<AgentConfig> {
+    const oldAgentConfig = await this.get(soClient, id);
+
+    if (!oldAgentConfig) {
+      throw new Error('Agent config not found');
+    }
+
+    return await this._update(
+      soClient,
+      id,
+      {
+        ...oldAgentConfig,
+        datasources: [...((oldAgentConfig.datasources || []) as string[])].concat(datasourceIds),
+      },
+      options?.user
+    );
+  }
+
+  public async unassignDatasources(
+    soClient: SavedObjectsClientContract,
+    id: string,
+    datasourceIds: string[],
+    options?: { user?: AuthenticatedUser }
+  ): Promise<AgentConfig> {
+    const oldAgentConfig = await this.get(soClient, id);
+
+    if (!oldAgentConfig) {
+      throw new Error('Agent config not found');
+    }
+
+    return await this._update(
+      soClient,
+      id,
+      {
+        ...oldAgentConfig,
+        datasources: [...((oldAgentConfig.datasources || []) as string[])].filter(
+          dsId => !datasourceIds.includes(dsId)
+        ),
+      },
+      options?.user
+    );
+  }
+
+  public async delete(
+    soClient: SavedObjectsClientContract,
+    ids: string[]
+  ): Promise<DeleteAgentConfigsResponse> {
+    const result: DeleteAgentConfigsResponse = [];
+
+    if (ids.includes(DEFAULT_AGENT_CONFIG_ID)) {
+      throw new Error('The default agent configuration cannot be deleted');
+    }
+
+    for (const id of ids) {
+      try {
+        await soClient.delete(SAVED_OBJECT_TYPE, id);
+        await this.triggerAgentConfigUpdatedEvent('deleted', id);
+        result.push({
+          id,
+          success: true,
+        });
+      } catch (e) {
+        result.push({
+          id,
+          success: false,
+        });
+      }
+    }
+
+    return result;
+  }
+}
+
+export const agentConfigService = new AgentConfigService();
diff --git a/x-pack/plugins/ingest_manager/server/services/app_context.ts b/x-pack/plugins/ingest_manager/server/services/app_context.ts
new file mode 100644
index 0000000000000..69a014fca37fb
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/services/app_context.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.
+ */
+import { BehaviorSubject, Observable } from 'rxjs';
+import { first } from 'rxjs/operators';
+import { EncryptedSavedObjectsPluginStart } from '../../../encrypted_saved_objects/server';
+import { SecurityPluginSetup } from '../../../security/server';
+import { IngestManagerConfigType } from '../../common';
+import { IngestManagerAppContext } from '../plugin';
+
+class AppContextService {
+  private encryptedSavedObjects: EncryptedSavedObjectsPluginStart | undefined;
+  private security: SecurityPluginSetup | undefined;
+  private config$?: Observable<IngestManagerConfigType>;
+  private configSubject$?: BehaviorSubject<IngestManagerConfigType>;
+
+  public async start(appContext: IngestManagerAppContext) {
+    this.encryptedSavedObjects = appContext.encryptedSavedObjects;
+    this.security = appContext.security;
+
+    if (appContext.config$) {
+      this.config$ = appContext.config$;
+      const initialValue = await this.config$.pipe(first()).toPromise();
+      this.configSubject$ = new BehaviorSubject(initialValue);
+      this.config$.subscribe(this.configSubject$);
+    }
+  }
+
+  public stop() {}
+
+  public getEncryptedSavedObjects() {
+    return this.encryptedSavedObjects;
+  }
+
+  public getSecurity() {
+    return this.security;
+  }
+
+  public getConfig() {
+    return this.configSubject$?.value;
+  }
+
+  public getConfig$() {
+    return this.config$;
+  }
+}
+
+export const appContextService = new AppContextService();
diff --git a/x-pack/plugins/ingest_manager/server/services/datasource.ts b/x-pack/plugins/ingest_manager/server/services/datasource.ts
new file mode 100644
index 0000000000000..b305ccaab777b
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/services/datasource.ts
@@ -0,0 +1,132 @@
+/*
+ * 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 { DATASOURCE_SAVED_OBJECT_TYPE } from '../constants';
+import { NewDatasource, Datasource, DeleteDatasourcesResponse, ListWithKuery } from '../types';
+
+const SAVED_OBJECT_TYPE = DATASOURCE_SAVED_OBJECT_TYPE;
+
+class DatasourceService {
+  public async create(
+    soClient: SavedObjectsClientContract,
+    datasource: NewDatasource,
+    options?: { id?: string }
+  ): Promise<Datasource> {
+    const newSo = await soClient.create<Datasource>(
+      SAVED_OBJECT_TYPE,
+      datasource as Datasource,
+      options
+    );
+
+    return {
+      id: newSo.id,
+      ...newSo.attributes,
+    };
+  }
+
+  public async get(soClient: SavedObjectsClientContract, id: string): Promise<Datasource | null> {
+    const datasourceSO = await soClient.get<Datasource>(SAVED_OBJECT_TYPE, id);
+    if (!datasourceSO) {
+      return null;
+    }
+
+    if (datasourceSO.error) {
+      throw new Error(datasourceSO.error.message);
+    }
+
+    return {
+      id: datasourceSO.id,
+      ...datasourceSO.attributes,
+    };
+  }
+
+  public async getByIDs(
+    soClient: SavedObjectsClientContract,
+    ids: string[]
+  ): Promise<Datasource[] | null> {
+    const datasourceSO = await soClient.bulkGet<Datasource>(
+      ids.map(id => ({
+        id,
+        type: SAVED_OBJECT_TYPE,
+      }))
+    );
+    if (!datasourceSO) {
+      return null;
+    }
+
+    return datasourceSO.saved_objects.map(so => ({
+      id: so.id,
+      ...so.attributes,
+    }));
+  }
+
+  public async list(
+    soClient: SavedObjectsClientContract,
+    options: ListWithKuery
+  ): Promise<{ items: Datasource[]; total: number; page: number; perPage: number }> {
+    const { page = 1, perPage = 20, kuery } = options;
+
+    const datasources = await soClient.find<Datasource>({
+      type: SAVED_OBJECT_TYPE,
+      page,
+      perPage,
+      // To ensure users don't need to know about SO data structure...
+      filter: kuery
+        ? kuery.replace(
+            new RegExp(`${SAVED_OBJECT_TYPE}\.`, 'g'),
+            `${SAVED_OBJECT_TYPE}.attributes.`
+          )
+        : undefined,
+    });
+
+    return {
+      items: datasources.saved_objects.map<Datasource>(datasourceSO => {
+        return {
+          id: datasourceSO.id,
+          ...datasourceSO.attributes,
+        };
+      }),
+      total: datasources.total,
+      page,
+      perPage,
+    };
+  }
+
+  public async update(
+    soClient: SavedObjectsClientContract,
+    id: string,
+    datasource: NewDatasource
+  ): Promise<Datasource> {
+    await soClient.update<Datasource>(SAVED_OBJECT_TYPE, id, datasource);
+    return (await this.get(soClient, id)) as Datasource;
+  }
+
+  public async delete(
+    soClient: SavedObjectsClientContract,
+    ids: string[]
+  ): Promise<DeleteDatasourcesResponse> {
+    const result: DeleteDatasourcesResponse = [];
+
+    for (const id of ids) {
+      try {
+        await soClient.delete(SAVED_OBJECT_TYPE, id);
+        result.push({
+          id,
+          success: true,
+        });
+      } catch (e) {
+        result.push({
+          id,
+          success: false,
+        });
+      }
+    }
+
+    return result;
+  }
+}
+
+export const datasourceService = new DatasourceService();
diff --git a/x-pack/plugins/ingest_manager/server/services/index.ts b/x-pack/plugins/ingest_manager/server/services/index.ts
new file mode 100644
index 0000000000000..dd0c898afa425
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/services/index.ts
@@ -0,0 +1,11 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+export { appContextService } from './app_context';
+
+// Saved object services
+export { datasourceService } from './datasource';
+export { agentConfigService } from './agent_config';
+export { outputService } from './output';
diff --git a/x-pack/plugins/ingest_manager/server/services/output.ts b/x-pack/plugins/ingest_manager/server/services/output.ts
new file mode 100644
index 0000000000000..8f60ed295205d
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/services/output.ts
@@ -0,0 +1,113 @@
+/*
+ * 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, KibanaRequest } from 'kibana/server';
+import { NewOutput, Output } from '../types';
+import { DEFAULT_OUTPUT, DEFAULT_OUTPUT_ID, OUTPUT_SAVED_OBJECT_TYPE } from '../constants';
+import { appContextService } from './app_context';
+
+const SAVED_OBJECT_TYPE = OUTPUT_SAVED_OBJECT_TYPE;
+
+class OutputService {
+  public async createDefaultOutput(
+    soClient: SavedObjectsClientContract,
+    adminUser: { username: string; password: string }
+  ) {
+    let defaultOutput;
+
+    try {
+      defaultOutput = await this.get(soClient, DEFAULT_OUTPUT_ID);
+    } catch (err) {
+      if (!err.isBoom || err.output.statusCode !== 404) {
+        throw err;
+      }
+    }
+
+    if (!defaultOutput) {
+      const newDefaultOutput = {
+        ...DEFAULT_OUTPUT,
+        hosts: [appContextService.getConfig()!.fleet.defaultOutputHost],
+        api_key: await this.createDefaultOutputApiKey(adminUser.username, adminUser.password),
+        admin_username: adminUser.username,
+        admin_password: adminUser.password,
+      } as NewOutput;
+
+      await this.create(soClient, newDefaultOutput, {
+        id: DEFAULT_OUTPUT_ID,
+      });
+    }
+  }
+
+  public async getAdminUser() {
+    const so = await appContextService
+      .getEncryptedSavedObjects()
+      ?.getDecryptedAsInternalUser<Output>(OUTPUT_SAVED_OBJECT_TYPE, DEFAULT_OUTPUT_ID);
+
+    return {
+      username: so!.attributes.admin_username,
+      password: so!.attributes.admin_password,
+    };
+  }
+
+  // TODO: TEMPORARY this is going to be per agent
+  private async createDefaultOutputApiKey(username: string, password: string): Promise<string> {
+    const key = await appContextService.getSecurity()?.authc.createAPIKey(
+      {
+        headers: {
+          authorization: `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`,
+        },
+      } as KibanaRequest,
+      {
+        name: 'fleet-default-output',
+        role_descriptors: {
+          'fleet-output': {
+            cluster: ['monitor'],
+            index: [
+              {
+                names: ['logs-*', 'metrics-*'],
+                privileges: ['write'],
+              },
+            ],
+          },
+        },
+      }
+    );
+    if (!key) {
+      throw new Error('An error occured while creating default API Key');
+    }
+    return `${key.id}:${key.api_key}`;
+  }
+
+  public async create(
+    soClient: SavedObjectsClientContract,
+    output: NewOutput,
+    options?: { id?: string }
+  ): Promise<Output> {
+    const newSo = await soClient.create<Output>(SAVED_OBJECT_TYPE, output as Output, options);
+
+    return {
+      id: newSo.id,
+      ...newSo.attributes,
+    };
+  }
+
+  public async get(soClient: SavedObjectsClientContract, id: string): Promise<Output | null> {
+    const outputSO = await soClient.get<Output>(SAVED_OBJECT_TYPE, id);
+    if (!outputSO) {
+      return null;
+    }
+
+    if (outputSO.error) {
+      throw new Error(outputSO.error.message);
+    }
+
+    return {
+      id: outputSO.id,
+      ...outputSO.attributes,
+    };
+  }
+}
+
+export const outputService = new OutputService();
diff --git a/x-pack/plugins/ingest_manager/server/types/index.tsx b/x-pack/plugins/ingest_manager/server/types/index.tsx
new file mode 100644
index 0000000000000..f44d03923d424
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/types/index.tsx
@@ -0,0 +1,12 @@
+/*
+ * 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 './models';
+export * from './rest_spec';
+
+export type AgentConfigUpdateHandler = (
+  action: 'created' | 'updated' | 'deleted',
+  agentConfigId: string
+) => Promise<void>;
diff --git a/x-pack/plugins/ingest_manager/server/types/models/agent_config.ts b/x-pack/plugins/ingest_manager/server/types/models/agent_config.ts
new file mode 100644
index 0000000000000..d3acb0c167837
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/types/models/agent_config.ts
@@ -0,0 +1,38 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import { schema, TypeOf } from '@kbn/config-schema';
+import { DatasourceSchema } from './datasource';
+
+export enum AgentConfigStatus {
+  Active = 'active',
+  Inactive = 'inactive',
+}
+
+const AgentConfigBaseSchema = {
+  name: schema.string(),
+  namespace: schema.string(),
+  description: schema.maybe(schema.string()),
+};
+
+export const NewAgentConfigSchema = schema.object({
+  ...AgentConfigBaseSchema,
+});
+
+export const AgentConfigSchema = schema.object({
+  ...AgentConfigBaseSchema,
+  id: schema.string(),
+  status: schema.oneOf([
+    schema.literal(AgentConfigStatus.Active),
+    schema.literal(AgentConfigStatus.Inactive),
+  ]),
+  datasources: schema.oneOf([schema.arrayOf(schema.string()), schema.arrayOf(DatasourceSchema)]),
+  updated_on: schema.string(),
+  updated_by: schema.string(),
+});
+
+export type NewAgentConfig = TypeOf<typeof NewAgentConfigSchema>;
+
+export type AgentConfig = TypeOf<typeof AgentConfigSchema>;
diff --git a/x-pack/plugins/ingest_manager/server/types/models/datasource.ts b/x-pack/plugins/ingest_manager/server/types/models/datasource.ts
new file mode 100644
index 0000000000000..4179d4c3a511a
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/types/models/datasource.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.
+ */
+import { schema, TypeOf } from '@kbn/config-schema';
+
+const DatasourceBaseSchema = {
+  name: schema.string(),
+  namespace: schema.maybe(schema.string()),
+  read_alias: schema.maybe(schema.string()),
+  agent_config_id: schema.string(),
+  package: schema.maybe(
+    schema.object({
+      assets: schema.arrayOf(
+        schema.object({
+          id: schema.string(),
+          type: schema.string(),
+        })
+      ),
+      description: schema.string(),
+      name: schema.string(),
+      title: schema.string(),
+      version: schema.string(),
+    })
+  ),
+  streams: schema.arrayOf(
+    schema.object({
+      config: schema.recordOf(schema.string(), schema.any()),
+      input: schema.object({
+        type: schema.string(),
+        config: schema.recordOf(schema.string(), schema.any()),
+        fields: schema.maybe(schema.arrayOf(schema.recordOf(schema.string(), schema.any()))),
+        ilm_policy: schema.maybe(schema.string()),
+        index_template: schema.maybe(schema.string()),
+        ingest_pipelines: schema.maybe(schema.arrayOf(schema.string())),
+      }),
+      output_id: schema.string(),
+      processors: schema.maybe(schema.arrayOf(schema.string())),
+    })
+  ),
+};
+
+export const NewDatasourceSchema = schema.object({
+  ...DatasourceBaseSchema,
+});
+
+export const DatasourceSchema = schema.object({
+  ...DatasourceBaseSchema,
+  id: schema.string(),
+});
+
+export type NewDatasource = TypeOf<typeof NewDatasourceSchema>;
+
+export type Datasource = TypeOf<typeof DatasourceSchema>;
diff --git a/x-pack/plugins/ingest_manager/server/types/models/index.ts b/x-pack/plugins/ingest_manager/server/types/models/index.ts
new file mode 100644
index 0000000000000..959dfe1d937b9
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/types/models/index.ts
@@ -0,0 +1,8 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+export * from './agent_config';
+export * from './datasource';
+export * from './output';
diff --git a/x-pack/plugins/ingest_manager/server/types/models/output.ts b/x-pack/plugins/ingest_manager/server/types/models/output.ts
new file mode 100644
index 0000000000000..610fa6796cc2d
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/types/models/output.ts
@@ -0,0 +1,37 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import { schema, TypeOf } from '@kbn/config-schema';
+
+export enum OutputType {
+  Elasticsearch = 'elasticsearch',
+}
+
+const OutputBaseSchema = {
+  name: schema.string(),
+  type: schema.oneOf([schema.literal(OutputType.Elasticsearch)]),
+  username: schema.maybe(schema.string()),
+  password: schema.maybe(schema.string()),
+  index_name: schema.maybe(schema.string()),
+  ingest_pipeline: schema.maybe(schema.string()),
+  hosts: schema.maybe(schema.arrayOf(schema.string())),
+  api_key: schema.maybe(schema.string()),
+  admin_username: schema.maybe(schema.string()),
+  admin_password: schema.maybe(schema.string()),
+  config: schema.maybe(schema.recordOf(schema.string(), schema.any())),
+};
+
+export const NewOutputSchema = schema.object({
+  ...OutputBaseSchema,
+});
+
+export const OutputSchema = schema.object({
+  ...OutputBaseSchema,
+  id: schema.string(),
+});
+
+export type NewOutput = TypeOf<typeof NewOutputSchema>;
+
+export type Output = TypeOf<typeof OutputSchema>;
diff --git a/x-pack/plugins/ingest_manager/server/types/rest_spec/agent_config.ts b/x-pack/plugins/ingest_manager/server/types/rest_spec/agent_config.ts
new file mode 100644
index 0000000000000..cb4680a4eed04
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/types/rest_spec/agent_config.ts
@@ -0,0 +1,61 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import { schema } from '@kbn/config-schema';
+import { AgentConfig, NewAgentConfigSchema } from '../models';
+import { ListWithKuerySchema } from './common';
+
+export const GetAgentConfigsRequestSchema = {
+  query: ListWithKuerySchema,
+};
+
+export interface GetAgentConfigsResponse {
+  items: AgentConfig[];
+  total: number;
+  page: number;
+  perPage: number;
+  success: boolean;
+}
+
+export const GetOneAgentConfigRequestSchema = {
+  params: schema.object({
+    agentConfigId: schema.string(),
+  }),
+};
+
+export interface GetOneAgentConfigResponse {
+  item: AgentConfig;
+  success: boolean;
+}
+
+export const CreateAgentConfigRequestSchema = {
+  body: NewAgentConfigSchema,
+};
+
+export interface CreateAgentConfigResponse {
+  item: AgentConfig;
+  success: boolean;
+}
+
+export const UpdateAgentConfigRequestSchema = {
+  ...GetOneAgentConfigRequestSchema,
+  body: NewAgentConfigSchema,
+};
+
+export interface UpdateAgentConfigResponse {
+  item: AgentConfig;
+  success: boolean;
+}
+
+export const DeleteAgentConfigsRequestSchema = {
+  body: schema.object({
+    agentConfigIds: schema.arrayOf(schema.string()),
+  }),
+};
+
+export type DeleteAgentConfigsResponse = Array<{
+  id: string;
+  success: boolean;
+}>;
diff --git a/x-pack/plugins/ingest_manager/server/types/rest_spec/common.ts b/x-pack/plugins/ingest_manager/server/types/rest_spec/common.ts
new file mode 100644
index 0000000000000..2c8134d2e8f92
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/types/rest_spec/common.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 { schema, TypeOf } from '@kbn/config-schema';
+
+export const ListWithKuerySchema = schema.object({
+  page: schema.number({ defaultValue: 1 }),
+  perPage: schema.number({ defaultValue: 20 }),
+  kuery: schema.maybe(schema.string()),
+});
+
+export type ListWithKuery = TypeOf<typeof ListWithKuerySchema>;
diff --git a/x-pack/plugins/ingest_manager/server/types/rest_spec/datasource.ts b/x-pack/plugins/ingest_manager/server/types/rest_spec/datasource.ts
new file mode 100644
index 0000000000000..5c165517e9c9d
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/types/rest_spec/datasource.ts
@@ -0,0 +1,38 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import { schema } from '@kbn/config-schema';
+import { NewDatasourceSchema } from '../models';
+import { ListWithKuerySchema } from './common';
+
+export const GetDatasourcesRequestSchema = {
+  query: ListWithKuerySchema,
+};
+
+export const GetOneDatasourceRequestSchema = {
+  params: schema.object({
+    datasourceId: schema.string(),
+  }),
+};
+
+export const CreateDatasourceRequestSchema = {
+  body: NewDatasourceSchema,
+};
+
+export const UpdateDatasourceRequestSchema = {
+  ...GetOneDatasourceRequestSchema,
+  body: NewDatasourceSchema,
+};
+
+export const DeleteDatasourcesRequestSchema = {
+  body: schema.object({
+    datasourceIds: schema.arrayOf(schema.string()),
+  }),
+};
+
+export type DeleteDatasourcesResponse = Array<{
+  id: string;
+  success: boolean;
+}>;
diff --git a/x-pack/plugins/ingest_manager/server/types/rest_spec/fleet_setup.ts b/x-pack/plugins/ingest_manager/server/types/rest_spec/fleet_setup.ts
new file mode 100644
index 0000000000000..3772e6c24c56e
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/types/rest_spec/fleet_setup.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 { schema } from '@kbn/config-schema';
+
+export const GetFleetSetupRequestSchema = {};
+
+export const CreateFleetSetupRequestSchema = {
+  body: schema.object({
+    admin_username: schema.string(),
+    admin_password: schema.string(),
+  }),
+};
+
+export interface CreateFleetSetupResponse {
+  isInitialized: boolean;
+}
diff --git a/x-pack/plugins/ingest_manager/server/types/rest_spec/index.ts b/x-pack/plugins/ingest_manager/server/types/rest_spec/index.ts
new file mode 100644
index 0000000000000..7d0d7e67f2db0
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/types/rest_spec/index.ts
@@ -0,0 +1,9 @@
+/*
+ * 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 './common';
+export * from './datasource';
+export * from './agent_config';
+export * from './fleet_setup';
diff --git a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/watch_visualization.tsx b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/watch_visualization.tsx
index b2627e1c58aff..96f1669d8f0e3 100644
--- a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/watch_visualization.tsx
+++ b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/watch_visualization.tsx
@@ -180,7 +180,7 @@ export const WatchVisualization = () => {
               defaultMessage="Cannot load watch visualization"
             />
           }
-          error={error as Error}
+          error={(error as unknown) as Error}
         />
         <EuiSpacer size="l" />
       </Fragment>
diff --git a/x-pack/plugins/watcher/public/application/sections/watch_list/components/watch_list.tsx b/x-pack/plugins/watcher/public/application/sections/watch_list/components/watch_list.tsx
index 2d552d7fbdda6..54f4209a137b9 100644
--- a/x-pack/plugins/watcher/public/application/sections/watch_list/components/watch_list.tsx
+++ b/x-pack/plugins/watcher/public/application/sections/watch_list/components/watch_list.tsx
@@ -226,7 +226,7 @@ export const WatchList = () => {
             defaultMessage="Error loading watches"
           />
         }
-        error={error as Error}
+        error={(error as unknown) as Error}
       />
     );
   } else if (availableWatches) {
diff --git a/x-pack/plugins/watcher/public/application/sections/watch_status/components/watch_history.tsx b/x-pack/plugins/watcher/public/application/sections/watch_status/components/watch_history.tsx
index b78a2e4230171..541df89d042cb 100644
--- a/x-pack/plugins/watcher/public/application/sections/watch_status/components/watch_history.tsx
+++ b/x-pack/plugins/watcher/public/application/sections/watch_status/components/watch_history.tsx
@@ -107,7 +107,7 @@ export const WatchHistory = () => {
               defaultMessage="Error loading execution history"
             />
           }
-          error={historyError as Error}
+          error={(historyError as unknown) as Error}
         />
       </Fragment>
     );
@@ -186,7 +186,7 @@ export const WatchHistory = () => {
                   defaultMessage="Error loading execution details"
                 />
               }
-              error={watchHistoryDetailsError as Error}
+              error={(watchHistoryDetailsError as unknown) as Error}
               data-test-subj="errorMessage"
             />
           </EuiFlyoutBody>

From 02e09037665cf2c1e8e0293ed6e120ee8abdd9d7 Mon Sep 17 00:00:00 2001
From: Matthias Wilhelm <matthias.wilhelm@elastic.co>
Date: Wed, 19 Feb 2020 08:39:33 +0100
Subject: [PATCH 052/174] [Discover] Add check for the open perma link for
 cloud testing (#57884)

- fixes a functional test failure in cloud
---
 test/functional/page_objects/share_page.ts | 1 +
 1 file changed, 1 insertion(+)

diff --git a/test/functional/page_objects/share_page.ts b/test/functional/page_objects/share_page.ts
index fc8db9b78a03f..555e08b841461 100644
--- a/test/functional/page_objects/share_page.ts
+++ b/test/functional/page_objects/share_page.ts
@@ -76,6 +76,7 @@ export function SharePageProvider({ getService }: FtrProviderContext) {
     }
 
     async checkShortenUrl() {
+      await this.openPermaLinks();
       const shareForm = await testSubjects.find('shareUrlForm');
       await testSubjects.setCheckbox('useShortUrl', 'check');
       await shareForm.waitForDeletedByCssSelector('.euiLoadingSpinner');

From 7b125e3ce05262c580abcb7a226178676652caab Mon Sep 17 00:00:00 2001
From: Robert Oskamp <robert.oskamp@elastic.co>
Date: Wed, 19 Feb 2020 08:47:18 +0100
Subject: [PATCH 053/174] [ML] Functional tests - run all tests as poweruser
 (#57809)

This PR runs all functional ML and transform tests (except existing security and feature controls tests) as a poweruser instead of the default superuser.
---
 .../apis/ml/bucket_span_estimator.ts          | 108 ++---
 .../apis/ml/calculate_model_memory_limit.ts   | 240 +++++------
 .../apis/ml/categorization_field_examples.ts  | 392 +++++++++---------
 x-pack/test/api_integration/apis/ml/index.ts  |  14 +-
 x-pack/test/api_integration/services/index.ts |   2 +
 .../anomaly_detection/advanced_job.ts         |   1 +
 .../anomaly_detection/anomaly_explorer.ts     |   1 +
 .../anomaly_detection/categorization_job.ts   |   1 +
 .../anomaly_detection/multi_metric_job.ts     |   1 +
 .../anomaly_detection/population_job.ts       |   1 +
 .../anomaly_detection/saved_search_job.ts     |   1 +
 .../anomaly_detection/single_metric_job.ts    |   1 +
 .../anomaly_detection/single_metric_viewer.ts |   1 +
 .../classification_creation.ts                |   3 +-
 .../outlier_detection_creation.ts             |   3 +-
 .../regression_creation.ts                    |   3 +-
 .../data_visualizer/index_data_visualizer.ts  |   1 +
 .../functional/apps/machine_learning/index.ts |  14 +-
 .../functional/apps/machine_learning/pages.ts |   1 +
 .../apps/transform/creation_index_pattern.ts  |   8 +-
 .../apps/transform/creation_saved_search.ts   |  11 +-
 .../test/functional/apps/transform/index.ts   |  14 +-
 .../services/machine_learning/index.ts        |   2 +
 .../machine_learning/security_common.ts       | 106 +++++
 .../services/machine_learning/security_ui.ts  |  35 ++
 x-pack/test/functional/services/ml.ts         |   6 +
 x-pack/test/functional/services/transform.ts  |   6 +
 .../functional/services/transform_ui/index.ts |   2 +
 .../services/transform_ui/security_common.ts  | 112 +++++
 .../services/transform_ui/security_ui.ts      |  35 ++
 .../services/transform_ui/wizard.ts           |  75 +++-
 31 files changed, 820 insertions(+), 381 deletions(-)
 create mode 100644 x-pack/test/functional/services/machine_learning/security_common.ts
 create mode 100644 x-pack/test/functional/services/machine_learning/security_ui.ts
 create mode 100644 x-pack/test/functional/services/transform_ui/security_common.ts
 create mode 100644 x-pack/test/functional/services/transform_ui/security_ui.ts

diff --git a/x-pack/test/api_integration/apis/ml/bucket_span_estimator.ts b/x-pack/test/api_integration/apis/ml/bucket_span_estimator.ts
index b5e5168621584..47afe7553fe62 100644
--- a/x-pack/test/api_integration/apis/ml/bucket_span_estimator.ts
+++ b/x-pack/test/api_integration/apis/ml/bucket_span_estimator.ts
@@ -7,66 +7,71 @@
 import expect from '@kbn/expect';
 
 import { FtrProviderContext } from '../../ftr_provider_context';
+import { USER } from '../../../functional/services/machine_learning/security_common';
 
 const COMMON_HEADERS = {
   'kbn-xsrf': 'some-xsrf-token',
 };
 
-const testDataList = [
-  {
-    testTitleSuffix: 'with 1 field, 1 agg, no split',
-    requestBody: {
-      aggTypes: ['avg'],
-      duration: { start: 1560297859000, end: 1562975136000 },
-      fields: ['taxless_total_price'],
-      index: 'ecommerce',
-      query: { bool: { must: [{ match_all: {} }] } },
-      timeField: 'order_date',
-    },
-    expected: {
-      responseCode: 200,
-      responseBody: { name: '15m', ms: 900000 },
-    },
-  },
-  {
-    testTitleSuffix: 'with 2 fields, 2 aggs, no split',
-    requestBody: {
-      aggTypes: ['avg', 'sum'],
-      duration: { start: 1560297859000, end: 1562975136000 },
-      fields: ['products.base_price', 'products.base_unit_price'],
-      index: 'ecommerce',
-      query: { bool: { must: [{ match_all: {} }] } },
-      timeField: 'order_date',
-    },
-    expected: {
-      responseCode: 200,
-      responseBody: { name: '30m', ms: 1800000 },
-    },
-  },
-  {
-    testTitleSuffix: 'with 1 field, 1 agg, 1 split with cardinality 46',
-    requestBody: {
-      aggTypes: ['avg'],
-      duration: { start: 1560297859000, end: 1562975136000 },
-      fields: ['taxless_total_price'],
-      index: 'ecommerce',
-      query: { bool: { must: [{ match_all: {} }] } },
-      splitField: 'customer_first_name.keyword',
-      timeField: 'order_date',
-    },
-    expected: {
-      responseCode: 200,
-      responseBody: { name: '3h', ms: 10800000 },
-    },
-  },
-];
-
 // eslint-disable-next-line import/no-default-export
 export default ({ getService }: FtrProviderContext) => {
   const esArchiver = getService('esArchiver');
-  const supertest = getService('supertest');
+  const supertest = getService('supertestWithoutAuth');
+  const mlSecurity = getService('mlSecurity');
+
+  const testDataList = [
+    {
+      testTitleSuffix: 'with 1 field, 1 agg, no split',
+      user: USER.ML_POWERUSER,
+      requestBody: {
+        aggTypes: ['avg'],
+        duration: { start: 1560297859000, end: 1562975136000 },
+        fields: ['taxless_total_price'],
+        index: 'ecommerce',
+        query: { bool: { must: [{ match_all: {} }] } },
+        timeField: 'order_date',
+      },
+      expected: {
+        responseCode: 200,
+        responseBody: { name: '15m', ms: 900000 },
+      },
+    },
+    {
+      testTitleSuffix: 'with 2 fields, 2 aggs, no split',
+      user: USER.ML_POWERUSER,
+      requestBody: {
+        aggTypes: ['avg', 'sum'],
+        duration: { start: 1560297859000, end: 1562975136000 },
+        fields: ['products.base_price', 'products.base_unit_price'],
+        index: 'ecommerce',
+        query: { bool: { must: [{ match_all: {} }] } },
+        timeField: 'order_date',
+      },
+      expected: {
+        responseCode: 200,
+        responseBody: { name: '30m', ms: 1800000 },
+      },
+    },
+    {
+      testTitleSuffix: 'with 1 field, 1 agg, 1 split with cardinality 46',
+      user: USER.ML_POWERUSER,
+      requestBody: {
+        aggTypes: ['avg'],
+        duration: { start: 1560297859000, end: 1562975136000 },
+        fields: ['taxless_total_price'],
+        index: 'ecommerce',
+        query: { bool: { must: [{ match_all: {} }] } },
+        splitField: 'customer_first_name.keyword',
+        timeField: 'order_date',
+      },
+      expected: {
+        responseCode: 200,
+        responseBody: { name: '3h', ms: 10800000 },
+      },
+    },
+  ];
 
-  describe('bucket span estimator', () => {
+  describe('bucket span estimator', function() {
     before(async () => {
       await esArchiver.load('ml/ecommerce');
     });
@@ -79,6 +84,7 @@ export default ({ getService }: FtrProviderContext) => {
       it(`estimates the bucket span ${testData.testTitleSuffix}`, async () => {
         const { body } = await supertest
           .post('/api/ml/validate/estimate_bucket_span')
+          .auth(testData.user, mlSecurity.getPasswordForUser(testData.user))
           .set(COMMON_HEADERS)
           .send(testData.requestBody)
           .expect(testData.expected.responseCode);
diff --git a/x-pack/test/api_integration/apis/ml/calculate_model_memory_limit.ts b/x-pack/test/api_integration/apis/ml/calculate_model_memory_limit.ts
index b725996533732..e67d87ca37c01 100644
--- a/x-pack/test/api_integration/apis/ml/calculate_model_memory_limit.ts
+++ b/x-pack/test/api_integration/apis/ml/calculate_model_memory_limit.ts
@@ -7,138 +7,145 @@
 import expect from '@kbn/expect';
 
 import { FtrProviderContext } from '../../ftr_provider_context';
+import { USER } from '../../../functional/services/machine_learning/security_common';
 
 const COMMON_HEADERS = {
   'kbn-xsrf': 'some-xsrf-token',
 };
 
-const testDataList = [
-  {
-    testTitleSuffix: 'with 0 metrics, 0 influencers and no split field',
-    requestBody: {
-      indexPattern: 'ecommerce',
-      splitFieldName: '',
-      query: { bool: { must: [{ match_all: {} }], filter: [], must_not: [] } },
-      fieldNames: ['__ml_event_rate_count__'],
-      influencerNames: [],
-      timeFieldName: 'order_date',
-      earliestMs: 1560297859000,
-      latestMs: 1562975136000,
-    },
-    expected: {
-      responseCode: 400,
-      responseBody: {
-        statusCode: 400,
-        error: 'Bad Request',
-        message: "[illegal_argument_exception] specified fields can't be null or empty",
+// eslint-disable-next-line import/no-default-export
+export default ({ getService }: FtrProviderContext) => {
+  const esArchiver = getService('esArchiver');
+  const supertest = getService('supertestWithoutAuth');
+  const mlSecurity = getService('mlSecurity');
+
+  const testDataList = [
+    {
+      testTitleSuffix: 'with 0 metrics, 0 influencers and no split field',
+      user: USER.ML_POWERUSER,
+      requestBody: {
+        indexPattern: 'ecommerce',
+        splitFieldName: '',
+        query: { bool: { must: [{ match_all: {} }], filter: [], must_not: [] } },
+        fieldNames: ['__ml_event_rate_count__'],
+        influencerNames: [],
+        timeFieldName: 'order_date',
+        earliestMs: 1560297859000,
+        latestMs: 1562975136000,
+      },
+      expected: {
+        responseCode: 400,
+        responseBody: {
+          statusCode: 400,
+          error: 'Bad Request',
+          message: "[illegal_argument_exception] specified fields can't be null or empty",
+        },
       },
     },
-  },
-  {
-    testTitleSuffix: 'with 1 metrics and 1 influencers same as split field',
-    requestBody: {
-      indexPattern: 'ecommerce',
-      splitFieldName: 'geoip.city_name',
-      query: { bool: { must: [{ match_all: {} }], filter: [], must_not: [] } },
-      fieldNames: ['products.base_price'],
-      influencerNames: ['geoip.city_name'],
-      timeFieldName: 'order_date',
-      earliestMs: 1560297859000,
-      latestMs: 1562975136000,
-    },
-    expected: {
-      responseCode: 200,
-      responseBody: { modelMemoryLimit: '12MB' },
-    },
-  },
-  {
-    testTitleSuffix: 'with 3 metrics, 3 influencers, split by city',
-    requestBody: {
-      indexPattern: 'ecommerce',
-      splitFieldName: 'geoip.city_name',
-      query: { bool: { must: [{ match_all: {} }], filter: [], must_not: [] } },
-      fieldNames: ['products.base_price', 'taxful_total_price', 'products.discount_amount'],
-      influencerNames: ['geoip.city_name', 'customer_gender', 'customer_full_name.keyword'],
-      timeFieldName: 'order_date',
-      earliestMs: 1560297859000,
-      latestMs: 1562975136000,
-    },
-    expected: {
-      responseCode: 200,
-      responseBody: { modelMemoryLimit: '14MB' },
+    {
+      testTitleSuffix: 'with 1 metrics and 1 influencers same as split field',
+      user: USER.ML_POWERUSER,
+      requestBody: {
+        indexPattern: 'ecommerce',
+        splitFieldName: 'geoip.city_name',
+        query: { bool: { must: [{ match_all: {} }], filter: [], must_not: [] } },
+        fieldNames: ['products.base_price'],
+        influencerNames: ['geoip.city_name'],
+        timeFieldName: 'order_date',
+        earliestMs: 1560297859000,
+        latestMs: 1562975136000,
+      },
+      expected: {
+        responseCode: 200,
+        responseBody: { modelMemoryLimit: '12MB' },
+      },
     },
-  },
-  {
-    testTitleSuffix: 'with 4 metrics, 4 influencers, split by customer_id',
-    requestBody: {
-      indexPattern: 'ecommerce',
-      splitFieldName: 'customer_id',
-      query: { bool: { must: [{ match_all: {} }], filter: [], must_not: [] } },
-      fieldNames: [
-        'geoip.country_iso_code',
-        'taxless_total_price',
-        'taxful_total_price',
-        'products.discount_amount',
-      ],
-      influencerNames: [
-        'customer_id',
-        'geoip.country_iso_code',
-        'products.discount_percentage',
-        'products.discount_amount',
-      ],
-      timeFieldName: 'order_date',
-      earliestMs: 1560297859000,
-      latestMs: 1562975136000,
+    {
+      testTitleSuffix: 'with 3 metrics, 3 influencers, split by city',
+      user: USER.ML_POWERUSER,
+      requestBody: {
+        indexPattern: 'ecommerce',
+        splitFieldName: 'geoip.city_name',
+        query: { bool: { must: [{ match_all: {} }], filter: [], must_not: [] } },
+        fieldNames: ['products.base_price', 'taxful_total_price', 'products.discount_amount'],
+        influencerNames: ['geoip.city_name', 'customer_gender', 'customer_full_name.keyword'],
+        timeFieldName: 'order_date',
+        earliestMs: 1560297859000,
+        latestMs: 1562975136000,
+      },
+      expected: {
+        responseCode: 200,
+        responseBody: { modelMemoryLimit: '14MB' },
+      },
     },
-    expected: {
-      responseCode: 200,
-      responseBody: { modelMemoryLimit: '23MB' },
+    {
+      testTitleSuffix: 'with 4 metrics, 4 influencers, split by customer_id',
+      user: USER.ML_POWERUSER,
+      requestBody: {
+        indexPattern: 'ecommerce',
+        splitFieldName: 'customer_id',
+        query: { bool: { must: [{ match_all: {} }], filter: [], must_not: [] } },
+        fieldNames: [
+          'geoip.country_iso_code',
+          'taxless_total_price',
+          'taxful_total_price',
+          'products.discount_amount',
+        ],
+        influencerNames: [
+          'customer_id',
+          'geoip.country_iso_code',
+          'products.discount_percentage',
+          'products.discount_amount',
+        ],
+        timeFieldName: 'order_date',
+        earliestMs: 1560297859000,
+        latestMs: 1562975136000,
+      },
+      expected: {
+        responseCode: 200,
+        responseBody: { modelMemoryLimit: '23MB' },
+      },
     },
-  },
-  {
-    testTitleSuffix:
-      'with 4 metrics, 4 influencers, split by customer_id and filtering by country code',
-    requestBody: {
-      indexPattern: 'ecommerce',
-      splitFieldName: 'customer_id',
-      query: {
-        bool: {
-          filter: {
-            term: {
-              'geoip.country_iso_code': 'US',
+    {
+      testTitleSuffix:
+        'with 4 metrics, 4 influencers, split by customer_id and filtering by country code',
+      user: USER.ML_POWERUSER,
+      requestBody: {
+        indexPattern: 'ecommerce',
+        splitFieldName: 'customer_id',
+        query: {
+          bool: {
+            filter: {
+              term: {
+                'geoip.country_iso_code': 'US',
+              },
             },
           },
         },
+        fieldNames: [
+          'geoip.country_iso_code',
+          'taxless_total_price',
+          'taxful_total_price',
+          'products.discount_amount',
+        ],
+        influencerNames: [
+          'customer_id',
+          'geoip.country_iso_code',
+          'products.discount_percentage',
+          'products.discount_amount',
+        ],
+        timeFieldName: 'order_date',
+        earliestMs: 1560297859000,
+        latestMs: 1562975136000,
+      },
+      expected: {
+        responseCode: 200,
+        responseBody: { modelMemoryLimit: '14MB' },
       },
-      fieldNames: [
-        'geoip.country_iso_code',
-        'taxless_total_price',
-        'taxful_total_price',
-        'products.discount_amount',
-      ],
-      influencerNames: [
-        'customer_id',
-        'geoip.country_iso_code',
-        'products.discount_percentage',
-        'products.discount_amount',
-      ],
-      timeFieldName: 'order_date',
-      earliestMs: 1560297859000,
-      latestMs: 1562975136000,
-    },
-    expected: {
-      responseCode: 200,
-      responseBody: { modelMemoryLimit: '14MB' },
     },
-  },
-];
-
-// eslint-disable-next-line import/no-default-export
-export default ({ getService }: FtrProviderContext) => {
-  const esArchiver = getService('esArchiver');
-  const supertest = getService('supertest');
+  ];
 
-  describe('calculate model memory limit', () => {
+  describe('calculate model memory limit', function() {
     before(async () => {
       await esArchiver.load('ml/ecommerce');
     });
@@ -151,6 +158,7 @@ export default ({ getService }: FtrProviderContext) => {
       it(`calculates the model memory limit ${testData.testTitleSuffix}`, async () => {
         const { body } = await supertest
           .post('/api/ml/validate/calculate_model_memory_limit')
+          .auth(testData.user, mlSecurity.getPasswordForUser(testData.user))
           .set(COMMON_HEADERS)
           .send(testData.requestBody)
           .expect(testData.expected.responseCode);
diff --git a/x-pack/test/api_integration/apis/ml/categorization_field_examples.ts b/x-pack/test/api_integration/apis/ml/categorization_field_examples.ts
index 89dbd73f3fb64..ba7b9c31ad64c 100644
--- a/x-pack/test/api_integration/apis/ml/categorization_field_examples.ts
+++ b/x-pack/test/api_integration/apis/ml/categorization_field_examples.ts
@@ -7,6 +7,7 @@
 import expect from '@kbn/expect';
 
 import { FtrProviderContext } from '../../ftr_provider_context';
+import { USER } from '../../../functional/services/machine_learning/security_common';
 
 const COMMON_HEADERS = {
   'kbn-xsrf': 'some-xsrf-token',
@@ -74,207 +75,217 @@ const defaultRequestBody = {
   analyzer,
 };
 
-const testDataList = [
-  {
-    title: 'valid with good number of tokens',
-    requestBody: {
-      ...defaultRequestBody,
-      field: 'field1',
-    },
-    expected: {
-      responseCode: 200,
-      overallValidStatus: 'valid',
-      sampleSize: 1000,
-      exampleLength: 5,
-      validationChecks: [
-        {
-          id: 0,
-          valid: 'valid',
-          message: '1000 field values analyzed, 95% contain 3 or more tokens.',
-        },
-      ],
-    },
-  },
-  {
-    title: 'invalid, too many tokens.',
-    requestBody: {
-      ...defaultRequestBody,
-      field: 'field2',
-    },
-    expected: {
-      responseCode: 200,
-      overallValidStatus: 'invalid',
-      sampleSize: 500,
-      exampleLength: 5,
-      validationChecks: [
-        {
-          id: 1,
-          valid: 'partially_valid',
-          message: 'The median length for the field values analyzed is over 400 characters.',
-        },
-        {
-          id: 4,
-          valid: 'invalid',
-          message:
-            'Tokenization of field value examples has failed due to more than 10000 tokens being found in a sample of 50 values.',
-        },
-      ],
-    },
-  },
-  {
-    title: 'partially valid, more than 75% are null',
-    requestBody: {
-      ...defaultRequestBody,
-      field: 'field3',
-    },
-    expected: {
-      responseCode: 200,
-      overallValidStatus: 'partially_valid',
-      sampleSize: 250,
-      exampleLength: 5,
-      validationChecks: [
-        {
-          id: 0,
-          valid: 'valid',
-          message: '250 field values analyzed, 95% contain 3 or more tokens.',
-        },
-        {
-          id: 2,
-          valid: 'partially_valid',
-          message: 'More than 75% of field values are null.',
-        },
-      ],
-    },
-  },
-  {
-    title: 'partially valid, median length is over 400 characters',
-    requestBody: {
-      ...defaultRequestBody,
-      field: 'field4',
-    },
-    expected: {
-      responseCode: 200,
-      overallValidStatus: 'partially_valid',
-      sampleSize: 500,
-      exampleLength: 5,
-      validationChecks: [
-        {
-          id: 0,
-          valid: 'valid',
-          message: '500 field values analyzed, 100% contain 3 or more tokens.',
-        },
-        {
-          id: 1,
-          valid: 'partially_valid',
-          message: 'The median length for the field values analyzed is over 400 characters.',
-        },
-      ],
-    },
-  },
-  {
-    title: 'invalid, no values in any doc',
-    requestBody: {
-      ...defaultRequestBody,
-      field: 'field5',
-    },
-    expected: {
-      responseCode: 200,
-      overallValidStatus: 'invalid',
-      sampleSize: 0,
-      exampleLength: 0,
-      validationChecks: [
-        {
-          id: 3,
-          valid: 'invalid',
-          message:
-            'No examples for this field could be found. Please ensure the selected date range contains data.',
-        },
-      ],
+// eslint-disable-next-line import/no-default-export
+export default ({ getService }: FtrProviderContext) => {
+  const esArchiver = getService('esArchiver');
+  const supertest = getService('supertestWithoutAuth');
+  const mlSecurity = getService('mlSecurity');
+
+  const testDataList = [
+    {
+      title: 'valid with good number of tokens',
+      user: USER.ML_POWERUSER,
+      requestBody: {
+        ...defaultRequestBody,
+        field: 'field1',
+      },
+      expected: {
+        responseCode: 200,
+        overallValidStatus: 'valid',
+        sampleSize: 1000,
+        exampleLength: 5,
+        validationChecks: [
+          {
+            id: 0,
+            valid: 'valid',
+            message: '1000 field values analyzed, 95% contain 3 or more tokens.',
+          },
+        ],
+      },
     },
-  },
-  {
-    title: 'invalid, mostly made up of stop words, so no matched tokens',
-    requestBody: {
-      ...defaultRequestBody,
-      field: 'field6',
+    {
+      title: 'invalid, too many tokens.',
+      user: USER.ML_POWERUSER,
+      requestBody: {
+        ...defaultRequestBody,
+        field: 'field2',
+      },
+      expected: {
+        responseCode: 200,
+        overallValidStatus: 'invalid',
+        sampleSize: 500,
+        exampleLength: 5,
+        validationChecks: [
+          {
+            id: 1,
+            valid: 'partially_valid',
+            message: 'The median length for the field values analyzed is over 400 characters.',
+          },
+          {
+            id: 4,
+            valid: 'invalid',
+            message:
+              'Tokenization of field value examples has failed due to more than 10000 tokens being found in a sample of 50 values.',
+          },
+        ],
+      },
     },
-    expected: {
-      responseCode: 200,
-      overallValidStatus: 'invalid',
-      sampleSize: 1000,
-      exampleLength: 5,
-      validationChecks: [
-        {
-          id: 0,
-          valid: 'invalid',
-          message: '1000 field values analyzed, 0% contain 3 or more tokens.',
-        },
-      ],
+    {
+      title: 'partially valid, more than 75% are null',
+      user: USER.ML_POWERUSER,
+      requestBody: {
+        ...defaultRequestBody,
+        field: 'field3',
+      },
+      expected: {
+        responseCode: 200,
+        overallValidStatus: 'partially_valid',
+        sampleSize: 250,
+        exampleLength: 5,
+        validationChecks: [
+          {
+            id: 0,
+            valid: 'valid',
+            message: '250 field values analyzed, 95% contain 3 or more tokens.',
+          },
+          {
+            id: 2,
+            valid: 'partially_valid',
+            message: 'More than 75% of field values are null.',
+          },
+        ],
+      },
     },
-  },
-  {
-    title: 'valid, mostly made up of stop words, but analyser has no stop words. so it is ok.',
-    requestBody: {
-      ...defaultRequestBody,
-      field: 'field6',
-      analyzer: {
-        tokenizer: 'ml_classic',
+    {
+      title: 'partially valid, median length is over 400 characters',
+      user: USER.ML_POWERUSER,
+      requestBody: {
+        ...defaultRequestBody,
+        field: 'field4',
+      },
+      expected: {
+        responseCode: 200,
+        overallValidStatus: 'partially_valid',
+        sampleSize: 500,
+        exampleLength: 5,
+        validationChecks: [
+          {
+            id: 0,
+            valid: 'valid',
+            message: '500 field values analyzed, 100% contain 3 or more tokens.',
+          },
+          {
+            id: 1,
+            valid: 'partially_valid',
+            message: 'The median length for the field values analyzed is over 400 characters.',
+          },
+        ],
       },
     },
-    expected: {
-      responseCode: 200,
-      overallValidStatus: 'valid',
-      sampleSize: 1000,
-      exampleLength: 5,
-      validationChecks: [
-        {
-          id: 0,
-          valid: 'valid',
-          message: '1000 field values analyzed, 100% contain 3 or more tokens.',
-        },
-      ],
+    {
+      title: 'invalid, no values in any doc',
+      user: USER.ML_POWERUSER,
+      requestBody: {
+        ...defaultRequestBody,
+        field: 'field5',
+      },
+      expected: {
+        responseCode: 200,
+        overallValidStatus: 'invalid',
+        sampleSize: 0,
+        exampleLength: 0,
+        validationChecks: [
+          {
+            id: 3,
+            valid: 'invalid',
+            message:
+              'No examples for this field could be found. Please ensure the selected date range contains data.',
+          },
+        ],
+      },
     },
-  },
-  {
-    title: 'partially valid, half the docs are stop words.',
-    requestBody: {
-      ...defaultRequestBody,
-      field: 'field7',
+    {
+      title: 'invalid, mostly made up of stop words, so no matched tokens',
+      user: USER.ML_POWERUSER,
+      requestBody: {
+        ...defaultRequestBody,
+        field: 'field6',
+      },
+      expected: {
+        responseCode: 200,
+        overallValidStatus: 'invalid',
+        sampleSize: 1000,
+        exampleLength: 5,
+        validationChecks: [
+          {
+            id: 0,
+            valid: 'invalid',
+            message: '1000 field values analyzed, 0% contain 3 or more tokens.',
+          },
+        ],
+      },
     },
-    expected: {
-      responseCode: 200,
-      overallValidStatus: 'partially_valid',
-      sampleSize: 1000,
-      exampleLength: 5,
-      validationChecks: [
-        {
-          id: 0,
-          valid: 'partially_valid',
-          message: '1000 field values analyzed, 50% contain 3 or more tokens.',
+    {
+      title: 'valid, mostly made up of stop words, but analyser has no stop words. so it is ok.',
+      user: USER.ML_POWERUSER,
+      requestBody: {
+        ...defaultRequestBody,
+        field: 'field6',
+        analyzer: {
+          tokenizer: 'ml_classic',
         },
-      ],
+      },
+      expected: {
+        responseCode: 200,
+        overallValidStatus: 'valid',
+        sampleSize: 1000,
+        exampleLength: 5,
+        validationChecks: [
+          {
+            id: 0,
+            valid: 'valid',
+            message: '1000 field values analyzed, 100% contain 3 or more tokens.',
+          },
+        ],
+      },
     },
-  },
-  {
-    title: "endpoint error, index doesn't exist",
-    requestBody: {
-      ...defaultRequestBody,
-      indexPatternTitle: 'does_not_exist',
-      field: 'field1',
+    {
+      title: 'partially valid, half the docs are stop words.',
+      user: USER.ML_POWERUSER,
+      requestBody: {
+        ...defaultRequestBody,
+        field: 'field7',
+      },
+      expected: {
+        responseCode: 200,
+        overallValidStatus: 'partially_valid',
+        sampleSize: 1000,
+        exampleLength: 5,
+        validationChecks: [
+          {
+            id: 0,
+            valid: 'partially_valid',
+            message: '1000 field values analyzed, 50% contain 3 or more tokens.',
+          },
+        ],
+      },
     },
-    expected: {
-      responseCode: 404,
-      overallValidStatus: undefined,
-      sampleSize: undefined,
-      validationChecks: undefined,
+    {
+      title: "endpoint error, index doesn't exist",
+      user: USER.ML_POWERUSER,
+      requestBody: {
+        ...defaultRequestBody,
+        indexPatternTitle: 'does_not_exist',
+        field: 'field1',
+      },
+      expected: {
+        responseCode: 404,
+        overallValidStatus: undefined,
+        sampleSize: undefined,
+        validationChecks: undefined,
+      },
     },
-  },
-];
-
-// eslint-disable-next-line import/no-default-export
-export default ({ getService }: FtrProviderContext) => {
-  const esArchiver = getService('esArchiver');
-  const supertest = getService('supertest');
+  ];
 
   describe('Categorization example endpoint - ', function() {
     before(async () => {
@@ -289,6 +300,7 @@ export default ({ getService }: FtrProviderContext) => {
       it(testData.title, async () => {
         const { body } = await supertest
           .post('/api/ml/jobs/categorization_field_examples')
+          .auth(testData.user, mlSecurity.getPasswordForUser(testData.user))
           .set(COMMON_HEADERS)
           .send(testData.requestBody)
           .expect(testData.expected.responseCode);
diff --git a/x-pack/test/api_integration/apis/ml/index.ts b/x-pack/test/api_integration/apis/ml/index.ts
index 1df5dfe2941ce..e2000b661367f 100644
--- a/x-pack/test/api_integration/apis/ml/index.ts
+++ b/x-pack/test/api_integration/apis/ml/index.ts
@@ -6,10 +6,22 @@
 
 import { FtrProviderContext } from '../../ftr_provider_context';
 
-export default function({ loadTestFile }: FtrProviderContext) {
+export default function({ getService, loadTestFile }: FtrProviderContext) {
+  const mlSecurity = getService('mlSecurity');
+
   describe('Machine Learning', function() {
     this.tags(['mlqa']);
 
+    before(async () => {
+      await mlSecurity.createMlRoles();
+      await mlSecurity.createMlUsers();
+    });
+
+    after(async () => {
+      await mlSecurity.cleanMlUsers();
+      await mlSecurity.cleanMlRoles();
+    });
+
     loadTestFile(require.resolve('./bucket_span_estimator'));
     loadTestFile(require.resolve('./calculate_model_memory_limit'));
     loadTestFile(require.resolve('./categorization_field_examples'));
diff --git a/x-pack/test/api_integration/services/index.ts b/x-pack/test/api_integration/services/index.ts
index b6d77262d90e9..c29116e1270c5 100644
--- a/x-pack/test/api_integration/services/index.ts
+++ b/x-pack/test/api_integration/services/index.ts
@@ -21,6 +21,7 @@ import {
 } from './infraops_graphql_client';
 import { SiemGraphQLClientProvider, SiemGraphQLClientFactoryProvider } from './siem_graphql_client';
 import { InfraOpsSourceConfigurationProvider } from './infraops_source_configuration';
+import { MachineLearningSecurityCommonProvider } from '../../functional/services/machine_learning';
 
 export const services = {
   ...commonServices,
@@ -37,4 +38,5 @@ export const services = {
   siemGraphQLClientFactory: SiemGraphQLClientFactoryProvider,
   supertestWithoutAuth: SupertestWithoutAuthProvider,
   usageAPI: UsageAPIProvider,
+  mlSecurity: MachineLearningSecurityCommonProvider,
 };
diff --git a/x-pack/test/functional/apps/machine_learning/anomaly_detection/advanced_job.ts b/x-pack/test/functional/apps/machine_learning/anomaly_detection/advanced_job.ts
index acb92b270c4a1..3669ed3ab579b 100644
--- a/x-pack/test/functional/apps/machine_learning/anomaly_detection/advanced_job.ts
+++ b/x-pack/test/functional/apps/machine_learning/anomaly_detection/advanced_job.ts
@@ -279,6 +279,7 @@ export default function({ getService }: FtrProviderContext) {
     before(async () => {
       await esArchiver.load('ml/ecommerce');
       await ml.api.createCalendar('wizard-test-calendar');
+      await ml.securityUI.loginAsMlPowerUser();
     });
 
     after(async () => {
diff --git a/x-pack/test/functional/apps/machine_learning/anomaly_detection/anomaly_explorer.ts b/x-pack/test/functional/apps/machine_learning/anomaly_detection/anomaly_explorer.ts
index 83c348e824fa8..8a1ef5f196eba 100644
--- a/x-pack/test/functional/apps/machine_learning/anomaly_detection/anomaly_explorer.ts
+++ b/x-pack/test/functional/apps/machine_learning/anomaly_detection/anomaly_explorer.ts
@@ -47,6 +47,7 @@ export default function({ getService }: FtrProviderContext) {
     before(async () => {
       await esArchiver.load('ml/farequote');
       await ml.api.createAndRunAnomalyDetectionLookbackJob(JOB_CONFIG, DATAFEED_CONFIG);
+      await ml.securityUI.loginAsMlPowerUser();
     });
 
     after(async () => {
diff --git a/x-pack/test/functional/apps/machine_learning/anomaly_detection/categorization_job.ts b/x-pack/test/functional/apps/machine_learning/anomaly_detection/categorization_job.ts
index 6ea9e694d2955..cb344438e8e9b 100644
--- a/x-pack/test/functional/apps/machine_learning/anomaly_detection/categorization_job.ts
+++ b/x-pack/test/functional/apps/machine_learning/anomaly_detection/categorization_job.ts
@@ -79,6 +79,7 @@ export default function({ getService }: FtrProviderContext) {
     before(async () => {
       await esArchiver.load('ml/categorization');
       await ml.api.createCalendar('wizard-test-calendar');
+      await ml.securityUI.loginAsMlPowerUser();
     });
 
     after(async () => {
diff --git a/x-pack/test/functional/apps/machine_learning/anomaly_detection/multi_metric_job.ts b/x-pack/test/functional/apps/machine_learning/anomaly_detection/multi_metric_job.ts
index 6a12a28e8ac49..f886453f7c534 100644
--- a/x-pack/test/functional/apps/machine_learning/anomaly_detection/multi_metric_job.ts
+++ b/x-pack/test/functional/apps/machine_learning/anomaly_detection/multi_metric_job.ts
@@ -76,6 +76,7 @@ export default function({ getService }: FtrProviderContext) {
     before(async () => {
       await esArchiver.load('ml/farequote');
       await ml.api.createCalendar('wizard-test-calendar');
+      await ml.securityUI.loginAsMlPowerUser();
     });
 
     after(async () => {
diff --git a/x-pack/test/functional/apps/machine_learning/anomaly_detection/population_job.ts b/x-pack/test/functional/apps/machine_learning/anomaly_detection/population_job.ts
index 6593dd10928b4..e8f45891ce064 100644
--- a/x-pack/test/functional/apps/machine_learning/anomaly_detection/population_job.ts
+++ b/x-pack/test/functional/apps/machine_learning/anomaly_detection/population_job.ts
@@ -90,6 +90,7 @@ export default function({ getService }: FtrProviderContext) {
     before(async () => {
       await esArchiver.load('ml/ecommerce');
       await ml.api.createCalendar('wizard-test-calendar');
+      await ml.securityUI.loginAsMlPowerUser();
     });
 
     after(async () => {
diff --git a/x-pack/test/functional/apps/machine_learning/anomaly_detection/saved_search_job.ts b/x-pack/test/functional/apps/machine_learning/anomaly_detection/saved_search_job.ts
index 348910a2a8f84..a13cf3d61128e 100644
--- a/x-pack/test/functional/apps/machine_learning/anomaly_detection/saved_search_job.ts
+++ b/x-pack/test/functional/apps/machine_learning/anomaly_detection/saved_search_job.ts
@@ -275,6 +275,7 @@ export default function({ getService }: FtrProviderContext) {
     this.tags(['smoke', 'mlqa']);
     before(async () => {
       await esArchiver.load('ml/farequote');
+      await ml.securityUI.loginAsMlPowerUser();
     });
 
     after(async () => {
diff --git a/x-pack/test/functional/apps/machine_learning/anomaly_detection/single_metric_job.ts b/x-pack/test/functional/apps/machine_learning/anomaly_detection/single_metric_job.ts
index 13cac36d99a1b..0d7e87cf6bd38 100644
--- a/x-pack/test/functional/apps/machine_learning/anomaly_detection/single_metric_job.ts
+++ b/x-pack/test/functional/apps/machine_learning/anomaly_detection/single_metric_job.ts
@@ -75,6 +75,7 @@ export default function({ getService }: FtrProviderContext) {
     before(async () => {
       await esArchiver.load('ml/farequote');
       await ml.api.createCalendar('wizard-test-calendar');
+      await ml.securityUI.loginAsMlPowerUser();
     });
 
     after(async () => {
diff --git a/x-pack/test/functional/apps/machine_learning/anomaly_detection/single_metric_viewer.ts b/x-pack/test/functional/apps/machine_learning/anomaly_detection/single_metric_viewer.ts
index a52ef6442cf21..407e002f11f99 100644
--- a/x-pack/test/functional/apps/machine_learning/anomaly_detection/single_metric_viewer.ts
+++ b/x-pack/test/functional/apps/machine_learning/anomaly_detection/single_metric_viewer.ts
@@ -47,6 +47,7 @@ export default function({ getService }: FtrProviderContext) {
     before(async () => {
       await esArchiver.load('ml/farequote');
       await ml.api.createAndRunAnomalyDetectionLookbackJob(JOB_CONFIG, DATAFEED_CONFIG);
+      await ml.securityUI.loginAsMlPowerUser();
     });
 
     after(async () => {
diff --git a/x-pack/test/functional/apps/machine_learning/data_frame_analytics/classification_creation.ts b/x-pack/test/functional/apps/machine_learning/data_frame_analytics/classification_creation.ts
index 1b8c8299a8ac6..798a04cae3740 100644
--- a/x-pack/test/functional/apps/machine_learning/data_frame_analytics/classification_creation.ts
+++ b/x-pack/test/functional/apps/machine_learning/data_frame_analytics/classification_creation.ts
@@ -15,6 +15,7 @@ export default function({ getService }: FtrProviderContext) {
     this.tags(['smoke']);
     before(async () => {
       await esArchiver.load('ml/bm_classification');
+      await ml.securityUI.loginAsMlPowerUser();
     });
 
     after(async () => {
@@ -31,7 +32,7 @@ export default function({ getService }: FtrProviderContext) {
           "Classification job based on 'bank-marketing' dataset with dependentVariable 'y' and trainingPercent '20'",
         source: 'bank-marketing*',
         get destinationIndex(): string {
-          return `dest_${this.jobId}`;
+          return `user-${this.jobId}`;
         },
         dependentVariable: 'y',
         trainingPercent: '20',
diff --git a/x-pack/test/functional/apps/machine_learning/data_frame_analytics/outlier_detection_creation.ts b/x-pack/test/functional/apps/machine_learning/data_frame_analytics/outlier_detection_creation.ts
index 2b64847602c4c..173430706f7e7 100644
--- a/x-pack/test/functional/apps/machine_learning/data_frame_analytics/outlier_detection_creation.ts
+++ b/x-pack/test/functional/apps/machine_learning/data_frame_analytics/outlier_detection_creation.ts
@@ -15,6 +15,7 @@ export default function({ getService }: FtrProviderContext) {
     this.tags(['smoke']);
     before(async () => {
       await esArchiver.load('ml/ihp_outlier');
+      await ml.securityUI.loginAsMlPowerUser();
     });
 
     after(async () => {
@@ -30,7 +31,7 @@ export default function({ getService }: FtrProviderContext) {
         jobDescription: 'This is the job description',
         source: 'ihp_outlier',
         get destinationIndex(): string {
-          return `dest_${this.jobId}`;
+          return `user-${this.jobId}`;
         },
         modelMemory: '55mb',
         createIndexPattern: true,
diff --git a/x-pack/test/functional/apps/machine_learning/data_frame_analytics/regression_creation.ts b/x-pack/test/functional/apps/machine_learning/data_frame_analytics/regression_creation.ts
index 6a01afe6183ed..3bc524a88f787 100644
--- a/x-pack/test/functional/apps/machine_learning/data_frame_analytics/regression_creation.ts
+++ b/x-pack/test/functional/apps/machine_learning/data_frame_analytics/regression_creation.ts
@@ -15,6 +15,7 @@ export default function({ getService }: FtrProviderContext) {
     this.tags(['smoke']);
     before(async () => {
       await esArchiver.load('ml/egs_regression');
+      await ml.securityUI.loginAsMlPowerUser();
     });
 
     after(async () => {
@@ -30,7 +31,7 @@ export default function({ getService }: FtrProviderContext) {
         jobDescription: 'This is the job description',
         source: 'egs_regression',
         get destinationIndex(): string {
-          return `dest_${this.jobId}`;
+          return `user-${this.jobId}`;
         },
         dependentVariable: 'stab',
         trainingPercent: '20',
diff --git a/x-pack/test/functional/apps/machine_learning/data_visualizer/index_data_visualizer.ts b/x-pack/test/functional/apps/machine_learning/data_visualizer/index_data_visualizer.ts
index cb1b652073469..aacde7ee473b9 100644
--- a/x-pack/test/functional/apps/machine_learning/data_visualizer/index_data_visualizer.ts
+++ b/x-pack/test/functional/apps/machine_learning/data_visualizer/index_data_visualizer.ts
@@ -379,6 +379,7 @@ export default function({ getService }: FtrProviderContext) {
     this.tags(['smoke', 'mlqa']);
     before(async () => {
       await esArchiver.load('ml/farequote');
+      await ml.securityUI.loginAsMlPowerUser();
     });
 
     after(async () => {
diff --git a/x-pack/test/functional/apps/machine_learning/index.ts b/x-pack/test/functional/apps/machine_learning/index.ts
index 989f51f4bdde4..47c699e309491 100644
--- a/x-pack/test/functional/apps/machine_learning/index.ts
+++ b/x-pack/test/functional/apps/machine_learning/index.ts
@@ -5,10 +5,22 @@
  */
 import { FtrProviderContext } from '../../ftr_provider_context';
 
-export default function({ loadTestFile }: FtrProviderContext) {
+export default function({ getService, loadTestFile }: FtrProviderContext) {
+  const ml = getService('ml');
+
   describe('machine learning', function() {
     this.tags('ciGroup3');
 
+    before(async () => {
+      await ml.securityCommon.createMlRoles();
+      await ml.securityCommon.createMlUsers();
+    });
+
+    after(async () => {
+      await ml.securityCommon.cleanMlUsers();
+      await ml.securityCommon.cleanMlRoles();
+    });
+
     loadTestFile(require.resolve('./feature_controls'));
     loadTestFile(require.resolve('./pages'));
     loadTestFile(require.resolve('./anomaly_detection'));
diff --git a/x-pack/test/functional/apps/machine_learning/pages.ts b/x-pack/test/functional/apps/machine_learning/pages.ts
index 01d91bdfc3075..78c6ec4f1b2ff 100644
--- a/x-pack/test/functional/apps/machine_learning/pages.ts
+++ b/x-pack/test/functional/apps/machine_learning/pages.ts
@@ -14,6 +14,7 @@ export default function({ getService }: FtrProviderContext) {
     this.tags(['smoke', 'mlqa']);
     before(async () => {
       await esArchiver.load('empty_kibana');
+      await ml.securityUI.loginAsMlPowerUser();
     });
 
     after(async () => {
diff --git a/x-pack/test/functional/apps/transform/creation_index_pattern.ts b/x-pack/test/functional/apps/transform/creation_index_pattern.ts
index 3dbf61221abf9..6e35b0c1a81ca 100644
--- a/x-pack/test/functional/apps/transform/creation_index_pattern.ts
+++ b/x-pack/test/functional/apps/transform/creation_index_pattern.ts
@@ -21,6 +21,7 @@ export default function({ getService }: FtrProviderContext) {
     this.tags(['smoke']);
     before(async () => {
       await esArchiver.load('ml/ecommerce');
+      await transform.securityUI.loginAsTransformPowerUser();
     });
 
     after(async () => {
@@ -53,7 +54,7 @@ export default function({ getService }: FtrProviderContext) {
         transformDescription:
           'ecommerce batch transform with groups terms(category.keyword) + date_histogram(order_date) 1m and aggregation avg(products.base_price)',
         get destinationIndex(): string {
-          return `dest_${this.transformId}`;
+          return `user-${this.transformId}`;
         },
         expected: {
           pivotPreview: {
@@ -200,14 +201,17 @@ export default function({ getService }: FtrProviderContext) {
 
         it('displays the create and start button', async () => {
           await transform.wizard.assertCreateAndStartButtonExists();
+          await transform.wizard.assertCreateAndStartButtonEnabled(true);
         });
 
         it('displays the create button', async () => {
           await transform.wizard.assertCreateButtonExists();
+          await transform.wizard.assertCreateButtonEnabled(true);
         });
 
         it('displays the copy to clipboard button', async () => {
-          await transform.wizard.assertCreateAndStartButtonExists();
+          await transform.wizard.assertCopyToClipboardButtonExists();
+          await transform.wizard.assertCopyToClipboardButtonEnabled(true);
         });
 
         it('creates the transform', async () => {
diff --git a/x-pack/test/functional/apps/transform/creation_saved_search.ts b/x-pack/test/functional/apps/transform/creation_saved_search.ts
index 333a53a98c82b..2f5f60e1573c8 100644
--- a/x-pack/test/functional/apps/transform/creation_saved_search.ts
+++ b/x-pack/test/functional/apps/transform/creation_saved_search.ts
@@ -17,11 +17,11 @@ export default function({ getService }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
   const transform = getService('transform');
 
-  // flaky test, see #55179
-  describe.skip('creation_saved_search', function() {
+  describe('creation_saved_search', function() {
     this.tags(['smoke']);
     before(async () => {
       await esArchiver.load('ml/farequote');
+      await transform.securityUI.loginAsTransformPowerUser();
     });
 
     after(async () => {
@@ -49,7 +49,7 @@ export default function({ getService }: FtrProviderContext) {
         transformDescription:
           'farequote batch transform with groups terms(airline) and aggregation avg(responsetime.avg) with saved search filter',
         get destinationIndex(): string {
-          return `dest_${this.transformId}`;
+          return `user-${this.transformId}`;
         },
         expected: {
           pivotPreview: {
@@ -195,14 +195,17 @@ export default function({ getService }: FtrProviderContext) {
 
         it('displays the create and start button', async () => {
           await transform.wizard.assertCreateAndStartButtonExists();
+          await transform.wizard.assertCreateAndStartButtonEnabled(true);
         });
 
         it('displays the create button', async () => {
           await transform.wizard.assertCreateButtonExists();
+          await transform.wizard.assertCreateButtonEnabled(true);
         });
 
         it('displays the copy to clipboard button', async () => {
-          await transform.wizard.assertCreateAndStartButtonExists();
+          await transform.wizard.assertCopyToClipboardButtonExists();
+          await transform.wizard.assertCopyToClipboardButtonEnabled(true);
         });
 
         it('creates the transform', async () => {
diff --git a/x-pack/test/functional/apps/transform/index.ts b/x-pack/test/functional/apps/transform/index.ts
index 0a33ce0ebf08a..66a55105b3ca8 100644
--- a/x-pack/test/functional/apps/transform/index.ts
+++ b/x-pack/test/functional/apps/transform/index.ts
@@ -5,10 +5,22 @@
  */
 import { FtrProviderContext } from '../../ftr_provider_context';
 
-export default function({ loadTestFile }: FtrProviderContext) {
+export default function({ getService, loadTestFile }: FtrProviderContext) {
+  const transform = getService('transform');
+
   describe('transform', function() {
     this.tags(['ciGroup9', 'transform']);
 
+    before(async () => {
+      await transform.securityCommon.createTransformRoles();
+      await transform.securityCommon.createTransformUsers();
+    });
+
+    after(async () => {
+      await transform.securityCommon.cleanTransformUsers();
+      await transform.securityCommon.cleanTransformRoles();
+    });
+
     loadTestFile(require.resolve('./creation_index_pattern'));
     loadTestFile(require.resolve('./creation_saved_search'));
   });
diff --git a/x-pack/test/functional/services/machine_learning/index.ts b/x-pack/test/functional/services/machine_learning/index.ts
index 4cecd27631e18..b916c1e7909b1 100644
--- a/x-pack/test/functional/services/machine_learning/index.ts
+++ b/x-pack/test/functional/services/machine_learning/index.ts
@@ -25,5 +25,7 @@ export { MachineLearningJobWizardCategorizationProvider } from './job_wizard_cat
 export { MachineLearningJobWizardMultiMetricProvider } from './job_wizard_multi_metric';
 export { MachineLearningJobWizardPopulationProvider } from './job_wizard_population';
 export { MachineLearningNavigationProvider } from './navigation';
+export { MachineLearningSecurityCommonProvider } from './security_common';
+export { MachineLearningSecurityUIProvider } from './security_ui';
 export { MachineLearningSettingsProvider } from './settings';
 export { MachineLearningSingleMetricViewerProvider } from './single_metric_viewer';
diff --git a/x-pack/test/functional/services/machine_learning/security_common.ts b/x-pack/test/functional/services/machine_learning/security_common.ts
new file mode 100644
index 0000000000000..069aec1697ebd
--- /dev/null
+++ b/x-pack/test/functional/services/machine_learning/security_common.ts
@@ -0,0 +1,106 @@
+/*
+ * 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 { ProvidedType } from '@kbn/test/types/ftr';
+
+import { FtrProviderContext } from '../../ftr_provider_context';
+
+export type MlSecurityCommon = ProvidedType<typeof MachineLearningSecurityCommonProvider>;
+
+export enum USER {
+  ML_POWERUSER = 'ml_poweruser',
+  ML_VIEWER = 'ml_viewer',
+}
+
+export function MachineLearningSecurityCommonProvider({ getService }: FtrProviderContext) {
+  const security = getService('security');
+
+  const roles = [
+    {
+      name: 'ml_source',
+      elasticsearch: {
+        indices: [{ names: ['*'], privileges: ['read', 'view_index_metadata'] }],
+      },
+      kibana: [],
+    },
+    {
+      name: 'ml_dest',
+      elasticsearch: {
+        indices: [{ names: ['user-*'], privileges: ['read', 'index', 'manage'] }],
+      },
+      kibana: [],
+    },
+    {
+      name: 'ml_dest_readonly',
+      elasticsearch: {
+        indices: [{ names: ['user-*'], privileges: ['read'] }],
+      },
+      kibana: [],
+    },
+    {
+      name: 'ml_ui_extras',
+      elasticsearch: {
+        cluster: ['manage', 'manage_ingest_pipelines', 'monitor'],
+      },
+      kibana: [],
+    },
+  ];
+
+  const users = [
+    {
+      name: 'ml_poweruser',
+      full_name: 'ML Poweruser',
+      password: 'mlp001',
+      roles: ['kibana_admin', 'machine_learning_admin', 'ml_source', 'ml_dest', 'ml_ui_extras'],
+    },
+    {
+      name: 'ml_viewer',
+      full_name: 'ML Viewer',
+      password: 'mlv001',
+      roles: ['kibana_admin', 'machine_learning_user', 'ml_source', 'ml_dest_readonly'],
+    },
+  ];
+
+  return {
+    async createMlRoles() {
+      for (const role of roles) {
+        await security.role.create(role.name, {
+          elasticsearch: role.elasticsearch,
+          kibana: role.kibana,
+        });
+      }
+    },
+
+    async createMlUsers() {
+      for (const user of users) {
+        await security.user.create(user.name, {
+          password: user.password,
+          roles: user.roles,
+          full_name: user.full_name,
+        });
+      }
+    },
+
+    async cleanMlRoles() {
+      for (const role of roles) {
+        await security.role.delete(role.name);
+      }
+    },
+
+    async cleanMlUsers() {
+      for (const user of users) {
+        await security.user.delete(user.name);
+      }
+    },
+
+    getPasswordForUser(user: USER): string {
+      const userConfig = users.find(u => u.name === user);
+      if (userConfig === undefined) {
+        throw new Error(`Can't log in user ${user} - not defined`);
+      }
+      return userConfig.password;
+    },
+  };
+}
diff --git a/x-pack/test/functional/services/machine_learning/security_ui.ts b/x-pack/test/functional/services/machine_learning/security_ui.ts
new file mode 100644
index 0000000000000..73516ca58dd5d
--- /dev/null
+++ b/x-pack/test/functional/services/machine_learning/security_ui.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.
+ */
+
+import { FtrProviderContext } from '../../ftr_provider_context';
+import { MlSecurityCommon, USER } from './security_common';
+
+export function MachineLearningSecurityUIProvider(
+  { getPageObjects }: FtrProviderContext,
+  mlSecurityCommon: MlSecurityCommon
+) {
+  const PageObjects = getPageObjects(['security']);
+
+  return {
+    async loginAs(user: USER) {
+      const password = mlSecurityCommon.getPasswordForUser(user);
+
+      await PageObjects.security.forceLogout();
+
+      await PageObjects.security.login(user, password, {
+        expectSuccess: true,
+      });
+    },
+
+    async loginAsMlPowerUser() {
+      await this.loginAs(USER.ML_POWERUSER);
+    },
+
+    async loginAsMlViewer() {
+      await this.loginAs(USER.ML_VIEWER);
+    },
+  };
+}
diff --git a/x-pack/test/functional/services/ml.ts b/x-pack/test/functional/services/ml.ts
index 18574c62b84d9..2660a90662dec 100644
--- a/x-pack/test/functional/services/ml.ts
+++ b/x-pack/test/functional/services/ml.ts
@@ -28,6 +28,8 @@ import {
   MachineLearningJobWizardMultiMetricProvider,
   MachineLearningJobWizardPopulationProvider,
   MachineLearningNavigationProvider,
+  MachineLearningSecurityCommonProvider,
+  MachineLearningSecurityUIProvider,
   MachineLearningSettingsProvider,
   MachineLearningSingleMetricViewerProvider,
 } from './machine_learning';
@@ -55,6 +57,8 @@ export function MachineLearningProvider(context: FtrProviderContext) {
   const jobWizardMultiMetric = MachineLearningJobWizardMultiMetricProvider(context);
   const jobWizardPopulation = MachineLearningJobWizardPopulationProvider(context);
   const navigation = MachineLearningNavigationProvider(context);
+  const securityCommon = MachineLearningSecurityCommonProvider(context);
+  const securityUI = MachineLearningSecurityUIProvider(context, securityCommon);
   const settings = MachineLearningSettingsProvider(context);
   const singleMetricViewer = MachineLearningSingleMetricViewerProvider(context);
 
@@ -80,6 +84,8 @@ export function MachineLearningProvider(context: FtrProviderContext) {
     jobWizardMultiMetric,
     jobWizardPopulation,
     navigation,
+    securityCommon,
+    securityUI,
     settings,
     singleMetricViewer,
   };
diff --git a/x-pack/test/functional/services/transform.ts b/x-pack/test/functional/services/transform.ts
index f495626bac9b8..74416ea070882 100644
--- a/x-pack/test/functional/services/transform.ts
+++ b/x-pack/test/functional/services/transform.ts
@@ -10,6 +10,8 @@ import {
   TransformAPIProvider,
   TransformManagementProvider,
   TransformNavigationProvider,
+  TransformSecurityCommonProvider,
+  TransformSecurityUIProvider,
   TransformSourceSelectionProvider,
   TransformTableProvider,
   TransformWizardProvider,
@@ -19,6 +21,8 @@ export function TransformProvider(context: FtrProviderContext) {
   const api = TransformAPIProvider(context);
   const management = TransformManagementProvider(context);
   const navigation = TransformNavigationProvider(context);
+  const securityCommon = TransformSecurityCommonProvider(context);
+  const securityUI = TransformSecurityUIProvider(context, securityCommon);
   const sourceSelection = TransformSourceSelectionProvider(context);
   const table = TransformTableProvider(context);
   const wizard = TransformWizardProvider(context);
@@ -27,6 +31,8 @@ export function TransformProvider(context: FtrProviderContext) {
     api,
     management,
     navigation,
+    securityCommon,
+    securityUI,
     sourceSelection,
     table,
     wizard,
diff --git a/x-pack/test/functional/services/transform_ui/index.ts b/x-pack/test/functional/services/transform_ui/index.ts
index 5b5270a575028..b8a1ef4c7015e 100644
--- a/x-pack/test/functional/services/transform_ui/index.ts
+++ b/x-pack/test/functional/services/transform_ui/index.ts
@@ -7,6 +7,8 @@
 export { TransformAPIProvider } from './api';
 export { TransformManagementProvider } from './management';
 export { TransformNavigationProvider } from './navigation';
+export { TransformSecurityCommonProvider } from './security_common';
+export { TransformSecurityUIProvider } from './security_ui';
 export { TransformSourceSelectionProvider } from './source_selection';
 export { TransformTableProvider } from './transform_table';
 export { TransformWizardProvider } from './wizard';
diff --git a/x-pack/test/functional/services/transform_ui/security_common.ts b/x-pack/test/functional/services/transform_ui/security_common.ts
new file mode 100644
index 0000000000000..fdfa7ef9fbd44
--- /dev/null
+++ b/x-pack/test/functional/services/transform_ui/security_common.ts
@@ -0,0 +1,112 @@
+/*
+ * 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 { ProvidedType } from '@kbn/test/types/ftr';
+
+import { FtrProviderContext } from '../../ftr_provider_context';
+
+export type TransformSecurityCommon = ProvidedType<typeof TransformSecurityCommonProvider>;
+
+export enum USER {
+  TRANSFORM_POWERUSER = 'transform_poweruser',
+  TRANSFORM_VIEWER = 'transform_viewer',
+}
+
+export function TransformSecurityCommonProvider({ getService }: FtrProviderContext) {
+  const security = getService('security');
+
+  const roles = [
+    {
+      name: 'transform_source',
+      elasticsearch: {
+        indices: [{ names: ['*'], privileges: ['read', 'view_index_metadata'] }],
+      },
+      kibana: [],
+    },
+    {
+      name: 'transform_dest',
+      elasticsearch: {
+        indices: [{ names: ['user-*'], privileges: ['read', 'index', 'manage'] }],
+      },
+      kibana: [],
+    },
+    {
+      name: 'transform_dest_readonly',
+      elasticsearch: {
+        indices: [{ names: ['user-*'], privileges: ['read'] }],
+      },
+      kibana: [],
+    },
+    {
+      name: 'transform_ui_extras',
+      elasticsearch: {
+        cluster: ['monitor'],
+      },
+      kibana: [],
+    },
+  ];
+
+  const users = [
+    {
+      name: 'transform_poweruser',
+      full_name: 'Transform Poweruser',
+      password: 'tfp001',
+      roles: [
+        'kibana_admin',
+        'transform_admin',
+        'transform_source',
+        'transform_dest',
+        'transform_ui_extras',
+      ],
+    },
+    {
+      name: 'transform_viewer',
+      full_name: 'Transform Viewer',
+      password: 'tfv001',
+      roles: ['kibana_admin', 'transform_user', 'transform_dest_readonly'],
+    },
+  ];
+
+  return {
+    async createTransformRoles() {
+      for (const role of roles) {
+        await security.role.create(role.name, {
+          elasticsearch: role.elasticsearch,
+          kibana: role.kibana,
+        });
+      }
+    },
+
+    async createTransformUsers() {
+      for (const user of users) {
+        await security.user.create(user.name, {
+          password: user.password,
+          roles: user.roles,
+          full_name: user.full_name,
+        });
+      }
+    },
+
+    async cleanTransformRoles() {
+      for (const role of roles) {
+        await security.role.delete(role.name);
+      }
+    },
+
+    async cleanTransformUsers() {
+      for (const user of users) {
+        await security.user.delete(user.name);
+      }
+    },
+
+    getPasswordForUser(user: USER): string {
+      const userConfig = users.find(u => u.name === user);
+      if (userConfig === undefined) {
+        throw new Error(`Can't log in user ${user} - not defined`);
+      }
+      return userConfig.password;
+    },
+  };
+}
diff --git a/x-pack/test/functional/services/transform_ui/security_ui.ts b/x-pack/test/functional/services/transform_ui/security_ui.ts
new file mode 100644
index 0000000000000..9c167e429941f
--- /dev/null
+++ b/x-pack/test/functional/services/transform_ui/security_ui.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.
+ */
+
+import { FtrProviderContext } from '../../ftr_provider_context';
+import { TransformSecurityCommon, USER } from './security_common';
+
+export function TransformSecurityUIProvider(
+  { getPageObjects }: FtrProviderContext,
+  transformSecurityCommon: TransformSecurityCommon
+) {
+  const PageObjects = getPageObjects(['security']);
+
+  return {
+    async loginAs(user: USER) {
+      const password = transformSecurityCommon.getPasswordForUser(user);
+
+      await PageObjects.security.forceLogout();
+
+      await PageObjects.security.login(user, password, {
+        expectSuccess: true,
+      });
+    },
+
+    async loginAsTransformPowerUser() {
+      await this.loginAs(USER.TRANSFORM_POWERUSER);
+    },
+
+    async loginAsTransformViewer() {
+      await this.loginAs(USER.TRANSFORM_VIEWER);
+    },
+  };
+}
diff --git a/x-pack/test/functional/services/transform_ui/wizard.ts b/x-pack/test/functional/services/transform_ui/wizard.ts
index db7cdd148fd99..e823117ad7016 100644
--- a/x-pack/test/functional/services/transform_ui/wizard.ts
+++ b/x-pack/test/functional/services/transform_ui/wizard.ts
@@ -377,19 +377,75 @@ export function TransformWizardProvider({ getService }: FtrProviderContext) {
     },
 
     async assertCreateAndStartButtonExists() {
-      await testSubjects.existOrFail(`transformWizardCreateAndStartButton`);
+      await testSubjects.existOrFail('transformWizardCreateAndStartButton');
+      expect(await testSubjects.isDisplayed('transformWizardCreateAndStartButton')).to.eql(
+        true,
+        `Expected 'Create and start' button to be displayed`
+      );
+    },
+
+    async assertCreateAndStartButtonEnabled(expectedValue: boolean) {
+      const isEnabled = await testSubjects.isEnabled('transformWizardCreateAndStartButton');
+      expect(isEnabled).to.eql(
+        expectedValue,
+        `Expected 'Create and start' button to be '${
+          expectedValue ? 'enabled' : 'disabled'
+        }' (got ${isEnabled ? 'enabled' : 'disabled'}')`
+      );
     },
 
     async assertCreateButtonExists() {
-      await testSubjects.existOrFail(`transformWizardCreateButton`);
+      await testSubjects.existOrFail('transformWizardCreateButton');
+      expect(await testSubjects.isDisplayed('transformWizardCreateButton')).to.eql(
+        true,
+        `Expected 'Create' button to be displayed`
+      );
+    },
+
+    async assertCreateButtonEnabled(expectedValue: boolean) {
+      const isEnabled = await testSubjects.isEnabled('transformWizardCreateButton');
+      expect(isEnabled).to.eql(
+        expectedValue,
+        `Expected 'Create' button to be '${expectedValue ? 'enabled' : 'disabled'}' (got ${
+          isEnabled ? 'enabled' : 'disabled'
+        }')`
+      );
     },
 
     async assertCopyToClipboardButtonExists() {
-      await testSubjects.existOrFail(`transformWizardCopyToClipboardButton`);
+      await testSubjects.existOrFail('transformWizardCopyToClipboardButton');
+      expect(await testSubjects.isDisplayed('transformWizardCopyToClipboardButton')).to.eql(
+        true,
+        `Expected 'Copy to clipboard' button to be displayed`
+      );
+    },
+
+    async assertCopyToClipboardButtonEnabled(expectedValue: boolean) {
+      const isEnabled = await testSubjects.isEnabled('transformWizardCopyToClipboardButton');
+      expect(isEnabled).to.eql(
+        expectedValue,
+        `Expected 'Copy to clipboard' button to be '${
+          expectedValue ? 'enabled' : 'disabled'
+        }' (got ${isEnabled ? 'enabled' : 'disabled'}')`
+      );
     },
 
     async assertStartButtonExists() {
-      await testSubjects.existOrFail(`transformWizardStartButton`);
+      await testSubjects.existOrFail('transformWizardStartButton');
+      expect(await testSubjects.isDisplayed('transformWizardStartButton')).to.eql(
+        true,
+        `Expected 'Start' button to be displayed`
+      );
+    },
+
+    async assertStartButtonEnabled(expectedValue: boolean) {
+      const isEnabled = await testSubjects.isEnabled('transformWizardStartButton');
+      expect(isEnabled).to.eql(
+        expectedValue,
+        `Expected 'Start' button to be '${expectedValue ? 'enabled' : 'disabled'}' (got ${
+          isEnabled ? 'enabled' : 'disabled'
+        }')`
+      );
     },
 
     async assertManagementCardExists() {
@@ -423,20 +479,15 @@ export function TransformWizardProvider({ getService }: FtrProviderContext) {
     async createTransform() {
       await testSubjects.click('transformWizardCreateButton');
       await this.assertStartButtonExists();
+      await this.assertStartButtonEnabled(true);
       await this.assertManagementCardExists();
-      expect(await testSubjects.isEnabled('transformWizardCreateButton')).to.eql(
-        false,
-        'The create button should not be enabled any more'
-      );
+      await this.assertCreateButtonEnabled(false);
     },
 
     async startTransform() {
       await testSubjects.click('transformWizardStartButton');
       await this.assertDiscoverCardExists();
-      expect(await testSubjects.isEnabled('transformWizardStartButton')).to.eql(
-        false,
-        'The start button should not be enabled any more'
-      );
+      await this.assertStartButtonEnabled(false);
       await this.assertProgressbarExists();
     },
   };

From a4d8bc841b7125bc022de2f8b5eba4bec78854bc Mon Sep 17 00:00:00 2001
From: Kerry Gallagher <k.gallagher.05@gmail.com>
Date: Wed, 19 Feb 2020 09:02:26 +0000
Subject: [PATCH 054/174] Add enabled key schema validation (#57935)

---
 x-pack/plugins/infra/server/plugin.ts | 1 +
 1 file changed, 1 insertion(+)

diff --git a/x-pack/plugins/infra/server/plugin.ts b/x-pack/plugins/infra/server/plugin.ts
index ac45321e870e6..bcdbccf6f2294 100644
--- a/x-pack/plugins/infra/server/plugin.ts
+++ b/x-pack/plugins/infra/server/plugin.ts
@@ -30,6 +30,7 @@ import { InfraStaticSourceConfiguration } from './lib/sources/types';
 
 export const config = {
   schema: schema.object({
+    enabled: schema.boolean({ defaultValue: true }),
     query: schema.object({
       partitionSize: schema.number({ defaultValue: 75 }),
       partitionFactor: schema.number({ defaultValue: 1.2 }),

From 0101c638f949e569d1c6683bd498bab6eb30a04a Mon Sep 17 00:00:00 2001
From: Vadim Dalecky <streamich@users.noreply.github.com>
Date: Wed, 19 Feb 2020 10:31:41 +0100
Subject: [PATCH 055/174] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20allow=20disablin?=
 =?UTF-8?q?t=20action=20trigger=20execution=20in=20embeddables=20(#57691)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../public/embeddable/visualize_embeddable.ts      | 14 ++++++++------
 .../public/lib/embeddables/i_embeddable.ts         |  6 ++++++
 .../functions/common/saved_visualization.ts        |  1 +
 3 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts
index fddcf70c30605..9e388832283fa 100644
--- a/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts
+++ b/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts
@@ -301,13 +301,15 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut
           return;
         }
 
-        const eventName = event.name === 'brush' ? SELECT_RANGE_TRIGGER : VALUE_CLICK_TRIGGER;
+        if (!this.input.disableTriggers) {
+          const eventName = event.name === 'brush' ? SELECT_RANGE_TRIGGER : VALUE_CLICK_TRIGGER;
 
-        npStart.plugins.uiActions.executeTriggerActions(eventName, {
-          embeddable: this,
-          timeFieldName: this.vis.indexPattern.timeFieldName,
-          data: event.data,
-        });
+          npStart.plugins.uiActions.executeTriggerActions(eventName, {
+            embeddable: this,
+            timeFieldName: this.vis.indexPattern.timeFieldName,
+            data: event.data,
+          });
+        }
       })
     );
 
diff --git a/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts b/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts
index 0197582778940..46cffab879684 100644
--- a/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts
+++ b/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts
@@ -29,10 +29,16 @@ export interface EmbeddableInput {
   lastReloadRequestTime?: number;
   hidePanelTitles?: boolean;
   isEmptyState?: boolean;
+
   /**
    * List of action IDs that this embeddable should not render.
    */
   disabledActions?: string[];
+
+  /**
+   * Whether this embeddable should not execute triggers.
+   */
+  disableTriggers?: boolean;
 }
 
 export interface EmbeddableOutput {
diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.ts
index 737db985f99d0..6ac0d84bc9a73 100644
--- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.ts
+++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.ts
@@ -46,6 +46,7 @@ export function savedVisualization(): ExpressionFunctionDefinition<
         type: EmbeddableExpressionType,
         input: {
           id,
+          disableTriggers: true,
           ...buildEmbeddableFilters(filters),
         },
         embeddableType: EmbeddableTypes.visualization,

From 077879ae6efed1ab66ffe17fdebc84fd7707d04d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Casper=20H=C3=BCbertz?= <casper@elastic.co>
Date: Wed, 19 Feb 2020 10:44:41 +0100
Subject: [PATCH 056/174] [APM] Fix trace sample double pagination summary
 (#57558)

* [APM] Remove PaginationContainer

Removing the old pagination from the trace summary title.

* [APM] Removing PaginationContainer component ref

* [APM] Change the vertical alignment to center

* [APM] Leaving the EuiPagination component in place

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../WaterfallWithSummmary/index.tsx           | 34 ++++---------------
 1 file changed, 7 insertions(+), 27 deletions(-)

diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/index.tsx
index 6dcab6c6b97c1..69557241c42aa 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/index.tsx
@@ -16,10 +16,8 @@ import {
 import { i18n } from '@kbn/i18n';
 import { Location } from 'history';
 import React, { useEffect, useState } from 'react';
-import styled from 'styled-components';
 import { IBucket } from '../../../../../server/lib/transactions/distribution/get_buckets/transform';
 import { IUrlParams } from '../../../../context/UrlParamsContext/types';
-import { px, units } from '../../../../style/variables';
 import { history } from '../../../../utils/history';
 import { fromQuery, toQuery } from '../../../shared/Links/url_helpers';
 import { LoadingStatePrompt } from '../../../shared/LoadingStatePrompt';
@@ -29,20 +27,6 @@ import { MaybeViewTraceLink } from './MaybeViewTraceLink';
 import { TransactionTabs } from './TransactionTabs';
 import { IWaterfall } from './WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers';
 
-const PaginationContainer = styled.div`
-  margin-left: ${px(units.quarter)};
-  display: flex;
-  align-items: center;
-
-  > span:first-of-type {
-    font-weight: 600;
-  }
-
-  > span:last-of-type {
-    margin-right: ${px(units.half)};
-  }
-`;
-
 interface Props {
   urlParams: IUrlParams;
   location: Location;
@@ -102,7 +86,7 @@ export const WaterfallWithSummmary: React.FC<Props> = ({
   return (
     <EuiPanel paddingSize="m">
       <EuiFlexGroup>
-        <EuiFlexItem style={{ flexDirection: 'row', alignItems: 'baseLine' }}>
+        <EuiFlexItem style={{ flexDirection: 'row', alignItems: 'center' }}>
           <EuiTitle size="xs">
             <h5>
               {i18n.translate('xpack.apm.transactionDetails.traceSampleTitle', {
@@ -111,16 +95,12 @@ export const WaterfallWithSummmary: React.FC<Props> = ({
             </h5>
           </EuiTitle>
           {traceSamples && (
-            <PaginationContainer>
-              <span>{sampleActivePage + 1}</span>
-              <span>/{traceSamples.length}</span>
-              <EuiPagination
-                pageCount={traceSamples.length}
-                activePage={sampleActivePage}
-                onPageClick={goToSample}
-                compressed
-              />
-            </PaginationContainer>
+            <EuiPagination
+              pageCount={traceSamples.length}
+              activePage={sampleActivePage}
+              onPageClick={goToSample}
+              compressed
+            />
           )}
         </EuiFlexItem>
         <EuiFlexItem>

From e7b63863ab54d80d4f635db7c676e788eb51f366 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cau=C3=AA=20Marcondes?=
 <55978943+cauemarcondes@users.noreply.github.com>
Date: Wed, 19 Feb 2020 10:27:46 +0000
Subject: [PATCH 057/174] [APM] Use ES Permission API to check if a user has
 permissions to read from APM indices (#57311)

* get indices privileges from has_privileges api

* changing to ES privileges api

* changing missing permission page

* always show dimiss button

* always show dimiss button

* changing message and unit test

* fixing react warning message
---
 .../__test__/APMIndicesPermission.test.tsx    | 124 ++++++++++++++++++
 .../index.tsx                                 |  59 ++++++---
 .../shared/Links/ElasticDocsLink.tsx          |   6 +-
 .../apm/public/new-platform/plugin.tsx        |   7 +-
 .../apm/server/lib/helpers/es_client.ts       |  39 ++++--
 .../apm/server/lib/security/getPermissions.ts |  32 -----
 .../lib/security/get_indices_privileges.ts    |  24 ++++
 .../apm/server/routes/create_apm_api.ts       |   4 +-
 .../plugins/apm/server/routes/security.ts     |   8 +-
 9 files changed, 233 insertions(+), 70 deletions(-)
 create mode 100644 x-pack/legacy/plugins/apm/public/components/app/APMIndicesPermission/__test__/APMIndicesPermission.test.tsx
 rename x-pack/legacy/plugins/apm/public/components/app/{Permission => APMIndicesPermission}/index.tsx (65%)
 delete mode 100644 x-pack/legacy/plugins/apm/server/lib/security/getPermissions.ts
 create mode 100644 x-pack/legacy/plugins/apm/server/lib/security/get_indices_privileges.ts

diff --git a/x-pack/legacy/plugins/apm/public/components/app/APMIndicesPermission/__test__/APMIndicesPermission.test.tsx b/x-pack/legacy/plugins/apm/public/components/app/APMIndicesPermission/__test__/APMIndicesPermission.test.tsx
new file mode 100644
index 0000000000000..c2c396d5b8951
--- /dev/null
+++ b/x-pack/legacy/plugins/apm/public/components/app/APMIndicesPermission/__test__/APMIndicesPermission.test.tsx
@@ -0,0 +1,124 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import React from 'react';
+import { render, fireEvent } from '@testing-library/react';
+import { shallow } from 'enzyme';
+import { APMIndicesPermission } from '../';
+
+import * as hooks from '../../../../hooks/useFetcher';
+import {
+  expectTextsInDocument,
+  MockApmPluginContextWrapper,
+  expectTextsNotInDocument
+} from '../../../../utils/testHelpers';
+
+describe('APMIndicesPermission', () => {
+  it('returns empty component when api status is loading', () => {
+    spyOn(hooks, 'useFetcher').and.returnValue({
+      status: hooks.FETCH_STATUS.LOADING
+    });
+    const component = shallow(<APMIndicesPermission />);
+    expect(component.isEmptyRender()).toBeTruthy();
+  });
+  it('returns empty component when api status is pending', () => {
+    spyOn(hooks, 'useFetcher').and.returnValue({
+      status: hooks.FETCH_STATUS.PENDING
+    });
+    const component = shallow(<APMIndicesPermission />);
+    expect(component.isEmptyRender()).toBeTruthy();
+  });
+  it('renders missing permission page', () => {
+    spyOn(hooks, 'useFetcher').and.returnValue({
+      status: hooks.FETCH_STATUS.SUCCESS,
+      data: {
+        'apm-*': { read: false }
+      }
+    });
+    const component = render(
+      <MockApmPluginContextWrapper>
+        <APMIndicesPermission />
+      </MockApmPluginContextWrapper>
+    );
+    expectTextsInDocument(component, [
+      'Missing permissions to access APM',
+      'Dismiss',
+      'apm-*'
+    ]);
+  });
+  it('shows escape hatch button when at least one indice has read privileges', () => {
+    spyOn(hooks, 'useFetcher').and.returnValue({
+      status: hooks.FETCH_STATUS.SUCCESS,
+      data: {
+        'apm-7.5.1-error-*': { read: false },
+        'apm-7.5.1-metric-*': { read: false },
+        'apm-7.5.1-transaction-*': { read: false },
+        'apm-7.5.1-span-*': { read: true }
+      }
+    });
+    const component = render(
+      <MockApmPluginContextWrapper>
+        <APMIndicesPermission />
+      </MockApmPluginContextWrapper>
+    );
+    expectTextsInDocument(component, [
+      'Missing permissions to access APM',
+      'apm-7.5.1-error-*',
+      'apm-7.5.1-metric-*',
+      'apm-7.5.1-transaction-*',
+      'Dismiss'
+    ]);
+    expectTextsNotInDocument(component, ['apm-7.5.1-span-*']);
+  });
+
+  it('shows children component when indices have read privileges', () => {
+    spyOn(hooks, 'useFetcher').and.returnValue({
+      status: hooks.FETCH_STATUS.SUCCESS,
+      data: {
+        'apm-7.5.1-error-*': { read: true },
+        'apm-7.5.1-metric-*': { read: true },
+        'apm-7.5.1-transaction-*': { read: true },
+        'apm-7.5.1-span-*': { read: true }
+      }
+    });
+    const component = render(
+      <MockApmPluginContextWrapper>
+        <APMIndicesPermission>
+          <p>My amazing component</p>
+        </APMIndicesPermission>
+      </MockApmPluginContextWrapper>
+    );
+    expectTextsNotInDocument(component, [
+      'Missing permissions to access APM',
+      'apm-7.5.1-error-*',
+      'apm-7.5.1-metric-*',
+      'apm-7.5.1-transaction-*',
+      'apm-7.5.1-span-*'
+    ]);
+    expectTextsInDocument(component, ['My amazing component']);
+  });
+
+  it('dismesses the warning by clicking on the escape hatch', () => {
+    spyOn(hooks, 'useFetcher').and.returnValue({
+      status: hooks.FETCH_STATUS.SUCCESS,
+      data: {
+        'apm-7.5.1-error-*': { read: false },
+        'apm-7.5.1-metric-*': { read: false },
+        'apm-7.5.1-transaction-*': { read: false },
+        'apm-7.5.1-span-*': { read: true }
+      }
+    });
+    const component = render(
+      <MockApmPluginContextWrapper>
+        <APMIndicesPermission>
+          <p>My amazing component</p>
+        </APMIndicesPermission>
+      </MockApmPluginContextWrapper>
+    );
+    expectTextsInDocument(component, ['Dismiss']);
+    fireEvent.click(component.getByText('Dismiss'));
+    expectTextsInDocument(component, ['My amazing component']);
+  });
+});
diff --git a/x-pack/legacy/plugins/apm/public/components/app/Permission/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/APMIndicesPermission/index.tsx
similarity index 65%
rename from x-pack/legacy/plugins/apm/public/components/app/Permission/index.tsx
rename to x-pack/legacy/plugins/apm/public/components/app/APMIndicesPermission/index.tsx
index 177002e7e49bb..40e039dcd40c5 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/Permission/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/APMIndicesPermission/index.tsx
@@ -11,9 +11,11 @@ import {
   EuiFlexItem,
   EuiLink,
   EuiPanel,
+  EuiText,
   EuiTitle
 } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
+import { isEmpty } from 'lodash';
 import React, { useState } from 'react';
 import styled from 'styled-components';
 import { FETCH_STATUS, useFetcher } from '../../../hooks/useFetcher';
@@ -21,12 +23,15 @@ import { fontSize, pct, px, units } from '../../../style/variables';
 import { ElasticDocsLink } from '../../shared/Links/ElasticDocsLink';
 import { SetupInstructionsLink } from '../../shared/Links/SetupInstructionsLink';
 
-export const Permission: React.FC = ({ children }) => {
-  const [isPermissionPageEnabled, setIsPermissionsPageEnabled] = useState(true);
+export const APMIndicesPermission: React.FC = ({ children }) => {
+  const [
+    isPermissionWarningDismissed,
+    setIsPermissionWarningDismissed
+  ] = useState(false);
 
-  const { data, status } = useFetcher(callApmApi => {
+  const { data: indicesPrivileges = {}, status } = useFetcher(callApmApi => {
     return callApmApi({
-      pathname: '/api/apm/security/permissions'
+      pathname: '/api/apm/security/indices_privileges'
     });
   }, []);
 
@@ -34,12 +39,18 @@ export const Permission: React.FC = ({ children }) => {
   if (status === FETCH_STATUS.LOADING || status === FETCH_STATUS.PENDING) {
     return null;
   }
-  // When the user doesn't have the appropriate permissions and they
-  // did not use the escape hatch, show the missing permissions page
-  if (data?.hasPermission === false && isPermissionPageEnabled) {
+
+  const indicesWithoutPermission = Object.keys(indicesPrivileges).filter(
+    index => !indicesPrivileges[index].read
+  );
+
+  // Show permission warning when a user has at least one index without Read privilege,
+  // and he has not manually dismissed the warning
+  if (!isEmpty(indicesWithoutPermission) && !isPermissionWarningDismissed) {
     return (
-      <PermissionPage
-        onEscapeHatchClick={() => setIsPermissionsPageEnabled(false)}
+      <PermissionWarning
+        indicesWithoutPermission={indicesWithoutPermission}
+        onEscapeHatchClick={() => setIsPermissionWarningDismissed(true)}
       />
     );
   }
@@ -62,10 +73,14 @@ const EscapeHatch = styled.div`
 `;
 
 interface Props {
+  indicesWithoutPermission: string[];
   onEscapeHatchClick: () => void;
 }
 
-const PermissionPage = ({ onEscapeHatchClick }: Props) => {
+const PermissionWarning = ({
+  indicesWithoutPermission,
+  onEscapeHatchClick
+}: Props) => {
   return (
     <div style={{ height: pct(95) }}>
       <EuiFlexGroup alignItems="center">
@@ -96,12 +111,21 @@ const PermissionPage = ({ onEscapeHatchClick }: Props) => {
                 </h2>
               }
               body={
-                <p>
-                  {i18n.translate('xpack.apm.permission.description', {
-                    defaultMessage:
-                      "We've detected your current role in Kibana does not grant you access to the APM data. Please check with your Kibana administrator to get the proper privileges granted in order to start using APM."
-                  })}
-                </p>
+                <>
+                  <p>
+                    {i18n.translate('xpack.apm.permission.description', {
+                      defaultMessage:
+                        "Your user doesn't have access to all APM indices. You can still use the APM app but some data may be missing. You must be granted access to the following indices:"
+                    })}
+                  </p>
+                  <ul style={{ listStyleType: 'none' }}>
+                    {indicesWithoutPermission.map(index => (
+                      <li key={index} style={{ marginTop: units.half }}>
+                        <EuiText size="s">{index}</EuiText>
+                      </li>
+                    ))}
+                  </ul>
+                </>
               }
               actions={
                 <>
@@ -117,7 +141,6 @@ const PermissionPage = ({ onEscapeHatchClick }: Props) => {
                       </EuiButton>
                     )}
                   </ElasticDocsLink>
-
                   <EscapeHatch>
                     <EuiLink
                       color="subdued"
@@ -125,7 +148,7 @@ const PermissionPage = ({ onEscapeHatchClick }: Props) => {
                       style={{ fontSize }}
                     >
                       {i18n.translate('xpack.apm.permission.dismissWarning', {
-                        defaultMessage: 'Dismiss warning'
+                        defaultMessage: 'Dismiss'
                       })}
                     </EuiLink>
                   </EscapeHatch>
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/ElasticDocsLink.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/ElasticDocsLink.tsx
index d97a2331e457b..7645162ab2655 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Links/ElasticDocsLink.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/ElasticDocsLink.tsx
@@ -19,9 +19,11 @@ interface Props extends EuiLinkAnchorProps {
 export function ElasticDocsLink({ section, path, children, ...rest }: Props) {
   const { version } = useApmPluginContext().packageInfo;
   const href = `https://www.elastic.co/guide/en${section}/${version}${path}`;
-  return (
+  return typeof children === 'function' ? (
+    children(href)
+  ) : (
     <EuiLink href={href} {...rest}>
-      {typeof children === 'function' ? children(href) : children}
+      children
     </EuiLink>
   );
 }
diff --git a/x-pack/legacy/plugins/apm/public/new-platform/plugin.tsx b/x-pack/legacy/plugins/apm/public/new-platform/plugin.tsx
index 64deff9f1ae39..0054f963ba8f2 100644
--- a/x-pack/legacy/plugins/apm/public/new-platform/plugin.tsx
+++ b/x-pack/legacy/plugins/apm/public/new-platform/plugin.tsx
@@ -38,7 +38,7 @@ import { setHelpExtension } from './setHelpExtension';
 import { toggleAppLinkInNav } from './toggleAppLinkInNav';
 import { setReadonlyBadge } from './updateBadge';
 import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
-import { Permission } from '../components/app/Permission';
+import { APMIndicesPermission } from '../components/app/APMIndicesPermission';
 
 export const REACT_APP_ROOT_ID = 'react-apm-root';
 
@@ -53,14 +53,13 @@ const App = () => {
     <MainContainer data-test-subj="apmMainContainer" role="main">
       <UpdateBreadcrumbs routes={routes} />
       <Route component={ScrollToTopOnPathChange} />
-      {/* Check if user has the appropriate permissions to use the APM UI. */}
-      <Permission>
+      <APMIndicesPermission>
         <Switch>
           {routes.map((route, i) => (
             <ApmRoute key={i} {...route} />
           ))}
         </Switch>
-      </Permission>
+      </APMIndicesPermission>
     </MainContainer>
   );
 };
diff --git a/x-pack/legacy/plugins/apm/server/lib/helpers/es_client.ts b/x-pack/legacy/plugins/apm/server/lib/helpers/es_client.ts
index 2795c95a50034..06cf0047b0286 100644
--- a/x-pack/legacy/plugins/apm/server/lib/helpers/es_client.ts
+++ b/x-pack/legacy/plugins/apm/server/lib/helpers/es_client.ts
@@ -6,26 +6,38 @@
 
 /* eslint-disable no-console */
 import {
-  SearchParams,
   IndexDocumentParams,
+  IndicesCreateParams,
   IndicesDeleteParams,
-  IndicesCreateParams
+  SearchParams
 } from 'elasticsearch';
-import { merge, uniqueId } from 'lodash';
-import { cloneDeep, isString } from 'lodash';
+import { cloneDeep, isString, merge, uniqueId } from 'lodash';
 import { KibanaRequest } from 'src/core/server';
-import { OBSERVER_VERSION_MAJOR } from '../../../common/elasticsearch_fieldnames';
 import {
-  ESSearchResponse,
-  ESSearchRequest
+  ESSearchRequest,
+  ESSearchResponse
 } from '../../../../../../plugins/apm/typings/elasticsearch';
-import { APMRequestHandlerContext } from '../../routes/typings';
+import { OBSERVER_VERSION_MAJOR } from '../../../common/elasticsearch_fieldnames';
 import { pickKeys } from '../../../public/utils/pickKeys';
+import { APMRequestHandlerContext } from '../../routes/typings';
 import { getApmIndices } from '../settings/apm_indices/get_apm_indices';
 
 // `type` was deprecated in 7.0
 export type APMIndexDocumentParams<T> = Omit<IndexDocumentParams<T>, 'type'>;
 
+interface IndexPrivileges {
+  has_all_requested: boolean;
+  username: string;
+  index: Record<string, { read: boolean }>;
+}
+
+interface IndexPrivilegesParams {
+  index: Array<{
+    names: string[] | string;
+    privileges: string[];
+  }>;
+}
+
 export function isApmIndex(
   apmIndices: string[],
   indexParam: SearchParams['index']
@@ -181,6 +193,17 @@ export function getESClient(
     },
     indicesCreate: (params: IndicesCreateParams) => {
       return withTime(() => callMethod('indices.create', params));
+    },
+    hasPrivileges: (
+      params: IndexPrivilegesParams
+    ): Promise<IndexPrivileges> => {
+      return withTime(() =>
+        callMethod('transport.request', {
+          method: 'POST',
+          path: '/_security/user/_has_privileges',
+          body: params
+        })
+      );
     }
   };
 }
diff --git a/x-pack/legacy/plugins/apm/server/lib/security/getPermissions.ts b/x-pack/legacy/plugins/apm/server/lib/security/getPermissions.ts
deleted file mode 100644
index ed2a1f64e7f84..0000000000000
--- a/x-pack/legacy/plugins/apm/server/lib/security/getPermissions.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-import { Setup } from '../helpers/setup_request';
-
-export async function getPermissions(setup: Setup) {
-  const { client, indices } = setup;
-
-  const params = {
-    index: Object.values(indices),
-    body: {
-      size: 0,
-      query: {
-        match_all: {}
-      }
-    }
-  };
-
-  try {
-    await client.search(params);
-    return { hasPermission: true };
-  } catch (e) {
-    // If 403, it means the user doesnt have permission.
-    if (e.status === 403) {
-      return { hasPermission: false };
-    }
-    // if any other error happens, throw it.
-    throw e;
-  }
-}
diff --git a/x-pack/legacy/plugins/apm/server/lib/security/get_indices_privileges.ts b/x-pack/legacy/plugins/apm/server/lib/security/get_indices_privileges.ts
new file mode 100644
index 0000000000000..1a80a13b2ad19
--- /dev/null
+++ b/x-pack/legacy/plugins/apm/server/lib/security/get_indices_privileges.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.
+ */
+import { Setup } from '../helpers/setup_request';
+
+export async function getIndicesPrivileges(setup: Setup) {
+  const { client, indices } = setup;
+  const response = await client.hasPrivileges({
+    index: [
+      {
+        names: [
+          indices['apm_oss.errorIndices'],
+          indices['apm_oss.metricsIndices'],
+          indices['apm_oss.transactionIndices'],
+          indices['apm_oss.spanIndices']
+        ],
+        privileges: ['read']
+      }
+    ]
+  });
+  return response.index;
+}
diff --git a/x-pack/legacy/plugins/apm/server/routes/create_apm_api.ts b/x-pack/legacy/plugins/apm/server/routes/create_apm_api.ts
index 3ac9629f59edf..f65e271389938 100644
--- a/x-pack/legacy/plugins/apm/server/routes/create_apm_api.ts
+++ b/x-pack/legacy/plugins/apm/server/routes/create_apm_api.ts
@@ -59,7 +59,7 @@ import {
 } from './ui_filters';
 import { createApi } from './create_api';
 import { serviceMapRoute, serviceMapServiceNodeRoute } from './service_map';
-import { permissionsRoute } from './security';
+import { indicesPrivilegesRoute } from './security';
 
 const createApmApi = () => {
   const api = createApi()
@@ -128,7 +128,7 @@ const createApmApi = () => {
     .add(serviceMapServiceNodeRoute)
 
     // security
-    .add(permissionsRoute);
+    .add(indicesPrivilegesRoute);
 
   return api;
 };
diff --git a/x-pack/legacy/plugins/apm/server/routes/security.ts b/x-pack/legacy/plugins/apm/server/routes/security.ts
index 5f803fb438e31..0a8222b665d83 100644
--- a/x-pack/legacy/plugins/apm/server/routes/security.ts
+++ b/x-pack/legacy/plugins/apm/server/routes/security.ts
@@ -6,12 +6,12 @@
 
 import { createRoute } from './create_route';
 import { setupRequest } from '../lib/helpers/setup_request';
-import { getPermissions } from '../lib/security/getPermissions';
+import { getIndicesPrivileges } from '../lib/security/get_indices_privileges';
 
-export const permissionsRoute = createRoute(() => ({
-  path: '/api/apm/security/permissions',
+export const indicesPrivilegesRoute = createRoute(() => ({
+  path: '/api/apm/security/indices_privileges',
   handler: async ({ context, request }) => {
     const setup = await setupRequest(context, request);
-    return getPermissions(setup);
+    return getIndicesPrivileges(setup);
   }
 }));

From 28034d05a9fce7f17045215da48834bf2b479f76 Mon Sep 17 00:00:00 2001
From: Joe Reuter <johannes.reuter@elastic.co>
Date: Wed, 19 Feb 2020 12:51:26 +0100
Subject: [PATCH 058/174] Remove injected reference from home app (#57836)

---
 .../kibana/public/home/kibana_services.ts     |  3 +-
 .../home/np_ready/components/home.test.js     |  2 +-
 .../tutorial/replace_template_strings.js      |  6 +-
 .../core_plugins/kibana/public/home/plugin.ts |  9 ++-
 src/plugins/home/public/index.ts              | 11 +++-
 src/plugins/home/public/mocks/index.ts        |  3 +
 src/plugins/home/public/plugin.test.mocks.ts  |  3 +
 src/plugins/home/public/plugin.test.ts        | 18 +++++-
 src/plugins/home/public/plugin.ts             | 14 +++++
 .../services/environment/environment.ts       |  6 +-
 src/plugins/home/public/services/index.ts     |  1 +
 .../home/public/services/tutorials/index.ts   | 25 +++++++++
 .../tutorials/tutorial_service.mock.ts        | 50 +++++++++++++++++
 .../tutorials/tutorial_service.test.ts        | 55 +++++++++++++++++++
 .../services/tutorials/tutorial_service.ts    | 53 ++++++++++++++++++
 x-pack/plugins/cloud/public/plugin.ts         |  3 +
 16 files changed, 248 insertions(+), 14 deletions(-)
 create mode 100644 src/plugins/home/public/services/tutorials/index.ts
 create mode 100644 src/plugins/home/public/services/tutorials/tutorial_service.mock.ts
 create mode 100644 src/plugins/home/public/services/tutorials/tutorial_service.test.ts
 create mode 100644 src/plugins/home/public/services/tutorials/tutorial_service.ts

diff --git a/src/legacy/core_plugins/kibana/public/home/kibana_services.ts b/src/legacy/core_plugins/kibana/public/home/kibana_services.ts
index 4b09997fc6244..a4fbe83f60e84 100644
--- a/src/legacy/core_plugins/kibana/public/home/kibana_services.ts
+++ b/src/legacy/core_plugins/kibana/public/home/kibana_services.ts
@@ -31,6 +31,7 @@ import { TelemetryPluginStart } from '../../../../../plugins/telemetry/public';
 import {
   Environment,
   HomePublicPluginSetup,
+  TutorialStart,
   HomePublicPluginStart,
 } from '../../../../../plugins/home/public';
 import { KibanaLegacySetup } from '../../../../../plugins/kibana_legacy/public';
@@ -38,7 +39,6 @@ import { KibanaLegacySetup } from '../../../../../plugins/kibana_legacy/public';
 export interface HomeKibanaServices {
   indexPatternService: any;
   kibanaVersion: string;
-  getInjected: (name: string, defaultValue?: any) => unknown;
   chrome: ChromeStart;
   uiSettings: IUiSettingsClient;
   config: KibanaLegacySetup['config'];
@@ -54,6 +54,7 @@ export interface HomeKibanaServices {
   addBasePath: (url: string) => string;
   environment: Environment;
   telemetry?: TelemetryPluginStart;
+  tutorialVariables: TutorialStart['get'];
 }
 
 let services: HomeKibanaServices | null = null;
diff --git a/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.test.js b/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.test.js
index d25a1f81dae5a..b0d94711be7b6 100644
--- a/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.test.js
+++ b/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.test.js
@@ -29,7 +29,7 @@ import { FeatureCatalogueCategory } from '../../../../../../../plugins/home/publ
 jest.mock('../../kibana_services', () => ({
   getServices: () => ({
     getBasePath: () => 'path',
-    getInjected: () => '',
+    tutorialVariables: () => ({}),
     homeConfig: { disableWelcomeScreen: false },
   }),
 }));
diff --git a/src/legacy/core_plugins/kibana/public/home/np_ready/components/tutorial/replace_template_strings.js b/src/legacy/core_plugins/kibana/public/home/np_ready/components/tutorial/replace_template_strings.js
index c7e623657bf71..f9fa662e6d507 100644
--- a/src/legacy/core_plugins/kibana/public/home/np_ready/components/tutorial/replace_template_strings.js
+++ b/src/legacy/core_plugins/kibana/public/home/np_ready/components/tutorial/replace_template_strings.js
@@ -33,7 +33,7 @@ mustacheWriter.escapedValue = function escapedValue(token, context) {
 };
 
 export function replaceTemplateStrings(text, params = {}) {
-  const { getInjected, kibanaVersion, docLinks } = getServices();
+  const { tutorialVariables, kibanaVersion, docLinks } = getServices();
 
   const variables = {
     // '{' and '}' can not be used in template since they are used as template tags.
@@ -41,9 +41,7 @@ export function replaceTemplateStrings(text, params = {}) {
     curlyOpen: '{',
     curlyClose: '}',
     config: {
-      cloud: {
-        id: getInjected('cloudId'),
-      },
+      ...tutorialVariables(),
       docs: {
         base_url: docLinks.ELASTIC_WEBSITE_URL,
         beats: {
diff --git a/src/legacy/core_plugins/kibana/public/home/plugin.ts b/src/legacy/core_plugins/kibana/public/home/plugin.ts
index 1f4b5fe7cacef..f8c750cc80283 100644
--- a/src/legacy/core_plugins/kibana/public/home/plugin.ts
+++ b/src/legacy/core_plugins/kibana/public/home/plugin.ts
@@ -57,20 +57,22 @@ export class HomePlugin implements Plugin {
 
   constructor(private initializerContext: PluginInitializerContext) {}
 
-  setup(core: CoreSetup, { home, kibanaLegacy, usageCollection }: HomePluginSetupDependencies) {
+  setup(
+    core: CoreSetup<HomePluginStartDependencies>,
+    { home, kibanaLegacy, usageCollection }: HomePluginSetupDependencies
+  ) {
     kibanaLegacy.registerLegacyApp({
       id: 'home',
       title: 'Home',
       mount: async (params: AppMountParameters) => {
         const trackUiMetric = usageCollection.reportUiStats.bind(usageCollection, 'Kibana_home');
-        const [coreStart] = await core.getStartServices();
+        const [coreStart, { home: homeStart }] = await core.getStartServices();
         setServices({
           trackUiMetric,
           kibanaVersion: this.initializerContext.env.packageInfo.version,
           http: coreStart.http,
           toastNotifications: core.notifications.toasts,
           banners: coreStart.overlays.banners,
-          getInjected: core.injectedMetadata.getInjectedVar,
           docLinks: coreStart.docLinks,
           savedObjectsClient: this.savedObjectsClient!,
           chrome: coreStart.chrome,
@@ -82,6 +84,7 @@ export class HomePlugin implements Plugin {
           environment: this.environment!,
           config: kibanaLegacy.config,
           homeConfig: home.config,
+          tutorialVariables: homeStart.tutorials.get,
           featureCatalogue: this.featureCatalogue!,
         });
         const { renderApp } = await import('./np_ready/application');
diff --git a/src/plugins/home/public/index.ts b/src/plugins/home/public/index.ts
index 2a445cf242729..7738990bba0d0 100644
--- a/src/plugins/home/public/index.ts
+++ b/src/plugins/home/public/index.ts
@@ -22,10 +22,19 @@ import { PluginInitializerContext } from 'kibana/public';
 export {
   FeatureCatalogueSetup,
   FeatureCatalogueStart,
+  EnvironmentSetup,
+  EnvironmentStart,
+  TutorialSetup,
+  TutorialStart,
   HomePublicPluginSetup,
   HomePublicPluginStart,
 } from './plugin';
-export { FeatureCatalogueEntry, FeatureCatalogueCategory, Environment } from './services';
+export {
+  FeatureCatalogueEntry,
+  FeatureCatalogueCategory,
+  Environment,
+  TutorialVariables,
+} from './services';
 export * from '../common/instruction_variant';
 import { HomePublicPlugin } from './plugin';
 
diff --git a/src/plugins/home/public/mocks/index.ts b/src/plugins/home/public/mocks/index.ts
index dead50230ec85..42c61fe847250 100644
--- a/src/plugins/home/public/mocks/index.ts
+++ b/src/plugins/home/public/mocks/index.ts
@@ -20,16 +20,19 @@
 import { featureCatalogueRegistryMock } from '../services/feature_catalogue/feature_catalogue_registry.mock';
 import { environmentServiceMock } from '../services/environment/environment.mock';
 import { configSchema } from '../../config';
+import { tutorialServiceMock } from '../services/tutorials/tutorial_service.mock';
 
 const createSetupContract = () => ({
   featureCatalogue: featureCatalogueRegistryMock.createSetup(),
   environment: environmentServiceMock.createSetup(),
+  tutorials: tutorialServiceMock.createSetup(),
   config: configSchema.validate({}),
 });
 
 const createStartContract = () => ({
   featureCatalogue: featureCatalogueRegistryMock.createStart(),
   environment: environmentServiceMock.createStart(),
+  tutorials: tutorialServiceMock.createStart(),
 });
 
 export const homePluginMock = {
diff --git a/src/plugins/home/public/plugin.test.mocks.ts b/src/plugins/home/public/plugin.test.mocks.ts
index 461930ddfb80f..d047311968361 100644
--- a/src/plugins/home/public/plugin.test.mocks.ts
+++ b/src/plugins/home/public/plugin.test.mocks.ts
@@ -19,10 +19,13 @@
 
 import { featureCatalogueRegistryMock } from './services/feature_catalogue/feature_catalogue_registry.mock';
 import { environmentServiceMock } from './services/environment/environment.mock';
+import { tutorialServiceMock } from './services/tutorials/tutorial_service.mock';
 
 export const registryMock = featureCatalogueRegistryMock.create();
 export const environmentMock = environmentServiceMock.create();
+export const tutorialMock = tutorialServiceMock.create();
 jest.doMock('./services', () => ({
   FeatureCatalogueRegistry: jest.fn(() => registryMock),
   EnvironmentService: jest.fn(() => environmentMock),
+  TutorialService: jest.fn(() => tutorialMock),
 }));
diff --git a/src/plugins/home/public/plugin.test.ts b/src/plugins/home/public/plugin.test.ts
index fa44a110c63b7..0423ad3dd99f5 100644
--- a/src/plugins/home/public/plugin.test.ts
+++ b/src/plugins/home/public/plugin.test.ts
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-import { registryMock, environmentMock } from './plugin.test.mocks';
+import { registryMock, environmentMock, tutorialMock } from './plugin.test.mocks';
 import { HomePublicPlugin } from './plugin';
 import { coreMock } from '../../../core/public/mocks';
 
@@ -27,8 +27,10 @@ describe('HomePublicPlugin', () => {
   beforeEach(() => {
     registryMock.setup.mockClear();
     registryMock.start.mockClear();
+    tutorialMock.setup.mockClear();
     environmentMock.setup.mockClear();
     environmentMock.start.mockClear();
+    tutorialMock.start.mockClear();
   });
 
   describe('setup', () => {
@@ -43,6 +45,12 @@ describe('HomePublicPlugin', () => {
       expect(setup).toHaveProperty('environment');
       expect(setup.environment).toHaveProperty('update');
     });
+
+    test('wires up and returns tutorial service', async () => {
+      const setup = await new HomePublicPlugin(mockInitializerContext).setup();
+      expect(setup).toHaveProperty('tutorials');
+      expect(setup.tutorials).toHaveProperty('setVariable');
+    });
   });
 
   describe('start', () => {
@@ -66,5 +74,13 @@ describe('HomePublicPlugin', () => {
       expect(environmentMock.start).toHaveBeenCalled();
       expect(start.environment.get).toBeDefined();
     });
+
+    test('wires up and returns tutorial service', async () => {
+      const service = new HomePublicPlugin(mockInitializerContext);
+      await service.setup();
+      const start = await service.start(coreMock.createStart());
+      expect(tutorialMock.start).toHaveBeenCalled();
+      expect(start.tutorials.get).toBeDefined();
+    });
   });
 });
diff --git a/src/plugins/home/public/plugin.ts b/src/plugins/home/public/plugin.ts
index fe68dbc3e7e49..975fd7bfb23c0 100644
--- a/src/plugins/home/public/plugin.ts
+++ b/src/plugins/home/public/plugin.ts
@@ -26,12 +26,16 @@ import {
   FeatureCatalogueRegistry,
   FeatureCatalogueRegistrySetup,
   FeatureCatalogueRegistryStart,
+  TutorialService,
+  TutorialServiceSetup,
+  TutorialServiceStart,
 } from './services';
 import { ConfigSchema } from '../config';
 
 export class HomePublicPlugin implements Plugin<HomePublicPluginSetup, HomePublicPluginStart> {
   private readonly featuresCatalogueRegistry = new FeatureCatalogueRegistry();
   private readonly environmentService = new EnvironmentService();
+  private readonly tutorialService = new TutorialService();
 
   constructor(private readonly initializerContext: PluginInitializerContext<ConfigSchema>) {}
 
@@ -39,6 +43,7 @@ export class HomePublicPlugin implements Plugin<HomePublicPluginSetup, HomePubli
     return {
       featureCatalogue: { ...this.featuresCatalogueRegistry.setup() },
       environment: { ...this.environmentService.setup() },
+      tutorials: { ...this.tutorialService.setup() },
       config: this.initializerContext.config.get(),
     };
   }
@@ -50,6 +55,7 @@ export class HomePublicPlugin implements Plugin<HomePublicPluginSetup, HomePubli
           capabilities: core.application.capabilities,
         }),
       },
+      tutorials: { ...this.tutorialService.start() },
       environment: { ...this.environmentService.start() },
     };
   }
@@ -67,8 +73,15 @@ export type EnvironmentSetup = EnvironmentServiceSetup;
 /** @public */
 export type EnvironmentStart = EnvironmentServiceStart;
 
+/** @public */
+export type TutorialSetup = TutorialServiceSetup;
+
+/** @public */
+export type TutorialStart = TutorialServiceStart;
+
 /** @public */
 export interface HomePublicPluginSetup {
+  tutorials: TutorialServiceSetup;
   featureCatalogue: FeatureCatalogueSetup;
   /**
    * The environment service is only available for a transition period and will
@@ -81,6 +94,7 @@ export interface HomePublicPluginSetup {
 
 /** @public */
 export interface HomePublicPluginStart {
+  tutorials: TutorialServiceStart;
   featureCatalogue: FeatureCatalogueStart;
   environment: EnvironmentStart;
 }
diff --git a/src/plugins/home/public/services/environment/environment.ts b/src/plugins/home/public/services/environment/environment.ts
index 36c1afbca5e73..e2ddf912017e5 100644
--- a/src/plugins/home/public/services/environment/environment.ts
+++ b/src/plugins/home/public/services/environment/environment.ts
@@ -20,15 +20,15 @@
 /** @public */
 export interface Environment {
   /**
-   * Flag whether the home app should advertize cloud features
+   * Flag whether the home app should advertise cloud features
    */
   readonly cloud: boolean;
   /**
-   * Flag whether the home app should advertize apm features
+   * Flag whether the home app should advertise apm features
    */
   readonly apmUi: boolean;
   /**
-   * Flag whether the home app should advertize ml features
+   * Flag whether the home app should advertise ml features
    */
   readonly ml: boolean;
 }
diff --git a/src/plugins/home/public/services/index.ts b/src/plugins/home/public/services/index.ts
index a6542dd066a67..864dc61c87b75 100644
--- a/src/plugins/home/public/services/index.ts
+++ b/src/plugins/home/public/services/index.ts
@@ -19,3 +19,4 @@
 
 export * from './feature_catalogue';
 export * from './environment';
+export * from './tutorials';
diff --git a/src/plugins/home/public/services/tutorials/index.ts b/src/plugins/home/public/services/tutorials/index.ts
new file mode 100644
index 0000000000000..13a1c58c80f92
--- /dev/null
+++ b/src/plugins/home/public/services/tutorials/index.ts
@@ -0,0 +1,25 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export {
+  TutorialService,
+  TutorialVariables,
+  TutorialServiceSetup,
+  TutorialServiceStart,
+} from './tutorial_service';
diff --git a/src/plugins/home/public/services/tutorials/tutorial_service.mock.ts b/src/plugins/home/public/services/tutorials/tutorial_service.mock.ts
new file mode 100644
index 0000000000000..09397a0db8339
--- /dev/null
+++ b/src/plugins/home/public/services/tutorials/tutorial_service.mock.ts
@@ -0,0 +1,50 @@
+/*
+ * 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 { TutorialService, TutorialServiceSetup, TutorialServiceStart } from './tutorial_service';
+
+const createSetupMock = (): jest.Mocked<TutorialServiceSetup> => {
+  const setup = {
+    setVariable: jest.fn(),
+  };
+  return setup;
+};
+
+const createStartMock = (): jest.Mocked<TutorialServiceStart> => {
+  const start = {
+    get: jest.fn(),
+  };
+  return start;
+};
+
+const createMock = (): jest.Mocked<PublicMethodsOf<TutorialService>> => {
+  const service = {
+    setup: jest.fn(),
+    start: jest.fn(),
+  };
+  service.setup.mockImplementation(createSetupMock);
+  service.start.mockImplementation(createStartMock);
+  return service;
+};
+
+export const tutorialServiceMock = {
+  createSetup: createSetupMock,
+  createStart: createStartMock,
+  create: createMock,
+};
diff --git a/src/plugins/home/public/services/tutorials/tutorial_service.test.ts b/src/plugins/home/public/services/tutorials/tutorial_service.test.ts
new file mode 100644
index 0000000000000..04f6bce9b7caa
--- /dev/null
+++ b/src/plugins/home/public/services/tutorials/tutorial_service.test.ts
@@ -0,0 +1,55 @@
+/*
+ * 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 { TutorialService } from './tutorial_service';
+
+describe('TutorialService', () => {
+  describe('setup', () => {
+    test('allows multiple set calls', () => {
+      const setup = new TutorialService().setup();
+      expect(() => {
+        setup.setVariable('abc', 123);
+        setup.setVariable('def', 456);
+      }).not.toThrow();
+    });
+
+    test('throws when same variable is set twice', () => {
+      const setup = new TutorialService().setup();
+      expect(() => {
+        setup.setVariable('abc', 123);
+        setup.setVariable('abc', 456);
+      }).toThrow();
+    });
+  });
+
+  describe('start', () => {
+    test('returns empty object', () => {
+      const service = new TutorialService();
+      expect(service.start().get()).toEqual({});
+    });
+
+    test('returns last state of update calls', () => {
+      const service = new TutorialService();
+      const setup = service.setup();
+      setup.setVariable('abc', 123);
+      setup.setVariable('def', { subKey: 456 });
+      expect(service.start().get()).toEqual({ abc: 123, def: { subKey: 456 } });
+    });
+  });
+});
diff --git a/src/plugins/home/public/services/tutorials/tutorial_service.ts b/src/plugins/home/public/services/tutorials/tutorial_service.ts
new file mode 100644
index 0000000000000..824c3d46a76a3
--- /dev/null
+++ b/src/plugins/home/public/services/tutorials/tutorial_service.ts
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+/** @public */
+export type TutorialVariables = Partial<Record<string, unknown>>;
+
+export class TutorialService {
+  private tutorialVariables: TutorialVariables = {};
+
+  public setup() {
+    return {
+      /**
+       * Set a variable usable in tutorial templates. Access with `{config.<key>}`.
+       */
+      setVariable: (key: string, value: unknown) => {
+        if (this.tutorialVariables[key]) {
+          throw new Error('variable already set');
+        }
+        this.tutorialVariables[key] = value;
+      },
+    };
+  }
+
+  public start() {
+    return {
+      /**
+       * Retrieve the variables for substitution in tutorials. This API is only intended for internal
+       * use and is only exposed during a transition period of migrating the home app to the new platform.
+       * @deprecated
+       */
+      get: (): TutorialVariables => this.tutorialVariables,
+    };
+  }
+}
+
+export type TutorialServiceSetup = ReturnType<TutorialService['setup']>;
+export type TutorialServiceStart = ReturnType<TutorialService['start']>;
diff --git a/x-pack/plugins/cloud/public/plugin.ts b/x-pack/plugins/cloud/public/plugin.ts
index f6408afb31493..2b8247066bfc3 100644
--- a/x-pack/plugins/cloud/public/plugin.ts
+++ b/x-pack/plugins/cloud/public/plugin.ts
@@ -31,6 +31,9 @@ export class CloudPlugin implements Plugin<CloudSetup> {
 
     if (home) {
       home.environment.update({ cloud: isCloudEnabled });
+      if (isCloudEnabled) {
+        home.tutorials.setVariable('cloud', { id });
+      }
     }
 
     return {

From 9d37535b6437f9716d530052b8c091de64f4fa96 Mon Sep 17 00:00:00 2001
From: MadameSheema <snootchie.boochies@gmail.com>
Date: Wed, 19 Feb 2020 13:08:27 +0100
Subject: [PATCH 059/174] refactors 'url state' tests (#57981)

---
 .../integration/lib/url_state/index.ts        |  20 +-
 .../timeline/data_providers.spec.ts           |   4 +-
 .../smoke_tests/url_state/url_state.spec.ts   | 255 +++++++++---------
 .../plugins/siem/cypress/screens/calendar.ts  |  21 ++
 .../plugins/siem/cypress/screens/header.ts    |   2 +
 .../siem/cypress/screens/hosts/all_hosts.ts   |   4 +-
 .../siem/cypress/screens/hosts/main.ts        |   4 +
 .../siem/cypress/screens/network/flows.ts     |   7 +
 .../siem/cypress/screens/timeline/main.ts     |   2 +
 .../plugins/siem/cypress/tasks/calendar.ts    |  79 ++++++
 .../plugins/siem/cypress/tasks/header.ts      |   6 +-
 .../siem/cypress/tasks/hosts/all_hosts.ts     |  14 +-
 .../plugins/siem/cypress/tasks/hosts/main.ts  |  10 +-
 .../siem/cypress/tasks/network/flows.ts       |  12 +
 .../siem/cypress/tasks/timeline/main.ts       |   5 +
 .../legacy/plugins/siem/cypress/urls/state.ts |  21 ++
 16 files changed, 307 insertions(+), 159 deletions(-)
 create mode 100644 x-pack/legacy/plugins/siem/cypress/screens/calendar.ts
 create mode 100644 x-pack/legacy/plugins/siem/cypress/screens/network/flows.ts
 create mode 100644 x-pack/legacy/plugins/siem/cypress/tasks/calendar.ts
 create mode 100644 x-pack/legacy/plugins/siem/cypress/tasks/network/flows.ts
 create mode 100644 x-pack/legacy/plugins/siem/cypress/urls/state.ts

diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/url_state/index.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/url_state/index.ts
index 99d90e3c42aca..fa754cd4b8db4 100644
--- a/x-pack/legacy/plugins/siem/cypress/integration/lib/url_state/index.ts
+++ b/x-pack/legacy/plugins/siem/cypress/integration/lib/url_state/index.ts
@@ -12,24 +12,6 @@
  */
 
 export const ABSOLUTE_DATE_RANGE = {
-  endTime: '1564691609186',
-  endTimeFormat: '2019-08-01T20:33:29.186Z',
-  endTimeTimeline: '1564779809186',
-  endTimeTimelineFormat: '2019-08-02T21:03:29.186Z',
-  endTimeTimelineTyped: 'Aug 02, 2019 @ 21:03:29.186',
-  endTimeTyped: 'Aug 01, 2019 @ 14:33:29.186',
-  newEndTime: '1564693409186',
-  newEndTimeFormat: '2019-08-01T21:03:29.186Z',
-  newEndTimeTyped: 'Aug 01, 2019 @ 15:03:29.186',
-  newStartTime: '1564691609186',
-  newStartTimeFormat: '2019-08-01T20:33:29.186Z',
-  newStartTimeTyped: 'Aug 01, 2019 @ 14:33:29.186',
-  startTime: '1564689809186',
-  startTimeFormat: '2019-08-01T20:03:29.186Z',
-  startTimeTimeline: '1564776209186',
-  startTimeTimelineFormat: '2019-08-02T20:03:29.186Z',
-  startTimeTimelineTyped: 'Aug 02, 2019 @ 14:03:29.186',
-  startTimeTyped: 'Aug 01, 2019 @ 14:03:29.186',
   url:
     '/app/siem#/network/?timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))',
 
@@ -41,6 +23,8 @@ export const ABSOLUTE_DATE_RANGE = {
   urlKqlHostsHosts: `/app/siem#/hosts/allHosts?query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`,
   urlHost:
     '/app/siem#/hosts/authentications?timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))',
+  urlHostNew:
+    '/app/siem#/hosts/authentications?timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))',
 };
 export const DATE_PICKER_START_DATE_POPOVER_BUTTON =
   'div[data-test-subj="globalDatePicker"] button[data-test-subj="superDatePickerstartDatePopoverButton"]';
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/data_providers.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/data_providers.spec.ts
index c3fedfb06939b..7d1ee43b1b509 100644
--- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/data_providers.spec.ts
+++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/data_providers.spec.ts
@@ -11,7 +11,7 @@ import {
   dragFirstHostToTimeline,
   dragFirstHostToEmptyTimelineDataProviders,
 } from '../../../tasks/hosts/all_hosts';
-import { HOSTS_NAMES } from '../../../screens/hosts/all_hosts';
+import { HOSTS_NAMES_DRAGGABLE } from '../../../screens/hosts/all_hosts';
 import { DEFAULT_TIMEOUT, loginAndWaitForPage } from '../../../tasks/login';
 import { createNewTimeline } from '../../../tasks/timeline/main';
 import { openTimeline } from '../../../tasks/siem_main';
@@ -42,7 +42,7 @@ describe('timeline data providers', () => {
       .first()
       .invoke('text')
       .then(dataProviderText => {
-        cy.get(HOSTS_NAMES)
+        cy.get(HOSTS_NAMES_DRAGGABLE)
           .first()
           .invoke('text')
           .should(hostname => {
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/url_state/url_state.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/url_state/url_state.spec.ts
index cbd1b2a074a59..4345938c8867e 100644
--- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/url_state/url_state.spec.ts
+++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/url_state/url_state.spec.ts
@@ -4,32 +4,54 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+import { ABSOLUTE_DATE_RANGE } from '../../../urls/state';
+import { DEFAULT_TIMEOUT, loginAndWaitForPage } from '../../../tasks/login';
+import { HOSTS_PAGE } from '../../../urls/navigation';
 import {
-  ABSOLUTE_DATE_RANGE,
-  DATE_PICKER_ABSOLUTE_INPUT,
-  DATE_PICKER_ABSOLUTE_TAB,
-  DATE_PICKER_APPLY_BUTTON,
-  DATE_PICKER_APPLY_BUTTON_TIMELINE,
-  DATE_PICKER_END_DATE_POPOVER_BUTTON,
+  setStartDate,
+  setEndDate,
+  updateDates,
+  setTimelineStartDate,
+  setTimelineEndDate,
+  updateTimelineDates,
+} from '../../../tasks/calendar';
+import { waitForIpsTableToBeLoaded } from '../../../tasks/network/flows';
+import { openTimeline } from '../../../tasks/siem_main';
+import {
+  DATE_PICKER_START_DATE_POPOVER_BUTTON_TIMELINE,
   DATE_PICKER_END_DATE_POPOVER_BUTTON_TIMELINE,
   DATE_PICKER_START_DATE_POPOVER_BUTTON,
-  DATE_PICKER_START_DATE_POPOVER_BUTTON_TIMELINE,
-  KQL_INPUT,
-  TIMELINE_TITLE,
-  HOST_DETAIL_SIEM_KIBANA,
-  BREADCRUMBS,
-} from '../../lib/url_state';
-import { DEFAULT_TIMEOUT, loginAndWaitForPage } from '../../lib/util/helpers';
-import {
-  assertAtLeastOneEventMatchesSearch,
-  executeKQL,
-  hostExistsQuery,
-  toggleTimelineVisibility,
-} from '../../lib/timeline/helpers';
-import { NAVIGATION_NETWORK, NAVIGATION_HOSTS } from '../../lib/navigation/selectors';
-import { HOSTS_PAGE } from '../../lib/urls';
-import { waitForAllHostsWidget } from '../../lib/hosts/helpers';
-import { NAVIGATION_HOSTS_ALL_HOSTS, NAVIGATION_HOSTS_ANOMALIES } from '../../lib/hosts/selectors';
+  DATE_PICKER_END_DATE_POPOVER_BUTTON,
+} from '../../../screens/calendar';
+import { kqlSearch, navigateFromHeaderTo, clearSearchBar } from '../../../tasks/header';
+import { HOSTS, NETWORK, KQL_INPUT, BREADCRUMBS } from '../../../screens/header';
+import { openAllHosts } from '../../../tasks/hosts/main';
+import { ANOMALIES_TAB } from '../../../screens/hosts/main';
+import { waitForAllHostsToBeLoaded, openFirstHostDetails } from '../../../tasks/hosts/all_hosts';
+import { HOSTS_NAMES } from '../../../screens/hosts/all_hosts';
+import { executeTimelineKQL, addNameToTimeline } from '../../../tasks/timeline/main';
+import { SERVER_SIDE_EVENT_COUNT, TIMELINE_TITLE } from '../../../screens/timeline/main';
+
+const ABSOLUTE_DATE = {
+  endTime: '1564691609186',
+  endTimeFormat: '2019-08-01T20:33:29.186Z',
+  endTimeTimeline: '1564779809186',
+  endTimeTimelineFormat: '2019-08-02T21:03:29.186Z',
+  endTimeTimelineTyped: 'Aug 02, 2019 @ 21:03:29.186',
+  endTimeTyped: 'Aug 01, 2019 @ 14:33:29.186',
+  newEndTime: '1564693409186',
+  newEndTimeFormat: '2019-08-01T21:03:29.186Z',
+  newEndTimeTyped: 'Aug 01, 2019 @ 15:03:29.186',
+  newStartTime: '1564691609186',
+  newStartTimeFormat: '2019-08-01T20:33:29.186Z',
+  newStartTimeTyped: 'Aug 01, 2019 @ 14:33:29.186',
+  startTime: '1564689809186',
+  startTimeFormat: '2019-08-01T20:03:29.186Z',
+  startTimeTimeline: '1564776209186',
+  startTimeTimelineFormat: '2019-08-02T20:03:29.186Z',
+  startTimeTimelineTyped: 'Aug 02, 2019 @ 14:03:29.186',
+  startTimeTyped: 'Aug 01, 2019 @ 14:03:29.186',
+};
 
 describe('url state', () => {
   it('sets the global start and end dates from the url', () => {
@@ -37,162 +59,122 @@ describe('url state', () => {
     cy.get(DATE_PICKER_START_DATE_POPOVER_BUTTON).should(
       'have.attr',
       'title',
-      ABSOLUTE_DATE_RANGE.startTimeFormat
+      ABSOLUTE_DATE.startTimeFormat
     );
     cy.get(DATE_PICKER_END_DATE_POPOVER_BUTTON).should(
       'have.attr',
       'title',
-      ABSOLUTE_DATE_RANGE.endTimeFormat
+      ABSOLUTE_DATE.endTimeFormat
     );
   });
 
   it('sets the url state when start and end date are set', () => {
     loginAndWaitForPage(ABSOLUTE_DATE_RANGE.url);
-
-    cy.get(DATE_PICKER_START_DATE_POPOVER_BUTTON).click({ force: true });
-
-    cy.get(DATE_PICKER_ABSOLUTE_TAB)
-      .first()
-      .click({ force: true });
-
-    cy.get(DATE_PICKER_ABSOLUTE_INPUT, { timeout: DEFAULT_TIMEOUT })
-      .clear()
-      .type(`${ABSOLUTE_DATE_RANGE.newStartTimeTyped}`);
-
-    cy.get(DATE_PICKER_APPLY_BUTTON, { timeout: DEFAULT_TIMEOUT })
-      .click({ force: true })
-      .invoke('text', { timeout: DEFAULT_TIMEOUT })
-      .should('not.equal', 'Updating');
-
-    cy.get('[data-test-subj="table-topNFlowSource-loading-false"]', {
-      timeout: DEFAULT_TIMEOUT,
-    }).should('exist');
-
-    cy.get(DATE_PICKER_END_DATE_POPOVER_BUTTON).click({ force: true });
-
-    cy.get(DATE_PICKER_ABSOLUTE_TAB)
-      .first()
-      .click({ force: true });
-
-    cy.get(DATE_PICKER_ABSOLUTE_INPUT, { timeout: DEFAULT_TIMEOUT })
-      .clear()
-      .type(`${ABSOLUTE_DATE_RANGE.newEndTimeTyped}`);
-
-    cy.get(DATE_PICKER_APPLY_BUTTON, { timeout: DEFAULT_TIMEOUT })
-      .click({ force: true })
-      .invoke('text', { timeout: DEFAULT_TIMEOUT })
-      .should('not.equal', 'Updating');
+    setStartDate(ABSOLUTE_DATE.newStartTimeTyped);
+    updateDates();
+    waitForIpsTableToBeLoaded();
+    setEndDate(ABSOLUTE_DATE.newEndTimeTyped);
+    updateDates();
 
     cy.url().should(
       'include',
       `(global:(linkTo:!(timeline),timerange:(from:${new Date(
-        ABSOLUTE_DATE_RANGE.newStartTimeTyped
-      ).valueOf()},kind:absolute,to:${new Date(ABSOLUTE_DATE_RANGE.newEndTimeTyped).valueOf()}))`
+        ABSOLUTE_DATE.newStartTimeTyped
+      ).valueOf()},kind:absolute,to:${new Date(ABSOLUTE_DATE.newEndTimeTyped).valueOf()}))`
     );
   });
 
   it('sets the timeline start and end dates from the url when locked to global time', () => {
     loginAndWaitForPage(ABSOLUTE_DATE_RANGE.url);
-    toggleTimelineVisibility();
+    openTimeline();
+
     cy.get(DATE_PICKER_START_DATE_POPOVER_BUTTON_TIMELINE).should(
       'have.attr',
       'title',
-      ABSOLUTE_DATE_RANGE.startTimeFormat
+      ABSOLUTE_DATE.startTimeFormat
     );
     cy.get(DATE_PICKER_END_DATE_POPOVER_BUTTON_TIMELINE).should(
       'have.attr',
       'title',
-      ABSOLUTE_DATE_RANGE.endTimeFormat
+      ABSOLUTE_DATE.endTimeFormat
     );
   });
 
   it('sets the timeline start and end dates independently of the global start and end dates when times are unlocked', () => {
     loginAndWaitForPage(ABSOLUTE_DATE_RANGE.urlUnlinked);
+
     cy.get(DATE_PICKER_START_DATE_POPOVER_BUTTON).should(
       'have.attr',
       'title',
-      ABSOLUTE_DATE_RANGE.startTimeFormat
+      ABSOLUTE_DATE.startTimeFormat
     );
     cy.get(DATE_PICKER_END_DATE_POPOVER_BUTTON).should(
       'have.attr',
       'title',
-      ABSOLUTE_DATE_RANGE.endTimeFormat
+      ABSOLUTE_DATE.endTimeFormat
     );
 
-    toggleTimelineVisibility();
+    openTimeline();
+
     cy.get(DATE_PICKER_START_DATE_POPOVER_BUTTON_TIMELINE).should(
       'have.attr',
       'title',
-      ABSOLUTE_DATE_RANGE.startTimeTimelineFormat
+      ABSOLUTE_DATE.startTimeTimelineFormat
     );
     cy.get(DATE_PICKER_END_DATE_POPOVER_BUTTON_TIMELINE).should(
       'have.attr',
       'title',
-      ABSOLUTE_DATE_RANGE.endTimeTimelineFormat
+      ABSOLUTE_DATE.endTimeTimelineFormat
     );
   });
 
   it('sets the url state when timeline/global date pickers are unlinked and timeline start and end date are set', () => {
     loginAndWaitForPage(ABSOLUTE_DATE_RANGE.urlUnlinked);
-
-    toggleTimelineVisibility();
-    cy.get(DATE_PICKER_START_DATE_POPOVER_BUTTON_TIMELINE, { timeout: DEFAULT_TIMEOUT }).click({
-      force: true,
-    });
-
-    cy.get(DATE_PICKER_ABSOLUTE_TAB)
-      .first()
-      .click({ force: true });
-
-    cy.get(DATE_PICKER_ABSOLUTE_INPUT, { timeout: 5000 }).type(
-      `{selectall}{backspace}${ABSOLUTE_DATE_RANGE.newStartTimeTyped}`
-    );
-
-    cy.get(DATE_PICKER_APPLY_BUTTON_TIMELINE).click({ force: true });
-
-    cy.get(DATE_PICKER_END_DATE_POPOVER_BUTTON_TIMELINE).click({ force: true });
-
-    cy.get(DATE_PICKER_ABSOLUTE_TAB)
-      .first()
-      .click({ force: true });
-
-    cy.get(DATE_PICKER_ABSOLUTE_INPUT, { timeout: 5000 }).type(
-      `{selectall}{backspace}${ABSOLUTE_DATE_RANGE.newEndTimeTyped}{enter}`
-    );
-
-    cy.get(DATE_PICKER_APPLY_BUTTON_TIMELINE).click({ force: true });
+    openTimeline();
+    setTimelineStartDate(ABSOLUTE_DATE.newStartTimeTyped);
+    updateTimelineDates();
+    setTimelineEndDate(ABSOLUTE_DATE.newEndTimeTyped);
+    updateTimelineDates();
 
     cy.url().should(
       'include',
       `timeline:(linkTo:!(),timerange:(from:${new Date(
-        ABSOLUTE_DATE_RANGE.newStartTimeTyped
-      ).valueOf()},kind:absolute,to:${new Date(ABSOLUTE_DATE_RANGE.newEndTimeTyped).valueOf()}))`
+        ABSOLUTE_DATE.newStartTimeTyped
+      ).valueOf()},kind:absolute,to:${new Date(ABSOLUTE_DATE.newEndTimeTyped).valueOf()}))`
     );
   });
 
   it('sets kql on network page', () => {
     loginAndWaitForPage(ABSOLUTE_DATE_RANGE.urlKqlNetworkNetwork);
-    cy.get(KQL_INPUT, { timeout: 5000 }).should('have.attr', 'value', 'source.ip: "10.142.0.9"');
+    cy.get(KQL_INPUT, { timeout: DEFAULT_TIMEOUT }).should(
+      'have.attr',
+      'value',
+      'source.ip: "10.142.0.9"'
+    );
   });
 
   it('sets kql on hosts page', () => {
     loginAndWaitForPage(ABSOLUTE_DATE_RANGE.urlKqlHostsHosts);
-    cy.get(KQL_INPUT, { timeout: 5000 }).should('have.attr', 'value', 'source.ip: "10.142.0.9"');
+    cy.get(KQL_INPUT, { timeout: DEFAULT_TIMEOUT }).should(
+      'have.attr',
+      'value',
+      'source.ip: "10.142.0.9"'
+    );
   });
 
   it('sets the url state when kql is set', () => {
     loginAndWaitForPage(ABSOLUTE_DATE_RANGE.url);
-    cy.get(KQL_INPUT, { timeout: 5000 }).type('source.ip: "10.142.0.9" {enter}');
+    kqlSearch('source.ip: "10.142.0.9" {enter}');
+
     cy.url().should('include', `query=(language:kuery,query:'source.ip:%20%2210.142.0.9%22%20')`);
   });
 
   it('sets the url state when kql is set and check if href reflect this change', () => {
     loginAndWaitForPage(ABSOLUTE_DATE_RANGE.url);
-    cy.get(KQL_INPUT, { timeout: 5000 }).type('source.ip: "10.142.0.9" {enter}');
-    cy.get(NAVIGATION_HOSTS)
-      .first()
-      .click({ force: true });
-    cy.get(NAVIGATION_NETWORK).should(
+    kqlSearch('source.ip: "10.142.0.9" {enter}');
+    navigateFromHeaderTo(HOSTS);
+
+    cy.get(NETWORK).should(
       'have.attr',
       'href',
       "#/link-to/network?query=(language:kuery,query:'source.ip:%20%2210.142.0.9%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))"
@@ -200,65 +182,74 @@ describe('url state', () => {
   });
 
   it('sets KQL in host page and detail page and check if href match on breadcrumb, tabs and subTabs', () => {
-    loginAndWaitForPage(ABSOLUTE_DATE_RANGE.urlHost);
-    cy.get(KQL_INPUT, { timeout: 5000 }).type('host.name: "siem-kibana" {enter}');
-    cy.get(NAVIGATION_HOSTS_ALL_HOSTS, { timeout: 5000 })
-      .first()
-      .click({ force: true });
-    waitForAllHostsWidget();
-    cy.get(NAVIGATION_HOSTS).should(
+    loginAndWaitForPage(ABSOLUTE_DATE_RANGE.urlHostNew);
+    kqlSearch('host.name: "siem-kibana" {enter}');
+    openAllHosts();
+    waitForAllHostsToBeLoaded();
+
+    cy.get(HOSTS).should(
       'have.attr',
       'href',
-      "#/link-to/hosts?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))"
+      "#/link-to/hosts?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))"
     );
-    cy.get(NAVIGATION_NETWORK).should(
+    cy.get(NETWORK).should(
       'have.attr',
       'href',
-      "#/link-to/network?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))"
+      "#/link-to/network?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))"
     );
-    cy.get(HOST_DETAIL_SIEM_KIBANA, { timeout: 5000 })
+    cy.get(HOSTS_NAMES, { timeout: DEFAULT_TIMEOUT })
       .first()
       .invoke('text')
       .should('eq', 'siem-kibana');
-    cy.get(HOST_DETAIL_SIEM_KIBANA)
-      .first()
-      .click({ force: true });
-    cy.get(KQL_INPUT, { timeout: 5000 }).clear();
-    cy.get(KQL_INPUT, { timeout: 5000 }).type('agent.type: "auditbeat" {enter}');
-    cy.get(NAVIGATION_HOSTS_ANOMALIES).should(
+
+    openFirstHostDetails();
+    clearSearchBar();
+    kqlSearch('agent.type: "auditbeat" {enter}');
+
+    cy.get(ANOMALIES_TAB).should(
       'have.attr',
       'href',
-      "#/hosts/siem-kibana/anomalies?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))"
+      "#/hosts/siem-kibana/anomalies?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))"
     );
     cy.get(BREADCRUMBS)
       .eq(1)
       .should(
         'have.attr',
         'href',
-        "#/link-to/hosts?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))"
+        "#/link-to/hosts?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))"
       );
     cy.get(BREADCRUMBS)
       .eq(2)
       .should(
         'have.attr',
         'href',
-        "#/link-to/hosts/siem-kibana?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))"
+        "#/link-to/hosts/siem-kibana?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))"
       );
   });
 
   it('Do not clears kql when navigating to a new page', () => {
     loginAndWaitForPage(ABSOLUTE_DATE_RANGE.urlKqlHostsHosts);
-    cy.get(NAVIGATION_NETWORK).click({ force: true });
-    cy.get(KQL_INPUT, { timeout: 5000 }).should('have.attr', 'value', 'source.ip: "10.142.0.9"');
+    navigateFromHeaderTo(NETWORK);
+
+    cy.get(KQL_INPUT, { timeout: DEFAULT_TIMEOUT }).should(
+      'have.attr',
+      'value',
+      'source.ip: "10.142.0.9"'
+    );
   });
 
   it('sets and reads the url state for timeline by id', () => {
     loginAndWaitForPage(HOSTS_PAGE);
-    toggleTimelineVisibility();
-    executeKQL(hostExistsQuery);
-    assertAtLeastOneEventMatchesSearch();
+    openTimeline();
+    executeTimelineKQL('host.name: *');
+
+    cy.get(SERVER_SIDE_EVENT_COUNT, { timeout: DEFAULT_TIMEOUT })
+      .invoke('text')
+      .should('be.above', 0);
+
     const bestTimelineName = 'The Best Timeline';
-    cy.get(TIMELINE_TITLE, { timeout: 5000 }).type(bestTimelineName);
+    addNameToTimeline(bestTimelineName);
+
     cy.url().should('include', 'timeline=');
     cy.visit(
       `/app/siem#/timelines?timerange=(global:(linkTo:!(),timerange:(from:1565274377369,kind:absolute,to:1565360777369)),timeline:(linkTo:!(),timerange:(from:1565274377369,kind:absolute,to:1565360777369)))`
diff --git a/x-pack/legacy/plugins/siem/cypress/screens/calendar.ts b/x-pack/legacy/plugins/siem/cypress/screens/calendar.ts
new file mode 100644
index 0000000000000..caff721c683e9
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/cypress/screens/calendar.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 DATE_PICKER_START_DATE_POPOVER_BUTTON =
+  'div[data-test-subj="globalDatePicker"] button[data-test-subj="superDatePickerstartDatePopoverButton"]';
+export const DATE_PICKER_END_DATE_POPOVER_BUTTON =
+  '[data-test-subj="globalDatePicker"] [data-test-subj="superDatePickerendDatePopoverButton"]';
+export const DATE_PICKER_ABSOLUTE_TAB = '[data-test-subj="superDatePickerAbsoluteTab"]';
+export const DATE_PICKER_APPLY_BUTTON =
+  '[data-test-subj="globalDatePicker"] button[data-test-subj="querySubmitButton"]';
+export const DATE_PICKER_APPLY_BUTTON_TIMELINE =
+  '[data-test-subj="timeline-properties"] button[data-test-subj="superDatePickerApplyTimeButton"]';
+export const DATE_PICKER_ABSOLUTE_INPUT = '[data-test-subj="superDatePickerAbsoluteDateInput"]';
+export const DATE_PICKER_START_DATE_POPOVER_BUTTON_TIMELINE =
+  '[data-test-subj="timeline-properties"] [data-test-subj="superDatePickerstartDatePopoverButton"]';
+
+export const DATE_PICKER_END_DATE_POPOVER_BUTTON_TIMELINE =
+  '[data-test-subj="timeline-properties"] [data-test-subj="superDatePickerendDatePopoverButton"]';
diff --git a/x-pack/legacy/plugins/siem/cypress/screens/header.ts b/x-pack/legacy/plugins/siem/cypress/screens/header.ts
index 344fa1829bdec..4ffb497a62432 100644
--- a/x-pack/legacy/plugins/siem/cypress/screens/header.ts
+++ b/x-pack/legacy/plugins/siem/cypress/screens/header.ts
@@ -15,3 +15,5 @@ export const OVERVIEW = '[data-test-subj="navigation-overview"]';
 export const TIMELINES = '[data-test-subj="navigation-timelines"]';
 
 export const REFRESH_BUTTON = '[data-test-subj="querySubmitButton"]';
+
+export const BREADCRUMBS = '[data-test-subj="breadcrumbs"] a';
diff --git a/x-pack/legacy/plugins/siem/cypress/screens/hosts/all_hosts.ts b/x-pack/legacy/plugins/siem/cypress/screens/hosts/all_hosts.ts
index f316356580814..61f39ca7a8b0c 100644
--- a/x-pack/legacy/plugins/siem/cypress/screens/hosts/all_hosts.ts
+++ b/x-pack/legacy/plugins/siem/cypress/screens/hosts/all_hosts.ts
@@ -6,4 +6,6 @@
 
 export const ALL_HOSTS_TABLE = '[data-test-subj="table-allHosts-loading-false"]';
 
-export const HOSTS_NAMES = '[data-test-subj="draggable-content-host.name"]';
+export const HOSTS_NAMES_DRAGGABLE = '[data-test-subj="draggable-content-host.name"]';
+
+export const HOSTS_NAMES = '[data-test-subj="draggable-content-host.name"] a.euiLink';
diff --git a/x-pack/legacy/plugins/siem/cypress/screens/hosts/main.ts b/x-pack/legacy/plugins/siem/cypress/screens/hosts/main.ts
index 2187ca40a38a4..25696be526e5f 100644
--- a/x-pack/legacy/plugins/siem/cypress/screens/hosts/main.ts
+++ b/x-pack/legacy/plugins/siem/cypress/screens/hosts/main.ts
@@ -10,4 +10,8 @@ export const AUTHENTICATIONS_TAB = '[data-test-subj="navigation-authentications"
 
 export const UNCOMMON_PROCESSES_TAB = '[data-test-subj="navigation-uncommonProcesses"]';
 
+export const ALL_HOSTS_TAB = '[data-test-subj="navigation-allHosts';
+
+export const ANOMALIES_TAB = '[data-test-subj="navigation-anomalies"]';
+
 export const KQL_SEARCH_BAR = '[data-test-subj="queryInput"]';
diff --git a/x-pack/legacy/plugins/siem/cypress/screens/network/flows.ts b/x-pack/legacy/plugins/siem/cypress/screens/network/flows.ts
new file mode 100644
index 0000000000000..6a2160438c455
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/cypress/screens/network/flows.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 const IPS_TABLE_LOADED = '[data-test-subj="table-topNFlowSource-loading-false"]';
diff --git a/x-pack/legacy/plugins/siem/cypress/screens/timeline/main.ts b/x-pack/legacy/plugins/siem/cypress/screens/timeline/main.ts
index 7b47c159c4a0a..6df269b7691a8 100644
--- a/x-pack/legacy/plugins/siem/cypress/screens/timeline/main.ts
+++ b/x-pack/legacy/plugins/siem/cypress/screens/timeline/main.ts
@@ -49,3 +49,5 @@ export const TIMESTAMP_HEADER_FIELD = '[data-test-subj="header-text-@timestamp"]
 export const ID_HEADER_FIELD = '[data-test-subj="timeline"] [data-test-subj="header-text-_id"]';
 
 export const ID_FIELD = '[data-test-subj="timeline"] [data-test-subj="field-name-_id"]';
+
+export const TIMELINE_TITLE = '[data-test-subj="timeline-title"]';
diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/calendar.ts b/x-pack/legacy/plugins/siem/cypress/tasks/calendar.ts
new file mode 100644
index 0000000000000..16231317d6aef
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/cypress/tasks/calendar.ts
@@ -0,0 +1,79 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import {
+  DATE_PICKER_START_DATE_POPOVER_BUTTON_TIMELINE,
+  DATE_PICKER_END_DATE_POPOVER_BUTTON_TIMELINE,
+  DATE_PICKER_APPLY_BUTTON_TIMELINE,
+  DATE_PICKER_START_DATE_POPOVER_BUTTON,
+  DATE_PICKER_ABSOLUTE_TAB,
+  DATE_PICKER_ABSOLUTE_INPUT,
+  DATE_PICKER_APPLY_BUTTON,
+  DATE_PICKER_END_DATE_POPOVER_BUTTON,
+} from '../screens/calendar';
+
+import { DEFAULT_TIMEOUT } from '../tasks/login';
+
+export const setStartDate = (date: string) => {
+  cy.get(DATE_PICKER_START_DATE_POPOVER_BUTTON).click({ force: true });
+
+  cy.get(DATE_PICKER_ABSOLUTE_TAB)
+    .first()
+    .click({ force: true });
+
+  cy.get(DATE_PICKER_ABSOLUTE_INPUT, { timeout: DEFAULT_TIMEOUT })
+    .clear()
+    .type(date);
+};
+
+export const setEndDate = (date: string) => {
+  cy.get(DATE_PICKER_END_DATE_POPOVER_BUTTON).click({ force: true });
+
+  cy.get(DATE_PICKER_ABSOLUTE_TAB)
+    .first()
+    .click({ force: true });
+
+  cy.get(DATE_PICKER_ABSOLUTE_INPUT, { timeout: DEFAULT_TIMEOUT })
+    .clear()
+    .type(date);
+};
+
+export const updateDates = () => {
+  cy.get(DATE_PICKER_APPLY_BUTTON, { timeout: DEFAULT_TIMEOUT })
+    .click({ force: true })
+    .invoke('text', { timeout: DEFAULT_TIMEOUT })
+    .should('not.equal', 'Updating');
+};
+
+export const setTimelineStartDate = (date: string) => {
+  cy.get(DATE_PICKER_START_DATE_POPOVER_BUTTON_TIMELINE, { timeout: DEFAULT_TIMEOUT }).click({
+    force: true,
+  });
+
+  cy.get(DATE_PICKER_ABSOLUTE_TAB)
+    .first()
+    .click({ force: true });
+
+  cy.get(DATE_PICKER_ABSOLUTE_INPUT, { timeout: DEFAULT_TIMEOUT }).type(
+    `{selectall}{backspace}${date}{enter}`
+  );
+};
+
+export const setTimelineEndDate = (date: string) => {
+  cy.get(DATE_PICKER_END_DATE_POPOVER_BUTTON_TIMELINE).click({ force: true });
+
+  cy.get(DATE_PICKER_ABSOLUTE_TAB)
+    .first()
+    .click({ force: true });
+
+  cy.get(DATE_PICKER_ABSOLUTE_INPUT, { timeout: DEFAULT_TIMEOUT }).type(
+    `{selectall}{backspace}${date}{enter}`
+  );
+};
+
+export const updateTimelineDates = () => {
+  cy.get(DATE_PICKER_APPLY_BUTTON_TIMELINE).click({ force: true });
+};
diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/header.ts b/x-pack/legacy/plugins/siem/cypress/tasks/header.ts
index 1c2f21c40dfba..4067779136d9e 100644
--- a/x-pack/legacy/plugins/siem/cypress/tasks/header.ts
+++ b/x-pack/legacy/plugins/siem/cypress/tasks/header.ts
@@ -5,7 +5,7 @@
  */
 
 import { DEFAULT_TIMEOUT } from '../tasks/login';
-import { REFRESH_BUTTON, KQL_INPUT } from '../screens/header';
+import { KQL_INPUT, REFRESH_BUTTON } from '../screens/header';
 
 export const navigateFromHeaderTo = (page: string) => {
   cy.get(page).click({ force: true });
@@ -23,3 +23,7 @@ export const refreshPage = () => {
     .invoke('text', { timeout: DEFAULT_TIMEOUT })
     .should('not.equal', 'Updating');
 };
+
+export const kqlSearch = (search: string) => {
+  cy.get(KQL_INPUT, { timeout: DEFAULT_TIMEOUT }).type(search);
+};
diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/hosts/all_hosts.ts b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/all_hosts.ts
index 43e2a7e1bef11..7146c132db4a0 100644
--- a/x-pack/legacy/plugins/siem/cypress/tasks/hosts/all_hosts.ts
+++ b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/all_hosts.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { ALL_HOSTS_TABLE, HOSTS_NAMES } from '../../screens/hosts/all_hosts';
+import { ALL_HOSTS_TABLE, HOSTS_NAMES_DRAGGABLE, HOSTS_NAMES } from '../../screens/hosts/all_hosts';
 import {
   TIMELINE_DATA_PROVIDERS,
   TIMELINE_DATA_PROVIDERS_EMPTY,
@@ -17,20 +17,20 @@ export const waitForAllHostsToBeLoaded = () => {
 };
 
 export const dragAndDropFirstHostToTimeline = () => {
-  cy.get(HOSTS_NAMES)
+  cy.get(HOSTS_NAMES_DRAGGABLE)
     .first()
     .then(firstHost => drag(firstHost));
   cy.get(TIMELINE_DATA_PROVIDERS).then(dataProvidersDropArea => drop(dataProvidersDropArea));
 };
 
 export const dragFirstHostToTimeline = () => {
-  cy.get(HOSTS_NAMES)
+  cy.get(HOSTS_NAMES_DRAGGABLE)
     .first()
     .then(host => drag(host));
 };
 
 export const dragFirstHostToEmptyTimelineDataProviders = () => {
-  cy.get(HOSTS_NAMES)
+  cy.get(HOSTS_NAMES_DRAGGABLE)
     .first()
     .then(host => drag(host));
 
@@ -38,3 +38,9 @@ export const dragFirstHostToEmptyTimelineDataProviders = () => {
     dragWithoutDrop(dataProvidersDropArea)
   );
 };
+
+export const openFirstHostDetails = () => {
+  cy.get(HOSTS_NAMES)
+    .first()
+    .click({ force: true });
+};
diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/hosts/main.ts b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/main.ts
index 11cd0c8405f26..cbf410b4bc232 100644
--- a/x-pack/legacy/plugins/siem/cypress/tasks/hosts/main.ts
+++ b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/main.ts
@@ -6,7 +6,12 @@
 
 import { DEFAULT_TIMEOUT } from '../../integration/lib/util/helpers';
 
-import { EVENTS_TAB, AUTHENTICATIONS_TAB, UNCOMMON_PROCESSES_TAB } from '../../screens/hosts/main';
+import {
+  EVENTS_TAB,
+  AUTHENTICATIONS_TAB,
+  UNCOMMON_PROCESSES_TAB,
+  ALL_HOSTS_TAB,
+} from '../../screens/hosts/main';
 
 /** Clicks the Events tab on the hosts page */
 export const openEvents = () =>
@@ -17,3 +22,6 @@ export const openAuthentications = () =>
 
 export const openUncommonProcesses = () =>
   cy.get(UNCOMMON_PROCESSES_TAB, { timeout: DEFAULT_TIMEOUT }).click({ force: true });
+
+export const openAllHosts = () =>
+  cy.get(ALL_HOSTS_TAB, { timeout: DEFAULT_TIMEOUT }).click({ force: true });
diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/network/flows.ts b/x-pack/legacy/plugins/siem/cypress/tasks/network/flows.ts
new file mode 100644
index 0000000000000..c7b031aabc8cd
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/cypress/tasks/network/flows.ts
@@ -0,0 +1,12 @@
+/*
+ * 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 { IPS_TABLE_LOADED } from '../../screens/network/flows';
+import { DEFAULT_TIMEOUT } from '../../tasks/login';
+
+export const waitForIpsTableToBeLoaded = () => {
+  cy.get(IPS_TABLE_LOADED, { timeout: DEFAULT_TIMEOUT }).should('exist');
+};
diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/timeline/main.ts b/x-pack/legacy/plugins/siem/cypress/tasks/timeline/main.ts
index d26c0f2911f75..80b5e4379212c 100644
--- a/x-pack/legacy/plugins/siem/cypress/tasks/timeline/main.ts
+++ b/x-pack/legacy/plugins/siem/cypress/tasks/timeline/main.ts
@@ -19,6 +19,7 @@ import {
   ID_TOGGLE_FIELD,
   ID_HEADER_FIELD,
   ID_FIELD,
+  TIMELINE_TITLE,
 } from '../../screens/timeline/main';
 
 import { drag, drop } from '../../tasks/common';
@@ -86,3 +87,7 @@ export const dragAndDropIdToggleFieldToTimeline = () => {
     drop(headersDropArea)
   );
 };
+
+export const addNameToTimeline = (name: string) => {
+  cy.get(TIMELINE_TITLE, { timeout: DEFAULT_TIMEOUT }).type(name);
+};
diff --git a/x-pack/legacy/plugins/siem/cypress/urls/state.ts b/x-pack/legacy/plugins/siem/cypress/urls/state.ts
new file mode 100644
index 0000000000000..18f4628aa7137
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/cypress/urls/state.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 ABSOLUTE_DATE_RANGE = {
+  url:
+    '/app/siem#/network/?timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))',
+
+  urlUnlinked:
+    '/app/siem#/network/?timerange=(global:(linkTo:!(),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(),timerange:(from:1564776209186,kind:absolute,to:1564779809186)))',
+  urlKqlNetworkNetwork: `/app/siem#/network/?query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`,
+  urlKqlNetworkHosts: `/app/siem#/network/?query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`,
+  urlKqlHostsNetwork: `/app/siem#/hosts/allHosts?query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`,
+  urlKqlHostsHosts: `/app/siem#/hosts/allHosts?query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`,
+  urlHost:
+    '/app/siem#/hosts/authentications?timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))',
+  urlHostNew:
+    '/app/siem#/hosts/authentications?timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))',
+};

From 518546dad6f5c5fa8c45f0213d4a0271457d5bb9 Mon Sep 17 00:00:00 2001
From: Ahmad Bamieh <ahmadbamieh@gmail.com>
Date: Wed, 19 Feb 2020 14:11:13 +0200
Subject: [PATCH 060/174] [i18n] exclude target and vendor when extracting
 files (#57978)

* exclude target and vendor

* update snapshots
---
 .../i18n/__snapshots__/integrate_locale_files.test.ts.snap  | 6 ++++--
 src/dev/i18n/extract_default_translations.js                | 3 +++
 src/dev/i18n/serializers/__snapshots__/json.test.ts.snap    | 3 ++-
 src/dev/i18n/serializers/json.ts                            | 2 +-
 4 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/src/dev/i18n/__snapshots__/integrate_locale_files.test.ts.snap b/src/dev/i18n/__snapshots__/integrate_locale_files.test.ts.snap
index 83ded0984459a..c67895266c6c4 100644
--- a/src/dev/i18n/__snapshots__/integrate_locale_files.test.ts.snap
+++ b/src/dev/i18n/__snapshots__/integrate_locale_files.test.ts.snap
@@ -64,7 +64,8 @@ Array [
     \\"plugin-1.message-id-1\\": \\"Translated text 1\\",
     \\"plugin-1.message-id-2\\": \\"Translated text 2\\"
   }
-}",
+}
+",
 ]
 `;
 
@@ -131,7 +132,8 @@ Array [
   \\"messages\\": {
     \\"plugin-2.message-id\\": \\"Translated text\\"
   }
-}",
+}
+",
 ]
 `;
 
diff --git a/src/dev/i18n/extract_default_translations.js b/src/dev/i18n/extract_default_translations.js
index 9f34d40f12e7c..9daf431ad5401 100644
--- a/src/dev/i18n/extract_default_translations.js
+++ b/src/dev/i18n/extract_default_translations.js
@@ -63,6 +63,9 @@ export async function matchEntriesWithExctractors(inputPath, options = {}) {
   const ignore = [
     '**/node_modules/**',
     '**/__tests__/**',
+    '**/dist/**',
+    '**/target/**',
+    '**/vendor/**',
     '**/*.test.{js,jsx,ts,tsx}',
     '**/*.d.ts',
   ].concat(additionalIgnore);
diff --git a/src/dev/i18n/serializers/__snapshots__/json.test.ts.snap b/src/dev/i18n/serializers/__snapshots__/json.test.ts.snap
index d31d37c4b65cd..71b9d51c38af4 100644
--- a/src/dev/i18n/serializers/__snapshots__/json.test.ts.snap
+++ b/src/dev/i18n/serializers/__snapshots__/json.test.ts.snap
@@ -85,5 +85,6 @@ exports[`dev/i18n/serializers/json should serialize default messages to JSON 1`]
       \\"comment\\": \\"Message description\\"
     }
   }
-}"
+}
+"
 `;
diff --git a/src/dev/i18n/serializers/json.ts b/src/dev/i18n/serializers/json.ts
index b2b3985778fb1..f705486e35c25 100644
--- a/src/dev/i18n/serializers/json.ts
+++ b/src/dev/i18n/serializers/json.ts
@@ -34,5 +34,5 @@ export const serializeToJson: Serializer = (messages, formats = i18n.formats) =>
     }
   }
 
-  return JSON.stringify(resultJsonObject, undefined, 2);
+  return JSON.stringify(resultJsonObject, undefined, 2).concat('\n');
 };

From faac5be34c1f6af79d26bf9effd13c0f55d0ba97 Mon Sep 17 00:00:00 2001
From: Dmitry Lemeshko <dzmitry.lemechko@elastic.co>
Date: Wed, 19 Feb 2020 13:54:56 +0100
Subject: [PATCH 061/174] replace Promise.all with for loop for clicks (#57909)

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../discover/np_ready/angular/discover.html   |  1 +
 .../apps/discover/_doc_navigation.js          | 17 +++++++--------
 .../functional/page_objects/dashboard_page.ts | 21 +++++++++----------
 test/functional/page_objects/discover_page.js |  8 +++++++
 test/functional/page_objects/tile_map_page.ts |  4 +++-
 test/functional/services/flyout.ts            | 10 ++++-----
 6 files changed, 34 insertions(+), 27 deletions(-)

diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.html b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.html
index efde83a0e35f0..3fd3c5b5b7633 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.html
+++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.html
@@ -190,6 +190,7 @@ <h1 class="euiScreenReaderOnly">{{screenTitle}}</h1>
                 data-shared-item
                 data-title="{{opts.savedSearch.lastSavedTitle}}"
                 data-description="{{opts.savedSearch.description}}"
+                data-test-subj="discoverDocTable"
                 minimum-visible-rows="minimumVisibleRows"
                 render-complete
                 on-add-column="addColumn"
diff --git a/test/functional/apps/discover/_doc_navigation.js b/test/functional/apps/discover/_doc_navigation.js
index d7fc267ab1d5e..f0a7844b29987 100644
--- a/test/functional/apps/discover/_doc_navigation.js
+++ b/test/functional/apps/discover/_doc_navigation.js
@@ -37,15 +37,14 @@ export default function({ getService, getPageObjects }) {
       await esArchiver.loadIfNeeded('logstash_functional');
       await PageObjects.common.navigateToApp('discover');
       await PageObjects.timePicker.setDefaultAbsoluteRange();
-      await Promise.all(
-        TEST_COLUMN_NAMES.map(columnName => PageObjects.discover.clickFieldListItemAdd(columnName))
-      );
-      await Promise.all(
-        TEST_FILTER_COLUMN_NAMES.map(async ([columnName, value]) => {
-          await PageObjects.discover.clickFieldListItem(columnName);
-          await PageObjects.discover.clickFieldListPlusFilter(columnName, value);
-        })
-      );
+      await PageObjects.discover.waitForDocTableLoadingComplete();
+      for (const columnName of TEST_COLUMN_NAMES) {
+        await PageObjects.discover.clickFieldListItemAdd(columnName);
+      }
+      for (const [columnName, value] of TEST_FILTER_COLUMN_NAMES) {
+        await PageObjects.discover.clickFieldListItem(columnName);
+        await PageObjects.discover.clickFieldListPlusFilter(columnName, value);
+      }
     });
 
     it('should open the doc view of the selected document', async function() {
diff --git a/test/functional/page_objects/dashboard_page.ts b/test/functional/page_objects/dashboard_page.ts
index af0a0160a81d8..3fd7ce27e27d4 100644
--- a/test/functional/page_objects/dashboard_page.ts
+++ b/test/functional/page_objects/dashboard_page.ts
@@ -190,10 +190,8 @@ export function DashboardPageProvider({ getService, getPageObjects }: FtrProvide
       await testSubjects.click('dashboardEditMode');
       // wait until the count of dashboard panels equals the count of toggle menu icons
       await retry.waitFor('in edit mode', async () => {
-        const [panels, menuIcons] = await Promise.all([
-          testSubjects.findAll('embeddablePanel'),
-          testSubjects.findAll('embeddablePanelToggleMenuIcon'),
-        ]);
+        const panels = await testSubjects.findAll('embeddablePanel', 2500);
+        const menuIcons = await testSubjects.findAll('embeddablePanelToggleMenuIcon', 2500);
         return panels.length === menuIcons.length;
       });
     }
@@ -471,15 +469,16 @@ export function DashboardPageProvider({ getService, getPageObjects }: FtrProvide
 
     public async getPanelSharedItemData() {
       log.debug('in getPanelSharedItemData');
-      const sharedItems = await find.allByCssSelector('[data-shared-item]');
-      return await Promise.all(
-        sharedItems.map(async sharedItem => {
+      const sharedItemscontainer = await find.byCssSelector('[data-shared-items-count]');
+      const $ = await sharedItemscontainer.parseDomContent();
+      return $('[data-shared-item]')
+        .toArray()
+        .map(item => {
           return {
-            title: await sharedItem.getAttribute('data-title'),
-            description: await sharedItem.getAttribute('data-description'),
+            title: $(item).attr('data-title'),
+            description: $(item).attr('data-description'),
           };
-        })
-      );
+        });
     }
 
     public async checkHideTitle() {
diff --git a/test/functional/page_objects/discover_page.js b/test/functional/page_objects/discover_page.js
index 85d8cff675f2d..5ccc5625849d2 100644
--- a/test/functional/page_objects/discover_page.js
+++ b/test/functional/page_objects/discover_page.js
@@ -327,6 +327,14 @@ export function DiscoverPageProvider({ getService, getPageObjects }) {
     async waitForChartLoadingComplete(renderCount) {
       await elasticChart.waitForRenderingCount('discoverChart', renderCount);
     }
+
+    async waitForDocTableLoadingComplete() {
+      await testSubjects.waitForAttributeToChange(
+        'discoverDocTable',
+        'data-render-complete',
+        'true'
+      );
+    }
   }
 
   return new DiscoverPage();
diff --git a/test/functional/page_objects/tile_map_page.ts b/test/functional/page_objects/tile_map_page.ts
index b41578f782af4..beb2f029c3e1c 100644
--- a/test/functional/page_objects/tile_map_page.ts
+++ b/test/functional/page_objects/tile_map_page.ts
@@ -35,7 +35,9 @@ export function TileMapPageProvider({ getService, getPageObjects }: FtrProviderC
     public async clickMapButton(zoomSelector: string, waitForLoading?: boolean) {
       await retry.try(async () => {
         const zooms = await this.getZoomSelectors(zoomSelector);
-        await Promise.all(zooms.map(async zoom => await zoom.click()));
+        for (let i = 0; i < zooms.length; i++) {
+          await zooms[i].click();
+        }
         if (waitForLoading) {
           await header.waitUntilLoadingHasFinished();
         }
diff --git a/test/functional/services/flyout.ts b/test/functional/services/flyout.ts
index 2ee028aa24d2e..02aae733362cf 100644
--- a/test/functional/services/flyout.ts
+++ b/test/functional/services/flyout.ts
@@ -50,12 +50,10 @@ export function FlyoutProvider({ getService }: FtrProviderContext) {
         return;
       }
 
-      await Promise.all(
-        flyoutElements.map(async flyoutElement => {
-          const closeBtn = await flyoutElement.findByCssSelector('[aria-label*="Close"]');
-          await closeBtn.click();
-        })
-      );
+      for (let i = 0; i < flyoutElements.length; i++) {
+        const closeBtn = await flyoutElements[i].findByCssSelector('[aria-label*="Close"]');
+        await closeBtn.click();
+      }
 
       await retry.waitFor(
         'all flyouts to be closed',

From cf743f321649d20a7096220e361e811348801234 Mon Sep 17 00:00:00 2001
From: Alison Goryachev <alison.goryachev@elastic.co>
Date: Wed, 19 Feb 2020 08:10:18 -0500
Subject: [PATCH 062/174] [Remote clusters] Fix regression with
 xpack.remote_clusters.ui.enabled setting (#57895)

---
 .../plugins/remote_clusters/public/index.ts   |  4 +-
 .../plugins/remote_clusters/public/plugin.ts  | 72 +++++++++++--------
 .../plugins/remote_clusters/public/types.ts   |  6 ++
 .../plugins/remote_clusters/server/config.ts  |  5 +-
 4 files changed, 56 insertions(+), 31 deletions(-)

diff --git a/x-pack/plugins/remote_clusters/public/index.ts b/x-pack/plugins/remote_clusters/public/index.ts
index dbe22b71b48df..6ba021b157c3e 100644
--- a/x-pack/plugins/remote_clusters/public/index.ts
+++ b/x-pack/plugins/remote_clusters/public/index.ts
@@ -3,6 +3,8 @@
  * or more contributor license agreements. Licensed under the Elastic License;
  * you may not use this file except in compliance with the Elastic License.
  */
+import { PluginInitializerContext } from 'kibana/public';
 import { RemoteClustersUIPlugin } from './plugin';
 
-export const plugin = () => new RemoteClustersUIPlugin();
+export const plugin = (initializerContext: PluginInitializerContext) =>
+  new RemoteClustersUIPlugin(initializerContext);
diff --git a/x-pack/plugins/remote_clusters/public/plugin.ts b/x-pack/plugins/remote_clusters/public/plugin.ts
index 5b84fa1fde369..0fc70cdb60f95 100644
--- a/x-pack/plugins/remote_clusters/public/plugin.ts
+++ b/x-pack/plugins/remote_clusters/public/plugin.ts
@@ -5,50 +5,64 @@
  */
 
 import { i18n } from '@kbn/i18n';
-import { CoreSetup, Plugin, CoreStart } from 'kibana/public';
+import { CoreSetup, Plugin, CoreStart, PluginInitializerContext } from 'kibana/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';
 import { init as initUiMetric } from './application/services/ui_metric';
 import { init as initNotification } from './application/services/notification';
 import { init as initRedirect } from './application/services/redirect';
-import { Dependencies } from './types';
+import { Dependencies, ClientConfigType } from './types';
 
 export class RemoteClustersUIPlugin implements Plugin<void, void, Dependencies, any> {
+  constructor(private readonly initializerContext: PluginInitializerContext) {}
+
   setup(
     { notifications: { toasts }, http, getStartServices }: CoreSetup,
     { management, usageCollection }: Dependencies
   ) {
-    const esSection = management.sections.getSection('elasticsearch');
-
-    esSection!.registerApp({
-      id: 'remote_clusters',
-      title: i18n.translate('xpack.remoteClusters.appTitle', {
-        defaultMessage: 'Remote Clusters',
-      }),
-      mount: async ({ element, setBreadcrumbs }) => {
-        const [core] = await getStartServices();
-        const {
-          i18n: { Context: i18nContext },
-          docLinks,
-          fatalErrors,
-        } = core;
-
-        // Initialize services
-        initBreadcrumbs(setBreadcrumbs);
-        initDocumentation(docLinks);
-        initUiMetric(usageCollection);
-        initNotification(toasts, fatalErrors);
-        initHttp(http);
-
-        const { renderApp } = await import('./application');
-        return renderApp(element, i18nContext);
-      },
-    });
+    const {
+      ui: { enabled: isRemoteClustersUiEnabled },
+    } = this.initializerContext.config.get<ClientConfigType>();
+
+    if (isRemoteClustersUiEnabled) {
+      const esSection = management.sections.getSection('elasticsearch');
+
+      esSection!.registerApp({
+        id: 'remote_clusters',
+        title: i18n.translate('xpack.remoteClusters.appTitle', {
+          defaultMessage: 'Remote Clusters',
+        }),
+        mount: async ({ element, setBreadcrumbs }) => {
+          const [core] = await getStartServices();
+          const {
+            i18n: { Context: i18nContext },
+            docLinks,
+            fatalErrors,
+          } = core;
+
+          // Initialize services
+          initBreadcrumbs(setBreadcrumbs);
+          initDocumentation(docLinks);
+          initUiMetric(usageCollection);
+          initNotification(toasts, fatalErrors);
+          initHttp(http);
+
+          const { renderApp } = await import('./application');
+          return renderApp(element, i18nContext);
+        },
+      });
+    }
   }
 
   start({ application }: CoreStart) {
-    initRedirect(application.navigateToApp);
+    const {
+      ui: { enabled: isRemoteClustersUiEnabled },
+    } = this.initializerContext.config.get<ClientConfigType>();
+
+    if (isRemoteClustersUiEnabled) {
+      initRedirect(application.navigateToApp);
+    }
   }
 
   stop() {}
diff --git a/x-pack/plugins/remote_clusters/public/types.ts b/x-pack/plugins/remote_clusters/public/types.ts
index 45ae90f91587a..cee65f40c44ee 100644
--- a/x-pack/plugins/remote_clusters/public/types.ts
+++ b/x-pack/plugins/remote_clusters/public/types.ts
@@ -14,6 +14,12 @@ export interface Dependencies {
   usageCollection: UsageCollectionSetup;
 }
 
+export interface ClientConfigType {
+  ui: {
+    enabled: boolean;
+  };
+}
+
 export { RegisterManagementAppArgs };
 
 export { I18nStart };
diff --git a/x-pack/plugins/remote_clusters/server/config.ts b/x-pack/plugins/remote_clusters/server/config.ts
index 9525fe1e2a0db..e329789cb6b60 100644
--- a/x-pack/plugins/remote_clusters/server/config.ts
+++ b/x-pack/plugins/remote_clusters/server/config.ts
@@ -9,6 +9,9 @@ import { PluginConfigDescriptor } from 'kibana/server';
 
 export const configSchema = schema.object({
   enabled: schema.boolean({ defaultValue: true }),
+  ui: schema.object({
+    enabled: schema.boolean({ defaultValue: true }),
+  }),
 });
 
 export type ConfigType = TypeOf<typeof configSchema>;
@@ -16,6 +19,6 @@ export type ConfigType = TypeOf<typeof configSchema>;
 export const config: PluginConfigDescriptor<ConfigType> = {
   schema: configSchema,
   exposeToBrowser: {
-    enabled: true,
+    ui: true,
   },
 };

From 4fa59429b293dda0e4bb135f34e711f732e096bc Mon Sep 17 00:00:00 2001
From: Dima Arnautov <dmitrii.arnautov@elastic.co>
Date: Wed, 19 Feb 2020 14:40:18 +0100
Subject: [PATCH 063/174] [ML] New Platform server shim: update system routes
 (#57835)

* [ML] NP system routes

* [ML] apidoc.json

* [ML] address PR comments

* [ML] fix apidoc methods, passing es_search endpoint payload

* [ML] add dummy body validation for es_search, fix ignoreSpaces query param

* [ML] _has_privileges validate body
---
 .../lib/check_privileges/check_privileges.ts  |   8 +-
 .../ml/server/lib/check_privileges/upgrade.ts |   6 +-
 .../plugins/ml/server/routes/apidoc.json      |   8 +-
 .../legacy/plugins/ml/server/routes/system.js | 205 ---------------
 .../legacy/plugins/ml/server/routes/system.ts | 247 ++++++++++++++++++
 5 files changed, 261 insertions(+), 213 deletions(-)
 delete mode 100644 x-pack/legacy/plugins/ml/server/routes/system.js
 create mode 100644 x-pack/legacy/plugins/ml/server/routes/system.ts

diff --git a/x-pack/legacy/plugins/ml/server/lib/check_privileges/check_privileges.ts b/x-pack/legacy/plugins/ml/server/lib/check_privileges/check_privileges.ts
index 6b426169799a7..617778afbe121 100644
--- a/x-pack/legacy/plugins/ml/server/lib/check_privileges/check_privileges.ts
+++ b/x-pack/legacy/plugins/ml/server/lib/check_privileges/check_privileges.ts
@@ -4,9 +4,9 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+import { IScopedClusterClient } from 'kibana/server';
 import { Privileges, getDefaultPrivileges } from '../../../common/types/privileges';
 import { XPackMainPlugin } from '../../../../xpack_main/server/xpack_main';
-import { callWithRequestType } from '../../../common/types/kibana';
 import { isSecurityDisabled } from '../../lib/security_utils';
 import { upgradeCheckProvider } from './upgrade';
 import { checkLicense } from '../check_license';
@@ -24,12 +24,12 @@ interface Response {
 }
 
 export function privilegesProvider(
-  callWithRequest: callWithRequestType,
+  callAsCurrentUser: IScopedClusterClient['callAsCurrentUser'],
   xpackMainPlugin: XPackMainPlugin,
   isMlEnabledInSpace: () => Promise<boolean>,
   ignoreSpaces: boolean = false
 ) {
-  const { isUpgradeInProgress } = upgradeCheckProvider(callWithRequest);
+  const { isUpgradeInProgress } = upgradeCheckProvider(callAsCurrentUser);
   async function getPrivileges(): Promise<Response> {
     // get the default privileges, forced to be false.
     const privileges = getDefaultPrivileges();
@@ -74,7 +74,7 @@ export function privilegesProvider(
     } else {
       // security enabled
       // load all ml privileges for this user.
-      const { cluster } = await callWithRequest('ml.privilegeCheck', { body: mlPrivileges });
+      const { cluster } = await callAsCurrentUser('ml.privilegeCheck', { body: mlPrivileges });
       setGettingPrivileges(cluster, privileges);
       if (upgradeInProgress === false) {
         // if an upgrade is in progress, don't apply the "setting"
diff --git a/x-pack/legacy/plugins/ml/server/lib/check_privileges/upgrade.ts b/x-pack/legacy/plugins/ml/server/lib/check_privileges/upgrade.ts
index 9e62780c51b3e..a1d66f00f26e1 100644
--- a/x-pack/legacy/plugins/ml/server/lib/check_privileges/upgrade.ts
+++ b/x-pack/legacy/plugins/ml/server/lib/check_privileges/upgrade.ts
@@ -4,14 +4,14 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+import { IScopedClusterClient } from 'kibana/server';
 import { mlLog } from '../../client/log';
-import { callWithRequestType } from '../../../common/types/kibana';
 
-export function upgradeCheckProvider(callWithRequest: callWithRequestType) {
+export function upgradeCheckProvider(callAsCurrentUser: IScopedClusterClient['callAsCurrentUser']) {
   async function isUpgradeInProgress(): Promise<boolean> {
     let upgradeInProgress = false;
     try {
-      const info = await callWithRequest('ml.info');
+      const info = await callAsCurrentUser('ml.info');
       // if ml indices are currently being migrated, upgrade_mode will be set to true
       // pass this back with the privileges to allow for the disabling of UI controls.
       upgradeInProgress = info.upgrade_mode === true;
diff --git a/x-pack/legacy/plugins/ml/server/routes/apidoc.json b/x-pack/legacy/plugins/ml/server/routes/apidoc.json
index 35215f8008ec3..d975ac6472535 100644
--- a/x-pack/legacy/plugins/ml/server/routes/apidoc.json
+++ b/x-pack/legacy/plugins/ml/server/routes/apidoc.json
@@ -78,6 +78,12 @@
     "DeleteFilter",
     "GetFiltersStats",
     "Indices",
-    "FieldCaps"
+    "FieldCaps",
+    "SystemRoutes",
+    "HasPrivileges",
+    "MlCapabilities",
+    "MlNodeCount",
+    "MlInfo",
+    "MlEsSearch"
   ]
 }
diff --git a/x-pack/legacy/plugins/ml/server/routes/system.js b/x-pack/legacy/plugins/ml/server/routes/system.js
deleted file mode 100644
index fd4f3f9b61917..0000000000000
--- a/x-pack/legacy/plugins/ml/server/routes/system.js
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { callWithRequestFactory } from '../client/call_with_request_factory';
-import { callWithInternalUserFactory } from '../client/call_with_internal_user_factory';
-import { privilegesProvider } from '../lib/check_privileges';
-import { spacesUtilsProvider } from '../lib/spaces_utils';
-
-import { mlLog } from '../client/log';
-
-import { wrapError } from '../client/errors';
-import Boom from 'boom';
-
-import { isSecurityDisabled } from '../lib/security_utils';
-
-export function systemRoutes({
-  commonRouteConfig,
-  elasticsearchPlugin,
-  route,
-  xpackMainPlugin,
-  spacesPlugin,
-  cloud,
-}) {
-  const callWithInternalUser = callWithInternalUserFactory(elasticsearchPlugin);
-
-  function getNodeCount() {
-    const filterPath = 'nodes.*.attributes';
-    return callWithInternalUser('nodes.info', { filterPath }).then(resp => {
-      let count = 0;
-      if (typeof resp.nodes === 'object') {
-        Object.keys(resp.nodes).forEach(k => {
-          if (resp.nodes[k].attributes !== undefined) {
-            const maxOpenJobs = resp.nodes[k].attributes['ml.max_open_jobs'];
-            if (maxOpenJobs !== null && maxOpenJobs > 0) {
-              count++;
-            }
-          }
-        });
-      }
-      return { count };
-    });
-  }
-
-  route({
-    method: 'POST',
-    path: '/api/ml/_has_privileges',
-    async handler(request) {
-      const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
-      try {
-        let upgradeInProgress = false;
-        try {
-          const info = await callWithRequest('ml.info');
-          // if ml indices are currently being migrated, upgrade_mode will be set to true
-          // pass this back with the privileges to allow for the disabling of UI controls.
-          upgradeInProgress = info.upgrade_mode === true;
-        } catch (error) {
-          // if the ml.info check fails, it could be due to the user having insufficient privileges
-          // most likely they do not have the ml_user role and therefore will be blocked from using
-          // ML at all. However, we need to catch this error so the privilege check doesn't fail.
-          if (error.status === 403) {
-            mlLog.info(
-              'Unable to determine whether upgrade is being performed due to insufficient user privileges'
-            );
-          } else {
-            mlLog.warn('Unable to determine whether upgrade is being performed');
-          }
-        }
-
-        if (isSecurityDisabled(xpackMainPlugin)) {
-          // if xpack.security.enabled has been explicitly set to false
-          // return that security is disabled and don't call the privilegeCheck endpoint
-          return {
-            securityDisabled: true,
-            upgradeInProgress,
-          };
-        } else {
-          const body = request.payload;
-          const resp = await callWithRequest('ml.privilegeCheck', { body });
-          resp.upgradeInProgress = upgradeInProgress;
-          return resp;
-        }
-      } catch (error) {
-        return wrapError(error);
-      }
-    },
-    config: {
-      ...commonRouteConfig,
-    },
-  });
-
-  route({
-    method: 'GET',
-    path: '/api/ml/ml_capabilities',
-    async handler(request) {
-      const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
-      try {
-        const ignoreSpaces = request.query && request.query.ignoreSpaces === 'true';
-        // if spaces is disabled force isMlEnabledInSpace to be true
-        const { isMlEnabledInSpace } =
-          spacesPlugin !== undefined
-            ? spacesUtilsProvider(spacesPlugin, request)
-            : { isMlEnabledInSpace: async () => true };
-
-        const { getPrivileges } = privilegesProvider(
-          callWithRequest,
-          xpackMainPlugin,
-          isMlEnabledInSpace,
-          ignoreSpaces
-        );
-        return await getPrivileges();
-      } catch (error) {
-        return wrapError(error);
-      }
-    },
-    config: {
-      ...commonRouteConfig,
-    },
-  });
-
-  route({
-    method: 'GET',
-    path: '/api/ml/ml_node_count',
-    handler(request) {
-      const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
-      return new Promise((resolve, reject) => {
-        // check for basic license first for consistency with other
-        // security disabled checks
-        if (isSecurityDisabled(xpackMainPlugin)) {
-          getNodeCount()
-            .then(resolve)
-            .catch(reject);
-        } else {
-          // if security is enabled, check that the user has permission to
-          // view jobs before calling getNodeCount.
-          // getNodeCount calls the _nodes endpoint as the internal user
-          // and so could give the user access to more information than
-          // they are entitled to.
-          const body = {
-            cluster: [
-              'cluster:monitor/xpack/ml/job/get',
-              'cluster:monitor/xpack/ml/job/stats/get',
-              'cluster:monitor/xpack/ml/datafeeds/get',
-              'cluster:monitor/xpack/ml/datafeeds/stats/get',
-            ],
-          };
-          callWithRequest('ml.privilegeCheck', { body })
-            .then(resp => {
-              if (
-                resp.cluster['cluster:monitor/xpack/ml/job/get'] &&
-                resp.cluster['cluster:monitor/xpack/ml/job/stats/get'] &&
-                resp.cluster['cluster:monitor/xpack/ml/datafeeds/get'] &&
-                resp.cluster['cluster:monitor/xpack/ml/datafeeds/stats/get']
-              ) {
-                getNodeCount()
-                  .then(resolve)
-                  .catch(reject);
-              } else {
-                // if the user doesn't have permission to create jobs
-                // return a 403
-                reject(Boom.forbidden());
-              }
-            })
-            .catch(reject);
-        }
-      }).catch(error => wrapError(error));
-    },
-    config: {
-      ...commonRouteConfig,
-    },
-  });
-
-  route({
-    method: 'GET',
-    path: '/api/ml/info',
-    async handler(request) {
-      const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
-
-      try {
-        const info = await callWithRequest('ml.info');
-        const cloudId = cloud && cloud.cloudId;
-        return { ...info, cloudId };
-      } catch (error) {
-        return wrapError(error);
-      }
-    },
-    config: {
-      ...commonRouteConfig,
-    },
-  });
-
-  route({
-    method: 'POST',
-    path: '/api/ml/es_search',
-    handler(request) {
-      const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
-      return callWithRequest('search', request.payload).catch(resp => wrapError(resp));
-    },
-    config: {
-      ...commonRouteConfig,
-    },
-  });
-}
diff --git a/x-pack/legacy/plugins/ml/server/routes/system.ts b/x-pack/legacy/plugins/ml/server/routes/system.ts
new file mode 100644
index 0000000000000..5861b53d74875
--- /dev/null
+++ b/x-pack/legacy/plugins/ml/server/routes/system.ts
@@ -0,0 +1,247 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { schema } from '@kbn/config-schema';
+
+import { Request } from 'hapi';
+import { RequestHandlerContext } from 'kibana/server';
+import { wrapError } from '../client/error_wrapper';
+import { mlLog } from '../client/log';
+import { privilegesProvider } from '../lib/check_privileges';
+import { isSecurityDisabled } from '../lib/security_utils';
+import { spacesUtilsProvider } from '../lib/spaces_utils';
+import { licensePreRoutingFactory } from '../new_platform/licence_check_pre_routing_factory';
+import { RouteInitialization } from '../new_platform/plugin';
+
+/**
+ * System routes
+ */
+export function systemRoutes({
+  router,
+  xpackMainPlugin,
+  spacesPlugin,
+  cloud,
+}: RouteInitialization) {
+  async function getNodeCount(context: RequestHandlerContext) {
+    const filterPath = 'nodes.*.attributes';
+    const resp = await context.ml!.mlClient.callAsInternalUser('nodes.info', {
+      filterPath,
+    });
+
+    let count = 0;
+    if (typeof resp.nodes === 'object') {
+      Object.keys(resp.nodes).forEach(k => {
+        if (resp.nodes[k].attributes !== undefined) {
+          const maxOpenJobs = resp.nodes[k].attributes['ml.max_open_jobs'];
+          if (maxOpenJobs !== null && maxOpenJobs > 0) {
+            count++;
+          }
+        }
+      });
+    }
+    return { count };
+  }
+
+  /**
+   * @apiGroup SystemRoutes
+   *
+   * @api {post} /api/ml/_has_privileges Check privileges
+   * @apiName HasPrivileges
+   * @apiDescription Checks if the user has required privileges
+   */
+  router.post(
+    {
+      path: '/api/ml/_has_privileges',
+      validate: {
+        body: schema.maybe(schema.any()),
+      },
+    },
+    licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
+      try {
+        let upgradeInProgress = false;
+        try {
+          const info = await context.ml!.mlClient.callAsCurrentUser('ml.info');
+          // if ml indices are currently being migrated, upgrade_mode will be set to true
+          // pass this back with the privileges to allow for the disabling of UI controls.
+          upgradeInProgress = info.upgrade_mode === true;
+        } catch (error) {
+          // if the ml.info check fails, it could be due to the user having insufficient privileges
+          // most likely they do not have the ml_user role and therefore will be blocked from using
+          // ML at all. However, we need to catch this error so the privilege check doesn't fail.
+          if (error.status === 403) {
+            mlLog.info(
+              'Unable to determine whether upgrade is being performed due to insufficient user privileges'
+            );
+          } else {
+            mlLog.warn('Unable to determine whether upgrade is being performed');
+          }
+        }
+
+        if (isSecurityDisabled(xpackMainPlugin)) {
+          // if xpack.security.enabled has been explicitly set to false
+          // return that security is disabled and don't call the privilegeCheck endpoint
+          return response.ok({
+            body: {
+              securityDisabled: true,
+              upgradeInProgress,
+            },
+          });
+        } else {
+          const body = request.body;
+          const resp = await context.ml!.mlClient.callAsCurrentUser('ml.privilegeCheck', { body });
+          resp.upgradeInProgress = upgradeInProgress;
+          return response.ok({
+            body: resp,
+          });
+        }
+      } catch (error) {
+        return response.customError(wrapError(error));
+      }
+    })
+  );
+
+  /**
+   * @apiGroup SystemRoutes
+   *
+   * @api {get} /api/ml/ml_capabilities Check ML capabilities
+   * @apiName MlCapabilities
+   * @apiDescription Checks ML capabilities
+   */
+  router.get(
+    {
+      path: '/api/ml/ml_capabilities',
+      validate: {
+        query: schema.object({
+          ignoreSpaces: schema.maybe(schema.string()),
+        }),
+      },
+    },
+    licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
+      try {
+        const ignoreSpaces = request.query && request.query.ignoreSpaces === 'true';
+        // if spaces is disabled force isMlEnabledInSpace to be true
+        const { isMlEnabledInSpace } =
+          spacesPlugin !== undefined
+            ? spacesUtilsProvider(spacesPlugin, (request as unknown) as Request)
+            : { isMlEnabledInSpace: async () => true };
+
+        const { getPrivileges } = privilegesProvider(
+          context.ml!.mlClient.callAsCurrentUser,
+          xpackMainPlugin,
+          isMlEnabledInSpace,
+          ignoreSpaces
+        );
+        return response.ok({
+          body: await getPrivileges(),
+        });
+      } catch (error) {
+        return response.customError(wrapError(error));
+      }
+    })
+  );
+
+  /**
+   * @apiGroup SystemRoutes
+   *
+   * @api {get} /api/ml/ml_node_count Get the amount of ML nodes
+   * @apiName MlNodeCount
+   * @apiDescription Returns the amount of ML nodes.
+   */
+  router.get(
+    {
+      path: '/api/ml/ml_node_count',
+      validate: false,
+    },
+    licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
+      try {
+        // check for basic license first for consistency with other
+        // security disabled checks
+        if (isSecurityDisabled(xpackMainPlugin)) {
+          return response.ok({
+            body: await getNodeCount(context),
+          });
+        } else {
+          // if security is enabled, check that the user has permission to
+          // view jobs before calling getNodeCount.
+          // getNodeCount calls the _nodes endpoint as the internal user
+          // and so could give the user access to more information than
+          // they are entitled to.
+          const requiredPrivileges = [
+            'cluster:monitor/xpack/ml/job/get',
+            'cluster:monitor/xpack/ml/job/stats/get',
+            'cluster:monitor/xpack/ml/datafeeds/get',
+            'cluster:monitor/xpack/ml/datafeeds/stats/get',
+          ];
+          const body = { cluster: requiredPrivileges };
+          const resp = await context.ml!.mlClient.callAsCurrentUser('ml.privilegeCheck', { body });
+
+          if (resp.has_all_requested) {
+            return response.ok({
+              body: await getNodeCount(context),
+            });
+          } else {
+            // if the user doesn't have permission to create jobs
+            // return a 403
+            return response.forbidden();
+          }
+        }
+      } catch (e) {
+        return response.customError(wrapError(e));
+      }
+    })
+  );
+
+  /**
+   * @apiGroup SystemRoutes
+   *
+   * @api {get} /api/ml/info Get ML info
+   * @apiName MlInfo
+   * @apiDescription Returns defaults and limits used by machine learning.
+   */
+  router.get(
+    {
+      path: '/api/ml/info',
+      validate: false,
+    },
+    licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
+      try {
+        const info = await context.ml!.mlClient.callAsCurrentUser('ml.info');
+        const cloudId = cloud && cloud.cloudId;
+        return response.ok({
+          body: { ...info, cloudId },
+        });
+      } catch (error) {
+        return response.customError(wrapError(error));
+      }
+    })
+  );
+
+  /**
+   * @apiGroup SystemRoutes
+   *
+   * @apiDeprecated
+   *
+   * @api {post} /api/ml/es_search ES Search wrapper
+   * @apiName MlEsSearch
+   */
+  router.post(
+    {
+      path: '/api/ml/es_search',
+      validate: {
+        body: schema.maybe(schema.any()),
+      },
+    },
+    licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
+      try {
+        return response.ok({
+          body: await context.ml!.mlClient.callAsCurrentUser('search', request.body),
+        });
+      } catch (error) {
+        return response.customError(wrapError(error));
+      }
+    })
+  );
+}

From 6678436cca47ae94fdbc9f467a50a3049c6ca50a Mon Sep 17 00:00:00 2001
From: Vadim Dalecky <streamich@users.noreply.github.com>
Date: Wed, 19 Feb 2020 15:22:19 +0100
Subject: [PATCH 064/174] Expressions server-side (#57537)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* refactor: 💡 improve setup and documentations of expr service

* feat: 🎸 expose ExpressionsService on server-side

* feat: 🎸 freeze expression plugin contracts

* test: 💍 add basic integration test to server-side expr plugin

* feat: 🎸 export static exports from expressions server-side

* fix: 🐛 fix Jest mocks

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../common/service/expressions_services.ts    | 175 ++++++++++++------
 src/plugins/expressions/public/mocks.tsx      |   1 +
 src/plugins/expressions/public/plugin.ts      |  14 +-
 src/plugins/expressions/server/index.ts       |  82 ++++++++
 src/plugins/expressions/server/mocks.ts       |  23 ++-
 src/plugins/expressions/server/plugin.test.ts |  47 +++++
 src/plugins/expressions/server/plugin.ts      |  22 ++-
 7 files changed, 288 insertions(+), 76 deletions(-)
 create mode 100644 src/plugins/expressions/server/plugin.test.ts

diff --git a/src/plugins/expressions/common/service/expressions_services.ts b/src/plugins/expressions/common/service/expressions_services.ts
index 94019aa62841e..f1053c7bb8411 100644
--- a/src/plugins/expressions/common/service/expressions_services.ts
+++ b/src/plugins/expressions/common/service/expressions_services.ts
@@ -22,8 +22,41 @@ import { ExpressionRendererRegistry } from '../expression_renderers';
 import { ExpressionAstExpression } from '../ast';
 import { ExecutionContract } from '../execution/execution_contract';
 
-export type ExpressionsServiceSetup = ReturnType<ExpressionsService['setup']>;
-export type ExpressionsServiceStart = ReturnType<ExpressionsService['start']>;
+/**
+ * The public contract that `ExpressionsService` provides to other plugins
+ * in Kibana Platform in *setup* life-cycle.
+ */
+export type ExpressionsServiceSetup = Pick<
+  ExpressionsService,
+  | 'getFunction'
+  | 'getFunctions'
+  | 'getRenderer'
+  | 'getRenderers'
+  | 'getType'
+  | 'getTypes'
+  | 'registerFunction'
+  | 'registerRenderer'
+  | 'registerType'
+  | 'run'
+  | 'fork'
+>;
+
+/**
+ * The public contract that `ExpressionsService` provides to other plugins
+ * in Kibana Platform in *start* life-cycle.
+ */
+export type ExpressionsServiceStart = Pick<
+  ExpressionsService,
+  | 'getFunction'
+  | 'getFunctions'
+  | 'getRenderer'
+  | 'getRenderers'
+  | 'getType'
+  | 'getTypes'
+  | 'run'
+  | 'execute'
+  | 'fork'
+>;
 
 export interface ExpressionServiceParams {
   executor?: Executor;
@@ -97,6 +130,14 @@ export class ExpressionsService {
     ...args: Parameters<Executor['registerFunction']>
   ): ReturnType<Executor['registerFunction']> => this.executor.registerFunction(...args);
 
+  public readonly registerType = (
+    ...args: Parameters<Executor['registerType']>
+  ): ReturnType<Executor['registerType']> => this.executor.registerType(...args);
+
+  public readonly registerRenderer = (
+    ...args: Parameters<ExpressionRendererRegistry['register']>
+  ): ReturnType<ExpressionRendererRegistry['register']> => this.renderers.register(...args);
+
   /**
    * Executes expression string or a parsed expression AST and immediately
    * returns the result.
@@ -132,21 +173,51 @@ export class ExpressionsService {
   ): Promise<Output> => this.executor.run<Input, Output, ExtraContext>(ast, input, context);
 
   /**
-   * Create a new instance of `ExpressionsService`. The new instance inherits
-   * all state of the original `ExpressionsService`, including all expression
-   * types, expression functions and context. Also, all new types and functions
-   * registered in the original services AFTER the forking event will be
-   * available in the forked instance. However, all new types and functions
-   * registered in the forked instances will NOT be available to the original
-   * service.
+   * Get a registered `ExpressionFunction` by its name, which was registered
+   * using the `registerFunction` method. The returned `ExpressionFunction`
+   * instance is an internal representation of the function in Expressions
+   * service - do not mutate that object.
    */
-  public readonly fork = (): ExpressionsService => {
-    const executor = this.executor.fork();
-    const renderers = this.renderers;
-    const fork = new ExpressionsService({ executor, renderers });
+  public readonly getFunction = (name: string): ReturnType<Executor['getFunction']> =>
+    this.executor.getFunction(name);
 
-    return fork;
-  };
+  /**
+   * Returns POJO map of all registered expression functions, where keys are
+   * names of the functions and values are `ExpressionFunction` instances.
+   */
+  public readonly getFunctions = (): ReturnType<Executor['getFunctions']> =>
+    this.executor.getFunctions();
+
+  /**
+   * Get a registered `ExpressionRenderer` by its name, which was registered
+   * using the `registerRenderer` method. The returned `ExpressionRenderer`
+   * instance is an internal representation of the renderer in Expressions
+   * service - do not mutate that object.
+   */
+  public readonly getRenderer = (name: string): ReturnType<ExpressionRendererRegistry['get']> =>
+    this.renderers.get(name);
+
+  /**
+   * Returns POJO map of all registered expression renderers, where keys are
+   * names of the renderers and values are `ExpressionRenderer` instances.
+   */
+  public readonly getRenderers = (): ReturnType<ExpressionRendererRegistry['toJS']> =>
+    this.renderers.toJS();
+
+  /**
+   * Get a registered `ExpressionType` by its name, which was registered
+   * using the `registerType` method. The returned `ExpressionType`
+   * instance is an internal representation of the type in Expressions
+   * service - do not mutate that object.
+   */
+  public readonly getType = (name: string): ReturnType<Executor['getType']> =>
+    this.executor.getType(name);
+
+  /**
+   * Returns POJO map of all registered expression types, where keys are
+   * names of the types and values are `ExpressionType` instances.
+   */
+  public readonly getTypes = (): ReturnType<Executor['getTypes']> => this.executor.getTypes();
 
   /**
    * Starts expression execution and immediately returns `ExecutionContract`
@@ -168,53 +239,37 @@ export class ExpressionsService {
     return execution.contract;
   };
 
-  public setup() {
-    const { executor, renderers, registerFunction, run, fork } = this;
-
-    const getFunction = executor.getFunction.bind(executor);
-    const getFunctions = executor.getFunctions.bind(executor);
-    const getRenderer = renderers.get.bind(renderers);
-    const getRenderers = renderers.toJS.bind(renderers);
-    const getType = executor.getType.bind(executor);
-    const getTypes = executor.getTypes.bind(executor);
-    const registerRenderer = renderers.register.bind(renderers);
-    const registerType = executor.registerType.bind(executor);
-
-    return {
-      fork,
-      getFunction,
-      getFunctions,
-      getRenderer,
-      getRenderers,
-      getType,
-      getTypes,
-      registerFunction,
-      registerRenderer,
-      registerType,
-      run,
-    };
+  /**
+   * Create a new instance of `ExpressionsService`. The new instance inherits
+   * all state of the original `ExpressionsService`, including all expression
+   * types, expression functions and context. Also, all new types and functions
+   * registered in the original services AFTER the forking event will be
+   * available in the forked instance. However, all new types and functions
+   * registered in the forked instances will NOT be available to the original
+   * service.
+   */
+  public readonly fork = (): ExpressionsService => {
+    const executor = this.executor.fork();
+    const renderers = this.renderers;
+    const fork = new ExpressionsService({ executor, renderers });
+
+    return fork;
+  };
+
+  /**
+   * Returns Kibana Platform *setup* life-cycle contract. Useful to return the
+   * same contract on server-side and browser-side.
+   */
+  public setup(): ExpressionsServiceSetup {
+    return this;
   }
 
-  public start() {
-    const { execute, executor, renderers, run } = this;
-
-    const getFunction = executor.getFunction.bind(executor);
-    const getFunctions = executor.getFunctions.bind(executor);
-    const getRenderer = renderers.get.bind(renderers);
-    const getRenderers = renderers.toJS.bind(renderers);
-    const getType = executor.getType.bind(executor);
-    const getTypes = executor.getTypes.bind(executor);
-
-    return {
-      execute,
-      getFunction,
-      getFunctions,
-      getRenderer,
-      getRenderers,
-      getType,
-      getTypes,
-      run,
-    };
+  /**
+   * Returns Kibana Platform *start* life-cycle contract. Useful to return the
+   * same contract on server-side and browser-side.
+   */
+  public start(): ExpressionsServiceStart {
+    return this;
   }
 
   public stop() {}
diff --git a/src/plugins/expressions/public/mocks.tsx b/src/plugins/expressions/public/mocks.tsx
index eabc4034e7430..cb7089f814643 100644
--- a/src/plugins/expressions/public/mocks.tsx
+++ b/src/plugins/expressions/public/mocks.tsx
@@ -68,6 +68,7 @@ const createStartContract = (): Start => {
     execute: jest.fn(),
     ExpressionLoader: jest.fn(),
     ExpressionRenderHandler: jest.fn(),
+    fork: jest.fn(),
     getFunction: jest.fn(),
     getFunctions: jest.fn(),
     getRenderer: jest.fn(),
diff --git a/src/plugins/expressions/public/plugin.ts b/src/plugins/expressions/public/plugin.ts
index 4aa8003617652..7c0de271b7706 100644
--- a/src/plugins/expressions/public/plugin.ts
+++ b/src/plugins/expressions/public/plugin.ts
@@ -177,7 +177,7 @@ export class ExpressionsPublicPlugin
       },
     };
 
-    return setup;
+    return Object.freeze(setup);
   }
 
   public start(core: CoreStart, { inspector, bfetch }: ExpressionsStartDeps): ExpressionsStart {
@@ -186,17 +186,19 @@ export class ExpressionsPublicPlugin
     setNotifications(core.notifications);
 
     const { expressions } = this;
-    const expressionsStart = expressions.start();
-
-    return {
-      ...expressionsStart,
+    const start = {
+      ...expressions.start(),
       ExpressionLoader,
       ExpressionRenderHandler,
       loader,
       ReactExpressionRenderer,
       render,
     };
+
+    return Object.freeze(start);
   }
 
-  public stop() {}
+  public stop() {
+    this.expressions.stop();
+  }
 }
diff --git a/src/plugins/expressions/server/index.ts b/src/plugins/expressions/server/index.ts
index 6718602ccdef5..7894f55fad4f0 100644
--- a/src/plugins/expressions/server/index.ts
+++ b/src/plugins/expressions/server/index.ts
@@ -22,6 +22,88 @@ import { ExpressionsServerPlugin } from './plugin';
 
 export { ExpressionsServerSetup, ExpressionsServerStart } from './plugin';
 
+// Kibana Platform.
+export { ExpressionsServerPlugin as Plugin };
+export * from './plugin';
 export function plugin(initializerContext: PluginInitializerContext) {
   return new ExpressionsServerPlugin(initializerContext);
 }
+
+// Static exports.
+export {
+  AnyExpressionFunctionDefinition,
+  AnyExpressionTypeDefinition,
+  ArgumentType,
+  Datatable,
+  DatatableColumn,
+  DatatableColumnType,
+  DatatableRow,
+  Execution,
+  ExecutionContainer,
+  ExecutionContext,
+  ExecutionParams,
+  ExecutionState,
+  Executor,
+  ExecutorContainer,
+  ExecutorState,
+  ExpressionAstArgument,
+  ExpressionAstExpression,
+  ExpressionAstFunction,
+  ExpressionAstNode,
+  ExpressionFunction,
+  ExpressionFunctionDefinition,
+  ExpressionFunctionKibana,
+  ExpressionFunctionParameter,
+  ExpressionImage,
+  ExpressionRenderDefinition,
+  ExpressionRenderer,
+  ExpressionRendererRegistry,
+  ExpressionType,
+  ExpressionTypeDefinition,
+  ExpressionTypeStyle,
+  ExpressionValue,
+  ExpressionValueBoxed,
+  ExpressionValueConverter,
+  ExpressionValueError,
+  ExpressionValueNum,
+  ExpressionValueRender,
+  ExpressionValueSearchContext,
+  ExpressionValueUnboxed,
+  Filter,
+  Font,
+  FontLabel,
+  FontStyle,
+  FontValue,
+  FontWeight,
+  format,
+  formatExpression,
+  FunctionsRegistry,
+  IInterpreterRenderHandlers,
+  InterpreterErrorType,
+  IRegistry,
+  KIBANA_CONTEXT_NAME,
+  KibanaContext,
+  KibanaDatatable,
+  KibanaDatatableColumn,
+  KibanaDatatableRow,
+  KnownTypeToString,
+  Overflow,
+  parse,
+  parseExpression,
+  PointSeries,
+  PointSeriesColumn,
+  PointSeriesColumnName,
+  PointSeriesColumns,
+  PointSeriesRow,
+  Range,
+  SerializedDatatable,
+  SerializedFieldFormat,
+  Style,
+  TextAlignment,
+  TextDecoration,
+  TypesRegistry,
+  TypeString,
+  TypeToString,
+  UnmappedTypeStrings,
+  ExpressionValueRender as Render,
+} from '../common';
diff --git a/src/plugins/expressions/server/mocks.ts b/src/plugins/expressions/server/mocks.ts
index 4510ae6dc0b4a..1ace19a1848b0 100644
--- a/src/plugins/expressions/server/mocks.ts
+++ b/src/plugins/expressions/server/mocks.ts
@@ -30,6 +30,17 @@ export type Start = jest.Mocked<ExpressionsServerStart>;
 
 const createSetupContract = (): Setup => {
   const setupContract: Setup = {
+    fork: jest.fn(),
+    getFunction: jest.fn(),
+    getFunctions: jest.fn(),
+    getRenderer: jest.fn(),
+    getRenderers: jest.fn(),
+    getType: jest.fn(),
+    getTypes: jest.fn(),
+    registerFunction: jest.fn(),
+    registerRenderer: jest.fn(),
+    registerType: jest.fn(),
+    run: jest.fn(),
     __LEGACY: {
       register: jest.fn(),
       registries: jest.fn(),
@@ -39,7 +50,17 @@ const createSetupContract = (): Setup => {
 };
 
 const createStartContract = (): Start => {
-  const startContract: Start = {};
+  const startContract: Start = {
+    execute: jest.fn(),
+    fork: jest.fn(),
+    getFunction: jest.fn(),
+    getFunctions: jest.fn(),
+    getRenderer: jest.fn(),
+    getRenderers: jest.fn(),
+    getType: jest.fn(),
+    getTypes: jest.fn(),
+    run: jest.fn(),
+  };
 
   return startContract;
 };
diff --git a/src/plugins/expressions/server/plugin.test.ts b/src/plugins/expressions/server/plugin.test.ts
new file mode 100644
index 0000000000000..4c7377c294483
--- /dev/null
+++ b/src/plugins/expressions/server/plugin.test.ts
@@ -0,0 +1,47 @@
+/*
+ * 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 { expressionsPluginMock } from './mocks';
+import { add } from '../common/test_helpers/expression_functions/add';
+
+describe('ExpressionsServerPlugin', () => {
+  test('can instantiate from mocks', async () => {
+    const { setup } = await expressionsPluginMock.createPlugin();
+    expect(typeof setup.registerFunction).toBe('function');
+  });
+
+  describe('setup contract', () => {
+    describe('.registerFunction()', () => {
+      test('can register a function', async () => {
+        const { setup } = await expressionsPluginMock.createPlugin();
+        expect(setup.getFunctions().add).toBe(undefined);
+        setup.registerFunction(add);
+        expect(setup.getFunctions().add.name).toBe('add');
+      });
+    });
+
+    describe('.run()', () => {
+      test('can execute simple expression', async () => {
+        const { setup } = await expressionsPluginMock.createPlugin();
+        const bar = await setup.run('var_set name="foo" value="bar" | var name="foo"', null);
+        expect(bar).toBe('bar');
+      });
+    });
+  });
+});
diff --git a/src/plugins/expressions/server/plugin.ts b/src/plugins/expressions/server/plugin.ts
index 49229b6868062..b99958262c542 100644
--- a/src/plugins/expressions/server/plugin.ts
+++ b/src/plugins/expressions/server/plugin.ts
@@ -24,24 +24,21 @@ import {
   createLegacyServerInterpreterApi,
   createLegacyServerEndpoints,
 } from './legacy';
-import { ExpressionsService } from '../common';
+import { ExpressionsService, ExpressionsServiceSetup, ExpressionsServiceStart } from '../common';
 
-// eslint-disable-next-line
 export interface ExpressionsServerSetupDependencies {
   bfetch: BfetchServerSetup;
 }
 
-// eslint-disable-next-line
 export interface ExpressionsServerStartDependencies {
   bfetch: BfetchServerStart;
 }
 
-export interface ExpressionsServerSetup {
+export interface ExpressionsServerSetup extends ExpressionsServiceSetup {
   __LEGACY: LegacyInterpreterServerApi;
 }
 
-// eslint-disable-next-line
-export interface ExpressionsServerStart {}
+export type ExpressionsServerStart = ExpressionsServiceStart;
 
 export class ExpressionsServerPlugin
   implements
@@ -70,17 +67,24 @@ export class ExpressionsServerPlugin
     const legacyApi = createLegacyServerInterpreterApi();
     createLegacyServerEndpoints(legacyApi, logger, core, plugins);
 
-    return {
+    const setup = {
+      ...this.expressions.setup(),
       __LEGACY: legacyApi,
     };
+
+    return Object.freeze(setup);
   }
 
   public start(
     core: CoreStart,
     plugins: ExpressionsServerStartDependencies
   ): ExpressionsServerStart {
-    return {};
+    const start = this.expressions.start();
+
+    return Object.freeze(start);
   }
 
-  public stop() {}
+  public stop() {
+    this.expressions.stop();
+  }
 }

From d7c36d04f2faa690197f6fa3b3e12e586c6f49c5 Mon Sep 17 00:00:00 2001
From: spalger <spalger@users.noreply.github.com>
Date: Wed, 19 Feb 2020 07:35:17 -0700
Subject: [PATCH 065/174] skip flaky suite (#44631)

---
 .../apps/dashboard/feature_controls/dashboard_security.ts      | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts
index 98035d2558b09..bfffefaecd94c 100644
--- a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts
+++ b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts
@@ -22,7 +22,8 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
   const queryBar = getService('queryBar');
   const savedQueryManagementComponent = getService('savedQueryManagementComponent');
 
-  describe('dashboard security', () => {
+  // FLAKY: https://github.com/elastic/kibana/issues/44631
+  describe.skip('dashboard security', () => {
     before(async () => {
       await esArchiver.load('dashboard/feature_controls/security');
       await esArchiver.loadIfNeeded('logstash_functional');

From c91e4a77c1958bdf8287c728347efdf787a7b203 Mon Sep 17 00:00:00 2001
From: spalger <spalger@users.noreply.github.com>
Date: Wed, 19 Feb 2020 07:38:21 -0700
Subject: [PATCH 066/174] skip flaky suite (#57946)

---
 x-pack/test/functional/apps/endpoint/policy_list.ts | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/x-pack/test/functional/apps/endpoint/policy_list.ts b/x-pack/test/functional/apps/endpoint/policy_list.ts
index 1fe2492bed5a0..658e4dcd13e1e 100644
--- a/x-pack/test/functional/apps/endpoint/policy_list.ts
+++ b/x-pack/test/functional/apps/endpoint/policy_list.ts
@@ -10,7 +10,8 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
   const pageObjects = getPageObjects(['common', 'endpoint']);
   const testSubjects = getService('testSubjects');
 
-  describe('Endpoint Policy List', function() {
+  // FLAKY: https://github.com/elastic/kibana/issues/57946
+  describe.skip('Endpoint Policy List', function() {
     this.tags(['ciGroup7']);
     before(async () => {
       await pageObjects.common.navigateToUrlWithBrowserHistory('endpoint', '/policy');

From b7cc61f8b7b7cae28977aaeaa118c209debb40a2 Mon Sep 17 00:00:00 2001
From: Vadim Dalecky <streamich@users.noreply.github.com>
Date: Wed, 19 Feb 2020 15:42:05 +0100
Subject: [PATCH 067/174] =?UTF-8?q?test:=20=F0=9F=92=8D=20make=20tests=20l?=
 =?UTF-8?q?ess=20flaky=20(#57991)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

✅ Closes: #57823
---
 src/plugins/expressions/common/execution/execution.test.ts | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/plugins/expressions/common/execution/execution.test.ts b/src/plugins/expressions/common/execution/execution.test.ts
index eeb1ed80e8d0d..b6c1721e33eef 100644
--- a/src/plugins/expressions/common/execution/execution.test.ts
+++ b/src/plugins/expressions/common/execution/execution.test.ts
@@ -336,16 +336,18 @@ describe('Execution', () => {
     });
 
     test('execution state is "pending" while execution is in progress', async () => {
+      jest.useFakeTimers();
       const execution = createExecution('sleep 20');
       execution.start(null);
-      await new Promise(r => setTimeout(r, 5));
+      jest.advanceTimersByTime(5);
       expect(execution.state.get().state).toBe('pending');
+      jest.useRealTimers();
     });
 
     test('execution state is "result" when execution successfully completes', async () => {
       const execution = createExecution('sleep 1');
       execution.start(null);
-      await new Promise(r => setTimeout(r, 30));
+      await execution.result;
       expect(execution.state.get().state).toBe('result');
     });
 

From ca3f27aca9c3449b1a942af4950e3b9219319fbf Mon Sep 17 00:00:00 2001
From: Yara Tercero <yctercero@users.noreply.github.com>
Date: Wed, 19 Feb 2020 09:42:50 -0500
Subject: [PATCH 068/174] [SIEM] [Detections Engine] Import rules unit tests
 (#57466)

* Added unit tests for detection engine import_rules_route and moved out small portion of import_rules_route into a util to be unit tested as well.

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../routes/__mocks__/request_responses.ts     |  19 +
 .../routes/__mocks__/utils.ts                 |  53 ++
 .../routes/rules/import_rules_route.test.ts   | 457 ++++++++++++++++++
 .../routes/rules/import_rules_route.ts        |  30 +-
 .../routes/rules/utils.test.ts                | 101 +++-
 .../detection_engine/routes/rules/utils.ts    |  43 +-
 .../security_and_spaces/tests/import_rules.ts |  12 +-
 7 files changed, 689 insertions(+), 26 deletions(-)
 create mode 100644 x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/utils.ts
 create mode 100644 x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.test.ts

diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts
index f380b82c1e05f..1578c71dddc6a 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts
@@ -20,6 +20,7 @@ import {
 import { ShardsResponse } from '../../../types';
 import { RuleAlertType, IRuleSavedAttributesSavedObjectAttributes } from '../../rules/types';
 import { RuleAlertParamsRest, PrepackagedRules } from '../../types';
+import { TEST_BOUNDARY } from './utils';
 
 export const mockPrepackagedRule = (): PrepackagedRules => ({
   rule_id: 'rule-1',
@@ -224,6 +225,24 @@ export const getFindResultWithMultiHits = ({
   };
 };
 
+export const getImportRulesRequest = (payload?: Buffer): ServerInjectOptions => ({
+  method: 'POST',
+  url: `${DETECTION_ENGINE_RULES_URL}/_import`,
+  headers: {
+    'Content-Type': `multipart/form-data; boundary=${TEST_BOUNDARY}`,
+  },
+  payload,
+});
+
+export const getImportRulesRequestOverwriteTrue = (payload?: Buffer): ServerInjectOptions => ({
+  method: 'POST',
+  url: `${DETECTION_ENGINE_RULES_URL}/_import?overwrite=true`,
+  headers: {
+    'Content-Type': `multipart/form-data; boundary=${TEST_BOUNDARY}`,
+  },
+  payload,
+});
+
 export const getDeleteRequest = (): ServerInjectOptions => ({
   method: 'DELETE',
   url: `${DETECTION_ENGINE_RULES_URL}?rule_id=rule-1`,
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/utils.ts
new file mode 100644
index 0000000000000..f8c8e1f231ffa
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/utils.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.
+ */
+
+import { OutputRuleAlertRest } from '../../types';
+
+export const TEST_BOUNDARY = 'test_multipart_boundary';
+
+// Not parsable due to extra colon following `name` property - name::
+export const UNPARSABLE_LINE =
+  '{"name"::"Simple Rule Query","description":"Simple Rule Query","risk_score":1,"rule_id":"rule-1","severity":"high","type":"query","query":"user.name: root or user.name: admin"}';
+
+/**
+ * This is a typical simple rule for testing that is easy for most basic testing
+ * @param ruleId
+ */
+export const getSimpleRule = (ruleId = 'rule-1'): Partial<OutputRuleAlertRest> => ({
+  name: 'Simple Rule Query',
+  description: 'Simple Rule Query',
+  risk_score: 1,
+  rule_id: ruleId,
+  severity: 'high',
+  type: 'query',
+  query: 'user.name: root or user.name: admin',
+});
+
+/**
+ * Given an array of rule_id strings this will return a ndjson buffer which is useful
+ * for testing uploads.
+ * @param ruleIds Array of strings of rule_ids
+ * @param isNdjson Boolean to determine file extension
+ */
+export const getSimpleRuleAsMultipartContent = (ruleIds: string[], isNdjson = true): Buffer => {
+  const arrayOfRules = ruleIds.map(ruleId => {
+    const simpleRule = getSimpleRule(ruleId);
+    return JSON.stringify(simpleRule);
+  });
+  const stringOfRules = arrayOfRules.join('\r\n');
+
+  const resultingPayload =
+    `--${TEST_BOUNDARY}\r\n` +
+    `Content-Disposition: form-data; name="file"; filename="rules.${
+      isNdjson ? 'ndjson' : 'json'
+    }\r\n` +
+    'Content-Type: application/octet-stream\r\n' +
+    '\r\n' +
+    `${stringOfRules}\r\n` +
+    `--${TEST_BOUNDARY}--\r\n`;
+
+  return Buffer.from(resultingPayload);
+};
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.test.ts
new file mode 100644
index 0000000000000..e3283a750869c
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.test.ts
@@ -0,0 +1,457 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { omit } from 'lodash/fp';
+
+import {
+  getSimpleRuleAsMultipartContent,
+  TEST_BOUNDARY,
+  UNPARSABLE_LINE,
+  getSimpleRule,
+} from '../__mocks__/utils';
+import { ImportSuccessError } from '../utils';
+import {
+  getImportRulesRequest,
+  getImportRulesRequestOverwriteTrue,
+  getFindResult,
+  getResult,
+  getEmptyIndex,
+  getFindResultWithSingleHit,
+  getNonEmptyIndex,
+} from '../__mocks__/request_responses';
+import { createMockServer, createMockConfig, clientsServiceMock } from '../__mocks__';
+import { importRulesRoute } from './import_rules_route';
+import { DEFAULT_SIGNALS_INDEX } from '../../../../../common/constants';
+
+describe('import_rules_route', () => {
+  let server = createMockServer();
+  let config = createMockConfig();
+  let getClients = clientsServiceMock.createGetScoped();
+  let clients = clientsServiceMock.createClients();
+
+  beforeEach(() => {
+    jest.resetAllMocks();
+
+    server = createMockServer();
+    config = createMockConfig();
+    config = () => ({
+      get: jest.fn(value => {
+        switch (value) {
+          case 'savedObjects.maxImportPayloadBytes': {
+            return 10000;
+          }
+          case 'savedObjects.maxImportExportSize': {
+            return 10000;
+          }
+          case 'xpack.siem.signalsIndex': {
+            return DEFAULT_SIGNALS_INDEX;
+          }
+          default: {
+            const dummyMock = jest.fn();
+            return dummyMock();
+          }
+        }
+      }),
+      has: jest.fn(),
+    });
+    getClients = clientsServiceMock.createGetScoped();
+    clients = clientsServiceMock.createClients();
+
+    getClients.mockResolvedValue(clients);
+    clients.clusterClient.callAsCurrentUser.mockResolvedValue(getNonEmptyIndex());
+    clients.spacesClient.getSpaceId.mockReturnValue('default');
+
+    importRulesRoute(server.route, config, getClients);
+  });
+
+  describe('status codes with actionsClient and alertClient', () => {
+    test('returns 200 when importing a single rule with a valid actionClient and alertClient', async () => {
+      clients.alertsClient.find.mockResolvedValue(getFindResult());
+      clients.alertsClient.get.mockResolvedValue(getResult());
+      clients.alertsClient.create.mockResolvedValue(getResult());
+      const requestPayload = getSimpleRuleAsMultipartContent(['rule-1']);
+      const { statusCode } = await server.inject(getImportRulesRequest(requestPayload));
+      expect(statusCode).toEqual(200);
+    });
+
+    test('returns 404 if alertClient is not available on the route', async () => {
+      getClients.mockResolvedValue(omit('alertsClient', clients));
+      const { route, inject } = createMockServer();
+      importRulesRoute(route, config, getClients);
+      const requestPayload = getSimpleRuleAsMultipartContent(['rule-1']);
+      const { statusCode } = await inject(getImportRulesRequest(requestPayload));
+      expect(statusCode).toEqual(404);
+    });
+
+    test('returns 404 if actionsClient is not available on the route', async () => {
+      getClients.mockResolvedValue(omit('actionsClient', clients));
+      const { route, inject } = createMockServer();
+      importRulesRoute(route, config, getClients);
+      const requestPayload = getSimpleRuleAsMultipartContent(['rule-1']);
+      const { statusCode } = await inject(getImportRulesRequest(requestPayload));
+      expect(statusCode).toEqual(404);
+    });
+  });
+
+  describe('validation', () => {
+    test('returns reported error if index does not exist', async () => {
+      clients.clusterClient.callAsCurrentUser.mockResolvedValue(getEmptyIndex());
+      clients.alertsClient.find.mockResolvedValue(getFindResult());
+      clients.alertsClient.get.mockResolvedValue(getResult());
+      clients.alertsClient.create.mockResolvedValue(getResult());
+      const requestPayload = getSimpleRuleAsMultipartContent(['rule-1']);
+      const { statusCode, payload } = await server.inject(getImportRulesRequest(requestPayload));
+      const parsed: ImportSuccessError = JSON.parse(payload);
+
+      expect(parsed).toEqual({
+        errors: [
+          {
+            error: {
+              message:
+                'To create a rule, the index must exist first. Index .siem-signals-default does not exist',
+              status_code: 409,
+            },
+            rule_id: 'rule-1',
+          },
+        ],
+        success: false,
+        success_count: 0,
+      });
+      expect(statusCode).toEqual(200);
+    });
+
+    test('returns 400 when a thrown error is caught', async () => {
+      const mockFn = jest.fn();
+      const mockThrowError = (): Error => {
+        throw new Error();
+      };
+      clients.clusterClient.callAsCurrentUser.mockResolvedValue(
+        mockFn.mockImplementation(mockThrowError)
+      );
+      clients.alertsClient.find.mockResolvedValue(getFindResult());
+      clients.alertsClient.get.mockResolvedValue(getResult());
+      clients.alertsClient.create.mockResolvedValue(getResult());
+      const requestPayload = getSimpleRuleAsMultipartContent(['rule-1']);
+      const { statusCode, payload } = await server.inject(getImportRulesRequest(requestPayload));
+      const parsed: ImportSuccessError = JSON.parse(payload);
+
+      expect(parsed).toEqual({
+        errors: [
+          {
+            error: {
+              message: "Cannot read property 'total' of undefined",
+              status_code: 400,
+            },
+            rule_id: 'rule-1',
+          },
+        ],
+        success: false,
+        success_count: 0,
+      });
+      expect(statusCode).toEqual(200);
+    });
+
+    test('returns 400 if file extension type is not .ndjson', async () => {
+      clients.alertsClient.find.mockResolvedValue(getFindResult());
+      clients.alertsClient.get.mockResolvedValue(getResult());
+      clients.alertsClient.create.mockResolvedValue(getResult());
+      const requestPayload = getSimpleRuleAsMultipartContent(['rule-1'], false);
+      const { statusCode, payload } = await server.inject(getImportRulesRequest(requestPayload));
+      const parsed: ImportSuccessError = JSON.parse(payload);
+
+      expect(parsed).toEqual({
+        message: 'Invalid file extension .json',
+        status_code: 400,
+      });
+      expect(statusCode).toEqual(400);
+    });
+  });
+
+  describe('single rule import', () => {
+    test('returns 200 if rule imported successfully', async () => {
+      clients.alertsClient.find.mockResolvedValue(getFindResult());
+      clients.alertsClient.get.mockResolvedValue(getResult());
+      clients.alertsClient.create.mockResolvedValue(getResult());
+      const requestPayload = getSimpleRuleAsMultipartContent(['rule-1']);
+      const { statusCode, payload } = await server.inject(getImportRulesRequest(requestPayload));
+      const parsed: ImportSuccessError = JSON.parse(payload);
+
+      expect(parsed).toEqual({
+        errors: [],
+        success: true,
+        success_count: 1,
+      });
+      expect(statusCode).toEqual(200);
+    });
+
+    test('returns reported conflict if error parsing rule', async () => {
+      const multipartPayload =
+        `--${TEST_BOUNDARY}\r\n` +
+        `Content-Disposition: form-data; name="file"; filename="rules.ndjson"\r\n` +
+        'Content-Type: application/octet-stream\r\n' +
+        '\r\n' +
+        `${UNPARSABLE_LINE}\r\n` +
+        `--${TEST_BOUNDARY}--\r\n`;
+
+      clients.alertsClient.find.mockResolvedValue(getFindResult());
+      clients.alertsClient.get.mockResolvedValue(getResult());
+      clients.alertsClient.create.mockResolvedValue(getResult());
+      const requestPayload = Buffer.from(multipartPayload);
+      const { statusCode, payload } = await server.inject(getImportRulesRequest(requestPayload));
+      const parsed: ImportSuccessError = JSON.parse(payload);
+
+      expect(parsed).toEqual({
+        errors: [
+          {
+            error: {
+              message: 'Unexpected token : in JSON at position 8',
+              status_code: 400,
+            },
+            rule_id: '(unknown)',
+          },
+        ],
+        success: false,
+        success_count: 0,
+      });
+      expect(statusCode).toEqual(200);
+    });
+
+    describe('rule with existing rule_id', () => {
+      test('returns with reported conflict if `overwrite` is set to `false`', async () => {
+        clients.alertsClient.find.mockResolvedValue(getFindResult());
+
+        const requestPayload = getSimpleRuleAsMultipartContent(['rule-1']);
+        const { statusCode, payload } = await server.inject(getImportRulesRequest(requestPayload));
+        const parsed: ImportSuccessError = JSON.parse(payload);
+
+        expect(parsed).toEqual({
+          errors: [],
+          success: true,
+          success_count: 1,
+        });
+        expect(statusCode).toEqual(200);
+
+        clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit());
+        clients.alertsClient.get.mockResolvedValue(getResult());
+
+        const { statusCode: statusCodeRequest2, payload: payloadRequest2 } = await server.inject(
+          getImportRulesRequest(requestPayload)
+        );
+        const parsedRequest2: ImportSuccessError = JSON.parse(payloadRequest2);
+
+        expect(parsedRequest2).toEqual({
+          errors: [
+            {
+              error: {
+                message: 'rule_id: "rule-1" already exists',
+                status_code: 409,
+              },
+              rule_id: 'rule-1',
+            },
+          ],
+          success: false,
+          success_count: 0,
+        });
+        expect(statusCodeRequest2).toEqual(200);
+      });
+
+      test('returns with NO reported conflict if `overwrite` is set to `true`', async () => {
+        clients.alertsClient.find.mockResolvedValue(getFindResult());
+
+        const requestPayload = getSimpleRuleAsMultipartContent(['rule-1']);
+        const { statusCode, payload } = await server.inject(getImportRulesRequest(requestPayload));
+        const parsed: ImportSuccessError = JSON.parse(payload);
+
+        expect(parsed).toEqual({
+          errors: [],
+          success: true,
+          success_count: 1,
+        });
+        expect(statusCode).toEqual(200);
+
+        clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit());
+        clients.alertsClient.get.mockResolvedValue(getResult());
+
+        const { statusCode: statusCodeRequest2, payload: payloadRequest2 } = await server.inject(
+          getImportRulesRequestOverwriteTrue(requestPayload)
+        );
+        const parsedRequest2: ImportSuccessError = JSON.parse(payloadRequest2);
+
+        expect(parsedRequest2).toEqual({
+          errors: [],
+          success: true,
+          success_count: 1,
+        });
+        expect(statusCodeRequest2).toEqual(200);
+      });
+    });
+  });
+
+  describe('multi rule import', () => {
+    test('returns 200 if all rules imported successfully', async () => {
+      clients.alertsClient.find.mockResolvedValue(getFindResult());
+
+      const requestPayload = getSimpleRuleAsMultipartContent(['rule-1', 'rule-2']);
+      const { statusCode, payload } = await server.inject(getImportRulesRequest(requestPayload));
+      const parsed: ImportSuccessError = JSON.parse(payload);
+
+      expect(parsed).toEqual({
+        errors: [],
+        success: true,
+        success_count: 2,
+      });
+      expect(statusCode).toEqual(200);
+    });
+
+    test('returns 200 with reported conflict if error parsing rule', async () => {
+      const multipartPayload =
+        `--${TEST_BOUNDARY}\r\n` +
+        `Content-Disposition: form-data; name="file"; filename="rules.ndjson"\r\n` +
+        'Content-Type: application/octet-stream\r\n' +
+        '\r\n' +
+        `${UNPARSABLE_LINE}\r\n` +
+        `${JSON.stringify(getSimpleRule('rule-2'))}\r\n` +
+        `--${TEST_BOUNDARY}--\r\n`;
+
+      clients.alertsClient.find.mockResolvedValue(getFindResult());
+
+      const requestPayload = Buffer.from(multipartPayload);
+      const { statusCode, payload } = await server.inject(getImportRulesRequest(requestPayload));
+      const parsed: ImportSuccessError = JSON.parse(payload);
+
+      expect(parsed).toEqual({
+        errors: [
+          {
+            error: {
+              message: 'Unexpected token : in JSON at position 8',
+              status_code: 400,
+            },
+            rule_id: '(unknown)',
+          },
+        ],
+        success: false,
+        success_count: 1,
+      });
+      expect(statusCode).toEqual(200);
+    });
+
+    describe('rules with matching rule_id', () => {
+      test('returns with reported conflict if `overwrite` is set to `false`', async () => {
+        clients.alertsClient.find.mockResolvedValue(getFindResult());
+        clients.alertsClient.get.mockResolvedValue(getResult());
+
+        const requestPayload = getSimpleRuleAsMultipartContent(['rule-1', 'rule-1']);
+        const { statusCode, payload } = await server.inject(getImportRulesRequest(requestPayload));
+        const parsed: ImportSuccessError = JSON.parse(payload);
+
+        expect(parsed).toEqual({
+          errors: [
+            {
+              error: {
+                message: 'More than one rule with rule-id: "rule-1" found',
+                status_code: 400,
+              },
+              rule_id: 'rule-1',
+            },
+          ],
+          success: false,
+          success_count: 1,
+        });
+        expect(statusCode).toEqual(200);
+      });
+
+      test('returns with NO reported conflict if `overwrite` is set to `true`', async () => {
+        clients.alertsClient.find.mockResolvedValue(getFindResult());
+        clients.alertsClient.get.mockResolvedValue(getResult());
+
+        const requestPayload = getSimpleRuleAsMultipartContent(['rule-1', 'rule-1']);
+        const { statusCode, payload } = await server.inject(
+          getImportRulesRequestOverwriteTrue(requestPayload)
+        );
+        const parsed: ImportSuccessError = JSON.parse(payload);
+
+        expect(parsed).toEqual({
+          errors: [],
+          success: true,
+          success_count: 1,
+        });
+        expect(statusCode).toEqual(200);
+      });
+    });
+
+    describe('rules with existing rule_id', () => {
+      test('returns with reported conflict if `overwrite` is set to `false`', async () => {
+        clients.alertsClient.find.mockResolvedValue(getFindResult());
+
+        const requestPayload = getSimpleRuleAsMultipartContent(['rule-1']);
+        const { statusCode, payload } = await server.inject(getImportRulesRequest(requestPayload));
+        const parsedResult: ImportSuccessError = JSON.parse(payload);
+
+        expect(parsedResult).toEqual({
+          errors: [],
+          success: true,
+          success_count: 1,
+        });
+        expect(statusCode).toEqual(200);
+
+        clients.alertsClient.find.mockResolvedValueOnce(getFindResultWithSingleHit());
+        clients.alertsClient.get.mockResolvedValue(getResult());
+
+        const requestPayload2 = getSimpleRuleAsMultipartContent(['rule-1', 'rule-2', 'rule-3']);
+        const { statusCode: statusCodeRequest2, payload: payloadRequest2 } = await server.inject(
+          getImportRulesRequest(requestPayload2)
+        );
+        const parsed: ImportSuccessError = JSON.parse(payloadRequest2);
+
+        expect(parsed).toEqual({
+          errors: [
+            {
+              error: {
+                message: 'rule_id: "rule-1" already exists',
+                status_code: 409,
+              },
+              rule_id: 'rule-1',
+            },
+          ],
+          success: false,
+          success_count: 2,
+        });
+        expect(statusCodeRequest2).toEqual(200);
+      });
+
+      test('returns 200 with NO reported conflict if `overwrite` is set to `true`', async () => {
+        clients.alertsClient.find.mockResolvedValue(getFindResult());
+
+        const requestPayload = getSimpleRuleAsMultipartContent(['rule-1']);
+        const { statusCode, payload } = await server.inject(getImportRulesRequest(requestPayload));
+        const parsedResult: ImportSuccessError = JSON.parse(payload);
+
+        expect(parsedResult).toEqual({
+          errors: [],
+          success: true,
+          success_count: 1,
+        });
+        expect(statusCode).toEqual(200);
+
+        clients.alertsClient.find.mockResolvedValueOnce(getFindResultWithSingleHit());
+        clients.alertsClient.get.mockResolvedValue(getResult());
+
+        const requestPayload2 = getSimpleRuleAsMultipartContent(['rule-1', 'rule-2', 'rule-3']);
+        const { statusCode: statusCodeRequest2, payload: payloadRequest2 } = await server.inject(
+          getImportRulesRequestOverwriteTrue(requestPayload2)
+        );
+        const parsed: ImportSuccessError = JSON.parse(payloadRequest2);
+
+        expect(parsed).toEqual({
+          errors: [],
+          success: true,
+          success_count: 3,
+        });
+        expect(statusCodeRequest2).toEqual(200);
+      });
+    });
+  });
+});
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts
index a9188cc2adc12..cdb09ff7b04ed 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts
@@ -7,7 +7,6 @@
 import Hapi from 'hapi';
 import { chunk, isEmpty } from 'lodash/fp';
 import { extname } from 'path';
-import uuid from 'uuid';
 
 import { createPromiseFromStreams } from '../../../../../../../../../src/legacy/utils/streams';
 import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants';
@@ -21,6 +20,7 @@ import { createRulesStreamFromNdJson } from '../../rules/create_rules_stream_fro
 import { ImportRuleAlertRest } from '../../types';
 import { patchRules } from '../../rules/patch_rules';
 import { importRulesQuerySchema, importRulesPayloadSchema } from '../schemas/import_rules_schema';
+import { getTupleDuplicateErrorsAndUniqueRules } from './utils';
 import { GetScopedClients } from '../../../../services';
 
 type PromiseFromStreams = ImportRuleAlertRest | Error;
@@ -76,25 +76,9 @@ export const createImportRulesRoute = (
       const objectLimit = config().get<number>('savedObjects.maxImportExportSize');
       const readStream = createRulesStreamFromNdJson(request.payload.file, objectLimit);
       const parsedObjects = await createPromiseFromStreams<PromiseFromStreams[]>([readStream]);
-      const uniqueParsedObjects = Array.from(
-        parsedObjects
-          .reduce(
-            (acc, parsedRule) => {
-              if (parsedRule instanceof Error) {
-                acc.set(uuid.v4(), parsedRule);
-              } else {
-                const { rule_id: ruleId } = parsedRule;
-                if (ruleId != null) {
-                  acc.set(ruleId, parsedRule);
-                } else {
-                  acc.set(uuid.v4(), parsedRule);
-                }
-              }
-              return acc;
-            }, // using map (preserves ordering)
-            new Map()
-          )
-          .values()
+      const [duplicateIdErrors, uniqueParsedObjects] = getTupleDuplicateErrorsAndUniqueRules(
+        parsedObjects,
+        request.query.overwrite
       );
 
       const chunkParseObjects = chunk(CHUNK_PARSED_OBJECT_SIZE, uniqueParsedObjects);
@@ -251,7 +235,11 @@ export const createImportRulesRoute = (
             return [...accum, importsWorkerPromise];
           }, [])
         );
-        importRuleResponse = [...importRuleResponse, ...newImportRuleResponse];
+        importRuleResponse = [
+          ...duplicateIdErrors,
+          ...importRuleResponse,
+          ...newImportRuleResponse,
+        ];
       }
 
       const errorsResp = importRuleResponse.filter(resp => !isEmpty(resp.error));
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts
index fb3262c476b40..2b0da8251b387 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts
@@ -3,7 +3,7 @@
  * or more contributor license agreements. Licensed under the Elastic License;
  * you may not use this file except in compliance with the Elastic License.
  */
-
+import { Readable } from 'stream';
 import {
   transformAlertToRule,
   getIdError,
@@ -16,12 +16,18 @@ import {
   transformAlertsToRules,
   transformOrImportError,
   getDuplicates,
+  getTupleDuplicateErrorsAndUniqueRules,
 } from './utils';
 import { getResult } from '../__mocks__/request_responses';
 import { INTERNAL_IDENTIFIER } from '../../../../../common/constants';
-import { OutputRuleAlertRest } from '../../types';
+import { OutputRuleAlertRest, ImportRuleAlertRest } from '../../types';
 import { BulkError, ImportSuccessError } from '../utils';
 import { sampleRule } from '../../signals/__mocks__/es_results';
+import { getSimpleRule } from '../__mocks__/utils';
+import { createRulesStreamFromNdJson } from '../../rules/create_rules_stream_from_ndjson';
+import { createPromiseFromStreams } from '../../../../../../../../../src/legacy/utils/streams';
+
+type PromiseFromStreams = ImportRuleAlertRest | Error;
 
 describe('utils', () => {
   describe('transformAlertToRule', () => {
@@ -1224,4 +1230,95 @@ describe('utils', () => {
       expect(output).toEqual(expected);
     });
   });
+
+  describe('getTupleDuplicateErrorsAndUniqueRules', () => {
+    test('returns tuple of empty duplicate errors array and rule array with instance of Syntax Error when imported rule contains parse error', async () => {
+      const multipartPayload =
+        '{"name"::"Simple Rule Query","description":"Simple Rule Query","risk_score":1,"rule_id":"rule-1","severity":"high","type":"query","query":"user.name: root or user.name: admin"}\n';
+      const ndJsonStream = new Readable({
+        read() {
+          this.push(multipartPayload);
+          this.push(null);
+        },
+      });
+      const rulesObjectsStream = createRulesStreamFromNdJson(ndJsonStream, 1000);
+      const parsedObjects = await createPromiseFromStreams<PromiseFromStreams[]>([
+        rulesObjectsStream,
+      ]);
+      const [errors, output] = getTupleDuplicateErrorsAndUniqueRules(parsedObjects, false);
+      const isInstanceOfError = output[0] instanceof Error;
+
+      expect(isInstanceOfError).toEqual(true);
+      expect(errors.length).toEqual(0);
+    });
+
+    test('returns tuple of duplicate conflict error and single rule when rules with matching rule-ids passed in and `overwrite` is false', async () => {
+      const rule = getSimpleRule('rule-1');
+      const rule2 = getSimpleRule('rule-1');
+      const ndJsonStream = new Readable({
+        read() {
+          this.push(`${JSON.stringify(rule)}\n`);
+          this.push(`${JSON.stringify(rule2)}\n`);
+          this.push(null);
+        },
+      });
+      const rulesObjectsStream = createRulesStreamFromNdJson(ndJsonStream, 1000);
+      const parsedObjects = await createPromiseFromStreams<PromiseFromStreams[]>([
+        rulesObjectsStream,
+      ]);
+      const [errors, output] = getTupleDuplicateErrorsAndUniqueRules(parsedObjects, false);
+
+      expect(output.length).toEqual(1);
+      expect(errors).toEqual([
+        {
+          error: {
+            message: 'More than one rule with rule-id: "rule-1" found',
+            status_code: 400,
+          },
+          rule_id: 'rule-1',
+        },
+      ]);
+    });
+
+    test('returns tuple of empty duplicate errors array and single rule when rules with matching rule-ids passed in and `overwrite` is true', async () => {
+      const rule = getSimpleRule('rule-1');
+      const rule2 = getSimpleRule('rule-1');
+      const ndJsonStream = new Readable({
+        read() {
+          this.push(`${JSON.stringify(rule)}\n`);
+          this.push(`${JSON.stringify(rule2)}\n`);
+          this.push(null);
+        },
+      });
+      const rulesObjectsStream = createRulesStreamFromNdJson(ndJsonStream, 1000);
+      const parsedObjects = await createPromiseFromStreams<PromiseFromStreams[]>([
+        rulesObjectsStream,
+      ]);
+      const [errors, output] = getTupleDuplicateErrorsAndUniqueRules(parsedObjects, true);
+
+      expect(output.length).toEqual(1);
+      expect(errors.length).toEqual(0);
+    });
+
+    test('returns tuple of empty duplicate errors array and single rule when rules without a rule-id is passed in', async () => {
+      const simpleRule = getSimpleRule();
+      delete simpleRule.rule_id;
+      const multipartPayload = `${JSON.stringify(simpleRule)}\n`;
+      const ndJsonStream = new Readable({
+        read() {
+          this.push(multipartPayload);
+          this.push(null);
+        },
+      });
+      const rulesObjectsStream = createRulesStreamFromNdJson(ndJsonStream, 1000);
+      const parsedObjects = await createPromiseFromStreams<PromiseFromStreams[]>([
+        rulesObjectsStream,
+      ]);
+      const [errors, output] = getTupleDuplicateErrorsAndUniqueRules(parsedObjects, false);
+      const isInstanceOfError = output[0] instanceof Error;
+
+      expect(isInstanceOfError).toEqual(true);
+      expect(errors.length).toEqual(0);
+    });
+  });
 });
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts
index df9e3021e400f..21fc5a12db536 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts
@@ -7,6 +7,7 @@
 import { pickBy } from 'lodash/fp';
 import { Dictionary } from 'lodash';
 import { SavedObject } from 'kibana/server';
+import uuid from 'uuid';
 import { INTERNAL_IDENTIFIER } from '../../../../../common/constants';
 import {
   RuleAlertType,
@@ -17,7 +18,7 @@ import {
   isRuleStatusFindTypes,
   isRuleStatusSavedObjectType,
 } from '../../rules/types';
-import { OutputRuleAlertRest } from '../../types';
+import { OutputRuleAlertRest, ImportRuleAlertRest } from '../../types';
 import {
   createBulkErrorObject,
   BulkError,
@@ -27,6 +28,8 @@ import {
   OutputError,
 } from '../utils';
 
+type PromiseFromStreams = ImportRuleAlertRest | Error;
+
 export const getIdError = ({
   id,
   ruleId,
@@ -224,3 +227,41 @@ export const getDuplicates = (lodashDict: Dictionary<number>): string[] => {
   }
   return [];
 };
+
+export const getTupleDuplicateErrorsAndUniqueRules = (
+  rules: PromiseFromStreams[],
+  isOverwrite: boolean
+): [BulkError[], PromiseFromStreams[]] => {
+  const { errors, rulesAcc } = rules.reduce(
+    (acc, parsedRule) => {
+      if (parsedRule instanceof Error) {
+        acc.rulesAcc.set(uuid.v4(), parsedRule);
+      } else {
+        const { rule_id: ruleId } = parsedRule;
+        if (ruleId != null) {
+          if (acc.rulesAcc.has(ruleId) && !isOverwrite) {
+            acc.errors.set(
+              uuid.v4(),
+              createBulkErrorObject({
+                ruleId,
+                statusCode: 400,
+                message: `More than one rule with rule-id: "${ruleId}" found`,
+              })
+            );
+          }
+          acc.rulesAcc.set(ruleId, parsedRule);
+        } else {
+          acc.rulesAcc.set(uuid.v4(), parsedRule);
+        }
+      }
+
+      return acc;
+    }, // using map (preserves ordering)
+    {
+      errors: new Map<string, BulkError>(),
+      rulesAcc: new Map<string, PromiseFromStreams>(),
+    }
+  );
+
+  return [Array.from(errors.values()), Array.from(rulesAcc.values())];
+};
diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/import_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/import_rules.ts
index e8fd1e4298c22..79a1e667e5458 100644
--- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/import_rules.ts
+++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/import_rules.ts
@@ -115,8 +115,16 @@ export default ({ getService }: FtrProviderContext): void => {
           .expect(200);
 
         expect(body).to.eql({
-          errors: [], // TODO: This should have a conflict within it as an error rather than an empty array
-          success: true,
+          errors: [
+            {
+              error: {
+                message: 'More than one rule with rule-id: "rule-1" found',
+                status_code: 400,
+              },
+              rule_id: 'rule-1',
+            },
+          ],
+          success: false,
           success_count: 1,
         });
       });

From 85097d5fadd0a1be4a37704a7569841897c509c4 Mon Sep 17 00:00:00 2001
From: Gidi Meir Morris <github@gidi.io>
Date: Thu, 20 Feb 2020 04:08:01 +1300
Subject: [PATCH 069/174] adds pagination on Alert Instances list on Alert
 Details page (#57524)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* added pagination on alert instances page

* extracted default page size to a constant for alerting UI as a whole

* Fix test failure

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
Co-authored-by: Mike Côté <mikecote@users.noreply.github.com>
---
 .../public/application/constants/index.ts     |  2 +
 .../components/alert_instances.tsx            | 44 ++++++---
 .../alerts_list/components/alerts_list.tsx    |  4 +-
 .../apps/triggers_actions_ui/details.ts       | 93 ++++++++++++++++++-
 .../page_objects/alert_details.ts             |  4 +
 5 files changed, 133 insertions(+), 14 deletions(-)

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 11b094dea0e62..d469651b48b04 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
@@ -20,3 +20,5 @@ export enum SORT_ORDERS {
   ASCENDING = 'asc',
   DESCENDING = 'desc',
 }
+
+export const DEFAULT_SEARCH_PAGE_SIZE: number = 10;
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx
index 731489c61d60f..98aa981f40d11 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx
@@ -4,19 +4,20 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import React, { Fragment } from 'react';
+import React, { Fragment, useState } from 'react';
 import moment, { Duration } from 'moment';
 import { i18n } from '@kbn/i18n';
 import { EuiBasicTable, EuiButtonToggle, EuiBadge, EuiHealth } from '@elastic/eui';
 // @ts-ignore
 import { RIGHT_ALIGNMENT, CENTER_ALIGNMENT } from '@elastic/eui/lib/services';
-import { padLeft, difference } from 'lodash';
+import { padLeft, difference, chunk } from 'lodash';
 import { FormattedMessage } from '@kbn/i18n/react';
-import { Alert, AlertTaskState, RawAlertInstance } from '../../../../types';
+import { Alert, AlertTaskState, RawAlertInstance, Pagination } from '../../../../types';
 import {
   ComponentOpts as AlertApis,
   withBulkAlertOperations,
 } from '../../common/components/with_bulk_alert_api_operations';
+import { DEFAULT_SEARCH_PAGE_SIZE } from '../../../constants';
 
 type AlertInstancesProps = {
   alert: Alert;
@@ -134,22 +135,39 @@ export function AlertInstances({
   unmuteAlertInstance,
   requestRefresh,
 }: AlertInstancesProps) {
+  const [pagination, setPagination] = useState<Pagination>({
+    index: 0,
+    size: DEFAULT_SEARCH_PAGE_SIZE,
+  });
+
+  const mergedAlertInstances = [
+    ...Object.entries(alertInstances).map(([instanceId, instance]) =>
+      alertInstanceToListItem(alert, instanceId, instance)
+    ),
+    ...difference(alert.mutedInstanceIds, Object.keys(alertInstances)).map(instanceId =>
+      alertInstanceToListItem(alert, instanceId)
+    ),
+  ];
+  const pageOfAlertInstances = getPage(mergedAlertInstances, pagination);
+
   const onMuteAction = async (instance: AlertInstanceListItem) => {
     await (instance.isMuted
       ? unmuteAlertInstance(alert, instance.instance)
       : muteAlertInstance(alert, instance.instance));
     requestRefresh();
   };
+
   return (
     <EuiBasicTable
-      items={[
-        ...Object.entries(alertInstances).map(([instanceId, instance]) =>
-          alertInstanceToListItem(alert, instanceId, instance)
-        ),
-        ...difference(alert.mutedInstanceIds, Object.keys(alertInstances)).map(instanceId =>
-          alertInstanceToListItem(alert, instanceId)
-        ),
-      ]}
+      items={pageOfAlertInstances}
+      pagination={{
+        pageIndex: pagination.index,
+        pageSize: pagination.size,
+        totalItemCount: mergedAlertInstances.length,
+      }}
+      onChange={({ page: changedPage }: { page: Pagination }) => {
+        setPagination(changedPage);
+      }}
       rowProps={() => ({
         'data-test-subj': 'alert-instance-row',
       })}
@@ -163,6 +181,10 @@ export function AlertInstances({
 }
 export const AlertInstancesWithApi = withBulkAlertOperations(AlertInstances);
 
+function getPage(items: any[], pagination: Pagination) {
+  return chunk(items, pagination.size)[pagination.index] || [];
+}
+
 interface AlertInstanceListItemStatus {
   label: string;
   healthColor: string;
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx
index a89215e6c2964..d9ccb84452e47 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx
@@ -32,7 +32,7 @@ import { ActionTypeFilter } from './action_type_filter';
 import { loadAlerts, loadAlertTypes } from '../../../lib/alert_api';
 import { loadActionTypes } from '../../../lib/action_connector_api';
 import { hasDeleteAlertsCapability, hasSaveAlertsCapability } from '../../../lib/capabilities';
-import { routeToAlertDetails } from '../../../constants';
+import { routeToAlertDetails, DEFAULT_SEARCH_PAGE_SIZE } from '../../../constants';
 
 const ENTER_KEY = 13;
 
@@ -67,7 +67,7 @@ export const AlertsList: React.FunctionComponent = () => {
   const [actionTypes, setActionTypes] = useState<ActionType[]>([]);
   const [selectedIds, setSelectedIds] = useState<string[]>([]);
   const [isPerformingAction, setIsPerformingAction] = useState<boolean>(false);
-  const [page, setPage] = useState<Pagination>({ index: 0, size: 10 });
+  const [page, setPage] = useState<Pagination>({ index: 0, size: DEFAULT_SEARCH_PAGE_SIZE });
   const [searchText, setSearchText] = useState<string | undefined>();
   const [inputText, setInputText] = useState<string | undefined>();
   const [typesFilter, setTypesFilter] = useState<string[]>([]);
diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts
index 95371b5b501f5..3db4731f0adfb 100644
--- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts
+++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts
@@ -6,7 +6,7 @@
 
 import expect from '@kbn/expect';
 import uuid from 'uuid';
-import { omit, mapValues } from 'lodash';
+import { omit, mapValues, range, flatten } from 'lodash';
 import moment from 'moment';
 import { FtrProviderContext } from '../../ftr_provider_context';
 
@@ -331,5 +331,96 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
         await pageObjects.alertDetailsUI.ensureAlertInstanceExistance('eu-east', false);
       });
     });
+
+    describe('Alert Instance Pagination', function() {
+      const testRunUuid = uuid.v4();
+      let alert: any;
+
+      before(async () => {
+        await pageObjects.common.navigateToApp('triggersActions');
+
+        const actions = await Promise.all([
+          alerting.actions.createAction({
+            name: `server-log-${testRunUuid}-${0}`,
+            actionTypeId: '.server-log',
+            config: {},
+            secrets: {},
+          }),
+          alerting.actions.createAction({
+            name: `server-log-${testRunUuid}-${1}`,
+            actionTypeId: '.server-log',
+            config: {},
+            secrets: {},
+          }),
+        ]);
+
+        const instances = flatten(
+          range(10).map(index => [
+            { id: `us-central-${index}` },
+            { id: `us-east-${index}` },
+            { id: `us-west-${index}` },
+          ])
+        );
+        alert = await alerting.alerts.createAlwaysFiringWithActions(
+          `test-alert-${testRunUuid}`,
+          actions.map(action => ({
+            id: action.id,
+            group: 'default',
+            params: {
+              message: 'from alert 1s',
+              level: 'warn',
+            },
+          })),
+          {
+            instances,
+          }
+        );
+
+        // refresh to see alert
+        await browser.refresh();
+
+        await pageObjects.header.waitUntilLoadingHasFinished();
+
+        // Verify content
+        await testSubjects.existOrFail('alertsList');
+
+        // click on first alert
+        await pageObjects.triggersActionsUI.clickOnAlertInAlertsList(alert.name);
+
+        // await first run to complete so we have an initial state
+        await retry.try(async () => {
+          const { alertInstances } = await alerting.alerts.getAlertState(alert.id);
+          expect(Object.keys(alertInstances).length).to.eql(instances.length);
+        });
+      });
+
+      const PAGE_SIZE = 10;
+      it('renders the first page', async () => {
+        // Verify content
+        await testSubjects.existOrFail('alertInstancesList');
+
+        const { alertInstances } = await alerting.alerts.getAlertState(alert.id);
+
+        const items = await pageObjects.alertDetailsUI.getAlertInstancesList();
+        expect(items.length).to.eql(PAGE_SIZE);
+
+        const [firstItem] = items;
+        expect(firstItem.instance).to.eql(Object.keys(alertInstances)[0]);
+      });
+
+      it('navigates to the next page', async () => {
+        // Verify content
+        await testSubjects.existOrFail('alertInstancesList');
+
+        const { alertInstances } = await alerting.alerts.getAlertState(alert.id);
+
+        await pageObjects.alertDetailsUI.clickPaginationNextPage();
+
+        await retry.try(async () => {
+          const [firstItem] = await pageObjects.alertDetailsUI.getAlertInstancesList();
+          expect(firstItem.instance).to.eql(Object.keys(alertInstances)[PAGE_SIZE]);
+        });
+      });
+    });
   });
 };
diff --git a/x-pack/test/functional_with_es_ssl/page_objects/alert_details.ts b/x-pack/test/functional_with_es_ssl/page_objects/alert_details.ts
index fd936b3738677..900fe3237ffac 100644
--- a/x-pack/test/functional_with_es_ssl/page_objects/alert_details.ts
+++ b/x-pack/test/functional_with_es_ssl/page_objects/alert_details.ts
@@ -92,5 +92,9 @@ export function AlertDetailsPageProvider({ getService }: FtrProviderContext) {
         ).to.eql(shouldExist ? 1 : 0);
       });
     },
+    async clickPaginationNextPage() {
+      const nextButton = await testSubjects.find(`pagination-button-next`);
+      nextButton.click();
+    },
   };
 }

From 29ac6fea71ccd6ec2e5327b684979046058dbabd Mon Sep 17 00:00:00 2001
From: spalger <spalger@users.noreply.github.com>
Date: Wed, 19 Feb 2020 08:22:37 -0700
Subject: [PATCH 070/174] skip flaky suite (#57762) (#57997) (#57998)

---
 .../__jest__/client_integration/remote_clusters_edit.test.js | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_edit.test.js b/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_edit.test.js
index cab91854a5114..1136b7307176d 100644
--- a/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_edit.test.js
+++ b/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_edit.test.js
@@ -12,7 +12,10 @@ import { REMOTE_CLUSTER_EDIT, REMOTE_CLUSTER_EDIT_NAME } from './helpers/constan
 const { setup } = pageHelpers.remoteClustersEdit;
 const { setup: setupRemoteClustersAdd } = pageHelpers.remoteClustersAdd;
 
-describe('Edit Remote cluster', () => {
+// FLAKY: https://github.com/elastic/kibana/issues/57762
+// FLAKY: https://github.com/elastic/kibana/issues/57997
+// FLAKY: https://github.com/elastic/kibana/issues/57998
+describe.skip('Edit Remote cluster', () => {
   let server;
   let httpRequestsMockHelpers;
   let component;

From 2c78be3d4416cf91e90619a500726180a321a27a Mon Sep 17 00:00:00 2001
From: Joe Portner <5295965+jportner@users.noreply.github.com>
Date: Wed, 19 Feb 2020 11:16:17 -0500
Subject: [PATCH 071/174] Upgrade yargs (#57720)

---
 yarn.lock | 17 ++++-------------
 1 file changed, 4 insertions(+), 13 deletions(-)

diff --git a/yarn.lock b/yarn.lock
index 74daf198c44d5..6b3370407e3b2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -22247,15 +22247,6 @@ os-locale@^1.4.0:
   dependencies:
     lcid "^1.0.0"
 
-os-locale@^2.0.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2"
-  integrity sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==
-  dependencies:
-    execa "^0.7.0"
-    lcid "^1.0.0"
-    mem "^1.1.0"
-
 os-locale@^3.0.0:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.0.1.tgz#3b014fbf01d87f60a1e5348d80fe870dc82c4620"
@@ -32237,15 +32228,15 @@ yargs@4.8.1:
     yargs-parser "^2.4.1"
 
 yargs@^11.0.0:
-  version "11.1.0"
-  resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.0.tgz#90b869934ed6e871115ea2ff58b03f4724ed2d77"
-  integrity sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==
+  version "11.1.1"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.1.tgz#5052efe3446a4df5ed669c995886cc0f13702766"
+  integrity sha512-PRU7gJrJaXv3q3yQZ/+/X6KBswZiaQ+zOmdprZcouPYtQgvNU35i+68M4b1ZHLZtYFT5QObFLV+ZkmJYcwKdiw==
   dependencies:
     cliui "^4.0.0"
     decamelize "^1.1.1"
     find-up "^2.1.0"
     get-caller-file "^1.0.1"
-    os-locale "^2.0.0"
+    os-locale "^3.1.0"
     require-directory "^2.1.1"
     require-main-filename "^1.0.1"
     set-blocking "^2.0.0"

From 0340fac14967d1e9b072517c4c09d64476b67d89 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= <sorenlouv@gmail.com>
Date: Wed, 19 Feb 2020 17:25:55 +0100
Subject: [PATCH 072/174] =?UTF-8?q?[APM]=20Don=E2=80=99t=20include=20UI=20?=
 =?UTF-8?q?filters=20when=20fetching=20a=20specific=20transaction=20(#5793?=
 =?UTF-8?q?4)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* [APM] Don’t include UI filters when fetching transaction

When fetching an error sample, the related transaction is also fetched. This transaction should not be filtered by ui filters

* Fix snapshot

* Update term mock term used for ui filter
---
 .../plugins/apm/public/utils/testHelpers.tsx  | 21 +++----------
 .../errors/__snapshots__/queries.test.ts.snap |  6 ++--
 .../__snapshots__/queries.test.ts.snap        |  4 +--
 .../apm/server/lib/errors/get_error_group.ts  |  2 +-
 .../__snapshots__/queries.test.ts.snap        | 30 +++++++++----------
 .../__snapshots__/queries.test.ts.snap        |  6 ++--
 .../__snapshots__/queries.test.ts.snap        |  2 +-
 .../__snapshots__/queries.test.ts.snap        |  4 +--
 .../__snapshots__/queries.test.ts.snap        | 17 ++++-------
 .../lib/transactions/get_transaction/index.ts | 19 +++++++-----
 .../server/lib/transactions/queries.test.ts   |  2 +-
 .../__snapshots__/queries.test.ts.snap        |  2 +-
 12 files changed, 50 insertions(+), 65 deletions(-)

diff --git a/x-pack/legacy/plugins/apm/public/utils/testHelpers.tsx b/x-pack/legacy/plugins/apm/public/utils/testHelpers.tsx
index 3d34f016c6591..bcdeaa6d5ac23 100644
--- a/x-pack/legacy/plugins/apm/public/utils/testHelpers.tsx
+++ b/x-pack/legacy/plugins/apm/public/utils/testHelpers.tsx
@@ -151,23 +151,10 @@ export async function inspectSearchParams(
   const mockSetup = {
     start: 1528113600000,
     end: 1528977600000,
-    client: {
-      search: spy
-    } as any,
-    internalClient: {
-      search: spy
-    } as any,
-    config: new Proxy(
-      {},
-      {
-        get: () => 'myIndex'
-      }
-    ) as APMConfig,
-    uiFiltersES: [
-      {
-        term: { 'service.environment': 'prod' }
-      }
-    ],
+    client: { search: spy } as any,
+    internalClient: { search: spy } as any,
+    config: new Proxy({}, { get: () => 'myIndex' }) as APMConfig,
+    uiFiltersES: [{ term: { 'my.custom.ui.filter': 'foo-bar' } }],
     indices: {
       'apm_oss.sourcemapIndices': 'myIndex',
       'apm_oss.errorIndices': 'myIndex',
diff --git a/x-pack/legacy/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap b/x-pack/legacy/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap
index a2629366dd6d9..b9ac9d5431700 100644
--- a/x-pack/legacy/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap
+++ b/x-pack/legacy/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap
@@ -32,7 +32,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
         ],
@@ -119,7 +119,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
         ],
@@ -194,7 +194,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
         ],
diff --git a/x-pack/legacy/plugins/apm/server/lib/errors/distribution/__snapshots__/queries.test.ts.snap b/x-pack/legacy/plugins/apm/server/lib/errors/distribution/__snapshots__/queries.test.ts.snap
index 0065c28f60d2d..b71b2d697126a 100644
--- a/x-pack/legacy/plugins/apm/server/lib/errors/distribution/__snapshots__/queries.test.ts.snap
+++ b/x-pack/legacy/plugins/apm/server/lib/errors/distribution/__snapshots__/queries.test.ts.snap
@@ -40,7 +40,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
         ],
@@ -92,7 +92,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
           Object {
diff --git a/x-pack/legacy/plugins/apm/server/lib/errors/get_error_group.ts b/x-pack/legacy/plugins/apm/server/lib/errors/get_error_group.ts
index 8d19455651be3..5d45002b9f3ce 100644
--- a/x-pack/legacy/plugins/apm/server/lib/errors/get_error_group.ts
+++ b/x-pack/legacy/plugins/apm/server/lib/errors/get_error_group.ts
@@ -64,7 +64,7 @@ export async function getErrorGroup({
 
   let transaction;
   if (transactionId && traceId) {
-    transaction = await getTransaction(transactionId, traceId, setup);
+    transaction = await getTransaction({ transactionId, traceId, setup });
   }
 
   return {
diff --git a/x-pack/legacy/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap b/x-pack/legacy/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap
index 0b1fe575f7254..d8119ac96a536 100644
--- a/x-pack/legacy/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap
+++ b/x-pack/legacy/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap
@@ -87,7 +87,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
         ],
@@ -176,7 +176,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
           Object {
@@ -272,7 +272,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
           Object {
@@ -371,7 +371,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
           Object {
@@ -455,7 +455,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
           Object {
@@ -565,7 +565,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
         ],
@@ -660,7 +660,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
           Object {
@@ -762,7 +762,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
           Object {
@@ -867,7 +867,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
           Object {
@@ -957,7 +957,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
           Object {
@@ -1056,7 +1056,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
         ],
@@ -1140,7 +1140,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
           Object {
@@ -1231,7 +1231,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
           Object {
@@ -1325,7 +1325,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
           Object {
@@ -1404,7 +1404,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
           Object {
diff --git a/x-pack/legacy/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap b/x-pack/legacy/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap
index ce07759669d6b..3935ecda42db9 100644
--- a/x-pack/legacy/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap
+++ b/x-pack/legacy/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap
@@ -51,7 +51,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
         ],
@@ -120,7 +120,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
         ],
@@ -190,7 +190,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
         ],
diff --git a/x-pack/legacy/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap b/x-pack/legacy/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap
index bbf2a6882c3c7..3f8d6b22cd000 100644
--- a/x-pack/legacy/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap
+++ b/x-pack/legacy/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap
@@ -169,7 +169,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
         ],
diff --git a/x-pack/legacy/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap b/x-pack/legacy/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap
index 58fbe664cccab..1096c1638f3f2 100644
--- a/x-pack/legacy/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap
+++ b/x-pack/legacy/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap
@@ -80,7 +80,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
         ],
@@ -194,7 +194,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
         ],
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap b/x-pack/legacy/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap
index 3d485e39dbbbf..49e0e0669c241 100644
--- a/x-pack/legacy/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap
+++ b/x-pack/legacy/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap
@@ -30,11 +30,6 @@ Object {
               },
             },
           },
-          Object {
-            "term": Object {
-              "service.environment": "prod",
-            },
-          },
         ],
       },
     },
@@ -167,7 +162,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
         ],
@@ -302,7 +297,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
           Object {
@@ -399,7 +394,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
         ],
@@ -491,7 +486,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
           Object {
@@ -588,7 +583,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
           Object {
@@ -654,7 +649,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
         ],
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/get_transaction/index.ts b/x-pack/legacy/plugins/apm/server/lib/transactions/get_transaction/index.ts
index b5d7747c23a2c..e7ba453357a8a 100644
--- a/x-pack/legacy/plugins/apm/server/lib/transactions/get_transaction/index.ts
+++ b/x-pack/legacy/plugins/apm/server/lib/transactions/get_transaction/index.ts
@@ -18,12 +18,16 @@ import {
 } from '../../helpers/setup_request';
 import { ProcessorEvent } from '../../../../common/processor_event';
 
-export async function getTransaction(
-  transactionId: string,
-  traceId: string,
-  setup: Setup & SetupTimeRange & SetupUIFilters
-) {
-  const { start, end, uiFiltersES, client, indices } = setup;
+export async function getTransaction({
+  transactionId,
+  traceId,
+  setup
+}: {
+  transactionId: string;
+  traceId: string;
+  setup: Setup & SetupTimeRange & SetupUIFilters;
+}) {
+  const { start, end, client, indices } = setup;
 
   const params = {
     index: indices['apm_oss.transactionIndices'],
@@ -35,8 +39,7 @@ export async function getTransaction(
             { term: { [PROCESSOR_EVENT]: ProcessorEvent.transaction } },
             { term: { [TRANSACTION_ID]: transactionId } },
             { term: { [TRACE_ID]: traceId } },
-            { range: rangeFilter(start, end) },
-            ...uiFiltersES
+            { range: rangeFilter(start, end) }
           ]
         }
       }
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/queries.test.ts b/x-pack/legacy/plugins/apm/server/lib/transactions/queries.test.ts
index 13cb1328fdd01..a9e4204fde1ad 100644
--- a/x-pack/legacy/plugins/apm/server/lib/transactions/queries.test.ts
+++ b/x-pack/legacy/plugins/apm/server/lib/transactions/queries.test.ts
@@ -99,7 +99,7 @@ describe('transaction queries', () => {
 
   it('fetches a transaction', async () => {
     mock = await inspectSearchParams(setup =>
-      getTransaction('foo', 'bar', setup)
+      getTransaction({ transactionId: 'foo', traceId: 'bar', setup })
     );
 
     expect(mock.params).toMatchSnapshot();
diff --git a/x-pack/legacy/plugins/apm/server/lib/ui_filters/local_ui_filters/__snapshots__/queries.test.ts.snap b/x-pack/legacy/plugins/apm/server/lib/ui_filters/local_ui_filters/__snapshots__/queries.test.ts.snap
index 4b5b34e8af1d2..e6b6a9a52adfe 100644
--- a/x-pack/legacy/plugins/apm/server/lib/ui_filters/local_ui_filters/__snapshots__/queries.test.ts.snap
+++ b/x-pack/legacy/plugins/apm/server/lib/ui_filters/local_ui_filters/__snapshots__/queries.test.ts.snap
@@ -48,7 +48,7 @@ Object {
           },
           Object {
             "term": Object {
-              "service.environment": "prod",
+              "my.custom.ui.filter": "foo-bar",
             },
           },
         ],

From fbae654da626b542c70298b196e3194d3ccdd0bf Mon Sep 17 00:00:00 2001
From: Spencer <email@spalger.com>
Date: Wed, 19 Feb 2020 11:19:50 -0700
Subject: [PATCH 073/174] [kbn/optimizer] emit success event from reducer when
 all bundles cached (#57945)

* emit success event from reducer when all bundles cached

* verify that infinite streams can be broken by unsubscribing

* shift naming a smidge

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../src/common/event_stream_helpers.test.ts   | 227 +++++++++++++++---
 .../src/common/event_stream_helpers.ts        |  92 +++++--
 .../basic_optimization.test.ts                |   1 +
 .../kbn-optimizer/src/log_optimizer_state.ts  |  18 +-
 .../handle_optimizer_completion.test.ts       |   2 +-
 .../optimizer/handle_optimizer_completion.ts  |   7 +-
 packages/kbn-optimizer/src/optimizer/index.ts |   2 +-
 ...ptimizer_reducer.ts => optimizer_state.ts} |  21 +-
 packages/kbn-optimizer/src/run_optimizer.ts   |   8 +-
 9 files changed, 307 insertions(+), 71 deletions(-)
 rename packages/kbn-optimizer/src/optimizer/{optimizer_reducer.ts => optimizer_state.ts} (91%)

diff --git a/packages/kbn-optimizer/src/common/event_stream_helpers.test.ts b/packages/kbn-optimizer/src/common/event_stream_helpers.test.ts
index 60982abff2d87..f6f6841532799 100644
--- a/packages/kbn-optimizer/src/common/event_stream_helpers.test.ts
+++ b/packages/kbn-optimizer/src/common/event_stream_helpers.test.ts
@@ -18,52 +18,221 @@
  */
 
 import * as Rx from 'rxjs';
-import { toArray } from 'rxjs/operators';
-
-import { summarizeEvent$ } from './event_stream_helpers';
-
-it('emits each state with each event, ignoring events when reducer returns undefined', async () => {
-  const values = await summarizeEvent$(
-    Rx.of(1, 2, 3, 4, 5),
-    {
-      sum: 0,
-    },
-    (state, event) => {
-      if (event % 2) {
-        return {
-          sum: state.sum + event,
-        };
-      }
+import { toArray, take } from 'rxjs/operators';
+
+import { summarizeEventStream } from './event_stream_helpers';
+
+it('emits each state with each event, ignoring events when summarizer returns undefined', async () => {
+  const event$ = Rx.of(1, 2, 3, 4, 5);
+  const initial = 0;
+  const values = await summarizeEventStream(event$, initial, (state, event) => {
+    if (event % 2) {
+      return state + event;
+    }
+  })
+    .pipe(toArray())
+    .toPromise();
+
+  expect(values).toMatchInlineSnapshot(`
+    Array [
+      Object {
+        "state": 0,
+      },
+      Object {
+        "event": 1,
+        "state": 1,
+      },
+      Object {
+        "event": 3,
+        "state": 4,
+      },
+      Object {
+        "event": 5,
+        "state": 9,
+      },
+    ]
+  `);
+});
+
+it('interleaves injected events when source is synchronous', async () => {
+  const event$ = Rx.of(1, 7);
+  const initial = 0;
+  const values = await summarizeEventStream(event$, initial, (state, event, injectEvent) => {
+    if (event < 5) {
+      injectEvent(event + 2);
+    }
+
+    return state + event;
+  })
+    .pipe(toArray())
+    .toPromise();
+
+  expect(values).toMatchInlineSnapshot(`
+    Array [
+      Object {
+        "state": 0,
+      },
+      Object {
+        "event": 1,
+        "state": 1,
+      },
+      Object {
+        "event": 3,
+        "state": 4,
+      },
+      Object {
+        "event": 5,
+        "state": 9,
+      },
+      Object {
+        "event": 7,
+        "state": 16,
+      },
+    ]
+  `);
+});
+
+it('interleaves injected events when source is asynchronous', async () => {
+  const event$ = Rx.of(1, 7, Rx.asyncScheduler);
+  const initial = 0;
+  const values = await summarizeEventStream(event$, initial, (state, event, injectEvent) => {
+    if (event < 5) {
+      injectEvent(event + 2);
     }
-  )
+
+    return state + event;
+  })
     .pipe(toArray())
     .toPromise();
 
   expect(values).toMatchInlineSnapshot(`
     Array [
       Object {
-        "state": Object {
-          "sum": 0,
-        },
+        "state": 0,
       },
       Object {
         "event": 1,
-        "state": Object {
-          "sum": 1,
-        },
+        "state": 1,
       },
       Object {
         "event": 3,
-        "state": Object {
-          "sum": 4,
-        },
+        "state": 4,
       },
       Object {
         "event": 5,
-        "state": Object {
-          "sum": 9,
-        },
+        "state": 9,
+      },
+      Object {
+        "event": 7,
+        "state": 16,
       },
     ]
   `);
 });
+
+it('interleaves mulitple injected events in order', async () => {
+  const event$ = Rx.of(1);
+  const initial = 0;
+  const values = await summarizeEventStream(event$, initial, (state, event, injectEvent) => {
+    if (event < 10) {
+      injectEvent(10);
+      injectEvent(20);
+      injectEvent(30);
+    }
+
+    return state + event;
+  })
+    .pipe(toArray())
+    .toPromise();
+
+  expect(values).toMatchInlineSnapshot(`
+    Array [
+      Object {
+        "state": 0,
+      },
+      Object {
+        "event": 1,
+        "state": 1,
+      },
+      Object {
+        "event": 10,
+        "state": 11,
+      },
+      Object {
+        "event": 20,
+        "state": 31,
+      },
+      Object {
+        "event": 30,
+        "state": 61,
+      },
+    ]
+  `);
+});
+
+it('stops an infinite stream when unsubscribed', async () => {
+  const event$ = Rx.of(1);
+  const initial = 0;
+  const summarize = jest.fn((prev, event, injectEvent) => {
+    // always inject a follow up event, making this infinite and synchronous
+    injectEvent(event + 1);
+    return prev + event;
+  });
+
+  const values = await summarizeEventStream(event$, initial, summarize)
+    .pipe(take(11), toArray())
+    .toPromise();
+
+  expect(values).toMatchInlineSnapshot(`
+    Array [
+      Object {
+        "state": 0,
+      },
+      Object {
+        "event": 1,
+        "state": 1,
+      },
+      Object {
+        "event": 2,
+        "state": 3,
+      },
+      Object {
+        "event": 3,
+        "state": 6,
+      },
+      Object {
+        "event": 4,
+        "state": 10,
+      },
+      Object {
+        "event": 5,
+        "state": 15,
+      },
+      Object {
+        "event": 6,
+        "state": 21,
+      },
+      Object {
+        "event": 7,
+        "state": 28,
+      },
+      Object {
+        "event": 8,
+        "state": 36,
+      },
+      Object {
+        "event": 9,
+        "state": 45,
+      },
+      Object {
+        "event": 10,
+        "state": 55,
+      },
+    ]
+  `);
+
+  // ensure summarizer still only called 10 times after a timeout
+  expect(summarize).toHaveBeenCalledTimes(10);
+  await new Promise(resolve => setTimeout(resolve, 1000));
+  expect(summarize).toHaveBeenCalledTimes(10);
+});
diff --git a/packages/kbn-optimizer/src/common/event_stream_helpers.ts b/packages/kbn-optimizer/src/common/event_stream_helpers.ts
index c1585f79ede6e..d07af32f88897 100644
--- a/packages/kbn-optimizer/src/common/event_stream_helpers.ts
+++ b/packages/kbn-optimizer/src/common/event_stream_helpers.ts
@@ -18,39 +18,91 @@
  */
 
 import * as Rx from 'rxjs';
-import { scan, distinctUntilChanged, startWith } from 'rxjs/operators';
 
 export interface Update<Event, State> {
   event?: Event;
   state: State;
 }
 
-export type Summarizer<Event, State> = (prev: State, event: Event) => State | undefined;
+export type EventInjector<Event> = (event: Event) => void;
+export type Summarizer<Event, State> = (
+  prev: State,
+  event: Event,
+  injectEvent: EventInjector<Event>
+) => State | undefined;
 
 /**
  * Transform an event stream into a state update stream which emits
  * the events and individual states for each event.
  */
-export const summarizeEvent$ = <Event, State>(
+export const summarizeEventStream = <Event, State>(
   event$: Rx.Observable<Event>,
   initialState: State,
-  reducer: Summarizer<Event, State>
+  summarize: Summarizer<Event, State>
 ) => {
-  const initUpdate: Update<Event, State> = {
-    state: initialState,
-  };
-
-  return event$.pipe(
-    scan((prev, event): Update<Event, State> => {
-      const newState = reducer(prev.state, event);
-      return newState === undefined
-        ? prev
-        : {
+  return new Rx.Observable<Update<Event, State>>(subscriber => {
+    const eventBuffer: Event[] = [];
+
+    let processingEventBuffer = false;
+    let eventStreamComplete = false;
+    let previousState = initialState;
+
+    const injectEvent = (nextEvent: Event) => {
+      eventBuffer.push(nextEvent);
+
+      if (processingEventBuffer) {
+        return;
+      }
+
+      try {
+        processingEventBuffer = true;
+
+        while (eventBuffer.length && !subscriber.closed) {
+          const event = eventBuffer.shift()!;
+          const nextState = summarize(previousState, event, injectEvent);
+
+          if (nextState === undefined) {
+            // skip this event
+            continue;
+          }
+
+          // emit state update
+          previousState = nextState;
+          subscriber.next({
             event,
-            state: newState,
-          };
-    }, initUpdate),
-    distinctUntilChanged(),
-    startWith(initUpdate)
-  );
+            state: nextState,
+          });
+        }
+
+        if (eventStreamComplete) {
+          subscriber.complete();
+        }
+      } catch (error) {
+        subscriber.error(error);
+      } finally {
+        processingEventBuffer = false;
+      }
+    };
+
+    // send initial "update"
+    subscriber.next({
+      state: initialState,
+    });
+
+    // inject all subsequent events to the internal eventBuffer
+    subscriber.add(
+      event$.subscribe(
+        injectEvent,
+        error => {
+          subscriber.error(error);
+        },
+        () => {
+          eventStreamComplete = true;
+          if (!processingEventBuffer && eventBuffer.length === 0) {
+            subscriber.complete();
+          }
+        }
+      )
+    );
+  });
 };
diff --git a/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts b/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts
index b35788009dd92..fec31cbe40dfe 100644
--- a/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts
+++ b/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts
@@ -179,6 +179,7 @@ it('uses cache on second run and exist cleanly', async () => {
       "initializing",
       "initializing",
       "initialized",
+      "success",
     ]
   `);
 });
diff --git a/packages/kbn-optimizer/src/log_optimizer_state.ts b/packages/kbn-optimizer/src/log_optimizer_state.ts
index 1ee4e47bfd9ee..5217581d1b413 100644
--- a/packages/kbn-optimizer/src/log_optimizer_state.ts
+++ b/packages/kbn-optimizer/src/log_optimizer_state.ts
@@ -77,10 +77,6 @@ export function logOptimizerState(log: ToolingLog, config: OptimizerConfig) {
             loggedInit = true;
             log.info(`initialized, ${state.offlineBundles.length} bundles cached`);
           }
-
-          if (state.onlineBundles.length === 0) {
-            log.success(`all bundles cached, success after ${state.durSec}`);
-          }
           return;
         }
 
@@ -123,10 +119,16 @@ export function logOptimizerState(log: ToolingLog, config: OptimizerConfig) {
         if (state.phase === 'success') {
           const buildCount = bundlesThatWereBuilt.size;
           bundlesThatWereBuilt.clear();
-          log.success(
-            `${buildCount} bundles compiled successfully after ${state.durSec} sec` +
-              (config.watch ? ', watching for changes' : '')
-          );
+
+          if (state.offlineBundles.length && buildCount === 0) {
+            log.success(`all bundles cached, success after ${state.durSec} sec`);
+          } else {
+            log.success(
+              `${buildCount} bundles compiled successfully after ${state.durSec} sec` +
+                (config.watch ? ', watching for changes' : '')
+            );
+          }
+
           return true;
         }
 
diff --git a/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.test.ts b/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.test.ts
index 7a8575a6c91fe..3cc58e744a7b9 100644
--- a/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.test.ts
+++ b/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.test.ts
@@ -22,7 +22,7 @@ import { REPO_ROOT } from '@kbn/dev-utils';
 
 import { Update } from '../common';
 
-import { OptimizerState } from './optimizer_reducer';
+import { OptimizerState } from './optimizer_state';
 import { OptimizerConfig } from './optimizer_config';
 import { handleOptimizerCompletion } from './handle_optimizer_completion';
 import { toArray } from 'rxjs/operators';
diff --git a/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.ts b/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.ts
index 9587902cc4187..5dd500cd7a9e4 100644
--- a/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.ts
+++ b/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.ts
@@ -23,7 +23,7 @@ import { createFailError } from '@kbn/dev-utils';
 
 import { pipeClosure, Update } from '../common';
 
-import { OptimizerState } from './optimizer_reducer';
+import { OptimizerState } from './optimizer_state';
 import { OptimizerConfig } from './optimizer_config';
 
 export function handleOptimizerCompletion(config: OptimizerConfig) {
@@ -44,11 +44,6 @@ export function handleOptimizerCompletion(config: OptimizerConfig) {
             return;
           }
 
-          if (prevState?.phase === 'initialized' && prevState.onlineBundles.length === 0) {
-            // all bundles cached
-            return;
-          }
-
           if (prevState?.phase === 'issue') {
             throw createFailError('webpack issue');
           }
diff --git a/packages/kbn-optimizer/src/optimizer/index.ts b/packages/kbn-optimizer/src/optimizer/index.ts
index 3df8ed9302668..84fd395e98976 100644
--- a/packages/kbn-optimizer/src/optimizer/index.ts
+++ b/packages/kbn-optimizer/src/optimizer/index.ts
@@ -19,7 +19,7 @@
 
 export * from './optimizer_config';
 export { WorkerStdio } from './observe_worker';
-export * from './optimizer_reducer';
+export * from './optimizer_state';
 export * from './cache_keys';
 export * from './watch_bundles_for_changes';
 export * from './run_workers';
diff --git a/packages/kbn-optimizer/src/optimizer/optimizer_reducer.ts b/packages/kbn-optimizer/src/optimizer/optimizer_state.ts
similarity index 91%
rename from packages/kbn-optimizer/src/optimizer/optimizer_reducer.ts
rename to packages/kbn-optimizer/src/optimizer/optimizer_state.ts
index c1e6572bd7e75..ac2a9b8ce1f8b 100644
--- a/packages/kbn-optimizer/src/optimizer/optimizer_reducer.ts
+++ b/packages/kbn-optimizer/src/optimizer/optimizer_state.ts
@@ -30,8 +30,13 @@ export interface OptimizerInitializedEvent {
   type: 'optimizer initialized';
 }
 
+export interface AllBundlesCachedEvent {
+  type: 'all bundles cached';
+}
+
 export type OptimizerEvent =
   | OptimizerInitializedEvent
+  | AllBundlesCachedEvent
   | ChangeEvent
   | WorkerMsg
   | WorkerStatus
@@ -92,16 +97,28 @@ function getStatePhase(states: CompilerMsg[]) {
   throw new Error(`unable to summarize bundle states: ${JSON.stringify(states)}`);
 }
 
-export function createOptimizerReducer(
+export function createOptimizerStateSummarizer(
   config: OptimizerConfig
 ): Summarizer<OptimizerEvent, OptimizerState> {
-  return (state, event) => {
+  return (state, event, injectEvent) => {
     if (event.type === 'optimizer initialized') {
+      if (state.onlineBundles.length === 0) {
+        injectEvent({
+          type: 'all bundles cached',
+        });
+      }
+
       return createOptimizerState(state, {
         phase: 'initialized',
       });
     }
 
+    if (event.type === 'all bundles cached') {
+      return createOptimizerState(state, {
+        phase: 'success',
+      });
+    }
+
     if (event.type === 'worker error' || event.type === 'compiler error') {
       // unrecoverable error states
       const error = new Error(event.errorMsg);
diff --git a/packages/kbn-optimizer/src/run_optimizer.ts b/packages/kbn-optimizer/src/run_optimizer.ts
index d2daa79feab7e..ab12cc679bc62 100644
--- a/packages/kbn-optimizer/src/run_optimizer.ts
+++ b/packages/kbn-optimizer/src/run_optimizer.ts
@@ -20,7 +20,7 @@
 import * as Rx from 'rxjs';
 import { mergeMap, share, observeOn } from 'rxjs/operators';
 
-import { summarizeEvent$, Update } from './common';
+import { summarizeEventStream, Update } from './common';
 
 import {
   OptimizerConfig,
@@ -31,7 +31,7 @@ import {
   watchBundlesForChanges$,
   runWorkers,
   OptimizerInitializedEvent,
-  createOptimizerReducer,
+  createOptimizerStateSummarizer,
   handleOptimizerCompletion,
 } from './optimizer';
 
@@ -66,7 +66,7 @@ export function runOptimizer(config: OptimizerConfig) {
       const workerEvent$ = runWorkers(config, cacheKey, bundleCacheEvent$, changeEvent$);
 
       // create the stream that summarized all the events into specific states
-      return summarizeEvent$<OptimizerEvent, OptimizerState>(
+      return summarizeEventStream<OptimizerEvent, OptimizerState>(
         Rx.merge(init$, changeEvent$, workerEvent$),
         {
           phase: 'initializing',
@@ -76,7 +76,7 @@ export function runOptimizer(config: OptimizerConfig) {
           startTime,
           durSec: 0,
         },
-        createOptimizerReducer(config)
+        createOptimizerStateSummarizer(config)
       );
     }),
     handleOptimizerCompletion(config)

From c2d98a08418c13d7e47bb1f72132aa0f6ff161c8 Mon Sep 17 00:00:00 2001
From: Melissa Alvarez <melissa.alvarez@elastic.co>
Date: Wed, 19 Feb 2020 13:48:21 -0500
Subject: [PATCH 074/174] [ML] New Platform server shim: update job audit
 messages routes (#57925)

* migrate all job audit messages routes to NP

* update types and add missing api names from validation
---
 .../job_audit_messages/{index.js => index.ts} |  0
 .../job_audit_messages.d.ts                   | 14 ++++
 .../job_audit_messages/job_audit_messages.js  |  8 +-
 .../plugins/ml/server/new_platform/plugin.ts  |  1 -
 .../plugins/ml/server/routes/apidoc.json      | 10 ++-
 .../ml/server/routes/job_audit_messages.js    | 40 ---------
 .../ml/server/routes/job_audit_messages.ts    | 81 +++++++++++++++++++
 7 files changed, 108 insertions(+), 46 deletions(-)
 rename x-pack/legacy/plugins/ml/server/models/job_audit_messages/{index.js => index.ts} (100%)
 create mode 100644 x-pack/legacy/plugins/ml/server/models/job_audit_messages/job_audit_messages.d.ts
 delete mode 100644 x-pack/legacy/plugins/ml/server/routes/job_audit_messages.js
 create mode 100644 x-pack/legacy/plugins/ml/server/routes/job_audit_messages.ts

diff --git a/x-pack/legacy/plugins/ml/server/models/job_audit_messages/index.js b/x-pack/legacy/plugins/ml/server/models/job_audit_messages/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/ml/server/models/job_audit_messages/index.js
rename to x-pack/legacy/plugins/ml/server/models/job_audit_messages/index.ts
diff --git a/x-pack/legacy/plugins/ml/server/models/job_audit_messages/job_audit_messages.d.ts b/x-pack/legacy/plugins/ml/server/models/job_audit_messages/job_audit_messages.d.ts
new file mode 100644
index 0000000000000..c4910b2535fb1
--- /dev/null
+++ b/x-pack/legacy/plugins/ml/server/models/job_audit_messages/job_audit_messages.d.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 { APICaller } from 'src/core/server';
+
+export function jobAuditMessagesProvider(
+  callAsCurrentUser: APICaller
+): {
+  getJobAuditMessages: (jobId?: string, from?: string) => any;
+  getAuditMessagesSummary: (jobIds?: string[]) => any;
+};
diff --git a/x-pack/legacy/plugins/ml/server/models/job_audit_messages/job_audit_messages.js b/x-pack/legacy/plugins/ml/server/models/job_audit_messages/job_audit_messages.js
index 52495b3b732d0..2cdfc0ef4f4c5 100644
--- a/x-pack/legacy/plugins/ml/server/models/job_audit_messages/job_audit_messages.js
+++ b/x-pack/legacy/plugins/ml/server/models/job_audit_messages/job_audit_messages.js
@@ -34,14 +34,14 @@ const anomalyDetectorTypeFilter = {
   },
 };
 
-export function jobAuditMessagesProvider(callWithRequest) {
+export function jobAuditMessagesProvider(callAsCurrentUser) {
   // search for audit messages,
   // jobId is optional. without it, all jobs will be listed.
   // from is optional and should be a string formatted in ES time units. e.g. 12h, 1d, 7d
   async function getJobAuditMessages(jobId, from) {
     let gte = null;
     if (jobId !== undefined && from === undefined) {
-      const jobs = await callWithRequest('ml.jobs', { jobId });
+      const jobs = await callAsCurrentUser('ml.jobs', { jobId });
       if (jobs.count > 0 && jobs.jobs !== undefined) {
         gte = moment(jobs.jobs[0].create_time).valueOf();
       }
@@ -100,7 +100,7 @@ export function jobAuditMessagesProvider(callWithRequest) {
     }
 
     try {
-      const resp = await callWithRequest('search', {
+      const resp = await callAsCurrentUser('search', {
         index: ML_NOTIFICATION_INDEX_PATTERN,
         ignore_unavailable: true,
         rest_total_hits_as_int: true,
@@ -155,7 +155,7 @@ export function jobAuditMessagesProvider(callWithRequest) {
         levelsPerJobAggSize = jobIds.length;
       }
 
-      const resp = await callWithRequest('search', {
+      const resp = await callAsCurrentUser('search', {
         index: ML_NOTIFICATION_INDEX_PATTERN,
         ignore_unavailable: true,
         rest_total_hits_as_int: true,
diff --git a/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts b/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts
index 69b08bfeda13d..60ede82d0de85 100644
--- a/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts
+++ b/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts
@@ -46,7 +46,6 @@ import { fieldsService } from '../routes/fields_service';
 import { filtersRoutes } from '../routes/filters';
 import { resultsServiceRoutes } from '../routes/results_service';
 import { jobServiceRoutes } from '../routes/job_service';
-// @ts-ignore: could not find declaration file for module
 import { jobAuditMessagesRoutes } from '../routes/job_audit_messages';
 // @ts-ignore: could not find declaration file for module
 import { fileDataVisualizerRoutes } from '../routes/file_data_visualizer';
diff --git a/x-pack/legacy/plugins/ml/server/routes/apidoc.json b/x-pack/legacy/plugins/ml/server/routes/apidoc.json
index d975ac6472535..4682371c16424 100644
--- a/x-pack/legacy/plugins/ml/server/routes/apidoc.json
+++ b/x-pack/legacy/plugins/ml/server/routes/apidoc.json
@@ -84,6 +84,14 @@
     "MlCapabilities",
     "MlNodeCount",
     "MlInfo",
-    "MlEsSearch"
+    "MlEsSearch",
+    "JobAuditMessages",
+    "GetJobAuditMessages",
+    "GetAllJobAuditMessages",
+    "JobValidation",
+    "EstimateBucketSpan",
+    "CalculateModelMemoryLimit",
+    "ValidateCardinality",
+    "ValidateJob"
   ]
 }
diff --git a/x-pack/legacy/plugins/ml/server/routes/job_audit_messages.js b/x-pack/legacy/plugins/ml/server/routes/job_audit_messages.js
deleted file mode 100644
index 38e87d97044dd..0000000000000
--- a/x-pack/legacy/plugins/ml/server/routes/job_audit_messages.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { callWithRequestFactory } from '../client/call_with_request_factory';
-import { wrapError } from '../client/errors';
-import { jobAuditMessagesProvider } from '../models/job_audit_messages';
-
-export function jobAuditMessagesRoutes({ commonRouteConfig, elasticsearchPlugin, route }) {
-  route({
-    method: 'GET',
-    path: '/api/ml/job_audit_messages/messages/{jobId}',
-    handler(request) {
-      const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
-      const { getJobAuditMessages } = jobAuditMessagesProvider(callWithRequest);
-      const { jobId } = request.params;
-      const from = request.query.from;
-      return getJobAuditMessages(jobId, from).catch(resp => wrapError(resp));
-    },
-    config: {
-      ...commonRouteConfig,
-    },
-  });
-
-  route({
-    method: 'GET',
-    path: '/api/ml/job_audit_messages/messages',
-    handler(request) {
-      const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
-      const { getJobAuditMessages } = jobAuditMessagesProvider(callWithRequest);
-      const from = request.query.from;
-      return getJobAuditMessages(undefined, from).catch(resp => wrapError(resp));
-    },
-    config: {
-      ...commonRouteConfig,
-    },
-  });
-}
diff --git a/x-pack/legacy/plugins/ml/server/routes/job_audit_messages.ts b/x-pack/legacy/plugins/ml/server/routes/job_audit_messages.ts
new file mode 100644
index 0000000000000..7298312990005
--- /dev/null
+++ b/x-pack/legacy/plugins/ml/server/routes/job_audit_messages.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 { schema } from '@kbn/config-schema';
+import { licensePreRoutingFactory } from '../new_platform/licence_check_pre_routing_factory';
+import { wrapError } from '../client/error_wrapper';
+import { RouteInitialization } from '../new_platform/plugin';
+import { jobAuditMessagesProvider } from '../models/job_audit_messages';
+
+/**
+ * Routes for job audit message routes
+ */
+export function jobAuditMessagesRoutes({ xpackMainPlugin, router }: RouteInitialization) {
+  /**
+   * @apiGroup JobAuditMessages
+   *
+   * @api {get} /api/ml/job_audit_messages/messages/:jobId Get audit messages
+   * @apiName GetJobAuditMessages
+   * @apiDescription Returns audit messages for specified job ID
+   */
+  router.get(
+    {
+      path: '/api/ml/job_audit_messages/messages/{jobId}',
+      validate: {
+        params: schema.object({ jobId: schema.maybe(schema.string()) }),
+        query: schema.maybe(schema.object({ from: schema.maybe(schema.any()) })),
+      },
+    },
+    licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
+      try {
+        const { getJobAuditMessages } = jobAuditMessagesProvider(
+          context.ml!.mlClient.callAsCurrentUser
+        );
+        const { jobId } = request.params;
+        const { from } = request.query;
+        const resp = await getJobAuditMessages(jobId, from);
+
+        return response.ok({
+          body: resp,
+        });
+      } catch (e) {
+        return response.customError(wrapError(e));
+      }
+    })
+  );
+
+  /**
+   * @apiGroup JobAuditMessages
+   *
+   * @api {get} /api/ml/results/anomalies_table_data Get all audit messages
+   * @apiName GetAllJobAuditMessages
+   * @apiDescription Returns all audit messages
+   */
+  router.get(
+    {
+      path: '/api/ml/job_audit_messages/messages',
+      validate: {
+        params: schema.object({ jobId: schema.maybe(schema.string()) }),
+        query: schema.maybe(schema.object({ from: schema.maybe(schema.any()) })),
+      },
+    },
+    licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
+      try {
+        const { getJobAuditMessages } = jobAuditMessagesProvider(
+          context.ml!.mlClient.callAsCurrentUser
+        );
+        const { from } = request.query;
+        const resp = await getJobAuditMessages(undefined, from);
+
+        return response.ok({
+          body: resp,
+        });
+      } catch (e) {
+        return response.customError(wrapError(e));
+      }
+    })
+  );
+}

From ff0d2d99882f082626930dc88c5fd178c692965f Mon Sep 17 00:00:00 2001
From: Christos Nasikas <christos.nasikas@elastic.co>
Date: Wed, 19 Feb 2020 21:08:31 +0200
Subject: [PATCH 075/174] [SIEM][Case] Merge header components (#57816)

* Create edit node component

* Accept EditNodeComponent

* Switch to old header

* test

* Remove iconType

* Remove isEditTitle

* Move translations

* Delete header_page_new component

* Move editable title component to different folder

* Update jest snapshot

* Rename prop

* Make EditableTitleComponent more generic

* useCallback instead of inline functions in props

* Hardcode titles

* Move UI state inside EditableTitleComponent

* Seperate title's tests

* Create tests for EditableTitleComponent

* useCallbacks on EditableTitle component

* Create translation for aria-label in edit icon

* Check if switched to edit mode after pressing submit

* Test title when canceled

Co-authored-by: patrykkopycinski <contact@patrykkopycinski.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../editable_title.test.tsx.snap              |  26 ++
 .../__snapshots__/index.test.tsx.snap         |  25 +-
 .../__snapshots__/title.test.tsx.snap         |  19 ++
 .../header_page/editable_title.test.tsx       | 192 +++++++++++++++
 .../components/header_page/editable_title.tsx |  98 ++++++++
 .../components/header_page/index.test.tsx     |  45 ----
 .../public/components/header_page/index.tsx   |  68 +-----
 .../components/header_page/title.test.tsx     |  72 ++++++
 .../public/components/header_page/title.tsx   |  62 +++++
 .../components/header_page/translations.ts    |  21 ++
 .../public/components/header_page/types.ts    |  18 ++
 .../__snapshots__/index.test.tsx.snap         |  47 ----
 .../components/header_page_new/index.test.tsx | 224 ------------------
 .../components/header_page_new/index.tsx      | 220 -----------------
 .../header_page_new/translations.ts           |  15 --
 .../pages/case/components/case_view/index.tsx |  26 +-
 16 files changed, 546 insertions(+), 632 deletions(-)
 create mode 100644 x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/editable_title.test.tsx.snap
 create mode 100644 x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/title.test.tsx.snap
 create mode 100644 x-pack/legacy/plugins/siem/public/components/header_page/editable_title.test.tsx
 create mode 100644 x-pack/legacy/plugins/siem/public/components/header_page/editable_title.tsx
 create mode 100644 x-pack/legacy/plugins/siem/public/components/header_page/title.test.tsx
 create mode 100644 x-pack/legacy/plugins/siem/public/components/header_page/title.tsx
 create mode 100644 x-pack/legacy/plugins/siem/public/components/header_page/translations.ts
 create mode 100644 x-pack/legacy/plugins/siem/public/components/header_page/types.ts
 delete mode 100644 x-pack/legacy/plugins/siem/public/components/header_page_new/__snapshots__/index.test.tsx.snap
 delete mode 100644 x-pack/legacy/plugins/siem/public/components/header_page_new/index.test.tsx
 delete mode 100644 x-pack/legacy/plugins/siem/public/components/header_page_new/index.tsx
 delete mode 100644 x-pack/legacy/plugins/siem/public/components/header_page_new/translations.ts

diff --git a/x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/editable_title.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/editable_title.test.tsx.snap
new file mode 100644
index 0000000000000..30c70d7f5a2a6
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/editable_title.test.tsx.snap
@@ -0,0 +1,26 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`EditableTitle it renders 1`] = `
+<EuiFlexGroup
+  alignItems="center"
+  gutterSize="none"
+>
+  <EuiFlexItem
+    grow={false}
+  >
+    <Memo(TitleComponent)
+      title="Test title"
+    />
+  </EuiFlexItem>
+  <EuiFlexItem
+    grow={false}
+  >
+    <StyledEuiButtonIcon
+      aria-label="You can edit Test title by clicking"
+      data-test-subj="editable-title-edit-icon"
+      iconType="pencil"
+      onClick={[Function]}
+    />
+  </EuiFlexItem>
+</EuiFlexGroup>
+`;
diff --git a/x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/index.test.tsx.snap
index a4bcf20ea1bc2..a100f5e4f93b4 100644
--- a/x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/index.test.tsx.snap
+++ b/x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/index.test.tsx.snap
@@ -8,21 +8,16 @@ exports[`HeaderPage it renders 1`] = `
     alignItems="center"
   >
     <FlexItem>
-      <EuiTitle
-        size="l"
-      >
-        <h1
-          data-test-subj="header-page-title"
-        >
-          Test title
-           
-          <StyledEuiBetaBadge
-            label="Beta"
-            tooltipContent="Test tooltip"
-            tooltipPosition="bottom"
-          />
-        </h1>
-      </EuiTitle>
+      <Memo(TitleComponent)
+        badgeOptions={
+          Object {
+            "beta": true,
+            "text": "Beta",
+            "tooltip": "Test tooltip",
+          }
+        }
+        title="Test title"
+      />
       <Subtitle
         data-test-subj="header-page-subtitle"
         items="Test subtitle"
diff --git a/x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/title.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/title.test.tsx.snap
new file mode 100644
index 0000000000000..05af2fee2c2a2
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/title.test.tsx.snap
@@ -0,0 +1,19 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Title it renders 1`] = `
+<EuiTitle
+  size="l"
+>
+  <h1
+    data-test-subj="header-page-title"
+  >
+    Test title
+     
+    <StyledEuiBetaBadge
+      label="Beta"
+      tooltipContent="Test tooltip"
+      tooltipPosition="bottom"
+    />
+  </h1>
+</EuiTitle>
+`;
diff --git a/x-pack/legacy/plugins/siem/public/components/header_page/editable_title.test.tsx b/x-pack/legacy/plugins/siem/public/components/header_page/editable_title.test.tsx
new file mode 100644
index 0000000000000..fcfdd0d5dd237
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/header_page/editable_title.test.tsx
@@ -0,0 +1,192 @@
+/*
+ * 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 { shallow } from 'enzyme';
+import React from 'react';
+
+import { TestProviders } from '../../mock';
+import { EditableTitle } from './editable_title';
+import { useMountAppended } from '../../utils/use_mount_appended';
+
+describe('EditableTitle', () => {
+  const mount = useMountAppended();
+  const submitTitle = jest.fn();
+
+  test('it renders', () => {
+    const wrapper = shallow(
+      <EditableTitle title="Test title" onSubmit={submitTitle} isLoading={false} />
+    );
+
+    expect(wrapper).toMatchSnapshot();
+  });
+
+  test('it shows the edit title input field', () => {
+    const wrapper = mount(
+      <TestProviders>
+        <EditableTitle title="Test title" onSubmit={submitTitle} isLoading={false} />
+      </TestProviders>
+    );
+
+    wrapper.find('button[data-test-subj="editable-title-edit-icon"]').simulate('click');
+    wrapper.update();
+
+    expect(
+      wrapper
+        .find('[data-test-subj="editable-title-input-field"]')
+        .first()
+        .exists()
+    ).toBe(true);
+  });
+
+  test('it shows the submit button', () => {
+    const wrapper = mount(
+      <TestProviders>
+        <EditableTitle title="Test title" onSubmit={submitTitle} isLoading={false} />
+      </TestProviders>
+    );
+
+    wrapper.find('button[data-test-subj="editable-title-edit-icon"]').simulate('click');
+    wrapper.update();
+
+    expect(
+      wrapper
+        .find('[data-test-subj="editable-title-submit-btn"]')
+        .first()
+        .exists()
+    ).toBe(true);
+  });
+
+  test('it shows the cancel button', () => {
+    const wrapper = mount(
+      <TestProviders>
+        <EditableTitle title="Test title" onSubmit={submitTitle} isLoading={false} />
+      </TestProviders>
+    );
+
+    wrapper.find('button[data-test-subj="editable-title-edit-icon"]').simulate('click');
+    wrapper.update();
+
+    expect(
+      wrapper
+        .find('[data-test-subj="editable-title-cancel-btn"]')
+        .first()
+        .exists()
+    ).toBe(true);
+  });
+
+  test('it DOES NOT shows the edit icon when in edit mode', () => {
+    const wrapper = mount(
+      <TestProviders>
+        <EditableTitle title="Test title" onSubmit={submitTitle} isLoading={false} />
+      </TestProviders>
+    );
+
+    wrapper.find('button[data-test-subj="editable-title-edit-icon"]').simulate('click');
+    wrapper.update();
+
+    expect(
+      wrapper
+        .find('[data-test-subj="editable-title-edit-icon"]')
+        .first()
+        .exists()
+    ).toBe(false);
+  });
+
+  test('it switch to non edit mode when canceled', () => {
+    const wrapper = mount(
+      <TestProviders>
+        <EditableTitle title="Test title" onSubmit={submitTitle} isLoading={false} />
+      </TestProviders>
+    );
+
+    wrapper.find('button[data-test-subj="editable-title-edit-icon"]').simulate('click');
+    wrapper.update();
+    wrapper.find('button[data-test-subj="editable-title-cancel-btn"]').simulate('click');
+
+    expect(
+      wrapper
+        .find('[data-test-subj="editable-title-edit-icon"]')
+        .first()
+        .exists()
+    ).toBe(true);
+  });
+
+  test('it should change the title', () => {
+    const newTitle = 'new test title';
+
+    const wrapper = mount(
+      <TestProviders>
+        <EditableTitle title="Test title" onSubmit={submitTitle} isLoading={false} />
+      </TestProviders>
+    );
+
+    wrapper.find('button[data-test-subj="editable-title-edit-icon"]').simulate('click');
+    wrapper.update();
+
+    wrapper
+      .find('input[data-test-subj="editable-title-input-field"]')
+      .simulate('change', { target: { value: newTitle } });
+
+    wrapper.update();
+
+    expect(
+      wrapper.find('input[data-test-subj="editable-title-input-field"]').prop('value')
+    ).toEqual(newTitle);
+  });
+
+  test('it should NOT change the title when cancel', () => {
+    const title = 'Test title';
+    const newTitle = 'new test title';
+
+    const wrapper = mount(
+      <TestProviders>
+        <EditableTitle title={title} onSubmit={submitTitle} isLoading={false} />
+      </TestProviders>
+    );
+
+    wrapper.find('button[data-test-subj="editable-title-edit-icon"]').simulate('click');
+    wrapper.update();
+
+    wrapper
+      .find('input[data-test-subj="editable-title-input-field"]')
+      .simulate('change', { target: { value: newTitle } });
+    wrapper.update();
+
+    wrapper.find('button[data-test-subj="editable-title-cancel-btn"]').simulate('click');
+    wrapper.update();
+
+    expect(wrapper.find('h1[data-test-subj="header-page-title"]').text()).toEqual(title);
+  });
+
+  test('it submits the title', () => {
+    const newTitle = 'new test title';
+
+    const wrapper = mount(
+      <TestProviders>
+        <EditableTitle title="Test title" onSubmit={submitTitle} isLoading={false} />
+      </TestProviders>
+    );
+
+    wrapper.find('button[data-test-subj="editable-title-edit-icon"]').simulate('click');
+    wrapper.update();
+
+    wrapper
+      .find('input[data-test-subj="editable-title-input-field"]')
+      .simulate('change', { target: { value: newTitle } });
+
+    wrapper.find('button[data-test-subj="editable-title-submit-btn"]').simulate('click');
+    wrapper.update();
+
+    expect(submitTitle).toHaveBeenCalled();
+    expect(submitTitle.mock.calls[0][0]).toEqual(newTitle);
+    expect(
+      wrapper
+        .find('[data-test-subj="editable-title-edit-icon"]')
+        .first()
+        .exists()
+    ).toBe(true);
+  });
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/header_page/editable_title.tsx b/x-pack/legacy/plugins/siem/public/components/header_page/editable_title.tsx
new file mode 100644
index 0000000000000..bd8dabfc35ff4
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/header_page/editable_title.tsx
@@ -0,0 +1,98 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { useState, useCallback } from 'react';
+import styled, { css } from 'styled-components';
+
+import {
+  EuiButton,
+  EuiButtonEmpty,
+  EuiFlexGroup,
+  EuiFlexItem,
+  EuiFieldText,
+  EuiButtonIcon,
+} from '@elastic/eui';
+
+import * as i18n from './translations';
+
+import { Title } from './title';
+
+const StyledEuiButtonIcon = styled(EuiButtonIcon)`
+  ${({ theme }) => css`
+    margin-left: ${theme.eui.euiSize};
+  `}
+`;
+
+StyledEuiButtonIcon.displayName = 'StyledEuiButtonIcon';
+
+interface Props {
+  isLoading: boolean;
+  title: string | React.ReactNode;
+  onSubmit: (title: string) => void;
+}
+
+const EditableTitleComponent: React.FC<Props> = ({ onSubmit, isLoading, title }) => {
+  const [editMode, setEditMode] = useState(false);
+  const [changedTitle, onTitleChange] = useState(title);
+
+  const onCancel = useCallback(() => setEditMode(false), []);
+  const onClickEditIcon = useCallback(() => setEditMode(true), []);
+
+  const onClickSubmit = useCallback(
+    (newTitle: string): void => {
+      onSubmit(newTitle);
+      setEditMode(false);
+    },
+    [changedTitle]
+  );
+
+  return editMode ? (
+    <EuiFlexGroup alignItems="center" gutterSize="m" justifyContent="spaceBetween">
+      <EuiFlexItem grow={false}>
+        <EuiFieldText
+          onChange={e => onTitleChange(e.target.value)}
+          value={`${changedTitle}`}
+          data-test-subj="editable-title-input-field"
+        />
+      </EuiFlexItem>
+      <EuiFlexGroup gutterSize="none" responsive={false} wrap={true}>
+        <EuiFlexItem grow={false}>
+          <EuiButton
+            fill
+            isDisabled={isLoading}
+            isLoading={isLoading}
+            onClick={() => onClickSubmit(changedTitle as string)}
+            data-test-subj="editable-title-submit-btn"
+          >
+            {i18n.SUBMIT}
+          </EuiButton>
+        </EuiFlexItem>
+        <EuiFlexItem grow={false}>
+          <EuiButtonEmpty onClick={onCancel} data-test-subj="editable-title-cancel-btn">
+            {i18n.CANCEL}
+          </EuiButtonEmpty>
+        </EuiFlexItem>
+      </EuiFlexGroup>
+      <EuiFlexItem />
+    </EuiFlexGroup>
+  ) : (
+    <EuiFlexGroup alignItems="center" gutterSize="none">
+      <EuiFlexItem grow={false}>
+        <Title title={title} />
+      </EuiFlexItem>
+      <EuiFlexItem grow={false}>
+        <StyledEuiButtonIcon
+          aria-label={i18n.EDIT_TITLE_ARIA(title as string)}
+          iconType="pencil"
+          onClick={onClickEditIcon}
+          data-test-subj="editable-title-edit-icon"
+        />
+      </EuiFlexItem>
+    </EuiFlexGroup>
+  );
+};
+
+export const EditableTitle = React.memo(EditableTitleComponent);
diff --git a/x-pack/legacy/plugins/siem/public/components/header_page/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/header_page/index.test.tsx
index 83a70fd90d82b..05575f060bb36 100644
--- a/x-pack/legacy/plugins/siem/public/components/header_page/index.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/header_page/index.test.tsx
@@ -31,21 +31,6 @@ describe('HeaderPage', () => {
     expect(wrapper).toMatchSnapshot();
   });
 
-  test('it renders the title', () => {
-    const wrapper = mount(
-      <TestProviders>
-        <HeaderPage title="Test title" />
-      </TestProviders>
-    );
-
-    expect(
-      wrapper
-        .find('[data-test-subj="header-page-title"]')
-        .first()
-        .exists()
-    ).toBe(true);
-  });
-
   test('it renders the back link when provided', () => {
     const wrapper = mount(
       <TestProviders>
@@ -191,34 +176,4 @@ describe('HeaderPage', () => {
     expect(siemHeaderPage).not.toHaveStyleRule('border-bottom', euiDarkVars.euiBorderThin);
     expect(siemHeaderPage).not.toHaveStyleRule('padding-bottom', euiDarkVars.paddingSizes.l);
   });
-
-  test('it renders as a draggable when arguments provided', () => {
-    const wrapper = mount(
-      <TestProviders>
-        <HeaderPage draggableArguments={{ field: 'neat', value: 'cool' }} title="Test title" />
-      </TestProviders>
-    );
-
-    expect(
-      wrapper
-        .find('[data-test-subj="header-page-draggable"]')
-        .first()
-        .exists()
-    ).toBe(true);
-  });
-
-  test('it DOES NOT render as a draggable when arguments not provided', () => {
-    const wrapper = mount(
-      <TestProviders>
-        <HeaderPage title="Test title" />
-      </TestProviders>
-    );
-
-    expect(
-      wrapper
-        .find('[data-test-subj="header-page-draggable"]')
-        .first()
-        .exists()
-    ).toBe(false);
-  });
 });
diff --git a/x-pack/legacy/plugins/siem/public/components/header_page/index.tsx b/x-pack/legacy/plugins/siem/public/components/header_page/index.tsx
index d694aad3dd896..f88ffc3f3c6c4 100644
--- a/x-pack/legacy/plugins/siem/public/components/header_page/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/header_page/index.tsx
@@ -4,20 +4,14 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import {
-  EuiBetaBadge,
-  EuiBadge,
-  EuiFlexGroup,
-  EuiFlexItem,
-  EuiProgress,
-  EuiTitle,
-} from '@elastic/eui';
+import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiProgress } from '@elastic/eui';
 import React from 'react';
 import styled, { css } from 'styled-components';
 
-import { DefaultDraggable } from '../draggables';
 import { LinkIcon, LinkIconProps } from '../link_icon';
 import { Subtitle, SubtitleProps } from '../subtitle';
+import { Title } from './title';
+import { DraggableArguments, BadgeOptions, TitleProp } from './types';
 
 interface HeaderProps {
   border?: boolean;
@@ -63,28 +57,11 @@ const Badge = styled(EuiBadge)`
 `;
 Badge.displayName = 'Badge';
 
-const StyledEuiBetaBadge = styled(EuiBetaBadge)`
-  vertical-align: middle;
-`;
-
-StyledEuiBetaBadge.displayName = 'StyledEuiBetaBadge';
-
 interface BackOptions {
   href: LinkIconProps['href'];
   text: LinkIconProps['children'];
 }
 
-interface BadgeOptions {
-  beta?: boolean;
-  text: string;
-  tooltip?: string;
-}
-
-interface DraggableArguments {
-  field: string;
-  value: string;
-}
-
 export interface HeaderPageProps extends HeaderProps {
   backOptions?: BackOptions;
   badgeOptions?: BadgeOptions;
@@ -92,7 +69,8 @@ export interface HeaderPageProps extends HeaderProps {
   draggableArguments?: DraggableArguments;
   subtitle?: SubtitleProps['items'];
   subtitle2?: SubtitleProps['items'];
-  title: string | React.ReactNode;
+  title: TitleProp;
+  titleNode?: React.ReactElement;
 }
 
 const HeaderPageComponent: React.FC<HeaderPageProps> = ({
@@ -105,6 +83,7 @@ const HeaderPageComponent: React.FC<HeaderPageProps> = ({
   subtitle,
   subtitle2,
   title,
+  titleNode,
   ...rest
 }) => (
   <Header border={border} {...rest}>
@@ -118,34 +97,13 @@ const HeaderPageComponent: React.FC<HeaderPageProps> = ({
           </LinkBack>
         )}
 
-        <EuiTitle size="l">
-          <h1 data-test-subj="header-page-title">
-            {!draggableArguments ? (
-              title
-            ) : (
-              <DefaultDraggable
-                data-test-subj="header-page-draggable"
-                id={`header-page-draggable-${draggableArguments.field}-${draggableArguments.value}`}
-                field={draggableArguments.field}
-                value={`${draggableArguments.value}`}
-              />
-            )}
-            {badgeOptions && (
-              <>
-                {' '}
-                {badgeOptions.beta ? (
-                  <StyledEuiBetaBadge
-                    label={badgeOptions.text}
-                    tooltipContent={badgeOptions.tooltip}
-                    tooltipPosition="bottom"
-                  />
-                ) : (
-                  <Badge color="hollow">{badgeOptions.text}</Badge>
-                )}
-              </>
-            )}
-          </h1>
-        </EuiTitle>
+        {titleNode || (
+          <Title
+            draggableArguments={draggableArguments}
+            title={title}
+            badgeOptions={badgeOptions}
+          />
+        )}
 
         {subtitle && <Subtitle data-test-subj="header-page-subtitle" items={subtitle} />}
         {subtitle2 && <Subtitle data-test-subj="header-page-subtitle-2" items={subtitle2} />}
diff --git a/x-pack/legacy/plugins/siem/public/components/header_page/title.test.tsx b/x-pack/legacy/plugins/siem/public/components/header_page/title.test.tsx
new file mode 100644
index 0000000000000..48cb05914d4c5
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/header_page/title.test.tsx
@@ -0,0 +1,72 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { shallow } from 'enzyme';
+import React from 'react';
+
+import { TestProviders } from '../../mock';
+import { Title } from './title';
+import { useMountAppended } from '../../utils/use_mount_appended';
+
+describe('Title', () => {
+  const mount = useMountAppended();
+
+  test('it renders', () => {
+    const wrapper = shallow(
+      <Title
+        badgeOptions={{ beta: true, text: 'Beta', tooltip: 'Test tooltip' }}
+        title="Test title"
+      />
+    );
+
+    expect(wrapper).toMatchSnapshot();
+  });
+
+  test('it renders the title', () => {
+    const wrapper = mount(
+      <TestProviders>
+        <Title title="Test title" />
+      </TestProviders>
+    );
+
+    expect(
+      wrapper
+        .find('[data-test-subj="header-page-title"]')
+        .first()
+        .exists()
+    ).toBe(true);
+  });
+
+  test('it renders as a draggable when arguments provided', () => {
+    const wrapper = mount(
+      <TestProviders>
+        <Title draggableArguments={{ field: 'neat', value: 'cool' }} title="Test title" />
+      </TestProviders>
+    );
+
+    expect(
+      wrapper
+        .find('[data-test-subj="header-page-draggable"]')
+        .first()
+        .exists()
+    ).toBe(true);
+  });
+
+  test('it DOES NOT render as a draggable when arguments not provided', () => {
+    const wrapper = mount(
+      <TestProviders>
+        <Title title="Test title" />
+      </TestProviders>
+    );
+
+    expect(
+      wrapper
+        .find('[data-test-subj="header-page-draggable"]')
+        .first()
+        .exists()
+    ).toBe(false);
+  });
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/header_page/title.tsx b/x-pack/legacy/plugins/siem/public/components/header_page/title.tsx
new file mode 100644
index 0000000000000..a1f3cfd857148
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/header_page/title.tsx
@@ -0,0 +1,62 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { EuiBetaBadge, EuiBadge, EuiTitle } from '@elastic/eui';
+import styled from 'styled-components';
+
+import { DraggableArguments, BadgeOptions, TitleProp } from './types';
+import { DefaultDraggable } from '../draggables';
+
+const StyledEuiBetaBadge = styled(EuiBetaBadge)`
+  vertical-align: middle;
+`;
+
+StyledEuiBetaBadge.displayName = 'StyledEuiBetaBadge';
+
+const Badge = styled(EuiBadge)`
+  letter-spacing: 0;
+`;
+Badge.displayName = 'Badge';
+
+interface Props {
+  badgeOptions?: BadgeOptions;
+  title: TitleProp;
+  draggableArguments?: DraggableArguments;
+}
+
+const TitleComponent: React.FC<Props> = ({ draggableArguments, title, badgeOptions }) => (
+  <EuiTitle size="l">
+    <h1 data-test-subj="header-page-title">
+      {!draggableArguments ? (
+        title
+      ) : (
+        <DefaultDraggable
+          data-test-subj="header-page-draggable"
+          id={`header-page-draggable-${draggableArguments.field}-${draggableArguments.value}`}
+          field={draggableArguments.field}
+          value={`${draggableArguments.value}`}
+        />
+      )}
+      {badgeOptions && (
+        <>
+          {' '}
+          {badgeOptions.beta ? (
+            <StyledEuiBetaBadge
+              label={badgeOptions.text}
+              tooltipContent={badgeOptions.tooltip}
+              tooltipPosition="bottom"
+            />
+          ) : (
+            <Badge color="hollow">{badgeOptions.text}</Badge>
+          )}
+        </>
+      )}
+    </h1>
+  </EuiTitle>
+);
+
+export const Title = React.memo(TitleComponent);
diff --git a/x-pack/legacy/plugins/siem/public/components/header_page/translations.ts b/x-pack/legacy/plugins/siem/public/components/header_page/translations.ts
new file mode 100644
index 0000000000000..2bc2ac492b0b1
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/header_page/translations.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 { i18n } from '@kbn/i18n';
+
+export const SUBMIT = i18n.translate('xpack.siem.header.editableTitle.submit', {
+  defaultMessage: 'Submit',
+});
+
+export const CANCEL = i18n.translate('xpack.siem.header.editableTitle.cancel', {
+  defaultMessage: 'Cancel',
+});
+
+export const EDIT_TITLE_ARIA = (title: string) =>
+  i18n.translate('xpack.siem.header.editableTitle.editButtonAria', {
+    values: { title },
+    defaultMessage: 'You can edit {title} by clicking',
+  });
diff --git a/x-pack/legacy/plugins/siem/public/components/header_page/types.ts b/x-pack/legacy/plugins/siem/public/components/header_page/types.ts
new file mode 100644
index 0000000000000..3c16af83585e9
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/header_page/types.ts
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export type TitleProp = string | React.ReactNode;
+
+export interface DraggableArguments {
+  field: string;
+  value: string;
+}
+
+export interface BadgeOptions {
+  beta?: boolean;
+  text: string;
+  tooltip?: string;
+}
diff --git a/x-pack/legacy/plugins/siem/public/components/header_page_new/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/header_page_new/__snapshots__/index.test.tsx.snap
deleted file mode 100644
index 1b792503cf1c6..0000000000000
--- a/x-pack/legacy/plugins/siem/public/components/header_page_new/__snapshots__/index.test.tsx.snap
+++ /dev/null
@@ -1,47 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`HeaderPage it renders 1`] = `
-<Header
-  border={true}
->
-  <EuiFlexGroup
-    alignItems="center"
-    justifyContent="spaceBetween"
-  >
-    <FlexItem
-      grow={false}
-    >
-      <EuiTitle
-        size="l"
-      >
-        <h1
-          data-test-subj="header-page-title"
-        >
-          Test title
-           
-          <StyledEuiBetaBadge
-            label="Beta"
-            tooltipContent="Test tooltip"
-            tooltipPosition="bottom"
-          />
-        </h1>
-      </EuiTitle>
-      <Subtitle
-        data-test-subj="header-page-subtitle"
-        items="Test subtitle"
-      />
-      <Subtitle
-        data-test-subj="header-page-subtitle-2"
-        items="Test subtitle 2"
-      />
-    </FlexItem>
-    <FlexItem
-      data-test-subj="header-page-supplements"
-    >
-      <p>
-        Test supplement
-      </p>
-    </FlexItem>
-  </EuiFlexGroup>
-</Header>
-`;
diff --git a/x-pack/legacy/plugins/siem/public/components/header_page_new/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/header_page_new/index.test.tsx
deleted file mode 100644
index 83a70fd90d82b..0000000000000
--- a/x-pack/legacy/plugins/siem/public/components/header_page_new/index.test.tsx
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
-import { shallow } from 'enzyme';
-import React from 'react';
-
-import { TestProviders } from '../../mock';
-import { HeaderPage } from './index';
-import { useMountAppended } from '../../utils/use_mount_appended';
-
-describe('HeaderPage', () => {
-  const mount = useMountAppended();
-
-  test('it renders', () => {
-    const wrapper = shallow(
-      <HeaderPage
-        badgeOptions={{ beta: true, text: 'Beta', tooltip: 'Test tooltip' }}
-        border
-        subtitle="Test subtitle"
-        subtitle2="Test subtitle 2"
-        title="Test title"
-      >
-        <p>{'Test supplement'}</p>
-      </HeaderPage>
-    );
-
-    expect(wrapper).toMatchSnapshot();
-  });
-
-  test('it renders the title', () => {
-    const wrapper = mount(
-      <TestProviders>
-        <HeaderPage title="Test title" />
-      </TestProviders>
-    );
-
-    expect(
-      wrapper
-        .find('[data-test-subj="header-page-title"]')
-        .first()
-        .exists()
-    ).toBe(true);
-  });
-
-  test('it renders the back link when provided', () => {
-    const wrapper = mount(
-      <TestProviders>
-        <HeaderPage backOptions={{ href: '#', text: 'Test link' }} title="Test title" />
-      </TestProviders>
-    );
-
-    expect(
-      wrapper
-        .find('.siemHeaderPage__linkBack')
-        .first()
-        .exists()
-    ).toBe(true);
-  });
-
-  test('it DOES NOT render the back link when not provided', () => {
-    const wrapper = mount(
-      <TestProviders>
-        <HeaderPage title="Test title" />
-      </TestProviders>
-    );
-
-    expect(
-      wrapper
-        .find('.siemHeaderPage__linkBack')
-        .first()
-        .exists()
-    ).toBe(false);
-  });
-
-  test('it renders the first subtitle when provided', () => {
-    const wrapper = mount(
-      <TestProviders>
-        <HeaderPage subtitle="Test subtitle" title="Test title" />
-      </TestProviders>
-    );
-
-    expect(
-      wrapper
-        .find('[data-test-subj="header-page-subtitle"]')
-        .first()
-        .exists()
-    ).toBe(true);
-  });
-
-  test('it DOES NOT render the first subtitle when not provided', () => {
-    const wrapper = mount(
-      <TestProviders>
-        <HeaderPage title="Test title" />
-      </TestProviders>
-    );
-
-    expect(
-      wrapper
-        .find('[data-test-subj="header-section-subtitle"]')
-        .first()
-        .exists()
-    ).toBe(false);
-  });
-
-  test('it renders the second subtitle when provided', () => {
-    const wrapper = mount(
-      <TestProviders>
-        <HeaderPage subtitle2="Test subtitle 2" title="Test title" />
-      </TestProviders>
-    );
-
-    expect(
-      wrapper
-        .find('[data-test-subj="header-page-subtitle-2"]')
-        .first()
-        .exists()
-    ).toBe(true);
-  });
-
-  test('it DOES NOT render the second subtitle when not provided', () => {
-    const wrapper = mount(
-      <TestProviders>
-        <HeaderPage title="Test title" />
-      </TestProviders>
-    );
-
-    expect(
-      wrapper
-        .find('[data-test-subj="header-section-subtitle-2"]')
-        .first()
-        .exists()
-    ).toBe(false);
-  });
-
-  test('it renders supplements when children provided', () => {
-    const wrapper = mount(
-      <TestProviders>
-        <HeaderPage title="Test title">
-          <p>{'Test supplement'}</p>
-        </HeaderPage>
-      </TestProviders>
-    );
-
-    expect(
-      wrapper
-        .find('[data-test-subj="header-page-supplements"]')
-        .first()
-        .exists()
-    ).toBe(true);
-  });
-
-  test('it DOES NOT render supplements when children not provided', () => {
-    const wrapper = mount(
-      <TestProviders>
-        <HeaderPage title="Test title" />
-      </TestProviders>
-    );
-
-    expect(
-      wrapper
-        .find('[data-test-subj="header-page-supplements"]')
-        .first()
-        .exists()
-    ).toBe(false);
-  });
-
-  test('it applies border styles when border is true', () => {
-    const wrapper = mount(
-      <TestProviders>
-        <HeaderPage border title="Test title" />
-      </TestProviders>
-    );
-    const siemHeaderPage = wrapper.find('.siemHeaderPage').first();
-
-    expect(siemHeaderPage).toHaveStyleRule('border-bottom', euiDarkVars.euiBorderThin);
-    expect(siemHeaderPage).toHaveStyleRule('padding-bottom', euiDarkVars.paddingSizes.l);
-  });
-
-  test('it DOES NOT apply border styles when border is false', () => {
-    const wrapper = mount(
-      <TestProviders>
-        <HeaderPage title="Test title" />
-      </TestProviders>
-    );
-    const siemHeaderPage = wrapper.find('.siemHeaderPage').first();
-
-    expect(siemHeaderPage).not.toHaveStyleRule('border-bottom', euiDarkVars.euiBorderThin);
-    expect(siemHeaderPage).not.toHaveStyleRule('padding-bottom', euiDarkVars.paddingSizes.l);
-  });
-
-  test('it renders as a draggable when arguments provided', () => {
-    const wrapper = mount(
-      <TestProviders>
-        <HeaderPage draggableArguments={{ field: 'neat', value: 'cool' }} title="Test title" />
-      </TestProviders>
-    );
-
-    expect(
-      wrapper
-        .find('[data-test-subj="header-page-draggable"]')
-        .first()
-        .exists()
-    ).toBe(true);
-  });
-
-  test('it DOES NOT render as a draggable when arguments not provided', () => {
-    const wrapper = mount(
-      <TestProviders>
-        <HeaderPage title="Test title" />
-      </TestProviders>
-    );
-
-    expect(
-      wrapper
-        .find('[data-test-subj="header-page-draggable"]')
-        .first()
-        .exists()
-    ).toBe(false);
-  });
-});
diff --git a/x-pack/legacy/plugins/siem/public/components/header_page_new/index.tsx b/x-pack/legacy/plugins/siem/public/components/header_page_new/index.tsx
deleted file mode 100644
index 7e486c78fb9b9..0000000000000
--- a/x-pack/legacy/plugins/siem/public/components/header_page_new/index.tsx
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import {
-  EuiBadge,
-  EuiBetaBadge,
-  EuiButton,
-  EuiButtonEmpty,
-  EuiButtonIcon,
-  EuiFieldText,
-  EuiFlexGroup,
-  EuiFlexItem,
-  EuiProgress,
-  EuiTitle,
-} from '@elastic/eui';
-import React from 'react';
-import styled, { css } from 'styled-components';
-
-import { DefaultDraggable } from '../draggables';
-import { LinkIcon, LinkIconProps } from '../link_icon';
-import { Subtitle, SubtitleProps } from '../subtitle';
-import * as i18n from './translations';
-
-interface HeaderProps {
-  border?: boolean;
-  isLoading?: boolean;
-}
-
-const Header = styled.header.attrs({
-  className: 'siemHeaderPage',
-})<HeaderProps>`
-  ${({ border, theme }) => css`
-    margin-bottom: ${theme.eui.euiSizeL};
-
-    ${border &&
-      css`
-        border-bottom: ${theme.eui.euiBorderThin};
-        padding-bottom: ${theme.eui.paddingSizes.l};
-        .euiProgress {
-          top: ${theme.eui.paddingSizes.l};
-        }
-      `}
-  `}
-`;
-Header.displayName = 'Header';
-
-const FlexItem = styled(EuiFlexItem)`
-  display: block;
-`;
-FlexItem.displayName = 'FlexItem';
-
-const LinkBack = styled.div.attrs({
-  className: 'siemHeaderPage__linkBack',
-})`
-  ${({ theme }) => css`
-    font-size: ${theme.eui.euiFontSizeXS};
-    line-height: ${theme.eui.euiLineHeight};
-    margin-bottom: ${theme.eui.euiSizeS};
-  `}
-`;
-LinkBack.displayName = 'LinkBack';
-
-const Badge = styled(EuiBadge)`
-  letter-spacing: 0;
-`;
-Badge.displayName = 'Badge';
-
-const StyledEuiBetaBadge = styled(EuiBetaBadge)`
-  vertical-align: middle;
-`;
-
-StyledEuiBetaBadge.displayName = 'StyledEuiBetaBadge';
-
-const StyledEuiButtonIcon = styled(EuiButtonIcon)`
-  ${({ theme }) => css`
-    margin-left: ${theme.eui.euiSize};
-  `}
-`;
-
-StyledEuiButtonIcon.displayName = 'StyledEuiButtonIcon';
-
-interface BackOptions {
-  href: LinkIconProps['href'];
-  text: LinkIconProps['children'];
-}
-
-interface BadgeOptions {
-  beta?: boolean;
-  text: string;
-  tooltip?: string;
-}
-
-interface DraggableArguments {
-  field: string;
-  value: string;
-}
-interface IconAction {
-  'aria-label': string;
-  iconType: string;
-  onChange: (a: string) => void;
-  onClick: (b: boolean) => void;
-  onSubmit: () => void;
-}
-
-export interface HeaderPageProps extends HeaderProps {
-  backOptions?: BackOptions;
-  badgeOptions?: BadgeOptions;
-  children?: React.ReactNode;
-  draggableArguments?: DraggableArguments;
-  isEditTitle?: boolean;
-  iconAction?: IconAction;
-  subtitle2?: SubtitleProps['items'];
-  subtitle?: SubtitleProps['items'];
-  title: string | React.ReactNode;
-}
-
-const HeaderPageComponent: React.FC<HeaderPageProps> = ({
-  backOptions,
-  badgeOptions,
-  border,
-  children,
-  draggableArguments,
-  isEditTitle,
-  iconAction,
-  isLoading,
-  subtitle,
-  subtitle2,
-  title,
-  ...rest
-}) => (
-  <Header border={border} {...rest}>
-    <EuiFlexGroup alignItems="center" justifyContent="spaceBetween">
-      <FlexItem grow={false}>
-        {backOptions && (
-          <LinkBack>
-            <LinkIcon href={backOptions.href} iconType="arrowLeft">
-              {backOptions.text}
-            </LinkIcon>
-          </LinkBack>
-        )}
-
-        {isEditTitle && iconAction ? (
-          <EuiFlexGroup alignItems="center" gutterSize="m" justifyContent="spaceBetween">
-            <EuiFlexItem grow={false}>
-              <EuiFieldText
-                onChange={e => iconAction.onChange(e.target.value)}
-                value={`${title}`}
-              />
-            </EuiFlexItem>
-            <EuiFlexGroup gutterSize="none" responsive={false} wrap={true}>
-              <EuiFlexItem grow={false}>
-                <EuiButton
-                  fill
-                  isDisabled={isLoading}
-                  isLoading={isLoading}
-                  onClick={iconAction.onSubmit}
-                >
-                  {i18n.SUBMIT}
-                </EuiButton>
-              </EuiFlexItem>
-              <EuiFlexItem grow={false}>
-                <EuiButtonEmpty onClick={() => iconAction.onClick(false)}>
-                  {i18n.CANCEL}
-                </EuiButtonEmpty>
-              </EuiFlexItem>
-            </EuiFlexGroup>
-            <EuiFlexItem />
-          </EuiFlexGroup>
-        ) : (
-          <EuiTitle size="l">
-            <h1 data-test-subj="header-page-title">
-              {!draggableArguments ? (
-                title
-              ) : (
-                <DefaultDraggable
-                  data-test-subj="header-page-draggable"
-                  id={`header-page-draggable-${draggableArguments.field}-${draggableArguments.value}`}
-                  field={draggableArguments.field}
-                  value={`${draggableArguments.value}`}
-                />
-              )}
-              {badgeOptions && (
-                <>
-                  {' '}
-                  {badgeOptions.beta ? (
-                    <StyledEuiBetaBadge
-                      label={badgeOptions.text}
-                      tooltipContent={badgeOptions.tooltip}
-                      tooltipPosition="bottom"
-                    />
-                  ) : (
-                    <Badge color="hollow">{badgeOptions.text}</Badge>
-                  )}
-                </>
-              )}
-              {iconAction && (
-                <StyledEuiButtonIcon
-                  aria-label={iconAction['aria-label']}
-                  iconType={iconAction.iconType}
-                  onClick={() => iconAction.onClick(true)}
-                />
-              )}
-            </h1>
-          </EuiTitle>
-        )}
-
-        {subtitle && <Subtitle data-test-subj="header-page-subtitle" items={subtitle} />}
-        {subtitle2 && <Subtitle data-test-subj="header-page-subtitle-2" items={subtitle2} />}
-        {border && isLoading && <EuiProgress size="xs" color="accent" />}
-      </FlexItem>
-
-      {children && <FlexItem data-test-subj="header-page-supplements">{children}</FlexItem>}
-    </EuiFlexGroup>
-  </Header>
-);
-
-export const HeaderPage = React.memo(HeaderPageComponent);
diff --git a/x-pack/legacy/plugins/siem/public/components/header_page_new/translations.ts b/x-pack/legacy/plugins/siem/public/components/header_page_new/translations.ts
deleted file mode 100644
index 57b2cda0b0b01..0000000000000
--- a/x-pack/legacy/plugins/siem/public/components/header_page_new/translations.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { i18n } from '@kbn/i18n';
-
-export const SUBMIT = i18n.translate('xpack.siem.case.casePage.title.submit', {
-  defaultMessage: 'Submit',
-});
-
-export const CANCEL = i18n.translate('xpack.siem.case.casePage.title.cancel', {
-  defaultMessage: 'Cancel',
-});
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.tsx
index 4f43a6edeeac6..a92cf99097fce 100644
--- a/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.tsx
@@ -24,7 +24,8 @@ import { DescriptionMarkdown } from '../description_md_editor';
 import { Case } from '../../../../containers/case/types';
 import { FormattedRelativePreferenceDate } from '../../../../components/formatted_date';
 import { getCaseUrl } from '../../../../components/link_to';
-import { HeaderPage } from '../../../../components/header_page_new';
+import { HeaderPage } from '../../../../components/header_page';
+import { EditableTitle } from '../../../../components/header_page/editable_title';
 import { Markdown } from '../../../../components/markdown';
 import { PropertyActions } from '../property_actions';
 import { TagList } from '../tag_list';
@@ -50,6 +51,7 @@ const MyDescriptionList = styled(EuiDescriptionList)`
 const MyWrapper = styled(WrapperPage)`
   padding-bottom: 0;
 `;
+
 const BackgroundWrapper = styled.div`
   ${({ theme }) => css`
     background-color: ${theme.eui.euiColorEmptyShade};
@@ -67,7 +69,6 @@ interface CasesProps {
 export const Cases = React.memo<CasesProps>(({ caseId, initialData, isLoading }) => {
   const [{ data }, dispatchUpdateCaseProperty] = useUpdateCase(caseId, initialData);
   const [isEditDescription, setIsEditDescription] = useState(false);
-  const [isEditTitle, setIsEditTitle] = useState(false);
   const [isEditTags, setIsEditTags] = useState(false);
   const [isCaseOpen, setIsCaseOpen] = useState(data.state === 'open');
   const [description, setDescription] = useState(data.description);
@@ -83,7 +84,6 @@ export const Cases = React.memo<CasesProps>(({ caseId, initialData, isLoading })
               updateKey: 'title',
               updateValue,
             });
-            setIsEditTitle(false);
           }
           break;
         case 'description':
@@ -210,6 +210,17 @@ export const Cases = React.memo<CasesProps>(({ caseId, initialData, isLoading })
       ),
     },
   ];
+
+  const onSubmit = useCallback(
+    newTitle => {
+      onUpdateField('title', newTitle);
+      setTitle(newTitle);
+    },
+    [title]
+  );
+
+  const titleNode = <EditableTitle isLoading={isLoading} title={title} onSubmit={onSubmit} />;
+
   return (
     <>
       <MyWrapper>
@@ -218,14 +229,7 @@ export const Cases = React.memo<CasesProps>(({ caseId, initialData, isLoading })
             href: getCaseUrl(),
             text: i18n.BACK_TO_ALL,
           }}
-          iconAction={{
-            'aria-label': title,
-            iconType: 'pencil',
-            onChange: newTitle => setTitle(newTitle),
-            onSubmit: () => onUpdateField('title', title),
-            onClick: isEdit => setIsEditTitle(isEdit),
-          }}
-          isEditTitle={isEditTitle}
+          titleNode={titleNode}
           title={title}
         >
           <EuiFlexGroup gutterSize="l" justifyContent="flexEnd">

From e1fa139976850acc40d06fcfecfbac6e27d74955 Mon Sep 17 00:00:00 2001
From: Liza Katz <liza.katz@elastic.co>
Date: Wed, 19 Feb 2020 19:10:32 +0000
Subject: [PATCH 076/174] Allow custom paths in plugin generator (#57766)

* Allow custom paths

* Add translation file

* Fix jest test

* Added more tests

* Update docs
Content of translation jsons

* Leave only one translation file

* generate default translations file

* Simplify i18n setup

* Code review changes

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 packages/kbn-plugin-generator/index.js        |   8 +-
 .../kbn-plugin-generator/sao_template/sao.js  | 107 ++++++++++++++----
 .../sao_template/sao.test.js                  |  32 ++++++
 .../sao_template/template/i18nrc.json         |  10 ++
 .../template/translations/ja-JP.json          |  82 ++++++++++++++
 5 files changed, 209 insertions(+), 30 deletions(-)
 create mode 100644 packages/kbn-plugin-generator/sao_template/template/i18nrc.json
 create mode 100644 packages/kbn-plugin-generator/sao_template/template/translations/ja-JP.json

diff --git a/packages/kbn-plugin-generator/index.js b/packages/kbn-plugin-generator/index.js
index 15adce7f01c8e..5f20569886d88 100644
--- a/packages/kbn-plugin-generator/index.js
+++ b/packages/kbn-plugin-generator/index.js
@@ -44,19 +44,14 @@ exports.run = function run(argv) {
         # {dim Usage:} 
         node scripts/generate-plugin {bold [name]}
         Generate a fresh Kibana plugin in the plugins/ directory
-        
-        # {dim Core Kibana plugins:}
-        node scripts/generate-plugin {bold [name]} -i
-        To generate a core Kibana plugin inside the src/plugins/ directory, add the -i flag.
       `) + '\n'
     );
     process.exit(1);
   }
 
   const name = options._[0];
-  const isKibanaPlugin = options.internal;
   const template = resolve(__dirname, './sao_template');
-  const kibanaPlugins = resolve(__dirname, isKibanaPlugin ? '../../src/plugins' : '../../plugins');
+  const kibanaPlugins = resolve(process.cwd(), 'plugins');
   const targetPath = resolve(kibanaPlugins, snakeCase(name));
 
   sao({
@@ -64,7 +59,6 @@ exports.run = function run(argv) {
     targetPath: targetPath,
     configOptions: {
       name,
-      isKibanaPlugin,
       targetPath,
     },
   }).catch(error => {
diff --git a/packages/kbn-plugin-generator/sao_template/sao.js b/packages/kbn-plugin-generator/sao_template/sao.js
index aed4b9a02838f..8e5e106190194 100755
--- a/packages/kbn-plugin-generator/sao_template/sao.js
+++ b/packages/kbn-plugin-generator/sao_template/sao.js
@@ -17,7 +17,8 @@
  * under the License.
  */
 
-const { relative } = require('path');
+const { relative, resolve } = require('path');
+const fs = require('fs');
 
 const startCase = require('lodash.startcase');
 const camelCase = require('lodash.camelcase');
@@ -29,9 +30,55 @@ const pkg = require('../package.json');
 const kibanaPkgPath = require.resolve('../../../package.json');
 const kibanaPkg = require(kibanaPkgPath); // eslint-disable-line import/no-dynamic-require
 
-module.exports = function({ name, targetPath, isKibanaPlugin }) {
+async function gitInit(dir) {
+  // Only plugins in /plugins get git init
+  try {
+    await execa('git', ['init', dir]);
+    console.log(`Git repo initialized in ${dir}`);
+  } catch (error) {
+    console.error(error);
+    throw new Error(`Failure to git init ${dir}: ${error.all || error}`);
+  }
+}
+
+async function moveToCustomFolder(from, to) {
+  try {
+    await execa('mv', [from, to]);
+  } catch (error) {
+    console.error(error);
+    throw new Error(`Failure to move plugin to ${to}: ${error.all || error}`);
+  }
+}
+
+async function eslintPlugin(dir) {
+  try {
+    await execa('yarn', ['lint:es', `./${dir}/**/*.ts*`, '--no-ignore', '--fix']);
+  } catch (error) {
+    console.error(error);
+    throw new Error(`Failure when running prettier on the generated output: ${error.all || error}`);
+  }
+}
+
+module.exports = function({ name, targetPath }) {
   return {
     prompts: {
+      customPath: {
+        message: 'Would you like to create the plugin in a different folder?',
+        default: '/plugins',
+        filter(value) {
+          // Keep default value empty
+          if (value === '/plugins') return '';
+          // Remove leading slash
+          return value.startsWith('/') ? value.slice(1) : value;
+        },
+        validate(customPath) {
+          const p = resolve(process.cwd(), customPath);
+          const exists = fs.existsSync(p);
+          if (!exists)
+            return `Folder should exist relative to the kibana root folder. Consider /src/plugins or /x-pack/plugins.`;
+          return true;
+        },
+      },
       description: {
         message: 'Provide a short description',
         default: 'An awesome Kibana plugin',
@@ -50,11 +97,18 @@ module.exports = function({ name, targetPath, isKibanaPlugin }) {
         message: 'Should a server API be generated?',
         default: true,
       },
-      // generateTranslations: {
-      //   type: 'confirm',
-      //   message: 'Should translation files be generated?',
-      //   default: true,
-      // },
+      generateTranslations: {
+        type: 'confirm',
+        when: answers => {
+          // only for 3rd party plugins
+          return !answers.customPath && answers.generateApp;
+        },
+        message: 'Should translation files be generated?',
+        default({ customPath }) {
+          // only for 3rd party plugins
+          return !customPath;
+        },
+      },
       generateScss: {
         type: 'confirm',
         message: 'Should SCSS be used?',
@@ -64,19 +118,22 @@ module.exports = function({ name, targetPath, isKibanaPlugin }) {
       generateEslint: {
         type: 'confirm',
         message: 'Would you like to use a custom eslint file?',
-        default: !isKibanaPlugin,
+        default({ customPath }) {
+          return !customPath;
+        },
       },
     },
     filters: {
       'public/**/index.scss': 'generateScss',
       'public/**/*': 'generateApp',
       'server/**/*': 'generateApi',
-      // 'translations/**/*': 'generateTranslations',
-      // '.i18nrc.json': 'generateTranslations',
+      'translations/**/*': 'generateTranslations',
+      'i18nrc.json': 'generateTranslations',
       'eslintrc.js': 'generateEslint',
     },
     move: {
       'eslintrc.js': '.eslintrc.js',
+      'i18nrc.json': '.i18nrc.json',
     },
     data: answers =>
       Object.assign(
@@ -86,31 +143,35 @@ module.exports = function({ name, targetPath, isKibanaPlugin }) {
           camelCase,
           snakeCase,
           name,
-          isKibanaPlugin,
+          // kibana plugins are placed in a the non default path
+          isKibanaPlugin: !answers.customPath,
           kbnVersion: answers.kbnVersion,
           upperCamelCaseName: name.charAt(0).toUpperCase() + camelCase(name).slice(1),
           hasUi: !!answers.generateApp,
           hasServer: !!answers.generateApi,
           hasScss: !!answers.generateScss,
-          relRoot: isKibanaPlugin ? '../../../..' : '../../..',
+          relRoot: relative(
+            resolve(answers.customPath || targetPath, name, 'public'),
+            process.cwd()
+          ),
         },
         answers
       ),
     enforceNewFolder: true,
     installDependencies: false,
-    gitInit: !isKibanaPlugin,
-    async post({ log }) {
-      const dir = relative(process.cwd(), targetPath);
+    async post({ log, answers }) {
+      let dir = relative(process.cwd(), targetPath);
+      if (answers.customPath) {
+        // Move to custom path
+        moveToCustomFolder(targetPath, answers.customPath);
+        dir = relative(process.cwd(), resolve(answers.customPath, snakeCase(name)));
+      } else {
+        // Init git only in the default path
+        await gitInit(dir);
+      }
 
       // Apply eslint to the generated plugin
-      try {
-        await execa('yarn', ['lint:es', `./${dir}/**/*.ts*`, '--no-ignore', '--fix']);
-      } catch (error) {
-        console.error(error);
-        throw new Error(
-          `Failure when running prettier on the generated output: ${error.all || error}`
-        );
-      }
+      eslintPlugin(dir);
 
       log.success(chalk`🎉
 
diff --git a/packages/kbn-plugin-generator/sao_template/sao.test.js b/packages/kbn-plugin-generator/sao_template/sao.test.js
index 0dbdb7d3c097b..03d95e12d58da 100755
--- a/packages/kbn-plugin-generator/sao_template/sao.test.js
+++ b/packages/kbn-plugin-generator/sao_template/sao.test.js
@@ -23,6 +23,7 @@ const template = {
   fromPath: __dirname,
   configOptions: {
     name: 'Some fancy plugin',
+    targetPath: '',
   },
 };
 
@@ -46,6 +47,7 @@ describe('plugin generator sao integration', () => {
     const res = await sao.mockPrompt(template, {
       generateApp: true,
       generateApi: false,
+      generateScss: true,
     });
 
     // check output files
@@ -54,6 +56,7 @@ describe('plugin generator sao integration', () => {
     expect(res.fileList).toContain('public/plugin.ts');
     expect(res.fileList).toContain('public/types.ts');
     expect(res.fileList).toContain('public/components/app.tsx');
+    expect(res.fileList).toContain('public/index.scss');
     expect(res.fileList).not.toContain('server/index.ts');
   });
 
@@ -71,6 +74,20 @@ describe('plugin generator sao integration', () => {
     expect(res.fileList).toContain('server/routes/index.ts');
   });
 
+  it('skips eslintrc and scss', async () => {
+    const res = await sao.mockPrompt(template, {
+      generateApp: true,
+      generateApi: true,
+      generateScss: false,
+      generateEslint: false,
+    });
+
+    // check output files
+    expect(res.fileList).toContain('public/plugin.ts');
+    expect(res.fileList).not.toContain('public/index.scss');
+    expect(res.fileList).not.toContain('.eslintrc.js');
+  });
+
   it('plugin package has correct title', async () => {
     const res = await sao.mockPrompt(template, {
       generateApp: true,
@@ -120,5 +137,20 @@ describe('plugin generator sao integration', () => {
   it('includes dotfiles', async () => {
     const res = await sao.mockPrompt(template);
     expect(res.files['.eslintrc.js']).toBeTruthy();
+    expect(res.files['.i18nrc.json']).toBeTruthy();
+  });
+
+  it('validaes path override', async () => {
+    try {
+      await sao.mockPrompt(template, {
+        generateApp: true,
+        generateApi: true,
+        generateScss: false,
+        generateEslint: false,
+        customPath: 'banana',
+      });
+    } catch (e) {
+      expect(e.message).toContain('Validation failed at prompt "customPath"');
+    }
   });
 });
diff --git a/packages/kbn-plugin-generator/sao_template/template/i18nrc.json b/packages/kbn-plugin-generator/sao_template/template/i18nrc.json
new file mode 100644
index 0000000000000..a4b78b88e64e2
--- /dev/null
+++ b/packages/kbn-plugin-generator/sao_template/template/i18nrc.json
@@ -0,0 +1,10 @@
+{
+    "prefix": "<%= camelCase(name) %>",
+    "paths": {
+      "<%= camelCase(name) %>": "."
+    },
+    "translations": [
+      "translations/ja-JP.json"
+    ]
+  }
+  
\ No newline at end of file
diff --git a/packages/kbn-plugin-generator/sao_template/template/translations/ja-JP.json b/packages/kbn-plugin-generator/sao_template/template/translations/ja-JP.json
new file mode 100644
index 0000000000000..ab4503f2c129c
--- /dev/null
+++ b/packages/kbn-plugin-generator/sao_template/template/translations/ja-JP.json
@@ -0,0 +1,82 @@
+
+{
+    "formats": {
+      "number": {
+        "currency": {
+          "style": "currency"
+        },
+        "percent": {
+          "style": "percent"
+        }
+      },
+      "date": {
+        "short": {
+          "month": "numeric",
+          "day": "numeric",
+          "year": "2-digit"
+        },
+        "medium": {
+          "month": "short",
+          "day": "numeric",
+          "year": "numeric"
+        },
+        "long": {
+          "month": "long",
+          "day": "numeric",
+          "year": "numeric"
+        },
+        "full": {
+          "weekday": "long",
+          "month": "long",
+          "day": "numeric",
+          "year": "numeric"
+        }
+      },
+      "time": {
+        "short": {
+          "hour": "numeric",
+          "minute": "numeric"
+        },
+        "medium": {
+          "hour": "numeric",
+          "minute": "numeric",
+          "second": "numeric"
+        },
+        "long": {
+          "hour": "numeric",
+          "minute": "numeric",
+          "second": "numeric",
+          "timeZoneName": "short"
+        },
+        "full": {
+          "hour": "numeric",
+          "minute": "numeric",
+          "second": "numeric",
+          "timeZoneName": "short"
+        }
+      },
+      "relative": {
+        "years": {
+          "units": "year"
+        },
+        "months": {
+          "units": "month"
+        },
+        "days": {
+          "units": "day"
+        },
+        "hours": {
+          "units": "hour"
+        },
+        "minutes": {
+          "units": "minute"
+        },
+        "seconds": {
+          "units": "second"
+        }
+      }
+    },
+    "messages": {
+      "<%= camelCase(name) %>.buttonText": "Translate me to Japanese",
+    }
+}
\ No newline at end of file

From 59467290970579e20f382b3dc23e22485675fc4c Mon Sep 17 00:00:00 2001
From: Nicolas Chaulet <nicolas.chaulet@elastic.co>
Date: Wed, 19 Feb 2020 14:52:09 -0500
Subject: [PATCH 077/174] Fix useRequest to support query change (#57723)

---
 .../public/request/np_ready_request.ts        | 24 +++++++++++++------
 .../public/request/request.test.js            | 10 ++++----
 2 files changed, 22 insertions(+), 12 deletions(-)

diff --git a/src/plugins/es_ui_shared/public/request/np_ready_request.ts b/src/plugins/es_ui_shared/public/request/np_ready_request.ts
index d7532553f45f9..237e50e894aa3 100644
--- a/src/plugins/es_ui_shared/public/request/np_ready_request.ts
+++ b/src/plugins/es_ui_shared/public/request/np_ready_request.ts
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-import { useEffect, useState, useRef } from 'react';
+import { useEffect, useState, useRef, useMemo } from 'react';
 
 import { HttpSetup, HttpFetchQuery } from '../../../../../src/core/public';
 
@@ -78,6 +78,7 @@ export const useRequest = <D = any>(
     deserializer = (data: any): any => data,
   }: UseRequestConfig
 ): UseRequestResponse<D> => {
+  const sendRequestRef = useRef<() => Promise<SendRequestResponse<D>>>();
   // Main states for tracking request status and data
   const [error, setError] = useState<null | any>(null);
   const [isLoading, setIsLoading] = useState<boolean>(true);
@@ -102,7 +103,10 @@ export const useRequest = <D = any>(
 
     // Set new interval
     if (pollInterval.current) {
-      pollIntervalId.current = setTimeout(_sendRequest, pollInterval.current);
+      pollIntervalId.current = setTimeout(
+        () => (sendRequestRef.current ?? _sendRequest)(),
+        pollInterval.current
+      );
     }
   };
 
@@ -145,11 +149,17 @@ export const useRequest = <D = any>(
   };
 
   useEffect(() => {
-    _sendRequest();
-    // To be functionally correct we'd send a new request if the method, path, or body changes.
+    sendRequestRef.current = _sendRequest;
+  }, [_sendRequest]);
+
+  const stringifiedQuery = useMemo(() => JSON.stringify(query), [query]);
+
+  useEffect(() => {
+    (sendRequestRef.current ?? _sendRequest)();
+    // To be functionally correct we'd send a new request if the method, path, query or body changes.
     // But it doesn't seem likely that the method will change and body is likely to be a new
-    // object even if its shape hasn't changed, so for now we're just watching the path.
-  }, [path]);
+    // object even if its shape hasn't changed, so for now we're just watching the path and the query.
+  }, [path, stringifiedQuery]);
 
   useEffect(() => {
     scheduleRequest();
@@ -168,6 +178,6 @@ export const useRequest = <D = any>(
     isLoading,
     error,
     data,
-    sendRequest: _sendRequest, // Gives the user the ability to manually request data
+    sendRequest: sendRequestRef.current ?? _sendRequest, // Gives the user the ability to manually request data
   };
 };
diff --git a/src/plugins/es_ui_shared/public/request/request.test.js b/src/plugins/es_ui_shared/public/request/request.test.js
index 8c7e2b6ddc72a..44bf149d5fd1e 100644
--- a/src/plugins/es_ui_shared/public/request/request.test.js
+++ b/src/plugins/es_ui_shared/public/request/request.test.js
@@ -71,7 +71,7 @@ describe.skip('request lib', () => {
     it('uses the provided path, method, and body to send the request', async () => {
       const response = await sendRequest({ ...successRequest });
       sinon.assert.calledOnce(sendPost);
-      expect(response).toEqual({ data: successResponse.data });
+      expect(response).toEqual({ data: successResponse.data, error: null });
     });
 
     it('surfaces errors', async () => {
@@ -182,11 +182,11 @@ describe.skip('request lib', () => {
           expect(hook.error).toBe(errorResponse);
         });
 
-        it('is undefined when the request is successful', async () => {
+        it('is null when the request is successful', async () => {
           initUseRequest({ ...successRequest });
           await wait(50);
           expect(hook.isLoading).toBe(false);
-          expect(hook.error).toBeUndefined();
+          expect(hook.error).toBeNull();
         });
       });
 
@@ -205,11 +205,11 @@ describe.skip('request lib', () => {
           expect(hook.data).toBe(successResponse.data);
         });
 
-        it('is undefined when the request fails', async () => {
+        it('is null when the request fails', async () => {
           initUseRequest({ ...errorRequest });
           await wait(50);
           expect(hook.isLoading).toBe(false);
-          expect(hook.data).toBeUndefined();
+          expect(hook.data).toBeNull();
         });
       });
     });

From 63cfffbe11c09388e600e67bafbd0aed3e893ece Mon Sep 17 00:00:00 2001
From: Stacey Gammon <gammon@elastic.co>
Date: Wed, 19 Feb 2020 15:16:58 -0500
Subject: [PATCH 078/174] Embeddable add panel examples (#57319)

* Embeddable add panel examples

* add tests

* Fix type error after merge

* address code review comments

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../multi_task_todo_embeddable_factory.ts     |  15 +-
 examples/embeddable_examples/public/plugin.ts |  29 +++-
 .../public/todo/todo_embeddable_factory.ts    |  40 -----
 .../public/todo/todo_embeddable_factory.tsx   |  92 ++++++++++
 examples/embeddable_explorer/kibana.json      |   2 +-
 examples/embeddable_explorer/public/app.tsx   |  69 +++++---
 .../public/embeddable_panel_example.tsx       | 164 ++++++++++++++++++
 .../public/list_container_example.tsx         |  11 +-
 .../embeddable_explorer/public/plugin.tsx     |  27 ++-
 src/plugins/embeddable/public/api/types.ts    |   5 +-
 test/examples/config.js                       |   7 +
 test/examples/embeddables/adding_children.ts  |  43 +++++
 test/examples/embeddables/index.ts            |   1 +
 test/examples/embeddables/list_container.ts   |   2 +-
 14 files changed, 428 insertions(+), 79 deletions(-)
 delete mode 100644 examples/embeddable_examples/public/todo/todo_embeddable_factory.ts
 create mode 100644 examples/embeddable_examples/public/todo/todo_embeddable_factory.tsx
 create mode 100644 examples/embeddable_explorer/public/embeddable_panel_example.tsx
 create mode 100644 test/examples/embeddables/adding_children.ts

diff --git a/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_embeddable_factory.ts b/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_embeddable_factory.ts
index 37ac63e380f96..a54201b157a6c 100644
--- a/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_embeddable_factory.ts
+++ b/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_embeddable_factory.ts
@@ -23,9 +23,13 @@ import {
   MultiTaskTodoEmbeddable,
   MULTI_TASK_TODO_EMBEDDABLE,
   MultiTaskTodoInput,
+  MultiTaskTodoOutput,
 } from './multi_task_todo_embeddable';
 
-export class MultiTaskTodoEmbeddableFactory extends EmbeddableFactory {
+export class MultiTaskTodoEmbeddableFactory extends EmbeddableFactory<
+  MultiTaskTodoInput,
+  MultiTaskTodoOutput
+> {
   public readonly type = MULTI_TASK_TODO_EMBEDDABLE;
 
   public isEditable() {
@@ -36,6 +40,15 @@ export class MultiTaskTodoEmbeddableFactory extends EmbeddableFactory {
     return new MultiTaskTodoEmbeddable(initialInput, parent);
   }
 
+  /**
+   * Check out todo_embeddable_factory for a better example that asks for data from
+   * the user. This just returns default data.  That's okay too though, if you want to
+   * start with default data and expose an "edit" action to modify it.
+   */
+  public async getExplicitInput() {
+    return { title: 'default title', tasks: ['Im default data'] };
+  }
+
   public getDisplayName() {
     return i18n.translate('embeddableExamples.multiTaskTodo.displayName', {
       defaultMessage: 'Multi-task todo item',
diff --git a/examples/embeddable_examples/public/plugin.ts b/examples/embeddable_examples/public/plugin.ts
index e75165bfbef18..b7a4f5c078d54 100644
--- a/examples/embeddable_examples/public/plugin.ts
+++ b/examples/embeddable_examples/public/plugin.ts
@@ -17,11 +17,20 @@
  * under the License.
  */
 
-import { IEmbeddableSetup, IEmbeddableStart } from '../../../src/plugins/embeddable/public';
+import {
+  IEmbeddableSetup,
+  IEmbeddableStart,
+  EmbeddableFactory,
+} from '../../../src/plugins/embeddable/public';
 import { Plugin, CoreSetup, CoreStart } from '../../../src/core/public';
 import { HelloWorldEmbeddableFactory, HELLO_WORLD_EMBEDDABLE } from './hello_world';
-import { TODO_EMBEDDABLE, TodoEmbeddableFactory } from './todo';
-import { MULTI_TASK_TODO_EMBEDDABLE, MultiTaskTodoEmbeddableFactory } from './multi_task_todo';
+import { TODO_EMBEDDABLE, TodoEmbeddableFactory, TodoInput, TodoOutput } from './todo';
+import {
+  MULTI_TASK_TODO_EMBEDDABLE,
+  MultiTaskTodoEmbeddableFactory,
+  MultiTaskTodoOutput,
+  MultiTaskTodoInput,
+} from './multi_task_todo';
 import {
   SEARCHABLE_LIST_CONTAINER,
   SearchableListContainerFactory,
@@ -45,12 +54,9 @@ export class EmbeddableExamplesPlugin
       new HelloWorldEmbeddableFactory()
     );
 
-    deps.embeddable.registerEmbeddableFactory(TODO_EMBEDDABLE, new TodoEmbeddableFactory());
-
-    deps.embeddable.registerEmbeddableFactory(
-      MULTI_TASK_TODO_EMBEDDABLE,
-      new MultiTaskTodoEmbeddableFactory()
-    );
+    deps.embeddable.registerEmbeddableFactory<
+      EmbeddableFactory<MultiTaskTodoInput, MultiTaskTodoOutput>
+    >(MULTI_TASK_TODO_EMBEDDABLE, new MultiTaskTodoEmbeddableFactory());
   }
 
   public start(core: CoreStart, deps: EmbeddableExamplesStartDependencies) {
@@ -66,6 +72,11 @@ export class EmbeddableExamplesPlugin
       LIST_CONTAINER,
       new ListContainerFactory(deps.embeddable.getEmbeddableFactory)
     );
+
+    deps.embeddable.registerEmbeddableFactory<EmbeddableFactory<TodoInput, TodoOutput>>(
+      TODO_EMBEDDABLE,
+      new TodoEmbeddableFactory(core.overlays.openModal)
+    );
   }
 
   public stop() {}
diff --git a/examples/embeddable_examples/public/todo/todo_embeddable_factory.ts b/examples/embeddable_examples/public/todo/todo_embeddable_factory.ts
deleted file mode 100644
index 386b3f296d998..0000000000000
--- a/examples/embeddable_examples/public/todo/todo_embeddable_factory.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { i18n } from '@kbn/i18n';
-import { IContainer, EmbeddableFactory } from '../../../../src/plugins/embeddable/public';
-import { TodoEmbeddable, TODO_EMBEDDABLE, TodoInput } from './todo_embeddable';
-
-export class TodoEmbeddableFactory extends EmbeddableFactory {
-  public readonly type = TODO_EMBEDDABLE;
-
-  public isEditable() {
-    return true;
-  }
-
-  public async create(initialInput: TodoInput, parent?: IContainer) {
-    return new TodoEmbeddable(initialInput, parent);
-  }
-
-  public getDisplayName() {
-    return i18n.translate('embeddableExamples.todo.displayName', {
-      defaultMessage: 'Todo item',
-    });
-  }
-}
diff --git a/examples/embeddable_examples/public/todo/todo_embeddable_factory.tsx b/examples/embeddable_examples/public/todo/todo_embeddable_factory.tsx
new file mode 100644
index 0000000000000..dd2168bb39eee
--- /dev/null
+++ b/examples/embeddable_examples/public/todo/todo_embeddable_factory.tsx
@@ -0,0 +1,92 @@
+/*
+ * 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, { useState } from 'react';
+import { EuiModalBody } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { OverlayStart } from 'kibana/public';
+import { EuiFieldText } from '@elastic/eui';
+import { EuiButton } from '@elastic/eui';
+import { toMountPoint } from '../../../../src/plugins/kibana_react/public';
+import { IContainer, EmbeddableFactory } from '../../../../src/plugins/embeddable/public';
+import { TodoEmbeddable, TODO_EMBEDDABLE, TodoInput, TodoOutput } from './todo_embeddable';
+
+function TaskInput({ onSave }: { onSave: (task: string) => void }) {
+  const [task, setTask] = useState('');
+  return (
+    <EuiModalBody>
+      <EuiFieldText
+        data-test-subj="taskInputField"
+        value={task}
+        placeholder="Enter task here"
+        onChange={e => setTask(e.target.value)}
+      />
+      <EuiButton data-test-subj="createTodoEmbeddable" onClick={() => onSave(task)}>
+        Save
+      </EuiButton>
+    </EuiModalBody>
+  );
+}
+
+export class TodoEmbeddableFactory extends EmbeddableFactory<
+  TodoInput,
+  TodoOutput,
+  TodoEmbeddable
+> {
+  public readonly type = TODO_EMBEDDABLE;
+
+  constructor(private openModal: OverlayStart['openModal']) {
+    super();
+  }
+
+  public isEditable() {
+    return true;
+  }
+
+  public async create(initialInput: TodoInput, parent?: IContainer) {
+    return new TodoEmbeddable(initialInput, parent);
+  }
+
+  /**
+   * This function is used when dynamically creating a new embeddable to add to a
+   * container. Some input may be inherited from the container, but not all. This can be
+   * used to collect specific embeddable input that the container will not provide, like
+   * in this case, the task string.
+   */
+  public async getExplicitInput() {
+    return new Promise<{ task: string }>(resolve => {
+      const onSave = (task: string) => resolve({ task });
+      const overlay = this.openModal(
+        toMountPoint(
+          <TaskInput
+            onSave={(task: string) => {
+              onSave(task);
+              overlay.close();
+            }}
+          />
+        )
+      );
+    });
+  }
+
+  public getDisplayName() {
+    return i18n.translate('embeddableExamples.todo.displayName', {
+      defaultMessage: 'Todo item',
+    });
+  }
+}
diff --git a/examples/embeddable_explorer/kibana.json b/examples/embeddable_explorer/kibana.json
index 4ca63e1a36242..6c27bcd39f12d 100644
--- a/examples/embeddable_explorer/kibana.json
+++ b/examples/embeddable_explorer/kibana.json
@@ -5,6 +5,6 @@
   "configPath": ["embeddable_explorer"],
   "server": false,
   "ui": true,
-  "requiredPlugins": ["embeddable", "embeddableExamples"],
+  "requiredPlugins": ["uiActions", "inspector", "embeddable", "embeddableExamples"],
   "optionalPlugins": []
 }
diff --git a/examples/embeddable_explorer/public/app.tsx b/examples/embeddable_explorer/public/app.tsx
index be27fd04556ea..da7e8cc188e31 100644
--- a/examples/embeddable_explorer/public/app.tsx
+++ b/examples/embeddable_explorer/public/app.tsx
@@ -23,11 +23,21 @@ import { BrowserRouter as Router, Route, withRouter, RouteComponentProps } from
 
 import { EuiPage, EuiPageSideBar, EuiSideNav } from '@elastic/eui';
 
-import { IEmbeddableStart } from 'src/plugins/embeddable/public';
-import { AppMountContext, AppMountParameters, CoreStart } from '../../../src/core/public';
+import { IEmbeddableStart } from '../../../src/plugins/embeddable/public';
+import { UiActionsStart } from '../../../src/plugins/ui_actions/public';
+import { Start as InspectorStartContract } from '../../../src/plugins/inspector/public';
+import {
+  AppMountContext,
+  AppMountParameters,
+  CoreStart,
+  SavedObjectsStart,
+  IUiSettingsClient,
+  OverlayStart,
+} from '../../../src/core/public';
 import { HelloWorldEmbeddableExample } from './hello_world_embeddable_example';
 import { TodoEmbeddableExample } from './todo_embeddable_example';
 import { ListContainerExample } from './list_container_example';
+import { EmbeddablePanelExample } from './embeddable_panel_example';
 
 interface PageDef {
   title: string;
@@ -61,15 +71,29 @@ const Nav = withRouter(({ history, navigateToApp, pages }: NavProps) => {
   );
 });
 
+interface Props {
+  basename: string;
+  navigateToApp: CoreStart['application']['navigateToApp'];
+  embeddableApi: IEmbeddableStart;
+  uiActionsApi: UiActionsStart;
+  overlays: OverlayStart;
+  notifications: CoreStart['notifications'];
+  inspector: InspectorStartContract;
+  savedObject: SavedObjectsStart;
+  uiSettingsClient: IUiSettingsClient;
+}
+
 const EmbeddableExplorerApp = ({
   basename,
   navigateToApp,
   embeddableApi,
-}: {
-  basename: string;
-  navigateToApp: CoreStart['application']['navigateToApp'];
-  embeddableApi: IEmbeddableStart;
-}) => {
+  inspector,
+  uiSettingsClient,
+  savedObject,
+  overlays,
+  uiActionsApi,
+  notifications,
+}: Props) => {
   const pages: PageDef[] = [
     {
       title: 'Hello world embeddable',
@@ -90,6 +114,22 @@ const EmbeddableExplorerApp = ({
       id: 'listContainerSection',
       component: <ListContainerExample getEmbeddableFactory={embeddableApi.getEmbeddableFactory} />,
     },
+    {
+      title: 'Dynamically adding children to a container',
+      id: 'embeddablePanelExamplae',
+      component: (
+        <EmbeddablePanelExample
+          uiActionsApi={uiActionsApi}
+          getAllEmbeddableFactories={embeddableApi.getEmbeddableFactories}
+          getEmbeddableFactory={embeddableApi.getEmbeddableFactory}
+          overlays={overlays}
+          uiSettingsClient={uiSettingsClient}
+          savedObject={savedObject}
+          notifications={notifications}
+          inspector={inspector}
+        />
+      ),
+    },
   ];
 
   const routes = pages.map((page, i) => (
@@ -108,19 +148,8 @@ const EmbeddableExplorerApp = ({
   );
 };
 
-export const renderApp = (
-  core: CoreStart,
-  embeddableApi: IEmbeddableStart,
-  { appBasePath, element }: AppMountParameters
-) => {
-  ReactDOM.render(
-    <EmbeddableExplorerApp
-      basename={appBasePath}
-      navigateToApp={core.application.navigateToApp}
-      embeddableApi={embeddableApi}
-    />,
-    element
-  );
+export const renderApp = (props: Props, element: AppMountParameters['element']) => {
+  ReactDOM.render(<EmbeddableExplorerApp {...props} />, element);
 
   return () => ReactDOM.unmountComponentAtNode(element);
 };
diff --git a/examples/embeddable_explorer/public/embeddable_panel_example.tsx b/examples/embeddable_explorer/public/embeddable_panel_example.tsx
new file mode 100644
index 0000000000000..e6687d8563f59
--- /dev/null
+++ b/examples/embeddable_explorer/public/embeddable_panel_example.tsx
@@ -0,0 +1,164 @@
+/*
+ * 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, { useState, useEffect, useRef } from 'react';
+import {
+  EuiPanel,
+  EuiPageBody,
+  EuiPageContent,
+  EuiPageContentBody,
+  EuiPageHeader,
+  EuiPageHeaderSection,
+  EuiTitle,
+  EuiText,
+} from '@elastic/eui';
+import { EuiSpacer } from '@elastic/eui';
+import { OverlayStart, CoreStart, SavedObjectsStart, IUiSettingsClient } from 'kibana/public';
+import {
+  GetEmbeddableFactory,
+  EmbeddablePanel,
+  IEmbeddableStart,
+  IEmbeddable,
+} from '../../../src/plugins/embeddable/public';
+import {
+  HELLO_WORLD_EMBEDDABLE,
+  TODO_EMBEDDABLE,
+  MULTI_TASK_TODO_EMBEDDABLE,
+  SEARCHABLE_LIST_CONTAINER,
+} from '../../embeddable_examples/public';
+import { UiActionsStart } from '../../../src/plugins/ui_actions/public';
+import { Start as InspectorStartContract } from '../../../src/plugins/inspector/public';
+import { getSavedObjectFinder } from '../../../src/plugins/saved_objects/public';
+
+interface Props {
+  getAllEmbeddableFactories: IEmbeddableStart['getEmbeddableFactories'];
+  getEmbeddableFactory: GetEmbeddableFactory;
+  uiActionsApi: UiActionsStart;
+  overlays: OverlayStart;
+  notifications: CoreStart['notifications'];
+  inspector: InspectorStartContract;
+  savedObject: SavedObjectsStart;
+  uiSettingsClient: IUiSettingsClient;
+}
+
+export function EmbeddablePanelExample({
+  inspector,
+  notifications,
+  overlays,
+  getAllEmbeddableFactories,
+  getEmbeddableFactory,
+  uiActionsApi,
+  savedObject,
+  uiSettingsClient,
+}: Props) {
+  const searchableInput = {
+    id: '1',
+    title: 'My searchable todo list',
+    panels: {
+      '1': {
+        type: HELLO_WORLD_EMBEDDABLE,
+        explicitInput: {
+          id: '1',
+          title: 'Hello',
+        },
+      },
+      '2': {
+        type: TODO_EMBEDDABLE,
+        explicitInput: {
+          id: '2',
+          task: 'Goes out on Wednesdays!',
+          icon: 'broom',
+          title: 'Take out the trash',
+        },
+      },
+      '3': {
+        type: MULTI_TASK_TODO_EMBEDDABLE,
+        explicitInput: {
+          id: '3',
+          icon: 'searchProfilerApp',
+          title: 'Learn more',
+          tasks: ['Go to school', 'Watch planet earth', 'Read the encyclopedia'],
+        },
+      },
+    },
+  };
+
+  const [embeddable, setEmbeddable] = useState<IEmbeddable | undefined>(undefined);
+
+  const ref = useRef(false);
+
+  useEffect(() => {
+    ref.current = true;
+    if (!embeddable) {
+      const factory = getEmbeddableFactory(SEARCHABLE_LIST_CONTAINER);
+      const promise = factory?.create(searchableInput);
+      if (promise) {
+        promise.then(e => {
+          if (ref.current) {
+            setEmbeddable(e);
+          }
+        });
+      }
+    }
+    return () => {
+      ref.current = false;
+    };
+  });
+
+  return (
+    <EuiPageBody>
+      <EuiPageHeader>
+        <EuiPageHeaderSection>
+          <EuiTitle size="l">
+            <h1>The embeddable panel component</h1>
+          </EuiTitle>
+        </EuiPageHeaderSection>
+      </EuiPageHeader>
+      <EuiPageContent>
+        <EuiPageContentBody>
+          <EuiText>
+            You can render your embeddable inside the EmbeddablePanel component. This adds some
+            extra rendering and offers a context menu with pluggable actions. Using EmbeddablePanel
+            to render your embeddable means you get access to the &quote;Add panel flyout&quote;.
+            Now you can see how to add embeddables to your container, and how
+            &quote;getExplicitInput&quote; is used to grab input not provided by the container.
+          </EuiText>
+          <EuiPanel data-test-subj="embeddedPanelExample" paddingSize="none" role="figure">
+            {embeddable ? (
+              <EmbeddablePanel
+                embeddable={embeddable}
+                getActions={uiActionsApi.getTriggerCompatibleActions}
+                getEmbeddableFactory={getEmbeddableFactory}
+                getAllEmbeddableFactories={getAllEmbeddableFactories}
+                overlays={overlays}
+                notifications={notifications}
+                inspector={inspector}
+                SavedObjectFinder={getSavedObjectFinder(savedObject, uiSettingsClient)}
+              />
+            ) : (
+              <EuiText>Loading...</EuiText>
+            )}
+          </EuiPanel>
+
+          <EuiSpacer />
+        </EuiPageContentBody>
+      </EuiPageContent>
+    </EuiPageBody>
+  );
+}
diff --git a/examples/embeddable_explorer/public/list_container_example.tsx b/examples/embeddable_explorer/public/list_container_example.tsx
index 49cfae0d4e455..2c7b12a27d963 100644
--- a/examples/embeddable_explorer/public/list_container_example.tsx
+++ b/examples/embeddable_explorer/public/list_container_example.tsx
@@ -60,7 +60,7 @@ export function ListContainerExample({ getEmbeddableFactory }: Props) {
         type: TODO_EMBEDDABLE,
         explicitInput: {
           id: '2',
-          task: 'Goes out on Wenesdays!',
+          task: 'Goes out on Wednesdays!',
           icon: 'broom',
           title: 'Take out the trash',
         },
@@ -91,7 +91,7 @@ export function ListContainerExample({ getEmbeddableFactory }: Props) {
         type: TODO_EMBEDDABLE,
         explicitInput: {
           id: '2',
-          task: 'Goes out on Wenesdays!',
+          task: 'Goes out on Wednesdays!',
           icon: 'broom',
           title: 'Take out the trash',
         },
@@ -102,7 +102,7 @@ export function ListContainerExample({ getEmbeddableFactory }: Props) {
           id: '3',
           icon: 'searchProfilerApp',
           title: 'Learn more',
-          tasks: ['Go to school', 'Watch planet earth', 'Read the encylopedia'],
+          tasks: ['Go to school', 'Watch planet earth', 'Read the encyclopedia'],
         },
       },
     },
@@ -151,6 +151,11 @@ export function ListContainerExample({ getEmbeddableFactory }: Props) {
               The first HelloWorldEmbeddable does not emit the hasMatch output variable, so the
               container chooses to hide it.
             </p>
+
+            <p>
+              Check out the &quote;Dynamically adding children&quote; section, to see how to add
+              children to this container, and see it rendered inside an `EmbeddablePanel` component.
+            </p>
           </EuiText>
 
           <EuiSpacer />
diff --git a/examples/embeddable_explorer/public/plugin.tsx b/examples/embeddable_explorer/public/plugin.tsx
index 2576dea0cadbc..1294e0c89c9e7 100644
--- a/examples/embeddable_explorer/public/plugin.tsx
+++ b/examples/embeddable_explorer/public/plugin.tsx
@@ -18,17 +18,38 @@
  */
 
 import { Plugin, CoreSetup, AppMountParameters } from 'kibana/public';
+import { UiActionsService } from '../../../src/plugins/ui_actions/public';
 import { IEmbeddableStart } from '../../../src/plugins/embeddable/public';
+import { Start as InspectorStart } from '../../../src/plugins/inspector/public';
 
-export class EmbeddableExplorerPlugin implements Plugin {
-  public setup(core: CoreSetup<{ embeddable: IEmbeddableStart }>) {
+interface StartDeps {
+  uiActions: UiActionsService;
+  embeddable: IEmbeddableStart;
+  inspector: InspectorStart;
+}
+
+export class EmbeddableExplorerPlugin implements Plugin<void, void, {}, StartDeps> {
+  public setup(core: CoreSetup<StartDeps>) {
     core.application.register({
       id: 'embeddableExplorer',
       title: 'Embeddable explorer',
       async mount(params: AppMountParameters) {
         const [coreStart, depsStart] = await core.getStartServices();
         const { renderApp } = await import('./app');
-        return renderApp(coreStart, depsStart.embeddable, params);
+        return renderApp(
+          {
+            notifications: coreStart.notifications,
+            inspector: depsStart.inspector,
+            embeddableApi: depsStart.embeddable,
+            uiActionsApi: depsStart.uiActions,
+            basename: params.appBasePath,
+            uiSettingsClient: coreStart.uiSettings,
+            savedObject: coreStart.savedObjects,
+            overlays: coreStart.overlays,
+            navigateToApp: coreStart.application.navigateToApp,
+          },
+          params.element
+        );
       },
     });
   }
diff --git a/src/plugins/embeddable/public/api/types.ts b/src/plugins/embeddable/public/api/types.ts
index 30fa495785412..179d96a4aff8c 100644
--- a/src/plugins/embeddable/public/api/types.ts
+++ b/src/plugins/embeddable/public/api/types.ts
@@ -24,7 +24,10 @@ export interface EmbeddableApi {
   getEmbeddableFactory: (embeddableFactoryId: string) => EmbeddableFactory;
   getEmbeddableFactories: GetEmbeddableFactories;
   // TODO: Make `registerEmbeddableFactory` receive only `factory` argument.
-  registerEmbeddableFactory: (id: string, factory: EmbeddableFactory) => void;
+  registerEmbeddableFactory: <TEmbeddableFactory extends EmbeddableFactory>(
+    id: string,
+    factory: TEmbeddableFactory
+  ) => void;
 }
 
 export interface EmbeddableDependencies {
diff --git a/test/examples/config.js b/test/examples/config.js
index d9411be267930..49d75da286075 100644
--- a/test/examples/config.js
+++ b/test/examples/config.js
@@ -33,6 +33,13 @@ export default async function({ readConfigFile }) {
       ...functionalConfig.get('services'),
       ...services,
     },
+    uiSettings: {
+      defaults: {
+        'accessibility:disableAnimations': true,
+        'dateFormat:tz': 'UTC',
+        'telemetry:optIn': false,
+      },
+    },
     pageObjects: functionalConfig.get('pageObjects'),
     servers: functionalConfig.get('servers'),
     esTestCluster: functionalConfig.get('esTestCluster'),
diff --git a/test/examples/embeddables/adding_children.ts b/test/examples/embeddables/adding_children.ts
new file mode 100644
index 0000000000000..8f4951b0e22fe
--- /dev/null
+++ b/test/examples/embeddables/adding_children.ts
@@ -0,0 +1,43 @@
+/*
+ * 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 expect from '@kbn/expect';
+import { PluginFunctionalProviderContext } from 'test/plugin_functional/services';
+
+// eslint-disable-next-line import/no-default-export
+export default function({ getService }: PluginFunctionalProviderContext) {
+  const testSubjects = getService('testSubjects');
+
+  describe('creating and adding children', () => {
+    before(async () => {
+      await testSubjects.click('embeddablePanelExamplae');
+    });
+
+    it('Can create a new child', async () => {
+      await testSubjects.click('embeddablePanelToggleMenuIcon');
+      await testSubjects.click('embeddablePanelAction-ADD_PANEL_ACTION_ID');
+      await testSubjects.click('createNew');
+      await testSubjects.click('createNew-TODO_EMBEDDABLE');
+      await testSubjects.setValue('taskInputField', 'new task');
+      await testSubjects.click('createTodoEmbeddable');
+      const tasks = await testSubjects.getVisibleTextAll('todoEmbeddableTask');
+      expect(tasks).to.eql(['Goes out on Wednesdays!', 'new task']);
+    });
+  });
+}
diff --git a/test/examples/embeddables/index.ts b/test/examples/embeddables/index.ts
index ff7997b3b01d4..8ad0961fcc3b6 100644
--- a/test/examples/embeddables/index.ts
+++ b/test/examples/embeddables/index.ts
@@ -39,5 +39,6 @@ export default function({
     loadTestFile(require.resolve('./hello_world_embeddable'));
     loadTestFile(require.resolve('./todo_embeddable'));
     loadTestFile(require.resolve('./list_container'));
+    loadTestFile(require.resolve('./adding_children'));
   });
 }
diff --git a/test/examples/embeddables/list_container.ts b/test/examples/embeddables/list_container.ts
index 79e6131647900..b1b91ad2c37f1 100644
--- a/test/examples/embeddables/list_container.ts
+++ b/test/examples/embeddables/list_container.ts
@@ -45,7 +45,7 @@ export default function({ getService }: PluginFunctionalProviderContext) {
         expect(text).to.eql(['HELLO WORLD!', 'HELLO WORLD!']);
 
         const tasks = await testSubjects.getVisibleTextAll('multiTaskTodoTask');
-        expect(tasks).to.eql(['Go to school', 'Watch planet earth', 'Read the encylopedia']);
+        expect(tasks).to.eql(['Go to school', 'Watch planet earth', 'Read the encyclopedia']);
       });
     });
 

From ef1565347ce9982b0bf45ff578dd5c58ddbe41e5 Mon Sep 17 00:00:00 2001
From: Aaron Caldwell <aaron.caldwell@elastic.co>
Date: Wed, 19 Feb 2020 13:57:36 -0700
Subject: [PATCH 079/174] [Maps][Telemetry] Migrate Maps telemetry to NP
 (#55055)

* Move maps telemetry to NP. Some clean-up, some ts conversion

* Update naming & org to be more in-line with guidelines

* Get TELEMETRY_TYPE from constants

* Ignore ts error importing from js file

* Set original array type passed into the function to array of ILayerTypeCount. Set return type on reduce function

* Remove unneeded 'any' types where used. Add in interfaces for map & index pattern saved objects

* Review feedback. Add layer, source, map saved object types and use

* Review feedback. Updates based on type check

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../plugins/maps/common/descriptor_types.d.ts |  17 +++
 x-pack/legacy/plugins/maps/index.js           |  10 +-
 .../maps_telemetry/collectors/register.ts     |  30 +++++
 .../register_collector.test.js}               |   9 +-
 .../maps/server/maps_telemetry/index.js       |   7 --
 .../{maps_telemetry.js => maps_telemetry.ts}  | 118 +++++++++++++-----
 .../maps_telemetry/maps_usage_collector.js    |  22 ----
 x-pack/legacy/plugins/maps/server/plugin.js   |   7 +-
 8 files changed, 154 insertions(+), 66 deletions(-)
 create mode 100644 x-pack/legacy/plugins/maps/common/descriptor_types.d.ts
 create mode 100644 x-pack/legacy/plugins/maps/server/maps_telemetry/collectors/register.ts
 rename x-pack/legacy/plugins/maps/server/maps_telemetry/{maps_usage_collector.test.js => collectors/register_collector.test.js} (80%)
 delete mode 100644 x-pack/legacy/plugins/maps/server/maps_telemetry/index.js
 rename x-pack/legacy/plugins/maps/server/maps_telemetry/{maps_telemetry.js => maps_telemetry.ts} (56%)
 delete mode 100644 x-pack/legacy/plugins/maps/server/maps_telemetry/maps_usage_collector.js

diff --git a/x-pack/legacy/plugins/maps/common/descriptor_types.d.ts b/x-pack/legacy/plugins/maps/common/descriptor_types.d.ts
new file mode 100644
index 0000000000000..05123c9c86b63
--- /dev/null
+++ b/x-pack/legacy/plugins/maps/common/descriptor_types.d.ts
@@ -0,0 +1,17 @@
+/*
+ * 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 { IFieldType } from '../../../../../src/plugins/data/common/index_patterns/fields';
+
+export interface ISourceDescriptor {
+  id: string;
+  type: string;
+}
+
+export interface ILayerDescriptor {
+  sourceDescriptor: ISourceDescriptor;
+  id: string;
+}
diff --git a/x-pack/legacy/plugins/maps/index.js b/x-pack/legacy/plugins/maps/index.js
index 247dc8115c5c3..5cd5a8731a703 100644
--- a/x-pack/legacy/plugins/maps/index.js
+++ b/x-pack/legacy/plugins/maps/index.js
@@ -9,7 +9,6 @@ import mappings from './mappings.json';
 import { i18n } from '@kbn/i18n';
 import { resolve } from 'path';
 import { migrations } from './migrations';
-import { initTelemetryCollection } from './server/maps_telemetry';
 import { getAppTitle } from './common/i18n_getters';
 import { MapPlugin } from './server/plugin';
 import { APP_ID, APP_ICON, createMapPath, MAP_SAVED_OBJECT_TYPE } from './common/constants';
@@ -92,12 +91,15 @@ export function maps(kibana) {
 
     init(server) {
       const mapsEnabled = server.config().get('xpack.maps.enabled');
-      const { usageCollection } = server.newPlatform.setup.plugins;
       if (!mapsEnabled) {
         server.log(['info', 'maps'], 'Maps app disabled by configuration');
         return;
       }
-      initTelemetryCollection(usageCollection, server);
+
+      // Init saved objects client deps
+      const callCluster = server.plugins.elasticsearch.getCluster('admin').callWithInternalUser;
+      const { SavedObjectsClient, getSavedObjectsRepository } = server.savedObjects;
+      const internalRepository = getSavedObjectsRepository(callCluster);
 
       const coreSetup = server.newPlatform.setup.core;
       const newPlatformPlugins = server.newPlatform.setup.plugins;
@@ -105,6 +107,7 @@ export function maps(kibana) {
         featuresPlugin: newPlatformPlugins.features,
         licensing: newPlatformPlugins.licensing,
         home: newPlatformPlugins.home,
+        usageCollection: newPlatformPlugins.usageCollection,
       };
 
       // legacy dependencies
@@ -118,6 +121,7 @@ export function maps(kibana) {
           elasticsearch: server.plugins.elasticsearch,
         },
         savedObjects: {
+          savedObjectsClient: new SavedObjectsClient(internalRepository),
           getSavedObjectsRepository: server.savedObjects.getSavedObjectsRepository,
         },
         injectUiAppVars: server.injectUiAppVars,
diff --git a/x-pack/legacy/plugins/maps/server/maps_telemetry/collectors/register.ts b/x-pack/legacy/plugins/maps/server/maps_telemetry/collectors/register.ts
new file mode 100644
index 0000000000000..652bb83a0d781
--- /dev/null
+++ b/x-pack/legacy/plugins/maps/server/maps_telemetry/collectors/register.ts
@@ -0,0 +1,30 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
+// @ts-ignore
+import { SavedObjectsClientContract } from 'src/core/server';
+import { getMapsTelemetry } from '../maps_telemetry';
+// @ts-ignore
+import { TELEMETRY_TYPE } from '../../../common/constants';
+
+export function registerMapsUsageCollector(
+  usageCollection: UsageCollectionSetup,
+  savedObjectsClient: SavedObjectsClientContract,
+  config: Function
+): void {
+  if (!usageCollection) {
+    return;
+  }
+
+  const mapsUsageCollector = usageCollection.makeUsageCollector({
+    type: TELEMETRY_TYPE,
+    isReady: () => true,
+    fetch: async () => await getMapsTelemetry(savedObjectsClient, config),
+  });
+
+  usageCollection.registerCollector(mapsUsageCollector);
+}
diff --git a/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_usage_collector.test.js b/x-pack/legacy/plugins/maps/server/maps_telemetry/collectors/register_collector.test.js
similarity index 80%
rename from x-pack/legacy/plugins/maps/server/maps_telemetry/maps_usage_collector.test.js
rename to x-pack/legacy/plugins/maps/server/maps_telemetry/collectors/register_collector.test.js
index c5a3fca89b560..33eb33100acdf 100644
--- a/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_usage_collector.test.js
+++ b/x-pack/legacy/plugins/maps/server/maps_telemetry/collectors/register_collector.test.js
@@ -4,16 +4,20 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { initTelemetryCollection } from './maps_usage_collector';
+import { registerMapsUsageCollector } from './register';
 
 describe('buildCollectorObj#fetch', () => {
   let makeUsageCollectorStub;
+  let savedObjectsClient;
   let registerStub;
   let usageCollection;
+  let config;
 
   beforeEach(() => {
     makeUsageCollectorStub = jest.fn();
+    savedObjectsClient = jest.fn();
     registerStub = jest.fn();
+    config = jest.fn();
     usageCollection = {
       makeUsageCollector: makeUsageCollectorStub,
       registerCollector: registerStub,
@@ -21,8 +25,7 @@ describe('buildCollectorObj#fetch', () => {
   });
 
   test('makes and registers maps usage collector', async () => {
-    const serverPlaceholder = {};
-    initTelemetryCollection(usageCollection, serverPlaceholder);
+    registerMapsUsageCollector(usageCollection, savedObjectsClient, config);
 
     expect(registerStub).toHaveBeenCalledTimes(1);
     expect(makeUsageCollectorStub).toHaveBeenCalledTimes(1);
diff --git a/x-pack/legacy/plugins/maps/server/maps_telemetry/index.js b/x-pack/legacy/plugins/maps/server/maps_telemetry/index.js
deleted file mode 100644
index 513df3f765186..0000000000000
--- a/x-pack/legacy/plugins/maps/server/maps_telemetry/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-export { initTelemetryCollection } from './maps_usage_collector';
diff --git a/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_telemetry.js b/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_telemetry.ts
similarity index 56%
rename from x-pack/legacy/plugins/maps/server/maps_telemetry/maps_telemetry.js
rename to x-pack/legacy/plugins/maps/server/maps_telemetry/maps_telemetry.ts
index 848c964f4b6d4..87642d9f8bea8 100644
--- a/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_telemetry.js
+++ b/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_telemetry.ts
@@ -5,28 +5,73 @@
  */
 
 import _ from 'lodash';
+import {
+  SavedObjectsClientContract,
+  SavedObjectAttributes,
+  SavedObjectAttribute,
+} from 'src/core/server';
+import { IFieldType, IIndexPattern } from 'src/plugins/data/public';
 import {
   EMS_FILE,
   ES_GEO_FIELD_TYPE,
   MAP_SAVED_OBJECT_TYPE,
   TELEMETRY_TYPE,
+  // @ts-ignore
 } from '../../common/constants';
+import { ILayerDescriptor } from '../../common/descriptor_types';
+
+interface IStats {
+  [key: string]: {
+    min: number;
+    max: number;
+    avg: number;
+  };
+}
 
-function getSavedObjectsClient(server) {
-  const { SavedObjectsClient, getSavedObjectsRepository } = server.savedObjects;
-  const callCluster = server.plugins.elasticsearch.getCluster('admin').callWithInternalUser;
-  const internalRepository = getSavedObjectsRepository(callCluster);
-  return new SavedObjectsClient(internalRepository);
+interface ILayerTypeCount {
+  [key: string]: number;
+}
+
+interface IMapSavedObject {
+  [key: string]: any;
+  fields: IFieldType[];
+  title: string;
+  id?: string;
+  type?: string;
+  timeFieldName?: string;
+  fieldFormatMap?: Record<
+    string,
+    {
+      id: string;
+      params: unknown;
+    }
+  >;
+  attributes?: {
+    title?: string;
+    description?: string;
+    mapStateJSON?: string;
+    layerListJSON?: string;
+    uiStateJSON?: string;
+    bounds?: {
+      type?: string;
+      coordinates?: [];
+    };
+  };
 }
 
-function getUniqueLayerCounts(layerCountsList, mapsCount) {
+function getUniqueLayerCounts(layerCountsList: ILayerTypeCount[], mapsCount: number) {
   const uniqueLayerTypes = _.uniq(_.flatten(layerCountsList.map(lTypes => Object.keys(lTypes))));
 
-  return uniqueLayerTypes.reduce((accu, type) => {
-    const typeCounts = layerCountsList.reduce((accu, tCounts) => {
-      tCounts[type] && accu.push(tCounts[type]);
-      return accu;
-    }, []);
+  return uniqueLayerTypes.reduce((accu: IStats, type: string) => {
+    const typeCounts = layerCountsList.reduce(
+      (tCountsAccu: number[], tCounts: ILayerTypeCount): number[] => {
+        if (tCounts[type]) {
+          tCountsAccu.push(tCounts[type]);
+        }
+        return tCountsAccu;
+      },
+      []
+    );
     const typeCountsSum = _.sum(typeCounts);
     accu[type] = {
       min: typeCounts.length ? _.min(typeCounts) : 0,
@@ -37,25 +82,35 @@ function getUniqueLayerCounts(layerCountsList, mapsCount) {
   }, {});
 }
 
-function getIndexPatternsWithGeoFieldCount(indexPatterns) {
+function getIndexPatternsWithGeoFieldCount(indexPatterns: IIndexPattern[]) {
   const fieldLists = indexPatterns.map(indexPattern => JSON.parse(indexPattern.attributes.fields));
-  const fieldListsWithGeoFields = fieldLists.filter(fields => {
-    return fields.some(
-      field =>
+  const fieldListsWithGeoFields = fieldLists.filter(fields =>
+    fields.some(
+      (field: IFieldType) =>
         field.type === ES_GEO_FIELD_TYPE.GEO_POINT || field.type === ES_GEO_FIELD_TYPE.GEO_SHAPE
-    );
-  });
+    )
+  );
   return fieldListsWithGeoFields.length;
 }
 
-export function buildMapsTelemetry({ mapSavedObjects, indexPatternSavedObjects, settings }) {
+export function buildMapsTelemetry({
+  mapSavedObjects,
+  indexPatternSavedObjects,
+  settings,
+}: {
+  mapSavedObjects: IMapSavedObject[];
+  indexPatternSavedObjects: IIndexPattern[];
+  settings: SavedObjectAttribute;
+}): SavedObjectAttributes {
   const layerLists = mapSavedObjects.map(savedMapObject =>
-    JSON.parse(savedMapObject.attributes.layerListJSON)
+    savedMapObject.attributes && savedMapObject.attributes.layerListJSON
+      ? JSON.parse(savedMapObject.attributes.layerListJSON)
+      : []
   );
   const mapsCount = layerLists.length;
 
   const dataSourcesCount = layerLists.map(lList => {
-    const sourceIdList = lList.map(layer => layer.sourceDescriptor.id);
+    const sourceIdList = lList.map((layer: ILayerDescriptor) => layer.sourceDescriptor.id);
     return _.uniq(sourceIdList).length;
   });
 
@@ -65,7 +120,7 @@ export function buildMapsTelemetry({ mapSavedObjects, indexPatternSavedObjects,
   // Count of EMS Vector layers used
   const emsLayersCount = layerLists.map(lList =>
     _(lList)
-      .countBy(layer => {
+      .countBy((layer: ILayerDescriptor) => {
         const isEmsFile = _.get(layer, 'sourceDescriptor.type') === EMS_FILE;
         return isEmsFile && _.get(layer, 'sourceDescriptor.id');
       })
@@ -110,23 +165,26 @@ export function buildMapsTelemetry({ mapSavedObjects, indexPatternSavedObjects,
     },
   };
 }
-
-async function getMapSavedObjects(savedObjectsClient) {
+async function getMapSavedObjects(savedObjectsClient: SavedObjectsClientContract) {
   const mapsSavedObjects = await savedObjectsClient.find({ type: MAP_SAVED_OBJECT_TYPE });
   return _.get(mapsSavedObjects, 'saved_objects', []);
 }
 
-async function getIndexPatternSavedObjects(savedObjectsClient) {
+async function getIndexPatternSavedObjects(savedObjectsClient: SavedObjectsClientContract) {
   const indexPatternSavedObjects = await savedObjectsClient.find({ type: 'index-pattern' });
   return _.get(indexPatternSavedObjects, 'saved_objects', []);
 }
 
-export async function getMapsTelemetry(server) {
-  const savedObjectsClient = getSavedObjectsClient(server);
-  const mapSavedObjects = await getMapSavedObjects(savedObjectsClient);
-  const indexPatternSavedObjects = await getIndexPatternSavedObjects(savedObjectsClient);
-  const settings = {
-    showMapVisualizationTypes: server.config().get('xpack.maps.showMapVisualizationTypes'),
+export async function getMapsTelemetry(
+  savedObjectsClient: SavedObjectsClientContract,
+  config: Function
+) {
+  const mapSavedObjects: IMapSavedObject[] = await getMapSavedObjects(savedObjectsClient);
+  const indexPatternSavedObjects: IIndexPattern[] = await getIndexPatternSavedObjects(
+    savedObjectsClient
+  );
+  const settings: SavedObjectAttribute = {
+    showMapVisualizationTypes: config().get('xpack.maps.showMapVisualizationTypes'),
   };
   const mapsTelemetry = buildMapsTelemetry({ mapSavedObjects, indexPatternSavedObjects, settings });
   return await savedObjectsClient.create(TELEMETRY_TYPE, mapsTelemetry, {
diff --git a/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_usage_collector.js b/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_usage_collector.js
deleted file mode 100644
index 9c575e66f7556..0000000000000
--- a/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_usage_collector.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { getMapsTelemetry } from './maps_telemetry';
-import { TELEMETRY_TYPE } from '../../common/constants';
-
-export function initTelemetryCollection(usageCollection, server) {
-  if (!usageCollection) {
-    return;
-  }
-
-  const mapsUsageCollector = usageCollection.makeUsageCollector({
-    type: TELEMETRY_TYPE,
-    isReady: () => true,
-    fetch: async () => await getMapsTelemetry(server),
-  });
-
-  usageCollection.registerCollector(mapsUsageCollector);
-}
diff --git a/x-pack/legacy/plugins/maps/server/plugin.js b/x-pack/legacy/plugins/maps/server/plugin.js
index d52a9f12ba631..02e38ff54b300 100644
--- a/x-pack/legacy/plugins/maps/server/plugin.js
+++ b/x-pack/legacy/plugins/maps/server/plugin.js
@@ -8,13 +8,14 @@ import { APP_ID, APP_ICON, createMapPath, MAP_SAVED_OBJECT_TYPE } from '../commo
 import { getEcommerceSavedObjects } from './sample_data/ecommerce_saved_objects';
 import { getFlightsSavedObjects } from './sample_data/flights_saved_objects.js';
 import { getWebLogsSavedObjects } from './sample_data/web_logs_saved_objects.js';
+import { registerMapsUsageCollector } from './maps_telemetry/collectors/register';
 import { LICENSE_CHECK_STATE } from '../../../../plugins/licensing/server';
 import { initRoutes } from './routes';
 import { emsBoundariesSpecProvider } from './tutorials/ems';
 
 export class MapPlugin {
   setup(core, plugins, __LEGACY) {
-    const { featuresPlugin, home, licensing } = plugins;
+    const { featuresPlugin, home, licensing, usageCollection } = plugins;
     let routesInitialized = false;
 
     featuresPlugin.registerFeature({
@@ -52,6 +53,10 @@ export class MapPlugin {
       }
     });
 
+    // Init telemetry
+    const { savedObjectsClient } = __LEGACY.savedObjects;
+    registerMapsUsageCollector(usageCollection, savedObjectsClient, __LEGACY.config);
+
     const sampleDataLinkLabel = i18n.translate('xpack.maps.sampleDataLinkLabel', {
       defaultMessage: 'Map',
     });

From 96a0aa0266dfd1a8b83b15dd8ae0efd273520cf1 Mon Sep 17 00:00:00 2001
From: Oliver Gupte <ogupte@users.noreply.github.com>
Date: Wed, 19 Feb 2020 13:43:03 -0800
Subject: [PATCH 080/174] [APM] NP Migration - Moves plugin server files out of
 legacy (#57532)

* Closes @56832 Migrates uses of the saved objects client to the internal
and context scoped clients exposed in the new platform core setup

* moves apm server, common, and typings dirs to the new plugin directory

* fixes path imports and type errors

* fixes some lint errors

* fixes CI failure. Use internal saved objects client like before.

* uses the context-scoped saved objects client for saving runtime APM indices,
and uses the internal saved objects client when creating apm index
pattern, so that any user who navigates to apm can trigger it

* fixes the type check error by updating import paths

* renamed files and directories to snake_case to pass scripts/check_file_casing

* rebase fixes and commit filename case changes

* moves get_indices_privileges.ts out of legacy path
---
 x-pack/legacy/plugins/apm/index.ts            |   5 +-
 .../DetailView/ErrorTabs.tsx                  |   2 +-
 .../DetailView/ExceptionStacktrace.tsx        |   2 +-
 .../ErrorGroupDetails/DetailView/index.tsx    |   5 +-
 .../app/ErrorGroupDetails/index.tsx           |   2 +-
 .../app/ErrorGroupOverview/List/index.tsx     |   5 +-
 .../app/ErrorGroupOverview/index.tsx          |   2 +-
 .../app/Main/route_config/index.tsx           |   4 +-
 .../app/ServiceDetails/ServiceDetailTabs.tsx  |   5 +-
 .../createErrorGroupWatch.ts                  |   2 +-
 .../ServiceMap/Popover/ServiceMetricList.tsx  |   3 +-
 .../app/ServiceMap/get_cytoscape_elements.ts  |   8 +-
 .../components/app/ServiceMap/index.tsx       |   3 +-
 .../components/app/ServiceMetrics/index.tsx   |   2 +-
 .../app/ServiceNodeMetrics/index.tsx          |   2 +-
 .../app/ServiceNodeOverview/index.tsx         |   6 +-
 .../app/ServiceOverview/ServiceList/index.tsx |   5 +-
 .../components/app/ServiceOverview/index.tsx  |   2 +-
 .../AddEditFlyout/DeleteButton.tsx            |   2 +-
 .../AddEditFlyout/index.tsx                   |   8 +-
 .../AddEditFlyout/saveConfig.ts               |   4 +-
 .../AgentConfigurationList.tsx                |   5 +-
 .../Settings/AgentConfigurations/index.tsx    |   3 +-
 .../public/components/app/TraceLink/index.tsx |   4 +-
 .../app/TraceOverview/TraceList.tsx           |   3 +-
 .../components/app/TraceOverview/index.tsx    |   2 +-
 .../__test__/distribution.test.ts             |   3 +-
 .../TransactionDetails/Distribution/index.tsx |   6 +-
 .../MaybeViewTraceLink.tsx                    |   2 +-
 .../WaterfallWithSummmary/TransactionTabs.tsx |   2 +-
 .../Marks/__test__/get_agent_marks.test.ts    |   2 +-
 .../Marks/get_agent_marks.ts                  |   2 +-
 .../Marks/get_error_marks.ts                  |   2 +-
 .../Waterfall/FlyoutTopLevelProperties.tsx    |   4 +-
 .../Waterfall/SpanFlyout/DatabaseContext.tsx  |   2 +-
 .../Waterfall/SpanFlyout/HttpContext.tsx      |   2 +-
 .../SpanFlyout/StickySpanProperties.tsx       |   8 +-
 .../Waterfall/SpanFlyout/index.tsx            |   4 +-
 .../TransactionFlyout/DroppedSpansWarning.tsx |   2 +-
 .../Waterfall/TransactionFlyout/index.tsx     |   2 +-
 .../Waterfall/WaterfallItem.tsx               |   4 +-
 .../waterfall_helpers.test.ts                 |   6 +-
 .../waterfall_helpers/waterfall_helpers.ts    |   9 +-
 .../WaterfallWithSummmary/index.tsx           |   3 +-
 .../app/TransactionDetails/index.tsx          |   2 +-
 .../app/TransactionOverview/List/index.tsx    |   5 +-
 .../app/TransactionOverview/index.tsx         |   2 +-
 .../shared/EnvironmentFilter/index.tsx        |   2 +-
 .../shared/KeyValueTable/FormattedValue.tsx   |   2 +-
 .../shared/KueryBar/get_bool_filter.ts        |   2 +-
 .../Links/DiscoverLinks/DiscoverErrorLink.tsx |   4 +-
 .../Links/DiscoverLinks/DiscoverLink.tsx      |   2 +-
 .../Links/DiscoverLinks/DiscoverSpanLink.tsx  |   4 +-
 .../DiscoverLinks/DiscoverTransactionLink.tsx |   4 +-
 .../__test__/DiscoverErrorButton.test.tsx     |   2 +-
 .../__test__/DiscoverErrorLink.test.tsx       |   2 +-
 .../DiscoverLinks.integration.test.tsx        |   6 +-
 .../DiscoverTransactionButton.test.tsx        |   2 +-
 .../__test__/DiscoverTransactionLink.test.tsx |   2 +-
 .../Links/MachineLearningLinks/MLJobLink.tsx  |   2 +-
 .../components/shared/Links/url_helpers.ts    |   3 +-
 .../shared/LocalUIFilters/index.tsx           |   5 +-
 .../__test__/ErrorMetadata.test.tsx           |   2 +-
 .../MetadataTable/ErrorMetadata/index.tsx     |   2 +-
 .../__test__/SpanMetadata.test.tsx            |   2 +-
 .../MetadataTable/SpanMetadata/index.tsx      |   2 +-
 .../__test__/TransactionMetadata.test.tsx     |   2 +-
 .../TransactionMetadata/index.tsx             |   2 +-
 .../MetadataTable/__test__/helper.test.ts     |   2 +-
 .../components/shared/MetadataTable/helper.ts |   6 +-
 .../components/shared/ServiceForm/index.tsx   |   2 +-
 .../shared/Stacktrace/CauseStacktrace.tsx     |   2 +-
 .../components/shared/Stacktrace/Context.tsx  |   2 +-
 .../shared/Stacktrace/FrameHeading.tsx        |   2 +-
 .../shared/Stacktrace/LibraryStacktrace.tsx   |   2 +-
 .../shared/Stacktrace/Stackframe.tsx          |   2 +-
 .../shared/Stacktrace/Variables.tsx           |   2 +-
 .../Stacktrace/__test__/Stackframe.test.tsx   |   2 +-
 .../shared/Stacktrace/__test__/index.test.ts  |   2 +-
 .../components/shared/Stacktrace/index.tsx    |   2 +-
 .../StickyProperties/StickyProperties.test.js |   5 +-
 .../shared/Summary/TransactionSummary.tsx     |   4 +-
 .../shared/Summary/UserAgentSummaryItem.tsx   |   2 +-
 .../Summary/__fixtures__/transactions.ts      |   2 +-
 .../components/shared/Summary/index.tsx       |   2 +-
 .../TransactionActionMenu.tsx                 |   2 +-
 .../__test__/TransactionActionMenu.test.tsx   |   2 +-
 .../__test__/sections.test.ts                 |   2 +-
 .../shared/TransactionActionMenu/sections.ts  |   2 +-
 .../TransactionBreakdownGraph/index.tsx       |   9 +-
 .../charts/CustomPlot/AnnotationsPlot.tsx     |   4 +-
 .../charts/CustomPlot/plotUtils.test.ts       |   5 +-
 .../shared/charts/CustomPlot/plotUtils.tsx    |   5 +-
 .../shared/charts/MetricsChart/index.tsx      |   7 +-
 .../charts/Timeline/Marker/ErrorMarker.tsx    |   2 +-
 .../TransactionLineChart/index.tsx            |   2 +-
 .../shared/charts/TransactionCharts/index.tsx |   9 +-
 .../context/UrlParamsContext/helpers.ts       |   2 +-
 .../public/context/UrlParamsContext/index.tsx |   5 +-
 .../UrlParamsContext/resolveUrlParams.ts      |   3 +-
 .../public/context/UrlParamsContext/types.ts  |   5 +-
 .../public/hooks/useAvgDurationByBrowser.ts   |   7 +-
 .../public/hooks/useDynamicIndexPattern.ts    |   2 +-
 .../apm/public/hooks/useLocalUIFilters.ts     |   8 +-
 .../public/hooks/useServiceMetricCharts.ts    |   3 +-
 .../hooks/useTransactionDistribution.ts       |   3 +-
 .../apm/public/hooks/useTransactionList.ts    |   3 +-
 .../apm/public/selectors/chartSelectors.ts    |   8 +-
 .../public/services/rest/createCallApmApi.ts  |   6 +-
 .../plugins/apm/public/services/rest/ml.ts    |   7 +-
 .../plugins/apm/public/utils/flattenObject.ts |   2 +-
 .../apm/public/utils/formatters/duration.ts   |   4 +-
 .../apm/public/utils/formatters/size.ts       |   2 +-
 .../public/utils/getRangeFromTimeSeries.ts    |   2 +-
 .../public/utils/isValidCoordinateValue.ts    |   2 +-
 .../plugins/apm/public/utils/testHelpers.tsx  |   2 +-
 .../lib/helpers/saved_objects_client.test.ts  |  58 --
 .../lib/helpers/saved_objects_client.ts       |  16 -
 x-pack/plugins/apm/.prettierrc                |   4 +
 .../elasticsearch_fieldnames.test.ts.snap     |   0
 .../common/agent_configuration_constants.ts   |   0
 .../plugins/apm/common/agent_name.ts          |   2 +-
 .../plugins/apm/common/annotations.ts         |   0
 .../apm/common/apm_saved_object_constants.ts  |   0
 .../common/elasticsearch_fieldnames.test.ts   |   6 +-
 .../apm/common/elasticsearch_fieldnames.ts    |   0
 .../apm/common/environment_filter_values.ts   |   0
 .../{legacy => }/plugins/apm/common/i18n.ts   |   0
 .../apm/common/index_pattern_constants.ts     |   0
 .../apm/common/ml_job_constants.test.ts       |   0
 .../plugins/apm/common/ml_job_constants.ts    |   0
 .../plugins/apm/common/processor_event.ts     |   0
 .../plugins/apm/common/projections/errors.ts  |   2 +
 .../plugins/apm/common/projections/metrics.ts |   2 +
 .../apm/common/projections/service_nodes.ts   |   1 +
 .../apm/common/projections/services.ts        |   2 +
 .../common/projections/transaction_groups.ts  |   2 +
 .../apm/common/projections/transactions.ts    |   2 +
 .../plugins/apm/common/projections/typings.ts |   7 +-
 .../util/merge_projection/index.test.ts       |   0
 .../util/merge_projection/index.ts            |   4 +-
 .../date_as_string_rt/index.test.ts           |   0
 .../runtime_types/date_as_string_rt/index.ts  |   0
 .../runtime_types/json_rt/index.test.ts       |   0
 .../apm/common/runtime_types/json_rt/index.ts |   0
 .../transaction_max_spans_rt/index.test.ts    |   0
 .../transaction_max_spans_rt/index.ts         |   0
 .../transaction_sample_rate_rt/index.test.ts  |   0
 .../transaction_sample_rate_rt/index.ts       |   0
 .../plugins/apm/common/service_map.ts         |   0
 .../plugins/apm/common/service_nodes.ts       |   0
 .../plugins/apm/common/transaction_types.ts   |   0
 .../plugins/apm/common/viz_colors.ts          |   0
 x-pack/plugins/apm/kibana.json                |   2 +-
 x-pack/plugins/apm/server/index.ts            |  21 +-
 .../lib/apm_telemetry/__test__/index.test.ts  |  38 +-
 .../apm/server/lib/apm_telemetry/index.ts     |  35 +-
 .../errors/__snapshots__/queries.test.ts.snap |   0
 .../__snapshots__/queries.test.ts.snap        |   0
 .../__snapshots__/get_buckets.test.ts.snap    |   0
 .../__tests__/get_buckets.test.ts             |   2 +-
 .../lib/errors/distribution/get_buckets.ts    |   2 +-
 .../errors/distribution/get_distribution.ts   |   0
 .../lib/errors/distribution/queries.test.ts   |   2 +-
 .../apm/server/lib/errors/get_error_group.ts  |   2 +-
 .../apm/server/lib/errors/get_error_groups.ts |   4 +-
 .../apm/server/lib/errors/queries.test.ts     |   2 +-
 .../get_environment_ui_filter_es.test.ts      |   2 +-
 .../get_environment_ui_filter_es.ts           |   2 +-
 .../convert_ui_filters/get_ui_filters_es.ts   |   6 +-
 .../apm/server/lib/helpers/es_client.test.ts  |   0
 .../apm/server/lib/helpers/es_client.ts       |   4 +-
 .../helpers/get_bucket_size/calculate_auto.js |   0
 .../lib/helpers/get_bucket_size/index.ts      |   0
 .../get_bucket_size/unit_to_seconds.js        |   0
 .../get_internal_saved_objects_client.ts      |  16 +
 .../server/lib/helpers/input_validation.ts    |   0
 .../plugins/apm/server/lib/helpers/metrics.ts |   0
 .../apm/server/lib/helpers/range_filter.ts    |   0
 .../round_to_nearest_five_or_ten.test.ts      |   0
 .../helpers/round_to_nearest_five_or_ten.ts   |   0
 .../server/lib/helpers/setup_request.test.ts  |   4 +-
 .../apm/server/lib/helpers/setup_request.ts   |   8 +-
 .../create_static_index_pattern.test.ts       |  44 +-
 .../create_static_index_pattern.ts            |  15 +-
 .../get_dynamic_index_pattern.ts              |   4 +-
 .../__snapshots__/queries.test.ts.snap        |   0
 .../server/lib/metrics/by_agent/default.ts    |   0
 .../gc/fetch_and_transform_gc_metrics.ts}     |   0
 .../by_agent/java/gc/get_gc_rate_chart.ts}    |   2 +-
 .../by_agent/java/gc/get_gc_time_chart.ts}    |   2 +-
 .../by_agent/java/heap_memory/index.ts        |   0
 .../server/lib/metrics/by_agent/java/index.ts |   4 +-
 .../by_agent/java/non_heap_memory/index.ts    |   0
 .../by_agent/java/thread_count/index.ts       |   0
 .../lib/metrics/by_agent/shared/cpu/index.ts  |   0
 .../metrics/by_agent/shared/memory/index.ts   |   0
 .../metrics/fetch_and_transform_metrics.ts    |   2 +-
 .../get_metrics_chart_data_by_agent.ts        |   0
 .../apm/server/lib/metrics/queries.test.ts    |   2 +-
 .../metrics/transform_metrics_chart.test.ts   |   0
 .../lib/metrics/transform_metrics_chart.ts    |   4 +-
 .../plugins/apm/server/lib/metrics/types.ts   |   0
 .../lib/security/get_indices_privileges.ts    |   0
 .../server/lib/security/get_permissions.ts    |  32 +
 .../server/lib/service_map/get_service_map.ts |   0
 .../get_service_map_from_trace_ids.ts         |   0
 .../get_service_map_service_node_info.ts      |   2 +-
 .../lib/service_map/get_trace_sample_ids.ts   |   2 +-
 .../__snapshots__/queries.test.ts.snap        |   0
 .../apm/server/lib/service_nodes/index.ts     |   0
 .../server/lib/service_nodes/queries.test.ts  |   2 +-
 .../__snapshots__/queries.test.ts.snap        |   0
 .../__fixtures__/multiple_versions.json}      |   0
 .../__fixtures__/no_versions.json}            |   0
 .../__fixtures__/one_version.json}            |   0
 .../__fixtures__/versions_first_seen.json}    |   0
 .../lib/services/annotations/index.test.ts    |  10 +-
 .../server/lib/services/annotations/index.ts  |   2 +-
 .../lib/services/get_service_agent_name.ts    |   0
 .../lib/services/get_service_node_metadata.ts |   0
 .../services/get_service_transaction_types.ts |   0
 .../get_services/get_legacy_data_status.ts    |   0
 .../get_services/get_services_items.ts        |   0
 .../get_services/has_historical_agent_data.ts |   0
 .../server/lib/services/get_services/index.ts |   0
 .../plugins/apm/server/lib/services/map.ts    |   0
 .../apm/server/lib/services/queries.test.ts   |   2 +-
 .../__snapshots__/queries.test.ts.snap        |   0
 .../configuration_types.d.ts                  |   0
 .../create_agent_config_index.ts              |   4 +-
 .../create_or_update_configuration.ts         |   0
 .../delete_configuration.ts                   |   0
 .../get_agent_name_by_service.ts              |   0
 .../get_environments/get_all_environments.ts  |   0
 .../get_existing_environments_for_service.ts  |   0
 .../get_environments/index.ts                 |   0
 .../agent_configuration/get_service_names.ts  |   0
 .../list_configurations.ts                    |   0
 .../mark_applied_by_agent.ts                  |   0
 .../agent_configuration/queries.test.ts       |   2 +-
 .../settings/agent_configuration/search.ts    |   2 +-
 .../settings/apm_indices/get_apm_indices.ts   |  10 +-
 .../apm_indices/save_apm_indices.test.ts      |  17 +-
 .../settings/apm_indices/save_apm_indices.ts  |  16 +-
 .../traces/__snapshots__/queries.test.ts.snap |   0
 .../apm/server/lib/traces/get_trace.ts        |   0
 .../apm/server/lib/traces/get_trace_items.ts  |   6 +-
 .../apm/server/lib/traces/queries.test.ts     |   2 +-
 .../__snapshots__/fetcher.test.ts.snap        |   0
 .../__snapshots__/queries.test.ts.snap        |   0
 .../__snapshots__/transform.test.ts.snap      |   0
 .../lib/transaction_groups/fetcher.test.ts    |   2 +-
 .../server/lib/transaction_groups/fetcher.ts  |   4 +-
 .../server/lib/transaction_groups/index.ts    |   0
 .../transaction_groups_response.ts}           |   0
 .../lib/transaction_groups/queries.test.ts    |   2 +-
 .../lib/transaction_groups/transform.test.ts  |   2 +-
 .../lib/transaction_groups/transform.ts       |   0
 .../__snapshots__/queries.test.ts.snap        |   0
 .../__fixtures__/responses.ts                 |   2 +-
 .../avg_duration_by_browser/fetcher.test.ts   |   0
 .../avg_duration_by_browser/fetcher.ts        |   2 +-
 .../avg_duration_by_browser/index.test.ts     |   0
 .../avg_duration_by_browser/index.ts          |   0
 .../transformer.test.ts                       |   0
 .../avg_duration_by_browser/transformer.ts    |   0
 .../avg_duration_by_country/index.ts          |   0
 .../lib/transactions/breakdown/constants.ts   |   0
 .../lib/transactions/breakdown/index.test.ts  |   6 +-
 .../lib/transactions/breakdown/index.ts       |   0
 .../breakdown/mock_responses}/data.json       |   0
 .../breakdown/mock_responses/no_data.json}    |   0
 .../__snapshots__/fetcher.test.ts.snap        |   0
 .../__snapshots__/index.test.ts.snap          |   0
 .../__snapshots__/transform.test.ts.snap      |   0
 .../charts/get_anomaly_data/fetcher.test.ts   |   0
 .../charts/get_anomaly_data/fetcher.ts        |   0
 .../get_anomaly_data/get_ml_bucket_size.ts    |   0
 .../charts/get_anomaly_data/index.test.ts     |   6 +-
 .../charts/get_anomaly_data/index.ts          |   0
 .../mock_responses/ml_anomaly_response.ts}    |   0
 .../ml_bucket_span_response.ts}               |   0
 .../charts/get_anomaly_data/transform.test.ts |   2 +-
 .../charts/get_anomaly_data/transform.ts      |   0
 .../__snapshots__/fetcher.test.ts.snap        |   0
 .../__snapshots__/transform.test.ts.snap      |   0
 .../get_timeseries_data/fetcher.test.ts       |   2 +-
 .../charts/get_timeseries_data/fetcher.ts     |   2 +-
 .../charts/get_timeseries_data/index.ts       |   0
 .../mock_responses}/timeseries_response.ts    |   0
 .../get_timeseries_data/transform.test.ts     |   2 +-
 .../charts/get_timeseries_data/transform.ts   |   0
 .../server/lib/transactions/charts/index.ts   |   0
 .../apm/server/lib/transactions/constants.ts  |   0
 .../distribution/get_buckets/fetcher.ts       |   2 +-
 .../distribution/get_buckets/index.ts         |   0
 .../distribution/get_buckets/transform.ts     |   2 +-
 .../distribution/get_distribution_max.ts      |   0
 .../lib/transactions/distribution/index.ts    |   0
 .../lib/transactions/get_transaction/index.ts |   2 +-
 .../get_transaction_by_trace/index.ts         |   2 +-
 .../server/lib/transactions/queries.test.ts   |   2 +-
 .../__snapshots__/queries.test.ts.snap        |   0
 .../server/lib/ui_filters/get_environments.ts |   2 +-
 .../__snapshots__/queries.test.ts.snap        |   0
 .../lib/ui_filters/local_ui_filters/config.ts |   0
 .../get_local_filter_query.ts                 |   2 +-
 .../lib/ui_filters/local_ui_filters/index.ts  |   2 +-
 .../local_ui_filters/queries.test.ts          |   2 +-
 .../apm/server/lib/ui_filters/queries.test.ts |   2 +-
 x-pack/plugins/apm/server/plugin.ts           |  47 +-
 .../server/routes/create_api/index.test.ts    |   4 +-
 .../apm/server/routes/create_api/index.ts     |   2 +-
 .../apm/server/routes/create_apm_api.ts       |   0
 .../plugins/apm/server/routes/create_route.ts |   0
 .../apm/server/routes/default_api_types.ts    |   0
 .../plugins/apm/server/routes/errors.ts       |   0
 .../apm/server/routes/index_pattern.ts        |   6 +-
 .../plugins/apm/server/routes/metrics.ts      |   0
 .../plugins/apm/server/routes/security.ts     |   0
 .../plugins/apm/server/routes/service_map.ts  |   0
 .../apm/server/routes/service_nodes.ts        |   0
 .../plugins/apm/server/routes/services.ts     |  10 +-
 .../routes/settings/agent_configuration.ts    |   0
 .../apm/server/routes/settings/apm_indices.ts |   8 +-
 .../plugins/apm/server/routes/traces.ts       |   0
 .../plugins/apm/server/routes/transaction.ts  |   0
 .../apm/server/routes/transaction_groups.ts   |   0
 .../plugins/apm/server/routes/typings.ts      |   4 +-
 .../plugins/apm/server/routes/ui_filters.ts   |   0
 .../apm/server/tutorial/envs/elastic_cloud.ts |  42 +-
 .../apm/server/tutorial/envs/on_prem.ts       | 210 ++++--
 x-pack/plugins/apm/server/tutorial/index.ts   |  59 +-
 .../instructions/apm_agent_instructions.ts    | 660 +++++++++++-------
 .../instructions/apm_server_instructions.ts   |  95 +--
 x-pack/plugins/apm/tsconfig.json              |   3 +
 .../apm/typings/apm_rum_react.d.ts}           |   0
 .../plugins/apm/typings/common.d.ts           |   8 +-
 .../apm/typings/cytoscape_dagre.d.ts}         |   0
 .../apm/typings/elasticsearch/aggregations.ts |  30 +-
 .../apm/typings/elasticsearch/index.ts        |  13 +-
 .../typings/es_schemas/raw/apm_base_doc.ts}   |   0
 .../apm/typings/es_schemas/raw/error_raw.ts}  |  22 +-
 .../es_schemas/raw/fields/container.ts}       |   0
 .../typings/es_schemas/raw/fields/host.ts}    |   0
 .../typings/es_schemas/raw/fields/http.ts}    |   0
 .../es_schemas/raw/fields/kubernetes.ts}      |   0
 .../typings/es_schemas/raw/fields/page.ts}    |   0
 .../typings/es_schemas/raw/fields/process.ts} |   0
 .../typings/es_schemas/raw/fields/service.ts} |   0
 .../es_schemas/raw/fields/stackframe.ts}      |   0
 .../apm/typings/es_schemas/raw/fields/url.ts} |   0
 .../typings/es_schemas/raw/fields/user.ts}    |   0
 .../es_schemas/raw/fields/user_agent.ts}      |   0
 .../apm/typings/es_schemas/raw/span_raw.ts}   |   4 +-
 .../es_schemas/raw/transaction_raw.ts}        |  22 +-
 .../apm/typings/es_schemas/ui/apm_error.ts}   |   4 +-
 .../typings/es_schemas/ui/fields/agent.ts}    |   0
 .../apm/typings/es_schemas/ui/span.ts}        |   4 +-
 .../apm/typings/es_schemas/ui/transaction.ts} |   4 +-
 .../plugins/apm/typings/lodash.mean.d.ts      |   0
 .../plugins/apm/typings/numeral.d.ts          |   0
 .../apm/typings/react_vis.d.ts}               |   0
 .../plugins/apm/typings/timeseries.ts         |   0
 .../apm/typings/ui_filters.ts}                |   1 +
 .../routes/metadata/lib/has_apm_data.ts       |   4 +-
 367 files changed, 1229 insertions(+), 937 deletions(-)
 delete mode 100644 x-pack/legacy/plugins/apm/server/lib/helpers/saved_objects_client.test.ts
 delete mode 100644 x-pack/legacy/plugins/apm/server/lib/helpers/saved_objects_client.ts
 create mode 100644 x-pack/plugins/apm/.prettierrc
 rename x-pack/{legacy => }/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap (100%)
 rename x-pack/{legacy => }/plugins/apm/common/agent_configuration_constants.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/common/agent_name.ts (94%)
 rename x-pack/{legacy => }/plugins/apm/common/annotations.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/common/apm_saved_object_constants.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/common/elasticsearch_fieldnames.test.ts (95%)
 rename x-pack/{legacy => }/plugins/apm/common/elasticsearch_fieldnames.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/common/environment_filter_values.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/common/i18n.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/common/index_pattern_constants.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/common/ml_job_constants.test.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/common/ml_job_constants.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/common/processor_event.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/common/projections/errors.ts (90%)
 rename x-pack/{legacy => }/plugins/apm/common/projections/metrics.ts (92%)
 rename x-pack/{legacy => }/plugins/apm/common/projections/service_nodes.ts (94%)
 rename x-pack/{legacy => }/plugins/apm/common/projections/services.ts (90%)
 rename x-pack/{legacy => }/plugins/apm/common/projections/transaction_groups.ts (91%)
 rename x-pack/{legacy => }/plugins/apm/common/projections/transactions.ts (92%)
 rename x-pack/{legacy => }/plugins/apm/common/projections/typings.ts (81%)
 rename x-pack/{legacy => }/plugins/apm/common/projections/util/merge_projection/index.test.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/common/projections/util/merge_projection/index.ts (87%)
 rename x-pack/{legacy => }/plugins/apm/common/runtime_types/date_as_string_rt/index.test.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/common/runtime_types/date_as_string_rt/index.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/common/runtime_types/json_rt/index.test.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/common/runtime_types/json_rt/index.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/common/runtime_types/transaction_max_spans_rt/index.test.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/common/runtime_types/transaction_max_spans_rt/index.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/common/runtime_types/transaction_sample_rate_rt/index.test.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/common/runtime_types/transaction_sample_rate_rt/index.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/common/service_map.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/common/service_nodes.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/common/transaction_types.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/common/viz_colors.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/apm_telemetry/__test__/index.test.ts (65%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/apm_telemetry/index.ts (60%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/errors/distribution/__snapshots__/queries.test.ts.snap (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/errors/distribution/__tests__/__snapshots__/get_buckets.test.ts.snap (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/errors/distribution/__tests__/get_buckets.test.ts (96%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/errors/distribution/get_buckets.ts (95%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/errors/distribution/get_distribution.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/errors/distribution/queries.test.ts (93%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/errors/get_error_group.ts (96%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/errors/get_error_groups.ts (95%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/errors/queries.test.ts (95%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/helpers/convert_ui_filters/__test__/get_environment_ui_filter_es.test.ts (93%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/helpers/convert_ui_filters/get_environment_ui_filter_es.ts (90%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/helpers/convert_ui_filters/get_ui_filters_es.ts (88%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/helpers/es_client.test.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/helpers/es_client.ts (97%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/helpers/get_bucket_size/calculate_auto.js (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/helpers/get_bucket_size/index.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/helpers/get_bucket_size/unit_to_seconds.js (100%)
 create mode 100644 x-pack/plugins/apm/server/lib/helpers/get_internal_saved_objects_client.ts
 rename x-pack/{legacy => }/plugins/apm/server/lib/helpers/input_validation.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/helpers/metrics.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/helpers/range_filter.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/helpers/round_to_nearest_five_or_ten.test.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/helpers/round_to_nearest_five_or_ten.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/helpers/setup_request.test.ts (98%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/helpers/setup_request.ts (92%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/index_pattern/create_static_index_pattern.test.ts (65%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/index_pattern/create_static_index_pattern.ts (76%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/index_pattern/get_dynamic_index_pattern.ts (96%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/metrics/by_agent/default.ts (100%)
 rename x-pack/{legacy/plugins/apm/server/lib/metrics/by_agent/java/gc/fetchAndTransformGcMetrics.ts => plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts} (100%)
 rename x-pack/{legacy/plugins/apm/server/lib/metrics/by_agent/java/gc/getGcRateChart.ts => plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_rate_chart.ts} (94%)
 rename x-pack/{legacy/plugins/apm/server/lib/metrics/by_agent/java/gc/getGcTimeChart.ts => plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_time_chart.ts} (94%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/metrics/by_agent/java/index.ts (91%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/metrics/by_agent/shared/cpu/index.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/metrics/by_agent/shared/memory/index.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts (94%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/metrics/queries.test.ts (97%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/metrics/transform_metrics_chart.test.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/metrics/transform_metrics_chart.ts (92%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/metrics/types.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/security/get_indices_privileges.ts (100%)
 create mode 100644 x-pack/plugins/apm/server/lib/security/get_permissions.ts
 rename x-pack/{legacy => }/plugins/apm/server/lib/service_map/get_service_map.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/service_map/get_service_map_from_trace_ids.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts (98%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts (98%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/service_nodes/index.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/service_nodes/queries.test.ts (95%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap (100%)
 rename x-pack/{legacy/plugins/apm/server/lib/services/annotations/__fixtures__/multiple-versions.json => plugins/apm/server/lib/services/annotations/__fixtures__/multiple_versions.json} (100%)
 rename x-pack/{legacy/plugins/apm/server/lib/services/annotations/__fixtures__/no-versions.json => plugins/apm/server/lib/services/annotations/__fixtures__/no_versions.json} (100%)
 rename x-pack/{legacy/plugins/apm/server/lib/services/annotations/__fixtures__/one-version.json => plugins/apm/server/lib/services/annotations/__fixtures__/one_version.json} (100%)
 rename x-pack/{legacy/plugins/apm/server/lib/services/annotations/__fixtures__/versions-first-seen.json => plugins/apm/server/lib/services/annotations/__fixtures__/versions_first_seen.json} (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/services/annotations/index.test.ts (87%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/services/annotations/index.ts (97%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/services/get_service_agent_name.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/services/get_service_node_metadata.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/services/get_service_transaction_types.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/services/get_services/get_legacy_data_status.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/services/get_services/get_services_items.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/services/get_services/has_historical_agent_data.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/services/get_services/index.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/services/map.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/services/queries.test.ts (96%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/settings/agent_configuration/__snapshots__/queries.test.ts.snap (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/settings/agent_configuration/configuration_types.d.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/settings/agent_configuration/create_agent_config_index.ts (94%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/settings/agent_configuration/create_or_update_configuration.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/settings/agent_configuration/delete_configuration.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/settings/agent_configuration/get_environments/get_all_environments.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/settings/agent_configuration/get_environments/get_existing_environments_for_service.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/settings/agent_configuration/get_environments/index.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/settings/agent_configuration/get_service_names.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/settings/agent_configuration/list_configurations.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/settings/agent_configuration/mark_applied_by_agent.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/settings/agent_configuration/queries.test.ts (96%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/settings/agent_configuration/search.ts (95%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/settings/apm_indices/get_apm_indices.ts (93%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/settings/apm_indices/save_apm_indices.test.ts (69%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/settings/apm_indices/save_apm_indices.ts (66%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/traces/__snapshots__/queries.test.ts.snap (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/traces/get_trace.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/traces/get_trace_items.ts (94%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/traces/queries.test.ts (90%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transaction_groups/__snapshots__/fetcher.test.ts.snap (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transaction_groups/__snapshots__/transform.test.ts.snap (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transaction_groups/fetcher.test.ts (96%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transaction_groups/fetcher.ts (94%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transaction_groups/index.ts (100%)
 rename x-pack/{legacy/plugins/apm/server/lib/transaction_groups/mock-responses/transactionGroupsResponse.ts => plugins/apm/server/lib/transaction_groups/mock_responses/transaction_groups_response.ts} (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transaction_groups/queries.test.ts (93%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transaction_groups/transform.test.ts (96%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transaction_groups/transform.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/avg_duration_by_browser/__fixtures__/responses.ts (96%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.test.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.ts (96%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/avg_duration_by_browser/index.test.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/avg_duration_by_browser/index.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/avg_duration_by_browser/transformer.test.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/avg_duration_by_browser/transformer.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/avg_duration_by_country/index.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/breakdown/constants.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/breakdown/index.test.ts (95%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/breakdown/index.ts (100%)
 rename x-pack/{legacy/plugins/apm/server/lib/transactions/breakdown/mock-responses => plugins/apm/server/lib/transactions/breakdown/mock_responses}/data.json (100%)
 rename x-pack/{legacy/plugins/apm/server/lib/transactions/breakdown/mock-responses/noData.json => plugins/apm/server/lib/transactions/breakdown/mock_responses/no_data.json} (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/charts/get_anomaly_data/__snapshots__/fetcher.test.ts.snap (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/charts/get_anomaly_data/__snapshots__/index.test.ts.snap (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/charts/get_anomaly_data/__snapshots__/transform.test.ts.snap (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/charts/get_anomaly_data/fetcher.test.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/charts/get_anomaly_data/fetcher.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/charts/get_anomaly_data/get_ml_bucket_size.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.test.ts (91%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.ts (100%)
 rename x-pack/{legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/mock-responses/mlAnomalyResponse.ts => plugins/apm/server/lib/transactions/charts/get_anomaly_data/mock_responses/ml_anomaly_response.ts} (100%)
 rename x-pack/{legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/mock-responses/mlBucketSpanResponse.ts => plugins/apm/server/lib/transactions/charts/get_anomaly_data/mock_responses/ml_bucket_span_response.ts} (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/charts/get_anomaly_data/transform.test.ts (98%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/charts/get_anomaly_data/transform.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/charts/get_timeseries_data/__snapshots__/fetcher.test.ts.snap (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/charts/get_timeseries_data/__snapshots__/transform.test.ts.snap (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.test.ts (96%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.ts (96%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/charts/get_timeseries_data/index.ts (100%)
 rename x-pack/{legacy/plugins/apm/server/lib/transactions/charts/get_timeseries_data/mock-responses => plugins/apm/server/lib/transactions/charts/get_timeseries_data/mock_responses}/timeseries_response.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/charts/get_timeseries_data/transform.test.ts (97%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/charts/get_timeseries_data/transform.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/charts/index.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/constants.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/distribution/get_buckets/fetcher.ts (99%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/distribution/get_buckets/transform.ts (98%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/distribution/index.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/get_transaction/index.ts (95%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/get_transaction_by_trace/index.ts (95%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/transactions/queries.test.ts (97%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/ui_filters/__snapshots__/queries.test.ts.snap (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/ui_filters/get_environments.ts (95%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/ui_filters/local_ui_filters/__snapshots__/queries.test.ts.snap (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/ui_filters/local_ui_filters/config.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/ui_filters/local_ui_filters/get_local_filter_query.ts (96%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/ui_filters/local_ui_filters/index.ts (96%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/ui_filters/local_ui_filters/queries.test.ts (93%)
 rename x-pack/{legacy => }/plugins/apm/server/lib/ui_filters/queries.test.ts (92%)
 rename x-pack/{legacy => }/plugins/apm/server/routes/create_api/index.test.ts (98%)
 rename x-pack/{legacy => }/plugins/apm/server/routes/create_api/index.ts (98%)
 rename x-pack/{legacy => }/plugins/apm/server/routes/create_apm_api.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/routes/create_route.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/routes/default_api_types.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/routes/errors.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/routes/index_pattern.ts (79%)
 rename x-pack/{legacy => }/plugins/apm/server/routes/metrics.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/routes/security.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/routes/service_map.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/routes/service_nodes.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/routes/services.ts (88%)
 rename x-pack/{legacy => }/plugins/apm/server/routes/settings/agent_configuration.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/routes/settings/apm_indices.ts (86%)
 rename x-pack/{legacy => }/plugins/apm/server/routes/traces.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/routes/transaction.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/routes/transaction_groups.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/server/routes/typings.ts (97%)
 rename x-pack/{legacy => }/plugins/apm/server/routes/ui_filters.ts (100%)
 create mode 100644 x-pack/plugins/apm/tsconfig.json
 rename x-pack/{legacy/plugins/apm/typings/apm-rum-react.d.ts => plugins/apm/typings/apm_rum_react.d.ts} (100%)
 rename x-pack/{legacy => }/plugins/apm/typings/common.d.ts (78%)
 rename x-pack/{legacy/plugins/apm/typings/cytoscape-dagre.d.ts => plugins/apm/typings/cytoscape_dagre.d.ts} (100%)
 rename x-pack/{legacy/plugins/apm/typings/es_schemas/raw/APMBaseDoc.ts => plugins/apm/typings/es_schemas/raw/apm_base_doc.ts} (100%)
 rename x-pack/{legacy/plugins/apm/typings/es_schemas/raw/ErrorRaw.ts => plugins/apm/typings/es_schemas/raw/error_raw.ts} (71%)
 rename x-pack/{legacy/plugins/apm/typings/es_schemas/raw/fields/Container.ts => plugins/apm/typings/es_schemas/raw/fields/container.ts} (100%)
 rename x-pack/{legacy/plugins/apm/typings/es_schemas/raw/fields/Host.ts => plugins/apm/typings/es_schemas/raw/fields/host.ts} (100%)
 rename x-pack/{legacy/plugins/apm/typings/es_schemas/raw/fields/Http.ts => plugins/apm/typings/es_schemas/raw/fields/http.ts} (100%)
 rename x-pack/{legacy/plugins/apm/typings/es_schemas/raw/fields/Kubernetes.ts => plugins/apm/typings/es_schemas/raw/fields/kubernetes.ts} (100%)
 rename x-pack/{legacy/plugins/apm/typings/es_schemas/raw/fields/Page.ts => plugins/apm/typings/es_schemas/raw/fields/page.ts} (100%)
 rename x-pack/{legacy/plugins/apm/typings/es_schemas/raw/fields/Process.ts => plugins/apm/typings/es_schemas/raw/fields/process.ts} (100%)
 rename x-pack/{legacy/plugins/apm/typings/es_schemas/raw/fields/Service.ts => plugins/apm/typings/es_schemas/raw/fields/service.ts} (100%)
 rename x-pack/{legacy/plugins/apm/typings/es_schemas/raw/fields/Stackframe.ts => plugins/apm/typings/es_schemas/raw/fields/stackframe.ts} (100%)
 rename x-pack/{legacy/plugins/apm/typings/es_schemas/raw/fields/Url.ts => plugins/apm/typings/es_schemas/raw/fields/url.ts} (100%)
 rename x-pack/{legacy/plugins/apm/typings/es_schemas/raw/fields/User.ts => plugins/apm/typings/es_schemas/raw/fields/user.ts} (100%)
 rename x-pack/{legacy/plugins/apm/typings/es_schemas/raw/fields/UserAgent.ts => plugins/apm/typings/es_schemas/raw/fields/user_agent.ts} (100%)
 rename x-pack/{legacy/plugins/apm/typings/es_schemas/raw/SpanRaw.ts => plugins/apm/typings/es_schemas/raw/span_raw.ts} (91%)
 rename x-pack/{legacy/plugins/apm/typings/es_schemas/raw/TransactionRaw.ts => plugins/apm/typings/es_schemas/raw/transaction_raw.ts} (73%)
 rename x-pack/{legacy/plugins/apm/typings/es_schemas/ui/APMError.ts => plugins/apm/typings/es_schemas/ui/apm_error.ts} (78%)
 rename x-pack/{legacy/plugins/apm/typings/es_schemas/ui/fields/Agent.ts => plugins/apm/typings/es_schemas/ui/fields/agent.ts} (100%)
 rename x-pack/{legacy/plugins/apm/typings/es_schemas/ui/Span.ts => plugins/apm/typings/es_schemas/ui/span.ts} (78%)
 rename x-pack/{legacy/plugins/apm/typings/es_schemas/ui/Transaction.ts => plugins/apm/typings/es_schemas/ui/transaction.ts} (88%)
 rename x-pack/{legacy => }/plugins/apm/typings/lodash.mean.d.ts (100%)
 rename x-pack/{legacy => }/plugins/apm/typings/numeral.d.ts (100%)
 rename x-pack/{legacy/plugins/apm/typings/react-vis.d.ts => plugins/apm/typings/react_vis.d.ts} (100%)
 rename x-pack/{legacy => }/plugins/apm/typings/timeseries.ts (100%)
 rename x-pack/{legacy/plugins/apm/typings/ui-filters.ts => plugins/apm/typings/ui_filters.ts} (88%)

diff --git a/x-pack/legacy/plugins/apm/index.ts b/x-pack/legacy/plugins/apm/index.ts
index fa22dca58a08b..2efa13a0bbc8d 100644
--- a/x-pack/legacy/plugins/apm/index.ts
+++ b/x-pack/legacy/plugins/apm/index.ts
@@ -11,7 +11,6 @@ import { APMPluginContract } from '../../../plugins/apm/server';
 import { LegacyPluginInitializer } from '../../../../src/legacy/types';
 import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils';
 import mappings from './mappings.json';
-import { makeApmUsageCollector } from './server/lib/apm_telemetry';
 
 export const apm: LegacyPluginInitializer = kibana => {
   return new kibana.Plugin({
@@ -108,11 +107,9 @@ export const apm: LegacyPluginInitializer = kibana => {
           }
         }
       });
-      const { usageCollection } = server.newPlatform.setup.plugins;
-      makeApmUsageCollector(usageCollection, server);
+
       const apmPlugin = server.newPlatform.setup.plugins
         .apm as APMPluginContract;
-
       apmPlugin.registerLegacyAPI({ server });
     }
   });
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ErrorTabs.tsx b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ErrorTabs.tsx
index ab61ce444232d..33774c941ffd6 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ErrorTabs.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ErrorTabs.tsx
@@ -6,7 +6,7 @@
 
 import { i18n } from '@kbn/i18n';
 import { isEmpty } from 'lodash';
-import { APMError } from '../../../../../typings/es_schemas/ui/APMError';
+import { APMError } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/apm_error';
 
 export interface ErrorTab {
   key: 'log_stacktrace' | 'exception_stacktrace' | 'metadata';
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ExceptionStacktrace.tsx b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ExceptionStacktrace.tsx
index 0b91bddf862bc..75e518a278aea 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ExceptionStacktrace.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ExceptionStacktrace.tsx
@@ -6,7 +6,7 @@
 
 import React from 'react';
 import { EuiTitle } from '@elastic/eui';
-import { Exception } from '../../../../../typings/es_schemas/raw/ErrorRaw';
+import { Exception } from '../../../../../../../../plugins/apm/typings/es_schemas/raw/error_raw';
 import { Stacktrace } from '../../../shared/Stacktrace';
 import { CauseStacktrace } from '../../../shared/Stacktrace/CauseStacktrace';
 
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.tsx
index 29c5d3329d16b..490bf472065e3 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.tsx
@@ -19,8 +19,9 @@ import { Location } from 'history';
 import React from 'react';
 import styled from 'styled-components';
 import { first } from 'lodash';
-import { ErrorGroupAPIResponse } from '../../../../../server/lib/errors/get_error_group';
-import { APMError } from '../../../../../typings/es_schemas/ui/APMError';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { ErrorGroupAPIResponse } from '../../../../../../../../plugins/apm/server/lib/errors/get_error_group';
+import { APMError } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/apm_error';
 import { IUrlParams } from '../../../../context/UrlParamsContext/types';
 import { px, unit, units } from '../../../../style/variables';
 import { DiscoverErrorLink } from '../../../shared/Links/DiscoverLinks/DiscoverErrorLink';
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx
index d79f2a4ed481d..ccd720ceee075 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx
@@ -17,7 +17,7 @@ import theme from '@elastic/eui/dist/eui_theme_light.json';
 import { i18n } from '@kbn/i18n';
 import React, { Fragment } from 'react';
 import styled from 'styled-components';
-import { NOT_AVAILABLE_LABEL } from '../../../../common/i18n';
+import { NOT_AVAILABLE_LABEL } from '../../../../../../../plugins/apm/common/i18n';
 import { useFetcher } from '../../../hooks/useFetcher';
 import { fontFamilyCode, fontSizes, px, units } from '../../../style/variables';
 import { ApmHeader } from '../../shared/ApmHeader';
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/index.tsx
index e0d6e9108d3c0..b26833c02fe22 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/index.tsx
@@ -9,8 +9,9 @@ import numeral from '@elastic/numeral';
 import { i18n } from '@kbn/i18n';
 import React, { useMemo } from 'react';
 import styled from 'styled-components';
-import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n';
-import { ErrorGroupListAPIResponse } from '../../../../../server/lib/errors/get_error_groups';
+import { NOT_AVAILABLE_LABEL } from '../../../../../../../../plugins/apm/common/i18n';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { ErrorGroupListAPIResponse } from '../../../../../../../../plugins/apm/server/lib/errors/get_error_groups';
 import {
   fontFamilyCode,
   fontSizes,
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/index.tsx
index 9f7ff65d37d36..8c5a4545f1043 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/index.tsx
@@ -18,7 +18,7 @@ import { ErrorDistribution } from '../ErrorGroupDetails/Distribution';
 import { ErrorGroupList } from './List';
 import { useUrlParams } from '../../../hooks/useUrlParams';
 import { useTrackPageview } from '../../../../../../../plugins/observability/public';
-import { PROJECTION } from '../../../../common/projections/typings';
+import { PROJECTION } from '../../../../../../../plugins/apm/common/projections/typings';
 import { LocalUIFilters } from '../../shared/LocalUIFilters';
 
 const ErrorGroupOverview: React.FC = () => {
diff --git a/x-pack/legacy/plugins/apm/public/components/app/Main/route_config/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/Main/route_config/index.tsx
index 3be096d9db2bc..2e737382c67a5 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/Main/route_config/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/Main/route_config/index.tsx
@@ -7,7 +7,7 @@
 import { i18n } from '@kbn/i18n';
 import React from 'react';
 import { Redirect, RouteComponentProps } from 'react-router-dom';
-import { SERVICE_NODE_NAME_MISSING } from '../../../../../common/service_nodes';
+import { SERVICE_NODE_NAME_MISSING } from '../../../../../../../../plugins/apm/common/service_nodes';
 import { ErrorGroupDetails } from '../../ErrorGroupDetails';
 import { ServiceDetails } from '../../ServiceDetails';
 import { TransactionDetails } from '../../TransactionDetails';
@@ -20,7 +20,7 @@ import { ApmIndices } from '../../Settings/ApmIndices';
 import { toQuery } from '../../../shared/Links/url_helpers';
 import { ServiceNodeMetrics } from '../../ServiceNodeMetrics';
 import { resolveUrlParams } from '../../../../context/UrlParamsContext/resolveUrlParams';
-import { UNIDENTIFIED_SERVICE_NODES_LABEL } from '../../../../../common/i18n';
+import { UNIDENTIFIED_SERVICE_NODES_LABEL } from '../../../../../../../../plugins/apm/common/i18n';
 import { TraceLink } from '../../TraceLink';
 import { CustomizeUI } from '../../Settings/CustomizeUI';
 
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceDetailTabs.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceDetailTabs.tsx
index 7ab2f7bac8ae2..131bb7f65d4b3 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceDetailTabs.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceDetailTabs.tsx
@@ -7,7 +7,10 @@
 import { EuiTabs } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
 import React from 'react';
-import { isJavaAgentName, isRumAgentName } from '../../../../common/agent_name';
+import {
+  isJavaAgentName,
+  isRumAgentName
+} from '../../../../../../../plugins/apm/common/agent_name';
 import { useAgentName } from '../../../hooks/useAgentName';
 import { useApmPluginContext } from '../../../hooks/useApmPluginContext';
 import { useUrlParams } from '../../../hooks/useUrlParams';
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/createErrorGroupWatch.ts b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/createErrorGroupWatch.ts
index d45453e24f1c9..690db9fcdd8d6 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/createErrorGroupWatch.ts
+++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/createErrorGroupWatch.ts
@@ -17,7 +17,7 @@ import {
   ERROR_LOG_MESSAGE,
   PROCESSOR_EVENT,
   SERVICE_NAME
-} from '../../../../../common/elasticsearch_fieldnames';
+} from '../../../../../../../../plugins/apm/common/elasticsearch_fieldnames';
 import { createWatch } from '../../../../services/rest/watcher';
 
 function getSlackPathUrl(slackUrl?: string) {
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricList.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricList.tsx
index 8ce6d9d57c4ac..e91eb5e006d82 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricList.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricList.tsx
@@ -15,7 +15,8 @@ import { i18n } from '@kbn/i18n';
 import { isNumber } from 'lodash';
 import React from 'react';
 import styled from 'styled-components';
-import { ServiceNodeMetrics } from '../../../../../server/lib/service_map/get_service_map_service_node_info';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { ServiceNodeMetrics } from '../../../../../../../../plugins/apm/server/lib/service_map/get_service_map_service_node_info';
 import {
   asDuration,
   asPercent,
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/get_cytoscape_elements.ts b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/get_cytoscape_elements.ts
index 106e9a1d82f29..2403ed047cbc0 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/get_cytoscape_elements.ts
+++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/get_cytoscape_elements.ts
@@ -5,8 +5,12 @@
  */
 import { ValuesType } from 'utility-types';
 import { sortBy, isEqual } from 'lodash';
-import { Connection, ConnectionNode } from '../../../../common/service_map';
-import { ServiceMapAPIResponse } from '../../../../server/lib/service_map/get_service_map';
+import {
+  Connection,
+  ConnectionNode
+} from '../../../../../../../plugins/apm/common/service_map';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { ServiceMapAPIResponse } from '../../../../../../../plugins/apm/server/lib/service_map/get_service_map';
 import { getAPMHref } from '../../shared/Links/apm/APMLink';
 
 function getConnectionNodeId(node: ConnectionNode): string {
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx
index 4e24460f80ec1..5fea4be9ca0da 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx
@@ -17,7 +17,8 @@ import React, {
   useState
 } from 'react';
 import { toMountPoint } from '../../../../../../../../src/plugins/kibana_react/public';
-import { ServiceMapAPIResponse } from '../../../../server/lib/service_map/get_service_map';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { ServiceMapAPIResponse } from '../../../../../../../plugins/apm/server/lib/service_map/get_service_map';
 import { useApmPluginContext } from '../../../hooks/useApmPluginContext';
 import { useCallApmApi } from '../../../hooks/useCallApmApi';
 import { useDeepObjectIdentity } from '../../../hooks/useDeepObjectIdentity';
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMetrics/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMetrics/index.tsx
index 0fb8c00a2b162..060e635e83549 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMetrics/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMetrics/index.tsx
@@ -16,7 +16,7 @@ import { useServiceMetricCharts } from '../../../hooks/useServiceMetricCharts';
 import { MetricsChart } from '../../shared/charts/MetricsChart';
 import { useUrlParams } from '../../../hooks/useUrlParams';
 import { ChartsSyncContextProvider } from '../../../context/ChartsSyncContext';
-import { PROJECTION } from '../../../../common/projections/typings';
+import { PROJECTION } from '../../../../../../../plugins/apm/common/projections/typings';
 import { LocalUIFilters } from '../../shared/LocalUIFilters';
 
 interface ServiceMetricsProps {
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceNodeMetrics/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceNodeMetrics/index.tsx
index 3929c153ae419..2bf26946932ea 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ServiceNodeMetrics/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceNodeMetrics/index.tsx
@@ -20,7 +20,7 @@ import React from 'react';
 import { i18n } from '@kbn/i18n';
 import styled from 'styled-components';
 import { FormattedMessage } from '@kbn/i18n/react';
-import { SERVICE_NODE_NAME_MISSING } from '../../../../common/service_nodes';
+import { SERVICE_NODE_NAME_MISSING } from '../../../../../../../plugins/apm/common/service_nodes';
 import { ApmHeader } from '../../shared/ApmHeader';
 import { useUrlParams } from '../../../hooks/useUrlParams';
 import { useAgentName } from '../../../hooks/useAgentName';
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceNodeOverview/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceNodeOverview/index.tsx
index 4e57cb47691be..3af1a70ef3fdc 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ServiceNodeOverview/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceNodeOverview/index.tsx
@@ -13,9 +13,9 @@ import {
 } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
 import styled from 'styled-components';
-import { UNIDENTIFIED_SERVICE_NODES_LABEL } from '../../../../common/i18n';
-import { SERVICE_NODE_NAME_MISSING } from '../../../../common/service_nodes';
-import { PROJECTION } from '../../../../common/projections/typings';
+import { UNIDENTIFIED_SERVICE_NODES_LABEL } from '../../../../../../../plugins/apm/common/i18n';
+import { SERVICE_NODE_NAME_MISSING } from '../../../../../../../plugins/apm/common/service_nodes';
+import { PROJECTION } from '../../../../../../../plugins/apm/common/projections/typings';
 import { LocalUIFilters } from '../../shared/LocalUIFilters';
 import { useUrlParams } from '../../../hooks/useUrlParams';
 import { ManagedTable, ITableColumn } from '../../shared/ManagedTable';
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx
index 13e7a5bfd894e..1ac29c5626e3a 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx
@@ -8,8 +8,9 @@ import { EuiToolTip } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
 import React from 'react';
 import styled from 'styled-components';
-import { ServiceListAPIResponse } from '../../../../../server/lib/services/get_services';
-import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { ServiceListAPIResponse } from '../../../../../../../../plugins/apm/server/lib/services/get_services';
+import { NOT_AVAILABLE_LABEL } from '../../../../../../../../plugins/apm/common/i18n';
 import { fontSizes, truncate } from '../../../../style/variables';
 import { asDecimal, convertTo } from '../../../../utils/formatters';
 import { ManagedTable } from '../../../shared/ManagedTable';
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/index.tsx
index 762c10c0f48a7..52bc414a93a23 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/index.tsx
@@ -15,7 +15,7 @@ import { NoServicesMessage } from './NoServicesMessage';
 import { ServiceList } from './ServiceList';
 import { useUrlParams } from '../../../hooks/useUrlParams';
 import { useTrackPageview } from '../../../../../../../plugins/observability/public';
-import { PROJECTION } from '../../../../common/projections/typings';
+import { PROJECTION } from '../../../../../../../plugins/apm/common/projections/typings';
 import { LocalUIFilters } from '../../shared/LocalUIFilters';
 import { useApmPluginContext } from '../../../hooks/useApmPluginContext';
 
diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/DeleteButton.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/DeleteButton.tsx
index b5a59aea3286d..496147b02589b 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/DeleteButton.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/DeleteButton.tsx
@@ -10,7 +10,7 @@ import { NotificationsStart } from 'kibana/public';
 import { i18n } from '@kbn/i18n';
 import { useCallApmApi } from '../../../../../hooks/useCallApmApi';
 import { Config } from '../index';
-import { getOptionLabel } from '../../../../../../common/agent_configuration_constants';
+import { getOptionLabel } from '../../../../../../../../../plugins/apm/common/agent_configuration_constants';
 import { APMClient } from '../../../../../services/rest/createCallApmApi';
 import { useApmPluginContext } from '../../../../../hooks/useApmPluginContext';
 
diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx
index 4f808b3b4b5d9..653dedea733f2 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx
@@ -23,15 +23,15 @@ import React, { useState } from 'react';
 import { i18n } from '@kbn/i18n';
 import { isRight } from 'fp-ts/lib/Either';
 import { useCallApmApi } from '../../../../../hooks/useCallApmApi';
-import { transactionSampleRateRt } from '../../../../../../common/runtime_types/transaction_sample_rate_rt';
+import { transactionSampleRateRt } from '../../../../../../../../../plugins/apm/common/runtime_types/transaction_sample_rate_rt';
 import { Config } from '../index';
 import { SettingsSection } from './SettingsSection';
 import { ServiceForm } from '../../../../shared/ServiceForm';
 import { DeleteButton } from './DeleteButton';
-import { transactionMaxSpansRt } from '../../../../../../common/runtime_types/transaction_max_spans_rt';
+import { transactionMaxSpansRt } from '../../../../../../../../../plugins/apm/common/runtime_types/transaction_max_spans_rt';
 import { useFetcher } from '../../../../../hooks/useFetcher';
-import { isRumAgentName } from '../../../../../../common/agent_name';
-import { ALL_OPTION_VALUE } from '../../../../../../common/agent_configuration_constants';
+import { isRumAgentName } from '../../../../../../../../../plugins/apm/common/agent_name';
+import { ALL_OPTION_VALUE } from '../../../../../../../../../plugins/apm/common/agent_configuration_constants';
 import { saveConfig } from './saveConfig';
 import { useApmPluginContext } from '../../../../../hooks/useApmPluginContext';
 import { useUiTracker } from '../../../../../../../../../plugins/observability/public';
diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts
index a0c7c97e012a4..19934cafb4694 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts
+++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts
@@ -7,11 +7,11 @@
 import { i18n } from '@kbn/i18n';
 import { NotificationsStart } from 'kibana/public';
 import { APMClient } from '../../../../../services/rest/createCallApmApi';
-import { isRumAgentName } from '../../../../../../common/agent_name';
+import { isRumAgentName } from '../../../../../../../../../plugins/apm/common/agent_name';
 import {
   getOptionLabel,
   omitAllOption
-} from '../../../../../../common/agent_configuration_constants';
+} from '../../../../../../../../../plugins/apm/common/agent_configuration_constants';
 import { UiTracker } from '../../../../../../../../../plugins/observability/public';
 
 interface Settings {
diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationList.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationList.tsx
index c660455e1eed8..557945e9ba67a 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationList.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationList.tsx
@@ -18,11 +18,12 @@ import theme from '@elastic/eui/dist/eui_theme_light.json';
 import { FETCH_STATUS } from '../../../../hooks/useFetcher';
 import { ITableColumn, ManagedTable } from '../../../shared/ManagedTable';
 import { LoadingStatePrompt } from '../../../shared/LoadingStatePrompt';
-import { AgentConfigurationListAPIResponse } from '../../../../../server/lib/settings/agent_configuration/list_configurations';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { AgentConfigurationListAPIResponse } from '../../../../../../../../plugins/apm/server/lib/settings/agent_configuration/list_configurations';
 import { Config } from '.';
 import { TimestampTooltip } from '../../../shared/TimestampTooltip';
 import { px, units } from '../../../../style/variables';
-import { getOptionLabel } from '../../../../../common/agent_configuration_constants';
+import { getOptionLabel } from '../../../../../../../../plugins/apm/common/agent_configuration_constants';
 
 export function AgentConfigurationList({
   status,
diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/index.tsx
index 8812cccd2edaf..35cc68547d337 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/index.tsx
@@ -16,7 +16,8 @@ import {
 } from '@elastic/eui';
 import { isEmpty } from 'lodash';
 import { useFetcher } from '../../../../hooks/useFetcher';
-import { AgentConfigurationListAPIResponse } from '../../../../../server/lib/settings/agent_configuration/list_configurations';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { AgentConfigurationListAPIResponse } from '../../../../../../../../plugins/apm/server/lib/settings/agent_configuration/list_configurations';
 import { AgentConfigurationList } from './AgentConfigurationList';
 import { useTrackPageview } from '../../../../../../../../plugins/observability/public';
 import { AddEditFlyout } from './AddEditFlyout';
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TraceLink/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TraceLink/index.tsx
index b0489d58830d2..f0301f8917d10 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TraceLink/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TraceLink/index.tsx
@@ -9,8 +9,8 @@ import React from 'react';
 import { Redirect } from 'react-router-dom';
 import styled from 'styled-components';
 import url from 'url';
-import { TRACE_ID } from '../../../../common/elasticsearch_fieldnames';
-import { Transaction } from '../../../../typings/es_schemas/ui/Transaction';
+import { TRACE_ID } from '../../../../../../../plugins/apm/common/elasticsearch_fieldnames';
+import { Transaction } from '../../../../../../../plugins/apm/typings/es_schemas/ui/transaction';
 import { FETCH_STATUS, useFetcher } from '../../../hooks/useFetcher';
 import { useUrlParams } from '../../../hooks/useUrlParams';
 
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TraceOverview/TraceList.tsx b/x-pack/legacy/plugins/apm/public/components/app/TraceOverview/TraceList.tsx
index 9116e02870a80..91f3051acf077 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TraceOverview/TraceList.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TraceOverview/TraceList.tsx
@@ -8,7 +8,8 @@ import { EuiIcon, EuiToolTip } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
 import React from 'react';
 import styled from 'styled-components';
-import { ITransactionGroup } from '../../../../server/lib/transaction_groups/transform';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { ITransactionGroup } from '../../../../../../../plugins/apm/server/lib/transaction_groups/transform';
 import { fontSizes, truncate } from '../../../style/variables';
 import { convertTo } from '../../../utils/formatters';
 import { EmptyMessage } from '../../shared/EmptyMessage';
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TraceOverview/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TraceOverview/index.tsx
index 3bdcc3231cddc..bfbad78a5c026 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TraceOverview/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TraceOverview/index.tsx
@@ -11,7 +11,7 @@ import { TraceList } from './TraceList';
 import { useUrlParams } from '../../../hooks/useUrlParams';
 import { useTrackPageview } from '../../../../../../../plugins/observability/public';
 import { LocalUIFilters } from '../../shared/LocalUIFilters';
-import { PROJECTION } from '../../../../common/projections/typings';
+import { PROJECTION } from '../../../../../../../plugins/apm/common/projections/typings';
 
 export function TraceOverview() {
   const { urlParams, uiFilters } = useUrlParams();
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Distribution/__test__/distribution.test.ts b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Distribution/__test__/distribution.test.ts
index 85398a1324f7b..08682fb3be842 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Distribution/__test__/distribution.test.ts
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Distribution/__test__/distribution.test.ts
@@ -5,7 +5,8 @@
  */
 
 import { getFormattedBuckets } from '../index';
-import { IBucket } from '../../../../../../server/lib/transactions/distribution/get_buckets/transform';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { IBucket } from '../../../../../../../../../plugins/apm/server/lib/transactions/distribution/get_buckets/transform';
 
 describe('Distribution', () => {
   it('getFormattedBuckets', () => {
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx
index acd50438b4d54..e70133aabb679 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx
@@ -9,8 +9,10 @@ import { i18n } from '@kbn/i18n';
 import d3 from 'd3';
 import React, { FunctionComponent, useCallback } from 'react';
 import { isEmpty } from 'lodash';
-import { TransactionDistributionAPIResponse } from '../../../../../server/lib/transactions/distribution';
-import { IBucket } from '../../../../../server/lib/transactions/distribution/get_buckets/transform';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { TransactionDistributionAPIResponse } from '../../../../../../../../plugins/apm/server/lib/transactions/distribution';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { IBucket } from '../../../../../../../../plugins/apm/server/lib/transactions/distribution/get_buckets/transform';
 import { IUrlParams } from '../../../../context/UrlParamsContext/types';
 import { getDurationFormatter } from '../../../../utils/formatters';
 // @ts-ignore
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/MaybeViewTraceLink.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/MaybeViewTraceLink.tsx
index 322ec7c422571..4e105957f5f9d 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/MaybeViewTraceLink.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/MaybeViewTraceLink.tsx
@@ -7,7 +7,7 @@
 import React from 'react';
 import { EuiButton, EuiFlexItem, EuiToolTip } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
-import { Transaction as ITransaction } from '../../../../../typings/es_schemas/ui/Transaction';
+import { Transaction as ITransaction } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction';
 import { TransactionDetailLink } from '../../../shared/Links/apm/TransactionDetailLink';
 import { IWaterfall } from './WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers';
 
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TransactionTabs.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TransactionTabs.tsx
index f8318b9ae97e6..9026dd90ddceb 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TransactionTabs.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TransactionTabs.tsx
@@ -8,7 +8,7 @@ import { EuiSpacer, EuiTab, EuiTabs } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
 import { Location } from 'history';
 import React from 'react';
-import { Transaction } from '../../../../../typings/es_schemas/ui/Transaction';
+import { Transaction } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction';
 import { IUrlParams } from '../../../../context/UrlParamsContext/types';
 import { fromQuery, toQuery } from '../../../shared/Links/url_helpers';
 import { history } from '../../../../utils/history';
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/__test__/get_agent_marks.test.ts b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/__test__/get_agent_marks.test.ts
index 784c5fbebf3c2..030729522f35e 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/__test__/get_agent_marks.test.ts
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/__test__/get_agent_marks.test.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { Transaction } from '../../../../../../../../typings/es_schemas/ui/Transaction';
+import { Transaction } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction';
 import { getAgentMarks } from '../get_agent_marks';
 
 describe('getAgentMarks', () => {
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_agent_marks.ts b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_agent_marks.ts
index 7798d716cb219..1dcb1598662c9 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_agent_marks.ts
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_agent_marks.ts
@@ -5,7 +5,7 @@
  */
 
 import { sortBy } from 'lodash';
-import { Transaction } from '../../../../../../../typings/es_schemas/ui/Transaction';
+import { Transaction } from '../../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction';
 import { Mark } from '.';
 
 // Extends Mark without adding new properties to it.
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_error_marks.ts b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_error_marks.ts
index e2b00c13c5c1f..a9694efcbcae7 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_error_marks.ts
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_error_marks.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 import { isEmpty } from 'lodash';
-import { ErrorRaw } from '../../../../../../../typings/es_schemas/raw/ErrorRaw';
+import { ErrorRaw } from '../../../../../../../../../../plugins/apm/typings/es_schemas/raw/error_raw';
 import {
   IWaterfallError,
   IServiceColors
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/FlyoutTopLevelProperties.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/FlyoutTopLevelProperties.tsx
index a49a557127be6..6e58dbc5b6ea3 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/FlyoutTopLevelProperties.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/FlyoutTopLevelProperties.tsx
@@ -9,8 +9,8 @@ import React from 'react';
 import {
   SERVICE_NAME,
   TRANSACTION_NAME
-} from '../../../../../../../common/elasticsearch_fieldnames';
-import { Transaction } from '../../../../../../../typings/es_schemas/ui/Transaction';
+} from '../../../../../../../../../../plugins/apm/common/elasticsearch_fieldnames';
+import { Transaction } from '../../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction';
 import { TransactionDetailLink } from '../../../../../shared/Links/apm/TransactionDetailLink';
 import { StickyProperties } from '../../../../../shared/StickyProperties';
 import { TransactionOverviewLink } from '../../../../../shared/Links/apm/TransactionOverviewLink';
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/DatabaseContext.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/DatabaseContext.tsx
index 1a3f1f6831ff3..6200d5f098ad5 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/DatabaseContext.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/DatabaseContext.tsx
@@ -18,7 +18,7 @@ import SyntaxHighlighter, {
 // @ts-ignore
 import { xcode } from 'react-syntax-highlighter/dist/styles';
 import styled from 'styled-components';
-import { Span } from '../../../../../../../../typings/es_schemas/ui/Span';
+import { Span } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/span';
 import {
   borderRadius,
   fontFamilyCode,
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/HttpContext.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/HttpContext.tsx
index f767ce2e9f0d8..438e88df3351d 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/HttpContext.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/HttpContext.tsx
@@ -17,7 +17,7 @@ import {
   unit,
   units
 } from '../../../../../../../style/variables';
-import { Span } from '../../../../../../../../typings/es_schemas/ui/Span';
+import { Span } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/span';
 
 const ContextUrl = styled.div`
   padding: ${px(units.half)} ${px(unit)};
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/StickySpanProperties.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/StickySpanProperties.tsx
index 12cd9fe86c8c7..621497a0b22e0 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/StickySpanProperties.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/StickySpanProperties.tsx
@@ -6,14 +6,14 @@
 
 import { i18n } from '@kbn/i18n';
 import React from 'react';
-import { Transaction } from '../../../../../../../../typings/es_schemas/ui/Transaction';
+import { Transaction } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction';
 import {
   SPAN_NAME,
   TRANSACTION_NAME,
   SERVICE_NAME
-} from '../../../../../../../../common/elasticsearch_fieldnames';
-import { NOT_AVAILABLE_LABEL } from '../../../../../../../../common/i18n';
-import { Span } from '../../../../../../../../typings/es_schemas/ui/Span';
+} from '../../../../../../../../../../../plugins/apm/common/elasticsearch_fieldnames';
+import { NOT_AVAILABLE_LABEL } from '../../../../../../../../../../../plugins/apm/common/i18n';
+import { Span } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/span';
 import { StickyProperties } from '../../../../../../shared/StickyProperties';
 import { TransactionOverviewLink } from '../../../../../../shared/Links/apm/TransactionOverviewLink';
 import { TransactionDetailLink } from '../../../../../../shared/Links/apm/TransactionDetailLink';
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/index.tsx
index 6f93762e11a82..87102a486ab5f 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/index.tsx
@@ -25,8 +25,8 @@ import { px, units } from '../../../../../../../style/variables';
 import { Summary } from '../../../../../../shared/Summary';
 import { TimestampTooltip } from '../../../../../../shared/TimestampTooltip';
 import { DurationSummaryItem } from '../../../../../../shared/Summary/DurationSummaryItem';
-import { Span } from '../../../../../../../../typings/es_schemas/ui/Span';
-import { Transaction } from '../../../../../../../../typings/es_schemas/ui/Transaction';
+import { Span } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/span';
+import { Transaction } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction';
 import { DiscoverSpanLink } from '../../../../../../shared/Links/DiscoverLinks/DiscoverSpanLink';
 import { Stacktrace } from '../../../../../../shared/Stacktrace';
 import { ResponsiveFlyout } from '../ResponsiveFlyout';
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/DroppedSpansWarning.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/DroppedSpansWarning.tsx
index f3e29d7c75717..85cf0b642530f 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/DroppedSpansWarning.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/DroppedSpansWarning.tsx
@@ -7,7 +7,7 @@
 import { EuiCallOut, EuiHorizontalRule } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
 import React from 'react';
-import { Transaction } from '../../../../../../../../typings/es_schemas/ui/Transaction';
+import { Transaction } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction';
 import { ElasticDocsLink } from '../../../../../../shared/Links/ElasticDocsLink';
 
 export function DroppedSpansWarning({
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/index.tsx
index df95577c81eff..e24414bb28d52 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/index.tsx
@@ -16,7 +16,7 @@ import {
 } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
 import React from 'react';
-import { Transaction } from '../../../../../../../../typings/es_schemas/ui/Transaction';
+import { Transaction } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction';
 import { TransactionActionMenu } from '../../../../../../shared/TransactionActionMenu/TransactionActionMenu';
 import { TransactionSummary } from '../../../../../../shared/Summary/TransactionSummary';
 import { FlyoutTopLevelProperties } from '../FlyoutTopLevelProperties';
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/WaterfallItem.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/WaterfallItem.tsx
index f57ccc3c34467..b7b3e8d82ce61 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/WaterfallItem.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/WaterfallItem.tsx
@@ -10,13 +10,13 @@ import styled from 'styled-components';
 import { EuiIcon, EuiText, EuiTitle, EuiToolTip } from '@elastic/eui';
 import theme from '@elastic/eui/dist/eui_theme_light.json';
 import { i18n } from '@kbn/i18n';
-import { isRumAgentName } from '../../../../../../../common/agent_name';
+import { isRumAgentName } from '../../../../../../../../../../plugins/apm/common/agent_name';
 import { px, unit, units } from '../../../../../../style/variables';
 import { asDuration } from '../../../../../../utils/formatters';
 import { ErrorCount } from '../../ErrorCount';
 import { IWaterfallItem } from './waterfall_helpers/waterfall_helpers';
 import { ErrorOverviewLink } from '../../../../../shared/Links/apm/ErrorOverviewLink';
-import { TRACE_ID } from '../../../../../../../common/elasticsearch_fieldnames';
+import { TRACE_ID } from '../../../../../../../../../../plugins/apm/common/elasticsearch_fieldnames';
 import { SyncBadge } from './SyncBadge';
 
 type ItemType = 'transaction' | 'span' | 'error';
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.test.ts b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.test.ts
index 6b13b93200c61..416eb879d5974 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.test.ts
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.test.ts
@@ -5,8 +5,8 @@
  */
 
 import { groupBy } from 'lodash';
-import { Span } from '../../../../../../../../typings/es_schemas/ui/Span';
-import { Transaction } from '../../../../../../../../typings/es_schemas/ui/Transaction';
+import { Span } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/span';
+import { Transaction } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction';
 import {
   getClockSkew,
   getOrderedWaterfallItems,
@@ -15,7 +15,7 @@ import {
   IWaterfallTransaction,
   IWaterfallError
 } from './waterfall_helpers';
-import { APMError } from '../../../../../../../../typings/es_schemas/ui/APMError';
+import { APMError } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/apm_error';
 
 describe('waterfall_helpers', () => {
   describe('getWaterfall', () => {
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts
index 3b52163aa7fa4..58d9134c4d787 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts
@@ -15,10 +15,11 @@ import {
   uniq,
   zipObject
 } from 'lodash';
-import { TraceAPIResponse } from '../../../../../../../../server/lib/traces/get_trace';
-import { APMError } from '../../../../../../../../typings/es_schemas/ui/APMError';
-import { Span } from '../../../../../../../../typings/es_schemas/ui/Span';
-import { Transaction } from '../../../../../../../../typings/es_schemas/ui/Transaction';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { TraceAPIResponse } from '../../../../../../../../../../../plugins/apm/server/lib/traces/get_trace';
+import { APMError } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/apm_error';
+import { Span } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/span';
+import { Transaction } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction';
 
 interface IWaterfallGroup {
   [key: string]: IWaterfallItem[];
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/index.tsx
index 69557241c42aa..1082052c6929d 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/index.tsx
@@ -16,7 +16,8 @@ import {
 import { i18n } from '@kbn/i18n';
 import { Location } from 'history';
 import React, { useEffect, useState } from 'react';
-import { IBucket } from '../../../../../server/lib/transactions/distribution/get_buckets/transform';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { IBucket } from '../../../../../../../../plugins/apm/server/lib/transactions/distribution/get_buckets/transform';
 import { IUrlParams } from '../../../../context/UrlParamsContext/types';
 import { history } from '../../../../utils/history';
 import { fromQuery, toQuery } from '../../../shared/Links/url_helpers';
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/index.tsx
index a6712becf7968..e2634be0e0be8 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/index.tsx
@@ -27,7 +27,7 @@ import { FETCH_STATUS } from '../../../hooks/useFetcher';
 import { TransactionBreakdown } from '../../shared/TransactionBreakdown';
 import { ChartsSyncContextProvider } from '../../../context/ChartsSyncContext';
 import { useTrackPageview } from '../../../../../../../plugins/observability/public';
-import { PROJECTION } from '../../../../common/projections/typings';
+import { PROJECTION } from '../../../../../../../plugins/apm/common/projections/typings';
 import { LocalUIFilters } from '../../shared/LocalUIFilters';
 import { HeightRetainer } from '../../shared/HeightRetainer';
 
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/List/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/List/index.tsx
index 3d75011f52f19..16fda7c600906 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/List/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/List/index.tsx
@@ -8,8 +8,9 @@ import { EuiIcon, EuiToolTip } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
 import React, { useMemo } from 'react';
 import styled from 'styled-components';
-import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n';
-import { ITransactionGroup } from '../../../../../server/lib/transaction_groups/transform';
+import { NOT_AVAILABLE_LABEL } from '../../../../../../../../plugins/apm/common/i18n';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { ITransactionGroup } from '../../../../../../../../plugins/apm/server/lib/transaction_groups/transform';
 import { fontFamilyCode, truncate } from '../../../../style/variables';
 import { asDecimal, convertTo } from '../../../../utils/formatters';
 import { ImpactBar } from '../../../shared/ImpactBar';
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/index.tsx
index 73824f235ab02..b008c98417867 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/index.tsx
@@ -30,7 +30,7 @@ import { ChartsSyncContextProvider } from '../../../context/ChartsSyncContext';
 import { useTrackPageview } from '../../../../../../../plugins/observability/public';
 import { fromQuery, toQuery } from '../../shared/Links/url_helpers';
 import { LocalUIFilters } from '../../shared/LocalUIFilters';
-import { PROJECTION } from '../../../../common/projections/typings';
+import { PROJECTION } from '../../../../../../../plugins/apm/common/projections/typings';
 import { useUrlParams } from '../../../hooks/useUrlParams';
 import { useServiceTransactionTypes } from '../../../hooks/useServiceTransactionTypes';
 import { TransactionTypeFilter } from '../../shared/LocalUIFilters/TransactionTypeFilter';
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/EnvironmentFilter/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/EnvironmentFilter/index.tsx
index d17b3b7689b19..e911011f0979c 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/EnvironmentFilter/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/EnvironmentFilter/index.tsx
@@ -15,7 +15,7 @@ import { fromQuery, toQuery } from '../Links/url_helpers';
 import {
   ENVIRONMENT_ALL,
   ENVIRONMENT_NOT_DEFINED
-} from '../../../../common/environment_filter_values';
+} from '../../../../../../../plugins/apm/common/environment_filter_values';
 
 function updateEnvironmentUrl(
   location: ReturnType<typeof useLocation>,
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/KeyValueTable/FormattedValue.tsx b/x-pack/legacy/plugins/apm/public/components/shared/KeyValueTable/FormattedValue.tsx
index d33960fe5196b..4de07f75ff84f 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/KeyValueTable/FormattedValue.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/KeyValueTable/FormattedValue.tsx
@@ -8,7 +8,7 @@ import theme from '@elastic/eui/dist/eui_theme_light.json';
 import { isBoolean, isNumber, isObject } from 'lodash';
 import React from 'react';
 import styled from 'styled-components';
-import { NOT_AVAILABLE_LABEL } from '../../../../common/i18n';
+import { NOT_AVAILABLE_LABEL } from '../../../../../../../plugins/apm/common/i18n';
 
 const EmptyValue = styled.span`
   color: ${theme.euiColorMediumShade};
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/KueryBar/get_bool_filter.ts b/x-pack/legacy/plugins/apm/public/components/shared/KueryBar/get_bool_filter.ts
index 23feeed4b0418..19a9ae9538ad6 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/KueryBar/get_bool_filter.ts
+++ b/x-pack/legacy/plugins/apm/public/components/shared/KueryBar/get_bool_filter.ts
@@ -11,7 +11,7 @@ import {
   PROCESSOR_EVENT,
   TRANSACTION_NAME,
   SERVICE_NAME
-} from '../../../../common/elasticsearch_fieldnames';
+} from '../../../../../../../plugins/apm/common/elasticsearch_fieldnames';
 import { IUrlParams } from '../../../context/UrlParamsContext/types';
 
 export function getBoolFilter(urlParams: IUrlParams) {
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverErrorLink.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverErrorLink.tsx
index 38f7046685636..806d9f73369c3 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverErrorLink.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverErrorLink.tsx
@@ -8,8 +8,8 @@ import React from 'react';
 import {
   ERROR_GROUP_ID,
   SERVICE_NAME
-} from '../../../../../common/elasticsearch_fieldnames';
-import { APMError } from '../../../../../typings/es_schemas/ui/APMError';
+} from '../../../../../../../../plugins/apm/common/elasticsearch_fieldnames';
+import { APMError } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/apm_error';
 import { DiscoverLink } from './DiscoverLink';
 
 function getDiscoverQuery(error: APMError, kuery?: string) {
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverLink.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverLink.tsx
index b58a450d26644..6dc93292956fa 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverLink.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverLink.tsx
@@ -11,7 +11,7 @@ import url from 'url';
 import rison, { RisonValue } from 'rison-node';
 import { useLocation } from '../../../../hooks/useLocation';
 import { getTimepickerRisonData } from '../rison_helpers';
-import { APM_STATIC_INDEX_PATTERN_ID } from '../../../../../common/index_pattern_constants';
+import { APM_STATIC_INDEX_PATTERN_ID } from '../../../../../../../../plugins/apm/common/index_pattern_constants';
 import { useApmPluginContext } from '../../../../hooks/useApmPluginContext';
 import { AppMountContextBasePath } from '../../../../context/ApmPluginContext';
 
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverSpanLink.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverSpanLink.tsx
index 374454b74fce4..8fe5be28def22 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverSpanLink.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverSpanLink.tsx
@@ -5,8 +5,8 @@
  */
 
 import React from 'react';
-import { SPAN_ID } from '../../../../../common/elasticsearch_fieldnames';
-import { Span } from '../../../../../typings/es_schemas/ui/Span';
+import { SPAN_ID } from '../../../../../../../../plugins/apm/common/elasticsearch_fieldnames';
+import { Span } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/span';
 import { DiscoverLink } from './DiscoverLink';
 
 function getDiscoverQuery(span: Span) {
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverTransactionLink.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverTransactionLink.tsx
index 0e381c490f8b4..b0af33fd7d7f7 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverTransactionLink.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverTransactionLink.tsx
@@ -9,8 +9,8 @@ import {
   PROCESSOR_EVENT,
   TRACE_ID,
   TRANSACTION_ID
-} from '../../../../../common/elasticsearch_fieldnames';
-import { Transaction } from '../../../../../typings/es_schemas/ui/Transaction';
+} from '../../../../../../../../plugins/apm/common/elasticsearch_fieldnames';
+import { Transaction } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction';
 import { DiscoverLink } from './DiscoverLink';
 
 export function getDiscoverQuery(transaction: Transaction) {
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorButton.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorButton.test.tsx
index 33cf05401a729..eeb9fd20a4bcb 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorButton.test.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorButton.test.tsx
@@ -6,7 +6,7 @@
 
 import { shallow, ShallowWrapper } from 'enzyme';
 import React from 'react';
-import { APMError } from '../../../../../../typings/es_schemas/ui/APMError';
+import { APMError } from '../../../../../../../../../plugins/apm/typings/es_schemas/ui/apm_error';
 import { DiscoverErrorLink } from '../DiscoverErrorLink';
 
 describe('DiscoverErrorLink without kuery', () => {
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorLink.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorLink.test.tsx
index 33cf05401a729..eeb9fd20a4bcb 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorLink.test.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorLink.test.tsx
@@ -6,7 +6,7 @@
 
 import { shallow, ShallowWrapper } from 'enzyme';
 import React from 'react';
-import { APMError } from '../../../../../../typings/es_schemas/ui/APMError';
+import { APMError } from '../../../../../../../../../plugins/apm/typings/es_schemas/ui/apm_error';
 import { DiscoverErrorLink } from '../DiscoverErrorLink';
 
 describe('DiscoverErrorLink without kuery', () => {
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverLinks.integration.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverLinks.integration.test.tsx
index 2f49e476ef610..759caa785c1af 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverLinks.integration.test.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverLinks.integration.test.tsx
@@ -6,9 +6,9 @@
 
 import { Location } from 'history';
 import React from 'react';
-import { APMError } from '../../../../../../typings/es_schemas/ui/APMError';
-import { Span } from '../../../../../../typings/es_schemas/ui/Span';
-import { Transaction } from '../../../../../../typings/es_schemas/ui/Transaction';
+import { APMError } from '../../../../../../../../../plugins/apm/typings/es_schemas/ui/apm_error';
+import { Span } from '../../../../../../../../../plugins/apm/typings/es_schemas/ui/span';
+import { Transaction } from '../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction';
 import { getRenderedHref } from '../../../../../utils/testHelpers';
 import { DiscoverErrorLink } from '../DiscoverErrorLink';
 import { DiscoverSpanLink } from '../DiscoverSpanLink';
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionButton.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionButton.test.tsx
index a16bf389f177c..d72925c1956a4 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionButton.test.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionButton.test.tsx
@@ -6,7 +6,7 @@
 
 import { shallow } from 'enzyme';
 import React from 'react';
-import { Transaction } from '../../../../../../typings/es_schemas/ui/Transaction';
+import { Transaction } from '../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction';
 import {
   DiscoverTransactionLink,
   getDiscoverQuery
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionLink.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionLink.test.tsx
index 3b876aa5950b4..15a92474fcc6d 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionLink.test.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionLink.test.tsx
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { Transaction } from '../../../../../../typings/es_schemas/ui/Transaction';
+import { Transaction } from '../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction';
 // @ts-ignore
 import configureStore from '../../../../../store/config/configureStore';
 import { getDiscoverQuery } from '../DiscoverTransactionLink';
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.tsx
index 81c5d17d491c0..ecf788ddd2e69 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.tsx
@@ -5,7 +5,7 @@
  */
 
 import React from 'react';
-import { getMlJobId } from '../../../../../common/ml_job_constants';
+import { getMlJobId } from '../../../../../../../../plugins/apm/common/ml_job_constants';
 import { MLLink } from './MLLink';
 
 interface Props {
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts b/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts
index 36465309b736e..c7d71d0b6dac5 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts
@@ -5,7 +5,8 @@
  */
 
 import { parse, stringify } from 'query-string';
-import { LocalUIFilterName } from '../../../../server/lib/ui_filters/local_ui_filters/config';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { LocalUIFilterName } from '../../../../../../../plugins/apm/server/lib/ui_filters/local_ui_filters/config';
 import { url } from '../../../../../../../../src/plugins/kibana_utils/public';
 
 export function toQuery(search?: string): APMQueryParamsRaw {
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/LocalUIFilters/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/LocalUIFilters/index.tsx
index 737e78a223ae2..cede3e394cfab 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/LocalUIFilters/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/LocalUIFilters/index.tsx
@@ -13,10 +13,11 @@ import {
 } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
 import styled from 'styled-components';
-import { LocalUIFilterName } from '../../../../server/lib/ui_filters/local_ui_filters/config';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { LocalUIFilterName } from '../../../../../../../plugins/apm/server/lib/ui_filters/local_ui_filters/config';
 import { Filter } from './Filter';
 import { useLocalUIFilters } from '../../../hooks/useLocalUIFilters';
-import { PROJECTION } from '../../../../common/projections/typings';
+import { PROJECTION } from '../../../../../../../plugins/apm/common/projections/typings';
 
 interface Props {
   projection: PROJECTION;
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/__test__/ErrorMetadata.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/__test__/ErrorMetadata.test.tsx
index 5bbc194e35992..0c60d523b8f3f 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/__test__/ErrorMetadata.test.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/__test__/ErrorMetadata.test.tsx
@@ -7,7 +7,7 @@
 import React from 'react';
 import { ErrorMetadata } from '..';
 import { render } from '@testing-library/react';
-import { APMError } from '../../../../../../typings/es_schemas/ui/APMError';
+import { APMError } from '../../../../../../../../../plugins/apm/typings/es_schemas/ui/apm_error';
 import {
   expectTextsInDocument,
   expectTextsNotInDocument,
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/index.tsx
index 04a5b60467730..ce991d8b0dc00 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/index.tsx
@@ -6,7 +6,7 @@
 
 import React, { useMemo } from 'react';
 import { ERROR_METADATA_SECTIONS } from './sections';
-import { APMError } from '../../../../../typings/es_schemas/ui/APMError';
+import { APMError } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/apm_error';
 import { getSectionsWithRows } from '../helper';
 import { MetadataTable } from '..';
 
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/__test__/SpanMetadata.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/__test__/SpanMetadata.test.tsx
index 99d8a0790a816..ee66636d88ba9 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/__test__/SpanMetadata.test.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/__test__/SpanMetadata.test.tsx
@@ -7,7 +7,7 @@
 import React from 'react';
 import { render } from '@testing-library/react';
 import { SpanMetadata } from '..';
-import { Span } from '../../../../../../typings/es_schemas/ui/Span';
+import { Span } from '../../../../../../../../../plugins/apm/typings/es_schemas/ui/span';
 import {
   expectTextsInDocument,
   expectTextsNotInDocument,
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/index.tsx
index 03182062d324a..2134f12531a7a 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/index.tsx
@@ -6,7 +6,7 @@
 
 import React, { useMemo } from 'react';
 import { SPAN_METADATA_SECTIONS } from './sections';
-import { Span } from '../../../../../typings/es_schemas/ui/Span';
+import { Span } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/span';
 import { getSectionsWithRows } from '../helper';
 import { MetadataTable } from '..';
 
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/__test__/TransactionMetadata.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/__test__/TransactionMetadata.test.tsx
index 93e87e884ea76..f426074fbef80 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/__test__/TransactionMetadata.test.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/__test__/TransactionMetadata.test.tsx
@@ -7,7 +7,7 @@
 import React from 'react';
 import { TransactionMetadata } from '..';
 import { render } from '@testing-library/react';
-import { Transaction } from '../../../../../../typings/es_schemas/ui/Transaction';
+import { Transaction } from '../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction';
 import {
   expectTextsInDocument,
   expectTextsNotInDocument,
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/index.tsx
index 4216e37e0cb27..6f93de4e87e49 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/index.tsx
@@ -6,7 +6,7 @@
 
 import React, { useMemo } from 'react';
 import { TRANSACTION_METADATA_SECTIONS } from './sections';
-import { Transaction } from '../../../../../typings/es_schemas/ui/Transaction';
+import { Transaction } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction';
 import { getSectionsWithRows } from '../helper';
 import { MetadataTable } from '..';
 
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/__test__/helper.test.ts b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/__test__/helper.test.ts
index aaf73619e481a..b65b52bf30a5c 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/__test__/helper.test.ts
+++ b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/__test__/helper.test.ts
@@ -6,7 +6,7 @@
 
 import { getSectionsWithRows, filterSectionsByTerm } from '../helper';
 import { LABELS, HTTP, SERVICE } from '../sections';
-import { Transaction } from '../../../../../typings/es_schemas/ui/Transaction';
+import { Transaction } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction';
 
 describe('MetadataTable Helper', () => {
   const sections = [
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/helper.ts b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/helper.ts
index c272790826d8d..ef329abafa61b 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/helper.ts
+++ b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/helper.ts
@@ -6,9 +6,9 @@
 
 import { get, pick, isEmpty } from 'lodash';
 import { Section } from './sections';
-import { Transaction } from '../../../../typings/es_schemas/ui/Transaction';
-import { APMError } from '../../../../typings/es_schemas/ui/APMError';
-import { Span } from '../../../../typings/es_schemas/ui/Span';
+import { Transaction } from '../../../../../../../plugins/apm/typings/es_schemas/ui/transaction';
+import { APMError } from '../../../../../../../plugins/apm/typings/es_schemas/ui/apm_error';
+import { Span } from '../../../../../../../plugins/apm/typings/es_schemas/ui/span';
 import { flattenObject, KeyValuePair } from '../../../utils/flattenObject';
 
 export type SectionsWithRows = ReturnType<typeof getSectionsWithRows>;
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/ServiceForm/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/ServiceForm/index.tsx
index 58a203bded715..ab3accec90d1d 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/ServiceForm/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/ServiceForm/index.tsx
@@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n';
 import {
   omitAllOption,
   getOptionLabel
-} from '../../../../common/agent_configuration_constants';
+} from '../../../../../../../plugins/apm/common/agent_configuration_constants';
 import { useFetcher } from '../../../hooks/useFetcher';
 import { SelectWithPlaceholder } from '../SelectWithPlaceholder';
 
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/CauseStacktrace.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/CauseStacktrace.tsx
index 52f2ba4586718..eab414ad47c2c 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/CauseStacktrace.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/CauseStacktrace.tsx
@@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n';
 import { EuiAccordion, EuiTitle } from '@elastic/eui';
 import { px, unit } from '../../../style/variables';
 import { Stacktrace } from '.';
-import { IStackframe } from '../../../../typings/es_schemas/raw/fields/Stackframe';
+import { IStackframe } from '../../../../../../../plugins/apm/typings/es_schemas/raw/fields/stackframe';
 
 // @ts-ignore Styled Components has trouble inferring the types of the default props here.
 const Accordion = styled(EuiAccordion)`
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/Context.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/Context.tsx
index 4320156fad003..d289539ca44b1 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/Context.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/Context.tsx
@@ -22,7 +22,7 @@ import { registerLanguage } from 'react-syntax-highlighter/dist/light';
 // @ts-ignore
 import { xcode } from 'react-syntax-highlighter/dist/styles';
 import styled from 'styled-components';
-import { IStackframeWithLineContext } from '../../../../typings/es_schemas/raw/fields/Stackframe';
+import { IStackframeWithLineContext } from '../../../../../../../plugins/apm/typings/es_schemas/raw/fields/stackframe';
 import { borderRadius, px, unit, units } from '../../../style/variables';
 
 registerLanguage('javascript', javascript);
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/FrameHeading.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/FrameHeading.tsx
index 94cad732d5a94..daa722255bdf3 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/FrameHeading.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/FrameHeading.tsx
@@ -7,7 +7,7 @@
 import theme from '@elastic/eui/dist/eui_theme_light.json';
 import React, { Fragment } from 'react';
 import styled from 'styled-components';
-import { IStackframe } from '../../../../typings/es_schemas/raw/fields/Stackframe';
+import { IStackframe } from '../../../../../../../plugins/apm/typings/es_schemas/raw/fields/stackframe';
 import { fontFamilyCode, fontSize, px, units } from '../../../style/variables';
 
 const FileDetails = styled.div`
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/LibraryStacktrace.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/LibraryStacktrace.tsx
index f62ba95407893..be6595153aa77 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/LibraryStacktrace.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/LibraryStacktrace.tsx
@@ -8,7 +8,7 @@ import { EuiAccordion } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
 import React from 'react';
 import styled from 'styled-components';
-import { IStackframe } from '../../../../typings/es_schemas/raw/fields/Stackframe';
+import { IStackframe } from '../../../../../../../plugins/apm/typings/es_schemas/raw/fields/stackframe';
 import { Stackframe } from './Stackframe';
 import { px, unit } from '../../../style/variables';
 
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/Stackframe.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/Stackframe.tsx
index 3586368db84fc..404d474a7960a 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/Stackframe.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/Stackframe.tsx
@@ -11,7 +11,7 @@ import { EuiAccordion } from '@elastic/eui';
 import {
   IStackframe,
   IStackframeWithLineContext
-} from '../../../../typings/es_schemas/raw/fields/Stackframe';
+} from '../../../../../../../plugins/apm/typings/es_schemas/raw/fields/stackframe';
 import {
   borderRadius,
   fontFamilyCode,
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/Variables.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/Variables.tsx
index 6d1b10c90a999..0786116a659c7 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/Variables.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/Variables.tsx
@@ -10,7 +10,7 @@ import { EuiAccordion } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
 import React from 'react';
 import { borderRadius, px, unit, units } from '../../../style/variables';
-import { IStackframe } from '../../../../typings/es_schemas/raw/fields/Stackframe';
+import { IStackframe } from '../../../../../../../plugins/apm/typings/es_schemas/raw/fields/stackframe';
 import { KeyValueTable } from '../KeyValueTable';
 import { flattenObject } from '../../../utils/flattenObject';
 
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/Stackframe.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/Stackframe.test.tsx
index b485b4f844f64..1b2268326e6be 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/Stackframe.test.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/Stackframe.test.tsx
@@ -6,7 +6,7 @@
 
 import { mount, ReactWrapper, shallow } from 'enzyme';
 import React from 'react';
-import { IStackframe } from '../../../../../typings/es_schemas/raw/fields/Stackframe';
+import { IStackframe } from '../../../../../../../../plugins/apm/typings/es_schemas/raw/fields/stackframe';
 import { Stackframe } from '../Stackframe';
 import stacktracesMock from './stacktraces.json';
 
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/index.test.ts b/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/index.test.ts
index a6db266e657ce..9b6d74033e1c5 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/index.test.ts
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/index.test.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { IStackframe } from '../../../../../typings/es_schemas/raw/fields/Stackframe';
+import { IStackframe } from '../../../../../../../../plugins/apm/typings/es_schemas/raw/fields/stackframe';
 import { getGroupedStackframes } from '../index';
 import stacktracesMock from './stacktraces.json';
 
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/index.tsx
index b7963b5c75a92..141ed544a6166 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/index.tsx
@@ -8,7 +8,7 @@ import { EuiSpacer } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
 import { isEmpty, last } from 'lodash';
 import React, { Fragment } from 'react';
-import { IStackframe } from '../../../../typings/es_schemas/raw/fields/Stackframe';
+import { IStackframe } from '../../../../../../../plugins/apm/typings/es_schemas/raw/fields/stackframe';
 import { EmptyMessage } from '../../shared/EmptyMessage';
 import { LibraryStacktrace } from './LibraryStacktrace';
 import { Stackframe } from './Stackframe';
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/StickyProperties/StickyProperties.test.js b/x-pack/legacy/plugins/apm/public/components/shared/StickyProperties/StickyProperties.test.js
index b6acb6904f865..08283dee3825d 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/StickyProperties/StickyProperties.test.js
+++ b/x-pack/legacy/plugins/apm/public/components/shared/StickyProperties/StickyProperties.test.js
@@ -7,7 +7,10 @@
 import React from 'react';
 import { StickyProperties } from './index';
 import { shallow } from 'enzyme';
-import { USER_ID, URL_FULL } from '../../../../common/elasticsearch_fieldnames';
+import {
+  USER_ID,
+  URL_FULL
+} from '../../../../../../../plugins/apm/common/elasticsearch_fieldnames';
 import { mockMoment } from '../../../utils/testHelpers';
 
 describe('StickyProperties', () => {
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionSummary.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionSummary.tsx
index 51da61cd7c1a6..f0fe57e46f2fe 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionSummary.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionSummary.tsx
@@ -4,12 +4,12 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 import React from 'react';
-import { Transaction } from '../../../../typings/es_schemas/ui/Transaction';
+import { Transaction } from '../../../../../../../plugins/apm/typings/es_schemas/ui/transaction';
 import { Summary } from './';
 import { TimestampTooltip } from '../TimestampTooltip';
 import { DurationSummaryItem } from './DurationSummaryItem';
 import { ErrorCountSummaryItemBadge } from './ErrorCountSummaryItemBadge';
-import { isRumAgentName } from '../../../../common/agent_name';
+import { isRumAgentName } from '../../../../../../../plugins/apm/common/agent_name';
 import { HttpInfoSummaryItem } from './HttpInfoSummaryItem';
 import { TransactionResultSummaryItem } from './TransactionResultSummaryItem';
 import { UserAgentSummaryItem } from './UserAgentSummaryItem';
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/UserAgentSummaryItem.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Summary/UserAgentSummaryItem.tsx
index 1a019ae0a5e42..10a6bcc1ef7bd 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Summary/UserAgentSummaryItem.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Summary/UserAgentSummaryItem.tsx
@@ -9,7 +9,7 @@ import styled from 'styled-components';
 import theme from '@elastic/eui/dist/eui_theme_light.json';
 import { EuiToolTip } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
-import { UserAgent } from '../../../../typings/es_schemas/raw/fields/UserAgent';
+import { UserAgent } from '../../../../../../../plugins/apm/typings/es_schemas/raw/fields/user_agent';
 
 type UserAgentSummaryItemProps = UserAgent;
 
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/__fixtures__/transactions.ts b/x-pack/legacy/plugins/apm/public/components/shared/Summary/__fixtures__/transactions.ts
index f4346e47f23c2..05fb73a9e2749 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Summary/__fixtures__/transactions.ts
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Summary/__fixtures__/transactions.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { Transaction } from '../../../../../typings/es_schemas/ui/Transaction';
+import { Transaction } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction';
 
 export const httpOk: Transaction = {
   '@timestamp': '0',
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Summary/index.tsx
index ce6935d1858aa..ef99f3a4933a7 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/Summary/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/Summary/index.tsx
@@ -8,7 +8,7 @@ import { EuiFlexGrid, EuiFlexItem } from '@elastic/eui';
 import styled from 'styled-components';
 import euiLightVars from '@elastic/eui/dist/eui_theme_light.json';
 import { px, units } from '../../../../public/style/variables';
-import { Maybe } from '../../../../typings/common';
+import { Maybe } from '../../../../../../../plugins/apm/typings/common';
 
 interface Props {
   items: Array<Maybe<React.ReactElement>>;
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx
index 99f0b0d4fc223..dd022626807d0 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx
@@ -16,7 +16,7 @@ import {
   SectionSubtitle,
   SectionTitle
 } from '../../../../../../../plugins/observability/public';
-import { Transaction } from '../../../../typings/es_schemas/ui/Transaction';
+import { Transaction } from '../../../../../../../plugins/apm/typings/es_schemas/ui/transaction';
 import { useApmPluginContext } from '../../../hooks/useApmPluginContext';
 import { useLocation } from '../../../hooks/useLocation';
 import { useUrlParams } from '../../../hooks/useUrlParams';
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx
index e9f89034f58ee..ac3616e8c134c 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx
@@ -7,7 +7,7 @@
 import React from 'react';
 import { render, fireEvent } from '@testing-library/react';
 import { TransactionActionMenu } from '../TransactionActionMenu';
-import { Transaction } from '../../../../../typings/es_schemas/ui/Transaction';
+import { Transaction } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction';
 import * as Transactions from './mockData';
 import { MockApmPluginContextWrapper } from '../../../../utils/testHelpers';
 
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts
index 9f07bd508c95c..3032dd1704f4e 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts
+++ b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts
@@ -5,7 +5,7 @@
  */
 import { Location } from 'history';
 import { getSections } from '../sections';
-import { Transaction } from '../../../../../typings/es_schemas/ui/Transaction';
+import { Transaction } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction';
 import { AppMountContextBasePath } from '../../../../context/ApmPluginContext';
 
 describe('Transaction action menu', () => {
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/sections.ts b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/sections.ts
index 31efdb6355563..ffdf0b485da64 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/sections.ts
+++ b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/sections.ts
@@ -8,7 +8,7 @@ import { Location } from 'history';
 import { pick, isEmpty } from 'lodash';
 import moment from 'moment';
 import url from 'url';
-import { Transaction } from '../../../../typings/es_schemas/ui/Transaction';
+import { Transaction } from '../../../../../../../plugins/apm/typings/es_schemas/ui/transaction';
 import { IUrlParams } from '../../../context/UrlParamsContext/types';
 import { getDiscoverHref } from '../Links/DiscoverLinks/DiscoverLink';
 import { getDiscoverQuery } from '../Links/DiscoverLinks/DiscoverTransactionLink';
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownGraph/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownGraph/index.tsx
index a964b425073b5..50ea169c017f9 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownGraph/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownGraph/index.tsx
@@ -7,9 +7,12 @@
 import React, { useMemo } from 'react';
 import numeral from '@elastic/numeral';
 import { throttle } from 'lodash';
-import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n';
-import { Coordinate, TimeSeries } from '../../../../../typings/timeseries';
-import { Maybe } from '../../../../../typings/common';
+import { NOT_AVAILABLE_LABEL } from '../../../../../../../../plugins/apm/common/i18n';
+import {
+  Coordinate,
+  TimeSeries
+} from '../../../../../../../../plugins/apm/typings/timeseries';
+import { Maybe } from '../../../../../../../../plugins/apm/typings/common';
 import { TransactionLineChart } from '../../charts/TransactionCharts/TransactionLineChart';
 import { asPercent } from '../../../../utils/formatters';
 import { unit } from '../../../../style/variables';
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/AnnotationsPlot.tsx b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/AnnotationsPlot.tsx
index 6eff4759b2e7c..ec6168df5b134 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/AnnotationsPlot.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/AnnotationsPlot.tsx
@@ -14,8 +14,8 @@ import {
   EuiText
 } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
-import { Maybe } from '../../../../../typings/common';
-import { Annotation } from '../../../../../common/annotations';
+import { Maybe } from '../../../../../../../../plugins/apm/typings/common';
+import { Annotation } from '../../../../../../../../plugins/apm/common/annotations';
 import { PlotValues, SharedPlot } from './plotUtils';
 import { asAbsoluteDateTime } from '../../../../utils/formatters';
 
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.test.ts b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.test.ts
index b130deed7f098..bfc5c7c243f31 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.test.ts
+++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.test.ts
@@ -6,7 +6,10 @@
 
 // @ts-ignore
 import * as plotUtils from './plotUtils';
-import { TimeSeries, Coordinate } from '../../../../../typings/timeseries';
+import {
+  TimeSeries,
+  Coordinate
+} from '../../../../../../../../plugins/apm/typings/timeseries';
 
 describe('plotUtils', () => {
   describe('getPlotValues', () => {
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.tsx b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.tsx
index 64350a5741647..c489c270d19ac 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.tsx
@@ -11,7 +11,10 @@ import d3 from 'd3';
 import PropTypes from 'prop-types';
 import React from 'react';
 
-import { TimeSeries, Coordinate } from '../../../../../typings/timeseries';
+import {
+  TimeSeries,
+  Coordinate
+} from '../../../../../../../../plugins/apm/typings/timeseries';
 import { unit } from '../../../../style/variables';
 import { getDomainTZ, getTimeTicksTZ } from '../helper/timezone';
 
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/MetricsChart/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/charts/MetricsChart/index.tsx
index 30dcc99af31b9..2ceac87d9aab3 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/charts/MetricsChart/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/MetricsChart/index.tsx
@@ -5,7 +5,8 @@
  */
 import { EuiTitle } from '@elastic/eui';
 import React from 'react';
-import { GenericMetricsChart } from '../../../../../server/lib/metrics/transform_metrics_chart';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { GenericMetricsChart } from '../../../../../../../../plugins/apm/server/lib/metrics/transform_metrics_chart';
 // @ts-ignore
 import CustomPlot from '../CustomPlot';
 import {
@@ -16,10 +17,10 @@ import {
   getFixedByteFormatter,
   asDuration
 } from '../../../../utils/formatters';
-import { Coordinate } from '../../../../../typings/timeseries';
+import { Coordinate } from '../../../../../../../../plugins/apm/typings/timeseries';
 import { isValidCoordinateValue } from '../../../../utils/isValidCoordinateValue';
 import { useChartsSync } from '../../../../hooks/useChartsSync';
-import { Maybe } from '../../../../../typings/common';
+import { Maybe } from '../../../../../../../../plugins/apm/typings/common';
 
 interface Props {
   start: Maybe<number | string>;
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/ErrorMarker.tsx b/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/ErrorMarker.tsx
index 51368a4fb946d..48265ce7c80a8 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/ErrorMarker.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/ErrorMarker.tsx
@@ -11,7 +11,7 @@ import styled from 'styled-components';
 import {
   TRACE_ID,
   TRANSACTION_ID
-} from '../../../../../../common/elasticsearch_fieldnames';
+} from '../../../../../../../../../plugins/apm/common/elasticsearch_fieldnames';
 import { useUrlParams } from '../../../../../hooks/useUrlParams';
 import { px, unit, units } from '../../../../../style/variables';
 import { asDuration } from '../../../../../utils/formatters';
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/TransactionLineChart/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/TransactionLineChart/index.tsx
index 27c829f63cf0a..c9c31b05e264c 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/TransactionLineChart/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/TransactionLineChart/index.tsx
@@ -8,7 +8,7 @@ import React, { useCallback } from 'react';
 import {
   Coordinate,
   RectCoordinate
-} from '../../../../../../typings/timeseries';
+} from '../../../../../../../../../plugins/apm/typings/timeseries';
 import { useChartsSync } from '../../../../../hooks/useChartsSync';
 // @ts-ignore
 import CustomPlot from '../../CustomPlot';
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx
index b0555da705a30..368a39e4ad228 100644
--- a/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx
@@ -19,8 +19,11 @@ import { Location } from 'history';
 import React, { Component } from 'react';
 import { isEmpty, flatten } from 'lodash';
 import styled from 'styled-components';
-import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n';
-import { Coordinate, TimeSeries } from '../../../../../typings/timeseries';
+import { NOT_AVAILABLE_LABEL } from '../../../../../../../../plugins/apm/common/i18n';
+import {
+  Coordinate,
+  TimeSeries
+} from '../../../../../../../../plugins/apm/typings/timeseries';
 import { ITransactionChartData } from '../../../../selectors/chartSelectors';
 import { IUrlParams } from '../../../../context/UrlParamsContext/types';
 import {
@@ -39,7 +42,7 @@ import {
   TRANSACTION_PAGE_LOAD,
   TRANSACTION_ROUTE_CHANGE,
   TRANSACTION_REQUEST
-} from '../../../../../common/transaction_types';
+} from '../../../../../../../../plugins/apm/common/transaction_types';
 
 interface TransactionChartProps {
   hasMLJob: boolean;
diff --git a/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/helpers.ts b/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/helpers.ts
index f1e45fe45255d..b80db0e9ae073 100644
--- a/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/helpers.ts
+++ b/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/helpers.ts
@@ -7,7 +7,7 @@
 import { compact, pick } from 'lodash';
 import datemath from '@elastic/datemath';
 import { IUrlParams } from './types';
-import { ProcessorEvent } from '../../../common/processor_event';
+import { ProcessorEvent } from '../../../../../../plugins/apm/common/processor_event';
 
 interface PathParams {
   processorEvent?: ProcessorEvent;
diff --git a/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/index.tsx b/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/index.tsx
index 58057b2a9a201..588936039c2bc 100644
--- a/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/index.tsx
@@ -16,11 +16,12 @@ import { uniqueId, mapValues } from 'lodash';
 import { IUrlParams } from './types';
 import { getParsedDate } from './helpers';
 import { resolveUrlParams } from './resolveUrlParams';
-import { UIFilters } from '../../../typings/ui-filters';
+import { UIFilters } from '../../../../../../plugins/apm/typings/ui_filters';
 import {
   localUIFilterNames,
   LocalUIFilterName
-} from '../../../server/lib/ui_filters/local_ui_filters/config';
+  // eslint-disable-next-line @kbn/eslint/no-restricted-paths
+} from '../../../../../../plugins/apm/server/lib/ui_filters/local_ui_filters/config';
 import { pickKeys } from '../../utils/pickKeys';
 import { useDeepObjectIdentity } from '../../hooks/useDeepObjectIdentity';
 
diff --git a/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/resolveUrlParams.ts b/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/resolveUrlParams.ts
index 887b45e73462c..f022d2084583b 100644
--- a/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/resolveUrlParams.ts
+++ b/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/resolveUrlParams.ts
@@ -17,7 +17,8 @@ import {
 } from './helpers';
 import { toQuery } from '../../components/shared/Links/url_helpers';
 import { TIMEPICKER_DEFAULTS } from './constants';
-import { localUIFilterNames } from '../../../server/lib/ui_filters/local_ui_filters/config';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { localUIFilterNames } from '../../../../../../plugins/apm/server/lib/ui_filters/local_ui_filters/config';
 import { pickKeys } from '../../utils/pickKeys';
 
 type TimeUrlParams = Pick<
diff --git a/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/types.ts b/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/types.ts
index 496b28e168089..acde09308ab46 100644
--- a/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/types.ts
+++ b/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/types.ts
@@ -4,8 +4,9 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { LocalUIFilterName } from '../../../server/lib/ui_filters/local_ui_filters/config';
-import { ProcessorEvent } from '../../../common/processor_event';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { LocalUIFilterName } from '../../../../../../plugins/apm/server/lib/ui_filters/local_ui_filters/config';
+import { ProcessorEvent } from '../../../../../../plugins/apm/common/processor_event';
 
 export type IUrlParams = {
   detailTab?: string;
diff --git a/x-pack/legacy/plugins/apm/public/hooks/useAvgDurationByBrowser.ts b/x-pack/legacy/plugins/apm/public/hooks/useAvgDurationByBrowser.ts
index a1e9294455d54..256c2fa68bfbc 100644
--- a/x-pack/legacy/plugins/apm/public/hooks/useAvgDurationByBrowser.ts
+++ b/x-pack/legacy/plugins/apm/public/hooks/useAvgDurationByBrowser.ts
@@ -7,9 +7,10 @@
 import theme from '@elastic/eui/dist/eui_theme_light.json';
 import { useFetcher } from './useFetcher';
 import { useUrlParams } from './useUrlParams';
-import { AvgDurationByBrowserAPIResponse } from '../../server/lib/transactions/avg_duration_by_browser';
-import { TimeSeries } from '../../typings/timeseries';
-import { getVizColorForIndex } from '../../common/viz_colors';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { AvgDurationByBrowserAPIResponse } from '../../../../../plugins/apm/server/lib/transactions/avg_duration_by_browser';
+import { TimeSeries } from '../../../../../plugins/apm/typings/timeseries';
+import { getVizColorForIndex } from '../../../../../plugins/apm/common/viz_colors';
 
 function toTimeSeries(data?: AvgDurationByBrowserAPIResponse): TimeSeries[] {
   if (!data) {
diff --git a/x-pack/legacy/plugins/apm/public/hooks/useDynamicIndexPattern.ts b/x-pack/legacy/plugins/apm/public/hooks/useDynamicIndexPattern.ts
index c369806a4616f..747144690bb24 100644
--- a/x-pack/legacy/plugins/apm/public/hooks/useDynamicIndexPattern.ts
+++ b/x-pack/legacy/plugins/apm/public/hooks/useDynamicIndexPattern.ts
@@ -5,7 +5,7 @@
  */
 
 import { useFetcher } from './useFetcher';
-import { ProcessorEvent } from '../../common/processor_event';
+import { ProcessorEvent } from '../../../../../plugins/apm/common/processor_event';
 
 export function useDynamicIndexPattern(
   processorEvent: ProcessorEvent | undefined
diff --git a/x-pack/legacy/plugins/apm/public/hooks/useLocalUIFilters.ts b/x-pack/legacy/plugins/apm/public/hooks/useLocalUIFilters.ts
index 39164e8a5eff4..9f14b2b25fc94 100644
--- a/x-pack/legacy/plugins/apm/public/hooks/useLocalUIFilters.ts
+++ b/x-pack/legacy/plugins/apm/public/hooks/useLocalUIFilters.ts
@@ -6,16 +6,18 @@
 
 import { omit } from 'lodash';
 import { useFetcher } from './useFetcher';
-import { LocalUIFiltersAPIResponse } from '../../server/lib/ui_filters/local_ui_filters';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { LocalUIFiltersAPIResponse } from '../../../../../plugins/apm/server/lib/ui_filters/local_ui_filters';
 import { useUrlParams } from './useUrlParams';
 import {
   LocalUIFilterName,
   localUIFilters
-} from '../../server/lib/ui_filters/local_ui_filters/config';
+  // eslint-disable-next-line @kbn/eslint/no-restricted-paths
+} from '../../../../../plugins/apm/server/lib/ui_filters/local_ui_filters/config';
 import { history } from '../utils/history';
 import { toQuery, fromQuery } from '../components/shared/Links/url_helpers';
 import { removeUndefinedProps } from '../context/UrlParamsContext/helpers';
-import { PROJECTION } from '../../common/projections/typings';
+import { PROJECTION } from '../../../../../plugins/apm/common/projections/typings';
 import { pickKeys } from '../utils/pickKeys';
 import { useCallApi } from './useCallApi';
 
diff --git a/x-pack/legacy/plugins/apm/public/hooks/useServiceMetricCharts.ts b/x-pack/legacy/plugins/apm/public/hooks/useServiceMetricCharts.ts
index 51a632ac5f0a6..72618a6254f4c 100644
--- a/x-pack/legacy/plugins/apm/public/hooks/useServiceMetricCharts.ts
+++ b/x-pack/legacy/plugins/apm/public/hooks/useServiceMetricCharts.ts
@@ -4,7 +4,8 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { MetricsChartsByAgentAPIResponse } from '../../server/lib/metrics/get_metrics_chart_data_by_agent';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { MetricsChartsByAgentAPIResponse } from '../../../../../plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent';
 import { IUrlParams } from '../context/UrlParamsContext/types';
 import { useUiFilters } from '../context/UrlParamsContext';
 import { useFetcher } from './useFetcher';
diff --git a/x-pack/legacy/plugins/apm/public/hooks/useTransactionDistribution.ts b/x-pack/legacy/plugins/apm/public/hooks/useTransactionDistribution.ts
index e50ea7eab187f..9a93a2334924a 100644
--- a/x-pack/legacy/plugins/apm/public/hooks/useTransactionDistribution.ts
+++ b/x-pack/legacy/plugins/apm/public/hooks/useTransactionDistribution.ts
@@ -7,7 +7,8 @@
 import { IUrlParams } from '../context/UrlParamsContext/types';
 import { useFetcher } from './useFetcher';
 import { useUiFilters } from '../context/UrlParamsContext';
-import { TransactionDistributionAPIResponse } from '../../server/lib/transactions/distribution';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { TransactionDistributionAPIResponse } from '../../../../../plugins/apm/server/lib/transactions/distribution';
 
 const INITIAL_DATA = {
   buckets: [] as TransactionDistributionAPIResponse['buckets'],
diff --git a/x-pack/legacy/plugins/apm/public/hooks/useTransactionList.ts b/x-pack/legacy/plugins/apm/public/hooks/useTransactionList.ts
index 15356139dd607..6ede77023790b 100644
--- a/x-pack/legacy/plugins/apm/public/hooks/useTransactionList.ts
+++ b/x-pack/legacy/plugins/apm/public/hooks/useTransactionList.ts
@@ -8,7 +8,8 @@ import { useMemo } from 'react';
 import { IUrlParams } from '../context/UrlParamsContext/types';
 import { useUiFilters } from '../context/UrlParamsContext';
 import { useFetcher } from './useFetcher';
-import { TransactionGroupListAPIResponse } from '../../server/lib/transaction_groups';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { TransactionGroupListAPIResponse } from '../../../../../plugins/apm/server/lib/transaction_groups';
 
 const getRelativeImpact = (
   impact: number,
diff --git a/x-pack/legacy/plugins/apm/public/selectors/chartSelectors.ts b/x-pack/legacy/plugins/apm/public/selectors/chartSelectors.ts
index 75a558ac81a54..d60b63e243d71 100644
--- a/x-pack/legacy/plugins/apm/public/selectors/chartSelectors.ts
+++ b/x-pack/legacy/plugins/apm/public/selectors/chartSelectors.ts
@@ -9,13 +9,15 @@ import { i18n } from '@kbn/i18n';
 import { difference, zipObject } from 'lodash';
 import mean from 'lodash.mean';
 import { rgba } from 'polished';
-import { TimeSeriesAPIResponse } from '../../server/lib/transactions/charts';
-import { ApmTimeSeriesResponse } from '../../server/lib/transactions/charts/get_timeseries_data/transform';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { TimeSeriesAPIResponse } from '../../../../../plugins/apm/server/lib/transactions/charts';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { ApmTimeSeriesResponse } from '../../../../../plugins/apm/server/lib/transactions/charts/get_timeseries_data/transform';
 import {
   Coordinate,
   RectCoordinate,
   TimeSeries
-} from '../../typings/timeseries';
+} from '../../../../../plugins/apm/typings/timeseries';
 import { asDecimal, tpmUnit, convertTo } from '../utils/formatters';
 import { IUrlParams } from '../context/UrlParamsContext/types';
 import { getEmptySeries } from '../components/shared/charts/CustomPlot/getEmptySeries';
diff --git a/x-pack/legacy/plugins/apm/public/services/rest/createCallApmApi.ts b/x-pack/legacy/plugins/apm/public/services/rest/createCallApmApi.ts
index b4d060adec5a1..220320216788a 100644
--- a/x-pack/legacy/plugins/apm/public/services/rest/createCallApmApi.ts
+++ b/x-pack/legacy/plugins/apm/public/services/rest/createCallApmApi.ts
@@ -5,8 +5,10 @@
  */
 import { HttpSetup } from 'kibana/public';
 import { callApi, FetchOptions } from './callApi';
-import { APMAPI } from '../../../server/routes/create_apm_api';
-import { Client } from '../../../server/routes/typings';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { APMAPI } from '../../../../../../plugins/apm/server/routes/create_apm_api';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { Client } from '../../../../../../plugins/apm/server/routes/typings';
 
 export type APMClient = Client<APMAPI['_S']>;
 export type APMClientOptions = Omit<FetchOptions, 'query' | 'body'> & {
diff --git a/x-pack/legacy/plugins/apm/public/services/rest/ml.ts b/x-pack/legacy/plugins/apm/public/services/rest/ml.ts
index 187a051b1b044..5e64d7e1ce716 100644
--- a/x-pack/legacy/plugins/apm/public/services/rest/ml.ts
+++ b/x-pack/legacy/plugins/apm/public/services/rest/ml.ts
@@ -9,8 +9,11 @@ import {
   PROCESSOR_EVENT,
   SERVICE_NAME,
   TRANSACTION_TYPE
-} from '../../../common/elasticsearch_fieldnames';
-import { getMlJobId, getMlPrefix } from '../../../common/ml_job_constants';
+} from '../../../../../../plugins/apm/common/elasticsearch_fieldnames';
+import {
+  getMlJobId,
+  getMlPrefix
+} from '../../../../../../plugins/apm/common/ml_job_constants';
 import { callApi } from './callApi';
 import { ESFilter } from '../../../../../../plugins/apm/typings/elasticsearch';
 import { createCallApmApi, APMClient } from './createCallApmApi';
diff --git a/x-pack/legacy/plugins/apm/public/utils/flattenObject.ts b/x-pack/legacy/plugins/apm/public/utils/flattenObject.ts
index 295ea1f9f900f..020bfec2cbd6a 100644
--- a/x-pack/legacy/plugins/apm/public/utils/flattenObject.ts
+++ b/x-pack/legacy/plugins/apm/public/utils/flattenObject.ts
@@ -5,7 +5,7 @@
  */
 
 import { compact, isObject } from 'lodash';
-import { Maybe } from '../../typings/common';
+import { Maybe } from '../../../../../plugins/apm/typings/common';
 
 export interface KeyValuePair {
   key: string;
diff --git a/x-pack/legacy/plugins/apm/public/utils/formatters/duration.ts b/x-pack/legacy/plugins/apm/public/utils/formatters/duration.ts
index 39341e1ff4443..681d876ca3beb 100644
--- a/x-pack/legacy/plugins/apm/public/utils/formatters/duration.ts
+++ b/x-pack/legacy/plugins/apm/public/utils/formatters/duration.ts
@@ -7,10 +7,10 @@
 import { i18n } from '@kbn/i18n';
 import moment from 'moment';
 import { memoize } from 'lodash';
-import { NOT_AVAILABLE_LABEL } from '../../../common/i18n';
+import { NOT_AVAILABLE_LABEL } from '../../../../../../plugins/apm/common/i18n';
 import { asDecimal, asInteger } from './formatters';
 import { TimeUnit } from './datetime';
-import { Maybe } from '../../../typings/common';
+import { Maybe } from '../../../../../../plugins/apm/typings/common';
 
 interface FormatterOptions {
   defaultValue?: string;
diff --git a/x-pack/legacy/plugins/apm/public/utils/formatters/size.ts b/x-pack/legacy/plugins/apm/public/utils/formatters/size.ts
index 2cdf8af1d46de..8fe6ebf3e573d 100644
--- a/x-pack/legacy/plugins/apm/public/utils/formatters/size.ts
+++ b/x-pack/legacy/plugins/apm/public/utils/formatters/size.ts
@@ -5,7 +5,7 @@
  */
 import { memoize } from 'lodash';
 import { asDecimal } from './formatters';
-import { Maybe } from '../../../typings/common';
+import { Maybe } from '../../../../../../plugins/apm/typings/common';
 
 function asKilobytes(value: number) {
   return `${asDecimal(value / 1000)} KB`;
diff --git a/x-pack/legacy/plugins/apm/public/utils/getRangeFromTimeSeries.ts b/x-pack/legacy/plugins/apm/public/utils/getRangeFromTimeSeries.ts
index 1865d5ae574a7..4301ead2fc79f 100644
--- a/x-pack/legacy/plugins/apm/public/utils/getRangeFromTimeSeries.ts
+++ b/x-pack/legacy/plugins/apm/public/utils/getRangeFromTimeSeries.ts
@@ -5,7 +5,7 @@
  */
 
 import { flatten } from 'lodash';
-import { TimeSeries } from '../../typings/timeseries';
+import { TimeSeries } from '../../../../../plugins/apm/typings/timeseries';
 
 export const getRangeFromTimeSeries = (timeseries: TimeSeries[]) => {
   const dataPoints = flatten(timeseries.map(series => series.data));
diff --git a/x-pack/legacy/plugins/apm/public/utils/isValidCoordinateValue.ts b/x-pack/legacy/plugins/apm/public/utils/isValidCoordinateValue.ts
index c36efc232b782..f7c13603c3535 100644
--- a/x-pack/legacy/plugins/apm/public/utils/isValidCoordinateValue.ts
+++ b/x-pack/legacy/plugins/apm/public/utils/isValidCoordinateValue.ts
@@ -3,7 +3,7 @@
  * or more contributor license agreements. Licensed under the Elastic License;
  * you may not use this file except in compliance with the Elastic License.
  */
-import { Maybe } from '../../typings/common';
+import { Maybe } from '../../../../../plugins/apm/typings/common';
 
 export const isValidCoordinateValue = (value: Maybe<number>): value is number =>
   value !== null && value !== undefined;
diff --git a/x-pack/legacy/plugins/apm/public/utils/testHelpers.tsx b/x-pack/legacy/plugins/apm/public/utils/testHelpers.tsx
index bcdeaa6d5ac23..dec2257746e50 100644
--- a/x-pack/legacy/plugins/apm/public/utils/testHelpers.tsx
+++ b/x-pack/legacy/plugins/apm/public/utils/testHelpers.tsx
@@ -18,7 +18,7 @@ import { MemoryRouter } from 'react-router-dom';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { APMConfig } from '../../../../../plugins/apm/server';
 import { LocationProvider } from '../context/LocationContext';
-import { PromiseReturnType } from '../../typings/common';
+import { PromiseReturnType } from '../../../../../plugins/apm/typings/common';
 import {
   ESFilter,
   ESSearchResponse,
diff --git a/x-pack/legacy/plugins/apm/server/lib/helpers/saved_objects_client.test.ts b/x-pack/legacy/plugins/apm/server/lib/helpers/saved_objects_client.test.ts
deleted file mode 100644
index c685ffdd801dc..0000000000000
--- a/x-pack/legacy/plugins/apm/server/lib/helpers/saved_objects_client.test.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { getInternalSavedObjectsClient } from './saved_objects_client';
-
-describe('saved_objects/client', () => {
-  describe('getSavedObjectsClient', () => {
-    let server: any;
-    let savedObjectsClientInstance: any;
-    let callWithInternalUser: any;
-    let internalRepository: any;
-
-    beforeEach(() => {
-      savedObjectsClientInstance = { create: jest.fn() };
-      callWithInternalUser = jest.fn();
-      internalRepository = jest.fn();
-      server = {
-        savedObjects: {
-          SavedObjectsClient: jest.fn(() => savedObjectsClientInstance),
-          getSavedObjectsRepository: jest.fn(() => internalRepository)
-        },
-        plugins: {
-          elasticsearch: {
-            getCluster: jest.fn(() => ({ callWithInternalUser }))
-          }
-        }
-      };
-    });
-
-    it('should use internal user "admin"', () => {
-      getInternalSavedObjectsClient(server);
-
-      expect(server.plugins.elasticsearch.getCluster).toHaveBeenCalledWith(
-        'admin'
-      );
-    });
-
-    it('should call getSavedObjectsRepository with a cluster using the internal user context', () => {
-      getInternalSavedObjectsClient(server);
-
-      expect(
-        server.savedObjects.getSavedObjectsRepository
-      ).toHaveBeenCalledWith(callWithInternalUser);
-    });
-
-    it('should return a SavedObjectsClient initialized with the saved objects internal repository', () => {
-      const internalSavedObjectsClient = getInternalSavedObjectsClient(server);
-
-      expect(internalSavedObjectsClient).toBe(savedObjectsClientInstance);
-      expect(server.savedObjects.SavedObjectsClient).toHaveBeenCalledWith(
-        internalRepository
-      );
-    });
-  });
-});
diff --git a/x-pack/legacy/plugins/apm/server/lib/helpers/saved_objects_client.ts b/x-pack/legacy/plugins/apm/server/lib/helpers/saved_objects_client.ts
deleted file mode 100644
index ced6f77944b6c..0000000000000
--- a/x-pack/legacy/plugins/apm/server/lib/helpers/saved_objects_client.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { APMLegacyServer } from '../../routes/typings';
-
-export function getInternalSavedObjectsClient(server: APMLegacyServer) {
-  const { SavedObjectsClient, getSavedObjectsRepository } = server.savedObjects;
-  const { callWithInternalUser } = server.plugins.elasticsearch.getCluster(
-    'admin'
-  );
-  const internalRepository = getSavedObjectsRepository(callWithInternalUser);
-  return new SavedObjectsClient(internalRepository);
-}
diff --git a/x-pack/plugins/apm/.prettierrc b/x-pack/plugins/apm/.prettierrc
new file mode 100644
index 0000000000000..650cb880f6f5a
--- /dev/null
+++ b/x-pack/plugins/apm/.prettierrc
@@ -0,0 +1,4 @@
+{
+  "singleQuote": true,
+  "semi": true
+}
diff --git a/x-pack/legacy/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap b/x-pack/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap
similarity index 100%
rename from x-pack/legacy/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap
rename to x-pack/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap
diff --git a/x-pack/legacy/plugins/apm/common/agent_configuration_constants.ts b/x-pack/plugins/apm/common/agent_configuration_constants.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/common/agent_configuration_constants.ts
rename to x-pack/plugins/apm/common/agent_configuration_constants.ts
diff --git a/x-pack/legacy/plugins/apm/common/agent_name.ts b/x-pack/plugins/apm/common/agent_name.ts
similarity index 94%
rename from x-pack/legacy/plugins/apm/common/agent_name.ts
rename to x-pack/plugins/apm/common/agent_name.ts
index daaeb14dc9eaa..bb68eb88b8e18 100644
--- a/x-pack/legacy/plugins/apm/common/agent_name.ts
+++ b/x-pack/plugins/apm/common/agent_name.ts
@@ -11,7 +11,7 @@
  * definitions in mappings.json (for telemetry), the AgentName type, and the
  * agentNames object.
  */
-import { AgentName } from '../typings/es_schemas/ui/fields/Agent';
+import { AgentName } from '../typings/es_schemas/ui/fields/agent';
 
 const agentNames: { [agentName in AgentName]: agentName } = {
   python: 'python',
diff --git a/x-pack/legacy/plugins/apm/common/annotations.ts b/x-pack/plugins/apm/common/annotations.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/common/annotations.ts
rename to x-pack/plugins/apm/common/annotations.ts
diff --git a/x-pack/legacy/plugins/apm/common/apm_saved_object_constants.ts b/x-pack/plugins/apm/common/apm_saved_object_constants.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/common/apm_saved_object_constants.ts
rename to x-pack/plugins/apm/common/apm_saved_object_constants.ts
diff --git a/x-pack/legacy/plugins/apm/common/elasticsearch_fieldnames.test.ts b/x-pack/plugins/apm/common/elasticsearch_fieldnames.test.ts
similarity index 95%
rename from x-pack/legacy/plugins/apm/common/elasticsearch_fieldnames.test.ts
rename to x-pack/plugins/apm/common/elasticsearch_fieldnames.test.ts
index 82a679ccdd32e..1add2427d16a0 100644
--- a/x-pack/legacy/plugins/apm/common/elasticsearch_fieldnames.test.ts
+++ b/x-pack/plugins/apm/common/elasticsearch_fieldnames.test.ts
@@ -6,9 +6,9 @@
 
 import { get } from 'lodash';
 import { AllowUnknownProperties } from '../typings/common';
-import { APMError } from '../typings/es_schemas/ui/APMError';
-import { Span } from '../typings/es_schemas/ui/Span';
-import { Transaction } from '../typings/es_schemas/ui/Transaction';
+import { APMError } from '../typings/es_schemas/ui/apm_error';
+import { Span } from '../typings/es_schemas/ui/span';
+import { Transaction } from '../typings/es_schemas/ui/transaction';
 import * as fieldnames from './elasticsearch_fieldnames';
 
 describe('Transaction', () => {
diff --git a/x-pack/legacy/plugins/apm/common/elasticsearch_fieldnames.ts b/x-pack/plugins/apm/common/elasticsearch_fieldnames.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/common/elasticsearch_fieldnames.ts
rename to x-pack/plugins/apm/common/elasticsearch_fieldnames.ts
diff --git a/x-pack/legacy/plugins/apm/common/environment_filter_values.ts b/x-pack/plugins/apm/common/environment_filter_values.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/common/environment_filter_values.ts
rename to x-pack/plugins/apm/common/environment_filter_values.ts
diff --git a/x-pack/legacy/plugins/apm/common/i18n.ts b/x-pack/plugins/apm/common/i18n.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/common/i18n.ts
rename to x-pack/plugins/apm/common/i18n.ts
diff --git a/x-pack/legacy/plugins/apm/common/index_pattern_constants.ts b/x-pack/plugins/apm/common/index_pattern_constants.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/common/index_pattern_constants.ts
rename to x-pack/plugins/apm/common/index_pattern_constants.ts
diff --git a/x-pack/legacy/plugins/apm/common/ml_job_constants.test.ts b/x-pack/plugins/apm/common/ml_job_constants.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/common/ml_job_constants.test.ts
rename to x-pack/plugins/apm/common/ml_job_constants.test.ts
diff --git a/x-pack/legacy/plugins/apm/common/ml_job_constants.ts b/x-pack/plugins/apm/common/ml_job_constants.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/common/ml_job_constants.ts
rename to x-pack/plugins/apm/common/ml_job_constants.ts
diff --git a/x-pack/legacy/plugins/apm/common/processor_event.ts b/x-pack/plugins/apm/common/processor_event.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/common/processor_event.ts
rename to x-pack/plugins/apm/common/processor_event.ts
diff --git a/x-pack/legacy/plugins/apm/common/projections/errors.ts b/x-pack/plugins/apm/common/projections/errors.ts
similarity index 90%
rename from x-pack/legacy/plugins/apm/common/projections/errors.ts
rename to x-pack/plugins/apm/common/projections/errors.ts
index 27e1de43a1a94..b8de049f3bce9 100644
--- a/x-pack/legacy/plugins/apm/common/projections/errors.ts
+++ b/x-pack/plugins/apm/common/projections/errors.ts
@@ -8,12 +8,14 @@ import {
   Setup,
   SetupTimeRange,
   SetupUIFilters
+  // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 } from '../../server/lib/helpers/setup_request';
 import {
   PROCESSOR_EVENT,
   SERVICE_NAME,
   ERROR_GROUP_ID
 } from '../elasticsearch_fieldnames';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { rangeFilter } from '../../server/lib/helpers/range_filter';
 
 export function getErrorGroupsProjection({
diff --git a/x-pack/legacy/plugins/apm/common/projections/metrics.ts b/x-pack/plugins/apm/common/projections/metrics.ts
similarity index 92%
rename from x-pack/legacy/plugins/apm/common/projections/metrics.ts
rename to x-pack/plugins/apm/common/projections/metrics.ts
index 066f5789752a7..799c84ae3c1c9 100644
--- a/x-pack/legacy/plugins/apm/common/projections/metrics.ts
+++ b/x-pack/plugins/apm/common/projections/metrics.ts
@@ -8,12 +8,14 @@ import {
   Setup,
   SetupTimeRange,
   SetupUIFilters
+  // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 } from '../../server/lib/helpers/setup_request';
 import {
   SERVICE_NAME,
   PROCESSOR_EVENT,
   SERVICE_NODE_NAME
 } from '../elasticsearch_fieldnames';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { rangeFilter } from '../../server/lib/helpers/range_filter';
 import { SERVICE_NODE_NAME_MISSING } from '../service_nodes';
 
diff --git a/x-pack/legacy/plugins/apm/common/projections/service_nodes.ts b/x-pack/plugins/apm/common/projections/service_nodes.ts
similarity index 94%
rename from x-pack/legacy/plugins/apm/common/projections/service_nodes.ts
rename to x-pack/plugins/apm/common/projections/service_nodes.ts
index 42fcdd38cc9fd..c65d29e8ea00d 100644
--- a/x-pack/legacy/plugins/apm/common/projections/service_nodes.ts
+++ b/x-pack/plugins/apm/common/projections/service_nodes.ts
@@ -8,6 +8,7 @@ import {
   Setup,
   SetupTimeRange,
   SetupUIFilters
+  // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 } from '../../server/lib/helpers/setup_request';
 import { SERVICE_NODE_NAME } from '../elasticsearch_fieldnames';
 import { mergeProjection } from './util/merge_projection';
diff --git a/x-pack/legacy/plugins/apm/common/projections/services.ts b/x-pack/plugins/apm/common/projections/services.ts
similarity index 90%
rename from x-pack/legacy/plugins/apm/common/projections/services.ts
rename to x-pack/plugins/apm/common/projections/services.ts
index 3531607d59fc4..bdb0c4ef97895 100644
--- a/x-pack/legacy/plugins/apm/common/projections/services.ts
+++ b/x-pack/plugins/apm/common/projections/services.ts
@@ -8,8 +8,10 @@ import {
   Setup,
   SetupUIFilters,
   SetupTimeRange
+  // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 } from '../../server/lib/helpers/setup_request';
 import { SERVICE_NAME, PROCESSOR_EVENT } from '../elasticsearch_fieldnames';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { rangeFilter } from '../../server/lib/helpers/range_filter';
 
 export function getServicesProjection({
diff --git a/x-pack/legacy/plugins/apm/common/projections/transaction_groups.ts b/x-pack/plugins/apm/common/projections/transaction_groups.ts
similarity index 91%
rename from x-pack/legacy/plugins/apm/common/projections/transaction_groups.ts
rename to x-pack/plugins/apm/common/projections/transaction_groups.ts
index abda606f69384..c19a5d002c015 100644
--- a/x-pack/legacy/plugins/apm/common/projections/transaction_groups.ts
+++ b/x-pack/plugins/apm/common/projections/transaction_groups.ts
@@ -8,8 +8,10 @@ import {
   Setup,
   SetupTimeRange,
   SetupUIFilters
+  // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 } from '../../server/lib/helpers/setup_request';
 import { TRANSACTION_NAME, PARENT_ID } from '../elasticsearch_fieldnames';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { Options } from '../../server/lib/transaction_groups/fetcher';
 import { getTransactionsProjection } from './transactions';
 import { mergeProjection } from './util/merge_projection';
diff --git a/x-pack/legacy/plugins/apm/common/projections/transactions.ts b/x-pack/plugins/apm/common/projections/transactions.ts
similarity index 92%
rename from x-pack/legacy/plugins/apm/common/projections/transactions.ts
rename to x-pack/plugins/apm/common/projections/transactions.ts
index ecbd0c8bf1a31..34de5e8d2833a 100644
--- a/x-pack/legacy/plugins/apm/common/projections/transactions.ts
+++ b/x-pack/plugins/apm/common/projections/transactions.ts
@@ -8,6 +8,7 @@ import {
   Setup,
   SetupTimeRange,
   SetupUIFilters
+  // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 } from '../../server/lib/helpers/setup_request';
 import {
   SERVICE_NAME,
@@ -15,6 +16,7 @@ import {
   PROCESSOR_EVENT,
   TRANSACTION_NAME
 } from '../elasticsearch_fieldnames';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { rangeFilter } from '../../server/lib/helpers/range_filter';
 
 export function getTransactionsProjection({
diff --git a/x-pack/legacy/plugins/apm/common/projections/typings.ts b/x-pack/plugins/apm/common/projections/typings.ts
similarity index 81%
rename from x-pack/legacy/plugins/apm/common/projections/typings.ts
rename to x-pack/plugins/apm/common/projections/typings.ts
index 08a7bee5412a5..2b55395b70c6b 100644
--- a/x-pack/legacy/plugins/apm/common/projections/typings.ts
+++ b/x-pack/plugins/apm/common/projections/typings.ts
@@ -4,14 +4,11 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import {
-  ESSearchRequest,
-  ESSearchBody
-} from '../../../../../plugins/apm/typings/elasticsearch';
+import { ESSearchRequest, ESSearchBody } from '../../typings/elasticsearch';
 import {
   AggregationOptionsByType,
   AggregationInputMap
-} from '../../../../../plugins/apm/typings/elasticsearch/aggregations';
+} from '../../typings/elasticsearch/aggregations';
 
 export type Projection = Omit<ESSearchRequest, 'body'> & {
   body: Omit<ESSearchBody, 'aggs'> & {
diff --git a/x-pack/legacy/plugins/apm/common/projections/util/merge_projection/index.test.ts b/x-pack/plugins/apm/common/projections/util/merge_projection/index.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/common/projections/util/merge_projection/index.test.ts
rename to x-pack/plugins/apm/common/projections/util/merge_projection/index.test.ts
diff --git a/x-pack/legacy/plugins/apm/common/projections/util/merge_projection/index.ts b/x-pack/plugins/apm/common/projections/util/merge_projection/index.ts
similarity index 87%
rename from x-pack/legacy/plugins/apm/common/projections/util/merge_projection/index.ts
rename to x-pack/plugins/apm/common/projections/util/merge_projection/index.ts
index ef6089872b786..6a5089733bb33 100644
--- a/x-pack/legacy/plugins/apm/common/projections/util/merge_projection/index.ts
+++ b/x-pack/plugins/apm/common/projections/util/merge_projection/index.ts
@@ -5,11 +5,11 @@
  */
 import { merge, isPlainObject, cloneDeep } from 'lodash';
 import { DeepPartial } from 'utility-types';
-import { AggregationInputMap } from '../../../../../../../plugins/apm/typings/elasticsearch/aggregations';
+import { AggregationInputMap } from '../../../../typings/elasticsearch/aggregations';
 import {
   ESSearchRequest,
   ESSearchBody
-} from '../../../../../../../plugins/apm/typings/elasticsearch';
+} from '../../../../typings/elasticsearch';
 import { Projection } from '../../typings';
 
 type PlainObject = Record<string | number | symbol, any>;
diff --git a/x-pack/legacy/plugins/apm/common/runtime_types/date_as_string_rt/index.test.ts b/x-pack/plugins/apm/common/runtime_types/date_as_string_rt/index.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/common/runtime_types/date_as_string_rt/index.test.ts
rename to x-pack/plugins/apm/common/runtime_types/date_as_string_rt/index.test.ts
diff --git a/x-pack/legacy/plugins/apm/common/runtime_types/date_as_string_rt/index.ts b/x-pack/plugins/apm/common/runtime_types/date_as_string_rt/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/common/runtime_types/date_as_string_rt/index.ts
rename to x-pack/plugins/apm/common/runtime_types/date_as_string_rt/index.ts
diff --git a/x-pack/legacy/plugins/apm/common/runtime_types/json_rt/index.test.ts b/x-pack/plugins/apm/common/runtime_types/json_rt/index.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/common/runtime_types/json_rt/index.test.ts
rename to x-pack/plugins/apm/common/runtime_types/json_rt/index.test.ts
diff --git a/x-pack/legacy/plugins/apm/common/runtime_types/json_rt/index.ts b/x-pack/plugins/apm/common/runtime_types/json_rt/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/common/runtime_types/json_rt/index.ts
rename to x-pack/plugins/apm/common/runtime_types/json_rt/index.ts
diff --git a/x-pack/legacy/plugins/apm/common/runtime_types/transaction_max_spans_rt/index.test.ts b/x-pack/plugins/apm/common/runtime_types/transaction_max_spans_rt/index.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/common/runtime_types/transaction_max_spans_rt/index.test.ts
rename to x-pack/plugins/apm/common/runtime_types/transaction_max_spans_rt/index.test.ts
diff --git a/x-pack/legacy/plugins/apm/common/runtime_types/transaction_max_spans_rt/index.ts b/x-pack/plugins/apm/common/runtime_types/transaction_max_spans_rt/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/common/runtime_types/transaction_max_spans_rt/index.ts
rename to x-pack/plugins/apm/common/runtime_types/transaction_max_spans_rt/index.ts
diff --git a/x-pack/legacy/plugins/apm/common/runtime_types/transaction_sample_rate_rt/index.test.ts b/x-pack/plugins/apm/common/runtime_types/transaction_sample_rate_rt/index.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/common/runtime_types/transaction_sample_rate_rt/index.test.ts
rename to x-pack/plugins/apm/common/runtime_types/transaction_sample_rate_rt/index.test.ts
diff --git a/x-pack/legacy/plugins/apm/common/runtime_types/transaction_sample_rate_rt/index.ts b/x-pack/plugins/apm/common/runtime_types/transaction_sample_rate_rt/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/common/runtime_types/transaction_sample_rate_rt/index.ts
rename to x-pack/plugins/apm/common/runtime_types/transaction_sample_rate_rt/index.ts
diff --git a/x-pack/legacy/plugins/apm/common/service_map.ts b/x-pack/plugins/apm/common/service_map.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/common/service_map.ts
rename to x-pack/plugins/apm/common/service_map.ts
diff --git a/x-pack/legacy/plugins/apm/common/service_nodes.ts b/x-pack/plugins/apm/common/service_nodes.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/common/service_nodes.ts
rename to x-pack/plugins/apm/common/service_nodes.ts
diff --git a/x-pack/legacy/plugins/apm/common/transaction_types.ts b/x-pack/plugins/apm/common/transaction_types.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/common/transaction_types.ts
rename to x-pack/plugins/apm/common/transaction_types.ts
diff --git a/x-pack/legacy/plugins/apm/common/viz_colors.ts b/x-pack/plugins/apm/common/viz_colors.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/common/viz_colors.ts
rename to x-pack/plugins/apm/common/viz_colors.ts
diff --git a/x-pack/plugins/apm/kibana.json b/x-pack/plugins/apm/kibana.json
index d60846131da74..42232a8b89605 100644
--- a/x-pack/plugins/apm/kibana.json
+++ b/x-pack/plugins/apm/kibana.json
@@ -6,5 +6,5 @@
   "configPath": ["xpack", "apm"],
   "ui": false,
   "requiredPlugins": ["apm_oss", "data", "home"],
-  "optionalPlugins": ["cloud"]
+  "optionalPlugins": ["cloud", "usageCollection"]
 }
diff --git a/x-pack/plugins/apm/server/index.ts b/x-pack/plugins/apm/server/index.ts
index d936e2a467f52..ebd954015c910 100644
--- a/x-pack/plugins/apm/server/index.ts
+++ b/x-pack/plugins/apm/server/index.ts
@@ -12,7 +12,7 @@ import { APMPlugin } from './plugin';
 export const config = {
   exposeToBrowser: {
     serviceMapEnabled: true,
-    ui: true,
+    ui: true
   },
   schema: schema.object({
     enabled: schema.boolean({ defaultValue: true }),
@@ -21,14 +21,17 @@ export const config = {
     ui: schema.object({
       enabled: schema.boolean({ defaultValue: true }),
       transactionGroupBucketSize: schema.number({ defaultValue: 100 }),
-      maxTraceItems: schema.number({ defaultValue: 1000 }),
-    }),
-  }),
+      maxTraceItems: schema.number({ defaultValue: 1000 })
+    })
+  })
 };
 
 export type APMXPackConfig = TypeOf<typeof config.schema>;
 
-export function mergeConfigs(apmOssConfig: APMOSSConfig, apmConfig: APMXPackConfig) {
+export function mergeConfigs(
+  apmOssConfig: APMOSSConfig,
+  apmConfig: APMXPackConfig
+) {
   return {
     'apm_oss.transactionIndices': apmOssConfig.transactionIndices,
     'apm_oss.spanIndices': apmOssConfig.spanIndices,
@@ -40,13 +43,15 @@ export function mergeConfigs(apmOssConfig: APMOSSConfig, apmConfig: APMXPackConf
     'xpack.apm.serviceMapEnabled': apmConfig.serviceMapEnabled,
     'xpack.apm.ui.enabled': apmConfig.ui.enabled,
     'xpack.apm.ui.maxTraceItems': apmConfig.ui.maxTraceItems,
-    'xpack.apm.ui.transactionGroupBucketSize': apmConfig.ui.transactionGroupBucketSize,
-    'xpack.apm.autocreateApmIndexPattern': apmConfig.autocreateApmIndexPattern,
+    'xpack.apm.ui.transactionGroupBucketSize':
+      apmConfig.ui.transactionGroupBucketSize,
+    'xpack.apm.autocreateApmIndexPattern': apmConfig.autocreateApmIndexPattern
   };
 }
 
 export type APMConfig = ReturnType<typeof mergeConfigs>;
 
-export const plugin = (initContext: PluginInitializerContext) => new APMPlugin(initContext);
+export const plugin = (initContext: PluginInitializerContext) =>
+  new APMPlugin(initContext);
 
 export { APMPlugin, APMPluginContract } from './plugin';
diff --git a/x-pack/legacy/plugins/apm/server/lib/apm_telemetry/__test__/index.test.ts b/x-pack/plugins/apm/server/lib/apm_telemetry/__test__/index.test.ts
similarity index 65%
rename from x-pack/legacy/plugins/apm/server/lib/apm_telemetry/__test__/index.test.ts
rename to x-pack/plugins/apm/server/lib/apm_telemetry/__test__/index.test.ts
index 26cae303542a4..c45c74a791aee 100644
--- a/x-pack/legacy/plugins/apm/server/lib/apm_telemetry/__test__/index.test.ts
+++ b/x-pack/plugins/apm/server/lib/apm_telemetry/__test__/index.test.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { SavedObjectAttributes } from 'src/core/server';
+import { SavedObjectAttributes } from '../../../../../../../src/core/server';
 import { createApmTelementry, storeApmServicesTelemetry } from '../index';
 import {
   APM_SERVICES_TELEMETRY_SAVED_OBJECT_TYPE,
@@ -45,25 +45,10 @@ describe('apm_telemetry', () => {
   });
 
   describe('storeApmServicesTelemetry', () => {
-    let server: any;
     let apmTelemetry: SavedObjectAttributes;
-    let savedObjectsClientInstance: any;
+    let savedObjectsClient: any;
 
     beforeEach(() => {
-      savedObjectsClientInstance = { create: jest.fn() };
-      const callWithInternalUser = jest.fn();
-      const internalRepository = jest.fn();
-      server = {
-        savedObjects: {
-          SavedObjectsClient: jest.fn(() => savedObjectsClientInstance),
-          getSavedObjectsRepository: jest.fn(() => internalRepository)
-        },
-        plugins: {
-          elasticsearch: {
-            getCluster: jest.fn(() => ({ callWithInternalUser }))
-          }
-        }
-      };
       apmTelemetry = {
         has_any_services: true,
         services_per_agent: {
@@ -72,30 +57,27 @@ describe('apm_telemetry', () => {
           'js-base': 1
         }
       };
+      savedObjectsClient = { create: jest.fn() };
     });
 
     it('should call savedObjectsClient create with the given ApmTelemetry object', () => {
-      storeApmServicesTelemetry(server, apmTelemetry);
-      expect(savedObjectsClientInstance.create.mock.calls[0][1]).toBe(
-        apmTelemetry
-      );
+      storeApmServicesTelemetry(savedObjectsClient, apmTelemetry);
+      expect(savedObjectsClient.create.mock.calls[0][1]).toBe(apmTelemetry);
     });
 
     it('should call savedObjectsClient create with the apm-telemetry document type and ID', () => {
-      storeApmServicesTelemetry(server, apmTelemetry);
-      expect(savedObjectsClientInstance.create.mock.calls[0][0]).toBe(
+      storeApmServicesTelemetry(savedObjectsClient, apmTelemetry);
+      expect(savedObjectsClient.create.mock.calls[0][0]).toBe(
         APM_SERVICES_TELEMETRY_SAVED_OBJECT_TYPE
       );
-      expect(savedObjectsClientInstance.create.mock.calls[0][2].id).toBe(
+      expect(savedObjectsClient.create.mock.calls[0][2].id).toBe(
         APM_SERVICES_TELEMETRY_SAVED_OBJECT_ID
       );
     });
 
     it('should call savedObjectsClient create with overwrite: true', () => {
-      storeApmServicesTelemetry(server, apmTelemetry);
-      expect(savedObjectsClientInstance.create.mock.calls[0][2].overwrite).toBe(
-        true
-      );
+      storeApmServicesTelemetry(savedObjectsClient, apmTelemetry);
+      expect(savedObjectsClient.create.mock.calls[0][2].overwrite).toBe(true);
     });
   });
 });
diff --git a/x-pack/legacy/plugins/apm/server/lib/apm_telemetry/index.ts b/x-pack/plugins/apm/server/lib/apm_telemetry/index.ts
similarity index 60%
rename from x-pack/legacy/plugins/apm/server/lib/apm_telemetry/index.ts
rename to x-pack/plugins/apm/server/lib/apm_telemetry/index.ts
index ddfb4144d9636..a2b0494730826 100644
--- a/x-pack/legacy/plugins/apm/server/lib/apm_telemetry/index.ts
+++ b/x-pack/plugins/apm/server/lib/apm_telemetry/index.ts
@@ -5,15 +5,14 @@
  */
 
 import { countBy } from 'lodash';
-import { SavedObjectAttributes } from 'src/core/server';
+import { SavedObjectAttributes } from '../../../../../../src/core/server';
 import { isAgentName } from '../../../common/agent_name';
-import { getInternalSavedObjectsClient } from '../helpers/saved_objects_client';
 import {
   APM_SERVICES_TELEMETRY_SAVED_OBJECT_TYPE,
   APM_SERVICES_TELEMETRY_SAVED_OBJECT_ID
 } from '../../../common/apm_saved_object_constants';
-import { APMLegacyServer } from '../../routes/typings';
-import { UsageCollectionSetup } from '../../../../../../../src/plugins/usage_collection/server';
+import { UsageCollectionSetup } from '../../../../../../src/plugins/usage_collection/server';
+import { InternalSavedObjectsClient } from '../helpers/get_internal_saved_objects_client';
 
 export function createApmTelementry(
   agentNames: string[] = []
@@ -26,34 +25,28 @@ export function createApmTelementry(
 }
 
 export async function storeApmServicesTelemetry(
-  server: APMLegacyServer,
+  savedObjectsClient: InternalSavedObjectsClient,
   apmTelemetry: SavedObjectAttributes
 ) {
-  try {
-    const savedObjectsClient = getInternalSavedObjectsClient(server);
-    await savedObjectsClient.create(
-      APM_SERVICES_TELEMETRY_SAVED_OBJECT_TYPE,
-      apmTelemetry,
-      {
-        id: APM_SERVICES_TELEMETRY_SAVED_OBJECT_ID,
-        overwrite: true
-      }
-    );
-  } catch (e) {
-    server.log(['error'], `Unable to save APM telemetry data: ${e.message}`);
-  }
+  return savedObjectsClient.create(
+    APM_SERVICES_TELEMETRY_SAVED_OBJECT_TYPE,
+    apmTelemetry,
+    {
+      id: APM_SERVICES_TELEMETRY_SAVED_OBJECT_ID,
+      overwrite: true
+    }
+  );
 }
 
 export function makeApmUsageCollector(
   usageCollector: UsageCollectionSetup,
-  server: APMLegacyServer
+  savedObjectsRepository: InternalSavedObjectsClient
 ) {
   const apmUsageCollector = usageCollector.makeUsageCollector({
     type: 'apm',
     fetch: async () => {
-      const internalSavedObjectsClient = getInternalSavedObjectsClient(server);
       try {
-        const apmTelemetrySavedObject = await internalSavedObjectsClient.get(
+        const apmTelemetrySavedObject = await savedObjectsRepository.get(
           APM_SERVICES_TELEMETRY_SAVED_OBJECT_TYPE,
           APM_SERVICES_TELEMETRY_SAVED_OBJECT_ID
         );
diff --git a/x-pack/legacy/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap
rename to x-pack/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap
diff --git a/x-pack/legacy/plugins/apm/server/lib/errors/distribution/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/errors/distribution/__snapshots__/queries.test.ts.snap
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/errors/distribution/__snapshots__/queries.test.ts.snap
rename to x-pack/plugins/apm/server/lib/errors/distribution/__snapshots__/queries.test.ts.snap
diff --git a/x-pack/legacy/plugins/apm/server/lib/errors/distribution/__tests__/__snapshots__/get_buckets.test.ts.snap b/x-pack/plugins/apm/server/lib/errors/distribution/__tests__/__snapshots__/get_buckets.test.ts.snap
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/errors/distribution/__tests__/__snapshots__/get_buckets.test.ts.snap
rename to x-pack/plugins/apm/server/lib/errors/distribution/__tests__/__snapshots__/get_buckets.test.ts.snap
diff --git a/x-pack/legacy/plugins/apm/server/lib/errors/distribution/__tests__/get_buckets.test.ts b/x-pack/plugins/apm/server/lib/errors/distribution/__tests__/get_buckets.test.ts
similarity index 96%
rename from x-pack/legacy/plugins/apm/server/lib/errors/distribution/__tests__/get_buckets.test.ts
rename to x-pack/plugins/apm/server/lib/errors/distribution/__tests__/get_buckets.test.ts
index cf8798d445f8a..3ac47004279b3 100644
--- a/x-pack/legacy/plugins/apm/server/lib/errors/distribution/__tests__/get_buckets.test.ts
+++ b/x-pack/plugins/apm/server/lib/errors/distribution/__tests__/get_buckets.test.ts
@@ -6,7 +6,7 @@
 
 import { PROCESSOR_EVENT } from '../../../../../common/elasticsearch_fieldnames';
 import { getBuckets } from '../get_buckets';
-import { APMConfig } from '../../../../../../../../plugins/apm/server';
+import { APMConfig } from '../../../..';
 
 describe('timeseriesFetcher', () => {
   let clientSpy: jest.Mock;
diff --git a/x-pack/legacy/plugins/apm/server/lib/errors/distribution/get_buckets.ts b/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts
similarity index 95%
rename from x-pack/legacy/plugins/apm/server/lib/errors/distribution/get_buckets.ts
rename to x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts
index 0f6ace39aeca3..9274f96d58d83 100644
--- a/x-pack/legacy/plugins/apm/server/lib/errors/distribution/get_buckets.ts
+++ b/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { ESFilter } from '../../../../../../../plugins/apm/typings/elasticsearch';
+import { ESFilter } from '../../../../typings/elasticsearch';
 import {
   ERROR_GROUP_ID,
   PROCESSOR_EVENT,
diff --git a/x-pack/legacy/plugins/apm/server/lib/errors/distribution/get_distribution.ts b/x-pack/plugins/apm/server/lib/errors/distribution/get_distribution.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/errors/distribution/get_distribution.ts
rename to x-pack/plugins/apm/server/lib/errors/distribution/get_distribution.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/errors/distribution/queries.test.ts b/x-pack/plugins/apm/server/lib/errors/distribution/queries.test.ts
similarity index 93%
rename from x-pack/legacy/plugins/apm/server/lib/errors/distribution/queries.test.ts
rename to x-pack/plugins/apm/server/lib/errors/distribution/queries.test.ts
index fcc456c653303..0d539a09f091b 100644
--- a/x-pack/legacy/plugins/apm/server/lib/errors/distribution/queries.test.ts
+++ b/x-pack/plugins/apm/server/lib/errors/distribution/queries.test.ts
@@ -8,7 +8,7 @@ import { getErrorDistribution } from './get_distribution';
 import {
   SearchParamsMock,
   inspectSearchParams
-} from '../../../../public/utils/testHelpers';
+} from '../../../../../../legacy/plugins/apm/public/utils/testHelpers';
 
 describe('error distribution queries', () => {
   let mock: SearchParamsMock;
diff --git a/x-pack/legacy/plugins/apm/server/lib/errors/get_error_group.ts b/x-pack/plugins/apm/server/lib/errors/get_error_group.ts
similarity index 96%
rename from x-pack/legacy/plugins/apm/server/lib/errors/get_error_group.ts
rename to x-pack/plugins/apm/server/lib/errors/get_error_group.ts
index 5d45002b9f3ce..cc090a06c04dc 100644
--- a/x-pack/legacy/plugins/apm/server/lib/errors/get_error_group.ts
+++ b/x-pack/plugins/apm/server/lib/errors/get_error_group.ts
@@ -11,7 +11,7 @@ import {
   TRANSACTION_SAMPLED
 } from '../../../common/elasticsearch_fieldnames';
 import { PromiseReturnType } from '../../../typings/common';
-import { APMError } from '../../../typings/es_schemas/ui/APMError';
+import { APMError } from '../../../typings/es_schemas/ui/apm_error';
 import { rangeFilter } from '../helpers/range_filter';
 import {
   Setup,
diff --git a/x-pack/legacy/plugins/apm/server/lib/errors/get_error_groups.ts b/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts
similarity index 95%
rename from x-pack/legacy/plugins/apm/server/lib/errors/get_error_groups.ts
rename to x-pack/plugins/apm/server/lib/errors/get_error_groups.ts
index 92ee6a0a71f00..8ea6df5a9898a 100644
--- a/x-pack/legacy/plugins/apm/server/lib/errors/get_error_groups.ts
+++ b/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts
@@ -12,7 +12,7 @@ import {
   ERROR_LOG_MESSAGE
 } from '../../../common/elasticsearch_fieldnames';
 import { PromiseReturnType } from '../../../typings/common';
-import { APMError } from '../../../typings/es_schemas/ui/APMError';
+import { APMError } from '../../../typings/es_schemas/ui/apm_error';
 import {
   Setup,
   SetupTimeRange,
@@ -20,7 +20,7 @@ import {
 } from '../helpers/setup_request';
 import { getErrorGroupsProjection } from '../../../common/projections/errors';
 import { mergeProjection } from '../../../common/projections/util/merge_projection';
-import { SortOptions } from '../../../../../../plugins/apm/typings/elasticsearch/aggregations';
+import { SortOptions } from '../../../typings/elasticsearch/aggregations';
 
 export type ErrorGroupListAPIResponse = PromiseReturnType<
   typeof getErrorGroups
diff --git a/x-pack/legacy/plugins/apm/server/lib/errors/queries.test.ts b/x-pack/plugins/apm/server/lib/errors/queries.test.ts
similarity index 95%
rename from x-pack/legacy/plugins/apm/server/lib/errors/queries.test.ts
rename to x-pack/plugins/apm/server/lib/errors/queries.test.ts
index f1e5d31efd4bd..5b063c2fb2b61 100644
--- a/x-pack/legacy/plugins/apm/server/lib/errors/queries.test.ts
+++ b/x-pack/plugins/apm/server/lib/errors/queries.test.ts
@@ -9,7 +9,7 @@ import { getErrorGroups } from './get_error_groups';
 import {
   SearchParamsMock,
   inspectSearchParams
-} from '../../../public/utils/testHelpers';
+} from '../../../../../legacy/plugins/apm/public/utils/testHelpers';
 
 describe('error queries', () => {
   let mock: SearchParamsMock;
diff --git a/x-pack/legacy/plugins/apm/server/lib/helpers/convert_ui_filters/__test__/get_environment_ui_filter_es.test.ts b/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/__test__/get_environment_ui_filter_es.test.ts
similarity index 93%
rename from x-pack/legacy/plugins/apm/server/lib/helpers/convert_ui_filters/__test__/get_environment_ui_filter_es.test.ts
rename to x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/__test__/get_environment_ui_filter_es.test.ts
index b5061e303a40d..0f0a11a868d6d 100644
--- a/x-pack/legacy/plugins/apm/server/lib/helpers/convert_ui_filters/__test__/get_environment_ui_filter_es.test.ts
+++ b/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/__test__/get_environment_ui_filter_es.test.ts
@@ -7,7 +7,7 @@
 import { getEnvironmentUiFilterES } from '../get_environment_ui_filter_es';
 import { ENVIRONMENT_NOT_DEFINED } from '../../../../../common/environment_filter_values';
 import { SERVICE_ENVIRONMENT } from '../../../../../common/elasticsearch_fieldnames';
-import { ESFilter } from '../../../../../../../../plugins/apm/typings/elasticsearch';
+import { ESFilter } from '../../../../../typings/elasticsearch';
 
 describe('getEnvironmentUiFilterES', () => {
   it('should return undefined, when environment is undefined', () => {
diff --git a/x-pack/legacy/plugins/apm/server/lib/helpers/convert_ui_filters/get_environment_ui_filter_es.ts b/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_environment_ui_filter_es.ts
similarity index 90%
rename from x-pack/legacy/plugins/apm/server/lib/helpers/convert_ui_filters/get_environment_ui_filter_es.ts
rename to x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_environment_ui_filter_es.ts
index dcf2334ab4de2..57feaf6a6fd0e 100644
--- a/x-pack/legacy/plugins/apm/server/lib/helpers/convert_ui_filters/get_environment_ui_filter_es.ts
+++ b/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_environment_ui_filter_es.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { ESFilter } from '../../../../../../../plugins/apm/typings/elasticsearch';
+import { ESFilter } from '../../../../typings/elasticsearch';
 import { ENVIRONMENT_NOT_DEFINED } from '../../../../common/environment_filter_values';
 import { SERVICE_ENVIRONMENT } from '../../../../common/elasticsearch_fieldnames';
 
diff --git a/x-pack/legacy/plugins/apm/server/lib/helpers/convert_ui_filters/get_ui_filters_es.ts b/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_ui_filters_es.ts
similarity index 88%
rename from x-pack/legacy/plugins/apm/server/lib/helpers/convert_ui_filters/get_ui_filters_es.ts
rename to x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_ui_filters_es.ts
index 126ee49fd4218..020c8cd80afa8 100644
--- a/x-pack/legacy/plugins/apm/server/lib/helpers/convert_ui_filters/get_ui_filters_es.ts
+++ b/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_ui_filters_es.ts
@@ -4,8 +4,8 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { ESFilter } from '../../../../../../../plugins/apm/typings/elasticsearch';
-import { UIFilters } from '../../../../typings/ui-filters';
+import { ESFilter } from '../../../../typings/elasticsearch';
+import { UIFilters } from '../../../../typings/ui_filters';
 import { getEnvironmentUiFilterES } from './get_environment_ui_filter_es';
 import {
   localUIFilters,
@@ -14,7 +14,7 @@ import {
 import {
   esKuery,
   IIndexPattern
-} from '../../../../../../../../src/plugins/data/server';
+} from '../../../../../../../src/plugins/data/server';
 
 export function getUiFiltersES(
   indexPattern: IIndexPattern | undefined,
diff --git a/x-pack/legacy/plugins/apm/server/lib/helpers/es_client.test.ts b/x-pack/plugins/apm/server/lib/helpers/es_client.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/helpers/es_client.test.ts
rename to x-pack/plugins/apm/server/lib/helpers/es_client.test.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/helpers/es_client.ts b/x-pack/plugins/apm/server/lib/helpers/es_client.ts
similarity index 97%
rename from x-pack/legacy/plugins/apm/server/lib/helpers/es_client.ts
rename to x-pack/plugins/apm/server/lib/helpers/es_client.ts
index 06cf0047b0286..8ada02d085631 100644
--- a/x-pack/legacy/plugins/apm/server/lib/helpers/es_client.ts
+++ b/x-pack/plugins/apm/server/lib/helpers/es_client.ts
@@ -16,9 +16,9 @@ import { KibanaRequest } from 'src/core/server';
 import {
   ESSearchRequest,
   ESSearchResponse
-} from '../../../../../../plugins/apm/typings/elasticsearch';
+} from '../../../typings/elasticsearch';
 import { OBSERVER_VERSION_MAJOR } from '../../../common/elasticsearch_fieldnames';
-import { pickKeys } from '../../../public/utils/pickKeys';
+import { pickKeys } from '../../../../../legacy/plugins/apm/public/utils/pickKeys';
 import { APMRequestHandlerContext } from '../../routes/typings';
 import { getApmIndices } from '../settings/apm_indices/get_apm_indices';
 
diff --git a/x-pack/legacy/plugins/apm/server/lib/helpers/get_bucket_size/calculate_auto.js b/x-pack/plugins/apm/server/lib/helpers/get_bucket_size/calculate_auto.js
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/helpers/get_bucket_size/calculate_auto.js
rename to x-pack/plugins/apm/server/lib/helpers/get_bucket_size/calculate_auto.js
diff --git a/x-pack/legacy/plugins/apm/server/lib/helpers/get_bucket_size/index.ts b/x-pack/plugins/apm/server/lib/helpers/get_bucket_size/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/helpers/get_bucket_size/index.ts
rename to x-pack/plugins/apm/server/lib/helpers/get_bucket_size/index.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/helpers/get_bucket_size/unit_to_seconds.js b/x-pack/plugins/apm/server/lib/helpers/get_bucket_size/unit_to_seconds.js
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/helpers/get_bucket_size/unit_to_seconds.js
rename to x-pack/plugins/apm/server/lib/helpers/get_bucket_size/unit_to_seconds.js
diff --git a/x-pack/plugins/apm/server/lib/helpers/get_internal_saved_objects_client.ts b/x-pack/plugins/apm/server/lib/helpers/get_internal_saved_objects_client.ts
new file mode 100644
index 0000000000000..e0bb9ad354f58
--- /dev/null
+++ b/x-pack/plugins/apm/server/lib/helpers/get_internal_saved_objects_client.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.
+ */
+import { CoreSetup } from 'src/core/server';
+import { PromiseReturnType } from '../../../typings/common';
+
+export type InternalSavedObjectsClient = PromiseReturnType<
+  typeof getInternalSavedObjectsClient
+>;
+export async function getInternalSavedObjectsClient(core: CoreSetup) {
+  return core.getStartServices().then(async ([coreStart]) => {
+    return coreStart.savedObjects.createInternalRepository();
+  });
+}
diff --git a/x-pack/legacy/plugins/apm/server/lib/helpers/input_validation.ts b/x-pack/plugins/apm/server/lib/helpers/input_validation.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/helpers/input_validation.ts
rename to x-pack/plugins/apm/server/lib/helpers/input_validation.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/helpers/metrics.ts b/x-pack/plugins/apm/server/lib/helpers/metrics.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/helpers/metrics.ts
rename to x-pack/plugins/apm/server/lib/helpers/metrics.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/helpers/range_filter.ts b/x-pack/plugins/apm/server/lib/helpers/range_filter.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/helpers/range_filter.ts
rename to x-pack/plugins/apm/server/lib/helpers/range_filter.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/helpers/round_to_nearest_five_or_ten.test.ts b/x-pack/plugins/apm/server/lib/helpers/round_to_nearest_five_or_ten.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/helpers/round_to_nearest_five_or_ten.test.ts
rename to x-pack/plugins/apm/server/lib/helpers/round_to_nearest_five_or_ten.test.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/helpers/round_to_nearest_five_or_ten.ts b/x-pack/plugins/apm/server/lib/helpers/round_to_nearest_five_or_ten.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/helpers/round_to_nearest_five_or_ten.ts
rename to x-pack/plugins/apm/server/lib/helpers/round_to_nearest_five_or_ten.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/helpers/setup_request.test.ts b/x-pack/plugins/apm/server/lib/helpers/setup_request.test.ts
similarity index 98%
rename from x-pack/legacy/plugins/apm/server/lib/helpers/setup_request.test.ts
rename to x-pack/plugins/apm/server/lib/helpers/setup_request.test.ts
index 4272bdbddd26b..40a2a0e7216a0 100644
--- a/x-pack/legacy/plugins/apm/server/lib/helpers/setup_request.test.ts
+++ b/x-pack/plugins/apm/server/lib/helpers/setup_request.test.ts
@@ -4,9 +4,9 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 import { setupRequest } from './setup_request';
-import { APMConfig } from '../../../../../../plugins/apm/server';
+import { APMConfig } from '../..';
 import { APMRequestHandlerContext } from '../../routes/typings';
-import { KibanaRequest } from 'src/core/server';
+import { KibanaRequest } from '../../../../../../src/core/server';
 
 jest.mock('../settings/apm_indices/get_apm_indices', () => ({
   getApmIndices: async () => ({
diff --git a/x-pack/legacy/plugins/apm/server/lib/helpers/setup_request.ts b/x-pack/plugins/apm/server/lib/helpers/setup_request.ts
similarity index 92%
rename from x-pack/legacy/plugins/apm/server/lib/helpers/setup_request.ts
rename to x-pack/plugins/apm/server/lib/helpers/setup_request.ts
index f8946997e6e18..eeaaddafa8e04 100644
--- a/x-pack/legacy/plugins/apm/server/lib/helpers/setup_request.ts
+++ b/x-pack/plugins/apm/server/lib/helpers/setup_request.ts
@@ -5,14 +5,14 @@
  */
 
 import moment from 'moment';
-import { KibanaRequest } from 'src/core/server';
-import { IIndexPattern } from 'src/plugins/data/common';
-import { APMConfig } from '../../../../../../plugins/apm/server';
+import { KibanaRequest } from '../../../../../../src/core/server';
+import { IIndexPattern } from '../../../../../../src/plugins/data/common';
+import { APMConfig } from '../..';
 import {
   getApmIndices,
   ApmIndicesConfig
 } from '../settings/apm_indices/get_apm_indices';
-import { ESFilter } from '../../../../../../plugins/apm/typings/elasticsearch';
+import { ESFilter } from '../../../typings/elasticsearch';
 import { ESClient } from './es_client';
 import { getUiFiltersES } from './convert_ui_filters/get_ui_filters_es';
 import { APMRequestHandlerContext } from '../../routes/typings';
diff --git a/x-pack/legacy/plugins/apm/server/lib/index_pattern/create_static_index_pattern.test.ts b/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.test.ts
similarity index 65%
rename from x-pack/legacy/plugins/apm/server/lib/index_pattern/create_static_index_pattern.test.ts
rename to x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.test.ts
index 2a31563b53c2c..574712cbafd1e 100644
--- a/x-pack/legacy/plugins/apm/server/lib/index_pattern/create_static_index_pattern.test.ts
+++ b/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.test.ts
@@ -6,42 +6,37 @@
 
 import { createStaticIndexPattern } from './create_static_index_pattern';
 import { Setup } from '../helpers/setup_request';
-import * as savedObjectsClient from '../helpers/saved_objects_client';
 import * as HistoricalAgentData from '../services/get_services/has_historical_agent_data';
 import { APMRequestHandlerContext } from '../../routes/typings';
+import { InternalSavedObjectsClient } from '../helpers/get_internal_saved_objects_client';
 
 function getMockContext(config: Record<string, unknown>) {
   return ({
     config,
-    __LEGACY: {
-      server: {
-        savedObjects: {
-          getSavedObjectsRepository: jest.fn()
+    core: {
+      savedObjects: {
+        client: {
+          create: jest.fn()
         }
       }
     }
   } as unknown) as APMRequestHandlerContext;
 }
+function getMockSavedObjectsClient() {
+  return ({
+    create: jest.fn()
+  } as unknown) as InternalSavedObjectsClient;
+}
 
 describe('createStaticIndexPattern', () => {
-  let createSavedObject: jest.Mock;
-  beforeEach(() => {
-    createSavedObject = jest.fn();
-    jest
-      .spyOn(savedObjectsClient, 'getInternalSavedObjectsClient')
-      .mockReturnValue({
-        create: createSavedObject
-      } as any);
-  });
-
   it(`should not create index pattern if 'xpack.apm.autocreateApmIndexPattern=false'`, async () => {
     const setup = {} as Setup;
     const context = getMockContext({
       'xpack.apm.autocreateApmIndexPattern': false
     });
-    await createStaticIndexPattern(setup, context);
-
-    expect(createSavedObject).not.toHaveBeenCalled();
+    const savedObjectsClient = getMockSavedObjectsClient();
+    await createStaticIndexPattern(setup, context, savedObjectsClient);
+    expect(savedObjectsClient.create).not.toHaveBeenCalled();
   });
 
   it(`should not create index pattern if no APM data is found`, async () => {
@@ -55,8 +50,10 @@ describe('createStaticIndexPattern', () => {
       .spyOn(HistoricalAgentData, 'hasHistoricalAgentData')
       .mockResolvedValue(false);
 
-    await createStaticIndexPattern(setup, context);
-    expect(createSavedObject).not.toHaveBeenCalled();
+    const savedObjectsClient = getMockSavedObjectsClient();
+
+    await createStaticIndexPattern(setup, context, savedObjectsClient);
+    expect(savedObjectsClient.create).not.toHaveBeenCalled();
   });
 
   it(`should create index pattern`, async () => {
@@ -69,8 +66,11 @@ describe('createStaticIndexPattern', () => {
     jest
       .spyOn(HistoricalAgentData, 'hasHistoricalAgentData')
       .mockResolvedValue(true);
-    await createStaticIndexPattern(setup, context);
 
-    expect(createSavedObject).toHaveBeenCalled();
+    const savedObjectsClient = getMockSavedObjectsClient();
+
+    await createStaticIndexPattern(setup, context, savedObjectsClient);
+
+    expect(savedObjectsClient.create).toHaveBeenCalled();
   });
 });
diff --git a/x-pack/legacy/plugins/apm/server/lib/index_pattern/create_static_index_pattern.ts b/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.ts
similarity index 76%
rename from x-pack/legacy/plugins/apm/server/lib/index_pattern/create_static_index_pattern.ts
rename to x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.ts
index fefaa30c8c36b..16546bc6070c2 100644
--- a/x-pack/legacy/plugins/apm/server/lib/index_pattern/create_static_index_pattern.ts
+++ b/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.ts
@@ -3,19 +3,19 @@
  * or more contributor license agreements. Licensed under the Elastic License;
  * you may not use this file except in compliance with the Elastic License.
  */
-import { getInternalSavedObjectsClient } from '../helpers/saved_objects_client';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import apmIndexPattern from '../../../../../../plugins/apm/server/tutorial/index_pattern.json';
+import apmIndexPattern from '../../tutorial/index_pattern.json';
 import { APM_STATIC_INDEX_PATTERN_ID } from '../../../common/index_pattern_constants';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { SavedObjectsErrorHelpers } from '../../../../../../../src/core/server/saved_objects';
+import { SavedObjectsErrorHelpers } from '../../../../../../src/core/server/saved_objects';
 import { hasHistoricalAgentData } from '../services/get_services/has_historical_agent_data';
 import { Setup } from '../helpers/setup_request';
 import { APMRequestHandlerContext } from '../../routes/typings';
+import { InternalSavedObjectsClient } from '../helpers/get_internal_saved_objects_client.js';
 
 export async function createStaticIndexPattern(
   setup: Setup,
-  context: APMRequestHandlerContext
+  context: APMRequestHandlerContext,
+  savedObjectsClient: InternalSavedObjectsClient
 ): Promise<void> {
   const { config } = context;
 
@@ -33,10 +33,7 @@ export async function createStaticIndexPattern(
 
   try {
     const apmIndexPatternTitle = config['apm_oss.indexPattern'];
-    const internalSavedObjectsClient = getInternalSavedObjectsClient(
-      context.__LEGACY.server
-    );
-    await internalSavedObjectsClient.create(
+    await savedObjectsClient.create(
       'index-pattern',
       {
         ...apmIndexPattern.attributes,
diff --git a/x-pack/legacy/plugins/apm/server/lib/index_pattern/get_dynamic_index_pattern.ts b/x-pack/plugins/apm/server/lib/index_pattern/get_dynamic_index_pattern.ts
similarity index 96%
rename from x-pack/legacy/plugins/apm/server/lib/index_pattern/get_dynamic_index_pattern.ts
rename to x-pack/plugins/apm/server/lib/index_pattern/get_dynamic_index_pattern.ts
index 9eb99b7c21e75..b1e4906317f81 100644
--- a/x-pack/legacy/plugins/apm/server/lib/index_pattern/get_dynamic_index_pattern.ts
+++ b/x-pack/plugins/apm/server/lib/index_pattern/get_dynamic_index_pattern.ts
@@ -4,12 +4,12 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { APICaller } from 'src/core/server';
 import LRU from 'lru-cache';
+import { APICaller } from '../../../../../../src/core/server';
 import {
   IndexPatternsFetcher,
   IIndexPattern
-} from '../../../../../../../src/plugins/data/server';
+} from '../../../../../../src/plugins/data/server';
 import { ApmIndicesConfig } from '../settings/apm_indices/get_apm_indices';
 import { ProcessorEvent } from '../../../common/processor_event';
 import { APMRequestHandlerContext } from '../../routes/typings';
diff --git a/x-pack/legacy/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap
rename to x-pack/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap
diff --git a/x-pack/legacy/plugins/apm/server/lib/metrics/by_agent/default.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/metrics/by_agent/default.ts
rename to x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/metrics/by_agent/java/gc/fetchAndTransformGcMetrics.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/metrics/by_agent/java/gc/fetchAndTransformGcMetrics.ts
rename to x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/metrics/by_agent/java/gc/getGcRateChart.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_rate_chart.ts
similarity index 94%
rename from x-pack/legacy/plugins/apm/server/lib/metrics/by_agent/java/gc/getGcRateChart.ts
rename to x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_rate_chart.ts
index 21417891fa15f..fae15bcade7bc 100644
--- a/x-pack/legacy/plugins/apm/server/lib/metrics/by_agent/java/gc/getGcRateChart.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_rate_chart.ts
@@ -12,7 +12,7 @@ import {
   SetupTimeRange,
   SetupUIFilters
 } from '../../../../helpers/setup_request';
-import { fetchAndTransformGcMetrics } from './fetchAndTransformGcMetrics';
+import { fetchAndTransformGcMetrics } from './fetch_and_transform_gc_metrics';
 import { ChartBase } from '../../../types';
 
 const series = {
diff --git a/x-pack/legacy/plugins/apm/server/lib/metrics/by_agent/java/gc/getGcTimeChart.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_time_chart.ts
similarity index 94%
rename from x-pack/legacy/plugins/apm/server/lib/metrics/by_agent/java/gc/getGcTimeChart.ts
rename to x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_time_chart.ts
index ea7557fabacd0..422ce60ca59bc 100644
--- a/x-pack/legacy/plugins/apm/server/lib/metrics/by_agent/java/gc/getGcTimeChart.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_time_chart.ts
@@ -12,7 +12,7 @@ import {
   SetupTimeRange,
   SetupUIFilters
 } from '../../../../helpers/setup_request';
-import { fetchAndTransformGcMetrics } from './fetchAndTransformGcMetrics';
+import { fetchAndTransformGcMetrics } from './fetch_and_transform_gc_metrics';
 import { ChartBase } from '../../../types';
 
 const series = {
diff --git a/x-pack/legacy/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts
rename to x-pack/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/metrics/by_agent/java/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts
similarity index 91%
rename from x-pack/legacy/plugins/apm/server/lib/metrics/by_agent/java/index.ts
rename to x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts
index 191a7a2c14d23..c089d77828cf4 100644
--- a/x-pack/legacy/plugins/apm/server/lib/metrics/by_agent/java/index.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts
@@ -14,8 +14,8 @@ import { getNonHeapMemoryChart } from './non_heap_memory';
 import { getThreadCountChart } from './thread_count';
 import { getCPUChartData } from '../shared/cpu';
 import { getMemoryChartData } from '../shared/memory';
-import { getGcRateChart } from './gc/getGcRateChart';
-import { getGcTimeChart } from './gc/getGcTimeChart';
+import { getGcRateChart } from './gc/get_gc_rate_chart';
+import { getGcTimeChart } from './gc/get_gc_time_chart';
 
 export async function getJavaMetricsCharts(
   setup: Setup & SetupTimeRange & SetupUIFilters,
diff --git a/x-pack/legacy/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts
rename to x-pack/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts
rename to x-pack/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/metrics/by_agent/shared/cpu/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/cpu/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/metrics/by_agent/shared/cpu/index.ts
rename to x-pack/plugins/apm/server/lib/metrics/by_agent/shared/cpu/index.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/metrics/by_agent/shared/memory/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/memory/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/metrics/by_agent/shared/memory/index.ts
rename to x-pack/plugins/apm/server/lib/metrics/by_agent/shared/memory/index.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts b/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts
similarity index 94%
rename from x-pack/legacy/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts
rename to x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts
index 410d659a924fd..76460bb40bedb 100644
--- a/x-pack/legacy/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts
@@ -15,7 +15,7 @@ import { ChartBase } from './types';
 import { transformDataToMetricsChart } from './transform_metrics_chart';
 import { getMetricsProjection } from '../../../common/projections/metrics';
 import { mergeProjection } from '../../../common/projections/util/merge_projection';
-import { AggregationOptionsByType } from '../../../../../../plugins/apm/typings/elasticsearch/aggregations';
+import { AggregationOptionsByType } from '../../../typings/elasticsearch/aggregations';
 
 interface Aggs {
   [key: string]: Unionize<{
diff --git a/x-pack/legacy/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts b/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts
rename to x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/metrics/queries.test.ts b/x-pack/plugins/apm/server/lib/metrics/queries.test.ts
similarity index 97%
rename from x-pack/legacy/plugins/apm/server/lib/metrics/queries.test.ts
rename to x-pack/plugins/apm/server/lib/metrics/queries.test.ts
index f276fa69e20e3..ac4e13ae442cf 100644
--- a/x-pack/legacy/plugins/apm/server/lib/metrics/queries.test.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/queries.test.ts
@@ -12,7 +12,7 @@ import { getThreadCountChart } from './by_agent/java/thread_count';
 import {
   SearchParamsMock,
   inspectSearchParams
-} from '../../../public/utils/testHelpers';
+} from '../../../../../legacy/plugins/apm/public/utils/testHelpers';
 import { SERVICE_NODE_NAME_MISSING } from '../../../common/service_nodes';
 
 describe('metrics queries', () => {
diff --git a/x-pack/legacy/plugins/apm/server/lib/metrics/transform_metrics_chart.test.ts b/x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/metrics/transform_metrics_chart.test.ts
rename to x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.test.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/metrics/transform_metrics_chart.ts b/x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.ts
similarity index 92%
rename from x-pack/legacy/plugins/apm/server/lib/metrics/transform_metrics_chart.ts
rename to x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.ts
index 8e7c969eec96a..03f21e4f26e7b 100644
--- a/x-pack/legacy/plugins/apm/server/lib/metrics/transform_metrics_chart.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/transform_metrics_chart.ts
@@ -9,8 +9,8 @@ import { ChartBase } from './types';
 import {
   ESSearchResponse,
   ESSearchRequest
-} from '../../../../../../plugins/apm/typings/elasticsearch';
-import { AggregationOptionsByType } from '../../../../../../plugins/apm/typings/elasticsearch/aggregations';
+} from '../../../typings/elasticsearch';
+import { AggregationOptionsByType } from '../../../typings/elasticsearch/aggregations';
 import { getVizColorForIndex } from '../../../common/viz_colors';
 
 export type GenericMetricsChart = ReturnType<
diff --git a/x-pack/legacy/plugins/apm/server/lib/metrics/types.ts b/x-pack/plugins/apm/server/lib/metrics/types.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/metrics/types.ts
rename to x-pack/plugins/apm/server/lib/metrics/types.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/security/get_indices_privileges.ts b/x-pack/plugins/apm/server/lib/security/get_indices_privileges.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/security/get_indices_privileges.ts
rename to x-pack/plugins/apm/server/lib/security/get_indices_privileges.ts
diff --git a/x-pack/plugins/apm/server/lib/security/get_permissions.ts b/x-pack/plugins/apm/server/lib/security/get_permissions.ts
new file mode 100644
index 0000000000000..ed2a1f64e7f84
--- /dev/null
+++ b/x-pack/plugins/apm/server/lib/security/get_permissions.ts
@@ -0,0 +1,32 @@
+/*
+ * 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 { Setup } from '../helpers/setup_request';
+
+export async function getPermissions(setup: Setup) {
+  const { client, indices } = setup;
+
+  const params = {
+    index: Object.values(indices),
+    body: {
+      size: 0,
+      query: {
+        match_all: {}
+      }
+    }
+  };
+
+  try {
+    await client.search(params);
+    return { hasPermission: true };
+  } catch (e) {
+    // If 403, it means the user doesnt have permission.
+    if (e.status === 403) {
+      return { hasPermission: false };
+    }
+    // if any other error happens, throw it.
+    throw e;
+  }
+}
diff --git a/x-pack/legacy/plugins/apm/server/lib/service_map/get_service_map.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/service_map/get_service_map.ts
rename to x-pack/plugins/apm/server/lib/service_map/get_service_map.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/service_map/get_service_map_from_trace_ids.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map_from_trace_ids.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/service_map/get_service_map_from_trace_ids.ts
rename to x-pack/plugins/apm/server/lib/service_map/get_service_map_from_trace_ids.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts
similarity index 98%
rename from x-pack/legacy/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts
rename to x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts
index cff2d8570349e..6c4d540103cec 100644
--- a/x-pack/legacy/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts
+++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts
@@ -5,7 +5,7 @@
  */
 
 import { Setup, SetupTimeRange } from '../helpers/setup_request';
-import { ESFilter } from '../../../../../../plugins/apm/typings/elasticsearch';
+import { ESFilter } from '../../../typings/elasticsearch';
 import { rangeFilter } from '../helpers/range_filter';
 import {
   PROCESSOR_EVENT,
diff --git a/x-pack/legacy/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts b/x-pack/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts
similarity index 98%
rename from x-pack/legacy/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts
rename to x-pack/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts
index 92452c6dafff0..463fe7f2cf640 100644
--- a/x-pack/legacy/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts
+++ b/x-pack/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts
@@ -10,7 +10,7 @@ import {
   SetupTimeRange
 } from '../helpers/setup_request';
 import { rangeFilter } from '../helpers/range_filter';
-import { ESFilter } from '../../../../../../plugins/apm/typings/elasticsearch';
+import { ESFilter } from '../../../typings/elasticsearch';
 import {
   PROCESSOR_EVENT,
   SERVICE_NAME,
diff --git a/x-pack/legacy/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap
rename to x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap
diff --git a/x-pack/legacy/plugins/apm/server/lib/service_nodes/index.ts b/x-pack/plugins/apm/server/lib/service_nodes/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/service_nodes/index.ts
rename to x-pack/plugins/apm/server/lib/service_nodes/index.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/service_nodes/queries.test.ts b/x-pack/plugins/apm/server/lib/service_nodes/queries.test.ts
similarity index 95%
rename from x-pack/legacy/plugins/apm/server/lib/service_nodes/queries.test.ts
rename to x-pack/plugins/apm/server/lib/service_nodes/queries.test.ts
index 80cd94b1549d7..672d568b3a5e3 100644
--- a/x-pack/legacy/plugins/apm/server/lib/service_nodes/queries.test.ts
+++ b/x-pack/plugins/apm/server/lib/service_nodes/queries.test.ts
@@ -13,7 +13,7 @@ import { getServiceNodes } from './';
 import {
   SearchParamsMock,
   inspectSearchParams
-} from '../../../public/utils/testHelpers';
+} from '../../../../../legacy/plugins/apm/public/utils/testHelpers';
 import { getServiceNodeMetadata } from '../services/get_service_node_metadata';
 import { SERVICE_NODE_NAME_MISSING } from '../../../common/service_nodes';
 
diff --git a/x-pack/legacy/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap
rename to x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap
diff --git a/x-pack/legacy/plugins/apm/server/lib/services/annotations/__fixtures__/multiple-versions.json b/x-pack/plugins/apm/server/lib/services/annotations/__fixtures__/multiple_versions.json
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/services/annotations/__fixtures__/multiple-versions.json
rename to x-pack/plugins/apm/server/lib/services/annotations/__fixtures__/multiple_versions.json
diff --git a/x-pack/legacy/plugins/apm/server/lib/services/annotations/__fixtures__/no-versions.json b/x-pack/plugins/apm/server/lib/services/annotations/__fixtures__/no_versions.json
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/services/annotations/__fixtures__/no-versions.json
rename to x-pack/plugins/apm/server/lib/services/annotations/__fixtures__/no_versions.json
diff --git a/x-pack/legacy/plugins/apm/server/lib/services/annotations/__fixtures__/one-version.json b/x-pack/plugins/apm/server/lib/services/annotations/__fixtures__/one_version.json
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/services/annotations/__fixtures__/one-version.json
rename to x-pack/plugins/apm/server/lib/services/annotations/__fixtures__/one_version.json
diff --git a/x-pack/legacy/plugins/apm/server/lib/services/annotations/__fixtures__/versions-first-seen.json b/x-pack/plugins/apm/server/lib/services/annotations/__fixtures__/versions_first_seen.json
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/services/annotations/__fixtures__/versions-first-seen.json
rename to x-pack/plugins/apm/server/lib/services/annotations/__fixtures__/versions_first_seen.json
diff --git a/x-pack/legacy/plugins/apm/server/lib/services/annotations/index.test.ts b/x-pack/plugins/apm/server/lib/services/annotations/index.test.ts
similarity index 87%
rename from x-pack/legacy/plugins/apm/server/lib/services/annotations/index.test.ts
rename to x-pack/plugins/apm/server/lib/services/annotations/index.test.ts
index 75ac0642a1b8c..0e52982c6de28 100644
--- a/x-pack/legacy/plugins/apm/server/lib/services/annotations/index.test.ts
+++ b/x-pack/plugins/apm/server/lib/services/annotations/index.test.ts
@@ -7,11 +7,11 @@ import { getServiceAnnotations } from '.';
 import {
   SearchParamsMock,
   inspectSearchParams
-} from '../../../../public/utils/testHelpers';
-import noVersions from './__fixtures__/no-versions.json';
-import oneVersion from './__fixtures__/one-version.json';
-import multipleVersions from './__fixtures__/multiple-versions.json';
-import versionsFirstSeen from './__fixtures__/versions-first-seen.json';
+} from '../../../../../../legacy/plugins/apm/public/utils/testHelpers';
+import noVersions from './__fixtures__/no_versions.json';
+import oneVersion from './__fixtures__/one_version.json';
+import multipleVersions from './__fixtures__/multiple_versions.json';
+import versionsFirstSeen from './__fixtures__/versions_first_seen.json';
 
 describe('getServiceAnnotations', () => {
   let mock: SearchParamsMock;
diff --git a/x-pack/legacy/plugins/apm/server/lib/services/annotations/index.ts b/x-pack/plugins/apm/server/lib/services/annotations/index.ts
similarity index 97%
rename from x-pack/legacy/plugins/apm/server/lib/services/annotations/index.ts
rename to x-pack/plugins/apm/server/lib/services/annotations/index.ts
index 9c8e4f2281b5e..c03746ca220ee 100644
--- a/x-pack/legacy/plugins/apm/server/lib/services/annotations/index.ts
+++ b/x-pack/plugins/apm/server/lib/services/annotations/index.ts
@@ -5,7 +5,7 @@
  */
 import { isNumber } from 'lodash';
 import { Annotation, AnnotationType } from '../../../../common/annotations';
-import { ESFilter } from '../../../../../../../plugins/apm/typings/elasticsearch';
+import { ESFilter } from '../../../../typings/elasticsearch';
 import {
   SERVICE_NAME,
   SERVICE_ENVIRONMENT,
diff --git a/x-pack/legacy/plugins/apm/server/lib/services/get_service_agent_name.ts b/x-pack/plugins/apm/server/lib/services/get_service_agent_name.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/services/get_service_agent_name.ts
rename to x-pack/plugins/apm/server/lib/services/get_service_agent_name.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/services/get_service_node_metadata.ts b/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/services/get_service_node_metadata.ts
rename to x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/services/get_service_transaction_types.ts b/x-pack/plugins/apm/server/lib/services/get_service_transaction_types.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/services/get_service_transaction_types.ts
rename to x-pack/plugins/apm/server/lib/services/get_service_transaction_types.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/services/get_services/get_legacy_data_status.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_legacy_data_status.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/services/get_services/get_legacy_data_status.ts
rename to x-pack/plugins/apm/server/lib/services/get_services/get_legacy_data_status.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/services/get_services/get_services_items.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/services/get_services/get_services_items.ts
rename to x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/services/get_services/has_historical_agent_data.ts b/x-pack/plugins/apm/server/lib/services/get_services/has_historical_agent_data.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/services/get_services/has_historical_agent_data.ts
rename to x-pack/plugins/apm/server/lib/services/get_services/has_historical_agent_data.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/services/get_services/index.ts b/x-pack/plugins/apm/server/lib/services/get_services/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/services/get_services/index.ts
rename to x-pack/plugins/apm/server/lib/services/get_services/index.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/services/map.ts b/x-pack/plugins/apm/server/lib/services/map.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/services/map.ts
rename to x-pack/plugins/apm/server/lib/services/map.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/services/queries.test.ts b/x-pack/plugins/apm/server/lib/services/queries.test.ts
similarity index 96%
rename from x-pack/legacy/plugins/apm/server/lib/services/queries.test.ts
rename to x-pack/plugins/apm/server/lib/services/queries.test.ts
index 16490ace77744..e75d9ad05648c 100644
--- a/x-pack/legacy/plugins/apm/server/lib/services/queries.test.ts
+++ b/x-pack/plugins/apm/server/lib/services/queries.test.ts
@@ -12,7 +12,7 @@ import { hasHistoricalAgentData } from './get_services/has_historical_agent_data
 import {
   SearchParamsMock,
   inspectSearchParams
-} from '../../../public/utils/testHelpers';
+} from '../../../../../legacy/plugins/apm/public/utils/testHelpers';
 
 describe('services queries', () => {
   let mock: SearchParamsMock;
diff --git a/x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/settings/agent_configuration/__snapshots__/queries.test.ts.snap
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/__snapshots__/queries.test.ts.snap
rename to x-pack/plugins/apm/server/lib/settings/agent_configuration/__snapshots__/queries.test.ts.snap
diff --git a/x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/configuration_types.d.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/configuration_types.d.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/configuration_types.d.ts
rename to x-pack/plugins/apm/server/lib/settings/agent_configuration/configuration_types.d.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/create_agent_config_index.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/create_agent_config_index.ts
similarity index 94%
rename from x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/create_agent_config_index.ts
rename to x-pack/plugins/apm/server/lib/settings/agent_configuration/create_agent_config_index.ts
index 52ba22cbc0b99..af2d2a13eaa2f 100644
--- a/x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/create_agent_config_index.ts
+++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/create_agent_config_index.ts
@@ -5,8 +5,8 @@
  */
 
 import { IClusterClient } from 'src/core/server';
-import { APMConfig } from '../../../../../../../plugins/apm/server';
-import { CallCluster } from '../../../../../../../../src/legacy/core_plugins/elasticsearch';
+import { CallCluster } from 'src/legacy/core_plugins/elasticsearch';
+import { APMConfig } from '../../..';
 import { getApmIndicesConfig } from '../apm_indices/get_apm_indices';
 
 export async function createApmAgentConfigurationIndex({
diff --git a/x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/create_or_update_configuration.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/create_or_update_configuration.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/create_or_update_configuration.ts
rename to x-pack/plugins/apm/server/lib/settings/agent_configuration/create_or_update_configuration.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/delete_configuration.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/delete_configuration.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/delete_configuration.ts
rename to x-pack/plugins/apm/server/lib/settings/agent_configuration/delete_configuration.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts
rename to x-pack/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/get_environments/get_all_environments.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_environments/get_all_environments.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/get_environments/get_all_environments.ts
rename to x-pack/plugins/apm/server/lib/settings/agent_configuration/get_environments/get_all_environments.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/get_environments/get_existing_environments_for_service.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_environments/get_existing_environments_for_service.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/get_environments/get_existing_environments_for_service.ts
rename to x-pack/plugins/apm/server/lib/settings/agent_configuration/get_environments/get_existing_environments_for_service.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/get_environments/index.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_environments/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/get_environments/index.ts
rename to x-pack/plugins/apm/server/lib/settings/agent_configuration/get_environments/index.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/get_service_names.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_service_names.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/get_service_names.ts
rename to x-pack/plugins/apm/server/lib/settings/agent_configuration/get_service_names.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/list_configurations.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/list_configurations.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/list_configurations.ts
rename to x-pack/plugins/apm/server/lib/settings/agent_configuration/list_configurations.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/mark_applied_by_agent.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/mark_applied_by_agent.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/mark_applied_by_agent.ts
rename to x-pack/plugins/apm/server/lib/settings/agent_configuration/mark_applied_by_agent.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/queries.test.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/queries.test.ts
similarity index 96%
rename from x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/queries.test.ts
rename to x-pack/plugins/apm/server/lib/settings/agent_configuration/queries.test.ts
index 31b0937a1b957..a82d148781ad8 100644
--- a/x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/queries.test.ts
+++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/queries.test.ts
@@ -12,7 +12,7 @@ import { searchConfigurations } from './search';
 import {
   SearchParamsMock,
   inspectSearchParams
-} from '../../../../public/utils/testHelpers';
+} from '../../../../../../legacy/plugins/apm/public/utils/testHelpers';
 
 describe('agent configuration queries', () => {
   let mock: SearchParamsMock;
diff --git a/x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/search.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/search.ts
similarity index 95%
rename from x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/search.ts
rename to x-pack/plugins/apm/server/lib/settings/agent_configuration/search.ts
index 3f4225eaf22d6..766baead006b6 100644
--- a/x-pack/legacy/plugins/apm/server/lib/settings/agent_configuration/search.ts
+++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/search.ts
@@ -3,7 +3,7 @@
  * or more contributor license agreements. Licensed under the Elastic License;
  * you may not use this file except in compliance with the Elastic License.
  */
-import { ESSearchHit } from '../../../../../../../plugins/apm/typings/elasticsearch';
+import { ESSearchHit } from '../../../../typings/elasticsearch';
 import {
   SERVICE_NAME,
   SERVICE_ENVIRONMENT
diff --git a/x-pack/legacy/plugins/apm/server/lib/settings/apm_indices/get_apm_indices.ts b/x-pack/plugins/apm/server/lib/settings/apm_indices/get_apm_indices.ts
similarity index 93%
rename from x-pack/legacy/plugins/apm/server/lib/settings/apm_indices/get_apm_indices.ts
rename to x-pack/plugins/apm/server/lib/settings/apm_indices/get_apm_indices.ts
index e7b2330b472d8..00493e53f06dd 100644
--- a/x-pack/legacy/plugins/apm/server/lib/settings/apm_indices/get_apm_indices.ts
+++ b/x-pack/plugins/apm/server/lib/settings/apm_indices/get_apm_indices.ts
@@ -6,15 +6,17 @@
 
 import { merge } from 'lodash';
 import { Server } from 'hapi';
-import { SavedObjectsClientContract } from 'kibana/server';
+import { SavedObjectsClient } from 'src/core/server';
 import { PromiseReturnType } from '../../../../typings/common';
 import {
   APM_INDICES_SAVED_OBJECT_TYPE,
   APM_INDICES_SAVED_OBJECT_ID
 } from '../../../../common/apm_saved_object_constants';
-import { APMConfig } from '../../../../../../../plugins/apm/server';
+import { APMConfig } from '../../..';
 import { APMRequestHandlerContext } from '../../../routes/typings';
 
+type ISavedObjectsClient = Pick<SavedObjectsClient, 'get'>;
+
 export interface ApmIndicesConfig {
   'apm_oss.sourcemapIndices': string;
   'apm_oss.errorIndices': string;
@@ -32,7 +34,7 @@ export type ScopedSavedObjectsClient = ReturnType<
 >;
 
 async function getApmIndicesSavedObject(
-  savedObjectsClient: SavedObjectsClientContract
+  savedObjectsClient: ISavedObjectsClient
 ) {
   const apmIndices = await savedObjectsClient.get<Partial<ApmIndicesConfig>>(
     APM_INDICES_SAVED_OBJECT_TYPE,
@@ -59,7 +61,7 @@ export async function getApmIndices({
   savedObjectsClient
 }: {
   config: APMConfig;
-  savedObjectsClient: SavedObjectsClientContract;
+  savedObjectsClient: ISavedObjectsClient;
 }) {
   try {
     const apmIndicesSavedObject = await getApmIndicesSavedObject(
diff --git a/x-pack/legacy/plugins/apm/server/lib/settings/apm_indices/save_apm_indices.test.ts b/x-pack/plugins/apm/server/lib/settings/apm_indices/save_apm_indices.test.ts
similarity index 69%
rename from x-pack/legacy/plugins/apm/server/lib/settings/apm_indices/save_apm_indices.test.ts
rename to x-pack/plugins/apm/server/lib/settings/apm_indices/save_apm_indices.test.ts
index 6567736996502..4849b63ccb7c4 100644
--- a/x-pack/legacy/plugins/apm/server/lib/settings/apm_indices/save_apm_indices.test.ts
+++ b/x-pack/plugins/apm/server/lib/settings/apm_indices/save_apm_indices.test.ts
@@ -5,18 +5,13 @@
  */
 
 import { saveApmIndices } from './save_apm_indices';
+import { SavedObjectsClientContract } from '../../../../../../../src/core/server';
 
 describe('saveApmIndices', () => {
   it('should trim and strip empty settings', async () => {
-    const context = {
-      core: {
-        savedObjects: {
-          client: {
-            create: jest.fn()
-          }
-        }
-      }
-    } as any;
+    const savedObjectsClient = ({
+      create: jest.fn()
+    } as unknown) as SavedObjectsClientContract;
 
     const apmIndices = {
       settingA: 'aa',
@@ -27,8 +22,8 @@ describe('saveApmIndices', () => {
       settingF: 'ff',
       settingG: ' gg '
     } as any;
-    await saveApmIndices(context, apmIndices);
-    expect(context.core.savedObjects.client.create).toHaveBeenCalledWith(
+    await saveApmIndices(savedObjectsClient, apmIndices);
+    expect(savedObjectsClient.create).toHaveBeenCalledWith(
       expect.any(String),
       { settingA: 'aa', settingF: 'ff', settingG: 'gg' },
       expect.any(Object)
diff --git a/x-pack/legacy/plugins/apm/server/lib/settings/apm_indices/save_apm_indices.ts b/x-pack/plugins/apm/server/lib/settings/apm_indices/save_apm_indices.ts
similarity index 66%
rename from x-pack/legacy/plugins/apm/server/lib/settings/apm_indices/save_apm_indices.ts
rename to x-pack/plugins/apm/server/lib/settings/apm_indices/save_apm_indices.ts
index 4b68b248d8031..135b054b6f5bd 100644
--- a/x-pack/legacy/plugins/apm/server/lib/settings/apm_indices/save_apm_indices.ts
+++ b/x-pack/plugins/apm/server/lib/settings/apm_indices/save_apm_indices.ts
@@ -3,19 +3,18 @@
  * or more contributor license agreements. Licensed under the Elastic License;
  * you may not use this file except in compliance with the Elastic License.
  */
-
+import { SavedObjectsClientContract } from '../../../../../../../src/core/server';
 import {
   APM_INDICES_SAVED_OBJECT_TYPE,
   APM_INDICES_SAVED_OBJECT_ID
 } from '../../../../common/apm_saved_object_constants';
 import { ApmIndicesConfig } from './get_apm_indices';
-import { APMRequestHandlerContext } from '../../../routes/typings';
 
 export async function saveApmIndices(
-  context: APMRequestHandlerContext,
+  savedObjectsClient: SavedObjectsClientContract,
   apmIndices: Partial<ApmIndicesConfig>
 ) {
-  return await context.core.savedObjects.client.create(
+  return await savedObjectsClient.create(
     APM_INDICES_SAVED_OBJECT_TYPE,
     removeEmpty(apmIndices),
     {
@@ -27,9 +26,8 @@ export async function saveApmIndices(
 
 // remove empty/undefined values
 function removeEmpty(apmIndices: Partial<ApmIndicesConfig>) {
-  return Object.fromEntries(
-    Object.entries(apmIndices)
-      .map(([key, value]) => [key, value?.trim()])
-      .filter(([key, value]) => !!value)
-  );
+  return Object.entries(apmIndices)
+    .map(([key, value]) => [key, value?.trim()])
+    .filter(([key, value]) => !!value)
+    .reduce((obj, [key, value]) => ({ ...obj, [key as string]: value }), {});
 }
diff --git a/x-pack/legacy/plugins/apm/server/lib/traces/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/traces/__snapshots__/queries.test.ts.snap
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/traces/__snapshots__/queries.test.ts.snap
rename to x-pack/plugins/apm/server/lib/traces/__snapshots__/queries.test.ts.snap
diff --git a/x-pack/legacy/plugins/apm/server/lib/traces/get_trace.ts b/x-pack/plugins/apm/server/lib/traces/get_trace.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/traces/get_trace.ts
rename to x-pack/plugins/apm/server/lib/traces/get_trace.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/traces/get_trace_items.ts b/x-pack/plugins/apm/server/lib/traces/get_trace_items.ts
similarity index 94%
rename from x-pack/legacy/plugins/apm/server/lib/traces/get_trace_items.ts
rename to x-pack/plugins/apm/server/lib/traces/get_trace_items.ts
index 9d3e0d6db7f16..6ca01858f7ae8 100644
--- a/x-pack/legacy/plugins/apm/server/lib/traces/get_trace_items.ts
+++ b/x-pack/plugins/apm/server/lib/traces/get_trace_items.ts
@@ -13,9 +13,9 @@ import {
   TRANSACTION_ID,
   ERROR_LOG_LEVEL
 } from '../../../common/elasticsearch_fieldnames';
-import { Span } from '../../../typings/es_schemas/ui/Span';
-import { Transaction } from '../../../typings/es_schemas/ui/Transaction';
-import { APMError } from '../../../typings/es_schemas/ui/APMError';
+import { Span } from '../../../typings/es_schemas/ui/span';
+import { Transaction } from '../../../typings/es_schemas/ui/transaction';
+import { APMError } from '../../../typings/es_schemas/ui/apm_error';
 import { rangeFilter } from '../helpers/range_filter';
 import { Setup, SetupTimeRange } from '../helpers/setup_request';
 import { PromiseValueType } from '../../../typings/common';
diff --git a/x-pack/legacy/plugins/apm/server/lib/traces/queries.test.ts b/x-pack/plugins/apm/server/lib/traces/queries.test.ts
similarity index 90%
rename from x-pack/legacy/plugins/apm/server/lib/traces/queries.test.ts
rename to x-pack/plugins/apm/server/lib/traces/queries.test.ts
index 871d0fd1c7fb6..0c2ac4d0f9201 100644
--- a/x-pack/legacy/plugins/apm/server/lib/traces/queries.test.ts
+++ b/x-pack/plugins/apm/server/lib/traces/queries.test.ts
@@ -8,7 +8,7 @@ import { getTraceItems } from './get_trace_items';
 import {
   SearchParamsMock,
   inspectSearchParams
-} from '../../../public/utils/testHelpers';
+} from '../../../../../legacy/plugins/apm/public/utils/testHelpers';
 
 describe('trace queries', () => {
   let mock: SearchParamsMock;
diff --git a/x-pack/legacy/plugins/apm/server/lib/transaction_groups/__snapshots__/fetcher.test.ts.snap b/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/fetcher.test.ts.snap
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transaction_groups/__snapshots__/fetcher.test.ts.snap
rename to x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/fetcher.test.ts.snap
diff --git a/x-pack/legacy/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap
rename to x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap
diff --git a/x-pack/legacy/plugins/apm/server/lib/transaction_groups/__snapshots__/transform.test.ts.snap b/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/transform.test.ts.snap
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transaction_groups/__snapshots__/transform.test.ts.snap
rename to x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/transform.test.ts.snap
diff --git a/x-pack/legacy/plugins/apm/server/lib/transaction_groups/fetcher.test.ts b/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.test.ts
similarity index 96%
rename from x-pack/legacy/plugins/apm/server/lib/transaction_groups/fetcher.test.ts
rename to x-pack/plugins/apm/server/lib/transaction_groups/fetcher.test.ts
index 4121ff74bfacc..c4a0be0f48c14 100644
--- a/x-pack/legacy/plugins/apm/server/lib/transaction_groups/fetcher.test.ts
+++ b/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.test.ts
@@ -5,7 +5,7 @@
  */
 
 import { transactionGroupsFetcher } from './fetcher';
-import { APMConfig } from '../../../../../../plugins/apm/server';
+import { APMConfig } from '../..';
 
 function getSetup() {
   return {
diff --git a/x-pack/legacy/plugins/apm/server/lib/transaction_groups/fetcher.ts b/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts
similarity index 94%
rename from x-pack/legacy/plugins/apm/server/lib/transaction_groups/fetcher.ts
rename to x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts
index 18c61df0cfa7e..39f2be551ab6e 100644
--- a/x-pack/legacy/plugins/apm/server/lib/transaction_groups/fetcher.ts
+++ b/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts
@@ -13,8 +13,8 @@ import {
 import { getTransactionGroupsProjection } from '../../../common/projections/transaction_groups';
 import { mergeProjection } from '../../../common/projections/util/merge_projection';
 import { PromiseReturnType } from '../../../typings/common';
-import { SortOptions } from '../../../../../../plugins/apm/typings/elasticsearch/aggregations';
-import { Transaction } from '../../../typings/es_schemas/ui/Transaction';
+import { SortOptions } from '../../../typings/elasticsearch/aggregations';
+import { Transaction } from '../../../typings/es_schemas/ui/transaction';
 import {
   Setup,
   SetupTimeRange,
diff --git a/x-pack/legacy/plugins/apm/server/lib/transaction_groups/index.ts b/x-pack/plugins/apm/server/lib/transaction_groups/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transaction_groups/index.ts
rename to x-pack/plugins/apm/server/lib/transaction_groups/index.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/transaction_groups/mock-responses/transactionGroupsResponse.ts b/x-pack/plugins/apm/server/lib/transaction_groups/mock_responses/transaction_groups_response.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transaction_groups/mock-responses/transactionGroupsResponse.ts
rename to x-pack/plugins/apm/server/lib/transaction_groups/mock_responses/transaction_groups_response.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/transaction_groups/queries.test.ts b/x-pack/plugins/apm/server/lib/transaction_groups/queries.test.ts
similarity index 93%
rename from x-pack/legacy/plugins/apm/server/lib/transaction_groups/queries.test.ts
rename to x-pack/plugins/apm/server/lib/transaction_groups/queries.test.ts
index 73122d8580134..8d6495c2e0b5f 100644
--- a/x-pack/legacy/plugins/apm/server/lib/transaction_groups/queries.test.ts
+++ b/x-pack/plugins/apm/server/lib/transaction_groups/queries.test.ts
@@ -8,7 +8,7 @@ import { transactionGroupsFetcher } from './fetcher';
 import {
   SearchParamsMock,
   inspectSearchParams
-} from '../../../public/utils/testHelpers';
+} from '../../../../../legacy/plugins/apm/public/utils/testHelpers';
 
 describe('transaction group queries', () => {
   let mock: SearchParamsMock;
diff --git a/x-pack/legacy/plugins/apm/server/lib/transaction_groups/transform.test.ts b/x-pack/plugins/apm/server/lib/transaction_groups/transform.test.ts
similarity index 96%
rename from x-pack/legacy/plugins/apm/server/lib/transaction_groups/transform.test.ts
rename to x-pack/plugins/apm/server/lib/transaction_groups/transform.test.ts
index 709fa3afdc128..695d22d428aa2 100644
--- a/x-pack/legacy/plugins/apm/server/lib/transaction_groups/transform.test.ts
+++ b/x-pack/plugins/apm/server/lib/transaction_groups/transform.test.ts
@@ -5,7 +5,7 @@
  */
 
 import { ESResponse } from './fetcher';
-import { transactionGroupsResponse } from './mock-responses/transactionGroupsResponse';
+import { transactionGroupsResponse } from './mock_responses/transaction_groups_response';
 import { transactionGroupsTransformer } from './transform';
 
 describe('transactionGroupsTransformer', () => {
diff --git a/x-pack/legacy/plugins/apm/server/lib/transaction_groups/transform.ts b/x-pack/plugins/apm/server/lib/transaction_groups/transform.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transaction_groups/transform.ts
rename to x-pack/plugins/apm/server/lib/transaction_groups/transform.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap
rename to x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/__fixtures__/responses.ts b/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/__fixtures__/responses.ts
similarity index 96%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/__fixtures__/responses.ts
rename to x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/__fixtures__/responses.ts
index 9a9bfc31c21d4..3f0f8a84dc62f 100644
--- a/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/__fixtures__/responses.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/__fixtures__/responses.ts
@@ -7,7 +7,7 @@
 import {
   ESSearchResponse,
   ESSearchRequest
-} from '../../../../../../../../plugins/apm/typings/elasticsearch';
+} from '../../../../../typings/elasticsearch';
 
 export const response = ({
   hits: {
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.test.ts b/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.test.ts
rename to x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.test.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.ts b/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.ts
similarity index 96%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.ts
rename to x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.ts
index 9b0d704dbb43e..8a96a25aef50e 100644
--- a/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { ESFilter } from '../../../../../../../plugins/apm/typings/elasticsearch';
+import { ESFilter } from '../../../../typings/elasticsearch';
 import { PromiseReturnType } from '../../../../typings/common';
 import {
   PROCESSOR_EVENT,
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/index.test.ts b/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/index.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/index.test.ts
rename to x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/index.test.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/index.ts b/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/index.ts
rename to x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/index.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/transformer.test.ts b/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/transformer.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/transformer.test.ts
rename to x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/transformer.test.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/transformer.ts b/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/transformer.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_browser/transformer.ts
rename to x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/transformer.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_country/index.ts b/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_country/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/avg_duration_by_country/index.ts
rename to x-pack/plugins/apm/server/lib/transactions/avg_duration_by_country/index.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/breakdown/constants.ts b/x-pack/plugins/apm/server/lib/transactions/breakdown/constants.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/breakdown/constants.ts
rename to x-pack/plugins/apm/server/lib/transactions/breakdown/constants.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/breakdown/index.test.ts b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.test.ts
similarity index 95%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/breakdown/index.test.ts
rename to x-pack/plugins/apm/server/lib/transactions/breakdown/index.test.ts
index 476928a5bcb63..9ab31be9f7219 100644
--- a/x-pack/legacy/plugins/apm/server/lib/transactions/breakdown/index.test.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.test.ts
@@ -6,9 +6,9 @@
 
 import { getTransactionBreakdown } from '.';
 import * as constants from './constants';
-import noDataResponse from './mock-responses/noData.json';
-import dataResponse from './mock-responses/data.json';
-import { APMConfig } from '../../../../../../../plugins/apm/server';
+import noDataResponse from './mock_responses/no_data.json';
+import dataResponse from './mock_responses/data.json';
+import { APMConfig } from '../../..';
 
 const mockIndices = {
   'apm_oss.sourcemapIndices': 'myIndex',
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/breakdown/index.ts b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/breakdown/index.ts
rename to x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/breakdown/mock-responses/data.json b/x-pack/plugins/apm/server/lib/transactions/breakdown/mock_responses/data.json
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/breakdown/mock-responses/data.json
rename to x-pack/plugins/apm/server/lib/transactions/breakdown/mock_responses/data.json
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/breakdown/mock-responses/noData.json b/x-pack/plugins/apm/server/lib/transactions/breakdown/mock_responses/no_data.json
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/breakdown/mock-responses/noData.json
rename to x-pack/plugins/apm/server/lib/transactions/breakdown/mock_responses/no_data.json
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/__snapshots__/fetcher.test.ts.snap b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/__snapshots__/fetcher.test.ts.snap
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/__snapshots__/fetcher.test.ts.snap
rename to x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/__snapshots__/fetcher.test.ts.snap
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/__snapshots__/index.test.ts.snap b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/__snapshots__/index.test.ts.snap
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/__snapshots__/index.test.ts.snap
rename to x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/__snapshots__/index.test.ts.snap
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/__snapshots__/transform.test.ts.snap b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/__snapshots__/transform.test.ts.snap
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/__snapshots__/transform.test.ts.snap
rename to x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/__snapshots__/transform.test.ts.snap
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/fetcher.test.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/fetcher.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/fetcher.test.ts
rename to x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/fetcher.test.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/fetcher.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/fetcher.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/fetcher.ts
rename to x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/fetcher.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/get_ml_bucket_size.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/get_ml_bucket_size.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/get_ml_bucket_size.ts
rename to x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/get_ml_bucket_size.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.test.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.test.ts
similarity index 91%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.test.ts
rename to x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.test.ts
index 2a56f744f2f45..cc8fabe33e63d 100644
--- a/x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.test.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.test.ts
@@ -5,10 +5,10 @@
  */
 
 import { getAnomalySeries } from '.';
-import { mlAnomalyResponse } from './mock-responses/mlAnomalyResponse';
-import { mlBucketSpanResponse } from './mock-responses/mlBucketSpanResponse';
+import { mlAnomalyResponse } from './mock_responses/ml_anomaly_response';
+import { mlBucketSpanResponse } from './mock_responses/ml_bucket_span_response';
 import { PromiseReturnType } from '../../../../../typings/common';
-import { APMConfig } from '../../../../../../../../plugins/apm/server';
+import { APMConfig } from '../../../..';
 
 describe('getAnomalySeries', () => {
   let avgAnomalies: PromiseReturnType<typeof getAnomalySeries>;
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.ts
rename to x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/mock-responses/mlAnomalyResponse.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/mock_responses/ml_anomaly_response.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/mock-responses/mlAnomalyResponse.ts
rename to x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/mock_responses/ml_anomaly_response.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/mock-responses/mlBucketSpanResponse.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/mock_responses/ml_bucket_span_response.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/mock-responses/mlBucketSpanResponse.ts
rename to x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/mock_responses/ml_bucket_span_response.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/transform.test.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/transform.test.ts
similarity index 98%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/transform.test.ts
rename to x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/transform.test.ts
index 6f8fb39d89337..233da842c5571 100644
--- a/x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/transform.test.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/transform.test.ts
@@ -5,7 +5,7 @@
  */
 
 import { ESResponse } from './fetcher';
-import { mlAnomalyResponse } from './mock-responses/mlAnomalyResponse';
+import { mlAnomalyResponse } from './mock_responses/ml_anomaly_response';
 import { anomalySeriesTransform, replaceFirstAndLastBucket } from './transform';
 
 describe('anomalySeriesTransform', () => {
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/transform.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/transform.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_anomaly_data/transform.ts
rename to x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/transform.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_timeseries_data/__snapshots__/fetcher.test.ts.snap b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/__snapshots__/fetcher.test.ts.snap
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_timeseries_data/__snapshots__/fetcher.test.ts.snap
rename to x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/__snapshots__/fetcher.test.ts.snap
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_timeseries_data/__snapshots__/transform.test.ts.snap b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/__snapshots__/transform.test.ts.snap
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_timeseries_data/__snapshots__/transform.test.ts.snap
rename to x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/__snapshots__/transform.test.ts.snap
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.test.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.test.ts
similarity index 96%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.test.ts
rename to x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.test.ts
index 676ad4ded6b69..1970e39a2752e 100644
--- a/x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.test.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.test.ts
@@ -6,7 +6,7 @@
 
 import { PROCESSOR_EVENT } from '../../../../../common/elasticsearch_fieldnames';
 import { ESResponse, timeseriesFetcher } from './fetcher';
-import { APMConfig } from '../../../../../../../../plugins/apm/server';
+import { APMConfig } from '../../../../../server';
 
 describe('timeseriesFetcher', () => {
   let res: ESResponse;
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.ts
similarity index 96%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.ts
rename to x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.ts
index 67b807da59b9b..8a2e01c9a7891 100644
--- a/x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { ESFilter } from '../../../../../../../../plugins/apm/typings/elasticsearch';
+import { ESFilter } from '../../../../../typings/elasticsearch';
 import {
   PROCESSOR_EVENT,
   SERVICE_NAME,
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_timeseries_data/index.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_timeseries_data/index.ts
rename to x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/index.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_timeseries_data/mock-responses/timeseries_response.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/mock_responses/timeseries_response.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_timeseries_data/mock-responses/timeseries_response.ts
rename to x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/mock_responses/timeseries_response.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_timeseries_data/transform.test.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/transform.test.ts
similarity index 97%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_timeseries_data/transform.test.ts
rename to x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/transform.test.ts
index 1da800ae21ab2..0d11fae55f68f 100644
--- a/x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_timeseries_data/transform.test.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/transform.test.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { timeseriesResponse } from './mock-responses/timeseries_response';
+import { timeseriesResponse } from './mock_responses/timeseries_response';
 import {
   ApmTimeSeriesResponse,
   getTpmBuckets,
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_timeseries_data/transform.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/transform.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/charts/get_timeseries_data/transform.ts
rename to x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/transform.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/charts/index.ts b/x-pack/plugins/apm/server/lib/transactions/charts/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/charts/index.ts
rename to x-pack/plugins/apm/server/lib/transactions/charts/index.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/constants.ts b/x-pack/plugins/apm/server/lib/transactions/constants.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/constants.ts
rename to x-pack/plugins/apm/server/lib/transactions/constants.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/distribution/get_buckets/fetcher.ts b/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/fetcher.ts
similarity index 99%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/distribution/get_buckets/fetcher.ts
rename to x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/fetcher.ts
index b2f0834107df4..e6a989b9a6939 100644
--- a/x-pack/legacy/plugins/apm/server/lib/transactions/distribution/get_buckets/fetcher.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/fetcher.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { Transaction } from '../../../../../typings/es_schemas/ui/Transaction';
+import { Transaction } from '../../../../../typings/es_schemas/ui/transaction';
 import {
   PROCESSOR_EVENT,
   SERVICE_NAME,
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts b/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts
rename to x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/distribution/get_buckets/transform.ts b/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/transform.ts
similarity index 98%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/distribution/get_buckets/transform.ts
rename to x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/transform.ts
index 2e703dfb19680..9b22e1794f969 100644
--- a/x-pack/legacy/plugins/apm/server/lib/transactions/distribution/get_buckets/transform.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/transform.ts
@@ -5,7 +5,7 @@
  */
 
 import { PromiseReturnType } from '../../../../../typings/common';
-import { Transaction } from '../../../../../typings/es_schemas/ui/Transaction';
+import { Transaction } from '../../../../../typings/es_schemas/ui/transaction';
 import { bucketFetcher } from './fetcher';
 
 type DistributionBucketResponse = PromiseReturnType<typeof bucketFetcher>;
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts b/x-pack/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts
rename to x-pack/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/distribution/index.ts b/x-pack/plugins/apm/server/lib/transactions/distribution/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/distribution/index.ts
rename to x-pack/plugins/apm/server/lib/transactions/distribution/index.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/get_transaction/index.ts b/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts
similarity index 95%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/get_transaction/index.ts
rename to x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts
index e7ba453357a8a..a82109c2340d2 100644
--- a/x-pack/legacy/plugins/apm/server/lib/transactions/get_transaction/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts
@@ -9,7 +9,7 @@ import {
   TRACE_ID,
   TRANSACTION_ID
 } from '../../../../common/elasticsearch_fieldnames';
-import { Transaction } from '../../../../typings/es_schemas/ui/Transaction';
+import { Transaction } from '../../../../typings/es_schemas/ui/transaction';
 import { rangeFilter } from '../../helpers/range_filter';
 import {
   Setup,
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/get_transaction_by_trace/index.ts b/x-pack/plugins/apm/server/lib/transactions/get_transaction_by_trace/index.ts
similarity index 95%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/get_transaction_by_trace/index.ts
rename to x-pack/plugins/apm/server/lib/transactions/get_transaction_by_trace/index.ts
index a753908c545c4..f7ced239b7d4b 100644
--- a/x-pack/legacy/plugins/apm/server/lib/transactions/get_transaction_by_trace/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/get_transaction_by_trace/index.ts
@@ -9,7 +9,7 @@ import {
   TRACE_ID,
   PARENT_ID
 } from '../../../../common/elasticsearch_fieldnames';
-import { Transaction } from '../../../../typings/es_schemas/ui/Transaction';
+import { Transaction } from '../../../../typings/es_schemas/ui/transaction';
 import { Setup } from '../../helpers/setup_request';
 import { ProcessorEvent } from '../../../../common/processor_event';
 
diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/queries.test.ts b/x-pack/plugins/apm/server/lib/transactions/queries.test.ts
similarity index 97%
rename from x-pack/legacy/plugins/apm/server/lib/transactions/queries.test.ts
rename to x-pack/plugins/apm/server/lib/transactions/queries.test.ts
index a9e4204fde1ad..116738da5ef9b 100644
--- a/x-pack/legacy/plugins/apm/server/lib/transactions/queries.test.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/queries.test.ts
@@ -11,7 +11,7 @@ import { getTransaction } from './get_transaction';
 import {
   SearchParamsMock,
   inspectSearchParams
-} from '../../../public/utils/testHelpers';
+} from '../../../../../legacy/plugins/apm/public/utils/testHelpers';
 
 describe('transaction queries', () => {
   let mock: SearchParamsMock;
diff --git a/x-pack/legacy/plugins/apm/server/lib/ui_filters/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/ui_filters/__snapshots__/queries.test.ts.snap
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/ui_filters/__snapshots__/queries.test.ts.snap
rename to x-pack/plugins/apm/server/lib/ui_filters/__snapshots__/queries.test.ts.snap
diff --git a/x-pack/legacy/plugins/apm/server/lib/ui_filters/get_environments.ts b/x-pack/plugins/apm/server/lib/ui_filters/get_environments.ts
similarity index 95%
rename from x-pack/legacy/plugins/apm/server/lib/ui_filters/get_environments.ts
rename to x-pack/plugins/apm/server/lib/ui_filters/get_environments.ts
index 05985ee95a207..50c1926d1e4a0 100644
--- a/x-pack/legacy/plugins/apm/server/lib/ui_filters/get_environments.ts
+++ b/x-pack/plugins/apm/server/lib/ui_filters/get_environments.ts
@@ -12,7 +12,7 @@ import {
 import { rangeFilter } from '../helpers/range_filter';
 import { Setup, SetupTimeRange } from '../helpers/setup_request';
 import { ENVIRONMENT_NOT_DEFINED } from '../../../common/environment_filter_values';
-import { ESFilter } from '../../../../../../plugins/apm/typings/elasticsearch';
+import { ESFilter } from '../../../typings/elasticsearch';
 
 export async function getEnvironments(
   setup: Setup & SetupTimeRange,
diff --git a/x-pack/legacy/plugins/apm/server/lib/ui_filters/local_ui_filters/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/__snapshots__/queries.test.ts.snap
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/ui_filters/local_ui_filters/__snapshots__/queries.test.ts.snap
rename to x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/__snapshots__/queries.test.ts.snap
diff --git a/x-pack/legacy/plugins/apm/server/lib/ui_filters/local_ui_filters/config.ts b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/config.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/lib/ui_filters/local_ui_filters/config.ts
rename to x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/config.ts
diff --git a/x-pack/legacy/plugins/apm/server/lib/ui_filters/local_ui_filters/get_local_filter_query.ts b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/get_local_filter_query.ts
similarity index 96%
rename from x-pack/legacy/plugins/apm/server/lib/ui_filters/local_ui_filters/get_local_filter_query.ts
rename to x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/get_local_filter_query.ts
index 459fd05ae8176..b39a2875b7f3d 100644
--- a/x-pack/legacy/plugins/apm/server/lib/ui_filters/local_ui_filters/get_local_filter_query.ts
+++ b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/get_local_filter_query.ts
@@ -8,7 +8,7 @@ import { omit } from 'lodash';
 import { IIndexPattern } from 'src/plugins/data/server';
 import { mergeProjection } from '../../../../common/projections/util/merge_projection';
 import { Projection } from '../../../../common/projections/typings';
-import { UIFilters } from '../../../../typings/ui-filters';
+import { UIFilters } from '../../../../typings/ui_filters';
 import { getUiFiltersES } from '../../helpers/convert_ui_filters/get_ui_filters_es';
 import { localUIFilters, LocalUIFilterName } from './config';
 
diff --git a/x-pack/legacy/plugins/apm/server/lib/ui_filters/local_ui_filters/index.ts b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/index.ts
similarity index 96%
rename from x-pack/legacy/plugins/apm/server/lib/ui_filters/local_ui_filters/index.ts
rename to x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/index.ts
index 39a264b89ed81..1462cf2eeefa4 100644
--- a/x-pack/legacy/plugins/apm/server/lib/ui_filters/local_ui_filters/index.ts
+++ b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/index.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 import { cloneDeep, sortByOrder } from 'lodash';
-import { UIFilters } from '../../../../typings/ui-filters';
+import { UIFilters } from '../../../../typings/ui_filters';
 import { Projection } from '../../../../common/projections/typings';
 import { PromiseReturnType } from '../../../../typings/common';
 import { getLocalFilterQuery } from './get_local_filter_query';
diff --git a/x-pack/legacy/plugins/apm/server/lib/ui_filters/local_ui_filters/queries.test.ts b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/queries.test.ts
similarity index 93%
rename from x-pack/legacy/plugins/apm/server/lib/ui_filters/local_ui_filters/queries.test.ts
rename to x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/queries.test.ts
index b72186f528f28..21cc35da72cb9 100644
--- a/x-pack/legacy/plugins/apm/server/lib/ui_filters/local_ui_filters/queries.test.ts
+++ b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/queries.test.ts
@@ -8,7 +8,7 @@ import { getLocalUIFilters } from './';
 import {
   SearchParamsMock,
   inspectSearchParams
-} from '../../../../public/utils/testHelpers';
+} from '../../../../../../legacy/plugins/apm/public/utils/testHelpers';
 import { getServicesProjection } from '../../../../common/projections/services';
 
 describe('local ui filter queries', () => {
diff --git a/x-pack/legacy/plugins/apm/server/lib/ui_filters/queries.test.ts b/x-pack/plugins/apm/server/lib/ui_filters/queries.test.ts
similarity index 92%
rename from x-pack/legacy/plugins/apm/server/lib/ui_filters/queries.test.ts
rename to x-pack/plugins/apm/server/lib/ui_filters/queries.test.ts
index 079ab64f32db3..63c8c3e494bb0 100644
--- a/x-pack/legacy/plugins/apm/server/lib/ui_filters/queries.test.ts
+++ b/x-pack/plugins/apm/server/lib/ui_filters/queries.test.ts
@@ -8,7 +8,7 @@ import { getEnvironments } from './get_environments';
 import {
   SearchParamsMock,
   inspectSearchParams
-} from '../../../public/utils/testHelpers';
+} from '../../../../../legacy/plugins/apm/public/utils/testHelpers';
 
 describe('ui filter queries', () => {
   let mock: SearchParamsMock;
diff --git a/x-pack/plugins/apm/server/plugin.ts b/x-pack/plugins/apm/server/plugin.ts
index 83ece92aebe45..de23c4c5dd383 100644
--- a/x-pack/plugins/apm/server/plugin.ts
+++ b/x-pack/plugins/apm/server/plugin.ts
@@ -3,24 +3,22 @@
  * or more contributor license agreements. Licensed under the Elastic License;
  * you may not use this file except in compliance with the Elastic License.
  */
-import {
-  PluginInitializerContext,
-  Plugin,
-  CoreSetup,
-  SavedObjectsClientContract,
-} from 'src/core/server';
+import { PluginInitializerContext, Plugin, CoreSetup } from 'src/core/server';
 import { Observable, combineLatest, AsyncSubject } from 'rxjs';
 import { map } from 'rxjs/operators';
 import { Server } from 'hapi';
 import { once } from 'lodash';
+import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
+import { makeApmUsageCollector } from './lib/apm_telemetry';
 import { Plugin as APMOSSPlugin } from '../../../../src/plugins/apm_oss/server';
-import { createApmAgentConfigurationIndex } from '../../../legacy/plugins/apm/server/lib/settings/agent_configuration/create_agent_config_index';
-import { createApmApi } from '../../../legacy/plugins/apm/server/routes/create_apm_api';
-import { getApmIndices } from '../../../legacy/plugins/apm/server/lib/settings/apm_indices/get_apm_indices';
+import { createApmAgentConfigurationIndex } from './lib/settings/agent_configuration/create_agent_config_index';
+import { createApmApi } from './routes/create_apm_api';
+import { getApmIndices } from './lib/settings/apm_indices/get_apm_indices';
 import { APMConfig, mergeConfigs, APMXPackConfig } from '.';
 import { HomeServerPluginSetup } from '../../../../src/plugins/home/server';
 import { tutorialProvider } from './tutorial';
 import { CloudSetup } from '../../cloud/server';
+import { getInternalSavedObjectsClient } from './lib/helpers/get_internal_saved_objects_client';
 
 export interface LegacySetup {
   server: Server;
@@ -29,9 +27,7 @@ export interface LegacySetup {
 export interface APMPluginContract {
   config$: Observable<APMConfig>;
   registerLegacyAPI: (__LEGACY: LegacySetup) => void;
-  getApmIndices: (
-    savedObjectsClient: SavedObjectsClientContract
-  ) => ReturnType<typeof getApmIndices>;
+  getApmIndices: () => ReturnType<typeof getApmIndices>;
 }
 
 export class APMPlugin implements Plugin<APMPluginContract> {
@@ -49,6 +45,7 @@ export class APMPlugin implements Plugin<APMPluginContract> {
       apm_oss: APMOSSPlugin extends Plugin<infer TSetup> ? TSetup : never;
       home: HomeServerPluginSetup;
       cloud?: CloudSetup;
+      usageCollection?: UsageCollectionSetup;
     }
   ) {
     const config$ = this.initContext.config.create<APMXPackConfig>();
@@ -67,7 +64,7 @@ export class APMPlugin implements Plugin<APMPluginContract> {
         this.currentConfig = config;
         await createApmAgentConfigurationIndex({
           esClient: core.elasticsearch.dataClient,
-          config,
+          config
         });
         resolve();
       });
@@ -83,20 +80,34 @@ export class APMPlugin implements Plugin<APMPluginContract> {
           metricsIndices: this.currentConfig['apm_oss.metricsIndices'],
           onboardingIndices: this.currentConfig['apm_oss.onboardingIndices'],
           sourcemapIndices: this.currentConfig['apm_oss.sourcemapIndices'],
-          transactionIndices: this.currentConfig['apm_oss.transactionIndices'],
-        },
+          transactionIndices: this.currentConfig['apm_oss.transactionIndices']
+        }
       })
     );
 
+    const usageCollection = plugins.usageCollection;
+    if (usageCollection) {
+      getInternalSavedObjectsClient(core)
+        .then(savedObjectsClient => {
+          makeApmUsageCollector(usageCollection, savedObjectsClient);
+        })
+        .catch(error => {
+          logger.error('Unable to initialize use collection');
+          logger.error(error.message);
+        });
+    }
+
     return {
       config$: mergedConfig$,
       registerLegacyAPI: once((__LEGACY: LegacySetup) => {
         this.legacySetup$.next(__LEGACY);
         this.legacySetup$.complete();
       }),
-      getApmIndices: async (savedObjectsClient: SavedObjectsClientContract) => {
-        return getApmIndices({ savedObjectsClient, config: this.currentConfig });
-      },
+      getApmIndices: async () =>
+        getApmIndices({
+          savedObjectsClient: await getInternalSavedObjectsClient(core),
+          config: this.currentConfig
+        })
     };
   }
 
diff --git a/x-pack/legacy/plugins/apm/server/routes/create_api/index.test.ts b/x-pack/plugins/apm/server/routes/create_api/index.test.ts
similarity index 98%
rename from x-pack/legacy/plugins/apm/server/routes/create_api/index.test.ts
rename to x-pack/plugins/apm/server/routes/create_api/index.test.ts
index 309e503c4a497..e639bb5101e2f 100644
--- a/x-pack/legacy/plugins/apm/server/routes/create_api/index.test.ts
+++ b/x-pack/plugins/apm/server/routes/create_api/index.test.ts
@@ -8,8 +8,8 @@ import { createApi } from './index';
 import { CoreSetup, Logger } from 'src/core/server';
 import { Params } from '../typings';
 import { BehaviorSubject } from 'rxjs';
-import { APMConfig } from '../../../../../../plugins/apm/server';
-import { LegacySetup } from '../../../../../../plugins/apm/server/plugin';
+import { APMConfig } from '../..';
+import { LegacySetup } from '../../plugin';
 
 const getCoreMock = () => {
   const get = jest.fn();
diff --git a/x-pack/legacy/plugins/apm/server/routes/create_api/index.ts b/x-pack/plugins/apm/server/routes/create_api/index.ts
similarity index 98%
rename from x-pack/legacy/plugins/apm/server/routes/create_api/index.ts
rename to x-pack/plugins/apm/server/routes/create_api/index.ts
index c1a9838e90406..a84a24cea17d2 100644
--- a/x-pack/legacy/plugins/apm/server/routes/create_api/index.ts
+++ b/x-pack/plugins/apm/server/routes/create_api/index.ts
@@ -10,7 +10,7 @@ import * as t from 'io-ts';
 import { PathReporter } from 'io-ts/lib/PathReporter';
 import { isLeft } from 'fp-ts/lib/Either';
 import { KibanaResponseFactory, RouteRegistrar } from 'src/core/server';
-import { APMConfig } from '../../../../../../plugins/apm/server';
+import { APMConfig } from '../..';
 import {
   ServerAPI,
   RouteFactoryFn,
diff --git a/x-pack/legacy/plugins/apm/server/routes/create_apm_api.ts b/x-pack/plugins/apm/server/routes/create_apm_api.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/routes/create_apm_api.ts
rename to x-pack/plugins/apm/server/routes/create_apm_api.ts
diff --git a/x-pack/legacy/plugins/apm/server/routes/create_route.ts b/x-pack/plugins/apm/server/routes/create_route.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/routes/create_route.ts
rename to x-pack/plugins/apm/server/routes/create_route.ts
diff --git a/x-pack/legacy/plugins/apm/server/routes/default_api_types.ts b/x-pack/plugins/apm/server/routes/default_api_types.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/routes/default_api_types.ts
rename to x-pack/plugins/apm/server/routes/default_api_types.ts
diff --git a/x-pack/legacy/plugins/apm/server/routes/errors.ts b/x-pack/plugins/apm/server/routes/errors.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/routes/errors.ts
rename to x-pack/plugins/apm/server/routes/errors.ts
diff --git a/x-pack/legacy/plugins/apm/server/routes/index_pattern.ts b/x-pack/plugins/apm/server/routes/index_pattern.ts
similarity index 79%
rename from x-pack/legacy/plugins/apm/server/routes/index_pattern.ts
rename to x-pack/plugins/apm/server/routes/index_pattern.ts
index 539846430c7f8..b7964dc8e91ed 100644
--- a/x-pack/legacy/plugins/apm/server/routes/index_pattern.ts
+++ b/x-pack/plugins/apm/server/routes/index_pattern.ts
@@ -7,13 +7,15 @@ import * as t from 'io-ts';
 import { createStaticIndexPattern } from '../lib/index_pattern/create_static_index_pattern';
 import { createRoute } from './create_route';
 import { setupRequest } from '../lib/helpers/setup_request';
+import { getInternalSavedObjectsClient } from '../lib/helpers/get_internal_saved_objects_client';
 
-export const staticIndexPatternRoute = createRoute(() => ({
+export const staticIndexPatternRoute = createRoute(core => ({
   method: 'POST',
   path: '/api/apm/index_pattern/static',
   handler: async ({ context, request }) => {
     const setup = await setupRequest(context, request);
-    await createStaticIndexPattern(setup, context);
+    const savedObjectsClient = await getInternalSavedObjectsClient(core);
+    await createStaticIndexPattern(setup, context, savedObjectsClient);
 
     // send empty response regardless of outcome
     return undefined;
diff --git a/x-pack/legacy/plugins/apm/server/routes/metrics.ts b/x-pack/plugins/apm/server/routes/metrics.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/routes/metrics.ts
rename to x-pack/plugins/apm/server/routes/metrics.ts
diff --git a/x-pack/legacy/plugins/apm/server/routes/security.ts b/x-pack/plugins/apm/server/routes/security.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/routes/security.ts
rename to x-pack/plugins/apm/server/routes/security.ts
diff --git a/x-pack/legacy/plugins/apm/server/routes/service_map.ts b/x-pack/plugins/apm/server/routes/service_map.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/routes/service_map.ts
rename to x-pack/plugins/apm/server/routes/service_map.ts
diff --git a/x-pack/legacy/plugins/apm/server/routes/service_nodes.ts b/x-pack/plugins/apm/server/routes/service_nodes.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/routes/service_nodes.ts
rename to x-pack/plugins/apm/server/routes/service_nodes.ts
diff --git a/x-pack/legacy/plugins/apm/server/routes/services.ts b/x-pack/plugins/apm/server/routes/services.ts
similarity index 88%
rename from x-pack/legacy/plugins/apm/server/routes/services.ts
rename to x-pack/plugins/apm/server/routes/services.ts
index 18777183ea1de..2d4fae9d2707a 100644
--- a/x-pack/legacy/plugins/apm/server/routes/services.ts
+++ b/x-pack/plugins/apm/server/routes/services.ts
@@ -5,7 +5,7 @@
  */
 
 import * as t from 'io-ts';
-import { AgentName } from '../../typings/es_schemas/ui/fields/Agent';
+import { AgentName } from '../../typings/es_schemas/ui/fields/agent';
 import {
   createApmTelementry,
   storeApmServicesTelemetry
@@ -18,8 +18,9 @@ import { getServiceNodeMetadata } from '../lib/services/get_service_node_metadat
 import { createRoute } from './create_route';
 import { uiFiltersRt, rangeRt } from './default_api_types';
 import { getServiceAnnotations } from '../lib/services/annotations';
+import { getInternalSavedObjectsClient } from '../lib/helpers/get_internal_saved_objects_client';
 
-export const servicesRoute = createRoute(() => ({
+export const servicesRoute = createRoute(core => ({
   path: '/api/apm/services',
   params: {
     query: t.intersection([uiFiltersRt, rangeRt])
@@ -33,7 +34,10 @@ export const servicesRoute = createRoute(() => ({
       ({ agentName }) => agentName as AgentName
     );
     const apmTelemetry = createApmTelementry(agentNames);
-    storeApmServicesTelemetry(context.__LEGACY.server, apmTelemetry);
+    const savedObjectsClient = await getInternalSavedObjectsClient(core);
+    storeApmServicesTelemetry(savedObjectsClient, apmTelemetry).catch(error => {
+      context.logger.error(error.message);
+    });
 
     return services;
   }
diff --git a/x-pack/legacy/plugins/apm/server/routes/settings/agent_configuration.ts b/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/routes/settings/agent_configuration.ts
rename to x-pack/plugins/apm/server/routes/settings/agent_configuration.ts
diff --git a/x-pack/legacy/plugins/apm/server/routes/settings/apm_indices.ts b/x-pack/plugins/apm/server/routes/settings/apm_indices.ts
similarity index 86%
rename from x-pack/legacy/plugins/apm/server/routes/settings/apm_indices.ts
rename to x-pack/plugins/apm/server/routes/settings/apm_indices.ts
index a69fba52be3f0..4e71a3dc8a0c4 100644
--- a/x-pack/legacy/plugins/apm/server/routes/settings/apm_indices.ts
+++ b/x-pack/plugins/apm/server/routes/settings/apm_indices.ts
@@ -34,9 +34,12 @@ export const apmIndicesRoute = createRoute(() => ({
 }));
 
 // save ui indices
-export const saveApmIndicesRoute = createRoute(() => ({
+export const saveApmIndicesRoute = createRoute(core => ({
   method: 'POST',
   path: '/api/apm/settings/apm-indices/save',
+  options: {
+    tags: ['access:apm', 'access:apm_write']
+  },
   params: {
     body: t.partial({
       'apm_oss.sourcemapIndices': t.string,
@@ -49,6 +52,7 @@ export const saveApmIndicesRoute = createRoute(() => ({
   },
   handler: async ({ context, request }) => {
     const { body } = context.params;
-    return await saveApmIndices(context, body);
+    const savedObjectsClient = context.core.savedObjects.client;
+    return await saveApmIndices(savedObjectsClient, body);
   }
 }));
diff --git a/x-pack/legacy/plugins/apm/server/routes/traces.ts b/x-pack/plugins/apm/server/routes/traces.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/routes/traces.ts
rename to x-pack/plugins/apm/server/routes/traces.ts
diff --git a/x-pack/legacy/plugins/apm/server/routes/transaction.ts b/x-pack/plugins/apm/server/routes/transaction.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/routes/transaction.ts
rename to x-pack/plugins/apm/server/routes/transaction.ts
diff --git a/x-pack/legacy/plugins/apm/server/routes/transaction_groups.ts b/x-pack/plugins/apm/server/routes/transaction_groups.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/routes/transaction_groups.ts
rename to x-pack/plugins/apm/server/routes/transaction_groups.ts
diff --git a/x-pack/legacy/plugins/apm/server/routes/typings.ts b/x-pack/plugins/apm/server/routes/typings.ts
similarity index 97%
rename from x-pack/legacy/plugins/apm/server/routes/typings.ts
rename to x-pack/plugins/apm/server/routes/typings.ts
index 9b114eba72626..3dc485630c180 100644
--- a/x-pack/legacy/plugins/apm/server/routes/typings.ts
+++ b/x-pack/plugins/apm/server/routes/typings.ts
@@ -14,8 +14,8 @@ import {
 import { PickByValue, Optional } from 'utility-types';
 import { Observable } from 'rxjs';
 import { Server } from 'hapi';
-import { FetchOptions } from '../../public/services/rest/callApi';
-import { APMConfig } from '../../../../../plugins/apm/server';
+import { FetchOptions } from '../../../../legacy/plugins/apm/public/services/rest/callApi';
+import { APMConfig } from '..';
 
 export interface Params {
   query?: t.HasProps;
diff --git a/x-pack/legacy/plugins/apm/server/routes/ui_filters.ts b/x-pack/plugins/apm/server/routes/ui_filters.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/server/routes/ui_filters.ts
rename to x-pack/plugins/apm/server/routes/ui_filters.ts
diff --git a/x-pack/plugins/apm/server/tutorial/envs/elastic_cloud.ts b/x-pack/plugins/apm/server/tutorial/envs/elastic_cloud.ts
index 9c66dd299b2a5..98294dddbec41 100644
--- a/x-pack/plugins/apm/server/tutorial/envs/elastic_cloud.ts
+++ b/x-pack/plugins/apm/server/tutorial/envs/elastic_cloud.ts
@@ -16,7 +16,7 @@ import {
   createJsAgentInstructions,
   createGoAgentInstructions,
   createJavaAgentInstructions,
-  createDotNetAgentInstructions,
+  createDotNetAgentInstructions
 } from '../instructions/apm_agent_instructions';
 import { CloudSetup } from '../../../../cloud/server';
 
@@ -31,7 +31,7 @@ export function createElasticCloudInstructions(cloudSetup?: CloudSetup) {
   instructionSets.push(getApmAgentInstructionSet(cloudSetup));
 
   return {
-    instructionSets,
+    instructionSets
   };
 }
 
@@ -39,7 +39,7 @@ function getApmServerInstructionSet(cloudSetup?: CloudSetup) {
   const cloudId = cloudSetup?.cloudId;
   return {
     title: i18n.translate('xpack.apm.tutorial.apmServer.title', {
-      defaultMessage: 'APM Server',
+      defaultMessage: 'APM Server'
     }),
     instructionVariants: [
       {
@@ -50,12 +50,12 @@ function getApmServerInstructionSet(cloudSetup?: CloudSetup) {
             textPre: i18n.translate('xpack.apm.tutorial.elasticCloud.textPre', {
               defaultMessage:
                 'To enable the APM Server go to [the Elastic Cloud console](https://cloud.elastic.co/deployments?q={cloudId}) and enable APM in the deployment settings. Once enabled, refresh this page.',
-              values: { cloudId },
-            }),
-          },
-        ],
-      },
-    ],
+              values: { cloudId }
+            })
+          }
+        ]
+      }
+    ]
   };
 }
 
@@ -65,45 +65,45 @@ function getApmAgentInstructionSet(cloudSetup?: CloudSetup) {
 
   return {
     title: i18n.translate('xpack.apm.tutorial.elasticCloudInstructions.title', {
-      defaultMessage: 'APM Agents',
+      defaultMessage: 'APM Agents'
     }),
     instructionVariants: [
       {
         id: INSTRUCTION_VARIANT.NODE,
-        instructions: createNodeAgentInstructions(apmServerUrl, secretToken),
+        instructions: createNodeAgentInstructions(apmServerUrl, secretToken)
       },
       {
         id: INSTRUCTION_VARIANT.DJANGO,
-        instructions: createDjangoAgentInstructions(apmServerUrl, secretToken),
+        instructions: createDjangoAgentInstructions(apmServerUrl, secretToken)
       },
       {
         id: INSTRUCTION_VARIANT.FLASK,
-        instructions: createFlaskAgentInstructions(apmServerUrl, secretToken),
+        instructions: createFlaskAgentInstructions(apmServerUrl, secretToken)
       },
       {
         id: INSTRUCTION_VARIANT.RAILS,
-        instructions: createRailsAgentInstructions(apmServerUrl, secretToken),
+        instructions: createRailsAgentInstructions(apmServerUrl, secretToken)
       },
       {
         id: INSTRUCTION_VARIANT.RACK,
-        instructions: createRackAgentInstructions(apmServerUrl, secretToken),
+        instructions: createRackAgentInstructions(apmServerUrl, secretToken)
       },
       {
         id: INSTRUCTION_VARIANT.JS,
-        instructions: createJsAgentInstructions(apmServerUrl),
+        instructions: createJsAgentInstructions(apmServerUrl)
       },
       {
         id: INSTRUCTION_VARIANT.GO,
-        instructions: createGoAgentInstructions(apmServerUrl, secretToken),
+        instructions: createGoAgentInstructions(apmServerUrl, secretToken)
       },
       {
         id: INSTRUCTION_VARIANT.JAVA,
-        instructions: createJavaAgentInstructions(apmServerUrl, secretToken),
+        instructions: createJavaAgentInstructions(apmServerUrl, secretToken)
       },
       {
         id: INSTRUCTION_VARIANT.DOTNET,
-        instructions: createDotNetAgentInstructions(apmServerUrl, secretToken),
-      },
-    ],
+        instructions: createDotNetAgentInstructions(apmServerUrl, secretToken)
+      }
+    ]
   };
 }
diff --git a/x-pack/plugins/apm/server/tutorial/envs/on_prem.ts b/x-pack/plugins/apm/server/tutorial/envs/on_prem.ts
index 03cd21119b30b..1f7b7c8344c1d 100644
--- a/x-pack/plugins/apm/server/tutorial/envs/on_prem.ts
+++ b/x-pack/plugins/apm/server/tutorial/envs/on_prem.ts
@@ -13,7 +13,7 @@ import {
   createStartServerUnix,
   createDownloadServerRpm,
   createDownloadServerDeb,
-  createDownloadServerOsx,
+  createDownloadServerOsx
 } from '../instructions/apm_server_instructions';
 import {
   createNodeAgentInstructions,
@@ -24,7 +24,7 @@ import {
   createJsAgentInstructions,
   createGoAgentInstructions,
   createJavaAgentInstructions,
-  createDotNetAgentInstructions,
+  createDotNetAgentInstructions
 } from '../instructions/apm_agent_instructions';
 
 export function onPremInstructions({
@@ -32,7 +32,7 @@ export function onPremInstructions({
   transactionIndices,
   metricsIndices,
   sourcemapIndices,
-  onboardingIndices,
+  onboardingIndices
 }: {
   errorIndices: string;
   transactionIndices: string;
@@ -48,139 +48,199 @@ export function onPremInstructions({
     instructionSets: [
       {
         title: i18n.translate('xpack.apm.tutorial.apmServer.title', {
-          defaultMessage: 'APM Server',
+          defaultMessage: 'APM Server'
         }),
         callOut: {
           title: i18n.translate('xpack.apm.tutorial.apmServer.callOut.title', {
-            defaultMessage: 'Important: Updating to 7.0 or higher',
+            defaultMessage: 'Important: Updating to 7.0 or higher'
           }),
-          message: i18n.translate('xpack.apm.tutorial.apmServer.callOut.message', {
-            defaultMessage: `Please make sure your APM Server is updated to 7.0 or higher. \
-            You can also migrate your 6.x data with the migration assistant found in Kibana's management section.`,
-          }),
-          iconType: 'alert',
+          message: i18n.translate(
+            'xpack.apm.tutorial.apmServer.callOut.message',
+            {
+              defaultMessage: `Please make sure your APM Server is updated to 7.0 or higher. \
+            You can also migrate your 6.x data with the migration assistant found in Kibana's management section.`
+            }
+          ),
+          iconType: 'alert'
         },
         instructionVariants: [
           {
             id: INSTRUCTION_VARIANT.OSX,
-            instructions: [createDownloadServerOsx(), EDIT_CONFIG, START_SERVER_UNIX],
+            instructions: [
+              createDownloadServerOsx(),
+              EDIT_CONFIG,
+              START_SERVER_UNIX
+            ]
           },
           {
             id: INSTRUCTION_VARIANT.DEB,
-            instructions: [createDownloadServerDeb(), EDIT_CONFIG, START_SERVER_UNIX_SYSV],
+            instructions: [
+              createDownloadServerDeb(),
+              EDIT_CONFIG,
+              START_SERVER_UNIX_SYSV
+            ]
           },
           {
             id: INSTRUCTION_VARIANT.RPM,
-            instructions: [createDownloadServerRpm(), EDIT_CONFIG, START_SERVER_UNIX_SYSV],
+            instructions: [
+              createDownloadServerRpm(),
+              EDIT_CONFIG,
+              START_SERVER_UNIX_SYSV
+            ]
           },
           {
             id: INSTRUCTION_VARIANT.WINDOWS,
-            instructions: createWindowsServerInstructions(),
-          },
+            instructions: createWindowsServerInstructions()
+          }
         ],
         statusCheck: {
-          title: i18n.translate('xpack.apm.tutorial.apmServer.statusCheck.title', {
-            defaultMessage: 'APM Server status',
-          }),
-          text: i18n.translate('xpack.apm.tutorial.apmServer.statusCheck.text', {
-            defaultMessage:
-              'Make sure APM Server is running before you start implementing the APM agents.',
-          }),
-          btnLabel: i18n.translate('xpack.apm.tutorial.apmServer.statusCheck.btnLabel', {
-            defaultMessage: 'Check APM Server status',
-          }),
-          success: i18n.translate('xpack.apm.tutorial.apmServer.statusCheck.successMessage', {
-            defaultMessage: 'You have correctly setup APM Server',
-          }),
-          error: i18n.translate('xpack.apm.tutorial.apmServer.statusCheck.errorMessage', {
-            defaultMessage:
-              'No APM Server detected. Please make sure it is running and you have updated to 7.0 or higher.',
-          }),
+          title: i18n.translate(
+            'xpack.apm.tutorial.apmServer.statusCheck.title',
+            {
+              defaultMessage: 'APM Server status'
+            }
+          ),
+          text: i18n.translate(
+            'xpack.apm.tutorial.apmServer.statusCheck.text',
+            {
+              defaultMessage:
+                'Make sure APM Server is running before you start implementing the APM agents.'
+            }
+          ),
+          btnLabel: i18n.translate(
+            'xpack.apm.tutorial.apmServer.statusCheck.btnLabel',
+            {
+              defaultMessage: 'Check APM Server status'
+            }
+          ),
+          success: i18n.translate(
+            'xpack.apm.tutorial.apmServer.statusCheck.successMessage',
+            {
+              defaultMessage: 'You have correctly setup APM Server'
+            }
+          ),
+          error: i18n.translate(
+            'xpack.apm.tutorial.apmServer.statusCheck.errorMessage',
+            {
+              defaultMessage:
+                'No APM Server detected. Please make sure it is running and you have updated to 7.0 or higher.'
+            }
+          ),
           esHitsCheck: {
             index: onboardingIndices,
             query: {
               bool: {
                 filter: [
                   { term: { 'processor.event': 'onboarding' } },
-                  { range: { 'observer.version_major': { gte: 7 } } },
-                ],
-              },
-            },
-          },
-        },
+                  { range: { 'observer.version_major': { gte: 7 } } }
+                ]
+              }
+            }
+          }
+        }
       },
       {
         title: i18n.translate('xpack.apm.tutorial.apmAgents.title', {
-          defaultMessage: 'APM Agents',
+          defaultMessage: 'APM Agents'
         }),
         instructionVariants: [
           {
             id: INSTRUCTION_VARIANT.JAVA,
-            instructions: createJavaAgentInstructions(),
+            instructions: createJavaAgentInstructions()
           },
           {
             id: INSTRUCTION_VARIANT.JS,
-            instructions: createJsAgentInstructions(),
+            instructions: createJsAgentInstructions()
           },
           {
             id: INSTRUCTION_VARIANT.NODE,
-            instructions: createNodeAgentInstructions(),
+            instructions: createNodeAgentInstructions()
           },
           {
             id: INSTRUCTION_VARIANT.DJANGO,
-            instructions: createDjangoAgentInstructions(),
+            instructions: createDjangoAgentInstructions()
           },
           {
             id: INSTRUCTION_VARIANT.FLASK,
-            instructions: createFlaskAgentInstructions(),
+            instructions: createFlaskAgentInstructions()
           },
           {
             id: INSTRUCTION_VARIANT.RAILS,
-            instructions: createRailsAgentInstructions(),
+            instructions: createRailsAgentInstructions()
           },
           {
             id: INSTRUCTION_VARIANT.RACK,
-            instructions: createRackAgentInstructions(),
+            instructions: createRackAgentInstructions()
           },
           {
             id: INSTRUCTION_VARIANT.GO,
-            instructions: createGoAgentInstructions(),
+            instructions: createGoAgentInstructions()
           },
           {
             id: INSTRUCTION_VARIANT.DOTNET,
-            instructions: createDotNetAgentInstructions(),
-          },
+            instructions: createDotNetAgentInstructions()
+          }
         ],
         statusCheck: {
-          title: i18n.translate('xpack.apm.tutorial.apmAgents.statusCheck.title', {
-            defaultMessage: 'Agent status',
-          }),
-          text: i18n.translate('xpack.apm.tutorial.apmAgents.statusCheck.text', {
-            defaultMessage:
-              'Make sure your application is running and the agents are sending data.',
-          }),
-          btnLabel: i18n.translate('xpack.apm.tutorial.apmAgents.statusCheck.btnLabel', {
-            defaultMessage: 'Check agent status',
-          }),
-          success: i18n.translate('xpack.apm.tutorial.apmAgents.statusCheck.successMessage', {
-            defaultMessage: 'Data successfully received from one or more agents',
-          }),
-          error: i18n.translate('xpack.apm.tutorial.apmAgents.statusCheck.errorMessage', {
-            defaultMessage: 'No data has been received from agents yet',
-          }),
+          title: i18n.translate(
+            'xpack.apm.tutorial.apmAgents.statusCheck.title',
+            {
+              defaultMessage: 'Agent status'
+            }
+          ),
+          text: i18n.translate(
+            'xpack.apm.tutorial.apmAgents.statusCheck.text',
+            {
+              defaultMessage:
+                'Make sure your application is running and the agents are sending data.'
+            }
+          ),
+          btnLabel: i18n.translate(
+            'xpack.apm.tutorial.apmAgents.statusCheck.btnLabel',
+            {
+              defaultMessage: 'Check agent status'
+            }
+          ),
+          success: i18n.translate(
+            'xpack.apm.tutorial.apmAgents.statusCheck.successMessage',
+            {
+              defaultMessage:
+                'Data successfully received from one or more agents'
+            }
+          ),
+          error: i18n.translate(
+            'xpack.apm.tutorial.apmAgents.statusCheck.errorMessage',
+            {
+              defaultMessage: 'No data has been received from agents yet'
+            }
+          ),
           esHitsCheck: {
-            index: [errorIndices, transactionIndices, metricsIndices, sourcemapIndices],
+            index: [
+              errorIndices,
+              transactionIndices,
+              metricsIndices,
+              sourcemapIndices
+            ],
             query: {
               bool: {
                 filter: [
-                  { terms: { 'processor.event': ['error', 'transaction', 'metric', 'sourcemap'] } },
-                  { range: { 'observer.version_major': { gte: 7 } } },
-                ],
-              },
-            },
-          },
-        },
-      },
-    ],
+                  {
+                    terms: {
+                      'processor.event': [
+                        'error',
+                        'transaction',
+                        'metric',
+                        'sourcemap'
+                      ]
+                    }
+                  },
+                  { range: { 'observer.version_major': { gte: 7 } } }
+                ]
+              }
+            }
+          }
+        }
+      }
+    ]
   };
 }
diff --git a/x-pack/plugins/apm/server/tutorial/index.ts b/x-pack/plugins/apm/server/tutorial/index.ts
index 5399d13937179..1fbac0b6495df 100644
--- a/x-pack/plugins/apm/server/tutorial/index.ts
+++ b/x-pack/plugins/apm/server/tutorial/index.ts
@@ -9,17 +9,21 @@ import { onPremInstructions } from './envs/on_prem';
 import { createElasticCloudInstructions } from './envs/elastic_cloud';
 import apmIndexPattern from './index_pattern.json';
 import { CloudSetup } from '../../../cloud/server';
-import { ArtifactsSchema, TutorialsCategory } from '../../../../../src/plugins/home/server';
+import {
+  ArtifactsSchema,
+  TutorialsCategory
+} from '../../../../../src/plugins/home/server';
 
 const apmIntro = i18n.translate('xpack.apm.tutorial.introduction', {
-  defaultMessage: 'Collect in-depth performance metrics and errors from inside your applications.',
+  defaultMessage:
+    'Collect in-depth performance metrics and errors from inside your applications.'
 });
 
 export const tutorialProvider = ({
   isEnabled,
   indexPatternTitle,
   cloud,
-  indices,
+  indices
 }: {
   isEnabled: boolean;
   indexPatternTitle: string;
@@ -37,9 +41,9 @@ export const tutorialProvider = ({
       ...apmIndexPattern,
       attributes: {
         ...apmIndexPattern.attributes,
-        title: indexPatternTitle,
-      },
-    },
+        title: indexPatternTitle
+      }
+    }
   ];
 
   const artifacts: ArtifactsSchema = {
@@ -49,41 +53,47 @@ export const tutorialProvider = ({
         linkLabel: i18n.translate(
           'xpack.apm.tutorial.specProvider.artifacts.dashboards.linkLabel',
           {
-            defaultMessage: 'APM dashboard',
+            defaultMessage: 'APM dashboard'
           }
         ),
-        isOverview: true,
-      },
-    ],
+        isOverview: true
+      }
+    ]
   };
 
   if (isEnabled) {
     artifacts.application = {
       path: '/app/apm',
-      label: i18n.translate('xpack.apm.tutorial.specProvider.artifacts.application.label', {
-        defaultMessage: 'Launch APM',
-      }),
+      label: i18n.translate(
+        'xpack.apm.tutorial.specProvider.artifacts.application.label',
+        {
+          defaultMessage: 'Launch APM'
+        }
+      )
     };
   }
 
   return {
     id: 'apm',
     name: i18n.translate('xpack.apm.tutorial.specProvider.name', {
-      defaultMessage: 'APM',
+      defaultMessage: 'APM'
     }),
     category: TutorialsCategory.OTHER,
     shortDescription: apmIntro,
-    longDescription: i18n.translate('xpack.apm.tutorial.specProvider.longDescription', {
-      defaultMessage:
-        'Application Performance Monitoring (APM) collects in-depth \
+    longDescription: i18n.translate(
+      'xpack.apm.tutorial.specProvider.longDescription',
+      {
+        defaultMessage:
+          'Application Performance Monitoring (APM) collects in-depth \
 performance metrics and errors from inside your application. \
 It allows you to monitor the performance of thousands of applications in real time. \
 [Learn more]({learnMoreLink}).',
-      values: {
-        learnMoreLink:
-          '{config.docs.base_url}guide/en/apm/get-started/{config.docs.version}/index.html',
-      },
-    }),
+        values: {
+          learnMoreLink:
+            '{config.docs.base_url}guide/en/apm/get-started/{config.docs.version}/index.html'
+        }
+      }
+    ),
     euiIconType: 'apmApp',
     artifacts,
     onPrem: onPremInstructions(indices),
@@ -93,8 +103,9 @@ It allows you to monitor the performance of thousands of applications in real ti
     savedObjectsInstallMsg: i18n.translate(
       'xpack.apm.tutorial.specProvider.savedObjectsInstallMsg',
       {
-        defaultMessage: 'An APM index pattern is required for some features in the APM UI.',
+        defaultMessage:
+          'An APM index pattern is required for some features in the APM UI.'
       }
-    ),
+    )
   };
 };
diff --git a/x-pack/plugins/apm/server/tutorial/instructions/apm_agent_instructions.ts b/x-pack/plugins/apm/server/tutorial/instructions/apm_agent_instructions.ts
index d1ef92df20f6f..54dab4d13845e 100644
--- a/x-pack/plugins/apm/server/tutorial/instructions/apm_agent_instructions.ts
+++ b/x-pack/plugins/apm/server/tutorial/instructions/apm_agent_instructions.ts
@@ -6,48 +6,56 @@
 
 import { i18n } from '@kbn/i18n';
 
-export const createNodeAgentInstructions = (apmServerUrl = '', secretToken = '') => [
+export const createNodeAgentInstructions = (
+  apmServerUrl = '',
+  secretToken = ''
+) => [
   {
     title: i18n.translate('xpack.apm.tutorial.nodeClient.install.title', {
-      defaultMessage: 'Install the APM agent',
+      defaultMessage: 'Install the APM agent'
     }),
     textPre: i18n.translate('xpack.apm.tutorial.nodeClient.install.textPre', {
-      defaultMessage: 'Install the APM agent for Node.js as a dependency to your application.',
+      defaultMessage:
+        'Install the APM agent for Node.js as a dependency to your application.'
     }),
-    commands: ['npm install elastic-apm-node --save'],
+    commands: ['npm install elastic-apm-node --save']
   },
   {
     title: i18n.translate('xpack.apm.tutorial.nodeClient.configure.title', {
-      defaultMessage: 'Configure the agent',
+      defaultMessage: 'Configure the agent'
     }),
     textPre: i18n.translate('xpack.apm.tutorial.nodeClient.configure.textPre', {
       defaultMessage:
         'Agents are libraries that run inside of your application process. \
 APM services are created programmatically based on the `serviceName`. \
-This agent supports a vararity of frameworks but can also be used with your custom stack.',
+This agent supports a vararity of frameworks but can also be used with your custom stack.'
     }),
     commands: `// ${i18n.translate(
       'xpack.apm.tutorial.nodeClient.configure.commands.addThisToTheFileTopComment',
       {
-        defaultMessage: 'Add this to the VERY top of the first file loaded in your app',
+        defaultMessage:
+          'Add this to the VERY top of the first file loaded in your app'
       }
     )}
 var apm = require('elastic-apm-node').start({curlyOpen}
   // ${i18n.translate(
     'xpack.apm.tutorial.nodeClient.configure.commands.setRequiredServiceNameComment',
     {
-      defaultMessage: 'Override service name from package.json',
+      defaultMessage: 'Override service name from package.json'
+    }
+  )}
+  // ${i18n.translate(
+    'xpack.apm.tutorial.nodeClient.configure.commands.allowedCharactersComment',
+    {
+      defaultMessage: 'Allowed characters: a-z, A-Z, 0-9, -, _, and space'
     }
   )}
-  // ${i18n.translate('xpack.apm.tutorial.nodeClient.configure.commands.allowedCharactersComment', {
-    defaultMessage: 'Allowed characters: a-z, A-Z, 0-9, -, _, and space',
-  })}
   serviceName: '',
 
   // ${i18n.translate(
     'xpack.apm.tutorial.nodeClient.configure.commands.useIfApmRequiresTokenComment',
     {
-      defaultMessage: 'Use if APM Server requires a token',
+      defaultMessage: 'Use if APM Server requires a token'
     }
   )}
   secretToken: '${secretToken}',
@@ -55,48 +63,59 @@ var apm = require('elastic-apm-node').start({curlyOpen}
   // ${i18n.translate(
     'xpack.apm.tutorial.nodeClient.configure.commands.setCustomApmServerUrlComment',
     {
-      defaultMessage: 'Set custom APM Server URL (default: {defaultApmServerUrl})',
-      values: { defaultApmServerUrl: 'http://localhost:8200' },
+      defaultMessage:
+        'Set custom APM Server URL (default: {defaultApmServerUrl})',
+      values: { defaultApmServerUrl: 'http://localhost:8200' }
     }
   )}
   serverUrl: '${apmServerUrl}'
 {curlyClose})`.split('\n'),
-    textPost: i18n.translate('xpack.apm.tutorial.nodeClient.configure.textPost', {
-      defaultMessage:
-        'See [the documentation]({documentationLink}) for advanced usage, including how to use with \
+    textPost: i18n.translate(
+      'xpack.apm.tutorial.nodeClient.configure.textPost',
+      {
+        defaultMessage:
+          'See [the documentation]({documentationLink}) for advanced usage, including how to use with \
 [Babel/ES Modules]({babelEsModulesLink}).',
-      values: {
-        documentationLink: '{config.docs.base_url}guide/en/apm/agent/nodejs/current/index.html',
-        babelEsModulesLink:
-          '{config.docs.base_url}guide/en/apm/agent/nodejs/current/advanced-setup.html#es-modules',
-      },
-    }),
-  },
+        values: {
+          documentationLink:
+            '{config.docs.base_url}guide/en/apm/agent/nodejs/current/index.html',
+          babelEsModulesLink:
+            '{config.docs.base_url}guide/en/apm/agent/nodejs/current/advanced-setup.html#es-modules'
+        }
+      }
+    )
+  }
 ];
 
-export const createDjangoAgentInstructions = (apmServerUrl = '', secretToken = '') => [
+export const createDjangoAgentInstructions = (
+  apmServerUrl = '',
+  secretToken = ''
+) => [
   {
     title: i18n.translate('xpack.apm.tutorial.djangoClient.install.title', {
-      defaultMessage: 'Install the APM agent',
+      defaultMessage: 'Install the APM agent'
     }),
     textPre: i18n.translate('xpack.apm.tutorial.djangoClient.install.textPre', {
-      defaultMessage: 'Install the APM agent for Python as a dependency.',
+      defaultMessage: 'Install the APM agent for Python as a dependency.'
     }),
-    commands: ['$ pip install elastic-apm'],
+    commands: ['$ pip install elastic-apm']
   },
   {
     title: i18n.translate('xpack.apm.tutorial.djangoClient.configure.title', {
-      defaultMessage: 'Configure the agent',
-    }),
-    textPre: i18n.translate('xpack.apm.tutorial.djangoClient.configure.textPre', {
-      defaultMessage:
-        'Agents are libraries that run inside of your application process. \
-APM services are created programmatically based on the `SERVICE_NAME`.',
+      defaultMessage: 'Configure the agent'
     }),
+    textPre: i18n.translate(
+      'xpack.apm.tutorial.djangoClient.configure.textPre',
+      {
+        defaultMessage:
+          'Agents are libraries that run inside of your application process. \
+APM services are created programmatically based on the `SERVICE_NAME`.'
+      }
+    ),
     commands: `# ${i18n.translate(
       'xpack.apm.tutorial.djangoClient.configure.commands.addAgentComment',
       {
-        defaultMessage: 'Add the agent to the installed apps',
+        defaultMessage: 'Add the agent to the installed apps'
       }
     )}
 INSTALLED_APPS = (
@@ -108,13 +127,13 @@ ELASTIC_APM = {curlyOpen}
   # ${i18n.translate(
     'xpack.apm.tutorial.djangoClient.configure.commands.setRequiredServiceNameComment',
     {
-      defaultMessage: 'Set required service name. Allowed characters:',
+      defaultMessage: 'Set required service name. Allowed characters:'
     }
   )}
   # ${i18n.translate(
     'xpack.apm.tutorial.djangoClient.configure.commands.allowedCharactersComment',
     {
-      defaultMessage: 'a-z, A-Z, 0-9, -, _, and space',
+      defaultMessage: 'a-z, A-Z, 0-9, -, _, and space'
     }
   )}
   'SERVICE_NAME': '',
@@ -122,7 +141,7 @@ ELASTIC_APM = {curlyOpen}
   # ${i18n.translate(
     'xpack.apm.tutorial.djangoClient.configure.commands.useIfApmServerRequiresTokenComment',
     {
-      defaultMessage: 'Use if APM Server requires a token',
+      defaultMessage: 'Use if APM Server requires a token'
     }
   )}
   'SECRET_TOKEN': '${secretToken}',
@@ -130,8 +149,9 @@ ELASTIC_APM = {curlyOpen}
   # ${i18n.translate(
     'xpack.apm.tutorial.djangoClient.configure.commands.setCustomApmServerUrlComment',
     {
-      defaultMessage: 'Set custom APM Server URL (default: {defaultApmServerUrl})',
-      values: { defaultApmServerUrl: 'http://localhost:8200' },
+      defaultMessage:
+        'Set custom APM Server URL (default: {defaultApmServerUrl})',
+      values: { defaultApmServerUrl: 'http://localhost:8200' }
     }
   )}
   'SERVER_URL': '${apmServerUrl}',
@@ -140,72 +160,90 @@ ELASTIC_APM = {curlyOpen}
 # ${i18n.translate(
       'xpack.apm.tutorial.djangoClient.configure.commands.addTracingMiddlewareComment',
       {
-        defaultMessage: 'To send performance metrics, add our tracing middleware:',
+        defaultMessage:
+          'To send performance metrics, add our tracing middleware:'
       }
     )}
 MIDDLEWARE = (
   'elasticapm.contrib.django.middleware.TracingMiddleware',
   #...
 )`.split('\n'),
-    textPost: i18n.translate('xpack.apm.tutorial.djangoClient.configure.textPost', {
-      defaultMessage: 'See the [documentation]({documentationLink}) for advanced usage.',
-      values: {
-        documentationLink:
-          '{config.docs.base_url}guide/en/apm/agent/python/current/django-support.html',
-      },
-    }),
-  },
+    textPost: i18n.translate(
+      'xpack.apm.tutorial.djangoClient.configure.textPost',
+      {
+        defaultMessage:
+          'See the [documentation]({documentationLink}) for advanced usage.',
+        values: {
+          documentationLink:
+            '{config.docs.base_url}guide/en/apm/agent/python/current/django-support.html'
+        }
+      }
+    )
+  }
 ];
 
-export const createFlaskAgentInstructions = (apmServerUrl = '', secretToken = '') => [
+export const createFlaskAgentInstructions = (
+  apmServerUrl = '',
+  secretToken = ''
+) => [
   {
     title: i18n.translate('xpack.apm.tutorial.flaskClient.install.title', {
-      defaultMessage: 'Install the APM agent',
+      defaultMessage: 'Install the APM agent'
     }),
     textPre: i18n.translate('xpack.apm.tutorial.flaskClient.install.textPre', {
-      defaultMessage: 'Install the APM agent for Python as a dependency.',
+      defaultMessage: 'Install the APM agent for Python as a dependency.'
     }),
-    commands: ['$ pip install elastic-apm[flask]'],
+    commands: ['$ pip install elastic-apm[flask]']
   },
   {
     title: i18n.translate('xpack.apm.tutorial.flaskClient.configure.title', {
-      defaultMessage: 'Configure the agent',
-    }),
-    textPre: i18n.translate('xpack.apm.tutorial.flaskClient.configure.textPre', {
-      defaultMessage:
-        'Agents are libraries that run inside of your application process. \
-APM services are created programmatically based on the `SERVICE_NAME`.',
+      defaultMessage: 'Configure the agent'
     }),
+    textPre: i18n.translate(
+      'xpack.apm.tutorial.flaskClient.configure.textPre',
+      {
+        defaultMessage:
+          'Agents are libraries that run inside of your application process. \
+APM services are created programmatically based on the `SERVICE_NAME`.'
+      }
+    ),
     commands: `# ${i18n.translate(
       'xpack.apm.tutorial.flaskClient.configure.commands.initializeUsingEnvironmentVariablesComment',
       {
-        defaultMessage: 'initialize using environment variables',
+        defaultMessage: 'initialize using environment variables'
       }
     )}
 from elasticapm.contrib.flask import ElasticAPM
 app = Flask(__name__)
 apm = ElasticAPM(app)
 
-# ${i18n.translate('xpack.apm.tutorial.flaskClient.configure.commands.configureElasticApmComment', {
-      defaultMessage: "or configure to use ELASTIC_APM in your application's settings",
-    })}
+# ${i18n.translate(
+      'xpack.apm.tutorial.flaskClient.configure.commands.configureElasticApmComment',
+      {
+        defaultMessage:
+          "or configure to use ELASTIC_APM in your application's settings"
+      }
+    )}
 from elasticapm.contrib.flask import ElasticAPM
 app.config['ELASTIC_APM'] = {curlyOpen}
   # ${i18n.translate(
     'xpack.apm.tutorial.flaskClient.configure.commands.setRequiredServiceNameComment',
     {
-      defaultMessage: 'Set required service name. Allowed characters:',
+      defaultMessage: 'Set required service name. Allowed characters:'
+    }
+  )}
+  # ${i18n.translate(
+    'xpack.apm.tutorial.flaskClient.configure.commands.allowedCharactersComment',
+    {
+      defaultMessage: 'a-z, A-Z, 0-9, -, _, and space'
     }
   )}
-  # ${i18n.translate('xpack.apm.tutorial.flaskClient.configure.commands.allowedCharactersComment', {
-    defaultMessage: 'a-z, A-Z, 0-9, -, _, and space',
-  })}
   'SERVICE_NAME': '',
 
   # ${i18n.translate(
     'xpack.apm.tutorial.flaskClient.configure.commands.useIfApmServerRequiresTokenComment',
     {
-      defaultMessage: 'Use if APM Server requires a token',
+      defaultMessage: 'Use if APM Server requires a token'
     }
   )}
   'SECRET_TOKEN': '${secretToken}',
@@ -213,43 +251,54 @@ app.config['ELASTIC_APM'] = {curlyOpen}
   # ${i18n.translate(
     'xpack.apm.tutorial.flaskClient.configure.commands.setCustomApmServerUrlComment',
     {
-      defaultMessage: 'Set custom APM Server URL (default: {defaultApmServerUrl})',
-      values: { defaultApmServerUrl: 'http://localhost:8200' },
+      defaultMessage:
+        'Set custom APM Server URL (default: {defaultApmServerUrl})',
+      values: { defaultApmServerUrl: 'http://localhost:8200' }
     }
   )}
   'SERVER_URL': '${apmServerUrl}',
 {curlyClose}
 
 apm = ElasticAPM(app)`.split('\n'),
-    textPost: i18n.translate('xpack.apm.tutorial.flaskClient.configure.textPost', {
-      defaultMessage: 'See the [documentation]({documentationLink}) for advanced usage.',
-      values: {
-        documentationLink:
-          '{config.docs.base_url}guide/en/apm/agent/python/current/flask-support.html',
-      },
-    }),
-  },
+    textPost: i18n.translate(
+      'xpack.apm.tutorial.flaskClient.configure.textPost',
+      {
+        defaultMessage:
+          'See the [documentation]({documentationLink}) for advanced usage.',
+        values: {
+          documentationLink:
+            '{config.docs.base_url}guide/en/apm/agent/python/current/flask-support.html'
+        }
+      }
+    )
+  }
 ];
 
-export const createRailsAgentInstructions = (apmServerUrl = '', secretToken = '') => [
+export const createRailsAgentInstructions = (
+  apmServerUrl = '',
+  secretToken = ''
+) => [
   {
     title: i18n.translate('xpack.apm.tutorial.railsClient.install.title', {
-      defaultMessage: 'Install the APM agent',
+      defaultMessage: 'Install the APM agent'
     }),
     textPre: i18n.translate('xpack.apm.tutorial.railsClient.install.textPre', {
-      defaultMessage: 'Add the agent to your Gemfile.',
+      defaultMessage: 'Add the agent to your Gemfile.'
     }),
-    commands: [`gem 'elastic-apm'`],
+    commands: [`gem 'elastic-apm'`]
   },
   {
     title: i18n.translate('xpack.apm.tutorial.railsClient.configure.title', {
-      defaultMessage: 'Configure the agent',
-    }),
-    textPre: i18n.translate('xpack.apm.tutorial.railsClient.configure.textPre', {
-      defaultMessage:
-        'APM is automatically started when your app boots. Configure the agent, by creating the config file {configFile}',
-      values: { configFile: '`config/elastic_apm.yml`' },
+      defaultMessage: 'Configure the agent'
     }),
+    textPre: i18n.translate(
+      'xpack.apm.tutorial.railsClient.configure.textPre',
+      {
+        defaultMessage:
+          'APM is automatically started when your app boots. Configure the agent, by creating the config file {configFile}',
+        values: { configFile: '`config/elastic_apm.yml`' }
+      }
+    ),
     commands: `# config/elastic_apm.yml:
 
 # Set service name - allowed characters: a-z, A-Z, 0-9, -, _ and space
@@ -261,33 +310,40 @@ export const createRailsAgentInstructions = (apmServerUrl = '', secretToken = ''
 
 # Set custom APM Server URL (default: http://localhost:8200)
 # server_url: '${apmServerUrl || 'http://localhost:8200'}'`.split('\n'),
-    textPost: i18n.translate('xpack.apm.tutorial.railsClient.configure.textPost', {
-      defaultMessage:
-        'See the [documentation]({documentationLink}) for configuration options and advanced usage.\n\n',
-      values: {
-        documentationLink: '{config.docs.base_url}guide/en/apm/agent/ruby/current/index.html',
-      },
-    }),
-  },
+    textPost: i18n.translate(
+      'xpack.apm.tutorial.railsClient.configure.textPost',
+      {
+        defaultMessage:
+          'See the [documentation]({documentationLink}) for configuration options and advanced usage.\n\n',
+        values: {
+          documentationLink:
+            '{config.docs.base_url}guide/en/apm/agent/ruby/current/index.html'
+        }
+      }
+    )
+  }
 ];
 
-export const createRackAgentInstructions = (apmServerUrl = '', secretToken = '') => [
+export const createRackAgentInstructions = (
+  apmServerUrl = '',
+  secretToken = ''
+) => [
   {
     title: i18n.translate('xpack.apm.tutorial.rackClient.install.title', {
-      defaultMessage: 'Install the APM agent',
+      defaultMessage: 'Install the APM agent'
     }),
     textPre: i18n.translate('xpack.apm.tutorial.rackClient.install.textPre', {
-      defaultMessage: 'Add the agent to your Gemfile.',
+      defaultMessage: 'Add the agent to your Gemfile.'
     }),
-    commands: [`gem 'elastic-apm'`],
+    commands: [`gem 'elastic-apm'`]
   },
   {
     title: i18n.translate('xpack.apm.tutorial.rackClient.configure.title', {
-      defaultMessage: 'Configure the agent',
+      defaultMessage: 'Configure the agent'
     }),
     textPre: i18n.translate('xpack.apm.tutorial.rackClient.configure.textPre', {
       defaultMessage:
-        'For Rack or a compatible framework (e.g. Sinatra), include the middleware in your app and start the agent.',
+        'For Rack or a compatible framework (e.g. Sinatra), include the middleware in your app and start the agent.'
     }),
     commands: `# config.ru
   require 'sinatra/base'
@@ -302,38 +358,45 @@ export const createRackAgentInstructions = (apmServerUrl = '', secretToken = '')
     app: MySinatraApp, # ${i18n.translate(
       'xpack.apm.tutorial.rackClient.configure.commands.requiredComment',
       {
-        defaultMessage: 'required',
+        defaultMessage: 'required'
       }
     )}
     config_file: '' # ${i18n.translate(
       'xpack.apm.tutorial.rackClient.configure.commands.optionalComment',
       {
-        defaultMessage: 'optional, defaults to config/elastic_apm.yml',
+        defaultMessage: 'optional, defaults to config/elastic_apm.yml'
       }
     )}
   )
 
   run MySinatraApp
 
-  at_exit {curlyOpen} ElasticAPM.stop {curlyClose}`.split('\n'),
+  at_exit {curlyOpen} ElasticAPM.stop {curlyClose}`.split('\n')
   },
   {
     title: i18n.translate('xpack.apm.tutorial.rackClient.createConfig.title', {
-      defaultMessage: 'Create config file',
-    }),
-    textPre: i18n.translate('xpack.apm.tutorial.rackClient.createConfig.textPre', {
-      defaultMessage: 'Create a config file {configFile}:',
-      values: { configFile: '`config/elastic_apm.yml`' },
+      defaultMessage: 'Create config file'
     }),
+    textPre: i18n.translate(
+      'xpack.apm.tutorial.rackClient.createConfig.textPre',
+      {
+        defaultMessage: 'Create a config file {configFile}:',
+        values: { configFile: '`config/elastic_apm.yml`' }
+      }
+    ),
     commands: `# config/elastic_apm.yml:
 
-# ${i18n.translate('xpack.apm.tutorial.rackClient.createConfig.commands.setServiceNameComment', {
-      defaultMessage: 'Set service name - allowed characters: a-z, A-Z, 0-9, -, _ and space',
-    })}
+# ${i18n.translate(
+      'xpack.apm.tutorial.rackClient.createConfig.commands.setServiceNameComment',
+      {
+        defaultMessage:
+          'Set service name - allowed characters: a-z, A-Z, 0-9, -, _ and space'
+      }
+    )}
 # ${i18n.translate(
       'xpack.apm.tutorial.rackClient.createConfig.commands.defaultsToTheNameOfRackAppClassComment',
       {
-        defaultMessage: "Defaults to the name of your Rack app's class.",
+        defaultMessage: "Defaults to the name of your Rack app's class."
       }
     )}
 # service_name: 'my-service'
@@ -341,7 +404,7 @@ export const createRackAgentInstructions = (apmServerUrl = '', secretToken = '')
 # ${i18n.translate(
       'xpack.apm.tutorial.rackClient.createConfig.commands.useIfApmServerRequiresTokenComment',
       {
-        defaultMessage: 'Use if APM Server requires a token',
+        defaultMessage: 'Use if APM Server requires a token'
       }
     )}
 # secret_token: '${secretToken}'
@@ -349,46 +412,63 @@ export const createRackAgentInstructions = (apmServerUrl = '', secretToken = '')
 # ${i18n.translate(
       'xpack.apm.tutorial.rackClient.createConfig.commands.setCustomApmServerComment',
       {
-        defaultMessage: 'Set custom APM Server URL (default: {defaultServerUrl})',
-        values: { defaultServerUrl: 'http://localhost:8200' },
+        defaultMessage:
+          'Set custom APM Server URL (default: {defaultServerUrl})',
+        values: { defaultServerUrl: 'http://localhost:8200' }
       }
     )}
 # server_url: '${apmServerUrl || 'http://localhost:8200'}'`.split('\n'),
-    textPost: i18n.translate('xpack.apm.tutorial.rackClient.createConfig.textPost', {
-      defaultMessage:
-        'See the [documentation]({documentationLink}) for configuration options and advanced usage.\n\n',
-      values: {
-        documentationLink: '{config.docs.base_url}guide/en/apm/agent/ruby/current/index.html',
-      },
-    }),
-  },
+    textPost: i18n.translate(
+      'xpack.apm.tutorial.rackClient.createConfig.textPost',
+      {
+        defaultMessage:
+          'See the [documentation]({documentationLink}) for configuration options and advanced usage.\n\n',
+        values: {
+          documentationLink:
+            '{config.docs.base_url}guide/en/apm/agent/ruby/current/index.html'
+        }
+      }
+    )
+  }
 ];
 
 export const createJsAgentInstructions = (apmServerUrl = '') => [
   {
-    title: i18n.translate('xpack.apm.tutorial.jsClient.enableRealUserMonitoring.title', {
-      defaultMessage: 'Enable Real User Monitoring support in APM server',
-    }),
-    textPre: i18n.translate('xpack.apm.tutorial.jsClient.enableRealUserMonitoring.textPre', {
-      defaultMessage:
-        'APM Server disables RUM support by default. See the [documentation]({documentationLink}) \
+    title: i18n.translate(
+      'xpack.apm.tutorial.jsClient.enableRealUserMonitoring.title',
+      {
+        defaultMessage: 'Enable Real User Monitoring support in APM server'
+      }
+    ),
+    textPre: i18n.translate(
+      'xpack.apm.tutorial.jsClient.enableRealUserMonitoring.textPre',
+      {
+        defaultMessage:
+          'APM Server disables RUM support by default. See the [documentation]({documentationLink}) \
 for details on how to enable RUM support.',
-      values: {
-        documentationLink:
-          '{config.docs.base_url}guide/en/apm/server/{config.docs.version}/configuration-rum.html',
-      },
-    }),
+        values: {
+          documentationLink:
+            '{config.docs.base_url}guide/en/apm/server/{config.docs.version}/configuration-rum.html'
+        }
+      }
+    )
   },
   {
-    title: i18n.translate('xpack.apm.tutorial.jsClient.installDependency.title', {
-      defaultMessage: 'Set up the Agent as a dependency',
-    }),
-    textPre: i18n.translate('xpack.apm.tutorial.jsClient.installDependency.textPre', {
-      defaultMessage:
-        'You can install the Agent as a dependency to your application with \
+    title: i18n.translate(
+      'xpack.apm.tutorial.jsClient.installDependency.title',
+      {
+        defaultMessage: 'Set up the Agent as a dependency'
+      }
+    ),
+    textPre: i18n.translate(
+      'xpack.apm.tutorial.jsClient.installDependency.textPre',
+      {
+        defaultMessage:
+          'You can install the Agent as a dependency to your application with \
 `npm install @elastic/apm-rum --save`.\n\n\
-The Agent can then be initialized and configured in your application like this:',
-    }),
+The Agent can then be initialized and configured in your application like this:'
+      }
+    ),
     commands: `import {curlyOpen} init as initApm {curlyClose} from '@elastic/apm-rum'
 var apm = initApm({curlyOpen}
 
@@ -396,7 +476,7 @@ var apm = initApm({curlyOpen}
     'xpack.apm.tutorial.jsClient.installDependency.commands.setRequiredServiceNameComment',
     {
       defaultMessage:
-        'Set required service name (allowed characters: a-z, A-Z, 0-9, -, _, and space)',
+        'Set required service name (allowed characters: a-z, A-Z, 0-9, -, _, and space)'
     }
   )}
   serviceName: 'your-app-name',
@@ -404,8 +484,9 @@ var apm = initApm({curlyOpen}
   // ${i18n.translate(
     'xpack.apm.tutorial.jsClient.installDependency.commands.setCustomApmServerUrlComment',
     {
-      defaultMessage: 'Set custom APM Server URL (default: {defaultApmServerUrl})',
-      values: { defaultApmServerUrl: 'http://localhost:8200' },
+      defaultMessage:
+        'Set custom APM Server URL (default: {defaultApmServerUrl})',
+      values: { defaultApmServerUrl: 'http://localhost:8200' }
     }
   )}
   serverUrl: '${apmServerUrl}',
@@ -413,24 +494,27 @@ var apm = initApm({curlyOpen}
   // ${i18n.translate(
     'xpack.apm.tutorial.jsClient.installDependency.commands.setServiceVersionComment',
     {
-      defaultMessage: 'Set service version (required for source map feature)',
+      defaultMessage: 'Set service version (required for source map feature)'
     }
   )}
   serviceVersion: ''
 {curlyClose})`.split('\n'),
-    textPost: i18n.translate('xpack.apm.tutorial.jsClient.installDependency.textPost', {
-      defaultMessage:
-        'Framework integrations, like React or Angular, have custom dependencies. \
+    textPost: i18n.translate(
+      'xpack.apm.tutorial.jsClient.installDependency.textPost',
+      {
+        defaultMessage:
+          'Framework integrations, like React or Angular, have custom dependencies. \
 See the [integration documentation]({docLink}) for more information.',
-      values: {
-        docLink:
-          '{config.docs.base_url}guide/en/apm/agent/rum-js/{config.docs.version}/framework-integrations.html',
-      },
-    }),
+        values: {
+          docLink:
+            '{config.docs.base_url}guide/en/apm/agent/rum-js/{config.docs.version}/framework-integrations.html'
+        }
+      }
+    )
   },
   {
     title: i18n.translate('xpack.apm.tutorial.jsClient.scriptTags.title', {
-      defaultMessage: 'Set up the Agent with Script Tags',
+      defaultMessage: 'Set up the Agent with Script Tags'
     }),
     textPre: i18n.translate('xpack.apm.tutorial.jsClient.scriptTags.textPre', {
       defaultMessage:
@@ -439,9 +523,11 @@ Add a `<script>` tag to the HTML page and use the `elasticApm` global object to
 Don't forget to download the latest version of the RUM Agent from [GitHub]({GitHubLink}) or [UNPKG]({UnpkgLink}), \
 and host the file on your Server/CDN before deploying to production.",
       values: {
-        GitHubLink: 'https://github.com/elastic/apm-agent-rum-js/releases/latest',
-        UnpkgLink: 'https://unpkg.com/@elastic/apm-rum/dist/bundles/elastic-apm-rum.umd.min.js',
-      },
+        GitHubLink:
+          'https://github.com/elastic/apm-agent-rum-js/releases/latest',
+        UnpkgLink:
+          'https://unpkg.com/@elastic/apm-rum/dist/bundles/elastic-apm-rum.umd.min.js'
+      }
     }),
     commands: `\
 <script src="https://your-cdn-host.com/path/to/elastic-apm-rum.umd.min.js" crossorigin></script>
@@ -451,72 +537,91 @@ and host the file on your Server/CDN before deploying to production.",
     serverUrl: 'http://localhost:8200',
   {curlyClose})
 </script>
-`.split('\n'),
-  },
+`.split('\n')
+  }
 ];
 
-export const createGoAgentInstructions = (apmServerUrl = '', secretToken = '') => [
+export const createGoAgentInstructions = (
+  apmServerUrl = '',
+  secretToken = ''
+) => [
   {
     title: i18n.translate('xpack.apm.tutorial.goClient.install.title', {
-      defaultMessage: 'Install the APM agent',
+      defaultMessage: 'Install the APM agent'
     }),
     textPre: i18n.translate('xpack.apm.tutorial.goClient.install.textPre', {
-      defaultMessage: 'Install the APM agent packages for Go.',
+      defaultMessage: 'Install the APM agent packages for Go.'
     }),
-    commands: ['go get go.elastic.co/apm'],
+    commands: ['go get go.elastic.co/apm']
   },
   {
     title: i18n.translate('xpack.apm.tutorial.goClient.configure.title', {
-      defaultMessage: 'Configure the agent',
+      defaultMessage: 'Configure the agent'
     }),
     textPre: i18n.translate('xpack.apm.tutorial.goClient.configure.textPre', {
       defaultMessage:
         'Agents are libraries that run inside of your application process. \
 APM services are created programmatically based on the executable \
-file name, or the `ELASTIC_APM_SERVICE_NAME` environment variable.',
+file name, or the `ELASTIC_APM_SERVICE_NAME` environment variable.'
     }),
     commands: `# ${i18n.translate(
       'xpack.apm.tutorial.goClient.configure.commands.initializeUsingEnvironmentVariablesComment',
       {
-        defaultMessage: 'Initialize using environment variables:',
+        defaultMessage: 'Initialize using environment variables:'
       }
     )}
 
-# ${i18n.translate('xpack.apm.tutorial.goClient.configure.commands.setServiceNameComment', {
-      defaultMessage: 'Set the service name. Allowed characters: # a-z, A-Z, 0-9, -, _, and space.',
-    })}
-# ${i18n.translate('xpack.apm.tutorial.goClient.configure.commands.usedExecutableNameComment', {
-      defaultMessage:
-        'If ELASTIC_APM_SERVICE_NAME is not specified, the executable name will be used.',
-    })}
+# ${i18n.translate(
+      'xpack.apm.tutorial.goClient.configure.commands.setServiceNameComment',
+      {
+        defaultMessage:
+          'Set the service name. Allowed characters: # a-z, A-Z, 0-9, -, _, and space.'
+      }
+    )}
+# ${i18n.translate(
+      'xpack.apm.tutorial.goClient.configure.commands.usedExecutableNameComment',
+      {
+        defaultMessage:
+          'If ELASTIC_APM_SERVICE_NAME is not specified, the executable name will be used.'
+      }
+    )}
 export ELASTIC_APM_SERVICE_NAME=
 
-# ${i18n.translate('xpack.apm.tutorial.goClient.configure.commands.setCustomApmServerUrlComment', {
-      defaultMessage: 'Set custom APM Server URL (default: {defaultApmServerUrl})',
-      values: { defaultApmServerUrl: 'http://localhost:8200' },
-    })}
+# ${i18n.translate(
+      'xpack.apm.tutorial.goClient.configure.commands.setCustomApmServerUrlComment',
+      {
+        defaultMessage:
+          'Set custom APM Server URL (default: {defaultApmServerUrl})',
+        values: { defaultApmServerUrl: 'http://localhost:8200' }
+      }
+    )}
 export ELASTIC_APM_SERVER_URL=${apmServerUrl}
 
-# ${i18n.translate('xpack.apm.tutorial.goClient.configure.commands.useIfApmRequiresTokenComment', {
-      defaultMessage: 'Use if APM Server requires a token',
-    })}
+# ${i18n.translate(
+      'xpack.apm.tutorial.goClient.configure.commands.useIfApmRequiresTokenComment',
+      {
+        defaultMessage: 'Use if APM Server requires a token'
+      }
+    )}
 export ELASTIC_APM_SECRET_TOKEN=${secretToken}
 `.split('\n'),
     textPost: i18n.translate('xpack.apm.tutorial.goClient.configure.textPost', {
-      defaultMessage: 'See the [documentation]({documentationLink}) for advanced configuration.',
+      defaultMessage:
+        'See the [documentation]({documentationLink}) for advanced configuration.',
       values: {
-        documentationLink: '{config.docs.base_url}guide/en/apm/agent/go/current/configuration.html',
-      },
-    }),
+        documentationLink:
+          '{config.docs.base_url}guide/en/apm/agent/go/current/configuration.html'
+      }
+    })
   },
   {
     title: i18n.translate('xpack.apm.tutorial.goClient.instrument.title', {
-      defaultMessage: 'Instrument your application',
+      defaultMessage: 'Instrument your application'
     }),
     textPre: i18n.translate('xpack.apm.tutorial.goClient.instrument.textPre', {
       defaultMessage:
         'Instrument your Go application by using one of the provided instrumentation modules or \
-by using the tracer API directly.',
+by using the tracer API directly.'
     }),
     commands: `\
 import (
@@ -531,93 +636,125 @@ func main() {curlyOpen}
 	http.ListenAndServe(":8080", apmhttp.Wrap(mux))
 {curlyClose}
 `.split('\n'),
-    textPost: i18n.translate('xpack.apm.tutorial.goClient.instrument.textPost', {
-      defaultMessage:
-        'See the [documentation]({documentationLink}) for a detailed \
+    textPost: i18n.translate(
+      'xpack.apm.tutorial.goClient.instrument.textPost',
+      {
+        defaultMessage:
+          'See the [documentation]({documentationLink}) for a detailed \
 guide to instrumenting Go source code.',
-      values: {
-        documentationLink:
-          '{config.docs.base_url}guide/en/apm/agent/go/current/instrumenting-source.html',
-      },
-    }),
-  },
+        values: {
+          documentationLink:
+            '{config.docs.base_url}guide/en/apm/agent/go/current/instrumenting-source.html'
+        }
+      }
+    )
+  }
 ];
 
-export const createJavaAgentInstructions = (apmServerUrl = '', secretToken = '') => [
+export const createJavaAgentInstructions = (
+  apmServerUrl = '',
+  secretToken = ''
+) => [
   {
     title: i18n.translate('xpack.apm.tutorial.javaClient.download.title', {
-      defaultMessage: 'Download the APM agent',
+      defaultMessage: 'Download the APM agent'
     }),
     textPre: i18n.translate('xpack.apm.tutorial.javaClient.download.textPre', {
       defaultMessage:
         'Download the agent jar from [Maven Central]({mavenCentralLink}). \
 Do **not** add the agent as a dependency to your application.',
       values: {
-        mavenCentralLink: 'http://search.maven.org/#search%7Cga%7C1%7Ca%3Aelastic-apm-agent',
-      },
-    }),
+        mavenCentralLink:
+          'http://search.maven.org/#search%7Cga%7C1%7Ca%3Aelastic-apm-agent'
+      }
+    })
   },
   {
-    title: i18n.translate('xpack.apm.tutorial.javaClient.startApplication.title', {
-      defaultMessage: 'Start your application with the javaagent flag',
-    }),
-    textPre: i18n.translate('xpack.apm.tutorial.javaClient.startApplication.textPre', {
-      defaultMessage:
-        'Add the `-javaagent` flag and configure the agent with system properties.\n\n \
+    title: i18n.translate(
+      'xpack.apm.tutorial.javaClient.startApplication.title',
+      {
+        defaultMessage: 'Start your application with the javaagent flag'
+      }
+    ),
+    textPre: i18n.translate(
+      'xpack.apm.tutorial.javaClient.startApplication.textPre',
+      {
+        defaultMessage:
+          'Add the `-javaagent` flag and configure the agent with system properties.\n\n \
 * Set required service name (allowed characters: a-z, A-Z, 0-9, -, _, and space)\n \
 * Set custom APM Server URL (default: {customApmServerUrl})\n \
 * Set the base package of your application',
-      values: { customApmServerUrl: 'http://localhost:8200' },
-    }),
+        values: { customApmServerUrl: 'http://localhost:8200' }
+      }
+    ),
     commands: `java -javaagent:/path/to/elastic-apm-agent-<version>.jar \\
      -Delastic.apm.service_name=my-application \\
      -Delastic.apm.server_url=${apmServerUrl || 'http://localhost:8200'} \\
      -Delastic.apm.secret_token=${secretToken} \\
      -Delastic.apm.application_packages=org.example \\
      -jar my-application.jar`.split('\n'),
-    textPost: i18n.translate('xpack.apm.tutorial.javaClient.startApplication.textPost', {
-      defaultMessage:
-        'See the [documentation]({documentationLink}) for configuration options and advanced \
+    textPost: i18n.translate(
+      'xpack.apm.tutorial.javaClient.startApplication.textPost',
+      {
+        defaultMessage:
+          'See the [documentation]({documentationLink}) for configuration options and advanced \
 usage.',
-      values: {
-        documentationLink: '{config.docs.base_url}guide/en/apm/agent/java/current/index.html',
-      },
-    }),
-  },
+        values: {
+          documentationLink:
+            '{config.docs.base_url}guide/en/apm/agent/java/current/index.html'
+        }
+      }
+    )
+  }
 ];
 
-export const createDotNetAgentInstructions = (apmServerUrl = '', secretToken = '') => [
+export const createDotNetAgentInstructions = (
+  apmServerUrl = '',
+  secretToken = ''
+) => [
   {
     title: i18n.translate('xpack.apm.tutorial.dotNetClient.download.title', {
-      defaultMessage: 'Download the APM agent',
+      defaultMessage: 'Download the APM agent'
     }),
-    textPre: i18n.translate('xpack.apm.tutorial.dotNetClient.download.textPre', {
-      defaultMessage:
-        'Add the the agent package(s) from [NuGet]({allNuGetPackagesLink}) to your .NET application. There are multiple \
+    textPre: i18n.translate(
+      'xpack.apm.tutorial.dotNetClient.download.textPre',
+      {
+        defaultMessage:
+          'Add the the agent package(s) from [NuGet]({allNuGetPackagesLink}) to your .NET application. There are multiple \
       NuGet packages available for different use cases. \n\nFor an ASP.NET Core application with Entity Framework \
       Core download the [Elastic.Apm.NetCoreAll]({netCoreAllApmPackageLink}) package. This package will automatically add every \
       agent component to your application. \n\n In case you would like to to minimize the dependencies, you can use the \
       [Elastic.Apm.AspNetCore]({aspNetCorePackageLink}) package for just \
       ASP.NET Core monitoring or the [Elastic.Apm.EfCore]({efCorePackageLink}) package for just Entity Framework Core monitoring. \n\n \
       In case you only want to use the public Agent API for manual instrumentation use the [Elastic.Apm]({elasticApmPackageLink}) package.',
-      values: {
-        allNuGetPackagesLink: 'https://www.nuget.org/packages?q=Elastic.apm',
-        netCoreAllApmPackageLink: 'https://www.nuget.org/packages/Elastic.Apm.NetCoreAll',
-        aspNetCorePackageLink: 'https://www.nuget.org/packages/Elastic.Apm.AspNetCore',
-        efCorePackageLink: 'https://www.nuget.org/packages/Elastic.Apm.EntityFrameworkCore',
-        elasticApmPackageLink: 'https://www.nuget.org/packages/Elastic.Apm',
-      },
-    }),
+        values: {
+          allNuGetPackagesLink: 'https://www.nuget.org/packages?q=Elastic.apm',
+          netCoreAllApmPackageLink:
+            'https://www.nuget.org/packages/Elastic.Apm.NetCoreAll',
+          aspNetCorePackageLink:
+            'https://www.nuget.org/packages/Elastic.Apm.AspNetCore',
+          efCorePackageLink:
+            'https://www.nuget.org/packages/Elastic.Apm.EntityFrameworkCore',
+          elasticApmPackageLink: 'https://www.nuget.org/packages/Elastic.Apm'
+        }
+      }
+    )
   },
   {
-    title: i18n.translate('xpack.apm.tutorial.dotNetClient.configureApplication.title', {
-      defaultMessage: 'Add the agent to the application',
-    }),
-    textPre: i18n.translate('xpack.apm.tutorial.dotNetClient.configureApplication.textPre', {
-      defaultMessage:
-        'In case of ASP.NET Core with the `Elastic.Apm.NetCoreAll` package, call the `UseAllElasticApm` \
-      method in the `Configure` method within the `Startup.cs` file.',
-    }),
+    title: i18n.translate(
+      'xpack.apm.tutorial.dotNetClient.configureApplication.title',
+      {
+        defaultMessage: 'Add the agent to the application'
+      }
+    ),
+    textPre: i18n.translate(
+      'xpack.apm.tutorial.dotNetClient.configureApplication.textPre',
+      {
+        defaultMessage:
+          'In case of ASP.NET Core with the `Elastic.Apm.NetCoreAll` package, call the `UseAllElasticApm` \
+      method in the `Configure` method within the `Startup.cs` file.'
+      }
+    ),
     commands: `public class Startup
 {curlyOpen}
   public void Configure(IApplicationBuilder app, IHostingEnvironment env)
@@ -627,16 +764,22 @@ export const createDotNetAgentInstructions = (apmServerUrl = '', secretToken = '
   {curlyClose}
   //…rest of the class
 {curlyClose}`.split('\n'),
-    textPost: i18n.translate('xpack.apm.tutorial.dotNetClient.configureApplication.textPost', {
-      defaultMessage:
-        'Passing an `IConfiguration` instance is optional and by doing so, the agent will read config settings through this \
-      `IConfiguration` instance (e.g. from the `appsettings.json` file).',
-    }),
+    textPost: i18n.translate(
+      'xpack.apm.tutorial.dotNetClient.configureApplication.textPost',
+      {
+        defaultMessage:
+          'Passing an `IConfiguration` instance is optional and by doing so, the agent will read config settings through this \
+      `IConfiguration` instance (e.g. from the `appsettings.json` file).'
+      }
+    )
   },
   {
-    title: i18n.translate('xpack.apm.tutorial.dotNetClient.configureAgent.title', {
-      defaultMessage: 'Sample appsettings.json file:',
-    }),
+    title: i18n.translate(
+      'xpack.apm.tutorial.dotNetClient.configureAgent.title',
+      {
+        defaultMessage: 'Sample appsettings.json file:'
+      }
+    ),
     commands: `{curlyOpen}
     "ElasticApm": {curlyOpen}
     "SecretToken": "${secretToken}",
@@ -645,15 +788,18 @@ export const createDotNetAgentInstructions = (apmServerUrl = '', secretToken = '
     "ServiceName" : "MyApp", //allowed characters: a-z, A-Z, 0-9, -, _, and space. Default is the entry assembly of the application
   {curlyClose}
 {curlyClose}`.split('\n'),
-    textPost: i18n.translate('xpack.apm.tutorial.dotNetClient.configureAgent.textPost', {
-      defaultMessage:
-        'In case you don’t pass an `IConfiguration` instance to the agent (e.g. in case of non ASP.NET Core applications) \
+    textPost: i18n.translate(
+      'xpack.apm.tutorial.dotNetClient.configureAgent.textPost',
+      {
+        defaultMessage:
+          'In case you don’t pass an `IConfiguration` instance to the agent (e.g. in case of non ASP.NET Core applications) \
       you can also configure the agent through environment variables. \n \
       See [the documentation]({documentationLink}) for advanced usage.',
-      values: {
-        documentationLink:
-          '{config.docs.base_url}guide/en/apm/agent/dotnet/current/configuration.html',
-      },
-    }),
-  },
+        values: {
+          documentationLink:
+            '{config.docs.base_url}guide/en/apm/agent/dotnet/current/configuration.html'
+        }
+      }
+    )
+  }
 ];
diff --git a/x-pack/plugins/apm/server/tutorial/instructions/apm_server_instructions.ts b/x-pack/plugins/apm/server/tutorial/instructions/apm_server_instructions.ts
index 991c37c9759a9..371c3928802ae 100644
--- a/x-pack/plugins/apm/server/tutorial/instructions/apm_server_instructions.ts
+++ b/x-pack/plugins/apm/server/tutorial/instructions/apm_server_instructions.ts
@@ -8,29 +8,29 @@ import { i18n } from '@kbn/i18n';
 
 export const createEditConfig = () => ({
   title: i18n.translate('xpack.apm.tutorial.editConfig.title', {
-    defaultMessage: 'Edit the configuration',
+    defaultMessage: 'Edit the configuration'
   }),
   textPre: i18n.translate('xpack.apm.tutorial.editConfig.textPre', {
     defaultMessage:
       "If you're using an X-Pack secured version of Elastic Stack, you must specify \
-credentials in the `apm-server.yml` config file.",
+credentials in the `apm-server.yml` config file."
   }),
   commands: [
     'output.elasticsearch:',
     '    hosts: ["<es_url>"]',
     '    username: <username>',
-    '    password: <password>',
-  ],
+    '    password: <password>'
+  ]
 });
 
 const createStartServer = () => ({
   title: i18n.translate('xpack.apm.tutorial.startServer.title', {
-    defaultMessage: 'Start APM Server',
+    defaultMessage: 'Start APM Server'
   }),
   textPre: i18n.translate('xpack.apm.tutorial.startServer.textPre', {
     defaultMessage:
-      'The server processes and stores application performance metrics in Elasticsearch.',
-  }),
+      'The server processes and stores application performance metrics in Elasticsearch.'
+  })
 });
 
 export function createStartServerUnixSysv() {
@@ -39,7 +39,7 @@ export function createStartServerUnixSysv() {
   return {
     title: START_SERVER.title,
     textPre: START_SERVER.textPre,
-    commands: ['service apm-server start'],
+    commands: ['service apm-server start']
   };
 }
 
@@ -49,13 +49,13 @@ export function createStartServerUnix() {
   return {
     title: START_SERVER.title,
     textPre: START_SERVER.textPre,
-    commands: ['./apm-server -e'],
+    commands: ['./apm-server -e']
   };
 }
 
 const createDownloadServerTitle = () =>
   i18n.translate('xpack.apm.tutorial.downloadServer.title', {
-    defaultMessage: 'Download and unpack APM Server',
+    defaultMessage: 'Download and unpack APM Server'
   });
 
 export const createDownloadServerOsx = () => ({
@@ -63,32 +63,38 @@ export const createDownloadServerOsx = () => ({
   commands: [
     'curl -L -O https://artifacts.elastic.co/downloads/apm-server/apm-server-{config.kibana.version}-darwin-x86_64.tar.gz',
     'tar xzvf apm-server-{config.kibana.version}-darwin-x86_64.tar.gz',
-    'cd apm-server-{config.kibana.version}-darwin-x86_64/',
-  ],
+    'cd apm-server-{config.kibana.version}-darwin-x86_64/'
+  ]
 });
 
 export const createDownloadServerDeb = () => ({
   title: createDownloadServerTitle(),
   commands: [
     'curl -L -O https://artifacts.elastic.co/downloads/apm-server/apm-server-{config.kibana.version}-amd64.deb',
-    'sudo dpkg -i apm-server-{config.kibana.version}-amd64.deb',
+    'sudo dpkg -i apm-server-{config.kibana.version}-amd64.deb'
   ],
   textPost: i18n.translate('xpack.apm.tutorial.downloadServerTitle', {
-    defaultMessage: 'Looking for the 32-bit packages? See the [Download page]({downloadPageLink}).',
-    values: { downloadPageLink: '{config.docs.base_url}downloads/apm/apm-server' },
-  }),
+    defaultMessage:
+      'Looking for the 32-bit packages? See the [Download page]({downloadPageLink}).',
+    values: {
+      downloadPageLink: '{config.docs.base_url}downloads/apm/apm-server'
+    }
+  })
 });
 
 export const createDownloadServerRpm = () => ({
   title: createDownloadServerTitle(),
   commands: [
     'curl -L -O https://artifacts.elastic.co/downloads/apm-server/apm-server-{config.kibana.version}-x86_64.rpm',
-    'sudo rpm -vi apm-server-{config.kibana.version}-x86_64.rpm',
+    'sudo rpm -vi apm-server-{config.kibana.version}-x86_64.rpm'
   ],
   textPost: i18n.translate('xpack.apm.tutorial.downloadServerRpm', {
-    defaultMessage: 'Looking for the 32-bit packages? See the [Download page]({downloadPageLink}).',
-    values: { downloadPageLink: '{config.docs.base_url}downloads/apm/apm-server' },
-  }),
+    defaultMessage:
+      'Looking for the 32-bit packages? See the [Download page]({downloadPageLink}).',
+    values: {
+      downloadPageLink: '{config.docs.base_url}downloads/apm/apm-server'
+    }
+  })
 });
 
 export function createWindowsServerInstructions() {
@@ -97,38 +103,47 @@ export function createWindowsServerInstructions() {
   return [
     {
       title: createDownloadServerTitle(),
-      textPre: i18n.translate('xpack.apm.tutorial.windowsServerInstructions.textPre', {
-        defaultMessage:
-          '1. Download the APM Server Windows zip file from the \
+      textPre: i18n.translate(
+        'xpack.apm.tutorial.windowsServerInstructions.textPre',
+        {
+          defaultMessage:
+            '1. Download the APM Server Windows zip file from the \
 [Download page]({downloadPageLink}).\n2. Extract the contents of \
 the zip file into {zipFileExtractFolder}.\n3. Rename the {apmServerDirectory} \
 directory to `APM-Server`.\n4. Open a PowerShell prompt as an Administrator \
 (right-click the PowerShell icon and select \
 **Run As Administrator**). If you are running Windows XP, you might need to download and install \
 PowerShell.\n5. From the PowerShell prompt, run the following commands to install APM Server as a Windows service:',
-        values: {
-          downloadPageLink: 'https://www.elastic.co/downloads/apm/apm-server',
-          zipFileExtractFolder: '`C:\\Program Files`',
-          apmServerDirectory: '`apm-server-{config.kibana.version}-windows`',
-        },
-      }),
-      commands: [`cd 'C:\\Program Files\\APM-Server'`, `.\\install-service-apm-server.ps1`],
-      textPost: i18n.translate('xpack.apm.tutorial.windowsServerInstructions.textPost', {
-        defaultMessage:
-          'Note: If script execution is disabled on your system, \
+          values: {
+            downloadPageLink: 'https://www.elastic.co/downloads/apm/apm-server',
+            zipFileExtractFolder: '`C:\\Program Files`',
+            apmServerDirectory: '`apm-server-{config.kibana.version}-windows`'
+          }
+        }
+      ),
+      commands: [
+        `cd 'C:\\Program Files\\APM-Server'`,
+        `.\\install-service-apm-server.ps1`
+      ],
+      textPost: i18n.translate(
+        'xpack.apm.tutorial.windowsServerInstructions.textPost',
+        {
+          defaultMessage:
+            'Note: If script execution is disabled on your system, \
 you need to set the execution policy for the current session \
 to allow the script to run. For example: {command}.',
-        values: {
-          command:
-            '`PowerShell.exe -ExecutionPolicy UnRestricted -File .\\install-service-apm-server.ps1`',
-        },
-      }),
+          values: {
+            command:
+              '`PowerShell.exe -ExecutionPolicy UnRestricted -File .\\install-service-apm-server.ps1`'
+          }
+        }
+      )
     },
     createEditConfig(),
     {
       title: START_SERVER.title,
       textPre: START_SERVER.textPre,
-      commands: ['Start-Service apm-server'],
-    },
+      commands: ['Start-Service apm-server']
+    }
   ];
 }
diff --git a/x-pack/plugins/apm/tsconfig.json b/x-pack/plugins/apm/tsconfig.json
new file mode 100644
index 0000000000000..618c6c3e97b57
--- /dev/null
+++ b/x-pack/plugins/apm/tsconfig.json
@@ -0,0 +1,3 @@
+{
+  "extends": "../../../tsconfig.json"
+}
diff --git a/x-pack/legacy/plugins/apm/typings/apm-rum-react.d.ts b/x-pack/plugins/apm/typings/apm_rum_react.d.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/typings/apm-rum-react.d.ts
rename to x-pack/plugins/apm/typings/apm_rum_react.d.ts
diff --git a/x-pack/legacy/plugins/apm/typings/common.d.ts b/x-pack/plugins/apm/typings/common.d.ts
similarity index 78%
rename from x-pack/legacy/plugins/apm/typings/common.d.ts
rename to x-pack/plugins/apm/typings/common.d.ts
index 1e718f818246c..bdd2c75f161e9 100644
--- a/x-pack/legacy/plugins/apm/typings/common.d.ts
+++ b/x-pack/plugins/apm/typings/common.d.ts
@@ -4,12 +4,12 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import '../../infra/types/rison_node';
-import '../../infra/types/eui';
+import '../../../legacy/plugins/infra/types/rison_node';
+import '../../../legacy/plugins/infra/types/eui';
 // EUIBasicTable
-import {} from '../../reporting/public/components/report_listing';
+import '../../../legacy/plugins/reporting/public/components/report_listing';
 // .svg
-import '../../canvas/types/webpack';
+import '../../../legacy/plugins/canvas/types/webpack';
 
 // Allow unknown properties in an object
 export type AllowUnknownProperties<T> = T extends Array<infer X>
diff --git a/x-pack/legacy/plugins/apm/typings/cytoscape-dagre.d.ts b/x-pack/plugins/apm/typings/cytoscape_dagre.d.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/typings/cytoscape-dagre.d.ts
rename to x-pack/plugins/apm/typings/cytoscape_dagre.d.ts
diff --git a/x-pack/plugins/apm/typings/elasticsearch/aggregations.ts b/x-pack/plugins/apm/typings/elasticsearch/aggregations.ts
index 2ee8df7b5d1e1..6d3620f11a87b 100644
--- a/x-pack/plugins/apm/typings/elasticsearch/aggregations.ts
+++ b/x-pack/plugins/apm/typings/elasticsearch/aggregations.ts
@@ -147,13 +147,19 @@ type BucketSubAggregationResponse<
   ? AggregationResponseMap<TAggregationInputMap, TDocument>
   : {};
 
-interface AggregationResponsePart<TAggregationOptionsMap extends AggregationOptionsMap, TDocument> {
+interface AggregationResponsePart<
+  TAggregationOptionsMap extends AggregationOptionsMap,
+  TDocument
+> {
   terms: {
     buckets: Array<
       {
         doc_count: number;
         key: string | number;
-      } & BucketSubAggregationResponse<TAggregationOptionsMap['aggs'], TDocument>
+      } & BucketSubAggregationResponse<
+        TAggregationOptionsMap['aggs'],
+        TDocument
+      >
     >;
   };
   histogram: {
@@ -161,7 +167,10 @@ interface AggregationResponsePart<TAggregationOptionsMap extends AggregationOpti
       {
         doc_count: number;
         key: number;
-      } & BucketSubAggregationResponse<TAggregationOptionsMap['aggs'], TDocument>
+      } & BucketSubAggregationResponse<
+        TAggregationOptionsMap['aggs'],
+        TDocument
+      >
     >;
   };
   date_histogram: {
@@ -170,7 +179,10 @@ interface AggregationResponsePart<TAggregationOptionsMap extends AggregationOpti
         doc_count: number;
         key: number;
         key_as_string: string;
-      } & BucketSubAggregationResponse<TAggregationOptionsMap['aggs'], TDocument>
+      } & BucketSubAggregationResponse<
+        TAggregationOptionsMap['aggs'],
+        TDocument
+      >
     >;
   };
   avg: MetricsAggregationResponsePart;
@@ -215,7 +227,10 @@ interface AggregationResponsePart<TAggregationOptionsMap extends AggregationOpti
   } & AggregationResponseMap<TAggregationOptionsMap['aggs'], TDocument>;
   filters: TAggregationOptionsMap extends { filters: { filters: any[] } }
     ? Array<
-        { doc_count: number } & AggregationResponseMap<TAggregationOptionsMap['aggs'], TDocument>
+        { doc_count: number } & AggregationResponseMap<
+          TAggregationOptionsMap['aggs'],
+          TDocument
+        >
       >
     : TAggregationOptionsMap extends {
         filters: {
@@ -249,7 +264,10 @@ interface AggregationResponsePart<TAggregationOptionsMap extends AggregationOpti
       {
         key: Record<GetCompositeKeys<TAggregationOptionsMap>, number>;
         doc_count: number;
-      } & BucketSubAggregationResponse<TAggregationOptionsMap['aggs'], TDocument>
+      } & BucketSubAggregationResponse<
+        TAggregationOptionsMap['aggs'],
+        TDocument
+      >
     >;
   };
   diversified_sampler: {
diff --git a/x-pack/plugins/apm/typings/elasticsearch/index.ts b/x-pack/plugins/apm/typings/elasticsearch/index.ts
index e1d4aaeb597e2..064b684cf9aa6 100644
--- a/x-pack/plugins/apm/typings/elasticsearch/index.ts
+++ b/x-pack/plugins/apm/typings/elasticsearch/index.ts
@@ -31,7 +31,10 @@ export type ESSearchResponse<
 > = Omit<SearchResponse<TDocument>, 'aggregations' | 'hits'> &
   (TSearchRequest extends { body: { aggs: AggregationInputMap } }
     ? {
-        aggregations?: AggregationResponseMap<TSearchRequest['body']['aggs'], TDocument>;
+        aggregations?: AggregationResponseMap<
+          TSearchRequest['body']['aggs'],
+          TDocument
+        >;
       }
     : {}) & {
     hits: Omit<SearchResponse<TDocument>['hits'], 'total'> &
@@ -49,6 +52,12 @@ export type ESSearchResponse<
 
 export interface ESFilter {
   [key: string]: {
-    [key: string]: string | string[] | number | boolean | Record<string, unknown> | ESFilter[];
+    [key: string]:
+      | string
+      | string[]
+      | number
+      | boolean
+      | Record<string, unknown>
+      | ESFilter[];
   };
 }
diff --git a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/APMBaseDoc.ts b/x-pack/plugins/apm/typings/es_schemas/raw/apm_base_doc.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/typings/es_schemas/raw/APMBaseDoc.ts
rename to x-pack/plugins/apm/typings/es_schemas/raw/apm_base_doc.ts
diff --git a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/ErrorRaw.ts b/x-pack/plugins/apm/typings/es_schemas/raw/error_raw.ts
similarity index 71%
rename from x-pack/legacy/plugins/apm/typings/es_schemas/raw/ErrorRaw.ts
rename to x-pack/plugins/apm/typings/es_schemas/raw/error_raw.ts
index 2f6e857e82470..daf65e44980b6 100644
--- a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/ErrorRaw.ts
+++ b/x-pack/plugins/apm/typings/es_schemas/raw/error_raw.ts
@@ -4,17 +4,17 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { APMBaseDoc } from './APMBaseDoc';
-import { Container } from './fields/Container';
-import { Host } from './fields/Host';
-import { Http } from './fields/Http';
-import { Kubernetes } from './fields/Kubernetes';
-import { Page } from './fields/Page';
-import { Process } from './fields/Process';
-import { Service } from './fields/Service';
-import { IStackframe } from './fields/Stackframe';
-import { Url } from './fields/Url';
-import { User } from './fields/User';
+import { APMBaseDoc } from './apm_base_doc';
+import { Container } from './fields/container';
+import { Host } from './fields/host';
+import { Http } from './fields/http';
+import { Kubernetes } from './fields/kubernetes';
+import { Page } from './fields/page';
+import { Process } from './fields/process';
+import { Service } from './fields/service';
+import { IStackframe } from './fields/stackframe';
+import { Url } from './fields/url';
+import { User } from './fields/user';
 
 interface Processor {
   name: 'error';
diff --git a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/Container.ts b/x-pack/plugins/apm/typings/es_schemas/raw/fields/container.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/Container.ts
rename to x-pack/plugins/apm/typings/es_schemas/raw/fields/container.ts
diff --git a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/Host.ts b/x-pack/plugins/apm/typings/es_schemas/raw/fields/host.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/Host.ts
rename to x-pack/plugins/apm/typings/es_schemas/raw/fields/host.ts
diff --git a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/Http.ts b/x-pack/plugins/apm/typings/es_schemas/raw/fields/http.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/Http.ts
rename to x-pack/plugins/apm/typings/es_schemas/raw/fields/http.ts
diff --git a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/Kubernetes.ts b/x-pack/plugins/apm/typings/es_schemas/raw/fields/kubernetes.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/Kubernetes.ts
rename to x-pack/plugins/apm/typings/es_schemas/raw/fields/kubernetes.ts
diff --git a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/Page.ts b/x-pack/plugins/apm/typings/es_schemas/raw/fields/page.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/Page.ts
rename to x-pack/plugins/apm/typings/es_schemas/raw/fields/page.ts
diff --git a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/Process.ts b/x-pack/plugins/apm/typings/es_schemas/raw/fields/process.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/Process.ts
rename to x-pack/plugins/apm/typings/es_schemas/raw/fields/process.ts
diff --git a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/Service.ts b/x-pack/plugins/apm/typings/es_schemas/raw/fields/service.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/Service.ts
rename to x-pack/plugins/apm/typings/es_schemas/raw/fields/service.ts
diff --git a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/Stackframe.ts b/x-pack/plugins/apm/typings/es_schemas/raw/fields/stackframe.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/Stackframe.ts
rename to x-pack/plugins/apm/typings/es_schemas/raw/fields/stackframe.ts
diff --git a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/Url.ts b/x-pack/plugins/apm/typings/es_schemas/raw/fields/url.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/Url.ts
rename to x-pack/plugins/apm/typings/es_schemas/raw/fields/url.ts
diff --git a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/User.ts b/x-pack/plugins/apm/typings/es_schemas/raw/fields/user.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/User.ts
rename to x-pack/plugins/apm/typings/es_schemas/raw/fields/user.ts
diff --git a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/UserAgent.ts b/x-pack/plugins/apm/typings/es_schemas/raw/fields/user_agent.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/typings/es_schemas/raw/fields/UserAgent.ts
rename to x-pack/plugins/apm/typings/es_schemas/raw/fields/user_agent.ts
diff --git a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/SpanRaw.ts b/x-pack/plugins/apm/typings/es_schemas/raw/span_raw.ts
similarity index 91%
rename from x-pack/legacy/plugins/apm/typings/es_schemas/raw/SpanRaw.ts
rename to x-pack/plugins/apm/typings/es_schemas/raw/span_raw.ts
index 60e523f1aa043..dbd9e7ede4256 100644
--- a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/SpanRaw.ts
+++ b/x-pack/plugins/apm/typings/es_schemas/raw/span_raw.ts
@@ -4,8 +4,8 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { APMBaseDoc } from './APMBaseDoc';
-import { IStackframe } from './fields/Stackframe';
+import { APMBaseDoc } from './apm_base_doc';
+import { IStackframe } from './fields/stackframe';
 
 interface Processor {
   name: 'transaction';
diff --git a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/TransactionRaw.ts b/x-pack/plugins/apm/typings/es_schemas/raw/transaction_raw.ts
similarity index 73%
rename from x-pack/legacy/plugins/apm/typings/es_schemas/raw/TransactionRaw.ts
rename to x-pack/plugins/apm/typings/es_schemas/raw/transaction_raw.ts
index 4dc5f8c897c26..3673f1f13c403 100644
--- a/x-pack/legacy/plugins/apm/typings/es_schemas/raw/TransactionRaw.ts
+++ b/x-pack/plugins/apm/typings/es_schemas/raw/transaction_raw.ts
@@ -4,17 +4,17 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { APMBaseDoc } from './APMBaseDoc';
-import { Container } from './fields/Container';
-import { Host } from './fields/Host';
-import { Http } from './fields/Http';
-import { Kubernetes } from './fields/Kubernetes';
-import { Page } from './fields/Page';
-import { Process } from './fields/Process';
-import { Service } from './fields/Service';
-import { Url } from './fields/Url';
-import { User } from './fields/User';
-import { UserAgent } from './fields/UserAgent';
+import { APMBaseDoc } from './apm_base_doc';
+import { Container } from './fields/container';
+import { Host } from './fields/host';
+import { Http } from './fields/http';
+import { Kubernetes } from './fields/kubernetes';
+import { Page } from './fields/page';
+import { Process } from './fields/process';
+import { Service } from './fields/service';
+import { Url } from './fields/url';
+import { User } from './fields/user';
+import { UserAgent } from './fields/user_agent';
 
 interface Processor {
   name: 'transaction';
diff --git a/x-pack/legacy/plugins/apm/typings/es_schemas/ui/APMError.ts b/x-pack/plugins/apm/typings/es_schemas/ui/apm_error.ts
similarity index 78%
rename from x-pack/legacy/plugins/apm/typings/es_schemas/ui/APMError.ts
rename to x-pack/plugins/apm/typings/es_schemas/ui/apm_error.ts
index ae4ce31ee57cf..dd7aeb003711d 100644
--- a/x-pack/legacy/plugins/apm/typings/es_schemas/ui/APMError.ts
+++ b/x-pack/plugins/apm/typings/es_schemas/ui/apm_error.ts
@@ -4,8 +4,8 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { ErrorRaw } from '../raw/ErrorRaw';
-import { Agent } from './fields/Agent';
+import { ErrorRaw } from '../raw/error_raw';
+import { Agent } from './fields/agent';
 
 export interface APMError extends ErrorRaw {
   agent: Agent;
diff --git a/x-pack/legacy/plugins/apm/typings/es_schemas/ui/fields/Agent.ts b/x-pack/plugins/apm/typings/es_schemas/ui/fields/agent.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/typings/es_schemas/ui/fields/Agent.ts
rename to x-pack/plugins/apm/typings/es_schemas/ui/fields/agent.ts
diff --git a/x-pack/legacy/plugins/apm/typings/es_schemas/ui/Span.ts b/x-pack/plugins/apm/typings/es_schemas/ui/span.ts
similarity index 78%
rename from x-pack/legacy/plugins/apm/typings/es_schemas/ui/Span.ts
rename to x-pack/plugins/apm/typings/es_schemas/ui/span.ts
index 07149d9ac0412..b282acccac2de 100644
--- a/x-pack/legacy/plugins/apm/typings/es_schemas/ui/Span.ts
+++ b/x-pack/plugins/apm/typings/es_schemas/ui/span.ts
@@ -4,8 +4,8 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { SpanRaw } from '../raw/SpanRaw';
-import { Agent } from './fields/Agent';
+import { SpanRaw } from '../raw/span_raw';
+import { Agent } from './fields/agent';
 
 export interface Span extends SpanRaw {
   agent: Agent;
diff --git a/x-pack/legacy/plugins/apm/typings/es_schemas/ui/Transaction.ts b/x-pack/plugins/apm/typings/es_schemas/ui/transaction.ts
similarity index 88%
rename from x-pack/legacy/plugins/apm/typings/es_schemas/ui/Transaction.ts
rename to x-pack/plugins/apm/typings/es_schemas/ui/transaction.ts
index a5ce7c9f0dff3..d6d1e7695d2ab 100644
--- a/x-pack/legacy/plugins/apm/typings/es_schemas/ui/Transaction.ts
+++ b/x-pack/plugins/apm/typings/es_schemas/ui/transaction.ts
@@ -4,8 +4,8 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { TransactionRaw } from '../raw/TransactionRaw';
-import { Agent } from './fields/Agent';
+import { TransactionRaw } from '../raw/transaction_raw';
+import { Agent } from './fields/agent';
 
 // Make `transaction.name` required instead of optional.
 // `transaction.name` can be missing in Elasticsearch but the UI will only aggregate on transactions with a name,
diff --git a/x-pack/legacy/plugins/apm/typings/lodash.mean.d.ts b/x-pack/plugins/apm/typings/lodash.mean.d.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/typings/lodash.mean.d.ts
rename to x-pack/plugins/apm/typings/lodash.mean.d.ts
diff --git a/x-pack/legacy/plugins/apm/typings/numeral.d.ts b/x-pack/plugins/apm/typings/numeral.d.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/typings/numeral.d.ts
rename to x-pack/plugins/apm/typings/numeral.d.ts
diff --git a/x-pack/legacy/plugins/apm/typings/react-vis.d.ts b/x-pack/plugins/apm/typings/react_vis.d.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/typings/react-vis.d.ts
rename to x-pack/plugins/apm/typings/react_vis.d.ts
diff --git a/x-pack/legacy/plugins/apm/typings/timeseries.ts b/x-pack/plugins/apm/typings/timeseries.ts
similarity index 100%
rename from x-pack/legacy/plugins/apm/typings/timeseries.ts
rename to x-pack/plugins/apm/typings/timeseries.ts
diff --git a/x-pack/legacy/plugins/apm/typings/ui-filters.ts b/x-pack/plugins/apm/typings/ui_filters.ts
similarity index 88%
rename from x-pack/legacy/plugins/apm/typings/ui-filters.ts
rename to x-pack/plugins/apm/typings/ui_filters.ts
index 0c62ef3b1c962..3f03e80325b49 100644
--- a/x-pack/legacy/plugins/apm/typings/ui-filters.ts
+++ b/x-pack/plugins/apm/typings/ui_filters.ts
@@ -4,6 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { LocalUIFilterName } from '../server/lib/ui_filters/local_ui_filters/config';
 
 export type UIFilters = {
diff --git a/x-pack/plugins/infra/server/routes/metadata/lib/has_apm_data.ts b/x-pack/plugins/infra/server/routes/metadata/lib/has_apm_data.ts
index 9ca0819d74d46..1f8029db80d86 100644
--- a/x-pack/plugins/infra/server/routes/metadata/lib/has_apm_data.ts
+++ b/x-pack/plugins/infra/server/routes/metadata/lib/has_apm_data.ts
@@ -18,9 +18,7 @@ export const hasAPMData = async (
   nodeId: string,
   nodeType: InventoryItemType
 ) => {
-  const apmIndices = await framework.plugins.apm.getApmIndices(
-    requestContext.core.savedObjects.client
-  );
+  const apmIndices = await framework.plugins.apm.getApmIndices();
   const apmIndex = apmIndices['apm_oss.transactionIndices'] || 'apm-*';
   const fields = findInventoryFields(nodeType, sourceConfiguration.fields);
 

From a36467e66e80238853978fb9ee804206ff5a636d Mon Sep 17 00:00:00 2001
From: John Dorlus <jsdorlus@elastic.co>
Date: Wed, 19 Feb 2020 17:27:50 -0500
Subject: [PATCH 081/174] Moved all of the show/hide toggles outside of ordered
 lists. (#57163)

* Moved all of the show/hide toggles outside of ordered lists.

* Fixed styling issues for indice list.

* Fixed i10n tag that I accidentally changed.

* Added component to show/hide indices.

* Abstracted out some of the common parts of the Show Hide component and implemented the general component in the pages. Also made conditional for the i18n tags.

* Fixed changes per comments. Restored <EuiText> to fix the issue with the bullet points. Updated the i18n tags to be more generic. Created 2 components for formatted message.

* Fixed internalization issues..

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../components/collapsible_indices_list.tsx   | 81 ++++++++++++++++++
 .../public/app/components/index.ts            |  1 +
 .../policy_form/steps/step_review.tsx         | 60 +-------------
 .../steps/step_review.tsx                     | 59 +------------
 .../policy_details/tabs/tab_summary.tsx       | 82 +------------------
 .../snapshot_details/tabs/tab_summary.tsx     | 80 ++----------------
 .../translations/translations/ja-JP.json      | 12 ---
 .../translations/translations/zh-CN.json      | 12 ---
 8 files changed, 98 insertions(+), 289 deletions(-)
 create mode 100644 x-pack/legacy/plugins/snapshot_restore/public/app/components/collapsible_indices_list.tsx

diff --git a/x-pack/legacy/plugins/snapshot_restore/public/app/components/collapsible_indices_list.tsx b/x-pack/legacy/plugins/snapshot_restore/public/app/components/collapsible_indices_list.tsx
new file mode 100644
index 0000000000000..96224ec1283e2
--- /dev/null
+++ b/x-pack/legacy/plugins/snapshot_restore/public/app/components/collapsible_indices_list.tsx
@@ -0,0 +1,81 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { useState } from 'react';
+import { EuiTitle, EuiLink, EuiIcon, EuiText, EuiSpacer } from '@elastic/eui';
+interface Props {
+  indices: string[] | string | undefined;
+}
+
+import { useAppDependencies } from '../index';
+
+export const CollapsibleIndicesList: React.FunctionComponent<Props> = ({ indices }) => {
+  const {
+    core: { i18n },
+  } = useAppDependencies();
+  const { FormattedMessage } = i18n;
+  const [isShowingFullIndicesList, setIsShowingFullIndicesList] = useState<boolean>(false);
+  const displayIndices = indices
+    ? typeof indices === 'string'
+      ? indices.split(',')
+      : indices
+    : undefined;
+  const hiddenIndicesCount =
+    displayIndices && displayIndices.length > 10 ? displayIndices.length - 10 : 0;
+  return (
+    <>
+      {displayIndices ? (
+        <>
+          <EuiText>
+            <ul>
+              {(isShowingFullIndicesList ? displayIndices : [...displayIndices].splice(0, 10)).map(
+                index => (
+                  <li key={index}>
+                    <EuiTitle size="xs">
+                      <span>{index}</span>
+                    </EuiTitle>
+                  </li>
+                )
+              )}
+            </ul>
+          </EuiText>
+          {hiddenIndicesCount ? (
+            <>
+              <EuiSpacer size="xs" />
+              <EuiLink
+                onClick={() =>
+                  isShowingFullIndicesList
+                    ? setIsShowingFullIndicesList(false)
+                    : setIsShowingFullIndicesList(true)
+                }
+              >
+                {isShowingFullIndicesList ? (
+                  <FormattedMessage
+                    id="xpack.snapshotRestore.indicesList.indicesCollapseAllLink"
+                    defaultMessage="Hide {count, plural, one {# index} other {# indices}}"
+                    values={{ count: hiddenIndicesCount }}
+                  />
+                ) : (
+                  <FormattedMessage
+                    id="xpack.snapshotRestore.indicesList.indicesExpandAllLink"
+                    defaultMessage="Show {count, plural, one {# index} other {# indices}}"
+                    values={{ count: hiddenIndicesCount }}
+                  />
+                )}{' '}
+                <EuiIcon type={isShowingFullIndicesList ? 'arrowUp' : 'arrowDown'} />
+              </EuiLink>
+            </>
+          ) : null}
+        </>
+      ) : (
+        <FormattedMessage
+          id="xpack.snapshotRestore.indicesList.allIndicesValue"
+          defaultMessage="All indices"
+        />
+      )}
+    </>
+  );
+};
diff --git a/x-pack/legacy/plugins/snapshot_restore/public/app/components/index.ts b/x-pack/legacy/plugins/snapshot_restore/public/app/components/index.ts
index 32b45c05d5cb3..a7038ebd71578 100644
--- a/x-pack/legacy/plugins/snapshot_restore/public/app/components/index.ts
+++ b/x-pack/legacy/plugins/snapshot_restore/public/app/components/index.ts
@@ -16,6 +16,7 @@ export { SnapshotDeleteProvider } from './snapshot_delete_provider';
 export { RestoreSnapshotForm } from './restore_snapshot_form';
 export { PolicyExecuteProvider } from './policy_execute_provider';
 export { PolicyDeleteProvider } from './policy_delete_provider';
+export { CollapsibleIndicesList } from './collapsible_indices_list';
 export {
   RetentionSettingsUpdateModalProvider,
   UpdateRetentionSettings,
diff --git a/x-pack/legacy/plugins/snapshot_restore/public/app/components/policy_form/steps/step_review.tsx b/x-pack/legacy/plugins/snapshot_restore/public/app/components/policy_form/steps/step_review.tsx
index 3ddbcd94009ac..a7f7748b7d72f 100644
--- a/x-pack/legacy/plugins/snapshot_restore/public/app/components/policy_form/steps/step_review.tsx
+++ b/x-pack/legacy/plugins/snapshot_restore/public/app/components/policy_form/steps/step_review.tsx
@@ -3,7 +3,7 @@
  * or more contributor license agreements. Licensed under the Elastic License;
  * you may not use this file except in compliance with the Elastic License.
  */
-import React, { Fragment, useState } from 'react';
+import React, { Fragment } from 'react';
 import {
   EuiCodeBlock,
   EuiFlexGroup,
@@ -13,7 +13,6 @@ import {
   EuiDescriptionListDescription,
   EuiSpacer,
   EuiTabbedContent,
-  EuiText,
   EuiTitle,
   EuiLink,
   EuiIcon,
@@ -22,6 +21,7 @@ import {
 import { serializePolicy } from '../../../../../common/lib';
 import { useAppDependencies } from '../../../index';
 import { StepProps } from './';
+import { CollapsibleIndicesList } from '../../collapsible_indices_list';
 
 export const PolicyStepReview: React.FunctionComponent<StepProps> = ({
   policy,
@@ -39,15 +39,6 @@ export const PolicyStepReview: React.FunctionComponent<StepProps> = ({
     partial: undefined,
   };
 
-  const [isShowingFullIndicesList, setIsShowingFullIndicesList] = useState<boolean>(false);
-  const displayIndices = indices
-    ? typeof indices === 'string'
-      ? indices.split(',')
-      : indices
-    : undefined;
-  const hiddenIndicesCount =
-    displayIndices && displayIndices.length > 10 ? displayIndices.length - 10 : 0;
-
   const serializedPolicy = serializePolicy(policy);
   const { retention: serializedRetention } = serializedPolicy;
 
@@ -164,52 +155,7 @@ export const PolicyStepReview: React.FunctionComponent<StepProps> = ({
               />
             </EuiDescriptionListTitle>
             <EuiDescriptionListDescription>
-              {displayIndices ? (
-                <EuiText>
-                  <ul>
-                    {(isShowingFullIndicesList
-                      ? displayIndices
-                      : [...displayIndices].splice(0, 10)
-                    ).map(index => (
-                      <li key={index}>
-                        <EuiTitle size="xs">
-                          <span>{index}</span>
-                        </EuiTitle>
-                      </li>
-                    ))}
-                    {hiddenIndicesCount ? (
-                      <li key="hiddenIndicesCount">
-                        <EuiTitle size="xs">
-                          {isShowingFullIndicesList ? (
-                            <EuiLink onClick={() => setIsShowingFullIndicesList(false)}>
-                              <FormattedMessage
-                                id="xpack.snapshotRestore.policyForm.stepReview.summaryTab.indicesCollapseAllLink"
-                                defaultMessage="Hide {count, plural, one {# index} other {# indices}}"
-                                values={{ count: hiddenIndicesCount }}
-                              />{' '}
-                              <EuiIcon type="arrowUp" />
-                            </EuiLink>
-                          ) : (
-                            <EuiLink onClick={() => setIsShowingFullIndicesList(true)}>
-                              <FormattedMessage
-                                id="xpack.snapshotRestore.policyForm.stepReview.summaryTab.indicesShowAllLink"
-                                defaultMessage="Show {count} more {count, plural, one {index} other {indices}}"
-                                values={{ count: hiddenIndicesCount }}
-                              />{' '}
-                              <EuiIcon type="arrowDown" />
-                            </EuiLink>
-                          )}
-                        </EuiTitle>
-                      </li>
-                    ) : null}
-                  </ul>
-                </EuiText>
-              ) : (
-                <FormattedMessage
-                  id="xpack.snapshotRestore.policyForm.stepReview.summaryTab.allIndicesValue"
-                  defaultMessage="All indices"
-                />
-              )}
+              <CollapsibleIndicesList indices={indices} />
             </EuiDescriptionListDescription>
           </EuiDescriptionList>
         </EuiFlexItem>
diff --git a/x-pack/legacy/plugins/snapshot_restore/public/app/components/restore_snapshot_form/steps/step_review.tsx b/x-pack/legacy/plugins/snapshot_restore/public/app/components/restore_snapshot_form/steps/step_review.tsx
index 92b0ee48fef01..0d2c2398c6012 100644
--- a/x-pack/legacy/plugins/snapshot_restore/public/app/components/restore_snapshot_form/steps/step_review.tsx
+++ b/x-pack/legacy/plugins/snapshot_restore/public/app/components/restore_snapshot_form/steps/step_review.tsx
@@ -3,7 +3,7 @@
  * or more contributor license agreements. Licensed under the Elastic License;
  * you may not use this file except in compliance with the Elastic License.
  */
-import React, { useState, Fragment } from 'react';
+import React, { Fragment } from 'react';
 import {
   EuiCodeEditor,
   EuiFlexGrid,
@@ -23,6 +23,7 @@ import {
 import { serializeRestoreSettings } from '../../../../../common/lib';
 import { useAppDependencies } from '../../../index';
 import { StepProps } from './';
+import { CollapsibleIndicesList } from '../../collapsible_indices_list';
 
 export const RestoreSnapshotStepReview: React.FunctionComponent<StepProps> = ({
   restoreSettings,
@@ -44,15 +45,6 @@ export const RestoreSnapshotStepReview: React.FunctionComponent<StepProps> = ({
   const serializedRestoreSettings = serializeRestoreSettings(restoreSettings);
   const { index_settings: serializedIndexSettings } = serializedRestoreSettings;
 
-  const [isShowingFullIndicesList, setIsShowingFullIndicesList] = useState<boolean>(false);
-  const displayIndices = restoreIndices
-    ? typeof restoreIndices === 'string'
-      ? restoreIndices.split(',')
-      : restoreIndices
-    : undefined;
-  const hiddenIndicesCount =
-    displayIndices && displayIndices.length > 10 ? displayIndices.length - 10 : 0;
-
   const renderSummaryTab = () => (
     <Fragment>
       <EuiSpacer size="m" />
@@ -88,52 +80,7 @@ export const RestoreSnapshotStepReview: React.FunctionComponent<StepProps> = ({
               />
             </EuiDescriptionListTitle>
             <EuiDescriptionListDescription>
-              {displayIndices ? (
-                <EuiText>
-                  <ul>
-                    {(isShowingFullIndicesList
-                      ? displayIndices
-                      : [...displayIndices].splice(0, 10)
-                    ).map(index => (
-                      <li key={index}>
-                        <EuiTitle size="xs">
-                          <span>{index}</span>
-                        </EuiTitle>
-                      </li>
-                    ))}
-                    {hiddenIndicesCount ? (
-                      <li key="hiddenIndicesCount">
-                        <EuiTitle size="xs">
-                          {isShowingFullIndicesList ? (
-                            <EuiLink onClick={() => setIsShowingFullIndicesList(false)}>
-                              <FormattedMessage
-                                id="xpack.snapshotRestore.restoreForm.stepReview.summaryTab.indicesCollapseAllLink"
-                                defaultMessage="Hide {count, plural, one {# index} other {# indices}}"
-                                values={{ count: hiddenIndicesCount }}
-                              />{' '}
-                              <EuiIcon type="arrowUp" />
-                            </EuiLink>
-                          ) : (
-                            <EuiLink onClick={() => setIsShowingFullIndicesList(true)}>
-                              <FormattedMessage
-                                id="xpack.snapshotRestore.restoreForm.stepReview.summaryTab.indicesShowAllLink"
-                                defaultMessage="Show {count} more {count, plural, one {index} other {indices}}"
-                                values={{ count: hiddenIndicesCount }}
-                              />{' '}
-                              <EuiIcon type="arrowDown" />
-                            </EuiLink>
-                          )}
-                        </EuiTitle>
-                      </li>
-                    ) : null}
-                  </ul>
-                </EuiText>
-              ) : (
-                <FormattedMessage
-                  id="xpack.snapshotRestore.restoreForm.stepReview.summaryTab.allIndicesValue"
-                  defaultMessage="All indices"
-                />
-              )}
+              <CollapsibleIndicesList indices={restoreIndices} />
             </EuiDescriptionListDescription>
           </EuiDescriptionList>
         </EuiFlexItem>
diff --git a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/policy_list/policy_details/tabs/tab_summary.tsx b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/policy_list/policy_details/tabs/tab_summary.tsx
index e719f3cd1451b..1f63115c3a5fb 100644
--- a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/policy_list/policy_details/tabs/tab_summary.tsx
+++ b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/policy_list/policy_details/tabs/tab_summary.tsx
@@ -3,7 +3,7 @@
  * or more contributor license agreements. Licensed under the Elastic License;
  * you may not use this file except in compliance with the Elastic License.
  */
-import React, { useState, useEffect, Fragment } from 'react';
+import React, { Fragment } from 'react';
 import {
   EuiCallOut,
   EuiFlexGroup,
@@ -13,8 +13,6 @@ import {
   EuiDescriptionList,
   EuiDescriptionListTitle,
   EuiDescriptionListDescription,
-  EuiIcon,
-  EuiText,
   EuiPanel,
   EuiStat,
   EuiSpacer,
@@ -23,7 +21,7 @@ import {
 
 import { SlmPolicy } from '../../../../../../../common/types';
 import { useAppDependencies } from '../../../../../index';
-import { FormattedDateTime } from '../../../../../components';
+import { FormattedDateTime, CollapsibleIndicesList } from '../../../../../components';
 import { linkToSnapshots, linkToRepository } from '../../../../../services/navigation';
 
 interface Props {
@@ -56,80 +54,6 @@ export const TabSummary: React.FunctionComponent<Props> = ({ policy }) => {
     partial: undefined,
   };
 
-  // Only show 10 indices initially
-  const [isShowingFullIndicesList, setIsShowingFullIndicesList] = useState<boolean>(false);
-  const displayIndices = typeof indices === 'string' ? indices.split(',') : indices;
-  const hiddenIndicesCount =
-    displayIndices && displayIndices.length > 10 ? displayIndices.length - 10 : 0;
-  const shortIndicesList =
-    displayIndices && displayIndices.length ? (
-      <EuiText size="m">
-        <ul>
-          {[...displayIndices].splice(0, 10).map((index: string) => (
-            <li key={index}>
-              <EuiTitle size="xs">
-                <span>{index}</span>
-              </EuiTitle>
-            </li>
-          ))}
-          {hiddenIndicesCount ? (
-            <li key="hiddenIndicesCount">
-              <EuiTitle size="xs">
-                <EuiLink onClick={() => setIsShowingFullIndicesList(true)}>
-                  <FormattedMessage
-                    id="xpack.snapshotRestore.policyDetails.indicesShowAllLink"
-                    defaultMessage="Show {count} more {count, plural, one {index} other {indices}}"
-                    values={{ count: hiddenIndicesCount }}
-                  />{' '}
-                  <EuiIcon type="arrowDown" />
-                </EuiLink>
-              </EuiTitle>
-            </li>
-          ) : null}
-        </ul>
-      </EuiText>
-    ) : (
-      <FormattedMessage
-        id="xpack.snapshotRestore.policyDetails.allIndicesLabel"
-        defaultMessage="All indices"
-      />
-    );
-  const fullIndicesList =
-    displayIndices && displayIndices.length && displayIndices.length > 10 ? (
-      <EuiText size="m">
-        <ul>
-          {displayIndices.map((index: string) => (
-            <li key={index}>
-              <EuiTitle size="xs">
-                <span>{index}</span>
-              </EuiTitle>
-            </li>
-          ))}
-          {hiddenIndicesCount ? (
-            <li key="hiddenIndicesCount">
-              <EuiTitle size="xs">
-                <EuiLink onClick={() => setIsShowingFullIndicesList(false)}>
-                  <FormattedMessage
-                    id="xpack.snapshotRestore.policyDetails.indicesCollapseAllLink"
-                    defaultMessage="Hide {count, plural, one {# index} other {# indices}}"
-                    values={{ count: hiddenIndicesCount }}
-                  />{' '}
-                  <EuiIcon type="arrowUp" />
-                </EuiLink>
-              </EuiTitle>
-            </li>
-          ) : null}
-        </ul>
-      </EuiText>
-    ) : null;
-
-  // Reset indices list state when clicking through different policies
-  useEffect(() => {
-    return () => {
-      setIsShowingFullIndicesList(false);
-    };
-  }, []);
-
   return (
     <Fragment>
       {isManagedPolicy ? (
@@ -314,7 +238,7 @@ export const TabSummary: React.FunctionComponent<Props> = ({ policy }) => {
             </EuiDescriptionListTitle>
 
             <EuiDescriptionListDescription className="eui-textBreakWord" data-test-subj="value">
-              {isShowingFullIndicesList ? fullIndicesList : shortIndicesList}
+              <CollapsibleIndicesList indices={indices} />
             </EuiDescriptionListDescription>
           </EuiFlexItem>
 
diff --git a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_details/tabs/tab_summary.tsx b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_details/tabs/tab_summary.tsx
index d3d32cb149064..c71fead0a6fc2 100644
--- a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_details/tabs/tab_summary.tsx
+++ b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/snapshot_list/snapshot_details/tabs/tab_summary.tsx
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import React, { useState, useEffect } from 'react';
+import React from 'react';
 
 import {
   EuiDescriptionList,
@@ -14,15 +14,16 @@ import {
   EuiFlexItem,
   EuiLink,
   EuiLoadingSpinner,
-  EuiText,
-  EuiTitle,
-  EuiIcon,
 } from '@elastic/eui';
 
 import { SnapshotDetails } from '../../../../../../../common/types';
 import { SNAPSHOT_STATE } from '../../../../../constants';
 import { useAppDependencies } from '../../../../../index';
-import { DataPlaceholder, FormattedDateTime } from '../../../../../components';
+import {
+  DataPlaceholder,
+  FormattedDateTime,
+  CollapsibleIndicesList,
+} from '../../../../../components';
 import { linkToPolicy } from '../../../../../services/navigation';
 import { SnapshotState } from './snapshot_state';
 
@@ -52,73 +53,6 @@ export const TabSummary: React.FC<Props> = ({ snapshotDetails }) => {
     policyName,
   } = snapshotDetails;
 
-  // Only show 10 indices initially
-  const [isShowingFullIndicesList, setIsShowingFullIndicesList] = useState<boolean>(false);
-  const hiddenIndicesCount = indices.length > 10 ? indices.length - 10 : 0;
-  const shortIndicesList = indices.length ? (
-    <ul>
-      {[...indices].splice(0, 10).map((index: string) => (
-        <li key={index}>
-          <EuiTitle size="xs">
-            <span>{index}</span>
-          </EuiTitle>
-        </li>
-      ))}
-      {hiddenIndicesCount ? (
-        <li key="hiddenIndicesCount">
-          <EuiTitle size="xs">
-            <EuiLink onClick={() => setIsShowingFullIndicesList(true)}>
-              <FormattedMessage
-                id="xpack.snapshotRestore.snapshotDetails.itemIndicesShowAllLink"
-                defaultMessage="Show {count} more {count, plural, one {index} other {indices}}"
-                values={{ count: hiddenIndicesCount }}
-              />{' '}
-              <EuiIcon type="arrowDown" />
-            </EuiLink>
-          </EuiTitle>
-        </li>
-      ) : null}
-    </ul>
-  ) : (
-    <FormattedMessage
-      id="xpack.snapshotRestore.snapshotDetails.itemIndicesNoneLabel"
-      defaultMessage="-"
-    />
-  );
-  const fullIndicesList =
-    indices.length && indices.length > 10 ? (
-      <ul>
-        {indices.map((index: string) => (
-          <li key={index}>
-            <EuiTitle size="xs">
-              <span>{index}</span>
-            </EuiTitle>
-          </li>
-        ))}
-        {hiddenIndicesCount ? (
-          <li key="hiddenIndicesCount">
-            <EuiTitle size="xs">
-              <EuiLink onClick={() => setIsShowingFullIndicesList(false)}>
-                <FormattedMessage
-                  id="xpack.snapshotRestore.snapshotDetails.itemIndicesCollapseAllLink"
-                  defaultMessage="Hide {count, plural, one {# index} other {# indices}}"
-                  values={{ count: hiddenIndicesCount }}
-                />{' '}
-                <EuiIcon type="arrowUp" />
-              </EuiLink>
-            </EuiTitle>
-          </li>
-        ) : null}
-      </ul>
-    ) : null;
-
-  // Reset indices list state when clicking through different snapshots
-  useEffect(() => {
-    return () => {
-      setIsShowingFullIndicesList(false);
-    };
-  }, []);
-
   return (
     <EuiDescriptionList textStyle="reverse">
       <EuiFlexGroup>
@@ -198,7 +132,7 @@ export const TabSummary: React.FC<Props> = ({ snapshotDetails }) => {
           </EuiDescriptionListTitle>
 
           <EuiDescriptionListDescription className="eui-textBreakWord" data-test-subj="value">
-            <EuiText>{isShowingFullIndicesList ? fullIndicesList : shortIndicesList}</EuiText>
+            <CollapsibleIndicesList indices={indices} />
           </EuiDescriptionListDescription>
         </EuiFlexItem>
       </EuiFlexGroup>
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index bb488c0a7b1fb..b171863f26c21 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -11551,7 +11551,6 @@
     "xpack.snapshotRestore.home.snapshotRestoreTitle": "スナップショットリポジドリ",
     "xpack.snapshotRestore.home.snapshotsTabTitle": "スナップショット",
     "xpack.snapshotRestore.policies.breadcrumbTitle": "ポリシー",
-    "xpack.snapshotRestore.policyDetails.allIndicesLabel": "すべてのインデックス",
     "xpack.snapshotRestore.policyDetails.closeButtonLabel": "閉じる",
     "xpack.snapshotRestore.policyDetails.deleteButtonLabel": "削除",
     "xpack.snapshotRestore.policyDetails.editButtonLabel": "編集",
@@ -11565,9 +11564,7 @@
     "xpack.snapshotRestore.policyDetails.includeGlobalStateFalseLabel": "いいえ",
     "xpack.snapshotRestore.policyDetails.includeGlobalStateLabel": "グローバルステータスを含める",
     "xpack.snapshotRestore.policyDetails.includeGlobalStateTrueLabel": "はい",
-    "xpack.snapshotRestore.policyDetails.indicesCollapseAllLink": "{count, plural, one {# インデックス} other {# インデックス}}を非表示",
     "xpack.snapshotRestore.policyDetails.indicesLabel": "インデックス",
-    "xpack.snapshotRestore.policyDetails.indicesShowAllLink": "{count}その他の{count, plural, one {インデックス} other {インデックス}}を表示",
     "xpack.snapshotRestore.policyDetails.inProgressSnapshotLinkText": "「{snapshotName}」が進行中",
     "xpack.snapshotRestore.policyDetails.lastFailure.dateLabel": "日付",
     "xpack.snapshotRestore.policyDetails.lastFailure.detailsAriaLabel": "ポリシー「{name}」の前回のエラーの詳細",
@@ -11667,7 +11664,6 @@
     "xpack.snapshotRestore.policyForm.stepReview.retentionTab.maxCountLabel": "最高カウント",
     "xpack.snapshotRestore.policyForm.stepReview.retentionTab.minCountLabel": "最低カウント",
     "xpack.snapshotRestore.policyForm.stepReview.retentionTab.sectionRetentionTitle": "スナップショットの保存",
-    "xpack.snapshotRestore.policyForm.stepReview.summaryTab.allIndicesValue": "すべてのインデックス",
     "xpack.snapshotRestore.policyForm.stepReview.summaryTab.editStepTooltip": "編集",
     "xpack.snapshotRestore.policyForm.stepReview.summaryTab.ignoreUnavailableFalseLabel": "いいえ",
     "xpack.snapshotRestore.policyForm.stepReview.summaryTab.ignoreUnavailableLabel": "利用不可能なインデックスを無視",
@@ -11675,9 +11671,7 @@
     "xpack.snapshotRestore.policyForm.stepReview.summaryTab.includeGlobalStateFalseLabel": "いいえ",
     "xpack.snapshotRestore.policyForm.stepReview.summaryTab.includeGlobalStateLabel": "グローバルステータスを含める",
     "xpack.snapshotRestore.policyForm.stepReview.summaryTab.includeGlobalStateTrueLabel": "はい",
-    "xpack.snapshotRestore.policyForm.stepReview.summaryTab.indicesCollapseAllLink": "{count, plural, one {# インデックス} other {# インデックス}}を非表示",
     "xpack.snapshotRestore.policyForm.stepReview.summaryTab.indicesLabel": "インデックス",
-    "xpack.snapshotRestore.policyForm.stepReview.summaryTab.indicesShowAllLink": "{count}その他の{count, plural, one {インデックス} other {インデックス}}を表示",
     "xpack.snapshotRestore.policyForm.stepReview.summaryTab.nameLabel": "ポリシー名",
     "xpack.snapshotRestore.policyForm.stepReview.summaryTab.partialFalseLabel": "いいえ",
     "xpack.snapshotRestore.policyForm.stepReview.summaryTab.partialLabel": "部分シャードを許可",
@@ -12069,16 +12063,13 @@
     "xpack.snapshotRestore.restoreForm.stepLogisticsTitle": "詳細を復元",
     "xpack.snapshotRestore.restoreForm.stepReview.jsonTab.jsonAriaLabel": "実行する設定を復元",
     "xpack.snapshotRestore.restoreForm.stepReview.jsonTabTitle": "JSON",
-    "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.allIndicesValue": "すべてのインデックス",
     "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.editStepTooltip": "編集",
     "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.ignoreIndexSettingsLabel": "リセット",
     "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.includeGlobalStateFalseValue": "いいえ",
     "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.includeGlobalStateLabel": "グローバル状態の復元",
     "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.includeGlobalStateTrueValue": "はい",
     "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.indexSettingsLabel": "修正",
-    "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.indicesCollapseAllLink": "{count, plural, one {# インデックス} other {# インデックス}}を非表示",
     "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.indicesLabel": "インデックス",
-    "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.indicesShowAllLink": "{count}その他の{count, plural, one {インデックス} other {インデックス}}を表示",
     "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.noSettingsValue": "インデックス設定の修正はありません",
     "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.partialFalseValue": "いいえ",
     "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.partialLabel": "部分復元",
@@ -12169,10 +12160,7 @@
     "xpack.snapshotRestore.snapshotDetails.itemIncludeGlobalStateLabel": "グローバルステータスを含める",
     "xpack.snapshotRestore.snapshotDetails.itemIncludeGlobalStateNoLabel": "いいえ",
     "xpack.snapshotRestore.snapshotDetails.itemIncludeGlobalStateYesLabel": "はい",
-    "xpack.snapshotRestore.snapshotDetails.itemIndicesCollapseAllLink": "{count, plural, one {# インデックス} other {# インデックス}}を非表示",
     "xpack.snapshotRestore.snapshotDetails.itemIndicesLabel": "インデックス ({indicesCount})",
-    "xpack.snapshotRestore.snapshotDetails.itemIndicesNoneLabel": "-",
-    "xpack.snapshotRestore.snapshotDetails.itemIndicesShowAllLink": "{count}その他の{count, plural, one {インデックス} other {インデックス}}を表示",
     "xpack.snapshotRestore.snapshotDetails.itemStartTimeLabel": "開始時刻",
     "xpack.snapshotRestore.snapshotDetails.itemStateLabel": "ステータス",
     "xpack.snapshotRestore.snapshotDetails.itemUuidLabel": "UUID",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 402ddd8288830..050e9bd40f58d 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -11550,7 +11550,6 @@
     "xpack.snapshotRestore.home.snapshotRestoreTitle": "快照存储库",
     "xpack.snapshotRestore.home.snapshotsTabTitle": "快照",
     "xpack.snapshotRestore.policies.breadcrumbTitle": "策略",
-    "xpack.snapshotRestore.policyDetails.allIndicesLabel": "所有索引",
     "xpack.snapshotRestore.policyDetails.closeButtonLabel": "关闭",
     "xpack.snapshotRestore.policyDetails.deleteButtonLabel": "删除",
     "xpack.snapshotRestore.policyDetails.editButtonLabel": "编辑",
@@ -11564,9 +11563,7 @@
     "xpack.snapshotRestore.policyDetails.includeGlobalStateFalseLabel": "否",
     "xpack.snapshotRestore.policyDetails.includeGlobalStateLabel": "包括全局状态",
     "xpack.snapshotRestore.policyDetails.includeGlobalStateTrueLabel": "是",
-    "xpack.snapshotRestore.policyDetails.indicesCollapseAllLink": "隐藏 {count, plural, one {# 个索引} other {# 个索引}}",
     "xpack.snapshotRestore.policyDetails.indicesLabel": "索引",
-    "xpack.snapshotRestore.policyDetails.indicesShowAllLink": "再显示 {count} 个 {count, plural, one {索引} other {索引}}",
     "xpack.snapshotRestore.policyDetails.inProgressSnapshotLinkText": "“{snapshotName}”正在进行中",
     "xpack.snapshotRestore.policyDetails.lastFailure.dateLabel": "日期",
     "xpack.snapshotRestore.policyDetails.lastFailure.detailsAriaLabel": "策略“{name}”的上次失败详情",
@@ -11666,7 +11663,6 @@
     "xpack.snapshotRestore.policyForm.stepReview.retentionTab.maxCountLabel": "最大计数",
     "xpack.snapshotRestore.policyForm.stepReview.retentionTab.minCountLabel": "最小计数",
     "xpack.snapshotRestore.policyForm.stepReview.retentionTab.sectionRetentionTitle": "快照保留",
-    "xpack.snapshotRestore.policyForm.stepReview.summaryTab.allIndicesValue": "所有索引",
     "xpack.snapshotRestore.policyForm.stepReview.summaryTab.editStepTooltip": "编辑",
     "xpack.snapshotRestore.policyForm.stepReview.summaryTab.ignoreUnavailableFalseLabel": "否",
     "xpack.snapshotRestore.policyForm.stepReview.summaryTab.ignoreUnavailableLabel": "忽略不可用索引",
@@ -11674,9 +11670,7 @@
     "xpack.snapshotRestore.policyForm.stepReview.summaryTab.includeGlobalStateFalseLabel": "否",
     "xpack.snapshotRestore.policyForm.stepReview.summaryTab.includeGlobalStateLabel": "包括全局状态",
     "xpack.snapshotRestore.policyForm.stepReview.summaryTab.includeGlobalStateTrueLabel": "是",
-    "xpack.snapshotRestore.policyForm.stepReview.summaryTab.indicesCollapseAllLink": "隐藏 {count, plural, one {# 个索引} other {# 个索引}}",
     "xpack.snapshotRestore.policyForm.stepReview.summaryTab.indicesLabel": "索引",
-    "xpack.snapshotRestore.policyForm.stepReview.summaryTab.indicesShowAllLink": "再显示 {count} 个 {count, plural, one {索引} other {索引}}",
     "xpack.snapshotRestore.policyForm.stepReview.summaryTab.nameLabel": "策略名称",
     "xpack.snapshotRestore.policyForm.stepReview.summaryTab.partialFalseLabel": "否",
     "xpack.snapshotRestore.policyForm.stepReview.summaryTab.partialLabel": "允许部分分片",
@@ -12068,16 +12062,13 @@
     "xpack.snapshotRestore.restoreForm.stepLogisticsTitle": "还原详情",
     "xpack.snapshotRestore.restoreForm.stepReview.jsonTab.jsonAriaLabel": "还原要执行的设置",
     "xpack.snapshotRestore.restoreForm.stepReview.jsonTabTitle": "JSON",
-    "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.allIndicesValue": "所有索引",
     "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.editStepTooltip": "缂栬緫",
     "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.ignoreIndexSettingsLabel": "重置",
     "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.includeGlobalStateFalseValue": "否",
     "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.includeGlobalStateLabel": "还原全局状态",
     "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.includeGlobalStateTrueValue": "鏄",
     "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.indexSettingsLabel": "修改",
-    "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.indicesCollapseAllLink": "隐藏 {count, plural, one {# 个索引} other {# 个索引}}",
     "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.indicesLabel": "索引",
-    "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.indicesShowAllLink": "再显示 {count} 个 {count, plural, one {索引} other {索引}}",
     "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.noSettingsValue": "无索引设置修改",
     "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.partialFalseValue": "否",
     "xpack.snapshotRestore.restoreForm.stepReview.summaryTab.partialLabel": "部分还原",
@@ -12168,10 +12159,7 @@
     "xpack.snapshotRestore.snapshotDetails.itemIncludeGlobalStateLabel": "包括全局状态",
     "xpack.snapshotRestore.snapshotDetails.itemIncludeGlobalStateNoLabel": "否",
     "xpack.snapshotRestore.snapshotDetails.itemIncludeGlobalStateYesLabel": "是",
-    "xpack.snapshotRestore.snapshotDetails.itemIndicesCollapseAllLink": "隐藏 {count, plural, one {# 个索引} other {# 个索引}}",
     "xpack.snapshotRestore.snapshotDetails.itemIndicesLabel": "索引 ({indicesCount})",
-    "xpack.snapshotRestore.snapshotDetails.itemIndicesNoneLabel": "-",
-    "xpack.snapshotRestore.snapshotDetails.itemIndicesShowAllLink": "再显示 {count} 个 {count, plural, one {索引} other {索引}}",
     "xpack.snapshotRestore.snapshotDetails.itemStartTimeLabel": "开始时间",
     "xpack.snapshotRestore.snapshotDetails.itemStateLabel": "状态",
     "xpack.snapshotRestore.snapshotDetails.itemUuidLabel": "UUID",

From 8d5a02fc90e2ff3c54d2c41abb6319f4e27659b8 Mon Sep 17 00:00:00 2001
From: Melissa Alvarez <melissa.alvarez@elastic.co>
Date: Wed, 19 Feb 2020 20:57:40 -0500
Subject: [PATCH 082/174] migrates notification server routes to NP (#57906)

---
 .../plugins/ml/server/new_platform/plugin.ts  |  1 -
 .../plugins/ml/server/routes/apidoc.json      |  4 +-
 .../ml/server/routes/notification_settings.js | 26 -----------
 .../ml/server/routes/notification_settings.ts | 43 +++++++++++++++++++
 4 files changed, 46 insertions(+), 28 deletions(-)
 delete mode 100644 x-pack/legacy/plugins/ml/server/routes/notification_settings.js
 create mode 100644 x-pack/legacy/plugins/ml/server/routes/notification_settings.ts

diff --git a/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts b/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts
index 60ede82d0de85..e006ad3d3718f 100644
--- a/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts
+++ b/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts
@@ -33,7 +33,6 @@ import { dataFeedRoutes } from '../routes/datafeeds';
 import { indicesRoutes } from '../routes/indices';
 import { jobValidationRoutes } from '../routes/job_validation';
 import { makeMlUsageCollector } from '../lib/ml_telemetry';
-// @ts-ignore: could not find declaration file for module
 import { notificationRoutes } from '../routes/notification_settings';
 // @ts-ignore: could not find declaration file for module
 import { systemRoutes } from '../routes/system';
diff --git a/x-pack/legacy/plugins/ml/server/routes/apidoc.json b/x-pack/legacy/plugins/ml/server/routes/apidoc.json
index 4682371c16424..89751abdbe20d 100644
--- a/x-pack/legacy/plugins/ml/server/routes/apidoc.json
+++ b/x-pack/legacy/plugins/ml/server/routes/apidoc.json
@@ -92,6 +92,8 @@
     "EstimateBucketSpan",
     "CalculateModelMemoryLimit",
     "ValidateCardinality",
-    "ValidateJob"
+    "ValidateJob",
+    "NotificationSettings",
+    "GetNotificationSettings"
   ]
 }
diff --git a/x-pack/legacy/plugins/ml/server/routes/notification_settings.js b/x-pack/legacy/plugins/ml/server/routes/notification_settings.js
deleted file mode 100644
index 3290b5a821902..0000000000000
--- a/x-pack/legacy/plugins/ml/server/routes/notification_settings.js
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { callWithRequestFactory } from '../client/call_with_request_factory';
-import { wrapError } from '../client/errors';
-
-export function notificationRoutes({ commonRouteConfig, elasticsearchPlugin, route }) {
-  route({
-    method: 'GET',
-    path: '/api/ml/notification_settings',
-    handler(request) {
-      const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
-      const params = {
-        includeDefaults: true,
-        filterPath: '**.xpack.notification',
-      };
-      return callWithRequest('cluster.getSettings', params).catch(resp => wrapError(resp));
-    },
-    config: {
-      ...commonRouteConfig,
-    },
-  });
-}
diff --git a/x-pack/legacy/plugins/ml/server/routes/notification_settings.ts b/x-pack/legacy/plugins/ml/server/routes/notification_settings.ts
new file mode 100644
index 0000000000000..c65627543b21d
--- /dev/null
+++ b/x-pack/legacy/plugins/ml/server/routes/notification_settings.ts
@@ -0,0 +1,43 @@
+/*
+ * 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 { licensePreRoutingFactory } from '../new_platform/licence_check_pre_routing_factory';
+import { wrapError } from '../client/error_wrapper';
+import { RouteInitialization } from '../new_platform/plugin';
+
+/**
+ * Routes for notification settings
+ */
+export function notificationRoutes({ xpackMainPlugin, router }: RouteInitialization) {
+  /**
+   * @apiGroup NotificationSettings
+   *
+   * @api {get} /api/ml/notification_settings Get notification settings
+   * @apiName GetNotificationSettings
+   * @apiDescription Returns cluster notification settings
+   */
+  router.get(
+    {
+      path: '/api/ml/notification_settings',
+      validate: false,
+    },
+    licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
+      try {
+        const params = {
+          includeDefaults: true,
+          filterPath: '**.xpack.notification',
+        };
+        const resp = await context.ml!.mlClient.callAsCurrentUser('cluster.getSettings', params);
+
+        return response.ok({
+          body: resp,
+        });
+      } catch (e) {
+        return response.customError(wrapError(e));
+      }
+    })
+  );
+}

From ba983f0683ea78978d7af74bb204f21d1b7dacbf Mon Sep 17 00:00:00 2001
From: spalger <spalger@users.noreply.github.com>
Date: Wed, 19 Feb 2020 19:26:40 -0700
Subject: [PATCH 083/174] skip flaky suite (#45348)

---
 .../apps/discover/feature_controls/discover_security.ts        | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts
index a1d6bfefdea64..53d0872b810fe 100644
--- a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts
+++ b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts
@@ -28,7 +28,8 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
     await PageObjects.timePicker.setDefaultAbsoluteRange();
   }
 
-  describe('security', () => {
+  // FLAKY: https://github.com/elastic/kibana/issues/45348
+  describe.skip('security', () => {
     before(async () => {
       await esArchiver.load('discover/feature_controls/security');
       await esArchiver.loadIfNeeded('logstash_functional');

From dbb4a66e0d743c820ea76ba6407d657c6d13d8fd Mon Sep 17 00:00:00 2001
From: spalger <spalger@users.noreply.github.com>
Date: Wed, 19 Feb 2020 19:29:20 -0700
Subject: [PATCH 084/174] skip flaky suite (#58059)

---
 x-pack/test/functional/apps/infra/logs_source_configuration.ts | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/x-pack/test/functional/apps/infra/logs_source_configuration.ts b/x-pack/test/functional/apps/infra/logs_source_configuration.ts
index ecad5a40ec42e..1dfbe3526ce40 100644
--- a/x-pack/test/functional/apps/infra/logs_source_configuration.ts
+++ b/x-pack/test/functional/apps/infra/logs_source_configuration.ts
@@ -15,7 +15,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
   const pageObjects = getPageObjects(['common', 'infraLogs']);
   const retry = getService('retry');
 
-  describe('Logs Source Configuration', function() {
+  // FLAKY: https://github.com/elastic/kibana/issues/58059
+  describe.skip('Logs Source Configuration', function() {
     this.tags('smoke');
 
     before(async () => {

From 1464706b5c31adfca6d7ea03b645256a66625756 Mon Sep 17 00:00:00 2001
From: spalger <spalger@users.noreply.github.com>
Date: Wed, 19 Feb 2020 19:32:58 -0700
Subject: [PATCH 085/174] skip flaky suite (#56816)

---
 x-pack/test/functional/apps/rollup_job/tsvb.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/x-pack/test/functional/apps/rollup_job/tsvb.js b/x-pack/test/functional/apps/rollup_job/tsvb.js
index f3782c4c91644..fb4a81406f9f1 100644
--- a/x-pack/test/functional/apps/rollup_job/tsvb.js
+++ b/x-pack/test/functional/apps/rollup_job/tsvb.js
@@ -21,7 +21,8 @@ export default function({ getService, getPageObjects }) {
     'timePicker',
   ]);
 
-  describe('tsvb integration', function() {
+  // FLAKY: https://github.com/elastic/kibana/issues/56816
+  describe.skip('tsvb integration', function() {
     //Since rollups can only be created once with the same name (even if you delete it),
     //we add the Date.now() to avoid name collision if you run the tests locally back to back.
     const rollupJobName = `tsvb-test-rollup-job-${Date.now()}`;

From 8097bd8dc1ca012bfdc516e1e44e47c3c943c7fc Mon Sep 17 00:00:00 2001
From: Frank Hassanabad <frank.hassanabad@elastic.co>
Date: Wed, 19 Feb 2020 19:44:11 -0700
Subject: [PATCH 086/174] [SIEM][Detection Engine] Fixes return codes where
 some were rule_id instead of id

## Summary

Fixes some return error codes where they were `rule_id` when they should have been `id`

- [x] [Unit or functional tests](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) were updated or added to match the most common scenarios
---
 .../routes/rules/import_rules_route.test.ts   |  4 +-
 .../routes/rules/import_rules_route.ts        |  1 -
 .../routes/rules/utils.test.ts                | 14 ++++-
 .../detection_engine/routes/rules/utils.ts    | 12 +++--
 .../lib/detection_engine/routes/utils.ts      | 51 +++++++++++++++----
 .../tests/delete_rules_bulk.ts                |  8 +--
 .../tests/patch_rules_bulk.ts                 |  4 +-
 .../tests/update_rules_bulk.ts                |  4 +-
 8 files changed, 72 insertions(+), 26 deletions(-)

diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.test.ts
index e3283a750869c..b1dd08f8ca371 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.test.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.test.ts
@@ -210,7 +210,7 @@ describe('import_rules_route', () => {
               message: 'Unexpected token : in JSON at position 8',
               status_code: 400,
             },
-            rule_id: '(unknown)',
+            rule_id: '(unknown id)',
           },
         ],
         success: false,
@@ -329,7 +329,7 @@ describe('import_rules_route', () => {
               message: 'Unexpected token : in JSON at position 8',
               status_code: 400,
             },
-            rule_id: '(unknown)',
+            rule_id: '(unknown id)',
           },
         ],
         success: false,
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts
index cdb09ff7b04ed..f438e0120f96a 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts
@@ -95,7 +95,6 @@ export const createImportRulesRoute = (
                   // early with the error and an (unknown) for the ruleId
                   resolve(
                     createBulkErrorObject({
-                      ruleId: '(unknown)',
                       statusCode: 400,
                       message: parsedRule.message,
                     })
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts
index 2b0da8251b387..593c55bcae9f2 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts
@@ -794,10 +794,20 @@ describe('utils', () => {
   });
 
   describe('getIdBulkError', () => {
+    test('outputs message about id and rule_id not being found if both are not null', () => {
+      const error = getIdBulkError({ id: '123', ruleId: '456' });
+      const expected: BulkError = {
+        id: '123',
+        rule_id: '456',
+        error: { message: 'id: "123" and rule_id: "456" not found', status_code: 404 },
+      };
+      expect(error).toEqual(expected);
+    });
+
     test('outputs message about id not being found if only id is defined and ruleId is undefined', () => {
       const error = getIdBulkError({ id: '123', ruleId: undefined });
       const expected: BulkError = {
-        rule_id: '123',
+        id: '123',
         error: { message: 'id: "123" not found', status_code: 404 },
       };
       expect(error).toEqual(expected);
@@ -806,7 +816,7 @@ describe('utils', () => {
     test('outputs message about id not being found if only id is defined and ruleId is null', () => {
       const error = getIdBulkError({ id: '123', ruleId: null });
       const expected: BulkError = {
-        rule_id: '123',
+        id: '123',
         error: { message: 'id: "123" not found', status_code: 404 },
       };
       expect(error).toEqual(expected);
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts
index 21fc5a12db536..198cdbfb9771d 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts
@@ -62,9 +62,16 @@ export const getIdBulkError = ({
   id: string | undefined | null;
   ruleId: string | undefined | null;
 }): BulkError => {
-  if (id != null) {
+  if (id != null && ruleId != null) {
+    return createBulkErrorObject({
+      id,
+      ruleId,
+      statusCode: 404,
+      message: `id: "${id}" and rule_id: "${ruleId}" not found`,
+    });
+  } else if (id != null) {
     return createBulkErrorObject({
-      ruleId: id,
+      id,
       statusCode: 404,
       message: `id: "${id}" not found`,
     });
@@ -76,7 +83,6 @@ export const getIdBulkError = ({
     });
   } else {
     return createBulkErrorObject({
-      ruleId: '(unknown id)',
       statusCode: 404,
       message: `id or rule_id should have been defined`,
     });
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts
index 4a586e21c9e7f..55832ab67dc6b 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts
@@ -44,7 +44,8 @@ export const transformError = (err: Error & { statusCode?: number }): OutputErro
 };
 
 export interface BulkError {
-  rule_id: string;
+  id?: string;
+  rule_id?: string;
   error: {
     status_code: number;
     message: string;
@@ -53,24 +54,54 @@ export interface BulkError {
 
 export const createBulkErrorObject = ({
   ruleId,
+  id,
   statusCode,
   message,
 }: {
-  ruleId: string;
+  ruleId?: string;
+  id?: string;
   statusCode: number;
   message: string;
 }): BulkError => {
-  return {
-    rule_id: ruleId,
-    error: {
-      status_code: statusCode,
-      message,
-    },
-  };
+  if (id != null && ruleId != null) {
+    return {
+      id,
+      rule_id: ruleId,
+      error: {
+        status_code: statusCode,
+        message,
+      },
+    };
+  } else if (id != null) {
+    return {
+      id,
+      error: {
+        status_code: statusCode,
+        message,
+      },
+    };
+  } else if (ruleId != null) {
+    return {
+      rule_id: ruleId,
+      error: {
+        status_code: statusCode,
+        message,
+      },
+    };
+  } else {
+    return {
+      rule_id: '(unknown id)',
+      error: {
+        status_code: statusCode,
+        message,
+      },
+    };
+  }
 };
 
 export interface ImportRuleResponse {
-  rule_id: string;
+  rule_id?: string;
+  id?: string;
   status_code?: number;
   message?: string;
   error?: {
diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/delete_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/delete_rules_bulk.ts
index 5a1c178f6b211..6b87c94029189 100644
--- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/delete_rules_bulk.ts
+++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/delete_rules_bulk.ts
@@ -129,7 +129,7 @@ export default ({ getService }: FtrProviderContext): void => {
               message: 'id: "fake_id" not found',
               status_code: 404,
             },
-            rule_id: 'fake_id', // TODO This is a known issue where it should be id and not rule_id
+            id: 'fake_id',
           },
         ]);
       });
@@ -152,7 +152,7 @@ export default ({ getService }: FtrProviderContext): void => {
         const bodyToCompare = removeServerGeneratedPropertiesIncludingRuleId(body[0]);
         expect([bodyToCompare, body[1]]).to.eql([
           getSimpleRuleOutputWithoutRuleId(),
-          { rule_id: 'fake_id', error: { status_code: 404, message: 'id: "fake_id" not found' } },
+          { id: 'fake_id', error: { status_code: 404, message: 'id: "fake_id" not found' } },
         ]);
       });
     });
@@ -262,7 +262,7 @@ export default ({ getService }: FtrProviderContext): void => {
               message: 'id: "fake_id" not found',
               status_code: 404,
             },
-            rule_id: 'fake_id', // TODO This is a known issue where it should be id and not rule_id
+            id: 'fake_id',
           },
         ]);
       });
@@ -285,7 +285,7 @@ export default ({ getService }: FtrProviderContext): void => {
         const bodyToCompare = removeServerGeneratedPropertiesIncludingRuleId(body[0]);
         expect([bodyToCompare, body[1]]).to.eql([
           getSimpleRuleOutputWithoutRuleId(),
-          { rule_id: 'fake_id', error: { status_code: 404, message: 'id: "fake_id" not found' } },
+          { id: 'fake_id', error: { status_code: 404, message: 'id: "fake_id" not found' } },
         ]);
       });
     });
diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/patch_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/patch_rules_bulk.ts
index 3d14bc2db47b4..c13e8909dacf9 100644
--- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/patch_rules_bulk.ts
+++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/patch_rules_bulk.ts
@@ -263,7 +263,7 @@ export default ({ getService }: FtrProviderContext) => {
           .expect(200);
 
         expect(body).to.eql([
-          { rule_id: 'fake_id', error: { status_code: 404, message: 'id: "fake_id" not found' } },
+          { id: 'fake_id', error: { status_code: 404, message: 'id: "fake_id" not found' } },
         ]);
       });
 
@@ -347,7 +347,7 @@ export default ({ getService }: FtrProviderContext) => {
               message: 'id: "fake_id" not found',
               status_code: 404,
             },
-            rule_id: 'fake_id', // TODO: This should be id and not rule_id in the codebase
+            id: 'fake_id',
           },
         ]);
       });
diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/update_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/update_rules_bulk.ts
index 4894cac2b2608..220a4af4c5c5e 100644
--- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/update_rules_bulk.ts
+++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/update_rules_bulk.ts
@@ -277,7 +277,7 @@ export default ({ getService }: FtrProviderContext) => {
           .expect(200);
 
         expect(body).to.eql([
-          { rule_id: 'fake_id', error: { status_code: 404, message: 'id: "fake_id" not found' } },
+          { id: 'fake_id', error: { status_code: 404, message: 'id: "fake_id" not found' } },
         ]);
       });
 
@@ -377,7 +377,7 @@ export default ({ getService }: FtrProviderContext) => {
               message: 'id: "fake_id" not found',
               status_code: 404,
             },
-            rule_id: 'fake_id', // TODO: This should be id and not rule_id in the codebase
+            id: 'fake_id',
           },
         ]);
       });

From b70d54438f761f5730a96a9e0c94af2f7ca844de Mon Sep 17 00:00:00 2001
From: patrykkopycinski <patryk.kopycinski@elastic.co>
Date: Thu, 20 Feb 2020 08:27:51 +0100
Subject: [PATCH 087/174] [SIEM] Fix ResizeObserver polyfill (#58046)

---
 .../plugins/siem/public/components/charts/areachart.tsx       | 2 +-
 .../legacy/plugins/siem/public/components/charts/barchart.tsx | 2 +-
 .../public/components/events_viewer/events_viewer.test.tsx    | 4 ++--
 .../siem/public/components/events_viewer/events_viewer.tsx    | 2 +-
 .../siem/public/components/events_viewer/index.test.tsx       | 4 ++--
 .../plugins/siem/public/components/timeline/timeline.test.tsx | 4 ++--
 .../plugins/siem/public/components/timeline/timeline.tsx      | 2 +-
 x-pack/legacy/plugins/siem/public/pages/home/index.tsx        | 2 +-
 .../siem/public/pages/hosts/details/details_tabs.test.tsx     | 4 ++--
 9 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx b/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx
index fd05b80e41235..b66cc77e30aad 100644
--- a/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx
@@ -16,7 +16,7 @@ import {
   RecursivePartial,
 } from '@elastic/charts';
 import { getOr, get, isNull, isNumber } from 'lodash/fp';
-import useResizeObserver from 'use-resize-observer';
+import useResizeObserver from 'use-resize-observer/polyfilled';
 
 import { ChartPlaceHolder } from './chart_place_holder';
 import { useTimeZone } from '../../lib/kibana';
diff --git a/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx b/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx
index 1355926d343df..da0f3d1d0047f 100644
--- a/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx
@@ -8,7 +8,7 @@ import React from 'react';
 import { Chart, BarSeries, Axis, Position, ScaleType, Settings } from '@elastic/charts';
 import { getOr, get, isNumber } from 'lodash/fp';
 import deepmerge from 'deepmerge';
-import useResizeObserver from 'use-resize-observer';
+import useResizeObserver from 'use-resize-observer/polyfilled';
 
 import { useTimeZone } from '../../lib/kibana';
 import { ChartPlaceHolder } from './chart_place_holder';
diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.test.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.test.tsx
index 8c4228b597dbb..d3cdf9886e469 100644
--- a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.test.tsx
@@ -6,7 +6,7 @@
 
 import React from 'react';
 import { MockedProvider } from 'react-apollo/test-utils';
-import useResizeObserver from 'use-resize-observer';
+import useResizeObserver from 'use-resize-observer/polyfilled';
 
 import { mockIndexPattern, TestProviders } from '../../mock';
 import { wait } from '../../lib/helpers';
@@ -29,7 +29,7 @@ mockUseFetchIndexPatterns.mockImplementation(() => [
 ]);
 
 const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock;
-jest.mock('use-resize-observer');
+jest.mock('use-resize-observer/polyfilled');
 mockUseResizeObserver.mockImplementation(() => ({}));
 
 const from = 1566943856794;
diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx
index 8e2c5bdd12d68..2a4d08ea214bc 100644
--- a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx
@@ -9,7 +9,7 @@ import deepEqual from 'fast-deep-equal';
 import { getOr, isEmpty, isEqual, union } from 'lodash/fp';
 import React, { useMemo } from 'react';
 import styled from 'styled-components';
-import useResizeObserver from 'use-resize-observer';
+import useResizeObserver from 'use-resize-observer/polyfilled';
 
 import { BrowserFields } from '../../containers/source';
 import { TimelineQuery } from '../../containers/timeline';
diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/index.test.tsx
index 2bedd1cb89b41..6f614c1e32f65 100644
--- a/x-pack/legacy/plugins/siem/public/components/events_viewer/index.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/index.test.tsx
@@ -6,7 +6,7 @@
 
 import React from 'react';
 import { MockedProvider } from 'react-apollo/test-utils';
-import useResizeObserver from 'use-resize-observer';
+import useResizeObserver from 'use-resize-observer/polyfilled';
 
 import { wait } from '../../lib/helpers';
 import { mockIndexPattern, TestProviders } from '../../mock';
@@ -28,7 +28,7 @@ mockUseFetchIndexPatterns.mockImplementation(() => [
 ]);
 
 const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock;
-jest.mock('use-resize-observer');
+jest.mock('use-resize-observer/polyfilled');
 mockUseResizeObserver.mockImplementation(() => ({}));
 
 const from = 1566943856794;
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.test.tsx
index 78899b7c5d628..4c5238d213e43 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.test.tsx
@@ -7,7 +7,7 @@
 import { shallow } from 'enzyme';
 import React from 'react';
 import { MockedProvider } from 'react-apollo/test-utils';
-import useResizeObserver from 'use-resize-observer';
+import useResizeObserver from 'use-resize-observer/polyfilled';
 
 import { timelineQuery } from '../../containers/timeline/index.gql_query';
 import { mockBrowserFields } from '../../containers/source/mock';
@@ -31,7 +31,7 @@ const testFlyoutHeight = 980;
 jest.mock('../../lib/kibana');
 
 const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock;
-jest.mock('use-resize-observer');
+jest.mock('use-resize-observer/polyfilled');
 mockUseResizeObserver.mockImplementation(() => ({}));
 
 describe('Timeline', () => {
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx
index e6aa6cc18f018..c9ff0296a40e2 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx
@@ -8,7 +8,7 @@ import { EuiFlexGroup } from '@elastic/eui';
 import { getOr, isEmpty } from 'lodash/fp';
 import React from 'react';
 import styled from 'styled-components';
-import useResizeObserver from 'use-resize-observer';
+import useResizeObserver from 'use-resize-observer/polyfilled';
 
 import { BrowserFields } from '../../containers/source';
 import { TimelineQuery } from '../../containers/timeline';
diff --git a/x-pack/legacy/plugins/siem/public/pages/home/index.tsx b/x-pack/legacy/plugins/siem/public/pages/home/index.tsx
index 4608079167ed5..7c5fd56bf1e91 100644
--- a/x-pack/legacy/plugins/siem/public/pages/home/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/home/index.tsx
@@ -7,7 +7,7 @@
 import React from 'react';
 import { Redirect, Route, Switch } from 'react-router-dom';
 import styled from 'styled-components';
-import useResizeObserver from 'use-resize-observer';
+import useResizeObserver from 'use-resize-observer/polyfilled';
 
 import { DragDropContextWrapper } from '../../components/drag_and_drop/drag_drop_context_wrapper';
 import { Flyout, flyoutHeaderHeight } from '../../components/flyout';
diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.test.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.test.tsx
index bdc0f79e96591..81c1b317d4596 100644
--- a/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.test.tsx
@@ -7,7 +7,7 @@
 import React from 'react';
 import { IIndexPattern } from 'src/plugins/data/public';
 import { MemoryRouter } from 'react-router-dom';
-import useResizeObserver from 'use-resize-observer';
+import useResizeObserver from 'use-resize-observer/polyfilled';
 
 import { mockIndexPattern } from '../../../mock/index_pattern';
 import { TestProviders } from '../../../mock/test_providers';
@@ -37,7 +37,7 @@ jest.mock('../../../components/query_bar', () => ({
 }));
 
 const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock;
-jest.mock('use-resize-observer');
+jest.mock('use-resize-observer/polyfilled');
 mockUseResizeObserver.mockImplementation(() => ({}));
 
 describe('body', () => {

From e23b547fb0fdddab6ed580165e958d5e45707173 Mon Sep 17 00:00:00 2001
From: Joe Reuter <johannes.reuter@elastic.co>
Date: Thu, 20 Feb 2020 10:30:00 +0100
Subject: [PATCH 088/174] Use static time for tsvb rollup test (#57701)

---
 x-pack/test/functional/apps/rollup_job/tsvb.js | 18 ++++++++----------
 1 file changed, 8 insertions(+), 10 deletions(-)

diff --git a/x-pack/test/functional/apps/rollup_job/tsvb.js b/x-pack/test/functional/apps/rollup_job/tsvb.js
index fb4a81406f9f1..acb73b170fbf2 100644
--- a/x-pack/test/functional/apps/rollup_job/tsvb.js
+++ b/x-pack/test/functional/apps/rollup_job/tsvb.js
@@ -4,7 +4,6 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import datemath from '@elastic/datemath';
 import expect from '@kbn/expect';
 import mockRolledUpData from './hybrid_index_helper';
 
@@ -12,7 +11,6 @@ export default function({ getService, getPageObjects }) {
   const es = getService('legacyEs');
   const esArchiver = getService('esArchiver');
   const retry = getService('retry');
-  const testSubjects = getService('testSubjects');
   const PageObjects = getPageObjects([
     'common',
     'settings',
@@ -21,18 +19,16 @@ export default function({ getService, getPageObjects }) {
     'timePicker',
   ]);
 
-  // FLAKY: https://github.com/elastic/kibana/issues/56816
-  describe.skip('tsvb integration', function() {
+  describe('tsvb integration', function() {
     //Since rollups can only be created once with the same name (even if you delete it),
     //we add the Date.now() to avoid name collision if you run the tests locally back to back.
     const rollupJobName = `tsvb-test-rollup-job-${Date.now()}`;
     const rollupSourceIndexName = 'rollup-source-data';
     const rollupTargetIndexName = `rollup-target-data`;
-    const now = new Date();
     const pastDates = [
-      datemath.parse('now-1m', { forceNow: now }),
-      datemath.parse('now-2m', { forceNow: now }),
-      datemath.parse('now-3m', { forceNow: now }),
+      new Date('October 15, 2019 05:35:32'),
+      new Date('October 15, 2019 05:34:32'),
+      new Date('October 15, 2019 05:33:32'),
     ];
 
     before(async () => {
@@ -78,10 +74,12 @@ export default function({ getService, getPageObjects }) {
       await PageObjects.visualize.navigateToNewVisualization();
       await PageObjects.visualize.clickVisualBuilder();
       await PageObjects.visualBuilder.checkVisualBuilderIsPresent();
-      await PageObjects.timePicker.openQuickSelectTimeMenu();
-      await testSubjects.click('superDatePickerCommonlyUsed_Last_24 hours');
       await PageObjects.visualBuilder.clickMetric();
       await PageObjects.visualBuilder.checkMetricTabIsPresent();
+      await PageObjects.timePicker.setAbsoluteRange(
+        'Oct 15, 2019 @ 00:00:01.000',
+        'Oct 15, 2019 @ 19:31:44.000'
+      );
       await PageObjects.visualBuilder.clickPanelOptions('metric');
       await PageObjects.visualBuilder.setIndexPatternValue(rollupTargetIndexName);
       await PageObjects.visualBuilder.setIntervalValue('1d');

From 0b644117b845984afeaea20ee5681dac358d5a0e Mon Sep 17 00:00:00 2001
From: Daniil Suleiman <31325372+sulemanof@users.noreply.github.com>
Date: Thu, 20 Feb 2020 13:40:34 +0300
Subject: [PATCH 089/174] [Visualize] Remove legacy appState in visualize
 (#57330)

* Remove legacy appState in visualize

* Read query and linked prop from scope

* Fix persisted state instance

* Fix functional tests

* Bound url updates with an editor

* Some improvements

* Fix comments
---
 .../components/editor/controls_tab.test.tsx   |   5 +-
 .../kibana/public/visualize/legacy_imports.ts |   3 -
 .../public/visualize/np_ready/application.ts  |   8 -
 .../visualize/np_ready/editor/editor.html     |  11 +-
 .../visualize/np_ready/editor/editor.js       | 218 +++++++++---------
 .../editor/lib/{index.js => index.ts}         |   3 +-
 .../np_ready/editor/lib/make_stateful.ts      |  58 +++++
 ...rate_app_state.js => migrate_app_state.ts} |  47 ++--
 .../editor/lib/visualize_app_state.ts         | 112 +++++++++
 .../np_ready/editor/visualization.js          |   5 +-
 .../np_ready/editor/visualization_editor.js   |   5 +-
 .../public/visualize/np_ready/types.d.ts      |  31 ++-
 .../public/components/sidebar/sidebar.tsx     |  17 +-
 .../public/components/vis_editor.js           |   1 -
 .../public/embeddable/visualize_embeddable.ts |   8 +-
 .../public/legacy/build_pipeline.test.ts      |  10 +-
 .../np_ready/public/legacy/build_pipeline.ts  |   4 +-
 .../public/np_ready/public/vis.ts             |   8 +
 .../public/np_ready/public/vis_impl.d.ts      |  10 +-
 .../apps/visualize/_heatmap_chart.js          |   4 +-
 20 files changed, 400 insertions(+), 168 deletions(-)
 rename src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/{index.js => index.ts} (87%)
 create mode 100644 src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/make_stateful.ts
 rename src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/{migrate_app_state.js => migrate_app_state.ts} (61%)
 create mode 100644 src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/visualize_app_state.ts

diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/controls_tab.test.tsx b/src/legacy/core_plugins/input_control_vis/public/components/editor/controls_tab.test.tsx
index 3419d773bd09e..d7a62e07b26f3 100644
--- a/src/legacy/core_plugins/input_control_vis/public/components/editor/controls_tab.test.tsx
+++ b/src/legacy/core_plugins/input_control_vis/public/components/editor/controls_tab.test.tsx
@@ -23,6 +23,7 @@ import { shallowWithIntl, mountWithIntl } from 'test_utils/enzyme_helpers';
 import { findTestSubject } from '@elastic/eui/lib/test';
 import { getDepsMock, getIndexPatternMock } from '../../test_utils';
 import { ControlsTab, ControlsTabUiProps } from './controls_tab';
+import { Vis } from 'src/legacy/core_plugins/visualizations/public';
 
 const indexPatternsMock = {
   get: getIndexPatternMock,
@@ -32,7 +33,7 @@ let props: ControlsTabUiProps;
 beforeEach(() => {
   props = {
     deps: getDepsMock(),
-    vis: {
+    vis: ({
       API: {
         indexPatterns: indexPatternsMock,
       },
@@ -46,7 +47,7 @@ beforeEach(() => {
         requiresSearch: false,
         hidden: false,
       },
-    },
+    } as unknown) as Vis,
     stateParams: {
       controls: [
         {
diff --git a/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts b/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts
index 92433799ba420..8b1bb0fda8c84 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts
+++ b/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts
@@ -24,14 +24,11 @@
  * directly where they are needed.
  */
 
-// @ts-ignore
-export { AppState, AppStateProvider } from 'ui/state_management/app_state';
 export { State } from 'ui/state_management/state';
 // @ts-ignore
 export { GlobalStateProvider } from 'ui/state_management/global_state';
 // @ts-ignore
 export { StateManagementConfigProvider } from 'ui/state_management/config_provider';
-export { stateMonitorFactory } from 'ui/state_management/state_monitor_factory';
 export { PersistedState } from 'ui/persisted_state';
 
 export { subscribeWithScope } from 'ui/utils/subscribe_with_scope';
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts b/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts
index bd7b478f827a6..6a8d9ce106f9d 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts
+++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts
@@ -22,8 +22,6 @@ import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular';
 
 import { AppMountContext } from 'kibana/public';
 import {
-  AppStateProvider,
-  AppState,
   configureAppAngularModule,
   createTopNavDirective,
   createTopNavHelper,
@@ -116,12 +114,6 @@ function createLocalStateModule() {
       'app/visualize/Promise',
       'app/visualize/PersistedState',
     ])
-    .factory('AppState', function(Private: IPrivate) {
-      return Private(AppStateProvider);
-    })
-    .service('getAppState', function(Private: IPrivate) {
-      return Private<AppState>(AppStateProvider).getAppState;
-    })
     .service('globalState', function(Private: IPrivate) {
       return Private(GlobalStateProvider);
     });
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.html b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.html
index 6190b92c9be3e..4979d9dc89a0c 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.html
+++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.html
@@ -2,7 +2,7 @@
   <!-- Linked search. -->
   <div
       ng-show="isVisible"
-      ng-if="vis.type.requiresSearch && state.linked"
+      ng-if="vis.type.requiresSearch && linked"
       class="fullWidth visEditor__linkedMessage"
     >
     <div class="kuiVerticalRhythmSmall">
@@ -42,9 +42,9 @@
     show-filter-bar="showFilterBar() && isVisible"
     show-date-picker="showQueryBarTimePicker()"
     show-auto-refresh-only="!showQueryBarTimePicker()"
-    query="state.query"
+    query="query"
     saved-query="savedQuery"
-    screen-title="state.vis.title"
+    screen-title="vis.title"
     on-query-submit="updateQueryAndFetch"
     index-patterns="[indexPattern]"
     filters="filters"
@@ -97,7 +97,9 @@
     ui-state="uiState"
     time-range="timeRange"
     filters="filters"
-    query="query"/>
+    query="query"
+    app-state="appState"
+    />
 
   <h1
     class="euiScreenReaderOnly"
@@ -117,6 +119,7 @@
     filters="filters"
     query="query"
     class="visEditor__content"
+    app-state="appState"
   />
 
 </visualize-app>
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js
index 409d4b41fbe69..415949f88e9d1 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js
+++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js
@@ -24,7 +24,7 @@ import { i18n } from '@kbn/i18n';
 
 import React from 'react';
 import { FormattedMessage } from '@kbn/i18n/react';
-import { migrateAppState } from './lib';
+import { makeStateful, useVisualizeAppState } from './lib';
 import { VisualizeConstants } from '../visualize_constants';
 import { getEditBreadcrumbs } from '../breadcrumbs';
 
@@ -45,7 +45,6 @@ import {
   absoluteToParsedUrl,
   KibanaParsedUrl,
   migrateLegacyQuery,
-  stateMonitorFactory,
   DashboardConstants,
 } from '../../legacy_imports';
 
@@ -68,15 +67,14 @@ function VisualizeAppController(
   $scope,
   $element,
   $route,
-  AppState,
   $window,
   $injector,
   $timeout,
   kbnUrl,
   redirectWhenMissing,
   Promise,
-  getAppState,
-  globalState
+  globalState,
+  config
 ) {
   const {
     indexPatterns,
@@ -99,7 +97,6 @@ function VisualizeAppController(
     setActiveUrl,
   } = getServices();
 
-  const filterStateManager = new FilterStateManager(globalState, getAppState, filterManager);
   // Retrieve the resolved SavedVis instance.
   const savedVis = $route.current.locals.savedVis;
   const _applyVis = () => {
@@ -113,9 +110,9 @@ function VisualizeAppController(
 
   $scope.vis = vis;
 
-  const $appStatus = (this.appStatus = {
+  const $appStatus = {
     dirty: !savedVis.id,
-  });
+  };
 
   vis.on('dirtyStateChange', ({ isDirty }) => {
     vis.dirty = isDirty;
@@ -265,53 +262,61 @@ function VisualizeAppController(
     },
   ];
 
-  let stateMonitor;
-
   if (savedVis.id) {
     chrome.docTitle.change(savedVis.title);
   }
 
+  const defaultQuery = {
+    query: '',
+    language:
+      localStorage.get('kibana.userQueryLanguage') || uiSettings.get('search:queryLanguage'),
+  };
+
   // Extract visualization state with filtered aggs. You can see these filtered aggs in the URL.
   // Consists of things like aggs, params, listeners, title, type, etc.
   const savedVisState = vis.getState();
   const stateDefaults = {
     uiState: savedVis.uiStateJSON ? JSON.parse(savedVis.uiStateJSON) : {},
-    linked: !!savedVis.savedSearchId,
-    query: searchSource.getOwnField('query') || {
-      query: '',
-      language:
-        localStorage.get('kibana.userQueryLanguage') || uiSettings.get('search:queryLanguage'),
-    },
+    query: searchSource.getOwnField('query') || defaultQuery,
     filters: searchSource.getOwnField('filter') || [],
     vis: savedVisState,
+    linked: !!savedVis.savedSearchId,
   };
 
-  // Instance of app_state.js.
-  const $state = (function initState() {
-    // This is used to sync visualization state with the url when `appState.save()` is called.
-    const appState = new AppState(stateDefaults);
-
-    // Initializing appState does two things - first it translates the defaults into AppState,
-    // second it updates appState based on the url (the url trumps the defaults). This means if
-    // we update the state format at all and want to handle BWC, we must not only migrate the
-    // data stored with saved vis, but also any old state in the url.
-    migrateAppState(appState);
-
-    // The savedVis is pulled from elasticsearch, but the appState is pulled from the url, with the
-    // defaults applied. If the url was from a previous session which included modifications to the
-    // appState then they won't be equal.
-    if (!angular.equals(appState.vis, savedVisState)) {
-      Promise.try(function() {
-        vis.setState(appState.vis);
-      }).catch(
-        redirectWhenMissing({
-          'index-pattern-field': '/visualize',
-        })
-      );
-    }
+  const useHash = config.get('state:storeInSessionStorage');
+  const { stateContainer, stopStateSync } = useVisualizeAppState({
+    useHash,
+    stateDefaults,
+  });
+
+  const filterStateManager = new FilterStateManager(
+    globalState,
+    () => {
+      // Temporary AppState replacement
+      return {
+        set filters(_filters) {
+          stateContainer.transitions.set('filters', _filters);
+        },
+        get filters() {
+          return stateContainer.getState().filters;
+        },
+      };
+    },
+    filterManager
+  );
 
-    return appState;
-  })();
+  // The savedVis is pulled from elasticsearch, but the appState is pulled from the url, with the
+  // defaults applied. If the url was from a previous session which included modifications to the
+  // appState then they won't be equal.
+  if (!_.isEqual(stateContainer.getState().vis, stateDefaults.vis)) {
+    try {
+      vis.setState(stateContainer.getState().vis);
+    } catch {
+      redirectWhenMissing({
+        'index-pattern-field': '/visualize',
+      });
+    }
+  }
 
   $scope.filters = filterManager.getFilters();
 
@@ -330,8 +335,6 @@ function VisualizeAppController(
   );
 
   function init() {
-    // export some objects
-    $scope.savedVis = savedVis;
     if (vis.indexPattern) {
       $scope.indexPattern = vis.indexPattern;
     } else {
@@ -340,14 +343,28 @@ function VisualizeAppController(
       });
     }
 
+    const initialState = stateContainer.getState();
+
+    $scope.appState = {
+      // mock implementation of the legacy appState.save()
+      // this could be even replaced by passing only "updateAppState" callback
+      save() {
+        stateContainer.transitions.updateVisState(vis.getState());
+      },
+    };
+
+    // Create a PersistedState instance for uiState.
+    const { persistedState, unsubscribePersisted, persistOnChange } = makeStateful(
+      'uiState',
+      stateContainer
+    );
+    $scope.uiState = persistedState;
+    $scope.savedVis = savedVis;
+    $scope.query = initialState.query;
+    $scope.linked = initialState.linked;
     $scope.searchSource = searchSource;
-    $scope.state = $state;
     $scope.refreshInterval = timefilter.getRefreshInterval();
 
-    // Create a PersistedState instance.
-    $scope.uiState = $state.makeStateful('uiState');
-    $scope.appStatus = $appStatus;
-
     const addToDashMode =
       $route.current.params[DashboardConstants.ADD_VISUALIZATION_TO_DASHBOARD_MODE_PARAM];
     kbnUrl.removeParam(DashboardConstants.ADD_VISUALIZATION_TO_DASHBOARD_MODE_PARAM);
@@ -372,22 +389,23 @@ function VisualizeAppController(
     $scope.timeRange = timefilter.getTime();
     $scope.opts = _.pick($scope, 'savedVis', 'isAddToDashMode');
 
-    stateMonitor = stateMonitorFactory.create($state, stateDefaults);
-    stateMonitor.ignoreProps(['vis.listeners']).onChange(status => {
-      $appStatus.dirty = status.dirty || !savedVis.id;
-    });
+    const unsubscribeStateUpdates = stateContainer.subscribe(state => {
+      const newQuery = migrateLegacyQuery(state.query);
+      if (!_.isEqual(state.query, newQuery)) {
+        stateContainer.transitions.set('query', newQuery);
+      }
+      persistOnChange(state);
 
-    $scope.$watch('state.query', (newQuery, oldQuery) => {
-      if (!_.isEqual(newQuery, oldQuery)) {
-        const query = migrateLegacyQuery(newQuery);
-        if (!_.isEqual(query, newQuery)) {
-          $state.query = query;
-        }
-        $scope.fetch();
+      // if the browser history was changed manually we need to reflect changes in the editor
+      if (!_.isEqual(vis.getState(), state.vis)) {
+        vis.setState(state.vis);
+        vis.forceReload();
+        vis.emit('updateEditor');
       }
-    });
 
-    $state.replace();
+      $appStatus.dirty = true;
+      $scope.fetch();
+    });
 
     const updateTimeRange = () => {
       $scope.timeRange = timefilter.getTime();
@@ -419,10 +437,11 @@ function VisualizeAppController(
 
     // update the searchSource when query updates
     $scope.fetch = function() {
-      $state.save();
-      $scope.query = $state.query;
-      savedVis.searchSource.setField('query', $state.query);
-      savedVis.searchSource.setField('filter', $state.filters);
+      const { query, filters, linked } = stateContainer.getState();
+      $scope.query = query;
+      $scope.linked = linked;
+      savedVis.searchSource.setField('query', query);
+      savedVis.searchSource.setField('filter', filters);
       $scope.$broadcast('render');
     };
 
@@ -446,10 +465,13 @@ function VisualizeAppController(
         $scope._handler.destroy();
       }
       savedVis.destroy();
-      stateMonitor.destroy();
       filterStateManager.destroy();
       subscriptions.unsubscribe();
       $scope.vis.off('apply', _applyVis);
+
+      unsubscribePersisted();
+      unsubscribeStateUpdates();
+      stopStateSync();
     });
 
     $timeout(() => {
@@ -459,10 +481,10 @@ function VisualizeAppController(
 
   $scope.updateQueryAndFetch = function({ query, dateRange }) {
     const isUpdate =
-      (query && !_.isEqual(query, $state.query)) ||
+      (query && !_.isEqual(query, stateContainer.getState().query)) ||
       (dateRange && !_.isEqual(dateRange, $scope.timeRange));
 
-    $state.query = query;
+    stateContainer.transitions.set('query', query);
     timefilter.setTime(dateRange);
 
     // If nothing has changed, trigger the fetch manually, otherwise it will happen as a result of the changes
@@ -488,20 +510,13 @@ function VisualizeAppController(
 
   $scope.onClearSavedQuery = () => {
     delete $scope.savedQuery;
-    delete $state.savedQuery;
-    $state.query = {
-      query: '',
-      language:
-        localStorage.get('kibana.userQueryLanguage') || uiSettings.get('search:queryLanguage'),
-    };
+    stateContainer.transitions.removeSavedQuery(defaultQuery);
     filterManager.setFilters(filterManager.getGlobalFilters());
-    $state.save();
     $scope.fetch();
   };
 
   const updateStateFromSavedQuery = savedQuery => {
-    $state.query = savedQuery.attributes.query;
-    $state.save();
+    stateContainer.transitions.set('query', savedQuery.attributes.query);
 
     const savedQueryFilters = savedQuery.attributes.filters || [];
     const globalFilters = filterManager.getGlobalFilters();
@@ -520,44 +535,38 @@ function VisualizeAppController(
     $scope.fetch();
   };
 
+  // update the query if savedQuery is stored
+  if (stateContainer.getState().savedQuery) {
+    savedQueryService.getSavedQuery(stateContainer.getState().savedQuery).then(savedQuery => {
+      $scope.$evalAsync(() => {
+        $scope.savedQuery = savedQuery;
+      });
+    });
+  }
+
   $scope.$watch('savedQuery', newSavedQuery => {
     if (!newSavedQuery) return;
-    $state.savedQuery = newSavedQuery.id;
-    $state.save();
+    stateContainer.transitions.set('savedQuery', newSavedQuery.id);
 
     updateStateFromSavedQuery(newSavedQuery);
   });
 
-  $scope.$watch('state.savedQuery', newSavedQueryId => {
-    if (!newSavedQueryId) {
-      $scope.savedQuery = undefined;
-      return;
-    }
-    if (!$scope.savedQuery || newSavedQueryId !== $scope.savedQuery.id) {
-      savedQueryService.getSavedQuery(newSavedQueryId).then(savedQuery => {
-        $scope.$evalAsync(() => {
-          $scope.savedQuery = savedQuery;
-          updateStateFromSavedQuery(savedQuery);
-        });
-      });
-    }
-  });
-
   /**
    * Called when the user clicks "Save" button.
    */
   function doSave(saveOptions) {
     // vis.title was not bound and it's needed to reflect title into visState
-    $state.vis.title = savedVis.title;
-    $state.vis.type = savedVis.type || $state.vis.type;
-    savedVis.visState = $state.vis;
+    stateContainer.transitions.setVis({
+      title: savedVis.title,
+      type: savedVis.type || stateContainer.getState().vis.type,
+    });
+    savedVis.visState = stateContainer.getState().vis;
     savedVis.uiStateJSON = angular.toJson($scope.uiState.getChanges());
+    $appStatus.dirty = false;
 
     return savedVis.save(saveOptions).then(
       function(id) {
         $scope.$evalAsync(() => {
-          stateMonitor.setInitialState($state.toJSON());
-
           if (id) {
             toastNotifications.addSuccess({
               title: i18n.translate(
@@ -601,8 +610,6 @@ function VisualizeAppController(
               chrome.setBreadcrumbs($injector.invoke(getEditBreadcrumbs));
               savedVis.vis.title = savedVis.title;
               savedVis.vis.description = savedVis.description;
-              // it's needed to save the state to update url string
-              $state.save();
             } else {
               kbnUrl.change(`${VisualizeConstants.EDIT_PATH}/{{id}}`, { id: savedVis.id });
             }
@@ -632,9 +639,8 @@ function VisualizeAppController(
   }
 
   $scope.unlink = function() {
-    if (!$state.linked) return;
+    if (!$scope.linked) return;
 
-    $state.linked = false;
     const searchSourceParent = searchSource.getParent();
     const searchSourceGrandparent = searchSourceParent.getParent();
 
@@ -645,8 +651,10 @@ function VisualizeAppController(
       _.union(searchSource.getOwnField('filter'), searchSourceParent.getOwnField('filter'))
     );
 
-    $state.query = searchSourceParent.getField('query');
-    $state.filters = searchSourceParent.getField('filter');
+    stateContainer.transitions.unlinkSavedSearch(
+      searchSourceParent.getField('query'),
+      searchSourceParent.getField('filter')
+    );
     searchSource.setField('index', searchSourceParent.getField('index'));
     searchSource.setParent(searchSourceGrandparent);
 
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/index.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/index.ts
similarity index 87%
rename from src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/index.js
rename to src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/index.ts
index 42284c9a03dcd..fa5b91b00edaf 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/index.js
+++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/index.ts
@@ -17,4 +17,5 @@
  * under the License.
  */
 
-export { migrateAppState } from './migrate_app_state';
+export { useVisualizeAppState } from './visualize_app_state';
+export { makeStateful } from './make_stateful';
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/make_stateful.ts b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/make_stateful.ts
new file mode 100644
index 0000000000000..137d4de1fe9a8
--- /dev/null
+++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/make_stateful.ts
@@ -0,0 +1,58 @@
+/*
+ * 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 { PersistedState } from '../../../legacy_imports';
+import { ReduxLikeStateContainer } from '../../../../../../../../plugins/kibana_utils/public';
+import { VisualizeAppState, VisualizeAppStateTransitions } from '../../types';
+
+/**
+ * @returns Create a PersistedState instance, initialize state changes subscriber/unsubscriber
+ */
+export function makeStateful(
+  prop: keyof VisualizeAppState,
+  stateContainer: ReduxLikeStateContainer<VisualizeAppState, VisualizeAppStateTransitions>
+) {
+  // set up the persistedState state
+  const persistedState = new PersistedState();
+
+  // update the appState when the stateful instance changes
+  const updateOnChange = function() {
+    stateContainer.transitions.set(prop, persistedState.getChanges());
+  };
+
+  const handlerOnChange = (method: 'on' | 'off') =>
+    persistedState[method]('change', updateOnChange);
+
+  handlerOnChange('on');
+  const unsubscribePersisted = () => handlerOnChange('off');
+
+  // update the stateful object when the app state changes
+  const persistOnChange = function(state: VisualizeAppState) {
+    if (state[prop]) {
+      persistedState.set(state[prop]);
+    }
+  };
+
+  const appState = stateContainer.getState();
+
+  // if the thing we're making stateful has an appState value, write to persisted state
+  if (appState[prop]) persistedState.setSilent(appState[prop]);
+
+  return { persistedState, unsubscribePersisted, persistOnChange };
+}
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/migrate_app_state.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/migrate_app_state.ts
similarity index 61%
rename from src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/migrate_app_state.js
rename to src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/migrate_app_state.ts
index 049ce048239db..7e09aece52e09 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/migrate_app_state.js
+++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/migrate_app_state.ts
@@ -18,6 +18,7 @@
  */
 
 import { get, omit } from 'lodash';
+import { VisualizeAppState } from '../../types';
 
 /**
  * Creates a new instance of AppState based on the table vis state.
@@ -25,37 +26,41 @@ import { get, omit } from 'lodash';
  * Dashboards have a similar implementation; see
  * core_plugins/kibana/public/dashboard/lib/migrate_app_state
  *
- * @param appState {AppState} AppState class to instantiate
+ * @param appState {VisualizeAppState}
  */
-export function migrateAppState(appState) {
+export function migrateAppState(appState: VisualizeAppState) {
   // For BWC in pre 7.0 versions where table visualizations could have multiple aggs
   // with `schema === 'split'`. This ensures that bookmarked URLs with deprecated params
   // are rewritten to the correct state. See core_plugins/table_vis/migrations.
   if (appState.vis.type !== 'table') {
-    return;
+    return appState;
   }
 
-  const visAggs = get(appState, 'vis.aggs', []);
-  let splitCount = 0;
-  const migratedAggs = visAggs.map(agg => {
-    if (agg.schema !== 'split') {
+  const visAggs: any = get<VisualizeAppState>(appState, 'vis.aggs');
+
+  if (visAggs) {
+    let splitCount = 0;
+    const migratedAggs = visAggs.map((agg: any) => {
+      if (agg.schema !== 'split') {
+        return agg;
+      }
+
+      splitCount++;
+      if (splitCount === 1) {
+        return agg; // leave the first split agg unchanged
+      }
+      agg.schema = 'bucket';
+      // the `row` param is exclusively used by split aggs, so we remove it
+      agg.params = omit(agg.params, ['row']);
       return agg;
-    }
+    });
 
-    splitCount++;
-    if (splitCount === 1) {
-      return agg; // leave the first split agg unchanged
+    if (splitCount <= 1) {
+      return appState; // do nothing; we only want to touch tables with multiple split aggs
     }
-    agg.schema = 'bucket';
-    // the `row` param is exclusively used by split aggs, so we remove it
-    agg.params = omit(agg.params, ['row']);
-    return agg;
-  });
-
-  if (splitCount <= 1) {
-    return; // do nothing; we only want to touch tables with multiple split aggs
+
+    appState.vis.aggs = migratedAggs;
   }
 
-  appState.vis.aggs = migratedAggs;
-  appState.save();
+  return appState;
 }
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/visualize_app_state.ts b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/visualize_app_state.ts
new file mode 100644
index 0000000000000..d8de81193d857
--- /dev/null
+++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/lib/visualize_app_state.ts
@@ -0,0 +1,112 @@
+/*
+ * 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 { createHashHistory } from 'history';
+import { isFunction, omit } from 'lodash';
+
+import { migrateAppState } from './migrate_app_state';
+import {
+  createKbnUrlStateStorage,
+  createStateContainer,
+  syncState,
+} from '../../../../../../../../plugins/kibana_utils/public';
+import { PureVisState, VisualizeAppState, VisualizeAppStateTransitions } from '../../types';
+
+const STATE_STORAGE_KEY = '_a';
+
+interface Arguments {
+  useHash: boolean;
+  stateDefaults: VisualizeAppState;
+}
+
+function toObject(state: PureVisState): PureVisState {
+  return omit(state, (value, key: string) => {
+    return key.charAt(0) === '$' || key.charAt(0) === '_' || isFunction(value);
+  });
+}
+
+export function useVisualizeAppState({ useHash, stateDefaults }: Arguments) {
+  const history = createHashHistory();
+  const kbnUrlStateStorage = createKbnUrlStateStorage({
+    useHash,
+    history,
+  });
+  const urlState = kbnUrlStateStorage.get<VisualizeAppState>(STATE_STORAGE_KEY);
+  const initialState = migrateAppState({
+    ...stateDefaults,
+    ...urlState,
+  });
+
+  /*
+    make sure url ('_a') matches initial state
+    Initializing appState does two things - first it translates the defaults into AppState,
+    second it updates appState based on the url (the url trumps the defaults). This means if
+    we update the state format at all and want to handle BWC, we must not only migrate the
+    data stored with saved vis, but also any old state in the url.
+  */
+  kbnUrlStateStorage.set(STATE_STORAGE_KEY, initialState, { replace: true });
+
+  const stateContainer = createStateContainer<VisualizeAppState, VisualizeAppStateTransitions>(
+    initialState,
+    {
+      set: state => (prop, value) => ({ ...state, [prop]: value }),
+      setVis: state => vis => ({
+        ...state,
+        vis: {
+          ...state.vis,
+          ...vis,
+        },
+      }),
+      removeSavedQuery: state => defaultQuery => {
+        const { savedQuery, ...rest } = state;
+
+        return {
+          ...rest,
+          query: defaultQuery,
+        };
+      },
+      unlinkSavedSearch: state => (query, filters) => ({
+        ...state,
+        query,
+        filters,
+        linked: false,
+      }),
+      updateVisState: state => newVisState => ({ ...state, vis: toObject(newVisState) }),
+    }
+  );
+
+  const { start: startStateSync, stop: stopStateSync } = syncState({
+    storageKey: STATE_STORAGE_KEY,
+    stateContainer: {
+      ...stateContainer,
+      set: state => {
+        if (state) {
+          // syncState utils requires to handle incoming "null" value
+          stateContainer.set(state);
+        }
+      },
+    },
+    stateStorage: kbnUrlStateStorage,
+  });
+
+  // start syncing the appState with the ('_a') url
+  startStateSync();
+
+  return { stateContainer, stopStateSync };
+}
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization.js
index 502bd6e56fb1f..6acdb0abdd0b5 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization.js
+++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization.js
@@ -18,7 +18,7 @@
  */
 
 export function initVisualizationDirective(app, deps) {
-  app.directive('visualizationEmbedded', function($timeout, getAppState) {
+  app.directive('visualizationEmbedded', function($timeout) {
     return {
       restrict: 'E',
       scope: {
@@ -27,6 +27,7 @@ export function initVisualizationDirective(app, deps) {
         timeRange: '=',
         filters: '=',
         query: '=',
+        appState: '=',
       },
       link: function($scope, element) {
         $scope.renderFunction = async () => {
@@ -37,7 +38,7 @@ export function initVisualizationDirective(app, deps) {
                 timeRange: $scope.timeRange,
                 filters: $scope.filters || [],
                 query: $scope.query,
-                appState: getAppState(),
+                appState: $scope.appState,
                 uiState: $scope.uiState,
               });
             $scope._handler.render(element[0]);
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization_editor.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization_editor.js
index 8032152f88173..c40a10115ae4e 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization_editor.js
+++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization_editor.js
@@ -18,7 +18,7 @@
  */
 
 export function initVisEditorDirective(app, deps) {
-  app.directive('visualizationEditor', function($timeout, getAppState) {
+  app.directive('visualizationEditor', function($timeout) {
     return {
       restrict: 'E',
       scope: {
@@ -27,6 +27,7 @@ export function initVisEditorDirective(app, deps) {
         timeRange: '=',
         filters: '=',
         query: '=',
+        appState: '=',
       },
       link: function($scope, element) {
         const Editor = $scope.savedObj.vis.type.editor;
@@ -41,7 +42,7 @@ export function initVisEditorDirective(app, deps) {
             timeRange: $scope.timeRange,
             filters: $scope.filters,
             query: $scope.query,
-            appState: getAppState(),
+            appState: $scope.appState,
           });
         };
 
diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/types.d.ts b/src/legacy/core_plugins/kibana/public/visualize/np_ready/types.d.ts
index 524bc4b3196b7..139c247aa29cc 100644
--- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/types.d.ts
+++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/types.d.ts
@@ -20,10 +20,37 @@
 import { TimeRange, Query, Filter, DataPublicPluginStart } from 'src/plugins/data/public';
 import { IEmbeddableStart } from 'src/plugins/embeddable/public';
 import { LegacyCoreStart } from 'kibana/public';
-import { VisSavedObject, AppState, PersistedState } from '../legacy_imports';
+import { VisState, Vis } from 'src/legacy/core_plugins/visualizations/public';
+import { VisSavedObject, PersistedState } from '../legacy_imports';
+
+export type PureVisState = ReturnType<Vis['getCurrentState']>;
+
+export interface VisualizeAppState {
+  filters: Filter[];
+  uiState: PersistedState;
+  vis: PureVisState;
+  query: Query;
+  savedQuery?: string;
+  linked: boolean;
+}
+
+export interface VisualizeAppStateTransitions {
+  set: (
+    state: VisualizeAppState
+  ) => <T extends keyof VisualizeAppState>(
+    prop: T,
+    value: VisualizeAppState[T]
+  ) => VisualizeAppState;
+  setVis: (state: VisualizeAppState) => (vis: Partial<PureVisState>) => VisualizeAppState;
+  removeSavedQuery: (state: VisualizeAppState) => (defaultQuery: Query) => VisualizeAppState;
+  unlinkSavedSearch: (
+    state: VisualizeAppState
+  ) => (query: Query, filters: Filter[]) => VisualizeAppState;
+  updateVisState: (state: VisualizeAppState) => (vis: PureVisState) => VisualizeAppState;
+}
 
 export interface EditorRenderProps {
-  appState: AppState;
+  appState: { save(): void };
   core: LegacyCoreStart;
   data: DataPublicPluginStart;
   embeddable: IEmbeddableStart;
diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/sidebar.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/sidebar.tsx
index e33e83fd19fec..8615bcdd1bfbd 100644
--- a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/sidebar.tsx
+++ b/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/sidebar.tsx
@@ -26,7 +26,7 @@ import { Vis } from 'src/legacy/core_plugins/visualizations/public';
 import { PersistedState, AggGroupNames } from '../../legacy_imports';
 import { DefaultEditorNavBar, OptionTab } from './navbar';
 import { DefaultEditorControls } from './controls';
-import { setStateParamValue, useEditorReducer, useEditorFormState } from './state';
+import { setStateParamValue, useEditorReducer, useEditorFormState, discardChanges } from './state';
 import { DefaultEditorAggCommonProps } from '../agg_common_props';
 
 interface DefaultEditorSideBarProps {
@@ -104,15 +104,26 @@ function DefaultEditorSideBar({
   );
 
   useEffect(() => {
-    vis.on('dirtyStateChange', ({ isDirty: dirty }: { isDirty: boolean }) => {
+    const changeHandler = ({ isDirty: dirty }: { isDirty: boolean }) => {
       setDirty(dirty);
 
       if (!dirty) {
         resetValidity();
       }
-    });
+    };
+    vis.on('dirtyStateChange', changeHandler);
+
+    return () => vis.off('dirtyStateChange', changeHandler);
   }, [resetValidity, vis]);
 
+  // subscribe on external vis changes using browser history, for example press back button
+  useEffect(() => {
+    const resetHandler = () => dispatch(discardChanges(vis));
+    vis.on('updateEditor', resetHandler);
+
+    return () => vis.off('updateEditor', resetHandler);
+  }, [dispatch, vis]);
+
   const dataTabProps = {
     dispatch,
     formIsTouched: formState.touched,
diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_editor.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_editor.js
index b2dd1813e6d20..0263f5b2c851c 100644
--- a/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_editor.js
+++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_editor.js
@@ -82,7 +82,6 @@ export class VisEditor extends Component {
     // This check should be redundant, since this method should only be called when we're in editor
     // mode where there's also an appState passed into us.
     if (this.props.appState) {
-      this.props.appState.vis = this.props.vis.getState();
       this.props.appState.save();
     }
   }, VIS_STATE_DEBOUNCE_DELAY);
diff --git a/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts
index 9e388832283fa..e7ca5ea803701 100644
--- a/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts
+++ b/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts
@@ -23,7 +23,6 @@ import { Subscription } from 'rxjs';
 import * as Rx from 'rxjs';
 import { buildPipeline } from 'ui/visualize/loader/pipeline_helpers';
 import { SavedObject } from 'ui/saved_objects/types';
-import { AppState } from 'ui/state_management/app_state';
 import { npStart } from 'ui/new_platform';
 import { IExpressionLoaderParams } from 'src/plugins/expressions/public';
 import { VISUALIZE_EMBEDDABLE_TYPE } from './constants';
@@ -68,7 +67,7 @@ export interface VisualizeEmbeddableConfiguration {
   indexPatterns?: IIndexPattern[];
   editUrl: string;
   editable: boolean;
-  appState?: AppState;
+  appState?: { save(): void };
   uiState?: PersistedState;
 }
 
@@ -79,7 +78,7 @@ export interface VisualizeInput extends EmbeddableInput {
   vis?: {
     colors?: { [key: string]: string };
   };
-  appState?: AppState;
+  appState?: { save(): void };
   uiState?: PersistedState;
 }
 
@@ -95,7 +94,7 @@ type ExpressionLoader = InstanceType<typeof npStart.plugins.expressions.Expressi
 export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOutput> {
   private handler?: ExpressionLoader;
   private savedVisualization: VisSavedObject;
-  private appState: AppState | undefined;
+  private appState: { save(): void } | undefined;
   private uiState: PersistedState;
   private timeRange?: TimeRange;
   private query?: Query;
@@ -389,7 +388,6 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut
 
   private handleVisUpdate = async () => {
     if (this.appState) {
-      this.appState.vis = this.savedVisualization.vis.getState();
       this.appState.save();
     }
 
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.test.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.test.ts
index d1017de35474a..f73dc3e19d0ef 100644
--- a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.test.ts
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.test.ts
@@ -26,7 +26,7 @@ import {
   SchemaConfig,
   Schemas,
 } from './build_pipeline';
-import { Vis, VisState } from '..';
+import { Vis } from '..';
 import { IAggConfig } from '../../../legacy_imports';
 import { searchSourceMock } from '../../../legacy_mocks';
 
@@ -83,7 +83,7 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
   });
 
   describe('buildPipelineVisFunction', () => {
-    let visStateDef: VisState;
+    let visStateDef: ReturnType<Vis['getCurrentState']>;
     let schemaConfig: SchemaConfig;
     let schemasDef: Schemas;
     let uiState: any;
@@ -94,7 +94,7 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
         // @ts-ignore
         type: 'type',
         params: {},
-      };
+      } as ReturnType<Vis['getCurrentState']>;
 
       schemaConfig = {
         accessor: 0,
@@ -349,7 +349,7 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
 
   describe('buildPipeline', () => {
     it('calls toExpression on vis_type if it exists', async () => {
-      const vis: Vis = {
+      const vis = ({
         getCurrentState: () => {},
         getUiState: () => null,
         isHierarchical: () => false,
@@ -360,7 +360,7 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
         type: {
           toExpression: () => 'testing custom expressions',
         },
-      };
+      } as unknown) as Vis;
       const expression = await buildPipeline(vis, { searchSource: searchSourceMock });
       expect(expression).toMatchSnapshot();
     });
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.ts
index 04a296a888e87..025eef834ca86 100644
--- a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.ts
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.ts
@@ -28,7 +28,7 @@ import {
   isDateHistogramBucketAggConfig,
   createFormat,
 } from '../../../legacy_imports';
-import { Vis, VisParams, VisState } from '..';
+import { Vis, VisParams } from '..';
 
 interface SchemaConfigParams {
   precision?: number;
@@ -59,7 +59,7 @@ export interface Schemas {
 }
 
 type buildVisFunction = (
-  visState: VisState,
+  visState: ReturnType<Vis['getCurrentState']>,
   schemas: Schemas,
   uiState: any,
   meta?: { savedObjectId?: string }
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.ts
index 59a8013523ef6..19375e25a9fb7 100644
--- a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.ts
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.ts
@@ -23,6 +23,14 @@ import { Status } from './legacy/update_status';
 
 export interface Vis {
   type: VisType;
+  getCurrentState: (
+    includeDisabled?: boolean
+  ) => {
+    title: string;
+    type: string;
+    params: VisParams;
+    aggs: Array<{ [key: string]: any }>;
+  };
 
   // Since we haven't typed everything here yet, we basically "any" the rest
   // of that interface. This should be removed as soon as this type definition
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_impl.d.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_impl.d.ts
index 45d65efb5dcdf..f9b7db5c02d93 100644
--- a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_impl.d.ts
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_impl.d.ts
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-import { Vis, VisState } from './vis';
+import { Vis, VisState, VisParams } from './vis';
 import { VisType } from './types';
 import { IIndexPattern } from '../../../../../../plugins/data/common';
 
@@ -35,6 +35,14 @@ export declare class VisImpl implements Vis {
   constructor(indexPattern: IIndexPattern, visState?: InitVisStateType);
 
   type: VisType;
+  getCurrentState: (
+    includeDisabled?: boolean
+  ) => {
+    title: string;
+    type: string;
+    params: VisParams;
+    aggs: Array<{ [key: string]: any }>;
+  };
 
   // Since we haven't typed everything here yet, we basically "any" the rest
   // of that interface. This should be removed as soon as this type definition
diff --git a/test/functional/apps/visualize/_heatmap_chart.js b/test/functional/apps/visualize/_heatmap_chart.js
index 2cea861d0f64d..cbf7ab8df6831 100644
--- a/test/functional/apps/visualize/_heatmap_chart.js
+++ b/test/functional/apps/visualize/_heatmap_chart.js
@@ -96,6 +96,8 @@ export default function({ getService, getPageObjects }) {
       await PageObjects.visEditor.clickOptionsTab();
       await PageObjects.visEditor.changeHeatmapColorNumbers(6);
       await PageObjects.visEditor.clickGo();
+      await PageObjects.visChart.waitForVisualizationRenderingStabilized();
+
       const legends = await PageObjects.visChart.getLegendEntries();
       const expectedLegends = [
         '0 - 267',
@@ -121,9 +123,9 @@ export default function({ getService, getPageObjects }) {
       log.debug('customize 2 last ranges');
       await PageObjects.visEditor.setCustomRangeByIndex(6, '650', '720');
       await PageObjects.visEditor.setCustomRangeByIndex(7, '800', '905');
+      await PageObjects.visEditor.clickGo();
 
       await PageObjects.visChart.waitForVisualizationRenderingStabilized();
-      await PageObjects.visEditor.clickGo();
       const legends = await PageObjects.visChart.getLegendEntries();
       const expectedLegends = [
         '0 - 100',

From dd8eb62d9aad1e4e279fc338a242a8a39d0422b6 Mon Sep 17 00:00:00 2001
From: Mikhail Shustov <restrry@gmail.com>
Date: Thu, 20 Feb 2020 12:05:08 +0100
Subject: [PATCH 090/174] Test NP logging config reload on SIGHUP (#57681)

* TSify reload config test and simplify logic

* get rid of mutable config in tests

* increase timeouts

* address comments
---
 .../reload_logging_config/kibana.test.yml     |   3 +
 .../kibana_log_console.test.yml               |  22 ++
 .../kibana_log_file.test.yml                  |  22 ++
 .../reload_logging_config.test.js             | 224 ---------------
 .../reload_logging_config.test.ts             | 263 ++++++++++++++++++
 5 files changed, 310 insertions(+), 224 deletions(-)
 create mode 100644 src/cli/serve/integration_tests/__fixtures__/reload_logging_config/kibana_log_console.test.yml
 create mode 100644 src/cli/serve/integration_tests/__fixtures__/reload_logging_config/kibana_log_file.test.yml
 delete mode 100644 src/cli/serve/integration_tests/reload_logging_config.test.js
 create mode 100644 src/cli/serve/integration_tests/reload_logging_config.test.ts

diff --git a/src/cli/serve/integration_tests/__fixtures__/reload_logging_config/kibana.test.yml b/src/cli/serve/integration_tests/__fixtures__/reload_logging_config/kibana.test.yml
index 23f33940283c0..594c2efc8adc9 100644
--- a/src/cli/serve/integration_tests/__fixtures__/reload_logging_config/kibana.test.yml
+++ b/src/cli/serve/integration_tests/__fixtures__/reload_logging_config/kibana.test.yml
@@ -1,4 +1,5 @@
 server:
+  autoListen: false
   port: 8274
 logging:
   json: true
@@ -6,3 +7,5 @@ optimize:
   enabled: false
 plugins:
   initialize: false
+migrations:
+  skip: true
diff --git a/src/cli/serve/integration_tests/__fixtures__/reload_logging_config/kibana_log_console.test.yml b/src/cli/serve/integration_tests/__fixtures__/reload_logging_config/kibana_log_console.test.yml
new file mode 100644
index 0000000000000..33dd4787efad9
--- /dev/null
+++ b/src/cli/serve/integration_tests/__fixtures__/reload_logging_config/kibana_log_console.test.yml
@@ -0,0 +1,22 @@
+server:
+  autoListen: false
+  port: 8274
+logging:
+  loggers:
+    - context: root
+      appenders:
+        - console
+      level: debug
+  appenders:
+    console:
+      kind: console
+      layout:
+        kind: json
+  root:
+    level: debug
+optimize:
+  enabled: false
+plugins:
+  initialize: false
+migrations:
+  skip: true
diff --git a/src/cli/serve/integration_tests/__fixtures__/reload_logging_config/kibana_log_file.test.yml b/src/cli/serve/integration_tests/__fixtures__/reload_logging_config/kibana_log_file.test.yml
new file mode 100644
index 0000000000000..f5148899ff854
--- /dev/null
+++ b/src/cli/serve/integration_tests/__fixtures__/reload_logging_config/kibana_log_file.test.yml
@@ -0,0 +1,22 @@
+server:
+  autoListen: false
+  port: 8274
+logging:
+  loggers:
+    - context: root
+      appenders:
+        - file
+      level: debug
+  appenders:
+    file:
+      kind: file
+      layout:
+        kind: pattern
+  root:
+    level: debug
+optimize:
+  enabled: false
+plugins:
+  initialize: false
+migrations:
+  skip: true
diff --git a/src/cli/serve/integration_tests/reload_logging_config.test.js b/src/cli/serve/integration_tests/reload_logging_config.test.js
deleted file mode 100644
index 82d514877aff6..0000000000000
--- a/src/cli/serve/integration_tests/reload_logging_config.test.js
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { spawn } from 'child_process';
-import fs from 'fs';
-import path from 'path';
-import os from 'os';
-import del from 'del';
-
-import { safeDump } from 'js-yaml';
-import {
-  createMapStream,
-  createSplitStream,
-  createPromiseFromStreams,
-} from '../../../legacy/utils/streams';
-import { getConfigFromFiles } from '../../../core/server/config/read_config';
-
-const testConfigFile = follow('__fixtures__/reload_logging_config/kibana.test.yml');
-const kibanaPath = follow('../../../../scripts/kibana.js');
-
-const second = 1000;
-const minute = second * 60;
-
-const tempDir = path.join(os.tmpdir(), 'kbn-reload-test');
-
-function follow(file) {
-  return path.relative(process.cwd(), path.resolve(__dirname, file));
-}
-
-function setLoggingJson(enabled) {
-  const conf = getConfigFromFiles([testConfigFile]);
-  conf.logging = conf.logging || {};
-  conf.logging.json = enabled;
-
-  const yaml = safeDump(conf);
-
-  fs.writeFileSync(testConfigFile, yaml);
-}
-
-describe('Server logging configuration', function() {
-  let child;
-  let isJson;
-
-  beforeEach(() => {
-    isJson = true;
-    setLoggingJson(true);
-
-    fs.mkdirSync(tempDir, { recursive: true });
-  });
-
-  afterEach(() => {
-    isJson = true;
-    setLoggingJson(true);
-
-    if (child !== undefined) {
-      child.kill();
-      child = undefined;
-    }
-
-    del.sync(tempDir, { force: true });
-  });
-
-  const isWindows = /^win/.test(process.platform);
-  if (isWindows) {
-    it('SIGHUP is not a feature of Windows.', () => {
-      // nothing to do for Windows
-    });
-  } else {
-    it(
-      'should be reloadable via SIGHUP process signaling',
-      async function() {
-        expect.assertions(3);
-
-        child = spawn(
-          process.execPath,
-          [kibanaPath, '--config', testConfigFile, '--oss', '--verbose'],
-          {
-            stdio: 'pipe',
-          }
-        );
-
-        let sawJson = false;
-        let sawNonjson = false;
-
-        const [exitCode] = await Promise.all([
-          Promise.race([
-            new Promise(r => child.once('exit', r)).then(code => (code === null ? 0 : code)),
-
-            new Promise(r => child.once('error', r)).then(err => {
-              throw new Error(
-                `error in child process while attempting to reload config. ${err.stack ||
-                  err.message ||
-                  err}`
-              );
-            }),
-          ]),
-
-          createPromiseFromStreams([
-            child.stdout,
-            createSplitStream('\n'),
-            createMapStream(async line => {
-              if (!line) {
-                // skip empty lines
-                return;
-              }
-
-              if (isJson) {
-                const data = JSON.parse(line);
-                sawJson = true;
-
-                // We know the sighup handler will be registered before
-                // root.setup() is called
-                if (data.message.includes('setting up root')) {
-                  isJson = false;
-                  setLoggingJson(false);
-
-                  // Reload logging config. We give it a little bit of time to just make
-                  // sure the process sighup handler is registered.
-                  await new Promise(r => setTimeout(r, 100));
-                  child.kill('SIGHUP');
-                }
-              } else if (line.startsWith('{')) {
-                // We have told Kibana to stop logging json, but it hasn't completed
-                // the switch yet, so we ignore before switching over.
-              } else {
-                // Kibana has successfully stopped logging json, so kill the server.
-                sawNonjson = true;
-
-                child && child.kill();
-                child = undefined;
-              }
-            }),
-          ]),
-        ]);
-
-        expect(exitCode).toEqual(0);
-        expect(sawJson).toEqual(true);
-        expect(sawNonjson).toEqual(true);
-      },
-      minute
-    );
-
-    it(
-      'should recreate file handler on SIGHUP',
-      function(done) {
-        expect.hasAssertions();
-
-        const logPath = path.resolve(tempDir, 'kibana.log');
-        const logPathArchived = path.resolve(tempDir, 'kibana_archive.log');
-
-        function watchFileUntil(path, matcher, timeout) {
-          return new Promise((resolve, reject) => {
-            const timeoutHandle = setTimeout(() => {
-              fs.unwatchFile(path);
-              reject(`watchFileUntil timed out for "${matcher}"`);
-            }, timeout);
-
-            fs.watchFile(path, () => {
-              try {
-                const contents = fs.readFileSync(path);
-
-                if (matcher.test(contents)) {
-                  clearTimeout(timeoutHandle);
-                  fs.unwatchFile(path);
-                  resolve(contents);
-                }
-              } catch (e) {
-                // noop
-              }
-            });
-          });
-        }
-
-        child = spawn(process.execPath, [
-          kibanaPath,
-          '--oss',
-          '--config',
-          testConfigFile,
-          '--logging.dest',
-          logPath,
-          '--plugins.initialize',
-          'false',
-          '--logging.json',
-          'false',
-          '--verbose',
-        ]);
-
-        watchFileUntil(logPath, /starting server/, 2 * minute)
-          .then(() => {
-            // once the server is running, archive the log file and issue SIGHUP
-            fs.renameSync(logPath, logPathArchived);
-            child.kill('SIGHUP');
-          })
-          .then(() =>
-            watchFileUntil(logPath, /Reloaded logging configuration due to SIGHUP/, 10 * second)
-          )
-          .then(contents => {
-            const lines = contents.toString().split('\n');
-            // should be the first line of the new log file
-            expect(lines[0]).toMatch(/Reloaded logging configuration due to SIGHUP/);
-            child.kill();
-          })
-          .then(done, done);
-      },
-      3 * minute
-    );
-  }
-});
diff --git a/src/cli/serve/integration_tests/reload_logging_config.test.ts b/src/cli/serve/integration_tests/reload_logging_config.test.ts
new file mode 100644
index 0000000000000..2def3569828d3
--- /dev/null
+++ b/src/cli/serve/integration_tests/reload_logging_config.test.ts
@@ -0,0 +1,263 @@
+/*
+ * 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 Child from 'child_process';
+import Fs from 'fs';
+import Path from 'path';
+import Os from 'os';
+import Del from 'del';
+
+import * as Rx from 'rxjs';
+import { map, filter, take } from 'rxjs/operators';
+import { safeDump } from 'js-yaml';
+
+import { getConfigFromFiles } from '../../../core/server/config/read_config';
+
+const legacyConfig = follow('__fixtures__/reload_logging_config/kibana.test.yml');
+const configFileLogConsole = follow(
+  '__fixtures__/reload_logging_config/kibana_log_console.test.yml'
+);
+const configFileLogFile = follow('__fixtures__/reload_logging_config/kibana_log_file.test.yml');
+
+const kibanaPath = follow('../../../../scripts/kibana.js');
+
+const second = 1000;
+const minute = second * 60;
+
+const tempDir = Path.join(Os.tmpdir(), 'kbn-reload-test');
+
+function follow(file: string) {
+  return Path.relative(process.cwd(), Path.resolve(__dirname, file));
+}
+
+function watchFileUntil(path: string, matcher: RegExp, timeout: number) {
+  return new Promise<string>((resolve, reject) => {
+    const timeoutHandle = setTimeout(() => {
+      Fs.unwatchFile(path);
+      reject(`watchFileUntil timed out for "${matcher}"`);
+    }, timeout);
+
+    Fs.watchFile(path, () => {
+      try {
+        const contents = Fs.readFileSync(path, 'utf-8');
+
+        if (matcher.test(contents)) {
+          clearTimeout(timeoutHandle);
+          Fs.unwatchFile(path);
+          resolve(contents);
+        }
+      } catch (e) {
+        // noop
+      }
+    });
+  });
+}
+
+function containsJsonOnly(content: string[]) {
+  return content.every(line => line.startsWith('{'));
+}
+
+function createConfigManager(configPath: string) {
+  return {
+    modify(fn: (input: Record<string, any>) => Record<string, any>) {
+      const oldContent = getConfigFromFiles([configPath]);
+      const yaml = safeDump(fn(oldContent));
+      Fs.writeFileSync(configPath, yaml);
+    },
+  };
+}
+
+describe('Server logging configuration', function() {
+  let child: Child.ChildProcess;
+  beforeEach(() => {
+    Fs.mkdirSync(tempDir, { recursive: true });
+  });
+
+  afterEach(async () => {
+    if (child !== undefined) {
+      child.kill();
+      // wait for child to be killed otherwise jest complains that process not finished
+      await new Promise(res => setTimeout(res, 1000));
+    }
+    Del.sync(tempDir, { force: true });
+  });
+
+  const isWindows = /^win/.test(process.platform);
+  if (isWindows) {
+    it('SIGHUP is not a feature of Windows.', () => {
+      // nothing to do for Windows
+    });
+  } else {
+    describe('legacy logging', () => {
+      it(
+        'should be reloadable via SIGHUP process signaling',
+        async function() {
+          const configFilePath = Path.resolve(tempDir, 'kibana.yml');
+          Fs.copyFileSync(legacyConfig, configFilePath);
+
+          child = Child.spawn(process.execPath, [
+            kibanaPath,
+            '--oss',
+            '--config',
+            configFilePath,
+            '--verbose',
+          ]);
+
+          const message$ = Rx.fromEvent(child.stdout, 'data').pipe(
+            map(messages =>
+              String(messages)
+                .split('\n')
+                .filter(Boolean)
+            )
+          );
+
+          await message$
+            .pipe(
+              // We know the sighup handler will be registered before this message logged
+              filter(messages => messages.some(m => m.includes('setting up root'))),
+              take(1)
+            )
+            .toPromise();
+
+          const lastMessage = await message$.pipe(take(1)).toPromise();
+          expect(containsJsonOnly(lastMessage)).toBe(true);
+
+          createConfigManager(configFilePath).modify(oldConfig => {
+            oldConfig.logging.json = false;
+            return oldConfig;
+          });
+
+          child.kill('SIGHUP');
+
+          await message$
+            .pipe(
+              filter(messages => !containsJsonOnly(messages)),
+              take(1)
+            )
+            .toPromise();
+        },
+        minute
+      );
+
+      it(
+        'should recreate file handle on SIGHUP',
+        async function() {
+          const logPath = Path.resolve(tempDir, 'kibana.log');
+          const logPathArchived = Path.resolve(tempDir, 'kibana_archive.log');
+
+          child = Child.spawn(process.execPath, [
+            kibanaPath,
+            '--oss',
+            '--config',
+            legacyConfig,
+            '--logging.dest',
+            logPath,
+            '--verbose',
+          ]);
+
+          await watchFileUntil(logPath, /setting up root/, 30 * second);
+          // once the server is running, archive the log file and issue SIGHUP
+          Fs.renameSync(logPath, logPathArchived);
+          child.kill('SIGHUP');
+
+          await watchFileUntil(
+            logPath,
+            /Reloaded logging configuration due to SIGHUP/,
+            30 * second
+          );
+        },
+        minute
+      );
+    });
+
+    describe('platform logging', () => {
+      it(
+        'should be reloadable via SIGHUP process signaling',
+        async function() {
+          const configFilePath = Path.resolve(tempDir, 'kibana.yml');
+          Fs.copyFileSync(configFileLogConsole, configFilePath);
+
+          child = Child.spawn(process.execPath, [kibanaPath, '--oss', '--config', configFilePath]);
+
+          const message$ = Rx.fromEvent(child.stdout, 'data').pipe(
+            map(messages =>
+              String(messages)
+                .split('\n')
+                .filter(Boolean)
+            )
+          );
+
+          await message$
+            .pipe(
+              // We know the sighup handler will be registered before this message logged
+              filter(messages => messages.some(m => m.includes('setting up root'))),
+              take(1)
+            )
+            .toPromise();
+
+          const lastMessage = await message$.pipe(take(1)).toPromise();
+          expect(containsJsonOnly(lastMessage)).toBe(true);
+
+          createConfigManager(configFilePath).modify(oldConfig => {
+            oldConfig.logging.appenders.console.layout.kind = 'pattern';
+            return oldConfig;
+          });
+          child.kill('SIGHUP');
+
+          await message$
+            .pipe(
+              filter(messages => !containsJsonOnly(messages)),
+              take(1)
+            )
+            .toPromise();
+        },
+        30 * second
+      );
+      it(
+        'should recreate file handle on SIGHUP',
+        async function() {
+          const configFilePath = Path.resolve(tempDir, 'kibana.yml');
+          Fs.copyFileSync(configFileLogFile, configFilePath);
+
+          const logPath = Path.resolve(tempDir, 'kibana.log');
+          const logPathArchived = Path.resolve(tempDir, 'kibana_archive.log');
+
+          createConfigManager(configFilePath).modify(oldConfig => {
+            oldConfig.logging.appenders.file.path = logPath;
+            return oldConfig;
+          });
+
+          child = Child.spawn(process.execPath, [kibanaPath, '--oss', '--config', configFilePath]);
+
+          await watchFileUntil(logPath, /setting up root/, 30 * second);
+          // once the server is running, archive the log file and issue SIGHUP
+          Fs.renameSync(logPath, logPathArchived);
+          child.kill('SIGHUP');
+
+          await watchFileUntil(
+            logPath,
+            /Reloaded logging configuration due to SIGHUP/,
+            30 * second
+          );
+        },
+        minute
+      );
+    });
+  }
+});

From d6aff77485ce28bc7d50a5f4bac513024c70ea57 Mon Sep 17 00:00:00 2001
From: Dmitry Lemeshko <dzmitry.lemechko@elastic.co>
Date: Thu, 20 Feb 2020 13:12:04 +0100
Subject: [PATCH 091/174] remove NODE_ENV=test (#58037)

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .ci/Jenkinsfile_coverage     | 6 +-----
 test/scripts/jenkins_unit.sh | 1 -
 2 files changed, 1 insertion(+), 6 deletions(-)

diff --git a/.ci/Jenkinsfile_coverage b/.ci/Jenkinsfile_coverage
index e40cc584dc376..fa1e141be93ea 100644
--- a/.ci/Jenkinsfile_coverage
+++ b/.ci/Jenkinsfile_coverage
@@ -13,11 +13,7 @@ stage("Kibana Pipeline") { // This stage is just here to help the BlueOcean UI a
           ]) {
             parallel([
               'kibana-intake-agent': {
-                withEnv([
-                  'NODE_ENV=test' // Needed for jest tests only
-                ]) {
-                  kibanaPipeline.intakeWorker('kibana-intake', './test/scripts/jenkins_unit.sh')()
-                }
+                kibanaPipeline.intakeWorker('kibana-intake', './test/scripts/jenkins_unit.sh')()
               },
               'x-pack-intake-agent': {
                 withEnv([
diff --git a/test/scripts/jenkins_unit.sh b/test/scripts/jenkins_unit.sh
index fe67594ad8ac2..a9751003e8425 100755
--- a/test/scripts/jenkins_unit.sh
+++ b/test/scripts/jenkins_unit.sh
@@ -5,7 +5,6 @@ source test/scripts/jenkins_test_setup.sh
 if [[ -z "$CODE_COVERAGE" ]] ; then
   "$(FORCE_COLOR=0 yarn bin)/grunt" jenkins:unit --dev;
 else
-  echo "NODE_ENV=$NODE_ENV"
   echo " -> Running jest tests with coverage"
   node scripts/jest --ci --verbose --coverage
   echo ""

From b07aa096060266e16366766d716fd3d5c7b1a30c Mon Sep 17 00:00:00 2001
From: Jean-Louis Leysens <jloleysens@gmail.com>
Date: Thu, 20 Feb 2020 13:25:02 +0100
Subject: [PATCH 092/174] [Watcher] Refactor to use client from
 `RequestHandlerContext` (#57834)

* Remove elasticsearch setup service from route deps

Removed the callWithRequestFactory entirely. This setup was introducing a pattern where
route handlers were not pulling the ES client fromt the route handler context. For this
refactor we need to extend the route handler context with watcher specific client actions
and so we also extend RequestHandlerContext globally.

In this commit we also update the types for params, query and body schema on each route to avoid
using any everwhere.

* Add generic types to license wrapper

Adding <P, Q, B> to the license wrapper made it a transparent
wrapper from a type perspective so we can remove the need to
eplicitly set RequestHandler<P, Q, B> on the handler.

Also cleaned up a variable name "response" -> "searchResults"

Also removed elasticsearch from the RouteDependencies type.

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 x-pack/plugins/watcher/server/index.ts        |   2 +
 .../server/lib/call_with_request_factory.ts   |  28 ----
 .../fetch_all_from_scroll.ts                  |  29 ++--
 .../license_pre_routing_factory.ts            |   6 +-
 x-pack/plugins/watcher/server/plugin.ts       |  31 +++-
 .../routes/api/indices/register_get_route.ts  |  96 ++++++------
 .../api/license/register_refresh_route.ts     |  10 +-
 .../routes/api/register_list_fields_route.ts  |  64 ++++----
 .../routes/api/register_load_history_route.ts |  82 +++++-----
 .../api/settings/register_load_route.ts       |  33 ++---
 .../action/register_acknowledge_route.ts      |  72 +++++----
 .../api/watch/register_activate_route.ts      |  75 +++++-----
 .../api/watch/register_deactivate_route.ts    |  76 +++++-----
 .../routes/api/watch/register_delete_route.ts |  55 ++++---
 .../api/watch/register_execute_route.ts       |  88 +++++------
 .../api/watch/register_history_route.ts       | 103 ++++++-------
 .../routes/api/watch/register_load_route.ts   |  82 +++++-----
 .../routes/api/watch/register_save_route.ts   | 140 +++++++++---------
 .../api/watch/register_visualize_route.ts     |  68 +++++----
 .../api/watches/register_delete_route.ts      |  42 +++---
 .../routes/api/watches/register_list_route.ts |  95 ++++++------
 x-pack/plugins/watcher/server/types.ts        |   4 +-
 22 files changed, 633 insertions(+), 648 deletions(-)
 delete mode 100644 x-pack/plugins/watcher/server/lib/call_with_request_factory.ts

diff --git a/x-pack/plugins/watcher/server/index.ts b/x-pack/plugins/watcher/server/index.ts
index 51eb7bfa543fe..356be781fb194 100644
--- a/x-pack/plugins/watcher/server/index.ts
+++ b/x-pack/plugins/watcher/server/index.ts
@@ -6,4 +6,6 @@
 import { PluginInitializerContext } from 'kibana/server';
 import { WatcherServerPlugin } from './plugin';
 
+export { WatcherContext } from './plugin';
+
 export const plugin = (ctx: PluginInitializerContext) => new WatcherServerPlugin(ctx);
diff --git a/x-pack/plugins/watcher/server/lib/call_with_request_factory.ts b/x-pack/plugins/watcher/server/lib/call_with_request_factory.ts
deleted file mode 100644
index 4884c75436c24..0000000000000
--- a/x-pack/plugins/watcher/server/lib/call_with_request_factory.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { ElasticsearchServiceSetup } from 'kibana/server';
-import { once } from 'lodash';
-import { elasticsearchJsPlugin } from './elasticsearch_js_plugin';
-
-const callWithRequest = once((elasticsearchService: ElasticsearchServiceSetup) => {
-  const config = { plugins: [elasticsearchJsPlugin] };
-  return elasticsearchService.createClient('watcher', config);
-});
-
-export const callWithRequestFactory = (
-  elasticsearchService: ElasticsearchServiceSetup,
-  request: any
-) => {
-  return (...args: any[]) => {
-    return (
-      callWithRequest(elasticsearchService)
-        .asScoped(request)
-        // @ts-ignore
-        .callAsCurrentUser(...args)
-    );
-  };
-};
diff --git a/x-pack/plugins/watcher/server/lib/fetch_all_from_scroll/fetch_all_from_scroll.ts b/x-pack/plugins/watcher/server/lib/fetch_all_from_scroll/fetch_all_from_scroll.ts
index de01bd5965504..8e8ca369dd02b 100644
--- a/x-pack/plugins/watcher/server/lib/fetch_all_from_scroll/fetch_all_from_scroll.ts
+++ b/x-pack/plugins/watcher/server/lib/fetch_all_from_scroll/fetch_all_from_scroll.ts
@@ -4,24 +4,31 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+import { IScopedClusterClient } from 'kibana/server';
 import { get } from 'lodash';
 import { ES_SCROLL_SETTINGS } from '../../../common/constants';
 
-export function fetchAllFromScroll(response: any, callWithRequest: any, hits: any[] = []) {
-  const newHits = get(response, 'hits.hits', []);
-  const scrollId = get(response, '_scroll_id');
+export function fetchAllFromScroll(
+  searchResuls: any,
+  dataClient: IScopedClusterClient,
+  hits: any[] = []
+): Promise<any> {
+  const newHits = get(searchResuls, 'hits.hits', []);
+  const scrollId = get(searchResuls, '_scroll_id');
 
   if (newHits.length > 0) {
     hits.push(...newHits);
 
-    return callWithRequest('scroll', {
-      body: {
-        scroll: ES_SCROLL_SETTINGS.KEEPALIVE,
-        scroll_id: scrollId,
-      },
-    }).then((innerResponse: any) => {
-      return fetchAllFromScroll(innerResponse, callWithRequest, hits);
-    });
+    return dataClient
+      .callAsCurrentUser('scroll', {
+        body: {
+          scroll: ES_SCROLL_SETTINGS.KEEPALIVE,
+          scroll_id: scrollId,
+        },
+      })
+      .then((innerResponse: any) => {
+        return fetchAllFromScroll(innerResponse, dataClient, hits);
+      });
   }
 
   return Promise.resolve(hits);
diff --git a/x-pack/plugins/watcher/server/lib/license_pre_routing_factory/license_pre_routing_factory.ts b/x-pack/plugins/watcher/server/lib/license_pre_routing_factory/license_pre_routing_factory.ts
index d010a23952725..1b2476fc78b45 100644
--- a/x-pack/plugins/watcher/server/lib/license_pre_routing_factory/license_pre_routing_factory.ts
+++ b/x-pack/plugins/watcher/server/lib/license_pre_routing_factory/license_pre_routing_factory.ts
@@ -12,13 +12,13 @@ import {
 } from 'kibana/server';
 import { RouteDependencies } from '../../types';
 
-export const licensePreRoutingFactory = (
+export const licensePreRoutingFactory = <P, Q, B>(
   { getLicenseStatus }: RouteDependencies,
-  handler: RequestHandler
+  handler: RequestHandler<P, Q, B>
 ) => {
   return function licenseCheck(
     ctx: RequestHandlerContext,
-    request: KibanaRequest,
+    request: KibanaRequest<P, Q, B>,
     response: KibanaResponseFactory
   ) {
     const licenseStatus = getLicenseStatus();
diff --git a/x-pack/plugins/watcher/server/plugin.ts b/x-pack/plugins/watcher/server/plugin.ts
index 1f7b3823609ec..51d85c2001bd2 100644
--- a/x-pack/plugins/watcher/server/plugin.ts
+++ b/x-pack/plugins/watcher/server/plugin.ts
@@ -3,7 +3,20 @@
  * 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, Logger, Plugin, PluginInitializerContext } from 'kibana/server';
+
+declare module 'kibana/server' {
+  interface RequestHandlerContext {
+    watcher?: WatcherContext;
+  }
+}
+
+import {
+  CoreSetup,
+  IScopedClusterClient,
+  Logger,
+  Plugin,
+  PluginInitializerContext,
+} from 'kibana/server';
 import { PLUGIN } from '../common/constants';
 import { Dependencies, LicenseStatus, RouteDependencies } from './types';
 import { LICENSE_CHECK_STATE } from '../../licensing/server';
@@ -15,6 +28,11 @@ import { registerWatchesRoutes } from './routes/api/watches';
 import { registerWatchRoutes } from './routes/api/watch';
 import { registerListFieldsRoute } from './routes/api/register_list_fields_route';
 import { registerLoadHistoryRoute } from './routes/api/register_load_history_route';
+import { elasticsearchJsPlugin } from './lib/elasticsearch_js_plugin';
+
+export interface WatcherContext {
+  client: IScopedClusterClient;
+}
 
 export class WatcherServerPlugin implements Plugin<void, void, any, any> {
   log: Logger;
@@ -31,15 +49,20 @@ export class WatcherServerPlugin implements Plugin<void, void, any, any> {
     { http, elasticsearch: elasticsearchService }: CoreSetup,
     { licensing }: Dependencies
   ) {
-    const elasticsearch = await elasticsearchService.adminClient;
     const router = http.createRouter();
     const routeDependencies: RouteDependencies = {
-      elasticsearch,
-      elasticsearchService,
       router,
       getLicenseStatus: () => this.licenseStatus,
     };
 
+    const config = { plugins: [elasticsearchJsPlugin] };
+    const watcherESClient = elasticsearchService.createClient('watcher', config);
+    http.registerRouteHandlerContext('watcher', (ctx, request) => {
+      return {
+        client: watcherESClient.asScoped(request),
+      };
+    });
+
     registerListFieldsRoute(routeDependencies);
     registerLoadHistoryRoute(routeDependencies);
     registerIndicesRoutes(routeDependencies);
diff --git a/x-pack/plugins/watcher/server/routes/api/indices/register_get_route.ts b/x-pack/plugins/watcher/server/routes/api/indices/register_get_route.ts
index 30607b82e3295..df6f62135baeb 100644
--- a/x-pack/plugins/watcher/server/routes/api/indices/register_get_route.ts
+++ b/x-pack/plugins/watcher/server/routes/api/indices/register_get_route.ts
@@ -5,13 +5,14 @@
  */
 
 import { schema } from '@kbn/config-schema';
-import { RequestHandler } from 'kibana/server';
+import { IScopedClusterClient } from 'kibana/server';
 import { reduce, size } from 'lodash';
-import { callWithRequestFactory } from '../../../lib/call_with_request_factory';
 import { isEsError } from '../../../lib/is_es_error';
 import { RouteDependencies } from '../../../types';
 import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory';
 
+const bodySchema = schema.object({ pattern: schema.string() }, { allowUnknowns: true });
+
 function getIndexNamesFromAliasesResponse(json: Record<string, any>) {
   return reduce(
     json,
@@ -26,67 +27,66 @@ function getIndexNamesFromAliasesResponse(json: Record<string, any>) {
   );
 }
 
-function getIndices(callWithRequest: any, pattern: string, limit = 10) {
-  return callWithRequest('indices.getAlias', {
-    index: pattern,
-    ignore: [404],
-  }).then((aliasResult: any) => {
-    if (aliasResult.status !== 404) {
-      const indicesFromAliasResponse = getIndexNamesFromAliasesResponse(aliasResult);
-      return indicesFromAliasResponse.slice(0, limit);
-    }
-
-    const params = {
+function getIndices(dataClient: IScopedClusterClient, pattern: string, limit = 10) {
+  return dataClient
+    .callAsCurrentUser('indices.getAlias', {
       index: pattern,
       ignore: [404],
-      body: {
-        size: 0, // no hits
-        aggs: {
-          indices: {
-            terms: {
-              field: '_index',
-              size: limit,
+    })
+    .then((aliasResult: any) => {
+      if (aliasResult.status !== 404) {
+        const indicesFromAliasResponse = getIndexNamesFromAliasesResponse(aliasResult);
+        return indicesFromAliasResponse.slice(0, limit);
+      }
+
+      const params = {
+        index: pattern,
+        ignore: [404],
+        body: {
+          size: 0, // no hits
+          aggs: {
+            indices: {
+              terms: {
+                field: '_index',
+                size: limit,
+              },
             },
           },
         },
-      },
-    };
+      };
 
-    return callWithRequest('search', params).then((response: any) => {
-      if (response.status === 404 || !response.aggregations) {
-        return [];
-      }
-      return response.aggregations.indices.buckets.map((bucket: any) => bucket.key);
+      return dataClient.callAsCurrentUser('search', params).then((response: any) => {
+        if (response.status === 404 || !response.aggregations) {
+          return [];
+        }
+        return response.aggregations.indices.buckets.map((bucket: any) => bucket.key);
+      });
     });
-  });
 }
 
 export function registerGetRoute(deps: RouteDependencies) {
-  const handler: RequestHandler<any, any, any> = async (ctx, request, response) => {
-    const callWithRequest = callWithRequestFactory(deps.elasticsearchService, request);
-    const { pattern } = request.body;
-
-    try {
-      const indices = await getIndices(callWithRequest, pattern);
-      return response.ok({ body: { indices } });
-    } catch (e) {
-      // Case: Error from Elasticsearch JS client
-      if (isEsError(e)) {
-        return response.customError({ statusCode: e.statusCode, body: e });
-      }
-
-      // Case: default
-      return response.internalError({ body: e });
-    }
-  };
-
   deps.router.post(
     {
       path: '/api/watcher/indices',
       validate: {
-        body: schema.object({}, { allowUnknowns: true }),
+        body: bodySchema,
       },
     },
-    licensePreRoutingFactory(deps, handler)
+    licensePreRoutingFactory(deps, async (ctx, request, response) => {
+      const { pattern } = request.body;
+
+      try {
+        const indices = await getIndices(ctx.watcher!.client, pattern);
+        return response.ok({ body: { indices } });
+      } catch (e) {
+        // Case: Error from Elasticsearch JS client
+        if (isEsError(e)) {
+          return response.customError({ statusCode: e.statusCode, body: e });
+        }
+
+        // Case: default
+        return response.internalError({ body: e });
+      }
+    })
   );
 }
diff --git a/x-pack/plugins/watcher/server/routes/api/license/register_refresh_route.ts b/x-pack/plugins/watcher/server/routes/api/license/register_refresh_route.ts
index a61fd16e8be4a..bd537cd6d21ab 100644
--- a/x-pack/plugins/watcher/server/routes/api/license/register_refresh_route.ts
+++ b/x-pack/plugins/watcher/server/routes/api/license/register_refresh_route.ts
@@ -4,7 +4,6 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { RequestHandler } from 'kibana/server';
 import { RouteDependencies } from '../../../types';
 import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory';
 /*
@@ -13,16 +12,15 @@ it needs to make a round-trip to the kibana server. This refresh endpoint is pro
 for when the client needs to check the license, but doesn't need to pull data from the
 server for any reason, i.e., when adding a new watch.
 */
-export function registerRefreshRoute(deps: RouteDependencies) {
-  const handler: RequestHandler<any, any, any> = (ctx, request, response) => {
-    return response.ok({ body: { success: true } });
-  };
 
+export function registerRefreshRoute(deps: RouteDependencies) {
   deps.router.get(
     {
       path: '/api/watcher/license/refresh',
       validate: false,
     },
-    licensePreRoutingFactory(deps, handler)
+    licensePreRoutingFactory(deps, (ctx, request, response) => {
+      return response.ok({ body: { success: true } });
+    })
   );
 }
diff --git a/x-pack/plugins/watcher/server/routes/api/register_list_fields_route.ts b/x-pack/plugins/watcher/server/routes/api/register_list_fields_route.ts
index 7c47379b87589..d72e5ad2f817d 100644
--- a/x-pack/plugins/watcher/server/routes/api/register_list_fields_route.ts
+++ b/x-pack/plugins/watcher/server/routes/api/register_list_fields_route.ts
@@ -5,15 +5,18 @@
  */
 
 import { schema } from '@kbn/config-schema';
-import { RequestHandler } from 'kibana/server';
-import { callWithRequestFactory } from '../../lib/call_with_request_factory';
+import { IScopedClusterClient } from 'kibana/server';
 import { isEsError } from '../../lib/is_es_error';
 // @ts-ignore
 import { Fields } from '../../models/fields/index';
 import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory';
 import { RouteDependencies } from '../../types';
 
-function fetchFields(callWithRequest: any, indexes: string[]) {
+const bodySchema = schema.object({
+  indexes: schema.arrayOf(schema.string()),
+});
+
+function fetchFields(dataClient: IScopedClusterClient, indexes: string[]) {
   const params = {
     index: indexes,
     fields: ['*'],
@@ -22,44 +25,39 @@ function fetchFields(callWithRequest: any, indexes: string[]) {
     ignore: 404,
   };
 
-  return callWithRequest('fieldCaps', params);
+  return dataClient.callAsCurrentUser('fieldCaps', params);
 }
 
 export function registerListFieldsRoute(deps: RouteDependencies) {
-  const handler: RequestHandler<any, any, any> = async (ctx, request, response) => {
-    const callWithRequest = callWithRequestFactory(deps.elasticsearchService, request);
-    const { indexes } = request.body;
-
-    try {
-      const fieldsResponse = await fetchFields(callWithRequest, indexes);
-      const json = fieldsResponse.status === 404 ? { fields: [] } : fieldsResponse;
-      const fields = Fields.fromUpstreamJson(json);
-      return response.ok({ body: fields.downstreamJson });
-    } catch (e) {
-      // Case: Error from Elasticsearch JS client
-      if (isEsError(e)) {
-        return response.customError({
-          statusCode: e.statusCode,
-          body: {
-            message: e.message,
-          },
-        });
-      }
-
-      // Case: default
-      return response.internalError({ body: e });
-    }
-  };
-
   deps.router.post(
     {
       path: '/api/watcher/fields',
       validate: {
-        body: schema.object({
-          indexes: schema.arrayOf(schema.string()),
-        }),
+        body: bodySchema,
       },
     },
-    licensePreRoutingFactory(deps, handler)
+    licensePreRoutingFactory(deps, async (ctx, request, response) => {
+      const { indexes } = request.body;
+
+      try {
+        const fieldsResponse = await fetchFields(ctx.watcher!.client, indexes);
+        const json = fieldsResponse.status === 404 ? { fields: [] } : fieldsResponse;
+        const fields = Fields.fromUpstreamJson(json);
+        return response.ok({ body: fields.downstreamJson });
+      } catch (e) {
+        // Case: Error from Elasticsearch JS client
+        if (isEsError(e)) {
+          return response.customError({
+            statusCode: e.statusCode,
+            body: {
+              message: e.message,
+            },
+          });
+        }
+
+        // Case: default
+        return response.internalError({ body: e });
+      }
+    })
   );
 }
diff --git a/x-pack/plugins/watcher/server/routes/api/register_load_history_route.ts b/x-pack/plugins/watcher/server/routes/api/register_load_history_route.ts
index 1be8477df79bc..8c9068123ce8d 100644
--- a/x-pack/plugins/watcher/server/routes/api/register_load_history_route.ts
+++ b/x-pack/plugins/watcher/server/routes/api/register_load_history_route.ts
@@ -6,8 +6,7 @@
 
 import { schema } from '@kbn/config-schema';
 import { get } from 'lodash';
-import { RequestHandler } from 'kibana/server';
-import { callWithRequestFactory } from '../../lib/call_with_request_factory';
+import { IScopedClusterClient } from 'kibana/server';
 import { isEsError } from '../../lib/is_es_error';
 import { INDEX_NAMES } from '../../../common/constants';
 import { RouteDependencies } from '../../types';
@@ -15,8 +14,12 @@ import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory'
 // @ts-ignore
 import { WatchHistoryItem } from '../../models/watch_history_item/index';
 
-function fetchHistoryItem(callWithRequest: any, watchHistoryItemId: string) {
-  return callWithRequest('search', {
+const paramsSchema = schema.object({
+  id: schema.string(),
+});
+
+function fetchHistoryItem(dataClient: IScopedClusterClient, watchHistoryItemId: string) {
+  return dataClient.callAsCurrentUser('search', {
     index: INDEX_NAMES.WATCHER_HISTORY,
     body: {
       query: {
@@ -29,49 +32,44 @@ function fetchHistoryItem(callWithRequest: any, watchHistoryItemId: string) {
 }
 
 export function registerLoadHistoryRoute(deps: RouteDependencies) {
-  const handler: RequestHandler<any, any, any> = async (ctx, request, response) => {
-    const callWithRequest = callWithRequestFactory(deps.elasticsearchService, request);
-    const id = request.params.id;
-
-    try {
-      const responseFromES = await fetchHistoryItem(callWithRequest, id);
-      const hit = get(responseFromES, 'hits.hits[0]');
-      if (!hit) {
-        return response.notFound({ body: `Watch History Item with id = ${id} not found` });
-      }
-      const watchHistoryItemJson = get(hit, '_source');
-      const watchId = get(hit, '_source.watch_id');
-      const json = {
-        id,
-        watchId,
-        watchHistoryItemJson,
-        includeDetails: true,
-      };
-
-      const watchHistoryItem = WatchHistoryItem.fromUpstreamJson(json);
-      return response.ok({
-        body: { watchHistoryItem: watchHistoryItem.downstreamJson },
-      });
-    } catch (e) {
-      // Case: Error from Elasticsearch JS client
-      if (isEsError(e)) {
-        return response.customError({ statusCode: e.statusCode, body: e });
-      }
-
-      // Case: default
-      return response.internalError({ body: e });
-    }
-  };
-
   deps.router.get(
     {
       path: '/api/watcher/history/{id}',
       validate: {
-        params: schema.object({
-          id: schema.string(),
-        }),
+        params: paramsSchema,
       },
     },
-    licensePreRoutingFactory(deps, handler)
+    licensePreRoutingFactory(deps, async (ctx, request, response) => {
+      const id = request.params.id;
+
+      try {
+        const responseFromES = await fetchHistoryItem(ctx.watcher!.client, id);
+        const hit = get(responseFromES, 'hits.hits[0]');
+        if (!hit) {
+          return response.notFound({ body: `Watch History Item with id = ${id} not found` });
+        }
+        const watchHistoryItemJson = get(hit, '_source');
+        const watchId = get(hit, '_source.watch_id');
+        const json = {
+          id,
+          watchId,
+          watchHistoryItemJson,
+          includeDetails: true,
+        };
+
+        const watchHistoryItem = WatchHistoryItem.fromUpstreamJson(json);
+        return response.ok({
+          body: { watchHistoryItem: watchHistoryItem.downstreamJson },
+        });
+      } catch (e) {
+        // Case: Error from Elasticsearch JS client
+        if (isEsError(e)) {
+          return response.customError({ statusCode: e.statusCode, body: e });
+        }
+
+        // Case: default
+        return response.internalError({ body: e });
+      }
+    })
   );
 }
diff --git a/x-pack/plugins/watcher/server/routes/api/settings/register_load_route.ts b/x-pack/plugins/watcher/server/routes/api/settings/register_load_route.ts
index 6c70c2d0d07b6..fe9dd32735692 100644
--- a/x-pack/plugins/watcher/server/routes/api/settings/register_load_route.ts
+++ b/x-pack/plugins/watcher/server/routes/api/settings/register_load_route.ts
@@ -4,14 +4,14 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { IClusterClient, RequestHandler } from 'kibana/server';
+import { IScopedClusterClient } from 'kibana/server';
 import { isEsError } from '../../../lib/is_es_error';
 // @ts-ignore
 import { Settings } from '../../../models/settings/index';
 import { RouteDependencies } from '../../../types';
 import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory';
 
-function fetchClusterSettings(client: IClusterClient) {
+function fetchClusterSettings(client: IScopedClusterClient) {
   return client.callAsInternalUser('cluster.getSettings', {
     includeDefaults: true,
     filterPath: '**.xpack.notification',
@@ -19,25 +19,24 @@ function fetchClusterSettings(client: IClusterClient) {
 }
 
 export function registerLoadRoute(deps: RouteDependencies) {
-  const handler: RequestHandler<any, any, any> = async (ctx, request, response) => {
-    try {
-      const settings = await fetchClusterSettings(deps.elasticsearch);
-      return response.ok({ body: Settings.fromUpstreamJson(settings).downstreamJson });
-    } catch (e) {
-      // Case: Error from Elasticsearch JS client
-      if (isEsError(e)) {
-        return response.customError({ statusCode: e.statusCode, body: e });
-      }
-
-      // Case: default
-      return response.internalError({ body: e });
-    }
-  };
   deps.router.get(
     {
       path: '/api/watcher/settings',
       validate: false,
     },
-    licensePreRoutingFactory(deps, handler)
+    licensePreRoutingFactory(deps, async (ctx, request, response) => {
+      try {
+        const settings = await fetchClusterSettings(ctx.watcher!.client);
+        return response.ok({ body: Settings.fromUpstreamJson(settings).downstreamJson });
+      } catch (e) {
+        // Case: Error from Elasticsearch JS client
+        if (isEsError(e)) {
+          return response.customError({ statusCode: e.statusCode, body: e });
+        }
+
+        // Case: default
+        return response.internalError({ body: e });
+      }
+    })
   );
 }
diff --git a/x-pack/plugins/watcher/server/routes/api/watch/action/register_acknowledge_route.ts b/x-pack/plugins/watcher/server/routes/api/watch/action/register_acknowledge_route.ts
index 08eec7456e3a5..9e024a63b82c5 100644
--- a/x-pack/plugins/watcher/server/routes/api/watch/action/register_acknowledge_route.ts
+++ b/x-pack/plugins/watcher/server/routes/api/watch/action/register_acknowledge_route.ts
@@ -6,60 +6,58 @@
 
 import { schema } from '@kbn/config-schema';
 import { get } from 'lodash';
-import { RequestHandler } from 'kibana/server';
-import { callWithRequestFactory } from '../../../../lib/call_with_request_factory';
+import { IScopedClusterClient } from 'kibana/server';
 import { isEsError } from '../../../../lib/is_es_error';
 // @ts-ignore
 import { WatchStatus } from '../../../../models/watch_status/index';
 import { RouteDependencies } from '../../../../types';
 import { licensePreRoutingFactory } from '../../../../lib/license_pre_routing_factory';
 
-function acknowledgeAction(callWithRequest: any, watchId: string, actionId: string) {
-  return callWithRequest('watcher.ackWatch', {
+const paramsSchema = schema.object({
+  watchId: schema.string(),
+  actionId: schema.string(),
+});
+
+function acknowledgeAction(dataClient: IScopedClusterClient, watchId: string, actionId: string) {
+  return dataClient.callAsCurrentUser('watcher.ackWatch', {
     id: watchId,
     action: actionId,
   });
 }
 
 export function registerAcknowledgeRoute(deps: RouteDependencies) {
-  const handler: RequestHandler<any, any, any> = async (ctx, request, response) => {
-    const callWithRequest = callWithRequestFactory(deps.elasticsearchService, request);
-    const { watchId, actionId } = request.params;
-
-    try {
-      const hit = await acknowledgeAction(callWithRequest, watchId, actionId);
-      const watchStatusJson = get(hit, 'status');
-      const json = {
-        id: watchId,
-        watchStatusJson,
-      };
-
-      const watchStatus = WatchStatus.fromUpstreamJson(json);
-      return response.ok({
-        body: { watchStatus: watchStatus.downstreamJson },
-      });
-    } catch (e) {
-      // Case: Error from Elasticsearch JS client
-      if (isEsError(e)) {
-        const body = e.statusCode === 404 ? `Watch with id = ${watchId} not found` : e;
-        return response.customError({ statusCode: e.statusCode, body });
-      }
-
-      // Case: default
-      return response.internalError({ body: e });
-    }
-  };
-
   deps.router.put(
     {
       path: '/api/watcher/watch/{watchId}/action/{actionId}/acknowledge',
       validate: {
-        params: schema.object({
-          watchId: schema.string(),
-          actionId: schema.string(),
-        }),
+        params: paramsSchema,
       },
     },
-    licensePreRoutingFactory(deps, handler)
+    licensePreRoutingFactory(deps, async (ctx, request, response) => {
+      const { watchId, actionId } = request.params;
+
+      try {
+        const hit = await acknowledgeAction(ctx.watcher!.client, watchId, actionId);
+        const watchStatusJson = get(hit, 'status');
+        const json = {
+          id: watchId,
+          watchStatusJson,
+        };
+
+        const watchStatus = WatchStatus.fromUpstreamJson(json);
+        return response.ok({
+          body: { watchStatus: watchStatus.downstreamJson },
+        });
+      } catch (e) {
+        // Case: Error from Elasticsearch JS client
+        if (isEsError(e)) {
+          const body = e.statusCode === 404 ? `Watch with id = ${watchId} not found` : e;
+          return response.customError({ statusCode: e.statusCode, body });
+        }
+
+        // Case: default
+        return response.internalError({ body: e });
+      }
+    })
   );
 }
diff --git a/x-pack/plugins/watcher/server/routes/api/watch/register_activate_route.ts b/x-pack/plugins/watcher/server/routes/api/watch/register_activate_route.ts
index fdc20854ed8c2..1afeeb4e80efb 100644
--- a/x-pack/plugins/watcher/server/routes/api/watch/register_activate_route.ts
+++ b/x-pack/plugins/watcher/server/routes/api/watch/register_activate_route.ts
@@ -5,62 +5,59 @@
  */
 
 import { schema } from '@kbn/config-schema';
-import { RequestHandler } from 'kibana/server';
+import { IScopedClusterClient } from 'kibana/server';
 import { get } from 'lodash';
-import { callWithRequestFactory } from '../../../lib/call_with_request_factory';
 import { isEsError } from '../../../lib/is_es_error';
 import { RouteDependencies } from '../../../types';
 import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory';
 // @ts-ignore
 import { WatchStatus } from '../../../models/watch_status/index';
 
-function activateWatch(callWithRequest: any, watchId: string) {
-  return callWithRequest('watcher.activateWatch', {
+function activateWatch(dataClient: IScopedClusterClient, watchId: string) {
+  return dataClient.callAsCurrentUser('watcher.activateWatch', {
     id: watchId,
   });
 }
 
-export function registerActivateRoute(deps: RouteDependencies) {
-  const handler: RequestHandler<any, any, any> = async (ctx, request, response) => {
-    const callWithRequest = callWithRequestFactory(deps.elasticsearchService, request);
-
-    const { watchId } = request.params;
-
-    try {
-      const hit = await activateWatch(callWithRequest, watchId);
-      const watchStatusJson = get(hit, 'status');
-      const json = {
-        id: watchId,
-        watchStatusJson,
-      };
-
-      const watchStatus = WatchStatus.fromUpstreamJson(json);
-      return response.ok({
-        body: {
-          watchStatus: watchStatus.downstreamJson,
-        },
-      });
-    } catch (e) {
-      // Case: Error from Elasticsearch JS client
-      if (isEsError(e)) {
-        const body = e.statusCode === 404 ? `Watch with id = ${watchId} not found` : e;
-        return response.customError({ statusCode: e.statusCode, body });
-      }
-
-      // Case: default
-      return response.internalError({ body: e });
-    }
-  };
+const paramsSchema = schema.object({
+  watchId: schema.string(),
+});
 
+export function registerActivateRoute(deps: RouteDependencies) {
   deps.router.put(
     {
       path: '/api/watcher/watch/{watchId}/activate',
       validate: {
-        params: schema.object({
-          watchId: schema.string(),
-        }),
+        params: paramsSchema,
       },
     },
-    licensePreRoutingFactory(deps, handler)
+    licensePreRoutingFactory(deps, async (ctx, request, response) => {
+      const { watchId } = request.params;
+
+      try {
+        const hit = await activateWatch(ctx.watcher!.client, watchId);
+        const watchStatusJson = get(hit, 'status');
+        const json = {
+          id: watchId,
+          watchStatusJson,
+        };
+
+        const watchStatus = WatchStatus.fromUpstreamJson(json);
+        return response.ok({
+          body: {
+            watchStatus: watchStatus.downstreamJson,
+          },
+        });
+      } catch (e) {
+        // Case: Error from Elasticsearch JS client
+        if (isEsError(e)) {
+          const body = e.statusCode === 404 ? `Watch with id = ${watchId} not found` : e;
+          return response.customError({ statusCode: e.statusCode, body });
+        }
+
+        // Case: default
+        return response.internalError({ body: e });
+      }
+    })
   );
 }
diff --git a/x-pack/plugins/watcher/server/routes/api/watch/register_deactivate_route.ts b/x-pack/plugins/watcher/server/routes/api/watch/register_deactivate_route.ts
index 08d99f42df054..3171d8ee2e1e5 100644
--- a/x-pack/plugins/watcher/server/routes/api/watch/register_deactivate_route.ts
+++ b/x-pack/plugins/watcher/server/routes/api/watch/register_deactivate_route.ts
@@ -3,63 +3,61 @@
  * or more contributor license agreements. Licensed under the Elastic License;
  * you may not use this file except in compliance with the Elastic License.
  */
+
 import { schema } from '@kbn/config-schema';
-import { RequestHandler } from 'kibana/server';
+import { IScopedClusterClient } from 'kibana/server';
 import { get } from 'lodash';
-import { callWithRequestFactory } from '../../../lib/call_with_request_factory';
 import { isEsError } from '../../../lib/is_es_error';
 import { RouteDependencies } from '../../../types';
 import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory';
 // @ts-ignore
 import { WatchStatus } from '../../../models/watch_status/index';
 
-function deactivateWatch(callWithRequest: any, watchId: string) {
-  return callWithRequest('watcher.deactivateWatch', {
+const paramsSchema = schema.object({
+  watchId: schema.string(),
+});
+
+function deactivateWatch(dataClient: IScopedClusterClient, watchId: string) {
+  return dataClient.callAsCurrentUser('watcher.deactivateWatch', {
     id: watchId,
   });
 }
 
 export function registerDeactivateRoute(deps: RouteDependencies) {
-  const handler: RequestHandler<any, any, any> = async (ctx, request, response) => {
-    const callWithRequest = callWithRequestFactory(deps.elasticsearchService, request);
-
-    const { watchId } = request.params;
-
-    try {
-      const hit = await deactivateWatch(callWithRequest, watchId);
-      const watchStatusJson = get(hit, 'status');
-      const json = {
-        id: watchId,
-        watchStatusJson,
-      };
-
-      const watchStatus = WatchStatus.fromUpstreamJson(json);
-      return response.ok({
-        body: {
-          watchStatus: watchStatus.downstreamJson,
-        },
-      });
-    } catch (e) {
-      // Case: Error from Elasticsearch JS client
-      if (isEsError(e)) {
-        const body = e.statusCode === 404 ? `Watch with id = ${watchId} not found` : e;
-        return response.customError({ statusCode: e.statusCode, body });
-      }
-
-      // Case: default
-      return response.internalError({ body: e });
-    }
-  };
-
   deps.router.put(
     {
       path: '/api/watcher/watch/{watchId}/deactivate',
       validate: {
-        params: schema.object({
-          watchId: schema.string(),
-        }),
+        params: paramsSchema,
       },
     },
-    licensePreRoutingFactory(deps, handler)
+    licensePreRoutingFactory(deps, async (ctx, request, response) => {
+      const { watchId } = request.params;
+
+      try {
+        const hit = await deactivateWatch(ctx.watcher!.client, watchId);
+        const watchStatusJson = get(hit, 'status');
+        const json = {
+          id: watchId,
+          watchStatusJson,
+        };
+
+        const watchStatus = WatchStatus.fromUpstreamJson(json);
+        return response.ok({
+          body: {
+            watchStatus: watchStatus.downstreamJson,
+          },
+        });
+      } catch (e) {
+        // Case: Error from Elasticsearch JS client
+        if (isEsError(e)) {
+          const body = e.statusCode === 404 ? `Watch with id = ${watchId} not found` : e;
+          return response.customError({ statusCode: e.statusCode, body });
+        }
+
+        // Case: default
+        return response.internalError({ body: e });
+      }
+    })
   );
 }
diff --git a/x-pack/plugins/watcher/server/routes/api/watch/register_delete_route.ts b/x-pack/plugins/watcher/server/routes/api/watch/register_delete_route.ts
index 6e95cf959bc9c..bfdf328550bbe 100644
--- a/x-pack/plugins/watcher/server/routes/api/watch/register_delete_route.ts
+++ b/x-pack/plugins/watcher/server/routes/api/watch/register_delete_route.ts
@@ -5,49 +5,46 @@
  */
 
 import { schema } from '@kbn/config-schema';
-import { RequestHandler } from 'kibana/server';
-import { callWithRequestFactory } from '../../../lib/call_with_request_factory';
+import { IScopedClusterClient } from 'kibana/server';
 import { isEsError } from '../../../lib/is_es_error';
 import { RouteDependencies } from '../../../types';
 import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory';
 
-function deleteWatch(callWithRequest: any, watchId: string) {
-  return callWithRequest('watcher.deleteWatch', {
+const paramsSchema = schema.object({
+  watchId: schema.string(),
+});
+
+function deleteWatch(dataClient: IScopedClusterClient, watchId: string) {
+  return dataClient.callAsCurrentUser('watcher.deleteWatch', {
     id: watchId,
   });
 }
 
 export function registerDeleteRoute(deps: RouteDependencies) {
-  const handler: RequestHandler<any, any, any> = async (ctx, request, response) => {
-    const callWithRequest = callWithRequestFactory(deps.elasticsearchService, request);
-
-    const { watchId } = request.params;
-
-    try {
-      return response.ok({
-        body: await deleteWatch(callWithRequest, watchId),
-      });
-    } catch (e) {
-      // Case: Error from Elasticsearch JS client
-      if (isEsError(e)) {
-        const body = e.statusCode === 404 ? `Watch with id = ${watchId} not found` : e;
-        return response.customError({ statusCode: e.statusCode, body });
-      }
-
-      // Case: default
-      return response.internalError({ body: e });
-    }
-  };
-
   deps.router.delete(
     {
       path: '/api/watcher/watch/{watchId}',
       validate: {
-        params: schema.object({
-          watchId: schema.string(),
-        }),
+        params: paramsSchema,
       },
     },
-    licensePreRoutingFactory(deps, handler)
+    licensePreRoutingFactory(deps, async (ctx, request, response) => {
+      const { watchId } = request.params;
+
+      try {
+        return response.ok({
+          body: await deleteWatch(ctx.watcher!.client, watchId),
+        });
+      } catch (e) {
+        // Case: Error from Elasticsearch JS client
+        if (isEsError(e)) {
+          const body = e.statusCode === 404 ? `Watch with id = ${watchId} not found` : e;
+          return response.customError({ statusCode: e.statusCode, body });
+        }
+
+        // Case: default
+        return response.internalError({ body: e });
+      }
+    })
   );
 }
diff --git a/x-pack/plugins/watcher/server/routes/api/watch/register_execute_route.ts b/x-pack/plugins/watcher/server/routes/api/watch/register_execute_route.ts
index fef6d07317da5..7aaa77c05a5f0 100644
--- a/x-pack/plugins/watcher/server/routes/api/watch/register_execute_route.ts
+++ b/x-pack/plugins/watcher/server/routes/api/watch/register_execute_route.ts
@@ -5,9 +5,8 @@
  */
 
 import { schema } from '@kbn/config-schema';
-import { RequestHandler } from 'kibana/server';
+import { IScopedClusterClient } from 'kibana/server';
 import { get } from 'lodash';
-import { callWithRequestFactory } from '../../../lib/call_with_request_factory';
 import { isEsError } from '../../../lib/is_es_error';
 import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory';
 
@@ -19,60 +18,63 @@ import { Watch } from '../../../models/watch/index';
 // @ts-ignore
 import { WatchHistoryItem } from '../../../models/watch_history_item/index';
 
-function executeWatch(callWithRequest: any, executeDetails: any, watchJson: any) {
+const bodySchema = schema.object({
+  executeDetails: schema.object({}, { allowUnknowns: true }),
+  watch: schema.object({}, { allowUnknowns: true }),
+});
+
+function executeWatch(dataClient: IScopedClusterClient, executeDetails: any, watchJson: any) {
   const body = executeDetails;
   body.watch = watchJson;
 
-  return callWithRequest('watcher.executeWatch', {
+  return dataClient.callAsCurrentUser('watcher.executeWatch', {
     body,
   });
 }
 
 export function registerExecuteRoute(deps: RouteDependencies) {
-  const handler: RequestHandler<any, any, any> = async (ctx, request, response) => {
-    const callWithRequest = callWithRequestFactory(deps.elasticsearchService, request);
-    const executeDetails = ExecuteDetails.fromDownstreamJson(request.body.executeDetails);
-    const watch = Watch.fromDownstreamJson(request.body.watch);
-
-    try {
-      const hit = await executeWatch(callWithRequest, executeDetails.upstreamJson, watch.watchJson);
-      const id = get(hit, '_id');
-      const watchHistoryItemJson = get(hit, 'watch_record');
-      const watchId = get(hit, 'watch_record.watch_id');
-      const json = {
-        id,
-        watchId,
-        watchHistoryItemJson,
-        includeDetails: true,
-      };
-
-      const watchHistoryItem = WatchHistoryItem.fromUpstreamJson(json);
-      return response.ok({
-        body: {
-          watchHistoryItem: watchHistoryItem.downstreamJson,
-        },
-      });
-    } catch (e) {
-      // Case: Error from Elasticsearch JS client
-      if (isEsError(e)) {
-        return response.customError({ statusCode: e.statusCode, body: e });
-      }
-
-      // Case: default
-      return response.internalError({ body: e });
-    }
-  };
-
   deps.router.put(
     {
       path: '/api/watcher/watch/execute',
       validate: {
-        body: schema.object({
-          executeDetails: schema.object({}, { allowUnknowns: true }),
-          watch: schema.object({}, { allowUnknowns: true }),
-        }),
+        body: bodySchema,
       },
     },
-    licensePreRoutingFactory(deps, handler)
+    licensePreRoutingFactory(deps, async (ctx, request, response) => {
+      const executeDetails = ExecuteDetails.fromDownstreamJson(request.body.executeDetails);
+      const watch = Watch.fromDownstreamJson(request.body.watch);
+
+      try {
+        const hit = await executeWatch(
+          ctx.watcher!.client,
+          executeDetails.upstreamJson,
+          watch.watchJson
+        );
+        const id = get(hit, '_id');
+        const watchHistoryItemJson = get(hit, 'watch_record');
+        const watchId = get(hit, 'watch_record.watch_id');
+        const json = {
+          id,
+          watchId,
+          watchHistoryItemJson,
+          includeDetails: true,
+        };
+
+        const watchHistoryItem = WatchHistoryItem.fromUpstreamJson(json);
+        return response.ok({
+          body: {
+            watchHistoryItem: watchHistoryItem.downstreamJson,
+          },
+        });
+      } catch (e) {
+        // Case: Error from Elasticsearch JS client
+        if (isEsError(e)) {
+          return response.customError({ statusCode: e.statusCode, body: e });
+        }
+
+        // Case: default
+        return response.internalError({ body: e });
+      }
+    })
   );
 }
diff --git a/x-pack/plugins/watcher/server/routes/api/watch/register_history_route.ts b/x-pack/plugins/watcher/server/routes/api/watch/register_history_route.ts
index 7f0f1ac8d66a3..b64c28e114b72 100644
--- a/x-pack/plugins/watcher/server/routes/api/watch/register_history_route.ts
+++ b/x-pack/plugins/watcher/server/routes/api/watch/register_history_route.ts
@@ -5,9 +5,8 @@
  */
 
 import { schema } from '@kbn/config-schema';
-import { RequestHandler } from 'kibana/server';
+import { IScopedClusterClient } from 'kibana/server';
 import { get } from 'lodash';
-import { callWithRequestFactory } from '../../../lib/call_with_request_factory';
 import { fetchAllFromScroll } from '../../../lib/fetch_all_from_scroll';
 import { INDEX_NAMES, ES_SCROLL_SETTINGS } from '../../../../common/constants';
 import { isEsError } from '../../../lib/is_es_error';
@@ -16,7 +15,15 @@ import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_facto
 // @ts-ignore
 import { WatchHistoryItem } from '../../../models/watch_history_item/index';
 
-function fetchHistoryItems(callWithRequest: any, watchId: any, startTime: any) {
+const paramsSchema = schema.object({
+  watchId: schema.string(),
+});
+
+const querySchema = schema.object({
+  startTime: schema.string(),
+});
+
+function fetchHistoryItems(dataClient: IScopedClusterClient, watchId: any, startTime: any) {
   const params: any = {
     index: INDEX_NAMES.WATCHER_HISTORY,
     scroll: ES_SCROLL_SETTINGS.KEEPALIVE,
@@ -37,61 +44,57 @@ function fetchHistoryItems(callWithRequest: any, watchId: any, startTime: any) {
     params.body.query.bool.must.push(timeRangeQuery);
   }
 
-  return callWithRequest('search', params).then((response: any) =>
-    fetchAllFromScroll(response, callWithRequest)
-  );
+  return dataClient
+    .callAsCurrentUser('search', params)
+    .then((response: any) => fetchAllFromScroll(response, dataClient));
 }
 
 export function registerHistoryRoute(deps: RouteDependencies) {
-  const handler: RequestHandler<any, any, any> = async (ctx, request, response) => {
-    const callWithRequest = callWithRequestFactory(deps.elasticsearchService, request);
-    const { watchId } = request.params;
-    const { startTime } = request.query;
-
-    try {
-      const hits = await fetchHistoryItems(callWithRequest, watchId, startTime);
-      const watchHistoryItems = hits.map((hit: any) => {
-        const id = get(hit, '_id');
-        const watchHistoryItemJson = get(hit, '_source');
-
-        const opts = { includeDetails: false };
-        return WatchHistoryItem.fromUpstreamJson(
-          {
-            id,
-            watchId,
-            watchHistoryItemJson,
-          },
-          opts
-        );
-      });
-
-      return response.ok({
-        body: {
-          watchHistoryItems: watchHistoryItems.map(
-            (watchHistoryItem: any) => watchHistoryItem.downstreamJson
-          ),
-        },
-      });
-    } catch (e) {
-      // Case: Error from Elasticsearch JS client
-      if (isEsError(e)) {
-        return response.customError({ statusCode: e.statusCode, body: e });
-      }
-
-      // Case: default
-      return response.internalError({ body: e });
-    }
-  };
-
   deps.router.get(
     {
       path: '/api/watcher/watch/{watchId}/history',
       validate: {
-        params: schema.object({
-          watchId: schema.string(),
-        }),
+        params: paramsSchema,
+        query: querySchema,
       },
     },
-    licensePreRoutingFactory(deps, handler)
+    licensePreRoutingFactory(deps, async (ctx, request, response) => {
+      const { watchId } = request.params;
+      const { startTime } = request.query;
+
+      try {
+        const hits = await fetchHistoryItems(ctx.watcher!.client, watchId, startTime);
+        const watchHistoryItems = hits.map((hit: any) => {
+          const id = get(hit, '_id');
+          const watchHistoryItemJson = get(hit, '_source');
+
+          const opts = { includeDetails: false };
+          return WatchHistoryItem.fromUpstreamJson(
+            {
+              id,
+              watchId,
+              watchHistoryItemJson,
+            },
+            opts
+          );
+        });
+
+        return response.ok({
+          body: {
+            watchHistoryItems: watchHistoryItems.map(
+              (watchHistoryItem: any) => watchHistoryItem.downstreamJson
+            ),
+          },
+        });
+      } catch (e) {
+        // Case: Error from Elasticsearch JS client
+        if (isEsError(e)) {
+          return response.customError({ statusCode: e.statusCode, body: e });
+        }
+
+        // Case: default
+        return response.internalError({ body: e });
+      }
+    })
   );
 }
diff --git a/x-pack/plugins/watcher/server/routes/api/watch/register_load_route.ts b/x-pack/plugins/watcher/server/routes/api/watch/register_load_route.ts
index 91d71cd737121..6363054921333 100644
--- a/x-pack/plugins/watcher/server/routes/api/watch/register_load_route.ts
+++ b/x-pack/plugins/watcher/server/routes/api/watch/register_load_route.ts
@@ -5,65 +5,63 @@
  */
 
 import { schema } from '@kbn/config-schema';
-import { RequestHandler } from 'kibana/server';
+import { IScopedClusterClient } from 'kibana/server';
 import { get } from 'lodash';
-import { callWithRequestFactory } from '../../../lib/call_with_request_factory';
 import { isEsError } from '../../../lib/is_es_error';
 import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory';
 // @ts-ignore
 import { Watch } from '../../../models/watch/index';
 import { RouteDependencies } from '../../../types';
 
-function fetchWatch(callWithRequest: any, watchId: string) {
-  return callWithRequest('watcher.getWatch', {
+const paramsSchema = schema.object({
+  id: schema.string(),
+});
+
+function fetchWatch(dataClient: IScopedClusterClient, watchId: string) {
+  return dataClient.callAsCurrentUser('watcher.getWatch', {
     id: watchId,
   });
 }
 
 export function registerLoadRoute(deps: RouteDependencies) {
-  const handler: RequestHandler<any, any, any> = async (ctx, request, response) => {
-    const callWithRequest = callWithRequestFactory(deps.elasticsearchService, request);
-
-    const id = request.params.id;
-
-    try {
-      const hit = await fetchWatch(callWithRequest, id);
-      const watchJson = get(hit, 'watch');
-      const watchStatusJson = get(hit, 'status');
-      const json = {
-        id,
-        watchJson,
-        watchStatusJson,
-      };
-
-      const watch = Watch.fromUpstreamJson(json, {
-        throwExceptions: {
-          Action: false,
-        },
-      });
-      return response.ok({
-        body: { watch: watch.downstreamJson },
-      });
-    } catch (e) {
-      // Case: Error from Elasticsearch JS client
-      if (isEsError(e)) {
-        const body = e.statusCode === 404 ? `Watch with id = ${id} not found` : e;
-        return response.customError({ statusCode: e.statusCode, body });
-      }
-
-      // Case: default
-      return response.internalError({ body: e });
-    }
-  };
   deps.router.get(
     {
       path: '/api/watcher/watch/{id}',
       validate: {
-        params: schema.object({
-          id: schema.string(),
-        }),
+        params: paramsSchema,
       },
     },
-    licensePreRoutingFactory(deps, handler)
+    licensePreRoutingFactory(deps, async (ctx, request, response) => {
+      const id = request.params.id;
+
+      try {
+        const hit = await fetchWatch(ctx.watcher!.client, id);
+        const watchJson = get(hit, 'watch');
+        const watchStatusJson = get(hit, 'status');
+        const json = {
+          id,
+          watchJson,
+          watchStatusJson,
+        };
+
+        const watch = Watch.fromUpstreamJson(json, {
+          throwExceptions: {
+            Action: false,
+          },
+        });
+        return response.ok({
+          body: { watch: watch.downstreamJson },
+        });
+      } catch (e) {
+        // Case: Error from Elasticsearch JS client
+        if (isEsError(e)) {
+          const body = e.statusCode === 404 ? `Watch with id = ${id} not found` : e;
+          return response.customError({ statusCode: e.statusCode, body });
+        }
+
+        // Case: default
+        return response.internalError({ body: e });
+      }
+    })
   );
 }
diff --git a/x-pack/plugins/watcher/server/routes/api/watch/register_save_route.ts b/x-pack/plugins/watcher/server/routes/api/watch/register_save_route.ts
index 7986424e6229a..572790f12a5f8 100644
--- a/x-pack/plugins/watcher/server/routes/api/watch/register_save_route.ts
+++ b/x-pack/plugins/watcher/server/routes/api/watch/register_save_route.ts
@@ -5,98 +5,104 @@
  */
 
 import { schema } from '@kbn/config-schema';
-import { RequestHandler } from 'kibana/server';
+import { IScopedClusterClient } from 'kibana/server';
 import { i18n } from '@kbn/i18n';
 import { WATCH_TYPES } from '../../../../common/constants';
 import { serializeJsonWatch, serializeThresholdWatch } from '../../../../common/lib/serialization';
-import { callWithRequestFactory } from '../../../lib/call_with_request_factory';
 import { isEsError } from '../../../lib/is_es_error';
 import { RouteDependencies } from '../../../types';
 import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory';
 
-function fetchWatch(callWithRequest: any, watchId: string) {
-  return callWithRequest('watcher.getWatch', {
+const paramsSchema = schema.object({
+  id: schema.string(),
+});
+
+const bodySchema = schema.object(
+  {
+    type: schema.string(),
+    isNew: schema.boolean(),
+  },
+  { allowUnknowns: true }
+);
+
+function fetchWatch(dataClient: IScopedClusterClient, watchId: string) {
+  return dataClient.callAsCurrentUser('watcher.getWatch', {
     id: watchId,
   });
 }
 
-function saveWatch(callWithRequest: any, id: string, body: any) {
-  return callWithRequest('watcher.putWatch', {
+function saveWatch(dataClient: IScopedClusterClient, id: string, body: any) {
+  return dataClient.callAsCurrentUser('watcher.putWatch', {
     id,
     body,
   });
 }
 
 export function registerSaveRoute(deps: RouteDependencies) {
-  const handler: RequestHandler<any, any, any> = async (ctx, request, response) => {
-    const callWithRequest = callWithRequestFactory(deps.elasticsearchService, request);
-    const { id } = request.params;
-    const { type, isNew, ...watchConfig } = request.body;
+  deps.router.put(
+    {
+      path: '/api/watcher/watch/{id}',
+      validate: {
+        params: paramsSchema,
+        body: bodySchema,
+      },
+    },
+    licensePreRoutingFactory(deps, async (ctx, request, response) => {
+      const { id } = request.params;
+      const { type, isNew, ...watchConfig } = request.body;
 
-    // For new watches, verify watch with the same ID doesn't already exist
-    if (isNew) {
-      try {
-        const existingWatch = await fetchWatch(callWithRequest, id);
-        if (existingWatch.found) {
-          return response.conflict({
-            body: {
-              message: i18n.translate('xpack.watcher.saveRoute.duplicateWatchIdErrorMessage', {
-                defaultMessage: "There is already a watch with ID '{watchId}'.",
-                values: {
-                  watchId: id,
-                },
-              }),
-            },
-          });
-        }
-      } catch (e) {
-        const es404 = isEsError(e) && e.statusCode === 404;
-        if (!es404) {
-          return response.internalError({ body: e });
+      // For new watches, verify watch with the same ID doesn't already exist
+      if (isNew) {
+        try {
+          const existingWatch = await fetchWatch(ctx.watcher!.client, id);
+          if (existingWatch.found) {
+            return response.conflict({
+              body: {
+                message: i18n.translate('xpack.watcher.saveRoute.duplicateWatchIdErrorMessage', {
+                  defaultMessage: "There is already a watch with ID '{watchId}'.",
+                  values: {
+                    watchId: id,
+                  },
+                }),
+              },
+            });
+          }
+        } catch (e) {
+          const es404 = isEsError(e) && e.statusCode === 404;
+          if (!es404) {
+            return response.internalError({ body: e });
+          }
+          // Else continue...
         }
-        // Else continue...
       }
-    }
 
-    let serializedWatch;
+      let serializedWatch;
 
-    switch (type) {
-      case WATCH_TYPES.JSON:
-        const { name, watch } = watchConfig;
-        serializedWatch = serializeJsonWatch(name, watch);
-        break;
+      switch (type) {
+        case WATCH_TYPES.JSON:
+          const { name, watch } = watchConfig as any;
+          serializedWatch = serializeJsonWatch(name, watch);
+          break;
 
-      case WATCH_TYPES.THRESHOLD:
-        serializedWatch = serializeThresholdWatch(watchConfig);
-        break;
-    }
-
-    try {
-      // Create new watch
-      return response.ok({
-        body: await saveWatch(callWithRequest, id, serializedWatch),
-      });
-    } catch (e) {
-      // Case: Error from Elasticsearch JS client
-      if (isEsError(e)) {
-        return response.customError({ statusCode: e.statusCode, body: e });
+        case WATCH_TYPES.THRESHOLD:
+          serializedWatch = serializeThresholdWatch(watchConfig);
+          break;
       }
 
-      // Case: default
-      return response.internalError({ body: e });
-    }
-  };
+      try {
+        // Create new watch
+        return response.ok({
+          body: await saveWatch(ctx.watcher!.client, id, serializedWatch),
+        });
+      } catch (e) {
+        // Case: Error from Elasticsearch JS client
+        if (isEsError(e)) {
+          return response.customError({ statusCode: e.statusCode, body: e });
+        }
 
-  deps.router.put(
-    {
-      path: '/api/watcher/watch/{id}',
-      validate: {
-        params: schema.object({
-          id: schema.string(),
-        }),
-        body: schema.object({}, { allowUnknowns: true }),
-      },
-    },
-    licensePreRoutingFactory(deps, handler)
+        // Case: default
+        return response.internalError({ body: e });
+      }
+    })
   );
 }
diff --git a/x-pack/plugins/watcher/server/routes/api/watch/register_visualize_route.ts b/x-pack/plugins/watcher/server/routes/api/watch/register_visualize_route.ts
index f2110bcc0ebdb..200b35953b6f2 100644
--- a/x-pack/plugins/watcher/server/routes/api/watch/register_visualize_route.ts
+++ b/x-pack/plugins/watcher/server/routes/api/watch/register_visualize_route.ts
@@ -5,8 +5,7 @@
  */
 
 import { schema } from '@kbn/config-schema';
-import { RequestHandler } from 'kibana/server';
-import { callWithRequestFactory } from '../../../lib/call_with_request_factory';
+import { IScopedClusterClient } from 'kibana/server';
 import { isEsError } from '../../../lib/is_es_error';
 import { RouteDependencies } from '../../../types';
 import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory';
@@ -16,7 +15,12 @@ import { Watch } from '../../../models/watch/index';
 // @ts-ignore
 import { VisualizeOptions } from '../../../models/visualize_options/index';
 
-function fetchVisualizeData(callWithRequest: any, index: any, body: any) {
+const bodySchema = schema.object({
+  watch: schema.object({}, { allowUnknowns: true }),
+  options: schema.object({}, { allowUnknowns: true }),
+});
+
+function fetchVisualizeData(dataClient: IScopedClusterClient, index: any, body: any) {
   const params = {
     index,
     body,
@@ -25,46 +29,40 @@ function fetchVisualizeData(callWithRequest: any, index: any, body: any) {
     ignore: [404],
   };
 
-  return callWithRequest('search', params);
+  return dataClient.callAsCurrentUser('search', params);
 }
 
 export function registerVisualizeRoute(deps: RouteDependencies) {
-  const handler: RequestHandler<any, any, any> = async (ctx, request, response) => {
-    const callWithRequest = callWithRequestFactory(deps.elasticsearchService, request);
-    const watch = Watch.fromDownstreamJson(request.body.watch);
-    const options = VisualizeOptions.fromDownstreamJson(request.body.options);
-    const body = watch.getVisualizeQuery(options);
-
-    try {
-      const hits = await fetchVisualizeData(callWithRequest, watch.index, body);
-      const visualizeData = watch.formatVisualizeData(hits);
-
-      return response.ok({
-        body: {
-          visualizeData,
-        },
-      });
-    } catch (e) {
-      // Case: Error from Elasticsearch JS client
-      if (isEsError(e)) {
-        return response.customError({ statusCode: e.statusCode, body: e });
-      }
-
-      // Case: default
-      return response.internalError({ body: e });
-    }
-  };
-
   deps.router.post(
     {
       path: '/api/watcher/watch/visualize',
       validate: {
-        body: schema.object({
-          watch: schema.object({}, { allowUnknowns: true }),
-          options: schema.object({}, { allowUnknowns: true }),
-        }),
+        body: bodySchema,
       },
     },
-    licensePreRoutingFactory(deps, handler)
+    licensePreRoutingFactory(deps, async (ctx, request, response) => {
+      const watch = Watch.fromDownstreamJson(request.body.watch);
+      const options = VisualizeOptions.fromDownstreamJson(request.body.options);
+      const body = watch.getVisualizeQuery(options);
+
+      try {
+        const hits = await fetchVisualizeData(ctx.watcher!.client, watch.index, body);
+        const visualizeData = watch.formatVisualizeData(hits);
+
+        return response.ok({
+          body: {
+            visualizeData,
+          },
+        });
+      } catch (e) {
+        // Case: Error from Elasticsearch JS client
+        if (isEsError(e)) {
+          return response.customError({ statusCode: e.statusCode, body: e });
+        }
+
+        // Case: default
+        return response.internalError({ body: e });
+      }
+    })
   );
 }
diff --git a/x-pack/plugins/watcher/server/routes/api/watches/register_delete_route.ts b/x-pack/plugins/watcher/server/routes/api/watches/register_delete_route.ts
index 2ac824529f9a6..71e0a77bff972 100644
--- a/x-pack/plugins/watcher/server/routes/api/watches/register_delete_route.ts
+++ b/x-pack/plugins/watcher/server/routes/api/watches/register_delete_route.ts
@@ -5,16 +5,20 @@
  */
 
 import { schema } from '@kbn/config-schema';
-import { RequestHandler } from 'kibana/server';
-import { callWithRequestFactory } from '../../../lib/call_with_request_factory';
+import { IScopedClusterClient } from 'kibana/server';
 import { RouteDependencies } from '../../../types';
 import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory';
 
-function deleteWatches(callWithRequest: any, watchIds: string[]) {
+const bodySchema = schema.object({
+  watchIds: schema.arrayOf(schema.string()),
+});
+
+function deleteWatches(dataClient: IScopedClusterClient, watchIds: string[]) {
   const deletePromises = watchIds.map(watchId => {
-    return callWithRequest('watcher.deleteWatch', {
-      id: watchId,
-    })
+    return dataClient
+      .callAsCurrentUser('watcher.deleteWatch', {
+        id: watchId,
+      })
       .then((success: Array<{ _id: string }>) => ({ success }))
       .catch((error: Array<{ _id: string }>) => ({ error }));
   });
@@ -22,7 +26,7 @@ function deleteWatches(callWithRequest: any, watchIds: string[]) {
   return Promise.all(deletePromises).then(results => {
     const errors: Error[] = [];
     const successes: boolean[] = [];
-    results.forEach(({ success, error }) => {
+    results.forEach(({ success, error }: { success?: any; error?: any }) => {
       if (success) {
         successes.push(success._id);
       } else if (error) {
@@ -38,26 +42,20 @@ function deleteWatches(callWithRequest: any, watchIds: string[]) {
 }
 
 export function registerDeleteRoute(deps: RouteDependencies) {
-  const handler: RequestHandler<any, any, any> = async (ctx, request, response) => {
-    const callWithRequest = callWithRequestFactory(deps.elasticsearchService, request);
-
-    try {
-      const results = await deleteWatches(callWithRequest, request.body.watchIds);
-      return response.ok({ body: { results } });
-    } catch (e) {
-      return response.internalError({ body: e });
-    }
-  };
-
   deps.router.post(
     {
       path: '/api/watcher/watches/delete',
       validate: {
-        body: schema.object({
-          watchIds: schema.arrayOf(schema.string()),
-        }),
+        body: bodySchema,
       },
     },
-    licensePreRoutingFactory(deps, handler)
+    licensePreRoutingFactory(deps, async (ctx, request, response) => {
+      try {
+        const results = await deleteWatches(ctx.watcher!.client, request.body.watchIds);
+        return response.ok({ body: { results } });
+      } catch (e) {
+        return response.internalError({ body: e });
+      }
+    })
   );
 }
diff --git a/x-pack/plugins/watcher/server/routes/api/watches/register_list_route.ts b/x-pack/plugins/watcher/server/routes/api/watches/register_list_route.ts
index fcbdf688a2ab4..5e823a0a8d2de 100644
--- a/x-pack/plugins/watcher/server/routes/api/watches/register_list_route.ts
+++ b/x-pack/plugins/watcher/server/routes/api/watches/register_list_route.ts
@@ -4,9 +4,8 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { RequestHandler } from 'kibana/server';
+import { IScopedClusterClient } from 'kibana/server';
 import { get } from 'lodash';
-import { callWithRequestFactory } from '../../../lib/call_with_request_factory';
 import { fetchAllFromScroll } from '../../../lib/fetch_all_from_scroll';
 import { INDEX_NAMES, ES_SCROLL_SETTINGS } from '../../../../common/constants';
 import { isEsError } from '../../../lib/is_es_error';
@@ -15,7 +14,7 @@ import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_facto
 // @ts-ignore
 import { Watch } from '../../../models/watch/index';
 
-function fetchWatches(callWithRequest: any) {
+function fetchWatches(dataClient: IScopedClusterClient) {
   const params = {
     index: INDEX_NAMES.WATCHES,
     scroll: ES_SCROLL_SETTINGS.KEEPALIVE,
@@ -25,62 +24,58 @@ function fetchWatches(callWithRequest: any) {
     ignore: [404],
   };
 
-  return callWithRequest('search', params).then((response: any) =>
-    fetchAllFromScroll(response, callWithRequest)
-  );
+  return dataClient
+    .callAsCurrentUser('search', params)
+    .then((response: any) => fetchAllFromScroll(response, dataClient));
 }
 
 export function registerListRoute(deps: RouteDependencies) {
-  const handler: RequestHandler<any, any, any> = async (ctx, request, response) => {
-    const callWithRequest = callWithRequestFactory(deps.elasticsearchService, request);
-
-    try {
-      const hits = await fetchWatches(callWithRequest);
-      const watches = hits.map((hit: any) => {
-        const id = get(hit, '_id');
-        const watchJson = get(hit, '_source');
-        const watchStatusJson = get(hit, '_source.status');
+  deps.router.get(
+    {
+      path: '/api/watcher/watches',
+      validate: false,
+    },
+    licensePreRoutingFactory(deps, async (ctx, request, response) => {
+      try {
+        const hits = await fetchWatches(ctx.watcher!.client);
+        const watches = hits.map((hit: any) => {
+          const id = get(hit, '_id');
+          const watchJson = get(hit, '_source');
+          const watchStatusJson = get(hit, '_source.status');
 
-        return Watch.fromUpstreamJson(
-          {
-            id,
-            watchJson,
-            watchStatusJson,
-          },
-          {
-            throwExceptions: {
-              Action: false,
+          return Watch.fromUpstreamJson(
+            {
+              id,
+              watchJson,
+              watchStatusJson,
             },
-          }
-        );
-      });
+            {
+              throwExceptions: {
+                Action: false,
+              },
+            }
+          );
+        });
 
-      return response.ok({
-        body: {
-          watches: watches.map((watch: any) => watch.downstreamJson),
-        },
-      });
-    } catch (e) {
-      // Case: Error from Elasticsearch JS client
-      if (isEsError(e)) {
-        return response.customError({
-          statusCode: e.statusCode,
+        return response.ok({
           body: {
-            message: e.message,
+            watches: watches.map((watch: any) => watch.downstreamJson),
           },
         });
-      }
-
-      // Case: default
-      return response.internalError({ body: e });
-    }
-  };
+      } catch (e) {
+        // Case: Error from Elasticsearch JS client
+        if (isEsError(e)) {
+          return response.customError({
+            statusCode: e.statusCode,
+            body: {
+              message: e.message,
+            },
+          });
+        }
 
-  deps.router.get(
-    {
-      path: '/api/watcher/watches',
-      validate: false,
-    },
-    licensePreRoutingFactory(deps, handler)
+        // Case: default
+        return response.internalError({ body: e });
+      }
+    })
   );
 }
diff --git a/x-pack/plugins/watcher/server/types.ts b/x-pack/plugins/watcher/server/types.ts
index d9f2d3c3b1e7a..dd941054114a8 100644
--- a/x-pack/plugins/watcher/server/types.ts
+++ b/x-pack/plugins/watcher/server/types.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { IRouter, ElasticsearchServiceSetup, IClusterClient } from 'kibana/server';
+import { IRouter } from 'kibana/server';
 import { LicensingPluginSetup } from '../../licensing/server';
 
 import { XPackMainPlugin } from '../../../legacy/plugins/xpack_main/server/xpack_main';
@@ -24,8 +24,6 @@ export interface ServerShim {
 export interface RouteDependencies {
   router: IRouter;
   getLicenseStatus: () => LicenseStatus;
-  elasticsearchService: ElasticsearchServiceSetup;
-  elasticsearch: IClusterClient;
 }
 
 export interface LicenseStatus {

From 16eb81628f7a56af2e826cf97d8a7e2db77910fb Mon Sep 17 00:00:00 2001
From: Robert Oskamp <robert.oskamp@elastic.co>
Date: Thu, 20 Feb 2020 13:31:04 +0100
Subject: [PATCH 093/174] [ML] Transform functional tests - bootstrap transform
 job for clone test (#57992)

This PR adds transform bootstrapping to the functional test transform.api service and demonstrates the usage in a new cloning test file, which will be completed as part of the transform cloning PR.
---
 .../test/functional/apps/transform/cloning.ts | 65 +++++++++++++
 .../test/functional/apps/transform/index.ts   |  1 +
 .../functional/services/transform_ui/api.ts   | 91 +++++++++++++++++++
 3 files changed, 157 insertions(+)
 create mode 100644 x-pack/test/functional/apps/transform/cloning.ts

diff --git a/x-pack/test/functional/apps/transform/cloning.ts b/x-pack/test/functional/apps/transform/cloning.ts
new file mode 100644
index 0000000000000..f06dc0a14a383
--- /dev/null
+++ b/x-pack/test/functional/apps/transform/cloning.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 { FtrProviderContext } from '../../ftr_provider_context';
+import { TransformPivotConfig } from '../../../../legacy/plugins/transform/public/app/common';
+
+function getTransformConfig(): TransformPivotConfig {
+  const date = Date.now();
+  return {
+    id: `ec_2_${date}`,
+    source: { index: ['ecommerce'] },
+    pivot: {
+      group_by: { category: { terms: { field: 'category.keyword' } } },
+      aggregations: { 'products.base_price.avg': { avg: { field: 'products.base_price' } } },
+    },
+    description:
+      'ecommerce batch transform with avg(products.base_price) grouped by terms(category.keyword)',
+    dest: { index: `user-ec_2_${date}` },
+  };
+}
+
+export default function({ getService }: FtrProviderContext) {
+  const esArchiver = getService('esArchiver');
+  const transform = getService('transform');
+
+  describe('cloning', function() {
+    this.tags(['smoke']);
+    const transformConfig = getTransformConfig();
+
+    before(async () => {
+      await esArchiver.load('ml/ecommerce');
+      await transform.api.createAndRunTransform(transformConfig);
+      await transform.securityUI.loginAsTransformPowerUser();
+    });
+
+    after(async () => {
+      await esArchiver.unload('ml/ecommerce');
+      await transform.api.deleteIndices(transformConfig.dest.index);
+      await transform.api.cleanTransformIndices();
+    });
+
+    const testDataList = [
+      {
+        suiteTitle: 'batch transform with terms group and avg agg',
+        expected: {},
+      },
+    ];
+
+    for (const testData of testDataList) {
+      describe(`${testData.suiteTitle}`, function() {
+        after(async () => {
+          // await transform.api.deleteIndices(<CLONE_DEST_INDEX>);
+        });
+
+        it('loads the home page', async () => {
+          await transform.navigation.navigateTo();
+          await transform.management.assertTransformListPageExists();
+        });
+      });
+    }
+  });
+}
diff --git a/x-pack/test/functional/apps/transform/index.ts b/x-pack/test/functional/apps/transform/index.ts
index 66a55105b3ca8..60b72f122f113 100644
--- a/x-pack/test/functional/apps/transform/index.ts
+++ b/x-pack/test/functional/apps/transform/index.ts
@@ -23,5 +23,6 @@ export default function({ getService, loadTestFile }: FtrProviderContext) {
 
     loadTestFile(require.resolve('./creation_index_pattern'));
     loadTestFile(require.resolve('./creation_saved_search'));
+    loadTestFile(require.resolve('./cloning'));
   });
 }
diff --git a/x-pack/test/functional/services/transform_ui/api.ts b/x-pack/test/functional/services/transform_ui/api.ts
index a6756e5940d72..6a4a1dfff6ea1 100644
--- a/x-pack/test/functional/services/transform_ui/api.ts
+++ b/x-pack/test/functional/services/transform_ui/api.ts
@@ -7,10 +7,17 @@ import expect from '@kbn/expect';
 
 import { FtrProviderContext } from '../../ftr_provider_context';
 
+import {
+  TRANSFORM_STATE,
+  TransformPivotConfig,
+  TransformStats,
+} from '../../../../legacy/plugins/transform/public/app/common';
+
 export function TransformAPIProvider({ getService }: FtrProviderContext) {
   const es = getService('legacyEs');
   const log = getService('log');
   const retry = getService('retry');
+  const esSupertest = getService('esSupertest');
 
   return {
     async deleteIndices(indices: string) {
@@ -39,5 +46,89 @@ export function TransformAPIProvider({ getService }: FtrProviderContext) {
     async cleanTransformIndices() {
       await this.deleteIndices('.transform-*');
     },
+
+    async getTransformStats(transformId: string): Promise<TransformStats> {
+      log.debug(`Fetching transform stats for transform ${transformId}`);
+      const statsResponse = await esSupertest
+        .get(`/_transform/${transformId}/_stats`)
+        .expect(200)
+        .then((res: any) => res.body);
+
+      expect(statsResponse.transforms).to.have.length(1);
+      return statsResponse.transforms[0];
+    },
+
+    async getTransformState(transformId: string): Promise<TRANSFORM_STATE> {
+      const stats = await this.getTransformStats(transformId);
+      const state: TRANSFORM_STATE = stats.state;
+
+      return state;
+    },
+
+    async waitForTransformState(transformId: string, expectedState: TRANSFORM_STATE) {
+      await retry.waitForWithTimeout(
+        `transform state to be ${expectedState}`,
+        2 * 60 * 1000,
+        async () => {
+          const state = await this.getTransformState(transformId);
+          if (state === expectedState) {
+            return true;
+          } else {
+            throw new Error(`expected transform state to be ${expectedState} but got ${state}`);
+          }
+        }
+      );
+    },
+
+    async waitForBatchTransformToComplete(transformId: string) {
+      await retry.waitForWithTimeout(`batch transform to complete`, 2 * 60 * 1000, async () => {
+        const stats = await this.getTransformStats(transformId);
+        if (stats.state === TRANSFORM_STATE.STOPPED && stats.checkpointing.last.checkpoint === 1) {
+          return true;
+        } else {
+          throw new Error(
+            `expected batch transform to be stopped with last checkpoint = 1 (got status: '${stats.state}', checkpoint: '${stats.checkpointing.last.checkpoint}')`
+          );
+        }
+      });
+    },
+
+    async getTransform(transformId: string) {
+      return await esSupertest.get(`/_transform/${transformId}`).expect(200);
+    },
+
+    async createTransform(transformConfig: TransformPivotConfig) {
+      const transformId = transformConfig.id;
+      log.debug(`Creating transform with id '${transformId}'...`);
+      await esSupertest
+        .put(`/_transform/${transformId}`)
+        .send(transformConfig)
+        .expect(200);
+
+      await retry.waitForWithTimeout(`'${transformId}' to be created`, 5 * 1000, async () => {
+        if (await this.getTransform(transformId)) {
+          return true;
+        } else {
+          throw new Error(`expected transform '${transformId}' to be created`);
+        }
+      });
+    },
+
+    async startTransform(transformId: string) {
+      log.debug(`Starting transform '${transformId}' ...`);
+      await esSupertest.post(`/_transform/${transformId}/_start`).expect(200);
+    },
+
+    async createAndRunTransform(transformConfig: TransformPivotConfig) {
+      await this.createTransform(transformConfig);
+      await this.startTransform(transformConfig.id);
+      if (transformConfig.sync === undefined) {
+        // batch mode
+        await this.waitForBatchTransformToComplete(transformConfig.id);
+      } else {
+        // continuous mode
+        await this.waitForTransformState(transformConfig.id, TRANSFORM_STATE.STARTED);
+      }
+    },
   };
 }

From ed2ca68d796fbb99d219336cd3dddc6ac8d71ba7 Mon Sep 17 00:00:00 2001
From: Alexey Antonov <alexwizp@gmail.com>
Date: Thu, 20 Feb 2020 15:40:00 +0300
Subject: [PATCH 094/174] [ui/agg_response/tabify] -> TypeScript & Jest & Shim
 (#57660)

* [ui/agg_response/tabify] -> TypeScript & Jest & Shim

[ui/agg_response/tabify] -> TypeScript & Jest & Shim

Part of #57394

* fix CI

* move tabify into new folder

* TypeScript _bucket.js

* rename _buckets -> bucket

* fix CI

* tabify.test.js -> tabify.test.ts

* tabify.js -> tabify.ts

* fix JEST

* Update src/legacy/core_plugins/data/public/search/tabify/types.ts

Co-Authored-By: Luke Elmers <lukeelmers@gmail.com>

* fake_hierarchical_data.js -> fake_hierarchical_data.ts

* TimeRange -> TabbedRangeFilterParams

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
Co-authored-by: Luke Elmers <lukeelmers@gmail.com>
---
 ...ical_data.js => fake_hierarchical_data.ts} |  16 +-
 src/legacy/core_plugins/data/public/index.ts  |   2 +
 .../data/public/search/aggs/agg_configs.ts    |   6 +-
 .../data/public/search/aggs/agg_params.ts     |   4 +-
 .../data/public/search/aggs/index.ts          |   3 +
 .../data/public/search/expressions/esaggs.ts  |   3 +-
 .../core_plugins/data/public/search/index.ts  |   1 +
 .../public/search/tabify/buckets.test.ts}     | 112 +++++-----
 .../data/public/search/tabify/buckets.ts      | 135 ++++++++++++
 .../public/search/tabify/get_columns.test.ts  | 191 +++++++++++++++++
 .../data/public/search/tabify/get_columns.ts} |   4 +-
 .../data/public/search/tabify/index.ts}       |   1 +
 .../search/tabify/response_writer.test.ts     | 170 +++++++++++++++
 .../public/search/tabify/response_writer.ts   |  88 ++++++++
 .../data/public/search/tabify/tabify.test.ts  | 172 +++++++++++++++
 .../data/public/search/tabify/tabify.ts       | 173 +++++++++++++++
 .../data/public/search/tabify/types.ts}       |  18 +-
 .../data/public/search/utils/types.ts         |   6 +
 .../kibana/public/discover/kibana_services.ts |   3 +-
 .../public/agg_table/__tests__/agg_table.js   |  12 +-
 .../agg_table/__tests__/agg_table_group.js    |   6 +-
 .../vis_type_table/public/legacy_imports.ts   |   4 +-
 .../vis_type_vislib/public/legacy_imports.ts  |   4 +-
 .../__tests__/visualizations/pie_chart.js     |   6 +-
 src/legacy/ui/public/agg_response/index.js    |   2 +-
 .../tabify/__tests__/_get_columns.js          | 199 ------------------
 .../tabify/__tests__/_integration.js          | 175 ---------------
 .../tabify/__tests__/_response_writer.js      | 186 ----------------
 .../ui/public/agg_response/tabify/_buckets.js | 123 -----------
 .../agg_response/tabify/_response_writer.js   |  97 ---------
 .../ui/public/agg_response/tabify/tabify.js   | 134 ------------
 .../es_geo_grid_source/es_geo_grid_source.js  |   2 +-
 32 files changed, 1053 insertions(+), 1005 deletions(-)
 rename src/fixtures/{fake_hierarchical_data.js => fake_hierarchical_data.ts} (98%)
 rename src/legacy/{ui/public/agg_response/tabify/__tests__/_buckets.js => core_plugins/data/public/search/tabify/buckets.test.ts} (66%)
 create mode 100644 src/legacy/core_plugins/data/public/search/tabify/buckets.ts
 create mode 100644 src/legacy/core_plugins/data/public/search/tabify/get_columns.test.ts
 rename src/legacy/{ui/public/agg_response/tabify/_get_columns.ts => core_plugins/data/public/search/tabify/get_columns.ts} (96%)
 rename src/legacy/{ui/public/agg_response/tabify/index.js => core_plugins/data/public/search/tabify/index.ts} (94%)
 create mode 100644 src/legacy/core_plugins/data/public/search/tabify/response_writer.test.ts
 create mode 100644 src/legacy/core_plugins/data/public/search/tabify/response_writer.ts
 create mode 100644 src/legacy/core_plugins/data/public/search/tabify/tabify.test.ts
 create mode 100644 src/legacy/core_plugins/data/public/search/tabify/tabify.ts
 rename src/legacy/{ui/public/agg_response/tabify/__tests__/tabify.js => core_plugins/data/public/search/tabify/types.ts} (69%)
 delete mode 100644 src/legacy/ui/public/agg_response/tabify/__tests__/_get_columns.js
 delete mode 100644 src/legacy/ui/public/agg_response/tabify/__tests__/_integration.js
 delete mode 100644 src/legacy/ui/public/agg_response/tabify/__tests__/_response_writer.js
 delete mode 100644 src/legacy/ui/public/agg_response/tabify/_buckets.js
 delete mode 100644 src/legacy/ui/public/agg_response/tabify/_response_writer.js
 delete mode 100644 src/legacy/ui/public/agg_response/tabify/tabify.js

diff --git a/src/fixtures/fake_hierarchical_data.js b/src/fixtures/fake_hierarchical_data.ts
similarity index 98%
rename from src/fixtures/fake_hierarchical_data.js
rename to src/fixtures/fake_hierarchical_data.ts
index b4ae02a487049..4480caae39664 100644
--- a/src/fixtures/fake_hierarchical_data.js
+++ b/src/fixtures/fake_hierarchical_data.ts
@@ -17,16 +17,14 @@
  * under the License.
  */
 
-const data = {};
-
-data.metricOnly = {
+export const metricOnly = {
   hits: { total: 1000, hits: [], max_score: 0 },
   aggregations: {
     agg_1: { value: 412032 },
   },
 };
 
-data.threeTermBuckets = {
+export const threeTermBuckets = {
   hits: { total: 1000, hits: [], max_score: 0 },
   aggregations: {
     agg_2: {
@@ -129,7 +127,7 @@ data.threeTermBuckets = {
   },
 };
 
-data.oneTermOneHistogramBucketWithTwoMetricsOneTopHitOneDerivative = {
+export const oneTermOneHistogramBucketWithTwoMetricsOneTopHitOneDerivative = {
   hits: { total: 1000, hits: [], max_score: 0 },
   aggregations: {
     agg_3: {
@@ -520,7 +518,7 @@ data.oneTermOneHistogramBucketWithTwoMetricsOneTopHitOneDerivative = {
   },
 };
 
-data.oneRangeBucket = {
+export const oneRangeBucket = {
   took: 35,
   timed_out: false,
   _shards: {
@@ -555,7 +553,7 @@ data.oneRangeBucket = {
   },
 };
 
-data.oneFilterBucket = {
+export const oneFilterBucket = {
   took: 11,
   timed_out: false,
   _shards: {
@@ -582,7 +580,7 @@ data.oneFilterBucket = {
   },
 };
 
-data.oneHistogramBucket = {
+export const oneHistogramBucket = {
   took: 37,
   timed_out: false,
   _shards: {
@@ -632,5 +630,3 @@ data.oneHistogramBucket = {
     },
   },
 };
-
-export default data;
diff --git a/src/legacy/core_plugins/data/public/index.ts b/src/legacy/core_plugins/data/public/index.ts
index 50120292a627a..ce46f534141f4 100644
--- a/src/legacy/core_plugins/data/public/index.ts
+++ b/src/legacy/core_plugins/data/public/index.ts
@@ -81,4 +81,6 @@ export {
   // search_source
   getRequestInspectorStats,
   getResponseInspectorStats,
+  tabifyAggResponse,
+  tabifyGetColumns,
 } from './search';
diff --git a/src/legacy/core_plugins/data/public/search/aggs/agg_configs.ts b/src/legacy/core_plugins/data/public/search/aggs/agg_configs.ts
index 7e7e4944b00da..8e091ed5f21ae 100644
--- a/src/legacy/core_plugins/data/public/search/aggs/agg_configs.ts
+++ b/src/legacy/core_plugins/data/public/search/aggs/agg_configs.ts
@@ -27,7 +27,7 @@
  */
 
 import _ from 'lodash';
-import { AggConfig, AggConfigOptions } from './agg_config';
+import { AggConfig, AggConfigOptions, IAggConfig } from './agg_config';
 import { Schema } from './schemas';
 import { AggGroupNames } from './agg_groups';
 import {
@@ -63,7 +63,7 @@ export class AggConfigs {
   public schemas: any;
   public timeRange?: TimeRange;
 
-  aggs: AggConfig[];
+  aggs: IAggConfig[];
 
   constructor(indexPattern: IndexPattern, configStates = [] as any, schemas?: any) {
     configStates = AggConfig.ensureIds(configStates);
@@ -74,7 +74,7 @@ export class AggConfigs {
 
     configStates.forEach((params: any) => this.createAggConfig(params));
 
-    if (this.schemas) {
+    if (schemas) {
       this.initializeDefaultsFromSchemas(schemas);
     }
   }
diff --git a/src/legacy/core_plugins/data/public/search/aggs/agg_params.ts b/src/legacy/core_plugins/data/public/search/aggs/agg_params.ts
index 34727ff4614b9..551cb81529a0a 100644
--- a/src/legacy/core_plugins/data/public/search/aggs/agg_params.ts
+++ b/src/legacy/core_plugins/data/public/search/aggs/agg_params.ts
@@ -76,7 +76,9 @@ export const writeParams = <
   aggs?: IAggConfigs,
   locals?: Record<string, any>
 ) => {
-  const output = { params: {} as Record<string, any> };
+  const output: Record<string, any> = {
+    params: {} as Record<string, any>,
+  };
   locals = locals || {};
 
   params.forEach(param => {
diff --git a/src/legacy/core_plugins/data/public/search/aggs/index.ts b/src/legacy/core_plugins/data/public/search/aggs/index.ts
index 0fef7f38aae74..0bdb92b8de65e 100644
--- a/src/legacy/core_plugins/data/public/search/aggs/index.ts
+++ b/src/legacy/core_plugins/data/public/search/aggs/index.ts
@@ -50,3 +50,6 @@ export { isValidJson, isValidInterval } from './utils';
 export { BUCKET_TYPES } from './buckets/bucket_agg_types';
 export { METRIC_TYPES } from './metrics/metric_agg_types';
 export { ISchemas, Schema, Schemas } from './schemas';
+
+// types
+export { IAggConfig, IAggConfigs } from './types';
diff --git a/src/legacy/core_plugins/data/public/search/expressions/esaggs.ts b/src/legacy/core_plugins/data/public/search/expressions/esaggs.ts
index 9aee7124c9521..302527e4ed549 100644
--- a/src/legacy/core_plugins/data/public/search/expressions/esaggs.ts
+++ b/src/legacy/core_plugins/data/public/search/expressions/esaggs.ts
@@ -39,8 +39,7 @@ import {
 
 import { buildTabularInspectorData } from './build_tabular_inspector_data';
 import { calculateObjectHash } from '../../../../visualizations/public';
-// @ts-ignore
-import { tabifyAggResponse } from '../../../../../ui/public/agg_response/tabify/tabify';
+import { tabifyAggResponse } from '../../../../../core_plugins/data/public';
 import { PersistedState } from '../../../../../ui/public/persisted_state';
 import { Adapters } from '../../../../../../plugins/inspector/public';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
diff --git a/src/legacy/core_plugins/data/public/search/index.ts b/src/legacy/core_plugins/data/public/search/index.ts
index 90e191b769a8d..96d2825559da2 100644
--- a/src/legacy/core_plugins/data/public/search/index.ts
+++ b/src/legacy/core_plugins/data/public/search/index.ts
@@ -20,3 +20,4 @@
 export * from './aggs';
 export { getRequestInspectorStats, getResponseInspectorStats } from './utils';
 export { serializeAggConfig } from './expressions/utils';
+export { tabifyAggResponse, tabifyGetColumns } from './tabify';
diff --git a/src/legacy/ui/public/agg_response/tabify/__tests__/_buckets.js b/src/legacy/core_plugins/data/public/search/tabify/buckets.test.ts
similarity index 66%
rename from src/legacy/ui/public/agg_response/tabify/__tests__/_buckets.js
rename to src/legacy/core_plugins/data/public/search/tabify/buckets.test.ts
index b85b45d3c5820..ef2748102623a 100644
--- a/src/legacy/ui/public/agg_response/tabify/__tests__/_buckets.js
+++ b/src/legacy/core_plugins/data/public/search/tabify/buckets.test.ts
@@ -17,31 +17,36 @@
  * under the License.
  */
 
-import expect from '@kbn/expect';
-import { TabifyBuckets } from '../_buckets';
+import { TabifyBuckets } from './buckets';
+import { AggGroupNames } from '../aggs';
 
-describe('Buckets wrapper', function() {
-  function test(aggResp, count, keys) {
-    it('reads the length', function() {
+jest.mock('ui/new_platform');
+
+describe('Buckets wrapper', () => {
+  const check = (aggResp: any, count: number, keys: string[]) => {
+    test('reads the length', () => {
       const buckets = new TabifyBuckets(aggResp);
-      expect(buckets).to.have.length(count);
+      expect(buckets).toHaveLength(count);
     });
 
-    it('iterates properly, passing in the key', function() {
+    test('iterates properly, passing in the key', () => {
       const buckets = new TabifyBuckets(aggResp);
-      const keysSent = [];
-      buckets.forEach(function(bucket, key) {
-        keysSent.push(key);
+      const keysSent: any[] = [];
+
+      buckets.forEach((bucket, key) => {
+        if (key) {
+          keysSent.push(key);
+        }
       });
 
-      expect(keysSent).to.have.length(count);
-      expect(keysSent).to.eql(keys);
+      expect(keysSent).toHaveLength(count);
+      expect(keysSent).toEqual(keys);
     });
-  }
+  };
 
-  describe('with object style buckets', function() {
-    const aggResp = {
-      buckets: {
+  describe('with object style buckets', () => {
+    let aggResp: any = {
+      [AggGroupNames.Buckets]: {
         '0-100': {},
         '100-200': {},
         '200-300': {},
@@ -51,11 +56,11 @@ describe('Buckets wrapper', function() {
     const count = 3;
     const keys = ['0-100', '100-200', '200-300'];
 
-    test(aggResp, count, keys);
+    check(aggResp, count, keys);
 
-    it('should accept filters agg queries with strings', () => {
-      const aggResp = {
-        buckets: {
+    test('should accept filters agg queries with strings', () => {
+      aggResp = {
+        [AggGroupNames.Buckets]: {
           'response:200': {},
           'response:404': {},
         },
@@ -75,15 +80,17 @@ describe('Buckets wrapper', function() {
       };
 
       const buckets = new TabifyBuckets(aggResp, aggParams);
-      expect(buckets).to.have.length(2);
+
+      expect(buckets).toHaveLength(2);
+
       buckets._keys.forEach(key => {
-        expect(key).to.be.a('string');
+        expect(typeof key).toBe('string');
       });
     });
 
-    it('should accept filters agg queries with query_string queries', () => {
-      const aggResp = {
-        buckets: {
+    test('should accept filters agg queries with query_string queries', () => {
+      aggResp = {
+        [AggGroupNames.Buckets]: {
           'response:200': {},
           'response:404': {},
         },
@@ -103,15 +110,17 @@ describe('Buckets wrapper', function() {
       };
 
       const buckets = new TabifyBuckets(aggResp, aggParams);
-      expect(buckets).to.have.length(2);
+
+      expect(buckets).toHaveLength(2);
+
       buckets._keys.forEach(key => {
-        expect(key).to.be.a('string');
+        expect(typeof key).toBe('string');
       });
     });
 
-    it('should accept filters agg queries with query dsl queries', () => {
-      const aggResp = {
-        buckets: {
+    test('should accept filters agg queries with query dsl queries', () => {
+      aggResp = {
+        [AggGroupNames.Buckets]: {
           '{match_all: {}}': {},
         },
       };
@@ -126,16 +135,18 @@ describe('Buckets wrapper', function() {
       };
 
       const buckets = new TabifyBuckets(aggResp, aggParams);
-      expect(buckets).to.have.length(1);
+
+      expect(buckets).toHaveLength(1);
+
       buckets._keys.forEach(key => {
-        expect(key).to.be.a('string');
+        expect(typeof key).toBe('string');
       });
     });
   });
 
-  describe('with array style buckets', function() {
+  describe('with array style buckets', () => {
     const aggResp = {
-      buckets: [
+      [AggGroupNames.Buckets]: [
         { key: '0-100', value: {} },
         { key: '100-200', value: {} },
         { key: '200-300', value: {} },
@@ -145,23 +156,24 @@ describe('Buckets wrapper', function() {
     const count = 3;
     const keys = ['0-100', '100-200', '200-300'];
 
-    test(aggResp, count, keys);
+    check(aggResp, count, keys);
   });
 
-  describe('with single bucket aggregations (filter)', function() {
-    it('creates single bucket from agg content', function() {
+  describe('with single bucket aggregations (filter)', () => {
+    test('creates single bucket from agg content', () => {
       const aggResp = {
         single_bucket: {},
         doc_count: 5,
       };
       const buckets = new TabifyBuckets(aggResp);
-      expect(buckets).to.have.length(1);
+
+      expect(buckets).toHaveLength(1);
     });
   });
 
-  describe('drop_partial option', function() {
+  describe('drop_partial option', () => {
     const aggResp = {
-      buckets: [
+      [AggGroupNames.Buckets]: [
         { key: 0, value: {} },
         { key: 100, value: {} },
         { key: 200, value: {} },
@@ -169,7 +181,7 @@ describe('Buckets wrapper', function() {
       ],
     };
 
-    it('drops partial buckets when enabled', function() {
+    test('drops partial buckets when enabled', () => {
       const aggParams = {
         drop_partials: true,
         field: {
@@ -182,10 +194,11 @@ describe('Buckets wrapper', function() {
         name: 'date',
       };
       const buckets = new TabifyBuckets(aggResp, aggParams, timeRange);
-      expect(buckets).to.have.length(1);
+
+      expect(buckets).toHaveLength(1);
     });
 
-    it('keeps partial buckets when disabled', function() {
+    test('keeps partial buckets when disabled', () => {
       const aggParams = {
         drop_partials: false,
         field: {
@@ -198,10 +211,11 @@ describe('Buckets wrapper', function() {
         name: 'date',
       };
       const buckets = new TabifyBuckets(aggResp, aggParams, timeRange);
-      expect(buckets).to.have.length(4);
+
+      expect(buckets).toHaveLength(4);
     });
 
-    it('keeps aligned buckets when enabled', function() {
+    test('keeps aligned buckets when enabled', () => {
       const aggParams = {
         drop_partials: true,
         field: {
@@ -214,10 +228,11 @@ describe('Buckets wrapper', function() {
         name: 'date',
       };
       const buckets = new TabifyBuckets(aggResp, aggParams, timeRange);
-      expect(buckets).to.have.length(3);
+
+      expect(buckets).toHaveLength(3);
     });
 
-    it('does not drop buckets for non-timerange fields', function() {
+    test('does not drop buckets for non-timerange fields', () => {
       const aggParams = {
         drop_partials: true,
         field: {
@@ -230,7 +245,8 @@ describe('Buckets wrapper', function() {
         name: 'date',
       };
       const buckets = new TabifyBuckets(aggResp, aggParams, timeRange);
-      expect(buckets).to.have.length(4);
+
+      expect(buckets).toHaveLength(4);
     });
   });
 });
diff --git a/src/legacy/core_plugins/data/public/search/tabify/buckets.ts b/src/legacy/core_plugins/data/public/search/tabify/buckets.ts
new file mode 100644
index 0000000000000..8078136299f8c
--- /dev/null
+++ b/src/legacy/core_plugins/data/public/search/tabify/buckets.ts
@@ -0,0 +1,135 @@
+/*
+ * 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 { get, isPlainObject, keys, findKey } from 'lodash';
+import moment from 'moment';
+import { IAggConfig } from '../aggs';
+import { TabbedRangeFilterParams } from './types';
+import { AggResponseBucket } from '../types';
+
+type AggParams = IAggConfig['params'] & {
+  drop_partials: boolean;
+  ranges: TabbedRangeFilterParams[];
+};
+
+const isRangeEqual = (range1: TabbedRangeFilterParams, range2: TabbedRangeFilterParams) =>
+  range1?.from === range2?.from && range1?.to === range2?.to;
+
+export class TabifyBuckets {
+  length: number;
+  objectMode: boolean;
+  buckets: any;
+  _keys: any[] = [];
+
+  constructor(aggResp: any, aggParams?: AggParams, timeRange?: TabbedRangeFilterParams) {
+    if (aggResp && aggResp.buckets) {
+      this.buckets = aggResp.buckets;
+    } else if (aggResp) {
+      // Some Bucket Aggs only return a single bucket (like filter).
+      // In those instances, the aggResp is the content of the single bucket.
+      this.buckets = [aggResp];
+    } else {
+      this.buckets = [];
+    }
+
+    this.objectMode = isPlainObject(this.buckets);
+
+    if (this.objectMode) {
+      this._keys = keys(this.buckets);
+      this.length = this._keys.length;
+    } else {
+      this.length = this.buckets.length;
+    }
+
+    if (this.length && aggParams) {
+      this.orderBucketsAccordingToParams(aggParams);
+      if (aggParams.drop_partials) {
+        this.dropPartials(aggParams, timeRange);
+      }
+    }
+  }
+
+  forEach(fn: (bucket: any, key: any) => void) {
+    const buckets = this.buckets;
+
+    if (this.objectMode) {
+      this._keys.forEach(key => {
+        fn(buckets[key], key);
+      });
+    } else {
+      buckets.forEach((bucket: AggResponseBucket) => {
+        fn(bucket, bucket.key);
+      });
+    }
+  }
+
+  private orderBucketsAccordingToParams(params: AggParams) {
+    if (params.filters && this.objectMode) {
+      this._keys = params.filters.map((filter: any) => {
+        const query = get(filter, 'input.query.query_string.query', filter.input.query);
+        const queryString = typeof query === 'string' ? query : JSON.stringify(query);
+
+        return filter.label || queryString || '*';
+      });
+    } else if (params.ranges && this.objectMode) {
+      this._keys = params.ranges.map((range: TabbedRangeFilterParams) =>
+        findKey(this.buckets, (el: TabbedRangeFilterParams) => isRangeEqual(el, range))
+      );
+    } else if (params.ranges && params.field.type !== 'date') {
+      let ranges = params.ranges;
+      if (params.ipRangeType) {
+        ranges = params.ipRangeType === 'mask' ? ranges.mask : ranges.fromTo;
+      }
+      this.buckets = ranges.map((range: any) => {
+        if (range.mask) {
+          return this.buckets.find((el: AggResponseBucket) => el.key === range.mask);
+        }
+
+        return this.buckets.find((el: TabbedRangeFilterParams) => isRangeEqual(el, range));
+      });
+    }
+  }
+
+  // dropPartials should only be called if the aggParam setting is enabled,
+  // and the agg field is the same as the Time Range.
+  private dropPartials(params: AggParams, timeRange?: TabbedRangeFilterParams) {
+    if (
+      !timeRange ||
+      this.buckets.length <= 1 ||
+      this.objectMode ||
+      params.field.name !== timeRange.name
+    ) {
+      return;
+    }
+
+    const interval = this.buckets[1].key - this.buckets[0].key;
+
+    this.buckets = this.buckets.filter((bucket: AggResponseBucket) => {
+      if (moment(bucket.key).isBefore(timeRange.gte)) {
+        return false;
+      }
+      if (moment(bucket.key + interval).isAfter(timeRange.lte)) {
+        return false;
+      }
+      return true;
+    });
+
+    this.length = this.buckets.length;
+  }
+}
diff --git a/src/legacy/core_plugins/data/public/search/tabify/get_columns.test.ts b/src/legacy/core_plugins/data/public/search/tabify/get_columns.test.ts
new file mode 100644
index 0000000000000..0328e87d8b832
--- /dev/null
+++ b/src/legacy/core_plugins/data/public/search/tabify/get_columns.test.ts
@@ -0,0 +1,191 @@
+/*
+ * 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 { tabifyGetColumns, AggColumn } from './get_columns';
+import { AggConfigs, AggGroupNames, Schemas } from '../aggs';
+
+jest.mock('ui/new_platform');
+
+describe('get columns', () => {
+  const createAggConfigs = (aggs: any[] = []) => {
+    const field = {
+      name: '@timestamp',
+    };
+
+    const indexPattern = {
+      id: '1234',
+      title: 'logstash-*',
+      fields: {
+        getByName: () => field,
+        filter: () => [field],
+      },
+    } as any;
+
+    return new AggConfigs(
+      indexPattern,
+      aggs,
+      new Schemas([
+        {
+          group: AggGroupNames.Metrics,
+          name: 'metric',
+          min: 1,
+          defaults: [{ schema: 'metric', type: 'count' }],
+        },
+      ]).all
+    );
+  };
+
+  test('should inject a count metric if no aggs exist', () => {
+    const columns = tabifyGetColumns(createAggConfigs().aggs, true);
+
+    expect(columns).toHaveLength(1);
+    expect(columns[0]).toHaveProperty('aggConfig');
+    expect(columns[0].aggConfig.type).toHaveProperty('name', 'count');
+  });
+
+  test('should inject a count metric if only buckets exist', () => {
+    const columns = tabifyGetColumns(
+      createAggConfigs([
+        {
+          type: 'date_histogram',
+          schema: 'segment',
+          params: { field: '@timestamp', interval: '10s' },
+        },
+      ]).aggs,
+      true
+    );
+
+    expect(columns).toHaveLength(2);
+    expect(columns[1]).toHaveProperty('aggConfig');
+    expect(columns[1].aggConfig.type).toHaveProperty('name', 'count');
+  });
+
+  test('should inject the metric after each bucket if the vis is hierarchical', () => {
+    const columns = tabifyGetColumns(
+      createAggConfigs([
+        {
+          type: 'date_histogram',
+          schema: 'segment',
+          params: { field: '@timestamp', interval: '10s' },
+        },
+        {
+          type: 'date_histogram',
+          schema: 'segment',
+          params: { field: '@timestamp', interval: '10s' },
+        },
+        {
+          type: 'date_histogram',
+          schema: 'segment',
+          params: { field: '@timestamp', interval: '10s' },
+        },
+        {
+          type: 'date_histogram',
+          schema: 'segment',
+          params: { field: '@timestamp', interval: '10s' },
+        },
+      ]).aggs,
+      false
+    );
+
+    expect(columns).toHaveLength(8);
+
+    columns.forEach((column, i) => {
+      expect(column).toHaveProperty('aggConfig');
+      expect(column.aggConfig.type).toHaveProperty('name', i % 2 ? 'count' : 'date_histogram');
+    });
+  });
+
+  test('should inject the multiple metrics after each bucket if the vis is hierarchical', () => {
+    const columns = tabifyGetColumns(
+      createAggConfigs([
+        {
+          type: 'date_histogram',
+          schema: 'segment',
+          params: { field: '@timestamp', interval: '10s' },
+        },
+        { type: 'avg', schema: 'metric', params: { field: 'bytes' } },
+        {
+          type: 'date_histogram',
+          schema: 'segment',
+          params: { field: '@timestamp', interval: '10s' },
+        },
+        {
+          type: 'date_histogram',
+          schema: 'segment',
+          params: { field: '@timestamp', interval: '10s' },
+        },
+        { type: 'sum', schema: 'metric', params: { field: 'bytes' } },
+        {
+          type: 'date_histogram',
+          schema: 'segment',
+          params: { field: '@timestamp', interval: '10s' },
+        },
+      ]).aggs,
+      false
+    );
+
+    function checkColumns(column: AggColumn, i: number) {
+      expect(column).toHaveProperty('aggConfig');
+
+      switch (i) {
+        case 0:
+          expect(column.aggConfig.type).toHaveProperty('name', 'date_histogram');
+          break;
+        case 1:
+          expect(column.aggConfig.type).toHaveProperty('name', 'avg');
+          break;
+        case 2:
+          expect(column.aggConfig.type).toHaveProperty('name', 'sum');
+          break;
+      }
+    }
+
+    expect(columns).toHaveLength(12);
+
+    for (let i = 0; i < columns.length; i += 3) {
+      columns.slice(i, i + 3).forEach(checkColumns);
+    }
+  });
+
+  test('should put all metrics at the end of the columns if the vis is not hierarchical', () => {
+    const columns = tabifyGetColumns(
+      createAggConfigs([
+        {
+          type: 'date_histogram',
+          schema: 'segment',
+          params: { field: '@timestamp', interval: '20s' },
+        },
+        { type: 'sum', schema: 'metric', params: { field: '@timestamp' } },
+        {
+          type: 'date_histogram',
+          schema: 'segment',
+          params: { field: '@timestamp', interval: '10s' },
+        },
+      ]).aggs,
+      false
+    );
+
+    expect(columns.map(c => c.name)).toEqual([
+      '@timestamp per 20 seconds',
+      'Sum of @timestamp',
+      '@timestamp per 10 seconds',
+      'Sum of @timestamp',
+    ]);
+  });
+});
diff --git a/src/legacy/ui/public/agg_response/tabify/_get_columns.ts b/src/legacy/core_plugins/data/public/search/tabify/get_columns.ts
similarity index 96%
rename from src/legacy/ui/public/agg_response/tabify/_get_columns.ts
rename to src/legacy/core_plugins/data/public/search/tabify/get_columns.ts
index 4144d5be16012..54f09f6c6364f 100644
--- a/src/legacy/ui/public/agg_response/tabify/_get_columns.ts
+++ b/src/legacy/core_plugins/data/public/search/tabify/get_columns.ts
@@ -18,7 +18,7 @@
  */
 
 import { groupBy } from 'lodash';
-import { IAggConfig } from '../../agg_types';
+import { IAggConfig } from '../aggs';
 
 export interface AggColumn {
   aggConfig: IAggConfig;
@@ -40,7 +40,7 @@ const getColumn = (agg: IAggConfig, i: number): AggColumn => {
  * @param {AggConfigs} aggs - the agg configs object to which the aggregation response correlates
  * @param {boolean} minimalColumns - setting to true will only return a column for the last bucket/metric instead of one for each level
  */
-export function tabifyGetColumns(aggs: IAggConfig[], minimalColumns: boolean) {
+export function tabifyGetColumns(aggs: IAggConfig[], minimalColumns: boolean): AggColumn[] {
   // pick the columns
   if (minimalColumns) {
     return aggs.map((agg, i) => getColumn(agg, i));
diff --git a/src/legacy/ui/public/agg_response/tabify/index.js b/src/legacy/core_plugins/data/public/search/tabify/index.ts
similarity index 94%
rename from src/legacy/ui/public/agg_response/tabify/index.js
rename to src/legacy/core_plugins/data/public/search/tabify/index.ts
index f14ca647e4b32..be8d64510033c 100644
--- a/src/legacy/ui/public/agg_response/tabify/index.js
+++ b/src/legacy/core_plugins/data/public/search/tabify/index.ts
@@ -18,3 +18,4 @@
  */
 
 export { tabifyAggResponse } from './tabify';
+export { tabifyGetColumns } from './get_columns';
diff --git a/src/legacy/core_plugins/data/public/search/tabify/response_writer.test.ts b/src/legacy/core_plugins/data/public/search/tabify/response_writer.test.ts
new file mode 100644
index 0000000000000..f5df0a683ca00
--- /dev/null
+++ b/src/legacy/core_plugins/data/public/search/tabify/response_writer.test.ts
@@ -0,0 +1,170 @@
+/*
+ * 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 { TabbedAggResponseWriter } from './response_writer';
+import { AggConfigs, AggGroupNames, Schemas, BUCKET_TYPES } from '../aggs';
+
+import { TabbedResponseWriterOptions } from './types';
+
+jest.mock('ui/new_platform');
+
+describe('TabbedAggResponseWriter class', () => {
+  let responseWriter: TabbedAggResponseWriter;
+
+  const splitAggConfig = [
+    {
+      type: BUCKET_TYPES.TERMS,
+      params: {
+        field: 'geo.src',
+      },
+    },
+  ];
+
+  const twoSplitsAggConfig = [
+    {
+      type: BUCKET_TYPES.TERMS,
+      params: {
+        field: 'geo.src',
+      },
+    },
+    {
+      type: BUCKET_TYPES.TERMS,
+      params: {
+        field: 'machine.os.raw',
+      },
+    },
+  ];
+
+  const createResponseWritter = (aggs: any[] = [], opts?: Partial<TabbedResponseWriterOptions>) => {
+    const field = {
+      name: 'geo.src',
+    };
+
+    const indexPattern = {
+      id: '1234',
+      title: 'logstash-*',
+      fields: {
+        getByName: () => field,
+        filter: () => [field],
+      },
+    } as any;
+
+    return new TabbedAggResponseWriter(
+      new AggConfigs(
+        indexPattern,
+        aggs,
+        new Schemas([
+          {
+            group: AggGroupNames.Metrics,
+            name: 'metric',
+            min: 1,
+            defaults: [{ schema: 'metric', type: 'count' }],
+          },
+        ]).all
+      ),
+      {
+        metricsAtAllLevels: false,
+        partialRows: false,
+        ...opts,
+      }
+    );
+  };
+
+  describe('Constructor', () => {
+    beforeEach(() => {
+      responseWriter = createResponseWritter(twoSplitsAggConfig);
+    });
+
+    test('generates columns', () => {
+      expect(responseWriter.columns.length).toEqual(3);
+    });
+
+    test('correctly generates columns with metricsAtAllLevels set to true', () => {
+      const minimalColumnsResponseWriter = createResponseWritter(twoSplitsAggConfig, {
+        metricsAtAllLevels: true,
+      });
+
+      expect(minimalColumnsResponseWriter.columns.length).toEqual(4);
+    });
+
+    describe('row()', () => {
+      beforeEach(() => {
+        responseWriter = createResponseWritter(splitAggConfig);
+      });
+
+      test('adds the row to the array', () => {
+        responseWriter.bucketBuffer = [{ id: 'col-0', value: 'US' }];
+        responseWriter.metricBuffer = [{ id: 'col-1', value: 5 }];
+
+        responseWriter.row();
+
+        expect(responseWriter.rows.length).toEqual(1);
+        expect(responseWriter.rows[0]).toEqual({ 'col-0': 'US', 'col-1': 5 });
+      });
+
+      test("doesn't add an empty row", () => {
+        responseWriter.row();
+
+        expect(responseWriter.rows.length).toEqual(0);
+      });
+    });
+
+    describe('response()', () => {
+      beforeEach(() => {
+        responseWriter = createResponseWritter(splitAggConfig);
+      });
+
+      test('produces correct response', () => {
+        responseWriter.bucketBuffer = [
+          { id: 'col-0-1', value: 'US' },
+          { id: 'col-1-2', value: 5 },
+        ];
+        responseWriter.row();
+
+        const response = responseWriter.response();
+
+        expect(response).toHaveProperty('rows');
+        expect(response.rows).toEqual([{ 'col-0-1': 'US', 'col-1-2': 5 }]);
+        expect(response).toHaveProperty('columns');
+        expect(response.columns.length).toEqual(2);
+        expect(response.columns[0]).toHaveProperty('id', 'col-0-1');
+        expect(response.columns[0]).toHaveProperty('name', 'geo.src: Descending');
+        expect(response.columns[0]).toHaveProperty('aggConfig');
+        expect(response.columns[1]).toHaveProperty('id', 'col-1-2');
+        expect(response.columns[1]).toHaveProperty('name', 'Count');
+        expect(response.columns[1]).toHaveProperty('aggConfig');
+      });
+
+      test('produces correct response for no data', () => {
+        const response = responseWriter.response();
+
+        expect(response).toHaveProperty('rows');
+        expect(response.rows.length).toBe(0);
+        expect(response).toHaveProperty('columns');
+        expect(response.columns.length).toEqual(2);
+        expect(response.columns[0]).toHaveProperty('id', 'col-0-1');
+        expect(response.columns[0]).toHaveProperty('name', 'geo.src: Descending');
+        expect(response.columns[0]).toHaveProperty('aggConfig');
+        expect(response.columns[1]).toHaveProperty('id', 'col-1-2');
+        expect(response.columns[1]).toHaveProperty('name', 'Count');
+        expect(response.columns[1]).toHaveProperty('aggConfig');
+      });
+    });
+  });
+});
diff --git a/src/legacy/core_plugins/data/public/search/tabify/response_writer.ts b/src/legacy/core_plugins/data/public/search/tabify/response_writer.ts
new file mode 100644
index 0000000000000..4c4578e505b71
--- /dev/null
+++ b/src/legacy/core_plugins/data/public/search/tabify/response_writer.ts
@@ -0,0 +1,88 @@
+/*
+ * 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 { isEmpty } from 'lodash';
+import { IAggConfigs } from '../aggs/agg_configs';
+import { AggColumn, tabifyGetColumns } from './get_columns';
+
+import { TabbedResponseWriterOptions } from './types';
+
+interface TabbedAggColumn {
+  id: string;
+  value: string | number;
+}
+
+type TabbedAggRow = Record<TabbedAggColumn['id'], TabbedAggColumn['value']>;
+
+/**
+ * Writer class that collects information about an aggregation response and
+ * produces a table, or a series of tables.
+ */
+export class TabbedAggResponseWriter {
+  columns: AggColumn[];
+  rows: TabbedAggRow[] = [];
+  bucketBuffer: TabbedAggColumn[] = [];
+  metricBuffer: TabbedAggColumn[] = [];
+
+  private readonly partialRows: boolean;
+
+  /**
+   * @param {AggConfigs} aggs - the agg configs object to which the aggregation response correlates
+   * @param {boolean} metricsAtAllLevels - setting to true will produce metrics for every bucket
+   * @param {boolean} partialRows - setting to true will not remove rows with missing values
+   */
+  constructor(
+    aggs: IAggConfigs,
+    { metricsAtAllLevels = false, partialRows = false }: Partial<TabbedResponseWriterOptions>
+  ) {
+    this.partialRows = partialRows;
+
+    this.columns = tabifyGetColumns(aggs.getResponseAggs(), !metricsAtAllLevels);
+    this.rows = [];
+  }
+
+  /**
+   * Create a new row by reading the row buffer and bucketBuffer
+   */
+  row() {
+    const rowBuffer: TabbedAggRow = {};
+
+    this.bucketBuffer.forEach(bucket => {
+      rowBuffer[bucket.id] = bucket.value;
+    });
+
+    this.metricBuffer.forEach(metric => {
+      rowBuffer[metric.id] = metric.value;
+    });
+
+    const isPartialRow =
+      this.partialRows && !this.columns.every(column => rowBuffer.hasOwnProperty(column.id));
+
+    if (!isEmpty(rowBuffer) && !isPartialRow) {
+      this.rows.push(rowBuffer);
+    }
+  }
+
+  response() {
+    return {
+      columns: this.columns,
+      rows: this.rows,
+    };
+  }
+}
diff --git a/src/legacy/core_plugins/data/public/search/tabify/tabify.test.ts b/src/legacy/core_plugins/data/public/search/tabify/tabify.test.ts
new file mode 100644
index 0000000000000..13fe7719b0a85
--- /dev/null
+++ b/src/legacy/core_plugins/data/public/search/tabify/tabify.test.ts
@@ -0,0 +1,172 @@
+/*
+ * 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 { IndexPattern } from '../../../../../../plugins/data/public';
+import { tabifyAggResponse } from './tabify';
+import { IAggConfig, IAggConfigs, AggGroupNames, Schemas, AggConfigs } from '../aggs';
+import { metricOnly, threeTermBuckets } from 'fixtures/fake_hierarchical_data';
+
+jest.mock('ui/new_platform');
+
+describe('tabifyAggResponse Integration', () => {
+  const createAggConfigs = (aggs: IAggConfig[] = []) => {
+    const field = {
+      name: '@timestamp',
+    };
+
+    const indexPattern = ({
+      id: '1234',
+      title: 'logstash-*',
+      fields: {
+        getByName: () => field,
+        filter: () => [field],
+      },
+    } as unknown) as IndexPattern;
+
+    return new AggConfigs(
+      indexPattern,
+      aggs,
+      new Schemas([
+        {
+          group: AggGroupNames.Metrics,
+          name: 'metric',
+          min: 1,
+          defaults: [{ schema: 'metric', type: 'count' }],
+        },
+      ]).all
+    );
+  };
+
+  const mockAggConfig = (agg: any): IAggConfig => (agg as unknown) as IAggConfig;
+
+  test('transforms a simple response properly', () => {
+    const aggConfigs = createAggConfigs();
+
+    const resp = tabifyAggResponse(aggConfigs, metricOnly, {
+      metricsAtAllLevels: true,
+    });
+
+    expect(resp).toHaveProperty('rows');
+    expect(resp).toHaveProperty('columns');
+
+    expect(resp.rows).toHaveLength(1);
+    expect(resp.columns).toHaveLength(1);
+
+    expect(resp.rows[0]).toEqual({ 'col-0-1': 1000 });
+    expect(resp.columns[0]).toHaveProperty('aggConfig', aggConfigs.aggs[0]);
+  });
+
+  describe('transforms a complex response', () => {
+    let esResp: typeof threeTermBuckets;
+    let aggConfigs: IAggConfigs;
+    let avg: IAggConfig;
+    let ext: IAggConfig;
+    let src: IAggConfig;
+    let os: IAggConfig;
+
+    beforeEach(() => {
+      aggConfigs = createAggConfigs([
+        mockAggConfig({ type: 'avg', schema: 'metric', params: { field: '@timestamp' } }),
+        mockAggConfig({ type: 'terms', schema: 'split', params: { field: '@timestamp' } }),
+        mockAggConfig({ type: 'terms', schema: 'segment', params: { field: '@timestamp' } }),
+        mockAggConfig({ type: 'terms', schema: 'segment', params: { field: '@timestamp' } }),
+      ]);
+
+      [avg, ext, src, os] = aggConfigs.aggs;
+
+      esResp = threeTermBuckets;
+      esResp.aggregations.agg_2.buckets[1].agg_3.buckets[0].agg_4.buckets = [];
+    });
+
+    // check that the columns of a table are formed properly
+    function expectColumns(table: ReturnType<typeof tabifyAggResponse>, aggs: IAggConfig[]) {
+      expect(table.columns).toHaveLength(aggs.length);
+
+      aggs.forEach((agg, i) => {
+        expect(table.columns[i]).toHaveProperty('aggConfig', agg);
+      });
+    }
+
+    // check that a row has expected values
+    function expectRow(
+      row: Record<string, string | number>,
+      asserts: Array<(val: string | number) => void>
+    ) {
+      expect(typeof row).toBe('object');
+
+      asserts.forEach((assert, i: number) => {
+        if (row[`col-${i}`]) {
+          assert(row[`col-${i}`]);
+        }
+      });
+    }
+
+    // check for two character country code
+    function expectCountry(val: string | number) {
+      expect(typeof val).toBe('string');
+      expect(val).toHaveLength(2);
+    }
+
+    // check for an OS term
+    function expectExtension(val: string | number) {
+      expect(val).toMatch(/^(js|png|html|css|jpg)$/);
+    }
+
+    // check for an OS term
+    function expectOS(val: string | number) {
+      expect(val).toMatch(/^(win|mac|linux)$/);
+    }
+
+    // check for something like an average bytes result
+    function expectAvgBytes(val: string | number) {
+      expect(typeof val).toBe('number');
+      expect(val === 0 || val > 1000).toBeDefined();
+    }
+
+    test('for non-hierarchical vis', () => {
+      // the default for a non-hierarchical vis is to display
+      // only complete rows, and only put the metrics at the end.
+
+      const tabbed = tabifyAggResponse(aggConfigs, esResp, { metricsAtAllLevels: false });
+
+      expectColumns(tabbed, [ext, src, os, avg]);
+
+      tabbed.rows.forEach(row => {
+        expectRow(row, [expectExtension, expectCountry, expectOS, expectAvgBytes]);
+      });
+    });
+
+    test('for hierarchical vis', () => {
+      const tabbed = tabifyAggResponse(aggConfigs, esResp, { metricsAtAllLevels: true });
+
+      expectColumns(tabbed, [ext, avg, src, avg, os, avg]);
+
+      tabbed.rows.forEach(row => {
+        expectRow(row, [
+          expectExtension,
+          expectAvgBytes,
+          expectCountry,
+          expectAvgBytes,
+          expectOS,
+          expectAvgBytes,
+        ]);
+      });
+    });
+  });
+});
diff --git a/src/legacy/core_plugins/data/public/search/tabify/tabify.ts b/src/legacy/core_plugins/data/public/search/tabify/tabify.ts
new file mode 100644
index 0000000000000..078d3f7f72759
--- /dev/null
+++ b/src/legacy/core_plugins/data/public/search/tabify/tabify.ts
@@ -0,0 +1,173 @@
+/*
+ * 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 { get } from 'lodash';
+import { TabbedAggResponseWriter } from './response_writer';
+import { TabifyBuckets } from './buckets';
+import { TabbedResponseWriterOptions, TabbedRangeFilterParams } from './types';
+import { AggResponseBucket } from '../types';
+import { IAggConfigs, AggGroupNames } from '../aggs';
+
+/**
+ * Sets up the ResponseWriter and kicks off bucket collection.
+ */
+export function tabifyAggResponse(
+  aggConfigs: IAggConfigs,
+  esResponse: Record<string, any>,
+  respOpts?: Partial<TabbedResponseWriterOptions>
+) {
+  /**
+   * read an aggregation from a bucket, which *might* be found at key (if
+   * the response came in object form), and will recurse down the aggregation
+   * tree and will pass the read values to the ResponseWriter.
+   */
+  function collectBucket(
+    aggs: IAggConfigs,
+    write: TabbedAggResponseWriter,
+    bucket: AggResponseBucket,
+    key: string,
+    aggScale: number
+  ) {
+    const column = write.columns.shift();
+
+    if (column) {
+      const agg = column.aggConfig;
+      const aggInfo = agg.write(aggs);
+      aggScale *= aggInfo.metricScale || 1;
+
+      switch (agg.type.type) {
+        case AggGroupNames.Buckets:
+          const aggBucket = get(bucket, agg.id);
+          const tabifyBuckets = new TabifyBuckets(aggBucket, agg.params, timeRange);
+
+          if (tabifyBuckets.length) {
+            tabifyBuckets.forEach((subBucket, tabifyBucketKey) => {
+              // if the bucket doesn't have value don't add it to the row
+              // we don't want rows like: { column1: undefined, column2: 10 }
+              const bucketValue = agg.getKey(subBucket, tabifyBucketKey);
+              const hasBucketValue = typeof bucketValue !== 'undefined';
+
+              if (hasBucketValue) {
+                write.bucketBuffer.push({ id: column.id, value: bucketValue });
+              }
+
+              collectBucket(
+                aggs,
+                write,
+                subBucket,
+                agg.getKey(subBucket, tabifyBucketKey),
+                aggScale
+              );
+
+              if (hasBucketValue) {
+                write.bucketBuffer.pop();
+              }
+            });
+          } else if (respOpts?.partialRows) {
+            // we don't have any buckets, but we do have metrics at this
+            // level, then pass all the empty buckets and jump back in for
+            // the metrics.
+            write.columns.unshift(column);
+            passEmptyBuckets(aggs, write, bucket, key, aggScale);
+            write.columns.shift();
+          } else {
+            // we don't have any buckets, and we don't have isHierarchical
+            // data, so no metrics, just try to write the row
+            write.row();
+          }
+          break;
+        case AggGroupNames.Metrics:
+          let value = agg.getValue(bucket);
+          // since the aggregation could be a non integer (such as a max date)
+          // only do the scaling calculation if it is needed.
+          if (aggScale !== 1) {
+            value *= aggScale;
+          }
+          write.metricBuffer.push({ id: column.id, value });
+
+          if (!write.columns.length) {
+            // row complete
+            write.row();
+          } else {
+            // process the next agg at this same level
+            collectBucket(aggs, write, bucket, key, aggScale);
+          }
+
+          write.metricBuffer.pop();
+
+          break;
+      }
+
+      write.columns.unshift(column);
+    }
+  }
+
+  // write empty values for each bucket agg, then write
+  // the metrics from the initial bucket using collectBucket()
+  function passEmptyBuckets(
+    aggs: IAggConfigs,
+    write: TabbedAggResponseWriter,
+    bucket: AggResponseBucket,
+    key: string,
+    aggScale: number
+  ) {
+    const column = write.columns.shift();
+
+    if (column) {
+      const agg = column.aggConfig;
+
+      switch (agg.type.type) {
+        case AggGroupNames.Metrics:
+          // pass control back to collectBucket()
+          write.columns.unshift(column);
+          collectBucket(aggs, write, bucket, key, aggScale);
+          return;
+
+        case AggGroupNames.Buckets:
+          passEmptyBuckets(aggs, write, bucket, key, aggScale);
+      }
+
+      write.columns.unshift(column);
+    }
+  }
+
+  const write = new TabbedAggResponseWriter(aggConfigs, respOpts || {});
+  const topLevelBucket: AggResponseBucket = {
+    ...esResponse.aggregations,
+    doc_count: esResponse.hits.total,
+  };
+
+  let timeRange: TabbedRangeFilterParams | undefined;
+
+  // Extract the time range object if provided
+  if (respOpts && respOpts.timeRange) {
+    const [timeRangeKey] = Object.keys(respOpts.timeRange);
+
+    if (timeRangeKey) {
+      timeRange = {
+        name: timeRangeKey,
+        ...respOpts.timeRange[timeRangeKey],
+      };
+    }
+  }
+
+  collectBucket(aggConfigs, write, topLevelBucket, '', 1);
+
+  return write.response();
+}
diff --git a/src/legacy/ui/public/agg_response/tabify/__tests__/tabify.js b/src/legacy/core_plugins/data/public/search/tabify/types.ts
similarity index 69%
rename from src/legacy/ui/public/agg_response/tabify/__tests__/tabify.js
rename to src/legacy/core_plugins/data/public/search/tabify/types.ts
index 38ed5408b603e..3a02a2b64f0c3 100644
--- a/src/legacy/ui/public/agg_response/tabify/__tests__/tabify.js
+++ b/src/legacy/core_plugins/data/public/search/tabify/types.ts
@@ -17,8 +17,16 @@
  * under the License.
  */
 
-import './_get_columns';
-import './_buckets';
-import './_response_writer';
-import './_integration';
-describe('Tabify Agg Response', function() {});
+import { RangeFilterParams } from '../../../../../../plugins/data/public';
+
+/** @internal **/
+export interface TabbedRangeFilterParams extends RangeFilterParams {
+  name: string;
+}
+
+/** @internal **/
+export interface TabbedResponseWriterOptions {
+  metricsAtAllLevels: boolean;
+  partialRows: boolean;
+  timeRange?: { [key: string]: RangeFilterParams };
+}
diff --git a/src/legacy/core_plugins/data/public/search/utils/types.ts b/src/legacy/core_plugins/data/public/search/utils/types.ts
index 305f27a86b398..e0afe99aa81fa 100644
--- a/src/legacy/core_plugins/data/public/search/utils/types.ts
+++ b/src/legacy/core_plugins/data/public/search/utils/types.ts
@@ -31,3 +31,9 @@ export interface RequestInspectorStats {
   hits?: InspectorStat;
   requestTime?: InspectorStat;
 }
+
+export interface AggResponseBucket {
+  key_as_string: string;
+  key: number;
+  doc_count: number;
+}
diff --git a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts
index b0bb17ce1ac7f..91b5c7f13dc95 100644
--- a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts
+++ b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts
@@ -58,8 +58,7 @@ export { stateMonitorFactory } from 'ui/state_management/state_monitor_factory';
 export { subscribeWithScope } from 'ui/utils/subscribe_with_scope';
 // @ts-ignore
 export { timezoneProvider } from 'ui/vis/lib/timezone';
-// @ts-ignore
-export { tabifyAggResponse } from 'ui/agg_response/tabify';
+export { tabifyAggResponse } from '../../../data/public';
 export { unhashUrl } from '../../../../../plugins/kibana_utils/public';
 export {
   migrateLegacyQuery,
diff --git a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table.js b/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table.js
index 0dbff60613cb0..9fe7920588cd2 100644
--- a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table.js
+++ b/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table.js
@@ -21,7 +21,11 @@ import $ from 'jquery';
 import moment from 'moment';
 import ngMock from 'ng_mock';
 import expect from '@kbn/expect';
-import fixtures from 'fixtures/fake_hierarchical_data';
+import {
+  metricOnly,
+  threeTermBuckets,
+  oneTermOneHistogramBucketWithTwoMetricsOneTopHitOneDerivative,
+} from 'fixtures/fake_hierarchical_data';
 import sinon from 'sinon';
 import { tabifyAggResponse, npStart } from '../../legacy_imports';
 import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
@@ -44,7 +48,7 @@ describe('Table Vis - AggTable Directive', function() {
 
   const init = () => {
     const vis1 = new visualizationsStart.Vis(indexPattern, 'table');
-    tabifiedData.metricOnly = tabifyAggResponse(vis1.aggs, fixtures.metricOnly);
+    tabifiedData.metricOnly = tabifyAggResponse(vis1.aggs, metricOnly);
 
     const vis2 = new visualizationsStart.Vis(indexPattern, {
       type: 'table',
@@ -61,7 +65,7 @@ describe('Table Vis - AggTable Directive', function() {
     vis2.aggs.aggs.forEach(function(agg, i) {
       agg.id = 'agg_' + (i + 1);
     });
-    tabifiedData.threeTermBuckets = tabifyAggResponse(vis2.aggs, fixtures.threeTermBuckets, {
+    tabifiedData.threeTermBuckets = tabifyAggResponse(vis2.aggs, threeTermBuckets, {
       metricsAtAllLevels: true,
     });
 
@@ -94,7 +98,7 @@ describe('Table Vis - AggTable Directive', function() {
 
     tabifiedData.oneTermOneHistogramBucketWithTwoMetricsOneTopHitOneDerivative = tabifyAggResponse(
       vis3.aggs,
-      fixtures.oneTermOneHistogramBucketWithTwoMetricsOneTopHitOneDerivative
+      oneTermOneHistogramBucketWithTwoMetricsOneTopHitOneDerivative
     );
   };
 
diff --git a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table_group.js b/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table_group.js
index f6ae41b024b7d..79d4d7c40d355 100644
--- a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table_group.js
+++ b/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table_group.js
@@ -20,7 +20,7 @@
 import $ from 'jquery';
 import ngMock from 'ng_mock';
 import expect from '@kbn/expect';
-import fixtures from 'fixtures/fake_hierarchical_data';
+import { metricOnly, threeTermBuckets } from 'fixtures/fake_hierarchical_data';
 import { tabifyAggResponse, npStart } from '../../legacy_imports';
 import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
 import { getAngularModule } from '../../get_inner_angular';
@@ -36,7 +36,7 @@ describe('Table Vis - AggTableGroup Directive', function() {
 
   const init = () => {
     const vis1 = new visualizationsStart.Vis(indexPattern, 'table');
-    tabifiedData.metricOnly = tabifyAggResponse(vis1.aggs, fixtures.metricOnly);
+    tabifiedData.metricOnly = tabifyAggResponse(vis1.aggs, metricOnly);
 
     const vis2 = new visualizationsStart.Vis(indexPattern, {
       type: 'pie',
@@ -50,7 +50,7 @@ describe('Table Vis - AggTableGroup Directive', function() {
     vis2.aggs.aggs.forEach(function(agg, i) {
       agg.id = 'agg_' + (i + 1);
     });
-    tabifiedData.threeTermBuckets = tabifyAggResponse(vis2.aggs, fixtures.threeTermBuckets);
+    tabifiedData.threeTermBuckets = tabifyAggResponse(vis2.aggs, threeTermBuckets);
   };
 
   const initLocalAngular = () => {
diff --git a/src/legacy/core_plugins/vis_type_table/public/legacy_imports.ts b/src/legacy/core_plugins/vis_type_table/public/legacy_imports.ts
index cb44814897bcf..90929150de9c3 100644
--- a/src/legacy/core_plugins/vis_type_table/public/legacy_imports.ts
+++ b/src/legacy/core_plugins/vis_type_table/public/legacy_imports.ts
@@ -24,9 +24,7 @@ export { IAggConfig, AggGroupNames, Schemas } from 'ui/agg_types';
 export { PaginateDirectiveProvider } from 'ui/directives/paginate';
 // @ts-ignore
 export { PaginateControlsDirectiveProvider } from 'ui/directives/paginate';
-export { tabifyGetColumns } from 'ui/agg_response/tabify/_get_columns';
-// @ts-ignore
-export { tabifyAggResponse } from 'ui/agg_response/tabify';
+export { tabifyAggResponse, tabifyGetColumns } from '../../data/public';
 export {
   configureAppAngularModule,
   KbnAccessibleClickProvider,
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/legacy_imports.ts b/src/legacy/core_plugins/vis_type_vislib/public/legacy_imports.ts
index 9c79be98a320c..1c8e679f7d61f 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/legacy_imports.ts
+++ b/src/legacy/core_plugins/vis_type_vislib/public/legacy_imports.ts
@@ -19,10 +19,8 @@
 
 export { AggType, AggGroupNames, IAggConfig, IAggType, Schemas } from 'ui/agg_types';
 export { getFormat } from 'ui/visualize/loader/pipeline_helpers/utilities';
-// @ts-ignore
-export { tabifyAggResponse } from 'ui/agg_response/tabify';
+export { tabifyAggResponse, tabifyGetColumns } from '../../data/public';
 // @ts-ignore
 export { buildHierarchicalData } from 'ui/agg_response/hierarchical/build_hierarchical_data';
 // @ts-ignore
 export { buildPointSeriesData } from 'ui/agg_response/point_series/point_series';
-export { tabifyGetColumns } from '../../../ui/public/agg_response/tabify/_get_columns';
diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/pie_chart.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/pie_chart.js
index 534a523103774..9c9c5a84f046c 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/pie_chart.js
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/pie_chart.js
@@ -22,7 +22,7 @@ import _ from 'lodash';
 import $ from 'jquery';
 import expect from '@kbn/expect';
 
-import fixtures from 'fixtures/fake_hierarchical_data';
+import { threeTermBuckets } from 'fixtures/fake_hierarchical_data';
 import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
 
 import { start as visualizationsStart } from '../../../../../visualizations/public/np_ready/public/legacy';
@@ -147,7 +147,7 @@ describe('No global chart settings', function() {
   });
 
   beforeEach(async () => {
-    const table1 = tabifyAggResponse(stubVis1.aggs, fixtures.threeTermBuckets, {
+    const table1 = tabifyAggResponse(stubVis1.aggs, threeTermBuckets, {
       metricsAtAllLevels: true,
     });
     data1 = await responseHandler(table1, rowAggDimensions);
@@ -234,7 +234,7 @@ describe('Vislib PieChart Class Test Suite', function() {
       });
 
       beforeEach(async () => {
-        const table = tabifyAggResponse(stubVis.aggs, fixtures.threeTermBuckets, {
+        const table = tabifyAggResponse(stubVis.aggs, threeTermBuckets, {
           metricsAtAllLevels: true,
         });
         data = await responseHandler(table, dataDimensions);
diff --git a/src/legacy/ui/public/agg_response/index.js b/src/legacy/ui/public/agg_response/index.js
index 41d45d1a06ca4..139a124356de2 100644
--- a/src/legacy/ui/public/agg_response/index.js
+++ b/src/legacy/ui/public/agg_response/index.js
@@ -19,7 +19,7 @@
 
 import { buildHierarchicalData } from './hierarchical/build_hierarchical_data';
 import { buildPointSeriesData } from './point_series/point_series';
-import { tabifyAggResponse } from './tabify/tabify';
+import { tabifyAggResponse } from '../../../core_plugins/data/public';
 
 export const aggResponseIndex = {
   hierarchical: buildHierarchicalData,
diff --git a/src/legacy/ui/public/agg_response/tabify/__tests__/_get_columns.js b/src/legacy/ui/public/agg_response/tabify/__tests__/_get_columns.js
deleted file mode 100644
index 3eb41c03050d0..0000000000000
--- a/src/legacy/ui/public/agg_response/tabify/__tests__/_get_columns.js
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import expect from '@kbn/expect';
-import ngMock from 'ng_mock';
-import { tabifyGetColumns } from '../_get_columns';
-import { start as visualizationsStart } from '../../../../../core_plugins/visualizations/public/np_ready/public/legacy';
-import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
-describe('get columns', function() {
-  let indexPattern;
-
-  beforeEach(ngMock.module('kibana'));
-  beforeEach(
-    ngMock.inject(function(Private) {
-      indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider);
-    })
-  );
-
-  it('should inject a count metric if no aggs exist', function() {
-    const vis = new visualizationsStart.Vis(indexPattern, {
-      type: 'pie',
-    });
-    while (vis.aggs.length) vis.aggs.pop();
-    const columns = tabifyGetColumns(
-      vis.getAggConfig().getResponseAggs(),
-      null,
-      vis.isHierarchical()
-    );
-
-    expect(columns).to.have.length(1);
-    expect(columns[0]).to.have.property('aggConfig');
-    expect(columns[0].aggConfig.type).to.have.property('name', 'count');
-  });
-
-  it('should inject a count metric if only buckets exist', function() {
-    const vis = new visualizationsStart.Vis(indexPattern, {
-      type: 'pie',
-      aggs: [
-        {
-          type: 'date_histogram',
-          schema: 'segment',
-          params: { field: '@timestamp', interval: '10s' },
-        },
-      ],
-    });
-
-    const columns = tabifyGetColumns(vis.getAggConfig().getResponseAggs(), !vis.isHierarchical());
-
-    expect(columns).to.have.length(2);
-    expect(columns[1]).to.have.property('aggConfig');
-    expect(columns[1].aggConfig.type).to.have.property('name', 'count');
-  });
-
-  it('should inject the metric after each bucket if the vis is hierarchical', function() {
-    const vis = new visualizationsStart.Vis(indexPattern, {
-      type: 'pie',
-      aggs: [
-        {
-          type: 'date_histogram',
-          schema: 'segment',
-          params: { field: '@timestamp', interval: '10s' },
-        },
-        {
-          type: 'date_histogram',
-          schema: 'segment',
-          params: { field: '@timestamp', interval: '10s' },
-        },
-        {
-          type: 'date_histogram',
-          schema: 'segment',
-          params: { field: '@timestamp', interval: '10s' },
-        },
-        {
-          type: 'date_histogram',
-          schema: 'segment',
-          params: { field: '@timestamp', interval: '10s' },
-        },
-      ],
-    });
-
-    const columns = tabifyGetColumns(vis.getAggConfig().getResponseAggs(), !vis.isHierarchical());
-
-    expect(columns).to.have.length(8);
-    columns.forEach(function(column, i) {
-      expect(column).to.have.property('aggConfig');
-      expect(column.aggConfig.type).to.have.property('name', i % 2 ? 'count' : 'date_histogram');
-    });
-  });
-
-  it('should inject the multiple metrics after each bucket if the vis is hierarchical', function() {
-    const vis = new visualizationsStart.Vis(indexPattern, {
-      type: 'pie',
-      aggs: [
-        {
-          type: 'date_histogram',
-          schema: 'segment',
-          params: { field: '@timestamp', interval: '10s' },
-        },
-        { type: 'avg', schema: 'metric', params: { field: 'bytes' } },
-        {
-          type: 'date_histogram',
-          schema: 'segment',
-          params: { field: '@timestamp', interval: '10s' },
-        },
-        {
-          type: 'date_histogram',
-          schema: 'segment',
-          params: { field: '@timestamp', interval: '10s' },
-        },
-        { type: 'sum', schema: 'metric', params: { field: 'bytes' } },
-        {
-          type: 'date_histogram',
-          schema: 'segment',
-          params: { field: '@timestamp', interval: '10s' },
-        },
-      ],
-    });
-
-    const columns = tabifyGetColumns(vis.getAggConfig().getResponseAggs(), !vis.isHierarchical());
-
-    function checkColumns(column, i) {
-      expect(column).to.have.property('aggConfig');
-      switch (i) {
-        case 0:
-          expect(column.aggConfig.type).to.have.property('name', 'date_histogram');
-          break;
-        case 1:
-          expect(column.aggConfig.type).to.have.property('name', 'avg');
-          break;
-        case 2:
-          expect(column.aggConfig.type).to.have.property('name', 'sum');
-          break;
-      }
-    }
-
-    expect(columns).to.have.length(12);
-    for (let i = 0; i < columns.length; i += 3) {
-      columns.slice(i, i + 3).forEach(checkColumns);
-    }
-  });
-
-  it('should put all metrics at the end of the columns if the vis is not hierarchical', function() {
-    const vis = new visualizationsStart.Vis(indexPattern, {
-      type: 'histogram',
-      aggs: [
-        {
-          type: 'date_histogram',
-          schema: 'segment',
-          params: { field: '@timestamp', interval: '10s' },
-        },
-        { type: 'avg', schema: 'metric', params: { field: 'bytes' } },
-        {
-          type: 'date_histogram',
-          schema: 'segment',
-          params: { field: '@timestamp', interval: '10s' },
-        },
-        {
-          type: 'date_histogram',
-          schema: 'segment',
-          params: { field: '@timestamp', interval: '10s' },
-        },
-        { type: 'sum', schema: 'metric', params: { field: 'bytes' } },
-        {
-          type: 'date_histogram',
-          schema: 'segment',
-          params: { field: '@timestamp', interval: '10s' },
-        },
-      ],
-    });
-
-    const columns = tabifyGetColumns(vis.getAggConfig().getResponseAggs(), !vis.isHierarchical());
-    expect(columns).to.have.length(6);
-
-    // sum should be last
-    expect(columns.pop().aggConfig.type).to.have.property('name', 'sum');
-    // avg should be before that
-    expect(columns.pop().aggConfig.type).to.have.property('name', 'avg');
-    // the rest are date_histograms
-    while (columns.length) {
-      expect(columns.pop().aggConfig.type).to.have.property('name', 'date_histogram');
-    }
-  });
-});
diff --git a/src/legacy/ui/public/agg_response/tabify/__tests__/_integration.js b/src/legacy/ui/public/agg_response/tabify/__tests__/_integration.js
deleted file mode 100644
index f3f2e20149acf..0000000000000
--- a/src/legacy/ui/public/agg_response/tabify/__tests__/_integration.js
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import _ from 'lodash';
-import fixtures from 'fixtures/fake_hierarchical_data';
-import expect from '@kbn/expect';
-import ngMock from 'ng_mock';
-import { tabifyAggResponse } from '../tabify';
-import { start as visualizationsStart } from '../../../../../core_plugins/visualizations/public/np_ready/public/legacy';
-import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
-
-describe('tabifyAggResponse Integration', function() {
-  let indexPattern;
-
-  beforeEach(ngMock.module('kibana'));
-  beforeEach(
-    ngMock.inject(function(Private) {
-      indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider);
-    })
-  );
-
-  function normalizeIds(vis) {
-    vis.aggs.aggs.forEach(function(agg, i) {
-      agg.id = 'agg_' + (i + 1);
-    });
-  }
-
-  it('transforms a simple response properly', function() {
-    const vis = new visualizationsStart.Vis(indexPattern, {
-      type: 'histogram',
-      aggs: [],
-    });
-    normalizeIds(vis);
-
-    const resp = tabifyAggResponse(vis.getAggConfig(), fixtures.metricOnly, {
-      metricsAtAllLevels: vis.isHierarchical(),
-    });
-
-    expect(resp)
-      .to.have.property('rows')
-      .and.property('columns');
-    expect(resp.rows).to.have.length(1);
-    expect(resp.columns).to.have.length(1);
-
-    expect(resp.rows[0]).to.eql({ 'col-0-agg_1': 1000 });
-    expect(resp.columns[0]).to.have.property('aggConfig', vis.aggs[0]);
-  });
-
-  describe('transforms a complex response', function() {
-    this.slow(1000);
-
-    let vis;
-    let avg;
-    let ext;
-    let src;
-    let os;
-    let esResp;
-
-    beforeEach(function() {
-      vis = new visualizationsStart.Vis(indexPattern, {
-        type: 'pie',
-        aggs: [
-          { type: 'avg', schema: 'metric', params: { field: 'bytes' } },
-          { type: 'terms', schema: 'split', params: { field: 'extension' } },
-          { type: 'terms', schema: 'segment', params: { field: 'geo.src' } },
-          { type: 'terms', schema: 'segment', params: { field: 'machine.os' } },
-        ],
-      });
-      normalizeIds(vis);
-
-      avg = vis.aggs[0];
-      ext = vis.aggs[1];
-      src = vis.aggs[2];
-      os = vis.aggs[3];
-
-      esResp = _.cloneDeep(fixtures.threeTermBuckets);
-      // remove the buckets for css              in MX
-      esResp.aggregations.agg_2.buckets[1].agg_3.buckets[0].agg_4.buckets = [];
-    });
-
-    // check that the columns of a table are formed properly
-    function expectColumns(table, aggs) {
-      expect(table.columns)
-        .to.be.an('array')
-        .and.have.length(aggs.length);
-      aggs.forEach(function(agg, i) {
-        expect(table.columns[i]).to.have.property('aggConfig', agg);
-      });
-    }
-
-    // check that a row has expected values
-    function expectRow(row, asserts) {
-      expect(row).to.be.an('object');
-      asserts.forEach(function(assert, i) {
-        if (row[`col-${i}`]) {
-          assert(row[`col-${i}`]);
-        }
-      });
-    }
-
-    // check for two character country code
-    function expectCountry(val) {
-      expect(val).to.be.a('string');
-      expect(val).to.have.length(2);
-    }
-
-    // check for an OS term
-    function expectExtension(val) {
-      expect(val).to.match(/^(js|png|html|css|jpg)$/);
-    }
-
-    // check for an OS term
-    function expectOS(val) {
-      expect(val).to.match(/^(win|mac|linux)$/);
-    }
-
-    // check for something like an average bytes result
-    function expectAvgBytes(val) {
-      expect(val).to.be.a('number');
-      expect(val === 0 || val > 1000).to.be.ok();
-    }
-
-    it('for non-hierarchical vis', function() {
-      // the default for a non-hierarchical vis is to display
-      // only complete rows, and only put the metrics at the end.
-
-      const tabbed = tabifyAggResponse(vis.getAggConfig(), esResp, { metricsAtAllLevels: false });
-
-      expectColumns(tabbed, [ext, src, os, avg]);
-
-      tabbed.rows.forEach(function(row) {
-        expectRow(row, [expectExtension, expectCountry, expectOS, expectAvgBytes]);
-      });
-    });
-
-    it('for hierarchical vis', function() {
-      // since we have partialRows we expect that one row will have some empty
-      // values, and since the vis is hierarchical and we are NOT using
-      // minimalColumns we should expect the partial row to be completely after
-      // the existing bucket and it's metric
-
-      vis.isHierarchical = _.constant(true);
-      const tabbed = tabifyAggResponse(vis.getAggConfig(), esResp, { metricsAtAllLevels: true });
-
-      expectColumns(tabbed, [ext, avg, src, avg, os, avg]);
-
-      tabbed.rows.forEach(function(row) {
-        expectRow(row, [
-          expectExtension,
-          expectAvgBytes,
-          expectCountry,
-          expectAvgBytes,
-          expectOS,
-          expectAvgBytes,
-        ]);
-      });
-    });
-  });
-});
diff --git a/src/legacy/ui/public/agg_response/tabify/__tests__/_response_writer.js b/src/legacy/ui/public/agg_response/tabify/__tests__/_response_writer.js
deleted file mode 100644
index b0c0f2f3d9100..0000000000000
--- a/src/legacy/ui/public/agg_response/tabify/__tests__/_response_writer.js
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import expect from '@kbn/expect';
-import ngMock from 'ng_mock';
-import { TabbedAggResponseWriter } from '../_response_writer';
-import { start as visualizationsStart } from '../../../../../core_plugins/visualizations/public/np_ready/public/legacy';
-import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
-
-describe('TabbedAggResponseWriter class', function() {
-  let Private;
-  let indexPattern;
-
-  beforeEach(ngMock.module('kibana'));
-  beforeEach(
-    ngMock.inject(function($injector) {
-      Private = $injector.get('Private');
-
-      indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider);
-    })
-  );
-
-  const splitAggConfig = [
-    {
-      type: 'terms',
-      params: {
-        field: 'geo.src',
-      },
-    },
-  ];
-
-  const twoSplitsAggConfig = [
-    {
-      type: 'terms',
-      params: {
-        field: 'geo.src',
-      },
-    },
-    {
-      type: 'terms',
-      params: {
-        field: 'machine.os.raw',
-      },
-    },
-  ];
-
-  const createResponseWritter = (aggs = [], opts = {}) => {
-    const vis = new visualizationsStart.Vis(indexPattern, { type: 'histogram', aggs: aggs });
-    return new TabbedAggResponseWriter(vis.getAggConfig(), opts);
-  };
-
-  describe('Constructor', function() {
-    let responseWriter;
-    beforeEach(() => {
-      responseWriter = createResponseWritter(twoSplitsAggConfig);
-    });
-
-    it('creates aggStack', () => {
-      expect(responseWriter.aggStack.length).to.eql(3);
-    });
-
-    it('generates columns', () => {
-      expect(responseWriter.columns.length).to.eql(3);
-    });
-
-    it('correctly generates columns with metricsAtAllLevels set to true', () => {
-      const minimalColumnsResponseWriter = createResponseWritter(twoSplitsAggConfig, {
-        metricsAtAllLevels: true,
-      });
-      expect(minimalColumnsResponseWriter.columns.length).to.eql(4);
-    });
-
-    describe('sets timeRange', function() {
-      it("to the first nested object's range", function() {
-        const vis = new visualizationsStart.Vis(indexPattern, { type: 'histogram', aggs: [] });
-        const range = {
-          gte: 0,
-          lte: 100,
-        };
-
-        const writer = new TabbedAggResponseWriter(vis.getAggConfig(), {
-          timeRange: {
-            '@timestamp': range,
-          },
-        });
-
-        expect(writer.timeRange.gte).to.be(range.gte);
-        expect(writer.timeRange.lte).to.be(range.lte);
-        expect(writer.timeRange.name).to.be('@timestamp');
-      });
-
-      it('to undefined if no nested object', function() {
-        const vis = new visualizationsStart.Vis(indexPattern, { type: 'histogram', aggs: [] });
-
-        const writer = new TabbedAggResponseWriter(vis.getAggConfig(), {
-          timeRange: {},
-        });
-        expect(writer).to.have.property('timeRange', undefined);
-      });
-    });
-  });
-
-  describe('row()', function() {
-    let responseWriter;
-
-    beforeEach(() => {
-      responseWriter = createResponseWritter(splitAggConfig, { partialRows: true });
-    });
-
-    it('adds the row to the array', () => {
-      responseWriter.rowBuffer['col-0'] = 'US';
-      responseWriter.rowBuffer['col-1'] = 5;
-      responseWriter.row();
-      expect(responseWriter.rows.length).to.eql(1);
-      expect(responseWriter.rows[0]).to.eql({ 'col-0': 'US', 'col-1': 5 });
-    });
-
-    it('correctly handles bucketBuffer', () => {
-      responseWriter.bucketBuffer.push({ id: 'col-0', value: 'US' });
-      responseWriter.rowBuffer['col-1'] = 5;
-      responseWriter.row();
-      expect(responseWriter.rows.length).to.eql(1);
-      expect(responseWriter.rows[0]).to.eql({ 'col-0': 'US', 'col-1': 5 });
-    });
-
-    it("doesn't add an empty row", () => {
-      responseWriter.row();
-      expect(responseWriter.rows.length).to.eql(0);
-    });
-  });
-
-  describe('response()', () => {
-    let responseWriter;
-
-    beforeEach(() => {
-      responseWriter = createResponseWritter(splitAggConfig);
-    });
-
-    it('produces correct response', () => {
-      responseWriter.rowBuffer['col-0-1'] = 'US';
-      responseWriter.rowBuffer['col-1-2'] = 5;
-      responseWriter.row();
-      const response = responseWriter.response();
-      expect(response).to.have.property('rows');
-      expect(response.rows).to.eql([{ 'col-0-1': 'US', 'col-1-2': 5 }]);
-      expect(response).to.have.property('columns');
-      expect(response.columns.length).to.equal(2);
-      expect(response.columns[0]).to.have.property('id', 'col-0-1');
-      expect(response.columns[0]).to.have.property('name', 'geo.src: Descending');
-      expect(response.columns[0]).to.have.property('aggConfig');
-      expect(response.columns[1]).to.have.property('id', 'col-1-2');
-      expect(response.columns[1]).to.have.property('name', 'Count');
-      expect(response.columns[1]).to.have.property('aggConfig');
-    });
-
-    it('produces correct response for no data', () => {
-      const response = responseWriter.response();
-      expect(response).to.have.property('rows');
-      expect(response.rows.length).to.be(0);
-      expect(response).to.have.property('columns');
-      expect(response.columns.length).to.equal(2);
-      expect(response.columns[0]).to.have.property('id', 'col-0-1');
-      expect(response.columns[0]).to.have.property('name', 'geo.src: Descending');
-      expect(response.columns[0]).to.have.property('aggConfig');
-      expect(response.columns[1]).to.have.property('id', 'col-1-2');
-      expect(response.columns[1]).to.have.property('name', 'Count');
-      expect(response.columns[1]).to.have.property('aggConfig');
-    });
-  });
-});
diff --git a/src/legacy/ui/public/agg_response/tabify/_buckets.js b/src/legacy/ui/public/agg_response/tabify/_buckets.js
deleted file mode 100644
index 7180a056ab0ca..0000000000000
--- a/src/legacy/ui/public/agg_response/tabify/_buckets.js
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import _ from 'lodash';
-import moment from 'moment';
-
-function TabifyBuckets(aggResp, aggParams, timeRange) {
-  if (_.has(aggResp, 'buckets')) {
-    this.buckets = aggResp.buckets;
-  } else if (aggResp) {
-    // Some Bucket Aggs only return a single bucket (like filter).
-    // In those instances, the aggResp is the content of the single bucket.
-    this.buckets = [aggResp];
-  } else {
-    this.buckets = [];
-  }
-
-  this.objectMode = _.isPlainObject(this.buckets);
-  if (this.objectMode) {
-    this._keys = _.keys(this.buckets);
-    this.length = this._keys.length;
-  } else {
-    this.length = this.buckets.length;
-  }
-
-  if (this.length && aggParams) {
-    this._orderBucketsAccordingToParams(aggParams);
-    if (aggParams.drop_partials) {
-      this._dropPartials(aggParams, timeRange);
-    }
-  }
-}
-
-TabifyBuckets.prototype.forEach = function(fn) {
-  const buckets = this.buckets;
-
-  if (this.objectMode) {
-    this._keys.forEach(function(key) {
-      fn(buckets[key], key);
-    });
-  } else {
-    buckets.forEach(function(bucket) {
-      fn(bucket, bucket.key);
-    });
-  }
-};
-
-TabifyBuckets.prototype._isRangeEqual = function(range1, range2) {
-  return (
-    _.get(range1, 'from', null) === _.get(range2, 'from', null) &&
-    _.get(range1, 'to', null) === _.get(range2, 'to', null)
-  );
-};
-
-TabifyBuckets.prototype._orderBucketsAccordingToParams = function(params) {
-  if (params.filters && this.objectMode) {
-    this._keys = params.filters.map(filter => {
-      const query = _.get(filter, 'input.query.query_string.query', filter.input.query);
-      const queryString = typeof query === 'string' ? query : JSON.stringify(query);
-      return filter.label || queryString || '*';
-    });
-  } else if (params.ranges && this.objectMode) {
-    this._keys = params.ranges.map(range => {
-      return _.findKey(this.buckets, el => this._isRangeEqual(el, range));
-    });
-  } else if (params.ranges && params.field.type !== 'date') {
-    let ranges = params.ranges;
-    if (params.ipRangeType) {
-      ranges = params.ipRangeType === 'mask' ? ranges.mask : ranges.fromTo;
-    }
-    this.buckets = ranges.map(range => {
-      if (range.mask) {
-        return this.buckets.find(el => el.key === range.mask);
-      }
-      return this.buckets.find(el => this._isRangeEqual(el, range));
-    });
-  }
-};
-
-// dropPartials should only be called if the aggParam setting is enabled,
-// and the agg field is the same as the Time Range.
-TabifyBuckets.prototype._dropPartials = function(params, timeRange) {
-  if (
-    !timeRange ||
-    this.buckets.length <= 1 ||
-    this.objectMode ||
-    params.field.name !== timeRange.name
-  ) {
-    return;
-  }
-
-  const interval = this.buckets[1].key - this.buckets[0].key;
-
-  this.buckets = this.buckets.filter(bucket => {
-    if (moment(bucket.key).isBefore(timeRange.gte)) {
-      return false;
-    }
-    if (moment(bucket.key + interval).isAfter(timeRange.lte)) {
-      return false;
-    }
-    return true;
-  });
-
-  this.length = this.buckets.length;
-};
-
-export { TabifyBuckets };
diff --git a/src/legacy/ui/public/agg_response/tabify/_response_writer.js b/src/legacy/ui/public/agg_response/tabify/_response_writer.js
deleted file mode 100644
index 85586c7ca7fda..0000000000000
--- a/src/legacy/ui/public/agg_response/tabify/_response_writer.js
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { toArray } from 'lodash';
-import { tabifyGetColumns } from './_get_columns';
-
-/**
- * Writer class that collects information about an aggregation response and
- * produces a table, or a series of tables.
- *
- * @param {AggConfigs} aggs - the agg configs object to which the aggregation response correlates
- * @param {boolean} metricsAtAllLevels - setting to true will produce metrics for every bucket
- * @param {boolean} partialRows - setting to true will not remove rows with missing values
- * @param {Object} timeRange - time range object, if provided
- */
-function TabbedAggResponseWriter(
-  aggs,
-  { metricsAtAllLevels = false, partialRows = false, timeRange } = {}
-) {
-  // Private
-  this._removePartialRows = !partialRows;
-
-  // Public
-  this.rowBuffer = {};
-  this.bucketBuffer = [];
-  this.metricBuffer = [];
-  this.aggs = aggs;
-  this.partialRows = partialRows;
-  this.columns = tabifyGetColumns(aggs.getResponseAggs(), !metricsAtAllLevels);
-  this.aggStack = [...this.columns];
-  this.rows = [];
-  // Extract the time range object if provided
-  if (timeRange) {
-    const timeRangeKey = Object.keys(timeRange)[0];
-    this.timeRange = timeRange[timeRangeKey];
-    if (this.timeRange) {
-      this.timeRange.name = timeRangeKey;
-    }
-  }
-}
-
-TabbedAggResponseWriter.prototype.isPartialRow = function(row) {
-  return !this.columns.map(column => row.hasOwnProperty(column.id)).every(c => c === true);
-};
-
-/**
- * Create a new row by reading the row buffer and bucketBuffer
- */
-TabbedAggResponseWriter.prototype.row = function() {
-  this.bucketBuffer.forEach(bucket => {
-    this.rowBuffer[bucket.id] = bucket.value;
-  });
-
-  this.metricBuffer.forEach(metric => {
-    this.rowBuffer[metric.id] = metric.value;
-  });
-
-  if (
-    !toArray(this.rowBuffer).length ||
-    (this._removePartialRows && this.isPartialRow(this.rowBuffer))
-  ) {
-    return;
-  }
-
-  this.rows.push(this.rowBuffer);
-  this.rowBuffer = {};
-};
-
-/**
- * Get the actual response
- *
- * @return {object} - the final table
- */
-TabbedAggResponseWriter.prototype.response = function() {
-  return {
-    columns: this.columns,
-    rows: this.rows,
-  };
-};
-
-export { TabbedAggResponseWriter };
diff --git a/src/legacy/ui/public/agg_response/tabify/tabify.js b/src/legacy/ui/public/agg_response/tabify/tabify.js
deleted file mode 100644
index 8316055cb15cc..0000000000000
--- a/src/legacy/ui/public/agg_response/tabify/tabify.js
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import _ from 'lodash';
-import { TabbedAggResponseWriter } from './_response_writer';
-import { TabifyBuckets } from './_buckets';
-
-/**
- * Sets up the ResponseWriter and kicks off bucket collection.
- *
- * @param {AggConfigs} aggs - the agg configs object to which the aggregation response correlates
- * @param {Object} esResponse - response that came back from Elasticsearch
- * @param {Object} respOpts - options object for the ResponseWriter with params set by Courier
- * @param {boolean} respOpts.metricsAtAllLevels - setting to true will produce metrics for every bucket
- * @param {boolean} respOpts.partialRows - setting to true will not remove rows with missing values
- * @param {Object} respOpts.timeRange - time range object, if provided
- */
-export function tabifyAggResponse(aggs, esResponse, respOpts = {}) {
-  const write = new TabbedAggResponseWriter(aggs, respOpts);
-
-  const topLevelBucket = _.assign({}, esResponse.aggregations, {
-    doc_count: esResponse.hits.total,
-  });
-
-  collectBucket(write, topLevelBucket, '', 1);
-
-  return write.response();
-}
-
-/**
- * read an aggregation from a bucket, which *might* be found at key (if
- * the response came in object form), and will recurse down the aggregation
- * tree and will pass the read values to the ResponseWriter.
- *
- * @param {object} bucket - a bucket from the aggResponse
- * @param {undefined|string} key - the key where the bucket was found
- * @returns {undefined}
- */
-function collectBucket(write, bucket, key, aggScale) {
-  const column = write.aggStack.shift();
-  const agg = column.aggConfig;
-  const aggInfo = agg.write(write.aggs);
-  aggScale *= aggInfo.metricScale || 1;
-
-  switch (agg.type.type) {
-    case 'buckets':
-      const buckets = new TabifyBuckets(bucket[agg.id], agg.params, write.timeRange);
-      if (buckets.length) {
-        buckets.forEach(function(subBucket, key) {
-          // if the bucket doesn't have value don't add it to the row
-          // we don't want rows like: { column1: undefined, column2: 10 }
-          const bucketValue = agg.getKey(subBucket, key);
-          const hasBucketValue = typeof bucketValue !== 'undefined';
-          if (hasBucketValue) {
-            write.bucketBuffer.push({ id: column.id, value: bucketValue });
-          }
-          collectBucket(write, subBucket, agg.getKey(subBucket, key), aggScale);
-          if (hasBucketValue) {
-            write.bucketBuffer.pop();
-          }
-        });
-      } else if (write.partialRows) {
-        // we don't have any buckets, but we do have metrics at this
-        // level, then pass all the empty buckets and jump back in for
-        // the metrics.
-        write.aggStack.unshift(column);
-        passEmptyBuckets(write, bucket, key, aggScale);
-        write.aggStack.shift();
-      } else {
-        // we don't have any buckets, and we don't have isHierarchical
-        // data, so no metrics, just try to write the row
-        write.row();
-      }
-      break;
-    case 'metrics':
-      let value = agg.getValue(bucket);
-      // since the aggregation could be a non integer (such as a max date)
-      // only do the scaling calculation if it is needed.
-      if (aggScale !== 1) {
-        value *= aggScale;
-      }
-      write.metricBuffer.push({ id: column.id, value: value });
-
-      if (!write.aggStack.length) {
-        // row complete
-        write.row();
-      } else {
-        // process the next agg at this same level
-        collectBucket(write, bucket, key, aggScale);
-      }
-
-      write.metricBuffer.pop();
-
-      break;
-  }
-
-  write.aggStack.unshift(column);
-}
-
-// write empty values for each bucket agg, then write
-// the metrics from the initial bucket using collectBucket()
-function passEmptyBuckets(write, bucket, key, aggScale) {
-  const column = write.aggStack.shift();
-  const agg = column.aggConfig;
-
-  switch (agg.type.type) {
-    case 'metrics':
-      // pass control back to collectBucket()
-      write.aggStack.unshift(column);
-      collectBucket(write, bucket, key, aggScale);
-      return;
-
-    case 'buckets':
-      passEmptyBuckets(write, bucket, key, aggScale);
-  }
-
-  write.aggStack.unshift(column);
-}
diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js
index cb8b43a6c312b..0912e5a9f1283 100644
--- a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js
+++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js
@@ -11,7 +11,7 @@ import { VECTOR_SHAPE_TYPES } from '../vector_feature_types';
 import { HeatmapLayer } from '../../heatmap_layer';
 import { VectorLayer } from '../../vector_layer';
 import { AggConfigs, Schemas } from 'ui/agg_types';
-import { tabifyAggResponse } from 'ui/agg_response/tabify';
+import { tabifyAggResponse } from '../../../../../../../../src/legacy/core_plugins/data/public';
 import { convertToGeoJson } from './convert_to_geojson';
 import { VectorStyle } from '../../styles/vector/vector_style';
 import {

From 39c835af605e9c832cde2d2a40200ef8283a46f8 Mon Sep 17 00:00:00 2001
From: Sebastian Grodzicki <sebastian.grodzicki@elastic.co>
Date: Thu, 20 Feb 2020 14:05:21 +0100
Subject: [PATCH 095/174] Add owners for monitoring plugin (#57987)

---
 .github/CODEOWNERS | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 56db8d3793f57..bea10a1c8b31c 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -75,6 +75,7 @@
 /x-pack/plugins/ingest_manager/ @elastic/ingest
 /x-pack/legacy/plugins/ingest_manager/ @elastic/ingest
 /x-pack/plugins/observability/ @elastic/logs-metrics-ui @elastic/apm-ui @elastic/uptime @elastic/ingest
+/x-pack/legacy/plugins/monitoring/ @elastic/stack-monitoring-ui
 
 # Machine Learning
 /x-pack/legacy/plugins/ml/  @elastic/ml-ui

From 47bb7c61b67c1370a73fe247f4248326b8f696b4 Mon Sep 17 00:00:00 2001
From: Melissa Alvarez <melissa.alvarez@elastic.co>
Date: Thu, 20 Feb 2020 09:13:49 -0500
Subject: [PATCH 096/174] update estimateBucketSpan schema and api test
 (#58004)

---
 .../new_platform/job_validation_schema.ts       |  1 +
 .../apis/ml/bucket_span_estimator.ts            | 17 +++++++++++++++++
 2 files changed, 18 insertions(+)

diff --git a/x-pack/legacy/plugins/ml/server/new_platform/job_validation_schema.ts b/x-pack/legacy/plugins/ml/server/new_platform/job_validation_schema.ts
index 1cc6e8a97ffc0..5917ec50884d8 100644
--- a/x-pack/legacy/plugins/ml/server/new_platform/job_validation_schema.ts
+++ b/x-pack/legacy/plugins/ml/server/new_platform/job_validation_schema.ts
@@ -11,6 +11,7 @@ export const estimateBucketSpanSchema = schema.object({
   aggTypes: schema.arrayOf(schema.nullable(schema.string())),
   duration: schema.object({ start: schema.number(), end: schema.number() }),
   fields: schema.arrayOf(schema.nullable(schema.string())),
+  filters: schema.maybe(schema.arrayOf(schema.any())),
   index: schema.string(),
   query: schema.any(),
   splitField: schema.maybe(schema.string()),
diff --git a/x-pack/test/api_integration/apis/ml/bucket_span_estimator.ts b/x-pack/test/api_integration/apis/ml/bucket_span_estimator.ts
index 47afe7553fe62..1c7245234b089 100644
--- a/x-pack/test/api_integration/apis/ml/bucket_span_estimator.ts
+++ b/x-pack/test/api_integration/apis/ml/bucket_span_estimator.ts
@@ -69,6 +69,23 @@ export default ({ getService }: FtrProviderContext) => {
         responseBody: { name: '3h', ms: 10800000 },
       },
     },
+    {
+      testTitleSuffix: 'with 1 field, 1 agg, no split, and empty filters',
+      user: USER.ML_POWERUSER,
+      requestBody: {
+        aggTypes: ['avg'],
+        duration: { start: 1560297859000, end: 1562975136000 },
+        fields: ['taxless_total_price'],
+        filters: [],
+        index: 'ecommerce',
+        query: { bool: { must: [{ match_all: {} }] } },
+        timeField: 'order_date',
+      },
+      expected: {
+        responseCode: 200,
+        responseBody: { name: '15m', ms: 900000 },
+      },
+    },
   ];
 
   describe('bucket span estimator', function() {

From 5cfc5ef9fffb3f39fedc06b495aecc881d1b731a Mon Sep 17 00:00:00 2001
From: Michail Yasonik <michail.yasonik@elastic.co>
Date: Thu, 20 Feb 2020 09:14:11 -0500
Subject: [PATCH 097/174] Reverting grouped nav UI (#57724)

* reverting grouped nav ui

* removing Management name change
---
 docs/management/advanced-options.asciidoc     |    3 -
 src/core/public/chrome/chrome_service.tsx     |    1 -
 .../__snapshots__/nav_drawer.test.tsx.snap    | 5283 -----------------
 src/core/public/chrome/ui/header/header.tsx   |   12 +-
 src/core/public/chrome/ui/header/index.ts     |    1 -
 .../chrome/ui/header/nav_drawer.test.tsx      |  103 -
 .../public/chrome/ui/header/nav_drawer.tsx    |  118 +-
 src/legacy/core_plugins/kibana/index.js       |    2 +-
 .../home/np_ready/components/home.test.js     |    2 +-
 .../kibana/public/management/index.js         |    2 +-
 .../kibana/ui_setting_defaults.js             |   19 -
 .../ui/public/management/breadcrumbs.ts       |    2 +-
 .../management_sidebar_nav.tsx                |    3 +-
 .../public/legacy/sections_register.js        |    4 +-
 .../management/public/management_app.tsx      |    3 +-
 src/plugins/management/public/plugin.ts       |    4 +-
 .../dashboard/create_and_add_embeddables.js   |    1 -
 .../apps/management/_index_pattern_filter.js  |    2 +-
 test/functional/page_objects/header_page.js   |    2 +-
 test/functional/page_objects/settings_page.ts |    7 -
 .../core_plugins/application_status.ts        |    6 +-
 .../test_suites/core_plugins/applications.ts  |    2 +-
 .../translations/translations/ja-JP.json      |    1 -
 .../translations/translations/zh-CN.json      |    1 -
 .../advanced_settings_security.ts             |    8 +-
 .../advanced_settings_spaces.ts               |    3 +-
 .../apps/apm/feature_controls/apm_security.ts |    4 +-
 .../apps/apm/feature_controls/apm_spaces.ts   |    3 +-
 .../feature_controls/canvas_security.ts       |    4 +-
 .../canvas/feature_controls/canvas_spaces.ts  |    3 +-
 .../feature_controls/dashboard_security.ts    |    4 +-
 .../feature_controls/dashboard_spaces.ts      |    9 +-
 .../dashboard_mode/dashboard_view_mode.js     |    9 +-
 .../feature_controls/dev_tools_security.ts    |    4 +-
 .../feature_controls/dev_tools_spaces.ts      |    9 +-
 .../feature_controls/discover_security.ts     |    4 +-
 .../feature_controls/discover_spaces.ts       |    2 -
 .../graph/feature_controls/graph_security.ts  |    4 +-
 .../graph/feature_controls/graph_spaces.ts    |    3 +-
 .../index_patterns_security.ts                |    8 +-
 .../feature_controls/index_patterns_spaces.ts |    3 +-
 .../infrastructure_security.ts                |    4 +-
 .../feature_controls/infrastructure_spaces.ts |    9 +-
 .../infra/feature_controls/logs_security.ts   |    4 +-
 .../infra/feature_controls/logs_spaces.ts     |    9 +-
 .../feature_controls/ml_security.ts           |    3 +-
 .../feature_controls/ml_spaces.ts             |    3 +-
 .../maps/feature_controls/maps_security.ts    |    6 +-
 .../feature_controls/monitoring_security.ts   |    3 +-
 .../feature_controls/monitoring_spaces.ts     |    3 +-
 .../feature_controls/spaces_security.ts       |    5 +-
 .../feature_controls/timelion_security.ts     |    4 +-
 .../feature_controls/timelion_spaces.ts       |    9 +-
 .../feature_controls/uptime_security.ts       |    4 +-
 .../uptime/feature_controls/uptime_spaces.ts  |    3 +-
 .../feature_controls/visualize_security.ts    |    4 +-
 .../feature_controls/visualize_spaces.ts      |    9 +-
 57 files changed, 79 insertions(+), 5671 deletions(-)
 delete mode 100644 src/core/public/chrome/ui/header/__snapshots__/nav_drawer.test.tsx.snap
 delete mode 100644 src/core/public/chrome/ui/header/nav_drawer.test.tsx

diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc
index ec626677d0902..80c9053dc5ae6 100644
--- a/docs/management/advanced-options.asciidoc
+++ b/docs/management/advanced-options.asciidoc
@@ -70,9 +70,6 @@ into the document when displaying it.
 `metrics:max_buckets`:: The maximum numbers of buckets that a single
 data source can return. This might arise when the user selects a
 short interval (for example, 1s) for a long time period (1 year).
-`pageNavigation`:: The style of navigation menu for Kibana.
-Choices are Individual, the legacy style where every plugin is represented in the nav,
-and Grouped, a new format that bundles related plugins together in nested navigation.
 `query:allowLeadingWildcards`:: Allows a wildcard (*) as the first character
 in a query clause. Only applies when experimental query features are
 enabled in the query bar. To disallow leading wildcards in Lucene queries,
diff --git a/src/core/public/chrome/chrome_service.tsx b/src/core/public/chrome/chrome_service.tsx
index 6ab9fe158742a..2b0b115ce068e 100644
--- a/src/core/public/chrome/chrome_service.tsx
+++ b/src/core/public/chrome/chrome_service.tsx
@@ -193,7 +193,6 @@ export class ChromeService {
             recentlyAccessed$={recentlyAccessed.get$()}
             navControlsLeft$={navControls.getLeft$()}
             navControlsRight$={navControls.getRight$()}
-            navSetting$={uiSettings.get$('pageNavigation')}
           />
         </React.Fragment>
       ),
diff --git a/src/core/public/chrome/ui/header/__snapshots__/nav_drawer.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/nav_drawer.test.tsx.snap
deleted file mode 100644
index cf3b48f237286..0000000000000
--- a/src/core/public/chrome/ui/header/__snapshots__/nav_drawer.test.tsx.snap
+++ /dev/null
@@ -1,5283 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`NavDrawer Advanced setting set to grouped renders grouped items 1`] = `
-<ForwardRef(navDrawerRenderer)
-  basePath={
-    Object {
-      "get": [Function],
-      "prepend": [Function],
-      "remove": [Function],
-    }
-  }
-  chromeNavLinks={
-    Array [
-      Object {
-        "baseUrl": "http://localhost:5601/app/discover",
-        "category": Object {
-          "label": "Analyze",
-          "order": 1000,
-        },
-        "id": "discover",
-        "legacy": true,
-        "order": 100,
-        "title": "discover",
-      },
-      Object {
-        "baseUrl": "http://localhost:5601/app/siem",
-        "category": Object {
-          "euiIconType": "logoSecurity",
-          "label": "Security",
-          "order": 3000,
-        },
-        "id": "siem",
-        "legacy": true,
-        "order": 500,
-        "title": "siem",
-      },
-      Object {
-        "baseUrl": "http://localhost:5601/app/metrics",
-        "category": Object {
-          "euiIconType": "logoObservability",
-          "label": "Observability",
-          "order": 2000,
-        },
-        "id": "metrics",
-        "legacy": true,
-        "order": 600,
-        "title": "metrics",
-      },
-      Object {
-        "baseUrl": "http://localhost:5601/app/monitoring",
-        "category": Object {
-          "euiIconType": "managementApp",
-          "label": "Management",
-        },
-        "id": "monitoring",
-        "legacy": true,
-        "order": 800,
-        "title": "monitoring",
-      },
-      Object {
-        "baseUrl": "http://localhost:5601/app/visualize",
-        "category": Object {
-          "label": "Analyze",
-          "order": 1000,
-        },
-        "id": "visualize",
-        "legacy": true,
-        "order": 200,
-        "title": "visualize",
-      },
-      Object {
-        "baseUrl": "http://localhost:5601/app/dashboard",
-        "category": Object {
-          "label": "Analyze",
-          "order": 1000,
-        },
-        "id": "dashboard",
-        "legacy": true,
-        "order": 300,
-        "title": "dashboard",
-      },
-      Object {
-        "baseUrl": "http://localhost:5601/app/canvas",
-        "category": Object {
-          "label": "customCategory",
-        },
-        "id": "canvas",
-        "legacy": true,
-        "order": 400,
-        "title": "canvas",
-      },
-      Object {
-        "baseUrl": "http://localhost:5601/app/logs",
-        "category": Object {
-          "euiIconType": "logoObservability",
-          "label": "Observability",
-          "order": 2000,
-        },
-        "id": "logs",
-        "legacy": true,
-        "order": 700,
-        "title": "logs",
-      },
-    ]
-  }
-  navLinks={
-    Array [
-      Object {
-        "category": Object {
-          "label": "Analyze",
-          "order": 1000,
-        },
-        "data-test-subj": "navDrawerFlyoutLink",
-        "href": "http://localhost:5601/app/discover",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "discover",
-        "label": "discover",
-        "onClick": [Function],
-        "order": 100,
-      },
-      Object {
-        "category": Object {
-          "euiIconType": "logoSecurity",
-          "label": "Security",
-          "order": 3000,
-        },
-        "data-test-subj": "navDrawerAppsMenuLink",
-        "href": "http://localhost:5601/app/siem",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "siem",
-        "label": "siem",
-        "onClick": [Function],
-        "order": 500,
-      },
-      Object {
-        "category": Object {
-          "euiIconType": "logoObservability",
-          "label": "Observability",
-          "order": 2000,
-        },
-        "data-test-subj": "navDrawerFlyoutLink",
-        "href": "http://localhost:5601/app/metrics",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "metrics",
-        "label": "metrics",
-        "onClick": [Function],
-        "order": 600,
-      },
-      Object {
-        "category": Object {
-          "euiIconType": "managementApp",
-          "label": "Management",
-        },
-        "data-test-subj": "navDrawerFlyoutLink",
-        "href": "http://localhost:5601/app/monitoring",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "monitoring",
-        "label": "monitoring",
-        "onClick": [Function],
-        "order": 800,
-      },
-      Object {
-        "category": Object {
-          "label": "Analyze",
-          "order": 1000,
-        },
-        "data-test-subj": "navDrawerFlyoutLink",
-        "href": "http://localhost:5601/app/visualize",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "visualize",
-        "label": "visualize",
-        "onClick": [Function],
-        "order": 200,
-      },
-      Object {
-        "category": Object {
-          "label": "Analyze",
-          "order": 1000,
-        },
-        "data-test-subj": "navDrawerFlyoutLink",
-        "href": "http://localhost:5601/app/dashboard",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "dashboard",
-        "label": "dashboard",
-        "onClick": [Function],
-        "order": 300,
-      },
-      Object {
-        "category": Object {
-          "label": "customCategory",
-        },
-        "data-test-subj": "navDrawerAppsMenuLink",
-        "href": "http://localhost:5601/app/canvas",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "canvas",
-        "label": "canvas",
-        "onClick": [Function],
-        "order": 400,
-      },
-      Object {
-        "category": Object {
-          "euiIconType": "logoObservability",
-          "label": "Observability",
-          "order": 2000,
-        },
-        "data-test-subj": "navDrawerFlyoutLink",
-        "href": "http://localhost:5601/app/logs",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "logs",
-        "label": "logs",
-        "onClick": [Function],
-        "order": 700,
-      },
-    ]
-  }
-  navSetting="grouped"
-  recentlyAccessedItems={Array []}
->
-  <EuiNavDrawer
-    aria-label="Primary"
-    data-test-subj="navDrawer"
-    showExpandButton={true}
-    showToolTips={true}
-  >
-    <EuiOutsideClickDetector
-      isDisabled={true}
-      onOutsideClick={[Function]}
-    >
-      <nav
-        aria-label="Primary"
-        className="euiNavDrawer euiNavDrawer-isCollapsed euiNavDrawer-flyoutIsCollapsed"
-        data-test-subj="navDrawer"
-        onMouseDown={[Function]}
-        onMouseUp={[Function]}
-        onTouchEnd={[Function]}
-        onTouchStart={[Function]}
-      >
-        <EuiFlexGroup
-          gutterSize="none"
-        >
-          <div
-            className="euiFlexGroup euiFlexGroup--directionRow euiFlexGroup--responsive"
-          >
-            <EuiFlexItem
-              grow={false}
-            >
-              <div
-                className="euiFlexItem euiFlexItem--flexGrowZero"
-              >
-                <div
-                  className="euiNavDrawerMenu euiNavDrawerMenu-hasFooter"
-                  id="navDrawerMenu"
-                  onClick={[Function]}
-                >
-                  <EuiListGroup
-                    className="euiNavDrawer__expandButton"
-                    flush={true}
-                  >
-                    <ul
-                      className="euiListGroup euiListGroup-flush euiListGroup-maxWidthDefault euiNavDrawer__expandButton"
-                    >
-                      <EuiI18n
-                        defaults={
-                          Array [
-                            "Collapse",
-                            "Expand",
-                            "Dock navigation",
-                            "Navigation is docked",
-                            "Navigation is undocked",
-                          ]
-                        }
-                        tokens={
-                          Array [
-                            "euiNavDrawer.sideNavCollapse",
-                            "euiNavDrawer.sideNavExpand",
-                            "euiNavDrawer.sideNavLockAriaLabel",
-                            "euiNavDrawer.sideNavLockExpanded",
-                            "euiNavDrawer.sideNavLockCollapsed",
-                          ]
-                        }
-                      >
-                        <EuiListGroupItem
-                          buttonRef={[Function]}
-                          className="navDrawerExpandButton-isCollapsed"
-                          data-test-subj="navDrawerExpandButton-isCollapsed"
-                          extraAction={
-                            Object {
-                              "aria-label": "Dock navigation",
-                              "aria-pressed": false,
-                              "className": "euiNavDrawer__expandButtonLockAction",
-                              "color": "text",
-                              "iconSize": "s",
-                              "iconType": "lockOpen",
-                              "onClick": [Function],
-                              "title": "Navigation is undocked",
-                            }
-                          }
-                          iconType="menuRight"
-                          label="Expand"
-                          onClick={[Function]}
-                          showToolTip={true}
-                          size="s"
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiListGroupItem-hasExtraAction navDrawerExpandButton-isCollapsed"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="Expand"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <button
-                                  className="euiListGroupItem__button"
-                                  data-test-subj="navDrawerExpandButton-isCollapsed"
-                                  disabled={false}
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  type="button"
-                                >
-                                  <EuiIcon
-                                    className="euiListGroupItem__icon"
-                                    type="menuRight"
-                                  >
-                                    <EuiIconEmpty
-                                      aria-hidden={true}
-                                      className="euiIcon euiIcon--medium euiIcon-isLoading euiListGroupItem__icon"
-                                      focusable="false"
-                                      role="img"
-                                      style={null}
-                                    >
-                                      <svg
-                                        aria-hidden={true}
-                                        className="euiIcon euiIcon--medium euiIcon-isLoading euiListGroupItem__icon"
-                                        focusable="false"
-                                        height={16}
-                                        role="img"
-                                        style={null}
-                                        viewBox="0 0 16 16"
-                                        width={16}
-                                        xmlns="http://www.w3.org/2000/svg"
-                                      />
-                                    </EuiIconEmpty>
-                                  </EuiIcon>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    Expand
-                                  </span>
-                                </button>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                      </EuiI18n>
-                    </ul>
-                  </EuiListGroup>
-                  <EuiNavDrawerGroup
-                    aria-label="Recently viewed links, navigation"
-                    flyoutMenuButtonClick={[Function]}
-                    key=".$.0"
-                    listItems={
-                      Array [
-                        Object {
-                          "flyoutMenu": Object {
-                            "listItems": Array [],
-                            "title": "Recent items",
-                          },
-                          "iconType": "recentlyViewedApp",
-                          "isDisabled": true,
-                          "label": "Recently viewed",
-                        },
-                      ]
-                    }
-                    showToolTips={true}
-                  >
-                    <EuiListGroup
-                      aria-label="Recently viewed links, navigation"
-                      className="euiNavDrawerGroup"
-                      listItems={
-                        Array [
-                          Object {
-                            "aria-expanded": false,
-                            "aria-label": "Recently viewed",
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "Recently viewed",
-                            "iconType": "recentlyViewedApp",
-                            "isDisabled": true,
-                            "label": "Recently viewed",
-                            "onClick": [Function],
-                            "size": "s",
-                          },
-                        ]
-                      }
-                      showToolTips={true}
-                    >
-                      <ul
-                        aria-label="Recently viewed links, navigation"
-                        className="euiListGroup euiListGroup-maxWidthDefault euiNavDrawerGroup"
-                      >
-                        <EuiListGroupItem
-                          aria-expanded={false}
-                          aria-label="Recently viewed"
-                          className="euiNavDrawerGroup__item"
-                          data-name="Recently viewed"
-                          iconType="recentlyViewedApp"
-                          isDisabled={true}
-                          key="title-0"
-                          label="Recently viewed"
-                          onClick={[Function]}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isDisabled euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="Recently viewed"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <button
-                                  aria-expanded={false}
-                                  aria-label="Recently viewed"
-                                  className="euiListGroupItem__button"
-                                  data-name="Recently viewed"
-                                  disabled={true}
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  type="button"
-                                >
-                                  <EuiIcon
-                                    className="euiListGroupItem__icon"
-                                    type="recentlyViewedApp"
-                                  >
-                                    <EuiIconEmpty
-                                      aria-hidden={true}
-                                      className="euiIcon euiIcon--medium euiIcon--app euiIcon-isLoading euiListGroupItem__icon"
-                                      focusable="false"
-                                      role="img"
-                                      style={null}
-                                    >
-                                      <svg
-                                        aria-hidden={true}
-                                        className="euiIcon euiIcon--medium euiIcon--app euiIcon-isLoading euiListGroupItem__icon"
-                                        focusable="false"
-                                        height={16}
-                                        role="img"
-                                        style={null}
-                                        viewBox="0 0 16 16"
-                                        width={16}
-                                        xmlns="http://www.w3.org/2000/svg"
-                                      />
-                                    </EuiIconEmpty>
-                                  </EuiIcon>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    Recently viewed
-                                  </span>
-                                </button>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                      </ul>
-                    </EuiListGroup>
-                  </EuiNavDrawerGroup>
-                  <EuiHorizontalRule
-                    key=".$.1"
-                    margin="none"
-                  >
-                    <hr
-                      className="euiHorizontalRule euiHorizontalRule--full"
-                    />
-                  </EuiHorizontalRule>
-                  <EuiNavDrawerGroup
-                    aria-label="Primary navigation links"
-                    data-test-subj="navDrawerAppsMenu"
-                    flyoutMenuButtonClick={[Function]}
-                    key=".$.2/.$.$.0"
-                    listItems={
-                      Array [
-                        Object {
-                          "data-test-subj": "navDrawerCategory",
-                          "flyoutMenu": Object {
-                            "listItems": Array [
-                              Object {
-                                "category": Object {
-                                  "label": "Analyze",
-                                  "order": 1000,
-                                },
-                                "data-test-subj": "navDrawerFlyoutLink",
-                                "href": "http://localhost:5601/app/discover",
-                                "icon": undefined,
-                                "iconType": undefined,
-                                "isActive": false,
-                                "isDisabled": undefined,
-                                "key": "discover",
-                                "label": "discover",
-                                "onClick": [Function],
-                                "order": 100,
-                              },
-                              Object {
-                                "category": Object {
-                                  "label": "Analyze",
-                                  "order": 1000,
-                                },
-                                "data-test-subj": "navDrawerFlyoutLink",
-                                "href": "http://localhost:5601/app/visualize",
-                                "icon": undefined,
-                                "iconType": undefined,
-                                "isActive": false,
-                                "isDisabled": undefined,
-                                "key": "visualize",
-                                "label": "visualize",
-                                "onClick": [Function],
-                                "order": 200,
-                              },
-                              Object {
-                                "category": Object {
-                                  "label": "Analyze",
-                                  "order": 1000,
-                                },
-                                "data-test-subj": "navDrawerFlyoutLink",
-                                "href": "http://localhost:5601/app/dashboard",
-                                "icon": undefined,
-                                "iconType": undefined,
-                                "isActive": false,
-                                "isDisabled": undefined,
-                                "key": "dashboard",
-                                "label": "dashboard",
-                                "onClick": [Function],
-                                "order": 300,
-                              },
-                            ],
-                            "title": "Analyze",
-                          },
-                          "iconType": undefined,
-                          "label": "Analyze",
-                        },
-                        Object {
-                          "data-test-subj": "navDrawerCategory",
-                          "flyoutMenu": Object {
-                            "listItems": Array [
-                              Object {
-                                "category": Object {
-                                  "euiIconType": "logoObservability",
-                                  "label": "Observability",
-                                  "order": 2000,
-                                },
-                                "data-test-subj": "navDrawerFlyoutLink",
-                                "href": "http://localhost:5601/app/metrics",
-                                "icon": undefined,
-                                "iconType": undefined,
-                                "isActive": false,
-                                "isDisabled": undefined,
-                                "key": "metrics",
-                                "label": "metrics",
-                                "onClick": [Function],
-                                "order": 600,
-                              },
-                              Object {
-                                "category": Object {
-                                  "euiIconType": "logoObservability",
-                                  "label": "Observability",
-                                  "order": 2000,
-                                },
-                                "data-test-subj": "navDrawerFlyoutLink",
-                                "href": "http://localhost:5601/app/logs",
-                                "icon": undefined,
-                                "iconType": undefined,
-                                "isActive": false,
-                                "isDisabled": undefined,
-                                "key": "logs",
-                                "label": "logs",
-                                "onClick": [Function],
-                                "order": 700,
-                              },
-                            ],
-                            "title": "Observability",
-                          },
-                          "iconType": "logoObservability",
-                          "label": "Observability",
-                        },
-                        Object {
-                          "category": Object {
-                            "euiIconType": "logoSecurity",
-                            "label": "Security",
-                            "order": 3000,
-                          },
-                          "data-test-subj": "navDrawerAppsMenuLink",
-                          "href": "http://localhost:5601/app/siem",
-                          "icon": undefined,
-                          "iconType": "logoSecurity",
-                          "isActive": false,
-                          "isDisabled": undefined,
-                          "key": "siem",
-                          "label": "Security",
-                          "onClick": [Function],
-                          "order": 500,
-                        },
-                        Object {
-                          "category": Object {
-                            "label": "customCategory",
-                          },
-                          "data-test-subj": "navDrawerAppsMenuLink",
-                          "href": "http://localhost:5601/app/canvas",
-                          "icon": undefined,
-                          "iconType": undefined,
-                          "isActive": false,
-                          "isDisabled": undefined,
-                          "key": "canvas",
-                          "label": "customCategory",
-                          "onClick": [Function],
-                          "order": 400,
-                        },
-                      ]
-                    }
-                    showToolTips={true}
-                  >
-                    <EuiListGroup
-                      aria-label="Primary navigation links"
-                      className="euiNavDrawerGroup"
-                      data-test-subj="navDrawerAppsMenu"
-                      listItems={
-                        Array [
-                          Object {
-                            "aria-expanded": false,
-                            "aria-label": "Analyze",
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "Analyze",
-                            "data-test-subj": "navDrawerCategory",
-                            "icon": <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              A
-                            </span>,
-                            "iconType": undefined,
-                            "label": "Analyze",
-                            "onClick": [Function],
-                            "size": "s",
-                          },
-                          Object {
-                            "aria-expanded": false,
-                            "aria-label": "Observability",
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "Observability",
-                            "data-test-subj": "navDrawerCategory",
-                            "iconType": "logoObservability",
-                            "label": "Observability",
-                            "onClick": [Function],
-                            "size": "s",
-                          },
-                          Object {
-                            "aria-label": "Security",
-                            "category": Object {
-                              "euiIconType": "logoSecurity",
-                              "label": "Security",
-                              "order": 3000,
-                            },
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "Security",
-                            "data-test-subj": "navDrawerAppsMenuLink",
-                            "href": "http://localhost:5601/app/siem",
-                            "icon": undefined,
-                            "iconType": "logoSecurity",
-                            "isActive": false,
-                            "isDisabled": undefined,
-                            "key": "siem",
-                            "label": "Security",
-                            "onClick": [Function],
-                            "order": 500,
-                            "size": "s",
-                          },
-                          Object {
-                            "aria-label": "customCategory",
-                            "category": Object {
-                              "label": "customCategory",
-                            },
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "customCategory",
-                            "data-test-subj": "navDrawerAppsMenuLink",
-                            "href": "http://localhost:5601/app/canvas",
-                            "icon": <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              c
-                            </span>,
-                            "iconType": undefined,
-                            "isActive": false,
-                            "isDisabled": undefined,
-                            "key": "canvas",
-                            "label": "customCategory",
-                            "onClick": [Function],
-                            "order": 400,
-                            "size": "s",
-                          },
-                        ]
-                      }
-                      showToolTips={true}
-                    >
-                      <ul
-                        aria-label="Primary navigation links"
-                        className="euiListGroup euiListGroup-maxWidthDefault euiNavDrawerGroup"
-                        data-test-subj="navDrawerAppsMenu"
-                      >
-                        <EuiListGroupItem
-                          aria-expanded={false}
-                          aria-label="Analyze"
-                          className="euiNavDrawerGroup__item"
-                          data-name="Analyze"
-                          data-test-subj="navDrawerCategory"
-                          icon={
-                            <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              A
-                            </span>
-                          }
-                          key="title-0"
-                          label="Analyze"
-                          onClick={[Function]}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="Analyze"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <button
-                                  aria-expanded={false}
-                                  aria-label="Analyze"
-                                  className="euiListGroupItem__button"
-                                  data-name="Analyze"
-                                  data-test-subj="navDrawerCategory"
-                                  disabled={false}
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  type="button"
-                                >
-                                  <span
-                                    className="euiListGroupItem__icon euiNavDrawerGroup__itemDefaultIcon"
-                                  >
-                                    A
-                                  </span>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    Analyze
-                                  </span>
-                                </button>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                        <EuiListGroupItem
-                          aria-expanded={false}
-                          aria-label="Observability"
-                          className="euiNavDrawerGroup__item"
-                          data-name="Observability"
-                          data-test-subj="navDrawerCategory"
-                          iconType="logoObservability"
-                          key="title-1"
-                          label="Observability"
-                          onClick={[Function]}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="Observability"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <button
-                                  aria-expanded={false}
-                                  aria-label="Observability"
-                                  className="euiListGroupItem__button"
-                                  data-name="Observability"
-                                  data-test-subj="navDrawerCategory"
-                                  disabled={false}
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  type="button"
-                                >
-                                  <EuiIcon
-                                    className="euiListGroupItem__icon"
-                                    type="logoObservability"
-                                  >
-                                    <EuiIconEmpty
-                                      aria-hidden={true}
-                                      className="euiIcon euiIcon--medium euiIcon-isLoading euiListGroupItem__icon"
-                                      focusable="false"
-                                      role="img"
-                                      style={null}
-                                    >
-                                      <svg
-                                        aria-hidden={true}
-                                        className="euiIcon euiIcon--medium euiIcon-isLoading euiListGroupItem__icon"
-                                        focusable="false"
-                                        height={16}
-                                        role="img"
-                                        style={null}
-                                        viewBox="0 0 16 16"
-                                        width={16}
-                                        xmlns="http://www.w3.org/2000/svg"
-                                      />
-                                    </EuiIconEmpty>
-                                  </EuiIcon>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    Observability
-                                  </span>
-                                </button>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                        <EuiListGroupItem
-                          aria-label="Security"
-                          category={
-                            Object {
-                              "euiIconType": "logoSecurity",
-                              "label": "Security",
-                              "order": 3000,
-                            }
-                          }
-                          className="euiNavDrawerGroup__item"
-                          data-name="Security"
-                          data-test-subj="navDrawerAppsMenuLink"
-                          href="http://localhost:5601/app/siem"
-                          iconType="logoSecurity"
-                          isActive={false}
-                          key="siem"
-                          label="Security"
-                          onClick={[Function]}
-                          order={500}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="Security"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <a
-                                  aria-label="Security"
-                                  category={
-                                    Object {
-                                      "euiIconType": "logoSecurity",
-                                      "label": "Security",
-                                      "order": 3000,
-                                    }
-                                  }
-                                  className="euiListGroupItem__button"
-                                  data-name="Security"
-                                  data-test-subj="navDrawerAppsMenuLink"
-                                  href="http://localhost:5601/app/siem"
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  order={500}
-                                >
-                                  <EuiIcon
-                                    className="euiListGroupItem__icon"
-                                    type="logoSecurity"
-                                  >
-                                    <EuiIconEmpty
-                                      aria-hidden={true}
-                                      className="euiIcon euiIcon--medium euiIcon-isLoading euiListGroupItem__icon"
-                                      focusable="false"
-                                      role="img"
-                                      style={null}
-                                    >
-                                      <svg
-                                        aria-hidden={true}
-                                        className="euiIcon euiIcon--medium euiIcon-isLoading euiListGroupItem__icon"
-                                        focusable="false"
-                                        height={16}
-                                        role="img"
-                                        style={null}
-                                        viewBox="0 0 16 16"
-                                        width={16}
-                                        xmlns="http://www.w3.org/2000/svg"
-                                      />
-                                    </EuiIconEmpty>
-                                  </EuiIcon>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    Security
-                                  </span>
-                                </a>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                        <EuiListGroupItem
-                          aria-label="customCategory"
-                          category={
-                            Object {
-                              "label": "customCategory",
-                            }
-                          }
-                          className="euiNavDrawerGroup__item"
-                          data-name="customCategory"
-                          data-test-subj="navDrawerAppsMenuLink"
-                          href="http://localhost:5601/app/canvas"
-                          icon={
-                            <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              c
-                            </span>
-                          }
-                          isActive={false}
-                          key="canvas"
-                          label="customCategory"
-                          onClick={[Function]}
-                          order={400}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="customCategory"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <a
-                                  aria-label="customCategory"
-                                  category={
-                                    Object {
-                                      "label": "customCategory",
-                                    }
-                                  }
-                                  className="euiListGroupItem__button"
-                                  data-name="customCategory"
-                                  data-test-subj="navDrawerAppsMenuLink"
-                                  href="http://localhost:5601/app/canvas"
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  order={400}
-                                >
-                                  <span
-                                    className="euiListGroupItem__icon euiNavDrawerGroup__itemDefaultIcon"
-                                  >
-                                    c
-                                  </span>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    customCategory
-                                  </span>
-                                </a>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                      </ul>
-                    </EuiListGroup>
-                  </EuiNavDrawerGroup>
-                  <EuiHorizontalRule
-                    key=".$.2/.$.$.1"
-                    margin="none"
-                  >
-                    <hr
-                      className="euiHorizontalRule euiHorizontalRule--full"
-                    />
-                  </EuiHorizontalRule>
-                  <EuiNavDrawerGroup
-                    aria-label="Management navigation links"
-                    data-test-subj="navDrawerManagementMenu"
-                    flyoutMenuButtonClick={[Function]}
-                    key=".$.2/.$.$.2"
-                    listItems={
-                      Array [
-                        Object {
-                          "data-test-subj": "navDrawerCategory",
-                          "flyoutMenu": Object {
-                            "listItems": Array [
-                              Object {
-                                "category": Object {
-                                  "euiIconType": "managementApp",
-                                  "label": "Management",
-                                },
-                                "data-test-subj": "navDrawerFlyoutLink",
-                                "href": "http://localhost:5601/app/monitoring",
-                                "icon": undefined,
-                                "iconType": undefined,
-                                "isActive": false,
-                                "isDisabled": undefined,
-                                "key": "monitoring",
-                                "label": "monitoring",
-                                "onClick": [Function],
-                                "order": 800,
-                              },
-                            ],
-                            "title": "Management",
-                          },
-                          "iconType": "managementApp",
-                          "label": "Management",
-                        },
-                      ]
-                    }
-                    showToolTips={true}
-                  >
-                    <EuiListGroup
-                      aria-label="Management navigation links"
-                      className="euiNavDrawerGroup"
-                      data-test-subj="navDrawerManagementMenu"
-                      listItems={
-                        Array [
-                          Object {
-                            "aria-expanded": false,
-                            "aria-label": "Management",
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "Management",
-                            "data-test-subj": "navDrawerCategory",
-                            "iconType": "managementApp",
-                            "label": "Management",
-                            "onClick": [Function],
-                            "size": "s",
-                          },
-                        ]
-                      }
-                      showToolTips={true}
-                    >
-                      <ul
-                        aria-label="Management navigation links"
-                        className="euiListGroup euiListGroup-maxWidthDefault euiNavDrawerGroup"
-                        data-test-subj="navDrawerManagementMenu"
-                      >
-                        <EuiListGroupItem
-                          aria-expanded={false}
-                          aria-label="Management"
-                          className="euiNavDrawerGroup__item"
-                          data-name="Management"
-                          data-test-subj="navDrawerCategory"
-                          iconType="managementApp"
-                          key="title-0"
-                          label="Management"
-                          onClick={[Function]}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="Management"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <button
-                                  aria-expanded={false}
-                                  aria-label="Management"
-                                  className="euiListGroupItem__button"
-                                  data-name="Management"
-                                  data-test-subj="navDrawerCategory"
-                                  disabled={false}
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  type="button"
-                                >
-                                  <EuiIcon
-                                    className="euiListGroupItem__icon"
-                                    type="managementApp"
-                                  >
-                                    <EuiIconEmpty
-                                      aria-hidden={true}
-                                      className="euiIcon euiIcon--medium euiIcon--app euiIcon-isLoading euiListGroupItem__icon"
-                                      focusable="false"
-                                      role="img"
-                                      style={null}
-                                    >
-                                      <svg
-                                        aria-hidden={true}
-                                        className="euiIcon euiIcon--medium euiIcon--app euiIcon-isLoading euiListGroupItem__icon"
-                                        focusable="false"
-                                        height={16}
-                                        role="img"
-                                        style={null}
-                                        viewBox="0 0 16 16"
-                                        width={16}
-                                        xmlns="http://www.w3.org/2000/svg"
-                                      />
-                                    </EuiIconEmpty>
-                                  </EuiIcon>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    Management
-                                  </span>
-                                </button>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                      </ul>
-                    </EuiListGroup>
-                  </EuiNavDrawerGroup>
-                </div>
-              </div>
-            </EuiFlexItem>
-            <EuiNavDrawerFlyout
-              id="navDrawerFlyout"
-              isCollapsed={true}
-              onClose={[Function]}
-              wrapText={true}
-            >
-              <div
-                aria-labelledby="navDrawerFlyoutTitle"
-                className="euiNavDrawerFlyout euiNavDrawerFlyout-isCollapsed"
-                id="navDrawerFlyout"
-                onKeyDown={[Function]}
-              >
-                <EuiTitle
-                  className="euiNavDrawerFlyout__title"
-                  size="xxs"
-                  tabIndex="-1"
-                >
-                  <div
-                    className="euiTitle euiTitle--xxsmall euiNavDrawerFlyout__title"
-                    id="navDrawerFlyoutTitle"
-                    tabIndex="-1"
-                  />
-                </EuiTitle>
-              </div>
-            </EuiNavDrawerFlyout>
-          </div>
-        </EuiFlexGroup>
-      </nav>
-    </EuiOutsideClickDetector>
-  </EuiNavDrawer>
-</ForwardRef(navDrawerRenderer)>
-`;
-
-exports[`NavDrawer Advanced setting set to grouped renders individual items if there are less than 7 1`] = `
-<ForwardRef(navDrawerRenderer)
-  basePath={
-    Object {
-      "get": [Function],
-      "prepend": [Function],
-      "remove": [Function],
-    }
-  }
-  chromeNavLinks={
-    Array [
-      Object {
-        "baseUrl": "http://localhost:5601/app/discover",
-        "category": Object {
-          "label": "Analyze",
-          "order": 1000,
-        },
-        "id": "discover",
-        "legacy": true,
-        "order": 100,
-        "title": "discover",
-      },
-      Object {
-        "baseUrl": "http://localhost:5601/app/siem",
-        "category": Object {
-          "euiIconType": "logoSecurity",
-          "label": "Security",
-          "order": 3000,
-        },
-        "id": "siem",
-        "legacy": true,
-        "order": 500,
-        "title": "siem",
-      },
-      Object {
-        "baseUrl": "http://localhost:5601/app/metrics",
-        "category": Object {
-          "euiIconType": "logoObservability",
-          "label": "Observability",
-          "order": 2000,
-        },
-        "id": "metrics",
-        "legacy": true,
-        "order": 600,
-        "title": "metrics",
-      },
-      Object {
-        "baseUrl": "http://localhost:5601/app/monitoring",
-        "category": Object {
-          "euiIconType": "managementApp",
-          "label": "Management",
-        },
-        "id": "monitoring",
-        "legacy": true,
-        "order": 800,
-        "title": "monitoring",
-      },
-      Object {
-        "baseUrl": "http://localhost:5601/app/visualize",
-        "category": Object {
-          "label": "Analyze",
-          "order": 1000,
-        },
-        "id": "visualize",
-        "legacy": true,
-        "order": 200,
-        "title": "visualize",
-      },
-    ]
-  }
-  navLinks={
-    Array [
-      Object {
-        "category": Object {
-          "label": "Analyze",
-          "order": 1000,
-        },
-        "data-test-subj": "navDrawerAppsMenuLink",
-        "href": "http://localhost:5601/app/discover",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "discover",
-        "label": "discover",
-        "onClick": [Function],
-        "order": 100,
-      },
-      Object {
-        "category": Object {
-          "euiIconType": "logoSecurity",
-          "label": "Security",
-          "order": 3000,
-        },
-        "data-test-subj": "navDrawerAppsMenuLink",
-        "href": "http://localhost:5601/app/siem",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "siem",
-        "label": "siem",
-        "onClick": [Function],
-        "order": 500,
-      },
-      Object {
-        "category": Object {
-          "euiIconType": "logoObservability",
-          "label": "Observability",
-          "order": 2000,
-        },
-        "data-test-subj": "navDrawerAppsMenuLink",
-        "href": "http://localhost:5601/app/metrics",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "metrics",
-        "label": "metrics",
-        "onClick": [Function],
-        "order": 600,
-      },
-      Object {
-        "category": Object {
-          "euiIconType": "managementApp",
-          "label": "Management",
-        },
-        "data-test-subj": "navDrawerAppsMenuLink",
-        "href": "http://localhost:5601/app/monitoring",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "monitoring",
-        "label": "monitoring",
-        "onClick": [Function],
-        "order": 800,
-      },
-      Object {
-        "category": Object {
-          "label": "Analyze",
-          "order": 1000,
-        },
-        "data-test-subj": "navDrawerAppsMenuLink",
-        "href": "http://localhost:5601/app/visualize",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "visualize",
-        "label": "visualize",
-        "onClick": [Function],
-        "order": 200,
-      },
-    ]
-  }
-  navSetting="grouped"
-  recentlyAccessedItems={Array []}
->
-  <EuiNavDrawer
-    aria-label="Primary"
-    data-test-subj="navDrawer"
-    showExpandButton={true}
-    showToolTips={true}
-  >
-    <EuiOutsideClickDetector
-      isDisabled={true}
-      onOutsideClick={[Function]}
-    >
-      <nav
-        aria-label="Primary"
-        className="euiNavDrawer euiNavDrawer-isCollapsed euiNavDrawer-flyoutIsCollapsed"
-        data-test-subj="navDrawer"
-        onMouseDown={[Function]}
-        onMouseUp={[Function]}
-        onTouchEnd={[Function]}
-        onTouchStart={[Function]}
-      >
-        <EuiFlexGroup
-          gutterSize="none"
-        >
-          <div
-            className="euiFlexGroup euiFlexGroup--directionRow euiFlexGroup--responsive"
-          >
-            <EuiFlexItem
-              grow={false}
-            >
-              <div
-                className="euiFlexItem euiFlexItem--flexGrowZero"
-              >
-                <div
-                  className="euiNavDrawerMenu euiNavDrawerMenu-hasFooter"
-                  id="navDrawerMenu"
-                  onClick={[Function]}
-                >
-                  <EuiListGroup
-                    className="euiNavDrawer__expandButton"
-                    flush={true}
-                  >
-                    <ul
-                      className="euiListGroup euiListGroup-flush euiListGroup-maxWidthDefault euiNavDrawer__expandButton"
-                    >
-                      <EuiI18n
-                        defaults={
-                          Array [
-                            "Collapse",
-                            "Expand",
-                            "Dock navigation",
-                            "Navigation is docked",
-                            "Navigation is undocked",
-                          ]
-                        }
-                        tokens={
-                          Array [
-                            "euiNavDrawer.sideNavCollapse",
-                            "euiNavDrawer.sideNavExpand",
-                            "euiNavDrawer.sideNavLockAriaLabel",
-                            "euiNavDrawer.sideNavLockExpanded",
-                            "euiNavDrawer.sideNavLockCollapsed",
-                          ]
-                        }
-                      >
-                        <EuiListGroupItem
-                          buttonRef={[Function]}
-                          className="navDrawerExpandButton-isCollapsed"
-                          data-test-subj="navDrawerExpandButton-isCollapsed"
-                          extraAction={
-                            Object {
-                              "aria-label": "Dock navigation",
-                              "aria-pressed": false,
-                              "className": "euiNavDrawer__expandButtonLockAction",
-                              "color": "text",
-                              "iconSize": "s",
-                              "iconType": "lockOpen",
-                              "onClick": [Function],
-                              "title": "Navigation is undocked",
-                            }
-                          }
-                          iconType="menuRight"
-                          label="Expand"
-                          onClick={[Function]}
-                          showToolTip={true}
-                          size="s"
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiListGroupItem-hasExtraAction navDrawerExpandButton-isCollapsed"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="Expand"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <button
-                                  className="euiListGroupItem__button"
-                                  data-test-subj="navDrawerExpandButton-isCollapsed"
-                                  disabled={false}
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  type="button"
-                                >
-                                  <EuiIcon
-                                    className="euiListGroupItem__icon"
-                                    type="menuRight"
-                                  >
-                                    <EuiIconEmpty
-                                      aria-hidden={true}
-                                      className="euiIcon euiIcon--medium euiIcon-isLoading euiListGroupItem__icon"
-                                      focusable="false"
-                                      role="img"
-                                      style={null}
-                                    >
-                                      <svg
-                                        aria-hidden={true}
-                                        className="euiIcon euiIcon--medium euiIcon-isLoading euiListGroupItem__icon"
-                                        focusable="false"
-                                        height={16}
-                                        role="img"
-                                        style={null}
-                                        viewBox="0 0 16 16"
-                                        width={16}
-                                        xmlns="http://www.w3.org/2000/svg"
-                                      />
-                                    </EuiIconEmpty>
-                                  </EuiIcon>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    Expand
-                                  </span>
-                                </button>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                      </EuiI18n>
-                    </ul>
-                  </EuiListGroup>
-                  <EuiNavDrawerGroup
-                    aria-label="Recently viewed links, navigation"
-                    flyoutMenuButtonClick={[Function]}
-                    key=".$.0"
-                    listItems={
-                      Array [
-                        Object {
-                          "flyoutMenu": Object {
-                            "listItems": Array [],
-                            "title": "Recent items",
-                          },
-                          "iconType": "recentlyViewedApp",
-                          "isDisabled": true,
-                          "label": "Recently viewed",
-                        },
-                      ]
-                    }
-                    showToolTips={true}
-                  >
-                    <EuiListGroup
-                      aria-label="Recently viewed links, navigation"
-                      className="euiNavDrawerGroup"
-                      listItems={
-                        Array [
-                          Object {
-                            "aria-expanded": false,
-                            "aria-label": "Recently viewed",
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "Recently viewed",
-                            "iconType": "recentlyViewedApp",
-                            "isDisabled": true,
-                            "label": "Recently viewed",
-                            "onClick": [Function],
-                            "size": "s",
-                          },
-                        ]
-                      }
-                      showToolTips={true}
-                    >
-                      <ul
-                        aria-label="Recently viewed links, navigation"
-                        className="euiListGroup euiListGroup-maxWidthDefault euiNavDrawerGroup"
-                      >
-                        <EuiListGroupItem
-                          aria-expanded={false}
-                          aria-label="Recently viewed"
-                          className="euiNavDrawerGroup__item"
-                          data-name="Recently viewed"
-                          iconType="recentlyViewedApp"
-                          isDisabled={true}
-                          key="title-0"
-                          label="Recently viewed"
-                          onClick={[Function]}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isDisabled euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="Recently viewed"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <button
-                                  aria-expanded={false}
-                                  aria-label="Recently viewed"
-                                  className="euiListGroupItem__button"
-                                  data-name="Recently viewed"
-                                  disabled={true}
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  type="button"
-                                >
-                                  <EuiIcon
-                                    className="euiListGroupItem__icon"
-                                    type="recentlyViewedApp"
-                                  >
-                                    <EuiIconEmpty
-                                      aria-hidden={true}
-                                      className="euiIcon euiIcon--medium euiIcon--app euiIcon-isLoading euiListGroupItem__icon"
-                                      focusable="false"
-                                      role="img"
-                                      style={null}
-                                    >
-                                      <svg
-                                        aria-hidden={true}
-                                        className="euiIcon euiIcon--medium euiIcon--app euiIcon-isLoading euiListGroupItem__icon"
-                                        focusable="false"
-                                        height={16}
-                                        role="img"
-                                        style={null}
-                                        viewBox="0 0 16 16"
-                                        width={16}
-                                        xmlns="http://www.w3.org/2000/svg"
-                                      />
-                                    </EuiIconEmpty>
-                                  </EuiIcon>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    Recently viewed
-                                  </span>
-                                </button>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                      </ul>
-                    </EuiListGroup>
-                  </EuiNavDrawerGroup>
-                  <EuiHorizontalRule
-                    key=".$.1"
-                    margin="none"
-                  >
-                    <hr
-                      className="euiHorizontalRule euiHorizontalRule--full"
-                    />
-                  </EuiHorizontalRule>
-                  <EuiNavDrawerGroup
-                    aria-label="Primary navigation links"
-                    data-test-subj="navDrawerAppsMenu"
-                    flyoutMenuButtonClick={[Function]}
-                    key=".$.2"
-                    listItems={
-                      Array [
-                        Object {
-                          "category": Object {
-                            "label": "Analyze",
-                            "order": 1000,
-                          },
-                          "data-test-subj": "navDrawerAppsMenuLink",
-                          "href": "http://localhost:5601/app/discover",
-                          "icon": undefined,
-                          "iconType": undefined,
-                          "isActive": false,
-                          "isDisabled": undefined,
-                          "key": "discover",
-                          "label": "discover",
-                          "onClick": [Function],
-                          "order": 100,
-                        },
-                        Object {
-                          "category": Object {
-                            "euiIconType": "logoSecurity",
-                            "label": "Security",
-                            "order": 3000,
-                          },
-                          "data-test-subj": "navDrawerAppsMenuLink",
-                          "href": "http://localhost:5601/app/siem",
-                          "icon": undefined,
-                          "iconType": undefined,
-                          "isActive": false,
-                          "isDisabled": undefined,
-                          "key": "siem",
-                          "label": "siem",
-                          "onClick": [Function],
-                          "order": 500,
-                        },
-                        Object {
-                          "category": Object {
-                            "euiIconType": "logoObservability",
-                            "label": "Observability",
-                            "order": 2000,
-                          },
-                          "data-test-subj": "navDrawerAppsMenuLink",
-                          "href": "http://localhost:5601/app/metrics",
-                          "icon": undefined,
-                          "iconType": undefined,
-                          "isActive": false,
-                          "isDisabled": undefined,
-                          "key": "metrics",
-                          "label": "metrics",
-                          "onClick": [Function],
-                          "order": 600,
-                        },
-                        Object {
-                          "category": Object {
-                            "euiIconType": "managementApp",
-                            "label": "Management",
-                          },
-                          "data-test-subj": "navDrawerAppsMenuLink",
-                          "href": "http://localhost:5601/app/monitoring",
-                          "icon": undefined,
-                          "iconType": undefined,
-                          "isActive": false,
-                          "isDisabled": undefined,
-                          "key": "monitoring",
-                          "label": "monitoring",
-                          "onClick": [Function],
-                          "order": 800,
-                        },
-                        Object {
-                          "category": Object {
-                            "label": "Analyze",
-                            "order": 1000,
-                          },
-                          "data-test-subj": "navDrawerAppsMenuLink",
-                          "href": "http://localhost:5601/app/visualize",
-                          "icon": undefined,
-                          "iconType": undefined,
-                          "isActive": false,
-                          "isDisabled": undefined,
-                          "key": "visualize",
-                          "label": "visualize",
-                          "onClick": [Function],
-                          "order": 200,
-                        },
-                      ]
-                    }
-                    showToolTips={true}
-                  >
-                    <EuiListGroup
-                      aria-label="Primary navigation links"
-                      className="euiNavDrawerGroup"
-                      data-test-subj="navDrawerAppsMenu"
-                      listItems={
-                        Array [
-                          Object {
-                            "aria-label": "discover",
-                            "category": Object {
-                              "label": "Analyze",
-                              "order": 1000,
-                            },
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "discover",
-                            "data-test-subj": "navDrawerAppsMenuLink",
-                            "href": "http://localhost:5601/app/discover",
-                            "icon": <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              d
-                            </span>,
-                            "iconType": undefined,
-                            "isActive": false,
-                            "isDisabled": undefined,
-                            "key": "discover",
-                            "label": "discover",
-                            "onClick": [Function],
-                            "order": 100,
-                            "size": "s",
-                          },
-                          Object {
-                            "aria-label": "siem",
-                            "category": Object {
-                              "euiIconType": "logoSecurity",
-                              "label": "Security",
-                              "order": 3000,
-                            },
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "siem",
-                            "data-test-subj": "navDrawerAppsMenuLink",
-                            "href": "http://localhost:5601/app/siem",
-                            "icon": <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              s
-                            </span>,
-                            "iconType": undefined,
-                            "isActive": false,
-                            "isDisabled": undefined,
-                            "key": "siem",
-                            "label": "siem",
-                            "onClick": [Function],
-                            "order": 500,
-                            "size": "s",
-                          },
-                          Object {
-                            "aria-label": "metrics",
-                            "category": Object {
-                              "euiIconType": "logoObservability",
-                              "label": "Observability",
-                              "order": 2000,
-                            },
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "metrics",
-                            "data-test-subj": "navDrawerAppsMenuLink",
-                            "href": "http://localhost:5601/app/metrics",
-                            "icon": <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              m
-                            </span>,
-                            "iconType": undefined,
-                            "isActive": false,
-                            "isDisabled": undefined,
-                            "key": "metrics",
-                            "label": "metrics",
-                            "onClick": [Function],
-                            "order": 600,
-                            "size": "s",
-                          },
-                          Object {
-                            "aria-label": "monitoring",
-                            "category": Object {
-                              "euiIconType": "managementApp",
-                              "label": "Management",
-                            },
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "monitoring",
-                            "data-test-subj": "navDrawerAppsMenuLink",
-                            "href": "http://localhost:5601/app/monitoring",
-                            "icon": <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              m
-                            </span>,
-                            "iconType": undefined,
-                            "isActive": false,
-                            "isDisabled": undefined,
-                            "key": "monitoring",
-                            "label": "monitoring",
-                            "onClick": [Function],
-                            "order": 800,
-                            "size": "s",
-                          },
-                          Object {
-                            "aria-label": "visualize",
-                            "category": Object {
-                              "label": "Analyze",
-                              "order": 1000,
-                            },
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "visualize",
-                            "data-test-subj": "navDrawerAppsMenuLink",
-                            "href": "http://localhost:5601/app/visualize",
-                            "icon": <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              v
-                            </span>,
-                            "iconType": undefined,
-                            "isActive": false,
-                            "isDisabled": undefined,
-                            "key": "visualize",
-                            "label": "visualize",
-                            "onClick": [Function],
-                            "order": 200,
-                            "size": "s",
-                          },
-                        ]
-                      }
-                      showToolTips={true}
-                    >
-                      <ul
-                        aria-label="Primary navigation links"
-                        className="euiListGroup euiListGroup-maxWidthDefault euiNavDrawerGroup"
-                        data-test-subj="navDrawerAppsMenu"
-                      >
-                        <EuiListGroupItem
-                          aria-label="discover"
-                          category={
-                            Object {
-                              "label": "Analyze",
-                              "order": 1000,
-                            }
-                          }
-                          className="euiNavDrawerGroup__item"
-                          data-name="discover"
-                          data-test-subj="navDrawerAppsMenuLink"
-                          href="http://localhost:5601/app/discover"
-                          icon={
-                            <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              d
-                            </span>
-                          }
-                          isActive={false}
-                          key="discover"
-                          label="discover"
-                          onClick={[Function]}
-                          order={100}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="discover"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <a
-                                  aria-label="discover"
-                                  category={
-                                    Object {
-                                      "label": "Analyze",
-                                      "order": 1000,
-                                    }
-                                  }
-                                  className="euiListGroupItem__button"
-                                  data-name="discover"
-                                  data-test-subj="navDrawerAppsMenuLink"
-                                  href="http://localhost:5601/app/discover"
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  order={100}
-                                >
-                                  <span
-                                    className="euiListGroupItem__icon euiNavDrawerGroup__itemDefaultIcon"
-                                  >
-                                    d
-                                  </span>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    discover
-                                  </span>
-                                </a>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                        <EuiListGroupItem
-                          aria-label="siem"
-                          category={
-                            Object {
-                              "euiIconType": "logoSecurity",
-                              "label": "Security",
-                              "order": 3000,
-                            }
-                          }
-                          className="euiNavDrawerGroup__item"
-                          data-name="siem"
-                          data-test-subj="navDrawerAppsMenuLink"
-                          href="http://localhost:5601/app/siem"
-                          icon={
-                            <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              s
-                            </span>
-                          }
-                          isActive={false}
-                          key="siem"
-                          label="siem"
-                          onClick={[Function]}
-                          order={500}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="siem"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <a
-                                  aria-label="siem"
-                                  category={
-                                    Object {
-                                      "euiIconType": "logoSecurity",
-                                      "label": "Security",
-                                      "order": 3000,
-                                    }
-                                  }
-                                  className="euiListGroupItem__button"
-                                  data-name="siem"
-                                  data-test-subj="navDrawerAppsMenuLink"
-                                  href="http://localhost:5601/app/siem"
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  order={500}
-                                >
-                                  <span
-                                    className="euiListGroupItem__icon euiNavDrawerGroup__itemDefaultIcon"
-                                  >
-                                    s
-                                  </span>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    siem
-                                  </span>
-                                </a>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                        <EuiListGroupItem
-                          aria-label="metrics"
-                          category={
-                            Object {
-                              "euiIconType": "logoObservability",
-                              "label": "Observability",
-                              "order": 2000,
-                            }
-                          }
-                          className="euiNavDrawerGroup__item"
-                          data-name="metrics"
-                          data-test-subj="navDrawerAppsMenuLink"
-                          href="http://localhost:5601/app/metrics"
-                          icon={
-                            <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              m
-                            </span>
-                          }
-                          isActive={false}
-                          key="metrics"
-                          label="metrics"
-                          onClick={[Function]}
-                          order={600}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="metrics"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <a
-                                  aria-label="metrics"
-                                  category={
-                                    Object {
-                                      "euiIconType": "logoObservability",
-                                      "label": "Observability",
-                                      "order": 2000,
-                                    }
-                                  }
-                                  className="euiListGroupItem__button"
-                                  data-name="metrics"
-                                  data-test-subj="navDrawerAppsMenuLink"
-                                  href="http://localhost:5601/app/metrics"
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  order={600}
-                                >
-                                  <span
-                                    className="euiListGroupItem__icon euiNavDrawerGroup__itemDefaultIcon"
-                                  >
-                                    m
-                                  </span>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    metrics
-                                  </span>
-                                </a>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                        <EuiListGroupItem
-                          aria-label="monitoring"
-                          category={
-                            Object {
-                              "euiIconType": "managementApp",
-                              "label": "Management",
-                            }
-                          }
-                          className="euiNavDrawerGroup__item"
-                          data-name="monitoring"
-                          data-test-subj="navDrawerAppsMenuLink"
-                          href="http://localhost:5601/app/monitoring"
-                          icon={
-                            <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              m
-                            </span>
-                          }
-                          isActive={false}
-                          key="monitoring"
-                          label="monitoring"
-                          onClick={[Function]}
-                          order={800}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="monitoring"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <a
-                                  aria-label="monitoring"
-                                  category={
-                                    Object {
-                                      "euiIconType": "managementApp",
-                                      "label": "Management",
-                                    }
-                                  }
-                                  className="euiListGroupItem__button"
-                                  data-name="monitoring"
-                                  data-test-subj="navDrawerAppsMenuLink"
-                                  href="http://localhost:5601/app/monitoring"
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  order={800}
-                                >
-                                  <span
-                                    className="euiListGroupItem__icon euiNavDrawerGroup__itemDefaultIcon"
-                                  >
-                                    m
-                                  </span>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    monitoring
-                                  </span>
-                                </a>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                        <EuiListGroupItem
-                          aria-label="visualize"
-                          category={
-                            Object {
-                              "label": "Analyze",
-                              "order": 1000,
-                            }
-                          }
-                          className="euiNavDrawerGroup__item"
-                          data-name="visualize"
-                          data-test-subj="navDrawerAppsMenuLink"
-                          href="http://localhost:5601/app/visualize"
-                          icon={
-                            <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              v
-                            </span>
-                          }
-                          isActive={false}
-                          key="visualize"
-                          label="visualize"
-                          onClick={[Function]}
-                          order={200}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="visualize"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <a
-                                  aria-label="visualize"
-                                  category={
-                                    Object {
-                                      "label": "Analyze",
-                                      "order": 1000,
-                                    }
-                                  }
-                                  className="euiListGroupItem__button"
-                                  data-name="visualize"
-                                  data-test-subj="navDrawerAppsMenuLink"
-                                  href="http://localhost:5601/app/visualize"
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  order={200}
-                                >
-                                  <span
-                                    className="euiListGroupItem__icon euiNavDrawerGroup__itemDefaultIcon"
-                                  >
-                                    v
-                                  </span>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    visualize
-                                  </span>
-                                </a>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                      </ul>
-                    </EuiListGroup>
-                  </EuiNavDrawerGroup>
-                </div>
-              </div>
-            </EuiFlexItem>
-            <EuiNavDrawerFlyout
-              id="navDrawerFlyout"
-              isCollapsed={true}
-              onClose={[Function]}
-              wrapText={true}
-            >
-              <div
-                aria-labelledby="navDrawerFlyoutTitle"
-                className="euiNavDrawerFlyout euiNavDrawerFlyout-isCollapsed"
-                id="navDrawerFlyout"
-                onKeyDown={[Function]}
-              >
-                <EuiTitle
-                  className="euiNavDrawerFlyout__title"
-                  size="xxs"
-                  tabIndex="-1"
-                >
-                  <div
-                    className="euiTitle euiTitle--xxsmall euiNavDrawerFlyout__title"
-                    id="navDrawerFlyoutTitle"
-                    tabIndex="-1"
-                  />
-                </EuiTitle>
-              </div>
-            </EuiNavDrawerFlyout>
-          </div>
-        </EuiFlexGroup>
-      </nav>
-    </EuiOutsideClickDetector>
-  </EuiNavDrawer>
-</ForwardRef(navDrawerRenderer)>
-`;
-
-exports[`NavDrawer Advanced setting set to grouped renders individual items if there is only 1 category 1`] = `
-<ForwardRef(navDrawerRenderer)
-  basePath={
-    Object {
-      "get": [Function],
-      "prepend": [Function],
-      "remove": [Function],
-    }
-  }
-  chromeNavLinks={
-    Array [
-      Object {
-        "baseUrl": "http://localhost:5601/app/discover",
-        "category": Object {
-          "label": "Analyze",
-          "order": 1000,
-        },
-        "id": "discover",
-        "legacy": true,
-        "order": 100,
-        "title": "discover",
-      },
-      Object {
-        "baseUrl": "http://localhost:5601/app/siem",
-        "category": Object {
-          "label": "Analyze",
-          "order": 1000,
-        },
-        "id": "siem",
-        "legacy": true,
-        "order": 500,
-        "title": "siem",
-      },
-      Object {
-        "baseUrl": "http://localhost:5601/app/metrics",
-        "category": Object {
-          "label": "Analyze",
-          "order": 1000,
-        },
-        "id": "metrics",
-        "legacy": true,
-        "order": 600,
-        "title": "metrics",
-      },
-      Object {
-        "baseUrl": "http://localhost:5601/app/monitoring",
-        "category": Object {
-          "label": "Analyze",
-          "order": 1000,
-        },
-        "id": "monitoring",
-        "legacy": true,
-        "order": 800,
-        "title": "monitoring",
-      },
-      Object {
-        "baseUrl": "http://localhost:5601/app/visualize",
-        "category": Object {
-          "label": "Analyze",
-          "order": 1000,
-        },
-        "id": "visualize",
-        "legacy": true,
-        "order": 200,
-        "title": "visualize",
-      },
-      Object {
-        "baseUrl": "http://localhost:5601/app/dashboard",
-        "category": Object {
-          "euiIconType": "managementApp",
-          "label": "Management",
-        },
-        "id": "dashboard",
-        "legacy": true,
-        "order": 300,
-        "title": "dashboard",
-      },
-      Object {
-        "baseUrl": "http://localhost:5601/app/canvas",
-        "category": Object {
-          "euiIconType": "managementApp",
-          "label": "Management",
-        },
-        "id": "canvas",
-        "legacy": true,
-        "order": 400,
-        "title": "canvas",
-      },
-      Object {
-        "baseUrl": "http://localhost:5601/app/logs",
-        "category": Object {
-          "euiIconType": "managementApp",
-          "label": "Management",
-        },
-        "id": "logs",
-        "legacy": true,
-        "order": 700,
-        "title": "logs",
-      },
-    ]
-  }
-  navLinks={
-    Array [
-      Object {
-        "category": Object {
-          "label": "Analyze",
-          "order": 1000,
-        },
-        "data-test-subj": "navDrawerAppsMenuLink",
-        "href": "http://localhost:5601/app/discover",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "discover",
-        "label": "discover",
-        "onClick": [Function],
-        "order": 100,
-      },
-      Object {
-        "category": Object {
-          "label": "Analyze",
-          "order": 1000,
-        },
-        "data-test-subj": "navDrawerAppsMenuLink",
-        "href": "http://localhost:5601/app/siem",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "siem",
-        "label": "siem",
-        "onClick": [Function],
-        "order": 500,
-      },
-      Object {
-        "category": Object {
-          "label": "Analyze",
-          "order": 1000,
-        },
-        "data-test-subj": "navDrawerAppsMenuLink",
-        "href": "http://localhost:5601/app/metrics",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "metrics",
-        "label": "metrics",
-        "onClick": [Function],
-        "order": 600,
-      },
-      Object {
-        "category": Object {
-          "label": "Analyze",
-          "order": 1000,
-        },
-        "data-test-subj": "navDrawerAppsMenuLink",
-        "href": "http://localhost:5601/app/monitoring",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "monitoring",
-        "label": "monitoring",
-        "onClick": [Function],
-        "order": 800,
-      },
-      Object {
-        "category": Object {
-          "label": "Analyze",
-          "order": 1000,
-        },
-        "data-test-subj": "navDrawerAppsMenuLink",
-        "href": "http://localhost:5601/app/visualize",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "visualize",
-        "label": "visualize",
-        "onClick": [Function],
-        "order": 200,
-      },
-      Object {
-        "category": Object {
-          "euiIconType": "managementApp",
-          "label": "Management",
-        },
-        "data-test-subj": "navDrawerAppsMenuLink",
-        "href": "http://localhost:5601/app/dashboard",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "dashboard",
-        "label": "dashboard",
-        "onClick": [Function],
-        "order": 300,
-      },
-      Object {
-        "category": Object {
-          "euiIconType": "managementApp",
-          "label": "Management",
-        },
-        "data-test-subj": "navDrawerAppsMenuLink",
-        "href": "http://localhost:5601/app/canvas",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "canvas",
-        "label": "canvas",
-        "onClick": [Function],
-        "order": 400,
-      },
-      Object {
-        "category": Object {
-          "euiIconType": "managementApp",
-          "label": "Management",
-        },
-        "data-test-subj": "navDrawerAppsMenuLink",
-        "href": "http://localhost:5601/app/logs",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "logs",
-        "label": "logs",
-        "onClick": [Function],
-        "order": 700,
-      },
-    ]
-  }
-  navSetting="grouped"
-  recentlyAccessedItems={Array []}
->
-  <EuiNavDrawer
-    aria-label="Primary"
-    data-test-subj="navDrawer"
-    showExpandButton={true}
-    showToolTips={true}
-  >
-    <EuiOutsideClickDetector
-      isDisabled={true}
-      onOutsideClick={[Function]}
-    >
-      <nav
-        aria-label="Primary"
-        className="euiNavDrawer euiNavDrawer-isCollapsed euiNavDrawer-flyoutIsCollapsed"
-        data-test-subj="navDrawer"
-        onMouseDown={[Function]}
-        onMouseUp={[Function]}
-        onTouchEnd={[Function]}
-        onTouchStart={[Function]}
-      >
-        <EuiFlexGroup
-          gutterSize="none"
-        >
-          <div
-            className="euiFlexGroup euiFlexGroup--directionRow euiFlexGroup--responsive"
-          >
-            <EuiFlexItem
-              grow={false}
-            >
-              <div
-                className="euiFlexItem euiFlexItem--flexGrowZero"
-              >
-                <div
-                  className="euiNavDrawerMenu euiNavDrawerMenu-hasFooter"
-                  id="navDrawerMenu"
-                  onClick={[Function]}
-                >
-                  <EuiListGroup
-                    className="euiNavDrawer__expandButton"
-                    flush={true}
-                  >
-                    <ul
-                      className="euiListGroup euiListGroup-flush euiListGroup-maxWidthDefault euiNavDrawer__expandButton"
-                    >
-                      <EuiI18n
-                        defaults={
-                          Array [
-                            "Collapse",
-                            "Expand",
-                            "Dock navigation",
-                            "Navigation is docked",
-                            "Navigation is undocked",
-                          ]
-                        }
-                        tokens={
-                          Array [
-                            "euiNavDrawer.sideNavCollapse",
-                            "euiNavDrawer.sideNavExpand",
-                            "euiNavDrawer.sideNavLockAriaLabel",
-                            "euiNavDrawer.sideNavLockExpanded",
-                            "euiNavDrawer.sideNavLockCollapsed",
-                          ]
-                        }
-                      >
-                        <EuiListGroupItem
-                          buttonRef={[Function]}
-                          className="navDrawerExpandButton-isCollapsed"
-                          data-test-subj="navDrawerExpandButton-isCollapsed"
-                          extraAction={
-                            Object {
-                              "aria-label": "Dock navigation",
-                              "aria-pressed": false,
-                              "className": "euiNavDrawer__expandButtonLockAction",
-                              "color": "text",
-                              "iconSize": "s",
-                              "iconType": "lockOpen",
-                              "onClick": [Function],
-                              "title": "Navigation is undocked",
-                            }
-                          }
-                          iconType="menuRight"
-                          label="Expand"
-                          onClick={[Function]}
-                          showToolTip={true}
-                          size="s"
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiListGroupItem-hasExtraAction navDrawerExpandButton-isCollapsed"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="Expand"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <button
-                                  className="euiListGroupItem__button"
-                                  data-test-subj="navDrawerExpandButton-isCollapsed"
-                                  disabled={false}
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  type="button"
-                                >
-                                  <EuiIcon
-                                    className="euiListGroupItem__icon"
-                                    type="menuRight"
-                                  >
-                                    <EuiIconEmpty
-                                      aria-hidden={true}
-                                      className="euiIcon euiIcon--medium euiIcon-isLoading euiListGroupItem__icon"
-                                      focusable="false"
-                                      role="img"
-                                      style={null}
-                                    >
-                                      <svg
-                                        aria-hidden={true}
-                                        className="euiIcon euiIcon--medium euiIcon-isLoading euiListGroupItem__icon"
-                                        focusable="false"
-                                        height={16}
-                                        role="img"
-                                        style={null}
-                                        viewBox="0 0 16 16"
-                                        width={16}
-                                        xmlns="http://www.w3.org/2000/svg"
-                                      />
-                                    </EuiIconEmpty>
-                                  </EuiIcon>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    Expand
-                                  </span>
-                                </button>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                      </EuiI18n>
-                    </ul>
-                  </EuiListGroup>
-                  <EuiNavDrawerGroup
-                    aria-label="Recently viewed links, navigation"
-                    flyoutMenuButtonClick={[Function]}
-                    key=".$.0"
-                    listItems={
-                      Array [
-                        Object {
-                          "flyoutMenu": Object {
-                            "listItems": Array [],
-                            "title": "Recent items",
-                          },
-                          "iconType": "recentlyViewedApp",
-                          "isDisabled": true,
-                          "label": "Recently viewed",
-                        },
-                      ]
-                    }
-                    showToolTips={true}
-                  >
-                    <EuiListGroup
-                      aria-label="Recently viewed links, navigation"
-                      className="euiNavDrawerGroup"
-                      listItems={
-                        Array [
-                          Object {
-                            "aria-expanded": false,
-                            "aria-label": "Recently viewed",
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "Recently viewed",
-                            "iconType": "recentlyViewedApp",
-                            "isDisabled": true,
-                            "label": "Recently viewed",
-                            "onClick": [Function],
-                            "size": "s",
-                          },
-                        ]
-                      }
-                      showToolTips={true}
-                    >
-                      <ul
-                        aria-label="Recently viewed links, navigation"
-                        className="euiListGroup euiListGroup-maxWidthDefault euiNavDrawerGroup"
-                      >
-                        <EuiListGroupItem
-                          aria-expanded={false}
-                          aria-label="Recently viewed"
-                          className="euiNavDrawerGroup__item"
-                          data-name="Recently viewed"
-                          iconType="recentlyViewedApp"
-                          isDisabled={true}
-                          key="title-0"
-                          label="Recently viewed"
-                          onClick={[Function]}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isDisabled euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="Recently viewed"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <button
-                                  aria-expanded={false}
-                                  aria-label="Recently viewed"
-                                  className="euiListGroupItem__button"
-                                  data-name="Recently viewed"
-                                  disabled={true}
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  type="button"
-                                >
-                                  <EuiIcon
-                                    className="euiListGroupItem__icon"
-                                    type="recentlyViewedApp"
-                                  >
-                                    <EuiIconEmpty
-                                      aria-hidden={true}
-                                      className="euiIcon euiIcon--medium euiIcon--app euiIcon-isLoading euiListGroupItem__icon"
-                                      focusable="false"
-                                      role="img"
-                                      style={null}
-                                    >
-                                      <svg
-                                        aria-hidden={true}
-                                        className="euiIcon euiIcon--medium euiIcon--app euiIcon-isLoading euiListGroupItem__icon"
-                                        focusable="false"
-                                        height={16}
-                                        role="img"
-                                        style={null}
-                                        viewBox="0 0 16 16"
-                                        width={16}
-                                        xmlns="http://www.w3.org/2000/svg"
-                                      />
-                                    </EuiIconEmpty>
-                                  </EuiIcon>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    Recently viewed
-                                  </span>
-                                </button>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                      </ul>
-                    </EuiListGroup>
-                  </EuiNavDrawerGroup>
-                  <EuiHorizontalRule
-                    key=".$.1"
-                    margin="none"
-                  >
-                    <hr
-                      className="euiHorizontalRule euiHorizontalRule--full"
-                    />
-                  </EuiHorizontalRule>
-                  <EuiNavDrawerGroup
-                    aria-label="Primary navigation links"
-                    data-test-subj="navDrawerAppsMenu"
-                    flyoutMenuButtonClick={[Function]}
-                    key=".$.2"
-                    listItems={
-                      Array [
-                        Object {
-                          "category": Object {
-                            "label": "Analyze",
-                            "order": 1000,
-                          },
-                          "data-test-subj": "navDrawerAppsMenuLink",
-                          "href": "http://localhost:5601/app/discover",
-                          "icon": undefined,
-                          "iconType": undefined,
-                          "isActive": false,
-                          "isDisabled": undefined,
-                          "key": "discover",
-                          "label": "discover",
-                          "onClick": [Function],
-                          "order": 100,
-                        },
-                        Object {
-                          "category": Object {
-                            "label": "Analyze",
-                            "order": 1000,
-                          },
-                          "data-test-subj": "navDrawerAppsMenuLink",
-                          "href": "http://localhost:5601/app/siem",
-                          "icon": undefined,
-                          "iconType": undefined,
-                          "isActive": false,
-                          "isDisabled": undefined,
-                          "key": "siem",
-                          "label": "siem",
-                          "onClick": [Function],
-                          "order": 500,
-                        },
-                        Object {
-                          "category": Object {
-                            "label": "Analyze",
-                            "order": 1000,
-                          },
-                          "data-test-subj": "navDrawerAppsMenuLink",
-                          "href": "http://localhost:5601/app/metrics",
-                          "icon": undefined,
-                          "iconType": undefined,
-                          "isActive": false,
-                          "isDisabled": undefined,
-                          "key": "metrics",
-                          "label": "metrics",
-                          "onClick": [Function],
-                          "order": 600,
-                        },
-                        Object {
-                          "category": Object {
-                            "label": "Analyze",
-                            "order": 1000,
-                          },
-                          "data-test-subj": "navDrawerAppsMenuLink",
-                          "href": "http://localhost:5601/app/monitoring",
-                          "icon": undefined,
-                          "iconType": undefined,
-                          "isActive": false,
-                          "isDisabled": undefined,
-                          "key": "monitoring",
-                          "label": "monitoring",
-                          "onClick": [Function],
-                          "order": 800,
-                        },
-                        Object {
-                          "category": Object {
-                            "label": "Analyze",
-                            "order": 1000,
-                          },
-                          "data-test-subj": "navDrawerAppsMenuLink",
-                          "href": "http://localhost:5601/app/visualize",
-                          "icon": undefined,
-                          "iconType": undefined,
-                          "isActive": false,
-                          "isDisabled": undefined,
-                          "key": "visualize",
-                          "label": "visualize",
-                          "onClick": [Function],
-                          "order": 200,
-                        },
-                        Object {
-                          "category": Object {
-                            "euiIconType": "managementApp",
-                            "label": "Management",
-                          },
-                          "data-test-subj": "navDrawerAppsMenuLink",
-                          "href": "http://localhost:5601/app/dashboard",
-                          "icon": undefined,
-                          "iconType": undefined,
-                          "isActive": false,
-                          "isDisabled": undefined,
-                          "key": "dashboard",
-                          "label": "dashboard",
-                          "onClick": [Function],
-                          "order": 300,
-                        },
-                        Object {
-                          "category": Object {
-                            "euiIconType": "managementApp",
-                            "label": "Management",
-                          },
-                          "data-test-subj": "navDrawerAppsMenuLink",
-                          "href": "http://localhost:5601/app/canvas",
-                          "icon": undefined,
-                          "iconType": undefined,
-                          "isActive": false,
-                          "isDisabled": undefined,
-                          "key": "canvas",
-                          "label": "canvas",
-                          "onClick": [Function],
-                          "order": 400,
-                        },
-                        Object {
-                          "category": Object {
-                            "euiIconType": "managementApp",
-                            "label": "Management",
-                          },
-                          "data-test-subj": "navDrawerAppsMenuLink",
-                          "href": "http://localhost:5601/app/logs",
-                          "icon": undefined,
-                          "iconType": undefined,
-                          "isActive": false,
-                          "isDisabled": undefined,
-                          "key": "logs",
-                          "label": "logs",
-                          "onClick": [Function],
-                          "order": 700,
-                        },
-                      ]
-                    }
-                    showToolTips={true}
-                  >
-                    <EuiListGroup
-                      aria-label="Primary navigation links"
-                      className="euiNavDrawerGroup"
-                      data-test-subj="navDrawerAppsMenu"
-                      listItems={
-                        Array [
-                          Object {
-                            "aria-label": "discover",
-                            "category": Object {
-                              "label": "Analyze",
-                              "order": 1000,
-                            },
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "discover",
-                            "data-test-subj": "navDrawerAppsMenuLink",
-                            "href": "http://localhost:5601/app/discover",
-                            "icon": <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              d
-                            </span>,
-                            "iconType": undefined,
-                            "isActive": false,
-                            "isDisabled": undefined,
-                            "key": "discover",
-                            "label": "discover",
-                            "onClick": [Function],
-                            "order": 100,
-                            "size": "s",
-                          },
-                          Object {
-                            "aria-label": "siem",
-                            "category": Object {
-                              "label": "Analyze",
-                              "order": 1000,
-                            },
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "siem",
-                            "data-test-subj": "navDrawerAppsMenuLink",
-                            "href": "http://localhost:5601/app/siem",
-                            "icon": <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              s
-                            </span>,
-                            "iconType": undefined,
-                            "isActive": false,
-                            "isDisabled": undefined,
-                            "key": "siem",
-                            "label": "siem",
-                            "onClick": [Function],
-                            "order": 500,
-                            "size": "s",
-                          },
-                          Object {
-                            "aria-label": "metrics",
-                            "category": Object {
-                              "label": "Analyze",
-                              "order": 1000,
-                            },
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "metrics",
-                            "data-test-subj": "navDrawerAppsMenuLink",
-                            "href": "http://localhost:5601/app/metrics",
-                            "icon": <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              m
-                            </span>,
-                            "iconType": undefined,
-                            "isActive": false,
-                            "isDisabled": undefined,
-                            "key": "metrics",
-                            "label": "metrics",
-                            "onClick": [Function],
-                            "order": 600,
-                            "size": "s",
-                          },
-                          Object {
-                            "aria-label": "monitoring",
-                            "category": Object {
-                              "label": "Analyze",
-                              "order": 1000,
-                            },
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "monitoring",
-                            "data-test-subj": "navDrawerAppsMenuLink",
-                            "href": "http://localhost:5601/app/monitoring",
-                            "icon": <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              m
-                            </span>,
-                            "iconType": undefined,
-                            "isActive": false,
-                            "isDisabled": undefined,
-                            "key": "monitoring",
-                            "label": "monitoring",
-                            "onClick": [Function],
-                            "order": 800,
-                            "size": "s",
-                          },
-                          Object {
-                            "aria-label": "visualize",
-                            "category": Object {
-                              "label": "Analyze",
-                              "order": 1000,
-                            },
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "visualize",
-                            "data-test-subj": "navDrawerAppsMenuLink",
-                            "href": "http://localhost:5601/app/visualize",
-                            "icon": <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              v
-                            </span>,
-                            "iconType": undefined,
-                            "isActive": false,
-                            "isDisabled": undefined,
-                            "key": "visualize",
-                            "label": "visualize",
-                            "onClick": [Function],
-                            "order": 200,
-                            "size": "s",
-                          },
-                          Object {
-                            "aria-label": "dashboard",
-                            "category": Object {
-                              "euiIconType": "managementApp",
-                              "label": "Management",
-                            },
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "dashboard",
-                            "data-test-subj": "navDrawerAppsMenuLink",
-                            "href": "http://localhost:5601/app/dashboard",
-                            "icon": <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              d
-                            </span>,
-                            "iconType": undefined,
-                            "isActive": false,
-                            "isDisabled": undefined,
-                            "key": "dashboard",
-                            "label": "dashboard",
-                            "onClick": [Function],
-                            "order": 300,
-                            "size": "s",
-                          },
-                          Object {
-                            "aria-label": "canvas",
-                            "category": Object {
-                              "euiIconType": "managementApp",
-                              "label": "Management",
-                            },
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "canvas",
-                            "data-test-subj": "navDrawerAppsMenuLink",
-                            "href": "http://localhost:5601/app/canvas",
-                            "icon": <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              c
-                            </span>,
-                            "iconType": undefined,
-                            "isActive": false,
-                            "isDisabled": undefined,
-                            "key": "canvas",
-                            "label": "canvas",
-                            "onClick": [Function],
-                            "order": 400,
-                            "size": "s",
-                          },
-                          Object {
-                            "aria-label": "logs",
-                            "category": Object {
-                              "euiIconType": "managementApp",
-                              "label": "Management",
-                            },
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "logs",
-                            "data-test-subj": "navDrawerAppsMenuLink",
-                            "href": "http://localhost:5601/app/logs",
-                            "icon": <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              l
-                            </span>,
-                            "iconType": undefined,
-                            "isActive": false,
-                            "isDisabled": undefined,
-                            "key": "logs",
-                            "label": "logs",
-                            "onClick": [Function],
-                            "order": 700,
-                            "size": "s",
-                          },
-                        ]
-                      }
-                      showToolTips={true}
-                    >
-                      <ul
-                        aria-label="Primary navigation links"
-                        className="euiListGroup euiListGroup-maxWidthDefault euiNavDrawerGroup"
-                        data-test-subj="navDrawerAppsMenu"
-                      >
-                        <EuiListGroupItem
-                          aria-label="discover"
-                          category={
-                            Object {
-                              "label": "Analyze",
-                              "order": 1000,
-                            }
-                          }
-                          className="euiNavDrawerGroup__item"
-                          data-name="discover"
-                          data-test-subj="navDrawerAppsMenuLink"
-                          href="http://localhost:5601/app/discover"
-                          icon={
-                            <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              d
-                            </span>
-                          }
-                          isActive={false}
-                          key="discover"
-                          label="discover"
-                          onClick={[Function]}
-                          order={100}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="discover"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <a
-                                  aria-label="discover"
-                                  category={
-                                    Object {
-                                      "label": "Analyze",
-                                      "order": 1000,
-                                    }
-                                  }
-                                  className="euiListGroupItem__button"
-                                  data-name="discover"
-                                  data-test-subj="navDrawerAppsMenuLink"
-                                  href="http://localhost:5601/app/discover"
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  order={100}
-                                >
-                                  <span
-                                    className="euiListGroupItem__icon euiNavDrawerGroup__itemDefaultIcon"
-                                  >
-                                    d
-                                  </span>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    discover
-                                  </span>
-                                </a>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                        <EuiListGroupItem
-                          aria-label="siem"
-                          category={
-                            Object {
-                              "label": "Analyze",
-                              "order": 1000,
-                            }
-                          }
-                          className="euiNavDrawerGroup__item"
-                          data-name="siem"
-                          data-test-subj="navDrawerAppsMenuLink"
-                          href="http://localhost:5601/app/siem"
-                          icon={
-                            <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              s
-                            </span>
-                          }
-                          isActive={false}
-                          key="siem"
-                          label="siem"
-                          onClick={[Function]}
-                          order={500}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="siem"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <a
-                                  aria-label="siem"
-                                  category={
-                                    Object {
-                                      "label": "Analyze",
-                                      "order": 1000,
-                                    }
-                                  }
-                                  className="euiListGroupItem__button"
-                                  data-name="siem"
-                                  data-test-subj="navDrawerAppsMenuLink"
-                                  href="http://localhost:5601/app/siem"
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  order={500}
-                                >
-                                  <span
-                                    className="euiListGroupItem__icon euiNavDrawerGroup__itemDefaultIcon"
-                                  >
-                                    s
-                                  </span>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    siem
-                                  </span>
-                                </a>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                        <EuiListGroupItem
-                          aria-label="metrics"
-                          category={
-                            Object {
-                              "label": "Analyze",
-                              "order": 1000,
-                            }
-                          }
-                          className="euiNavDrawerGroup__item"
-                          data-name="metrics"
-                          data-test-subj="navDrawerAppsMenuLink"
-                          href="http://localhost:5601/app/metrics"
-                          icon={
-                            <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              m
-                            </span>
-                          }
-                          isActive={false}
-                          key="metrics"
-                          label="metrics"
-                          onClick={[Function]}
-                          order={600}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="metrics"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <a
-                                  aria-label="metrics"
-                                  category={
-                                    Object {
-                                      "label": "Analyze",
-                                      "order": 1000,
-                                    }
-                                  }
-                                  className="euiListGroupItem__button"
-                                  data-name="metrics"
-                                  data-test-subj="navDrawerAppsMenuLink"
-                                  href="http://localhost:5601/app/metrics"
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  order={600}
-                                >
-                                  <span
-                                    className="euiListGroupItem__icon euiNavDrawerGroup__itemDefaultIcon"
-                                  >
-                                    m
-                                  </span>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    metrics
-                                  </span>
-                                </a>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                        <EuiListGroupItem
-                          aria-label="monitoring"
-                          category={
-                            Object {
-                              "label": "Analyze",
-                              "order": 1000,
-                            }
-                          }
-                          className="euiNavDrawerGroup__item"
-                          data-name="monitoring"
-                          data-test-subj="navDrawerAppsMenuLink"
-                          href="http://localhost:5601/app/monitoring"
-                          icon={
-                            <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              m
-                            </span>
-                          }
-                          isActive={false}
-                          key="monitoring"
-                          label="monitoring"
-                          onClick={[Function]}
-                          order={800}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="monitoring"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <a
-                                  aria-label="monitoring"
-                                  category={
-                                    Object {
-                                      "label": "Analyze",
-                                      "order": 1000,
-                                    }
-                                  }
-                                  className="euiListGroupItem__button"
-                                  data-name="monitoring"
-                                  data-test-subj="navDrawerAppsMenuLink"
-                                  href="http://localhost:5601/app/monitoring"
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  order={800}
-                                >
-                                  <span
-                                    className="euiListGroupItem__icon euiNavDrawerGroup__itemDefaultIcon"
-                                  >
-                                    m
-                                  </span>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    monitoring
-                                  </span>
-                                </a>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                        <EuiListGroupItem
-                          aria-label="visualize"
-                          category={
-                            Object {
-                              "label": "Analyze",
-                              "order": 1000,
-                            }
-                          }
-                          className="euiNavDrawerGroup__item"
-                          data-name="visualize"
-                          data-test-subj="navDrawerAppsMenuLink"
-                          href="http://localhost:5601/app/visualize"
-                          icon={
-                            <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              v
-                            </span>
-                          }
-                          isActive={false}
-                          key="visualize"
-                          label="visualize"
-                          onClick={[Function]}
-                          order={200}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="visualize"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <a
-                                  aria-label="visualize"
-                                  category={
-                                    Object {
-                                      "label": "Analyze",
-                                      "order": 1000,
-                                    }
-                                  }
-                                  className="euiListGroupItem__button"
-                                  data-name="visualize"
-                                  data-test-subj="navDrawerAppsMenuLink"
-                                  href="http://localhost:5601/app/visualize"
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  order={200}
-                                >
-                                  <span
-                                    className="euiListGroupItem__icon euiNavDrawerGroup__itemDefaultIcon"
-                                  >
-                                    v
-                                  </span>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    visualize
-                                  </span>
-                                </a>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                        <EuiListGroupItem
-                          aria-label="dashboard"
-                          category={
-                            Object {
-                              "euiIconType": "managementApp",
-                              "label": "Management",
-                            }
-                          }
-                          className="euiNavDrawerGroup__item"
-                          data-name="dashboard"
-                          data-test-subj="navDrawerAppsMenuLink"
-                          href="http://localhost:5601/app/dashboard"
-                          icon={
-                            <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              d
-                            </span>
-                          }
-                          isActive={false}
-                          key="dashboard"
-                          label="dashboard"
-                          onClick={[Function]}
-                          order={300}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="dashboard"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <a
-                                  aria-label="dashboard"
-                                  category={
-                                    Object {
-                                      "euiIconType": "managementApp",
-                                      "label": "Management",
-                                    }
-                                  }
-                                  className="euiListGroupItem__button"
-                                  data-name="dashboard"
-                                  data-test-subj="navDrawerAppsMenuLink"
-                                  href="http://localhost:5601/app/dashboard"
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  order={300}
-                                >
-                                  <span
-                                    className="euiListGroupItem__icon euiNavDrawerGroup__itemDefaultIcon"
-                                  >
-                                    d
-                                  </span>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    dashboard
-                                  </span>
-                                </a>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                        <EuiListGroupItem
-                          aria-label="canvas"
-                          category={
-                            Object {
-                              "euiIconType": "managementApp",
-                              "label": "Management",
-                            }
-                          }
-                          className="euiNavDrawerGroup__item"
-                          data-name="canvas"
-                          data-test-subj="navDrawerAppsMenuLink"
-                          href="http://localhost:5601/app/canvas"
-                          icon={
-                            <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              c
-                            </span>
-                          }
-                          isActive={false}
-                          key="canvas"
-                          label="canvas"
-                          onClick={[Function]}
-                          order={400}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="canvas"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <a
-                                  aria-label="canvas"
-                                  category={
-                                    Object {
-                                      "euiIconType": "managementApp",
-                                      "label": "Management",
-                                    }
-                                  }
-                                  className="euiListGroupItem__button"
-                                  data-name="canvas"
-                                  data-test-subj="navDrawerAppsMenuLink"
-                                  href="http://localhost:5601/app/canvas"
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  order={400}
-                                >
-                                  <span
-                                    className="euiListGroupItem__icon euiNavDrawerGroup__itemDefaultIcon"
-                                  >
-                                    c
-                                  </span>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    canvas
-                                  </span>
-                                </a>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                        <EuiListGroupItem
-                          aria-label="logs"
-                          category={
-                            Object {
-                              "euiIconType": "managementApp",
-                              "label": "Management",
-                            }
-                          }
-                          className="euiNavDrawerGroup__item"
-                          data-name="logs"
-                          data-test-subj="navDrawerAppsMenuLink"
-                          href="http://localhost:5601/app/logs"
-                          icon={
-                            <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              l
-                            </span>
-                          }
-                          isActive={false}
-                          key="logs"
-                          label="logs"
-                          onClick={[Function]}
-                          order={700}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="logs"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <a
-                                  aria-label="logs"
-                                  category={
-                                    Object {
-                                      "euiIconType": "managementApp",
-                                      "label": "Management",
-                                    }
-                                  }
-                                  className="euiListGroupItem__button"
-                                  data-name="logs"
-                                  data-test-subj="navDrawerAppsMenuLink"
-                                  href="http://localhost:5601/app/logs"
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  order={700}
-                                >
-                                  <span
-                                    className="euiListGroupItem__icon euiNavDrawerGroup__itemDefaultIcon"
-                                  >
-                                    l
-                                  </span>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    logs
-                                  </span>
-                                </a>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                      </ul>
-                    </EuiListGroup>
-                  </EuiNavDrawerGroup>
-                </div>
-              </div>
-            </EuiFlexItem>
-            <EuiNavDrawerFlyout
-              id="navDrawerFlyout"
-              isCollapsed={true}
-              onClose={[Function]}
-              wrapText={true}
-            >
-              <div
-                aria-labelledby="navDrawerFlyoutTitle"
-                className="euiNavDrawerFlyout euiNavDrawerFlyout-isCollapsed"
-                id="navDrawerFlyout"
-                onKeyDown={[Function]}
-              >
-                <EuiTitle
-                  className="euiNavDrawerFlyout__title"
-                  size="xxs"
-                  tabIndex="-1"
-                >
-                  <div
-                    className="euiTitle euiTitle--xxsmall euiNavDrawerFlyout__title"
-                    id="navDrawerFlyoutTitle"
-                    tabIndex="-1"
-                  />
-                </EuiTitle>
-              </div>
-            </EuiNavDrawerFlyout>
-          </div>
-        </EuiFlexGroup>
-      </nav>
-    </EuiOutsideClickDetector>
-  </EuiNavDrawer>
-</ForwardRef(navDrawerRenderer)>
-`;
-
-exports[`NavDrawer Advanced setting set to individual renders individual items 1`] = `
-<ForwardRef(navDrawerRenderer)
-  basePath={
-    Object {
-      "get": [Function],
-      "prepend": [Function],
-      "remove": [Function],
-    }
-  }
-  chromeNavLinks={
-    Array [
-      Object {
-        "baseUrl": "http://localhost:5601/app/discover",
-        "category": Object {
-          "label": "Analyze",
-          "order": 1000,
-        },
-        "id": "discover",
-        "legacy": true,
-        "order": 100,
-        "title": "discover",
-      },
-      Object {
-        "baseUrl": "http://localhost:5601/app/siem",
-        "category": Object {
-          "euiIconType": "logoSecurity",
-          "label": "Security",
-          "order": 3000,
-        },
-        "id": "siem",
-        "legacy": true,
-        "order": 500,
-        "title": "siem",
-      },
-      Object {
-        "baseUrl": "http://localhost:5601/app/metrics",
-        "category": Object {
-          "euiIconType": "logoObservability",
-          "label": "Observability",
-          "order": 2000,
-        },
-        "id": "metrics",
-        "legacy": true,
-        "order": 600,
-        "title": "metrics",
-      },
-      Object {
-        "baseUrl": "http://localhost:5601/app/monitoring",
-        "category": Object {
-          "euiIconType": "managementApp",
-          "label": "Management",
-        },
-        "id": "monitoring",
-        "legacy": true,
-        "order": 800,
-        "title": "monitoring",
-      },
-      Object {
-        "baseUrl": "http://localhost:5601/app/visualize",
-        "category": Object {
-          "label": "Analyze",
-          "order": 1000,
-        },
-        "id": "visualize",
-        "legacy": true,
-        "order": 200,
-        "title": "visualize",
-      },
-      Object {
-        "baseUrl": "http://localhost:5601/app/dashboard",
-        "category": Object {
-          "label": "Analyze",
-          "order": 1000,
-        },
-        "id": "dashboard",
-        "legacy": true,
-        "order": 300,
-        "title": "dashboard",
-      },
-      Object {
-        "baseUrl": "http://localhost:5601/app/canvas",
-        "category": Object {
-          "label": "customCategory",
-        },
-        "id": "canvas",
-        "legacy": true,
-        "order": 400,
-        "title": "canvas",
-      },
-      Object {
-        "baseUrl": "http://localhost:5601/app/logs",
-        "category": Object {
-          "euiIconType": "logoObservability",
-          "label": "Observability",
-          "order": 2000,
-        },
-        "id": "logs",
-        "legacy": true,
-        "order": 700,
-        "title": "logs",
-      },
-    ]
-  }
-  navLinks={
-    Array [
-      Object {
-        "category": Object {
-          "label": "Analyze",
-          "order": 1000,
-        },
-        "data-test-subj": "navDrawerAppsMenuLink",
-        "href": "http://localhost:5601/app/discover",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "discover",
-        "label": "discover",
-        "onClick": [Function],
-        "order": 100,
-      },
-      Object {
-        "category": Object {
-          "euiIconType": "logoSecurity",
-          "label": "Security",
-          "order": 3000,
-        },
-        "data-test-subj": "navDrawerAppsMenuLink",
-        "href": "http://localhost:5601/app/siem",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "siem",
-        "label": "siem",
-        "onClick": [Function],
-        "order": 500,
-      },
-      Object {
-        "category": Object {
-          "euiIconType": "logoObservability",
-          "label": "Observability",
-          "order": 2000,
-        },
-        "data-test-subj": "navDrawerAppsMenuLink",
-        "href": "http://localhost:5601/app/metrics",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "metrics",
-        "label": "metrics",
-        "onClick": [Function],
-        "order": 600,
-      },
-      Object {
-        "category": Object {
-          "euiIconType": "managementApp",
-          "label": "Management",
-        },
-        "data-test-subj": "navDrawerAppsMenuLink",
-        "href": "http://localhost:5601/app/monitoring",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "monitoring",
-        "label": "monitoring",
-        "onClick": [Function],
-        "order": 800,
-      },
-      Object {
-        "category": Object {
-          "label": "Analyze",
-          "order": 1000,
-        },
-        "data-test-subj": "navDrawerAppsMenuLink",
-        "href": "http://localhost:5601/app/visualize",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "visualize",
-        "label": "visualize",
-        "onClick": [Function],
-        "order": 200,
-      },
-      Object {
-        "category": Object {
-          "label": "Analyze",
-          "order": 1000,
-        },
-        "data-test-subj": "navDrawerAppsMenuLink",
-        "href": "http://localhost:5601/app/dashboard",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "dashboard",
-        "label": "dashboard",
-        "onClick": [Function],
-        "order": 300,
-      },
-      Object {
-        "category": Object {
-          "label": "customCategory",
-        },
-        "data-test-subj": "navDrawerAppsMenuLink",
-        "href": "http://localhost:5601/app/canvas",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "canvas",
-        "label": "canvas",
-        "onClick": [Function],
-        "order": 400,
-      },
-      Object {
-        "category": Object {
-          "euiIconType": "logoObservability",
-          "label": "Observability",
-          "order": 2000,
-        },
-        "data-test-subj": "navDrawerAppsMenuLink",
-        "href": "http://localhost:5601/app/logs",
-        "icon": undefined,
-        "iconType": undefined,
-        "isActive": false,
-        "isDisabled": undefined,
-        "key": "logs",
-        "label": "logs",
-        "onClick": [Function],
-        "order": 700,
-      },
-    ]
-  }
-  navSetting="individual"
-  recentlyAccessedItems={Array []}
->
-  <EuiNavDrawer
-    aria-label="Primary"
-    data-test-subj="navDrawer"
-    showExpandButton={true}
-    showToolTips={true}
-  >
-    <EuiOutsideClickDetector
-      isDisabled={true}
-      onOutsideClick={[Function]}
-    >
-      <nav
-        aria-label="Primary"
-        className="euiNavDrawer euiNavDrawer-isCollapsed euiNavDrawer-flyoutIsCollapsed"
-        data-test-subj="navDrawer"
-        onMouseDown={[Function]}
-        onMouseUp={[Function]}
-        onTouchEnd={[Function]}
-        onTouchStart={[Function]}
-      >
-        <EuiFlexGroup
-          gutterSize="none"
-        >
-          <div
-            className="euiFlexGroup euiFlexGroup--directionRow euiFlexGroup--responsive"
-          >
-            <EuiFlexItem
-              grow={false}
-            >
-              <div
-                className="euiFlexItem euiFlexItem--flexGrowZero"
-              >
-                <div
-                  className="euiNavDrawerMenu euiNavDrawerMenu-hasFooter"
-                  id="navDrawerMenu"
-                  onClick={[Function]}
-                >
-                  <EuiListGroup
-                    className="euiNavDrawer__expandButton"
-                    flush={true}
-                  >
-                    <ul
-                      className="euiListGroup euiListGroup-flush euiListGroup-maxWidthDefault euiNavDrawer__expandButton"
-                    >
-                      <EuiI18n
-                        defaults={
-                          Array [
-                            "Collapse",
-                            "Expand",
-                            "Dock navigation",
-                            "Navigation is docked",
-                            "Navigation is undocked",
-                          ]
-                        }
-                        tokens={
-                          Array [
-                            "euiNavDrawer.sideNavCollapse",
-                            "euiNavDrawer.sideNavExpand",
-                            "euiNavDrawer.sideNavLockAriaLabel",
-                            "euiNavDrawer.sideNavLockExpanded",
-                            "euiNavDrawer.sideNavLockCollapsed",
-                          ]
-                        }
-                      >
-                        <EuiListGroupItem
-                          buttonRef={[Function]}
-                          className="navDrawerExpandButton-isCollapsed"
-                          data-test-subj="navDrawerExpandButton-isCollapsed"
-                          extraAction={
-                            Object {
-                              "aria-label": "Dock navigation",
-                              "aria-pressed": false,
-                              "className": "euiNavDrawer__expandButtonLockAction",
-                              "color": "text",
-                              "iconSize": "s",
-                              "iconType": "lockOpen",
-                              "onClick": [Function],
-                              "title": "Navigation is undocked",
-                            }
-                          }
-                          iconType="menuRight"
-                          label="Expand"
-                          onClick={[Function]}
-                          showToolTip={true}
-                          size="s"
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiListGroupItem-hasExtraAction navDrawerExpandButton-isCollapsed"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="Expand"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <button
-                                  className="euiListGroupItem__button"
-                                  data-test-subj="navDrawerExpandButton-isCollapsed"
-                                  disabled={false}
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  type="button"
-                                >
-                                  <EuiIcon
-                                    className="euiListGroupItem__icon"
-                                    type="menuRight"
-                                  >
-                                    <EuiIconEmpty
-                                      aria-hidden={true}
-                                      className="euiIcon euiIcon--medium euiIcon-isLoading euiListGroupItem__icon"
-                                      focusable="false"
-                                      role="img"
-                                      style={null}
-                                    >
-                                      <svg
-                                        aria-hidden={true}
-                                        className="euiIcon euiIcon--medium euiIcon-isLoading euiListGroupItem__icon"
-                                        focusable="false"
-                                        height={16}
-                                        role="img"
-                                        style={null}
-                                        viewBox="0 0 16 16"
-                                        width={16}
-                                        xmlns="http://www.w3.org/2000/svg"
-                                      />
-                                    </EuiIconEmpty>
-                                  </EuiIcon>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    Expand
-                                  </span>
-                                </button>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                      </EuiI18n>
-                    </ul>
-                  </EuiListGroup>
-                  <EuiNavDrawerGroup
-                    aria-label="Recently viewed links, navigation"
-                    flyoutMenuButtonClick={[Function]}
-                    key=".$.0"
-                    listItems={
-                      Array [
-                        Object {
-                          "flyoutMenu": Object {
-                            "listItems": Array [],
-                            "title": "Recent items",
-                          },
-                          "iconType": "recentlyViewedApp",
-                          "isDisabled": true,
-                          "label": "Recently viewed",
-                        },
-                      ]
-                    }
-                    showToolTips={true}
-                  >
-                    <EuiListGroup
-                      aria-label="Recently viewed links, navigation"
-                      className="euiNavDrawerGroup"
-                      listItems={
-                        Array [
-                          Object {
-                            "aria-expanded": false,
-                            "aria-label": "Recently viewed",
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "Recently viewed",
-                            "iconType": "recentlyViewedApp",
-                            "isDisabled": true,
-                            "label": "Recently viewed",
-                            "onClick": [Function],
-                            "size": "s",
-                          },
-                        ]
-                      }
-                      showToolTips={true}
-                    >
-                      <ul
-                        aria-label="Recently viewed links, navigation"
-                        className="euiListGroup euiListGroup-maxWidthDefault euiNavDrawerGroup"
-                      >
-                        <EuiListGroupItem
-                          aria-expanded={false}
-                          aria-label="Recently viewed"
-                          className="euiNavDrawerGroup__item"
-                          data-name="Recently viewed"
-                          iconType="recentlyViewedApp"
-                          isDisabled={true}
-                          key="title-0"
-                          label="Recently viewed"
-                          onClick={[Function]}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isDisabled euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="Recently viewed"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <button
-                                  aria-expanded={false}
-                                  aria-label="Recently viewed"
-                                  className="euiListGroupItem__button"
-                                  data-name="Recently viewed"
-                                  disabled={true}
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  type="button"
-                                >
-                                  <EuiIcon
-                                    className="euiListGroupItem__icon"
-                                    type="recentlyViewedApp"
-                                  >
-                                    <EuiIconEmpty
-                                      aria-hidden={true}
-                                      className="euiIcon euiIcon--medium euiIcon--app euiIcon-isLoading euiListGroupItem__icon"
-                                      focusable="false"
-                                      role="img"
-                                      style={null}
-                                    >
-                                      <svg
-                                        aria-hidden={true}
-                                        className="euiIcon euiIcon--medium euiIcon--app euiIcon-isLoading euiListGroupItem__icon"
-                                        focusable="false"
-                                        height={16}
-                                        role="img"
-                                        style={null}
-                                        viewBox="0 0 16 16"
-                                        width={16}
-                                        xmlns="http://www.w3.org/2000/svg"
-                                      />
-                                    </EuiIconEmpty>
-                                  </EuiIcon>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    Recently viewed
-                                  </span>
-                                </button>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                      </ul>
-                    </EuiListGroup>
-                  </EuiNavDrawerGroup>
-                  <EuiHorizontalRule
-                    key=".$.1"
-                    margin="none"
-                  >
-                    <hr
-                      className="euiHorizontalRule euiHorizontalRule--full"
-                    />
-                  </EuiHorizontalRule>
-                  <EuiNavDrawerGroup
-                    aria-label="Primary navigation links"
-                    data-test-subj="navDrawerAppsMenu"
-                    flyoutMenuButtonClick={[Function]}
-                    key=".$.2"
-                    listItems={
-                      Array [
-                        Object {
-                          "category": Object {
-                            "label": "Analyze",
-                            "order": 1000,
-                          },
-                          "data-test-subj": "navDrawerAppsMenuLink",
-                          "href": "http://localhost:5601/app/discover",
-                          "icon": undefined,
-                          "iconType": undefined,
-                          "isActive": false,
-                          "isDisabled": undefined,
-                          "key": "discover",
-                          "label": "discover",
-                          "onClick": [Function],
-                          "order": 100,
-                        },
-                        Object {
-                          "category": Object {
-                            "euiIconType": "logoSecurity",
-                            "label": "Security",
-                            "order": 3000,
-                          },
-                          "data-test-subj": "navDrawerAppsMenuLink",
-                          "href": "http://localhost:5601/app/siem",
-                          "icon": undefined,
-                          "iconType": undefined,
-                          "isActive": false,
-                          "isDisabled": undefined,
-                          "key": "siem",
-                          "label": "siem",
-                          "onClick": [Function],
-                          "order": 500,
-                        },
-                        Object {
-                          "category": Object {
-                            "euiIconType": "logoObservability",
-                            "label": "Observability",
-                            "order": 2000,
-                          },
-                          "data-test-subj": "navDrawerAppsMenuLink",
-                          "href": "http://localhost:5601/app/metrics",
-                          "icon": undefined,
-                          "iconType": undefined,
-                          "isActive": false,
-                          "isDisabled": undefined,
-                          "key": "metrics",
-                          "label": "metrics",
-                          "onClick": [Function],
-                          "order": 600,
-                        },
-                        Object {
-                          "category": Object {
-                            "euiIconType": "managementApp",
-                            "label": "Management",
-                          },
-                          "data-test-subj": "navDrawerAppsMenuLink",
-                          "href": "http://localhost:5601/app/monitoring",
-                          "icon": undefined,
-                          "iconType": undefined,
-                          "isActive": false,
-                          "isDisabled": undefined,
-                          "key": "monitoring",
-                          "label": "monitoring",
-                          "onClick": [Function],
-                          "order": 800,
-                        },
-                        Object {
-                          "category": Object {
-                            "label": "Analyze",
-                            "order": 1000,
-                          },
-                          "data-test-subj": "navDrawerAppsMenuLink",
-                          "href": "http://localhost:5601/app/visualize",
-                          "icon": undefined,
-                          "iconType": undefined,
-                          "isActive": false,
-                          "isDisabled": undefined,
-                          "key": "visualize",
-                          "label": "visualize",
-                          "onClick": [Function],
-                          "order": 200,
-                        },
-                        Object {
-                          "category": Object {
-                            "label": "Analyze",
-                            "order": 1000,
-                          },
-                          "data-test-subj": "navDrawerAppsMenuLink",
-                          "href": "http://localhost:5601/app/dashboard",
-                          "icon": undefined,
-                          "iconType": undefined,
-                          "isActive": false,
-                          "isDisabled": undefined,
-                          "key": "dashboard",
-                          "label": "dashboard",
-                          "onClick": [Function],
-                          "order": 300,
-                        },
-                        Object {
-                          "category": Object {
-                            "label": "customCategory",
-                          },
-                          "data-test-subj": "navDrawerAppsMenuLink",
-                          "href": "http://localhost:5601/app/canvas",
-                          "icon": undefined,
-                          "iconType": undefined,
-                          "isActive": false,
-                          "isDisabled": undefined,
-                          "key": "canvas",
-                          "label": "canvas",
-                          "onClick": [Function],
-                          "order": 400,
-                        },
-                        Object {
-                          "category": Object {
-                            "euiIconType": "logoObservability",
-                            "label": "Observability",
-                            "order": 2000,
-                          },
-                          "data-test-subj": "navDrawerAppsMenuLink",
-                          "href": "http://localhost:5601/app/logs",
-                          "icon": undefined,
-                          "iconType": undefined,
-                          "isActive": false,
-                          "isDisabled": undefined,
-                          "key": "logs",
-                          "label": "logs",
-                          "onClick": [Function],
-                          "order": 700,
-                        },
-                      ]
-                    }
-                    showToolTips={true}
-                  >
-                    <EuiListGroup
-                      aria-label="Primary navigation links"
-                      className="euiNavDrawerGroup"
-                      data-test-subj="navDrawerAppsMenu"
-                      listItems={
-                        Array [
-                          Object {
-                            "aria-label": "discover",
-                            "category": Object {
-                              "label": "Analyze",
-                              "order": 1000,
-                            },
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "discover",
-                            "data-test-subj": "navDrawerAppsMenuLink",
-                            "href": "http://localhost:5601/app/discover",
-                            "icon": <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              d
-                            </span>,
-                            "iconType": undefined,
-                            "isActive": false,
-                            "isDisabled": undefined,
-                            "key": "discover",
-                            "label": "discover",
-                            "onClick": [Function],
-                            "order": 100,
-                            "size": "s",
-                          },
-                          Object {
-                            "aria-label": "siem",
-                            "category": Object {
-                              "euiIconType": "logoSecurity",
-                              "label": "Security",
-                              "order": 3000,
-                            },
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "siem",
-                            "data-test-subj": "navDrawerAppsMenuLink",
-                            "href": "http://localhost:5601/app/siem",
-                            "icon": <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              s
-                            </span>,
-                            "iconType": undefined,
-                            "isActive": false,
-                            "isDisabled": undefined,
-                            "key": "siem",
-                            "label": "siem",
-                            "onClick": [Function],
-                            "order": 500,
-                            "size": "s",
-                          },
-                          Object {
-                            "aria-label": "metrics",
-                            "category": Object {
-                              "euiIconType": "logoObservability",
-                              "label": "Observability",
-                              "order": 2000,
-                            },
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "metrics",
-                            "data-test-subj": "navDrawerAppsMenuLink",
-                            "href": "http://localhost:5601/app/metrics",
-                            "icon": <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              m
-                            </span>,
-                            "iconType": undefined,
-                            "isActive": false,
-                            "isDisabled": undefined,
-                            "key": "metrics",
-                            "label": "metrics",
-                            "onClick": [Function],
-                            "order": 600,
-                            "size": "s",
-                          },
-                          Object {
-                            "aria-label": "monitoring",
-                            "category": Object {
-                              "euiIconType": "managementApp",
-                              "label": "Management",
-                            },
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "monitoring",
-                            "data-test-subj": "navDrawerAppsMenuLink",
-                            "href": "http://localhost:5601/app/monitoring",
-                            "icon": <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              m
-                            </span>,
-                            "iconType": undefined,
-                            "isActive": false,
-                            "isDisabled": undefined,
-                            "key": "monitoring",
-                            "label": "monitoring",
-                            "onClick": [Function],
-                            "order": 800,
-                            "size": "s",
-                          },
-                          Object {
-                            "aria-label": "visualize",
-                            "category": Object {
-                              "label": "Analyze",
-                              "order": 1000,
-                            },
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "visualize",
-                            "data-test-subj": "navDrawerAppsMenuLink",
-                            "href": "http://localhost:5601/app/visualize",
-                            "icon": <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              v
-                            </span>,
-                            "iconType": undefined,
-                            "isActive": false,
-                            "isDisabled": undefined,
-                            "key": "visualize",
-                            "label": "visualize",
-                            "onClick": [Function],
-                            "order": 200,
-                            "size": "s",
-                          },
-                          Object {
-                            "aria-label": "dashboard",
-                            "category": Object {
-                              "label": "Analyze",
-                              "order": 1000,
-                            },
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "dashboard",
-                            "data-test-subj": "navDrawerAppsMenuLink",
-                            "href": "http://localhost:5601/app/dashboard",
-                            "icon": <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              d
-                            </span>,
-                            "iconType": undefined,
-                            "isActive": false,
-                            "isDisabled": undefined,
-                            "key": "dashboard",
-                            "label": "dashboard",
-                            "onClick": [Function],
-                            "order": 300,
-                            "size": "s",
-                          },
-                          Object {
-                            "aria-label": "canvas",
-                            "category": Object {
-                              "label": "customCategory",
-                            },
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "canvas",
-                            "data-test-subj": "navDrawerAppsMenuLink",
-                            "href": "http://localhost:5601/app/canvas",
-                            "icon": <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              c
-                            </span>,
-                            "iconType": undefined,
-                            "isActive": false,
-                            "isDisabled": undefined,
-                            "key": "canvas",
-                            "label": "canvas",
-                            "onClick": [Function],
-                            "order": 400,
-                            "size": "s",
-                          },
-                          Object {
-                            "aria-label": "logs",
-                            "category": Object {
-                              "euiIconType": "logoObservability",
-                              "label": "Observability",
-                              "order": 2000,
-                            },
-                            "className": "euiNavDrawerGroup__item",
-                            "data-name": "logs",
-                            "data-test-subj": "navDrawerAppsMenuLink",
-                            "href": "http://localhost:5601/app/logs",
-                            "icon": <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              l
-                            </span>,
-                            "iconType": undefined,
-                            "isActive": false,
-                            "isDisabled": undefined,
-                            "key": "logs",
-                            "label": "logs",
-                            "onClick": [Function],
-                            "order": 700,
-                            "size": "s",
-                          },
-                        ]
-                      }
-                      showToolTips={true}
-                    >
-                      <ul
-                        aria-label="Primary navigation links"
-                        className="euiListGroup euiListGroup-maxWidthDefault euiNavDrawerGroup"
-                        data-test-subj="navDrawerAppsMenu"
-                      >
-                        <EuiListGroupItem
-                          aria-label="discover"
-                          category={
-                            Object {
-                              "label": "Analyze",
-                              "order": 1000,
-                            }
-                          }
-                          className="euiNavDrawerGroup__item"
-                          data-name="discover"
-                          data-test-subj="navDrawerAppsMenuLink"
-                          href="http://localhost:5601/app/discover"
-                          icon={
-                            <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              d
-                            </span>
-                          }
-                          isActive={false}
-                          key="discover"
-                          label="discover"
-                          onClick={[Function]}
-                          order={100}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="discover"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <a
-                                  aria-label="discover"
-                                  category={
-                                    Object {
-                                      "label": "Analyze",
-                                      "order": 1000,
-                                    }
-                                  }
-                                  className="euiListGroupItem__button"
-                                  data-name="discover"
-                                  data-test-subj="navDrawerAppsMenuLink"
-                                  href="http://localhost:5601/app/discover"
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  order={100}
-                                >
-                                  <span
-                                    className="euiListGroupItem__icon euiNavDrawerGroup__itemDefaultIcon"
-                                  >
-                                    d
-                                  </span>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    discover
-                                  </span>
-                                </a>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                        <EuiListGroupItem
-                          aria-label="siem"
-                          category={
-                            Object {
-                              "euiIconType": "logoSecurity",
-                              "label": "Security",
-                              "order": 3000,
-                            }
-                          }
-                          className="euiNavDrawerGroup__item"
-                          data-name="siem"
-                          data-test-subj="navDrawerAppsMenuLink"
-                          href="http://localhost:5601/app/siem"
-                          icon={
-                            <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              s
-                            </span>
-                          }
-                          isActive={false}
-                          key="siem"
-                          label="siem"
-                          onClick={[Function]}
-                          order={500}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="siem"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <a
-                                  aria-label="siem"
-                                  category={
-                                    Object {
-                                      "euiIconType": "logoSecurity",
-                                      "label": "Security",
-                                      "order": 3000,
-                                    }
-                                  }
-                                  className="euiListGroupItem__button"
-                                  data-name="siem"
-                                  data-test-subj="navDrawerAppsMenuLink"
-                                  href="http://localhost:5601/app/siem"
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  order={500}
-                                >
-                                  <span
-                                    className="euiListGroupItem__icon euiNavDrawerGroup__itemDefaultIcon"
-                                  >
-                                    s
-                                  </span>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    siem
-                                  </span>
-                                </a>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                        <EuiListGroupItem
-                          aria-label="metrics"
-                          category={
-                            Object {
-                              "euiIconType": "logoObservability",
-                              "label": "Observability",
-                              "order": 2000,
-                            }
-                          }
-                          className="euiNavDrawerGroup__item"
-                          data-name="metrics"
-                          data-test-subj="navDrawerAppsMenuLink"
-                          href="http://localhost:5601/app/metrics"
-                          icon={
-                            <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              m
-                            </span>
-                          }
-                          isActive={false}
-                          key="metrics"
-                          label="metrics"
-                          onClick={[Function]}
-                          order={600}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="metrics"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <a
-                                  aria-label="metrics"
-                                  category={
-                                    Object {
-                                      "euiIconType": "logoObservability",
-                                      "label": "Observability",
-                                      "order": 2000,
-                                    }
-                                  }
-                                  className="euiListGroupItem__button"
-                                  data-name="metrics"
-                                  data-test-subj="navDrawerAppsMenuLink"
-                                  href="http://localhost:5601/app/metrics"
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  order={600}
-                                >
-                                  <span
-                                    className="euiListGroupItem__icon euiNavDrawerGroup__itemDefaultIcon"
-                                  >
-                                    m
-                                  </span>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    metrics
-                                  </span>
-                                </a>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                        <EuiListGroupItem
-                          aria-label="monitoring"
-                          category={
-                            Object {
-                              "euiIconType": "managementApp",
-                              "label": "Management",
-                            }
-                          }
-                          className="euiNavDrawerGroup__item"
-                          data-name="monitoring"
-                          data-test-subj="navDrawerAppsMenuLink"
-                          href="http://localhost:5601/app/monitoring"
-                          icon={
-                            <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              m
-                            </span>
-                          }
-                          isActive={false}
-                          key="monitoring"
-                          label="monitoring"
-                          onClick={[Function]}
-                          order={800}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="monitoring"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <a
-                                  aria-label="monitoring"
-                                  category={
-                                    Object {
-                                      "euiIconType": "managementApp",
-                                      "label": "Management",
-                                    }
-                                  }
-                                  className="euiListGroupItem__button"
-                                  data-name="monitoring"
-                                  data-test-subj="navDrawerAppsMenuLink"
-                                  href="http://localhost:5601/app/monitoring"
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  order={800}
-                                >
-                                  <span
-                                    className="euiListGroupItem__icon euiNavDrawerGroup__itemDefaultIcon"
-                                  >
-                                    m
-                                  </span>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    monitoring
-                                  </span>
-                                </a>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                        <EuiListGroupItem
-                          aria-label="visualize"
-                          category={
-                            Object {
-                              "label": "Analyze",
-                              "order": 1000,
-                            }
-                          }
-                          className="euiNavDrawerGroup__item"
-                          data-name="visualize"
-                          data-test-subj="navDrawerAppsMenuLink"
-                          href="http://localhost:5601/app/visualize"
-                          icon={
-                            <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              v
-                            </span>
-                          }
-                          isActive={false}
-                          key="visualize"
-                          label="visualize"
-                          onClick={[Function]}
-                          order={200}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="visualize"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <a
-                                  aria-label="visualize"
-                                  category={
-                                    Object {
-                                      "label": "Analyze",
-                                      "order": 1000,
-                                    }
-                                  }
-                                  className="euiListGroupItem__button"
-                                  data-name="visualize"
-                                  data-test-subj="navDrawerAppsMenuLink"
-                                  href="http://localhost:5601/app/visualize"
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  order={200}
-                                >
-                                  <span
-                                    className="euiListGroupItem__icon euiNavDrawerGroup__itemDefaultIcon"
-                                  >
-                                    v
-                                  </span>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    visualize
-                                  </span>
-                                </a>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                        <EuiListGroupItem
-                          aria-label="dashboard"
-                          category={
-                            Object {
-                              "label": "Analyze",
-                              "order": 1000,
-                            }
-                          }
-                          className="euiNavDrawerGroup__item"
-                          data-name="dashboard"
-                          data-test-subj="navDrawerAppsMenuLink"
-                          href="http://localhost:5601/app/dashboard"
-                          icon={
-                            <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              d
-                            </span>
-                          }
-                          isActive={false}
-                          key="dashboard"
-                          label="dashboard"
-                          onClick={[Function]}
-                          order={300}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="dashboard"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <a
-                                  aria-label="dashboard"
-                                  category={
-                                    Object {
-                                      "label": "Analyze",
-                                      "order": 1000,
-                                    }
-                                  }
-                                  className="euiListGroupItem__button"
-                                  data-name="dashboard"
-                                  data-test-subj="navDrawerAppsMenuLink"
-                                  href="http://localhost:5601/app/dashboard"
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  order={300}
-                                >
-                                  <span
-                                    className="euiListGroupItem__icon euiNavDrawerGroup__itemDefaultIcon"
-                                  >
-                                    d
-                                  </span>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    dashboard
-                                  </span>
-                                </a>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                        <EuiListGroupItem
-                          aria-label="canvas"
-                          category={
-                            Object {
-                              "label": "customCategory",
-                            }
-                          }
-                          className="euiNavDrawerGroup__item"
-                          data-name="canvas"
-                          data-test-subj="navDrawerAppsMenuLink"
-                          href="http://localhost:5601/app/canvas"
-                          icon={
-                            <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              c
-                            </span>
-                          }
-                          isActive={false}
-                          key="canvas"
-                          label="canvas"
-                          onClick={[Function]}
-                          order={400}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="canvas"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <a
-                                  aria-label="canvas"
-                                  category={
-                                    Object {
-                                      "label": "customCategory",
-                                    }
-                                  }
-                                  className="euiListGroupItem__button"
-                                  data-name="canvas"
-                                  data-test-subj="navDrawerAppsMenuLink"
-                                  href="http://localhost:5601/app/canvas"
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  order={400}
-                                >
-                                  <span
-                                    className="euiListGroupItem__icon euiNavDrawerGroup__itemDefaultIcon"
-                                  >
-                                    c
-                                  </span>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    canvas
-                                  </span>
-                                </a>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                        <EuiListGroupItem
-                          aria-label="logs"
-                          category={
-                            Object {
-                              "euiIconType": "logoObservability",
-                              "label": "Observability",
-                              "order": 2000,
-                            }
-                          }
-                          className="euiNavDrawerGroup__item"
-                          data-name="logs"
-                          data-test-subj="navDrawerAppsMenuLink"
-                          href="http://localhost:5601/app/logs"
-                          icon={
-                            <span
-                              className="euiNavDrawerGroup__itemDefaultIcon"
-                            >
-                              l
-                            </span>
-                          }
-                          isActive={false}
-                          key="logs"
-                          label="logs"
-                          onClick={[Function]}
-                          order={700}
-                          showToolTip={true}
-                          size="s"
-                          wrapText={false}
-                        >
-                          <li
-                            className="euiListGroupItem euiListGroupItem--small euiListGroupItem-isClickable euiNavDrawerGroup__item"
-                          >
-                            <EuiToolTip
-                              anchorClassName="euiListGroupItem__tooltip"
-                              content="logs"
-                              delay="long"
-                              position="right"
-                            >
-                              <span
-                                className="euiToolTipAnchor euiListGroupItem__tooltip"
-                                onMouseOut={[Function]}
-                                onMouseOver={[Function]}
-                              >
-                                <a
-                                  aria-label="logs"
-                                  category={
-                                    Object {
-                                      "euiIconType": "logoObservability",
-                                      "label": "Observability",
-                                      "order": 2000,
-                                    }
-                                  }
-                                  className="euiListGroupItem__button"
-                                  data-name="logs"
-                                  data-test-subj="navDrawerAppsMenuLink"
-                                  href="http://localhost:5601/app/logs"
-                                  onBlur={[Function]}
-                                  onClick={[Function]}
-                                  onFocus={[Function]}
-                                  order={700}
-                                >
-                                  <span
-                                    className="euiListGroupItem__icon euiNavDrawerGroup__itemDefaultIcon"
-                                  >
-                                    l
-                                  </span>
-                                  <span
-                                    className="euiListGroupItem__label"
-                                  >
-                                    logs
-                                  </span>
-                                </a>
-                              </span>
-                            </EuiToolTip>
-                          </li>
-                        </EuiListGroupItem>
-                      </ul>
-                    </EuiListGroup>
-                  </EuiNavDrawerGroup>
-                </div>
-              </div>
-            </EuiFlexItem>
-            <EuiNavDrawerFlyout
-              id="navDrawerFlyout"
-              isCollapsed={true}
-              onClose={[Function]}
-              wrapText={true}
-            >
-              <div
-                aria-labelledby="navDrawerFlyoutTitle"
-                className="euiNavDrawerFlyout euiNavDrawerFlyout-isCollapsed"
-                id="navDrawerFlyout"
-                onKeyDown={[Function]}
-              >
-                <EuiTitle
-                  className="euiNavDrawerFlyout__title"
-                  size="xxs"
-                  tabIndex="-1"
-                >
-                  <div
-                    className="euiTitle euiTitle--xxsmall euiNavDrawerFlyout__title"
-                    id="navDrawerFlyoutTitle"
-                    tabIndex="-1"
-                  />
-                </EuiTitle>
-              </div>
-            </EuiNavDrawerFlyout>
-          </div>
-        </EuiFlexGroup>
-      </nav>
-    </EuiOutsideClickDetector>
-  </EuiNavDrawer>
-</ForwardRef(navDrawerRenderer)>
-`;
diff --git a/src/core/public/chrome/ui/header/header.tsx b/src/core/public/chrome/ui/header/header.tsx
index c3cefd180b16f..c9a583f39b30c 100644
--- a/src/core/public/chrome/ui/header/header.tsx
+++ b/src/core/public/chrome/ui/header/header.tsx
@@ -42,7 +42,7 @@ import { InternalApplicationStart } from '../../../application/types';
 import { HttpStart } from '../../../http';
 import { ChromeHelpExtension } from '../../chrome_service';
 import { HeaderBadge } from './header_badge';
-import { NavSetting, OnIsLockedUpdate } from './';
+import { OnIsLockedUpdate } from './';
 import { HeaderBreadcrumbs } from './header_breadcrumbs';
 import { HeaderHelpMenu } from './header_help_menu';
 import { HeaderNavControls } from './header_nav_controls';
@@ -69,7 +69,6 @@ export interface HeaderProps {
   navControlsRight$: Rx.Observable<readonly ChromeNavControl[]>;
   basePath: HttpStart['basePath'];
   isLocked?: boolean;
-  navSetting$: Rx.Observable<NavSetting>;
   onIsLockedUpdate?: OnIsLockedUpdate;
 }
 
@@ -81,7 +80,6 @@ interface State {
   forceNavigation: boolean;
   navControlsLeft: readonly ChromeNavControl[];
   navControlsRight: readonly ChromeNavControl[];
-  navSetting: NavSetting;
   currentAppId: string | undefined;
 }
 
@@ -100,7 +98,6 @@ export class Header extends Component<HeaderProps, State> {
       forceNavigation: false,
       navControlsLeft: [],
       navControlsRight: [],
-      navSetting: 'grouped',
       currentAppId: '',
     };
   }
@@ -116,8 +113,7 @@ export class Header extends Component<HeaderProps, State> {
       Rx.combineLatest(
         this.props.navControlsLeft$,
         this.props.navControlsRight$,
-        this.props.application.currentAppId$,
-        this.props.navSetting$
+        this.props.application.currentAppId$
       )
     ).subscribe({
       next: ([
@@ -126,7 +122,7 @@ export class Header extends Component<HeaderProps, State> {
         forceNavigation,
         navLinks,
         recentlyAccessed,
-        [navControlsLeft, navControlsRight, currentAppId, navSetting],
+        [navControlsLeft, navControlsRight, currentAppId],
       ]) => {
         this.setState({
           appTitle,
@@ -136,7 +132,6 @@ export class Header extends Component<HeaderProps, State> {
           recentlyAccessed,
           navControlsLeft,
           navControlsRight,
-          navSetting,
           currentAppId,
         });
       },
@@ -225,7 +220,6 @@ export class Header extends Component<HeaderProps, State> {
           </EuiHeaderSection>
         </EuiHeader>
         <NavDrawer
-          navSetting={this.state.navSetting}
           isLocked={this.props.isLocked}
           onIsLockedUpdate={this.props.onIsLockedUpdate}
           navLinks={navLinks}
diff --git a/src/core/public/chrome/ui/header/index.ts b/src/core/public/chrome/ui/header/index.ts
index b396c94b3f2a3..4521f1f74b31b 100644
--- a/src/core/public/chrome/ui/header/index.ts
+++ b/src/core/public/chrome/ui/header/index.ts
@@ -26,5 +26,4 @@ export {
   ChromeHelpExtensionMenuDocumentationLink,
   ChromeHelpExtensionMenuGitHubLink,
 } from './header_help_menu';
-export type NavSetting = 'grouped' | 'individual';
 export type OnIsLockedUpdate = (isLocked: boolean) => void;
diff --git a/src/core/public/chrome/ui/header/nav_drawer.test.tsx b/src/core/public/chrome/ui/header/nav_drawer.test.tsx
deleted file mode 100644
index 7272935b93a52..0000000000000
--- a/src/core/public/chrome/ui/header/nav_drawer.test.tsx
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { cloneDeep } from 'lodash';
-import { mount } from 'enzyme';
-import React from 'react';
-import { NavSetting } from './';
-import { ChromeNavLink } from '../../../';
-import { AppCategory } from 'src/core/types';
-import { DEFAULT_APP_CATEGORIES } from '../../../../utils';
-import { NavDrawer } from './nav_drawer';
-import { euiNavLink } from './nav_link';
-
-const { analyze, management, observability, security } = DEFAULT_APP_CATEGORIES;
-const mockIBasePath = {
-  get: () => '/app',
-  prepend: () => '/app',
-  remove: () => '/app',
-};
-
-const getMockProps = (chromeNavLinks: ChromeNavLink[], navSetting: NavSetting = 'grouped') => ({
-  navSetting,
-  navLinks: chromeNavLinks.map(link =>
-    euiNavLink(link, true, undefined, mockIBasePath, () => Promise.resolve())
-  ),
-  chromeNavLinks,
-  recentlyAccessedItems: [],
-  basePath: mockIBasePath,
-});
-
-const makeLink = (id: string, order: number, category?: AppCategory) => ({
-  id,
-  category,
-  order,
-  title: id,
-  baseUrl: `http://localhost:5601/app/${id}`,
-  legacy: true,
-});
-
-const getMockChromeNavLink = () =>
-  cloneDeep([
-    makeLink('discover', 100, analyze),
-    makeLink('siem', 500, security),
-    makeLink('metrics', 600, observability),
-    makeLink('monitoring', 800, management),
-    makeLink('visualize', 200, analyze),
-    makeLink('dashboard', 300, analyze),
-    makeLink('canvas', 400, { label: 'customCategory' }),
-    makeLink('logs', 700, observability),
-  ]);
-
-describe('NavDrawer', () => {
-  describe('Advanced setting set to individual', () => {
-    it('renders individual items', () => {
-      const component = mount(
-        <NavDrawer {...getMockProps(getMockChromeNavLink(), 'individual')} />
-      );
-      expect(component).toMatchSnapshot();
-    });
-  });
-  describe('Advanced setting set to grouped', () => {
-    it('renders individual items if there are less than 7', () => {
-      const links = getMockChromeNavLink().slice(0, 5);
-      const component = mount(<NavDrawer {...getMockProps(links)} />);
-      expect(component).toMatchSnapshot();
-    });
-    it('renders individual items if there is only 1 category', () => {
-      // management doesn't count as a category
-      const navLinks = [
-        makeLink('discover', 100, analyze),
-        makeLink('siem', 500, analyze),
-        makeLink('metrics', 600, analyze),
-        makeLink('monitoring', 800, analyze),
-        makeLink('visualize', 200, analyze),
-        makeLink('dashboard', 300, management),
-        makeLink('canvas', 400, management),
-        makeLink('logs', 700, management),
-      ];
-      const component = mount(<NavDrawer {...getMockProps(navLinks)} />);
-      expect(component).toMatchSnapshot();
-    });
-    it('renders grouped items', () => {
-      const component = mount(<NavDrawer {...getMockProps(getMockChromeNavLink())} />);
-      expect(component).toMatchSnapshot();
-    });
-  });
-});
diff --git a/src/core/public/chrome/ui/header/nav_drawer.tsx b/src/core/public/chrome/ui/header/nav_drawer.tsx
index dbb68d5dd3901..c57faec1e428d 100644
--- a/src/core/public/chrome/ui/header/nav_drawer.tsx
+++ b/src/core/public/chrome/ui/header/nav_drawer.tsx
@@ -18,39 +18,16 @@
  */
 
 import React from 'react';
-import { groupBy, sortBy } from 'lodash';
 import { i18n } from '@kbn/i18n';
 // @ts-ignore
 import { EuiNavDrawer, EuiHorizontalRule, EuiNavDrawerGroup } from '@elastic/eui';
-import { NavSetting, OnIsLockedUpdate } from './';
+import { OnIsLockedUpdate } from './';
 import { ChromeNavLink, ChromeRecentlyAccessedHistoryItem } from '../../..';
-import { AppCategory } from '../../../../types';
 import { HttpStart } from '../../../http';
 import { NavLink } from './nav_link';
 import { RecentLinks } from './recent_links';
 
-function getAllCategories(allCategorizedLinks: Record<string, NavLink[]>) {
-  const allCategories = {} as Record<string, AppCategory | undefined>;
-
-  for (const [key, value] of Object.entries(allCategorizedLinks)) {
-    allCategories[key] = value[0].category;
-  }
-
-  return allCategories;
-}
-
-function getOrderedCategories(
-  mainCategories: Record<string, NavLink[]>,
-  categoryDictionary: ReturnType<typeof getAllCategories>
-) {
-  return sortBy(
-    Object.keys(mainCategories),
-    categoryName => categoryDictionary[categoryName]?.order
-  );
-}
-
 export interface Props {
-  navSetting: NavSetting;
   isLocked?: boolean;
   onIsLockedUpdate?: OnIsLockedUpdate;
   navLinks: NavLink[];
@@ -60,26 +37,9 @@ export interface Props {
 }
 
 function navDrawerRenderer(
-  {
-    navSetting,
-    isLocked,
-    onIsLockedUpdate,
-    navLinks,
-    chromeNavLinks,
-    recentlyAccessedItems,
-    basePath,
-  }: Props,
+  { isLocked, onIsLockedUpdate, navLinks, chromeNavLinks, recentlyAccessedItems, basePath }: Props,
   ref: React.Ref<HTMLElement>
 ) {
-  const disableGroupedNavSetting = navSetting === 'individual';
-  const groupedNavLinks = groupBy(navLinks, link => link?.category?.label);
-  const { undefined: unknowns, ...allCategorizedLinks } = groupedNavLinks;
-  const { Management: management, ...mainCategories } = allCategorizedLinks;
-  const categoryDictionary = getAllCategories(allCategorizedLinks);
-  const orderedCategories = getOrderedCategories(mainCategories, categoryDictionary);
-  const showUngroupedNav =
-    disableGroupedNavSetting || navLinks.length < 7 || Object.keys(mainCategories).length === 1;
-
   return (
     <EuiNavDrawer
       ref={ref}
@@ -96,73 +56,13 @@ function navDrawerRenderer(
         basePath,
       })}
       <EuiHorizontalRule margin="none" />
-      {showUngroupedNav ? (
-        <EuiNavDrawerGroup
-          data-test-subj="navDrawerAppsMenu"
-          listItems={navLinks}
-          aria-label={i18n.translate('core.ui.primaryNavList.screenReaderLabel', {
-            defaultMessage: 'Primary navigation links',
-          })}
-        />
-      ) : (
-        <>
-          <EuiNavDrawerGroup
-            data-test-subj="navDrawerAppsMenu"
-            aria-label={i18n.translate('core.ui.primaryNavList.screenReaderLabel', {
-              defaultMessage: 'Primary navigation links',
-            })}
-            listItems={[
-              ...orderedCategories.map(categoryName => {
-                const category = categoryDictionary[categoryName]!;
-                const links = mainCategories[categoryName];
-
-                if (links.length === 1) {
-                  return {
-                    ...links[0],
-                    label: category.label,
-                    iconType: category.euiIconType || links[0].iconType,
-                  };
-                }
-
-                return {
-                  'data-test-subj': 'navDrawerCategory',
-                  iconType: category.euiIconType,
-                  label: category.label,
-                  flyoutMenu: {
-                    title: category.label,
-                    listItems: sortBy(links, 'order').map(link => {
-                      link['data-test-subj'] = 'navDrawerFlyoutLink';
-                      return link;
-                    }),
-                  },
-                };
-              }),
-              ...sortBy(unknowns, 'order'),
-            ]}
-          />
-          <EuiHorizontalRule margin="none" />
-          <EuiNavDrawerGroup
-            data-test-subj="navDrawerManagementMenu"
-            aria-label={i18n.translate('core.ui.managementNavList.screenReaderLabel', {
-              defaultMessage: 'Management navigation links',
-            })}
-            listItems={[
-              {
-                label: categoryDictionary.Management!.label,
-                iconType: categoryDictionary.Management!.euiIconType,
-                'data-test-subj': 'navDrawerCategory',
-                flyoutMenu: {
-                  title: categoryDictionary.Management!.label,
-                  listItems: sortBy(management, 'order').map(link => {
-                    link['data-test-subj'] = 'navDrawerFlyoutLink';
-                    return link;
-                  }),
-                },
-              },
-            ]}
-          />
-        </>
-      )}
+      <EuiNavDrawerGroup
+        data-test-subj="navDrawerAppsMenu"
+        listItems={navLinks}
+        aria-label={i18n.translate('core.ui.primaryNavList.screenReaderLabel', {
+          defaultMessage: 'Primary navigation links',
+        })}
+      />
     </EuiNavDrawer>
   );
 }
diff --git a/src/legacy/core_plugins/kibana/index.js b/src/legacy/core_plugins/kibana/index.js
index 8e6bae0b588bc..221133a17d59a 100644
--- a/src/legacy/core_plugins/kibana/index.js
+++ b/src/legacy/core_plugins/kibana/index.js
@@ -115,7 +115,7 @@ export default function(kibana) {
         {
           id: 'kibana:stack_management',
           title: i18n.translate('kbn.managementTitle', {
-            defaultMessage: 'Stack Management',
+            defaultMessage: 'Management',
           }),
           order: 9003,
           url: `${kbnBaseUrl}#/management`,
diff --git a/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.test.js b/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.test.js
index b0d94711be7b6..db24cb3e3c1b7 100644
--- a/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.test.js
+++ b/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.test.js
@@ -131,7 +131,7 @@ describe('home', () => {
     test('should not render directory entry when showOnHomePage is false', async () => {
       const directoryEntry = {
         id: 'stack-management',
-        title: 'Stack Management',
+        title: 'Management',
         description: 'Your center console for managing the Elastic Stack.',
         icon: 'managementApp',
         path: 'management_landing_page',
diff --git a/src/legacy/core_plugins/kibana/public/management/index.js b/src/legacy/core_plugins/kibana/public/management/index.js
index 2cba9fab7be22..6a36391c56b5c 100644
--- a/src/legacy/core_plugins/kibana/public/management/index.js
+++ b/src/legacy/core_plugins/kibana/public/management/index.js
@@ -69,7 +69,7 @@ export function updateLandingPage(version) {
               <h1>
                 <FormattedMessage
                   id="kbn.management.landing.header"
-                  defaultMessage="Welcome to Stack Management {version}"
+                  defaultMessage="Kibana {version} management"
                   values={{ version }}
                 />
               </h1>
diff --git a/src/legacy/core_plugins/kibana/ui_setting_defaults.js b/src/legacy/core_plugins/kibana/ui_setting_defaults.js
index 744ede891b84a..f92694eabe58d 100644
--- a/src/legacy/core_plugins/kibana/ui_setting_defaults.js
+++ b/src/legacy/core_plugins/kibana/ui_setting_defaults.js
@@ -1174,24 +1174,5 @@ export function getUiSettingDefaults() {
       category: ['accessibility'],
       requiresPageReload: true,
     },
-    pageNavigation: {
-      name: i18n.translate('kbn.advancedSettings.pageNavigationName', {
-        defaultMessage: 'Side nav style',
-      }),
-      value: 'grouped',
-      description: i18n.translate('kbn.advancedSettings.pageNavigationDesc', {
-        defaultMessage: 'Change the style of navigation',
-      }),
-      type: 'select',
-      options: ['grouped', 'individual'],
-      optionLabels: {
-        grouped: i18n.translate('kbn.advancedSettings.pageNavigationGrouped', {
-          defaultMessage: 'Grouped',
-        }),
-        individual: i18n.translate('kbn.advancedSettings.pageNavigationIndividual', {
-          defaultMessage: 'Individual',
-        }),
-      },
-    },
   };
 }
diff --git a/src/legacy/ui/public/management/breadcrumbs.ts b/src/legacy/ui/public/management/breadcrumbs.ts
index 936e99caff565..e6156b6639ac4 100644
--- a/src/legacy/ui/public/management/breadcrumbs.ts
+++ b/src/legacy/ui/public/management/breadcrumbs.ts
@@ -21,7 +21,7 @@ import { i18n } from '@kbn/i18n';
 
 export const MANAGEMENT_BREADCRUMB = Object.freeze({
   text: i18n.translate('common.ui.stackManagement.breadcrumb', {
-    defaultMessage: 'Stack Management',
+    defaultMessage: 'Management',
   }),
   href: '#/management',
 });
diff --git a/src/plugins/management/public/components/management_sidebar_nav/management_sidebar_nav.tsx b/src/plugins/management/public/components/management_sidebar_nav/management_sidebar_nav.tsx
index 69ba813d2347e..01a98eb0ddb1f 100644
--- a/src/plugins/management/public/components/management_sidebar_nav/management_sidebar_nav.tsx
+++ b/src/plugins/management/public/components/management_sidebar_nav/management_sidebar_nav.tsx
@@ -168,8 +168,7 @@ export class ManagementSidebarNav extends React.Component<
         <EuiScreenReaderOnly>
           <h2 id={HEADER_ID}>
             {i18n.translate('management.nav.label', {
-              // todo
-              defaultMessage: 'Stack Management',
+              defaultMessage: 'Management',
             })}
           </h2>
         </EuiScreenReaderOnly>
diff --git a/src/plugins/management/public/legacy/sections_register.js b/src/plugins/management/public/legacy/sections_register.js
index ca35db56c340b..63d919377f89e 100644
--- a/src/plugins/management/public/legacy/sections_register.js
+++ b/src/plugins/management/public/legacy/sections_register.js
@@ -27,8 +27,7 @@ export class LegacyManagementAdapter {
       'management',
       {
         display: i18n.translate('management.displayName', {
-          // todo
-          defaultMessage: 'Stack Management',
+          defaultMessage: 'Management',
         }),
       },
       capabilities
@@ -36,7 +35,6 @@ export class LegacyManagementAdapter {
 
     this.main.register('data', {
       display: i18n.translate('management.connectDataDisplayName', {
-        // todo
         defaultMessage: 'Connect Data',
       }),
       order: 0,
diff --git a/src/plugins/management/public/management_app.tsx b/src/plugins/management/public/management_app.tsx
index 02b3ea306c23d..705d98eaaf2ff 100644
--- a/src/plugins/management/public/management_app.tsx
+++ b/src/plugins/management/public/management_app.tsx
@@ -64,8 +64,7 @@ export class ManagementApp {
           coreStart.chrome.setBreadcrumbs([
             {
               text: i18n.translate('management.breadcrumb', {
-                // todo
-                defaultMessage: 'Stack Management',
+                defaultMessage: 'Management',
               }),
               href: '#/management',
             },
diff --git a/src/plugins/management/public/plugin.ts b/src/plugins/management/public/plugin.ts
index df2398412dac2..1c9e1d5c89550 100644
--- a/src/plugins/management/public/plugin.ts
+++ b/src/plugins/management/public/plugin.ts
@@ -36,8 +36,8 @@ export class ManagementPlugin implements Plugin<ManagementSetup, ManagementStart
   ) {
     home.featureCatalogue.register({
       id: 'stack-management',
-      title: i18n.translate('management.stackManagement.managementLabel', {
-        defaultMessage: 'Stack Management',
+      title: i18n.translate('management.displayName', {
+        defaultMessage: 'Management',
       }),
       description: i18n.translate('management.stackManagement.managementDescription', {
         defaultMessage: 'Your center console for managing the Elastic Stack.',
diff --git a/test/functional/apps/dashboard/create_and_add_embeddables.js b/test/functional/apps/dashboard/create_and_add_embeddables.js
index 0b628100a98bd..5ebb9fdf6330f 100644
--- a/test/functional/apps/dashboard/create_and_add_embeddables.js
+++ b/test/functional/apps/dashboard/create_and_add_embeddables.js
@@ -34,7 +34,6 @@ export default function({ getService, getPageObjects }) {
       await esArchiver.load('dashboard/current/kibana');
       await kibanaServer.uiSettings.replace({
         defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c',
-        pageNavigation: 'individual',
       });
       await PageObjects.common.navigateToApp('dashboard');
       await PageObjects.dashboard.preserveCrossAppState();
diff --git a/test/functional/apps/management/_index_pattern_filter.js b/test/functional/apps/management/_index_pattern_filter.js
index e685c43e9ce98..a32024adb5ec7 100644
--- a/test/functional/apps/management/_index_pattern_filter.js
+++ b/test/functional/apps/management/_index_pattern_filter.js
@@ -27,7 +27,7 @@ export default function({ getService, getPageObjects }) {
   describe('index pattern filter', function describeIndexTests() {
     before(async function() {
       // delete .kibana index and then wait for Kibana to re-create it
-      await kibanaServer.uiSettings.replace({ pageNavigation: 'individual' });
+      await kibanaServer.uiSettings.replace({});
       await PageObjects.settings.navigateTo();
       await PageObjects.settings.clickKibanaIndexPatterns();
     });
diff --git a/test/functional/page_objects/header_page.js b/test/functional/page_objects/header_page.js
index 05edd64545a56..d0a237e8f42d0 100644
--- a/test/functional/page_objects/header_page.js
+++ b/test/functional/page_objects/header_page.js
@@ -60,7 +60,7 @@ export function HeaderPageProvider({ getService, getPageObjects }) {
     }
 
     async clickStackManagement() {
-      await appsMenu.clickLink('Stack Management');
+      await appsMenu.clickLink('Management');
       await this.awaitGlobalLoadingIndicatorHidden();
     }
 
diff --git a/test/functional/page_objects/settings_page.ts b/test/functional/page_objects/settings_page.ts
index e92780143f09a..d7e5064cf7280 100644
--- a/test/functional/page_objects/settings_page.ts
+++ b/test/functional/page_objects/settings_page.ts
@@ -19,7 +19,6 @@
 
 import { map as mapAsync } from 'bluebird';
 import expect from '@kbn/expect';
-import { NavSetting } from '../../../src/core/public/chrome/ui/header/';
 import { FtrProviderContext } from '../ftr_provider_context';
 
 export function SettingsPageProvider({ getService, getPageObjects }: FtrProviderContext) {
@@ -733,12 +732,6 @@ export function SettingsPageProvider({ getService, getPageObjects }: FtrProvider
       await checkBox.click();
       return await this.canSavedObjectsBeDeleted();
     }
-
-    async setNavType(navType: NavSetting) {
-      await PageObjects.common.navigateToApp('settings');
-      await this.clickKibanaSettings();
-      await this.setAdvancedSettingsSelect('pageNavigation', navType);
-    }
   }
 
   return new SettingsPage();
diff --git a/test/plugin_functional/test_suites/core_plugins/application_status.ts b/test/plugin_functional/test_suites/core_plugins/application_status.ts
index 0cc64277efe11..b6d13a5604011 100644
--- a/test/plugin_functional/test_suites/core_plugins/application_status.ts
+++ b/test/plugin_functional/test_suites/core_plugins/application_status.ts
@@ -28,7 +28,7 @@ import '../../plugins/core_app_status/public/types';
 
 // eslint-disable-next-line import/no-default-export
 export default function({ getService, getPageObjects }: PluginFunctionalProviderContext) {
-  const PageObjects = getPageObjects(['common', 'settings']);
+  const PageObjects = getPageObjects(['common']);
   const browser = getService('browser');
   const appsMenu = getService('appsMenu');
   const testSubjects = getService('testSubjects');
@@ -48,10 +48,6 @@ export default function({ getService, getPageObjects }: PluginFunctionalProvider
   };
 
   describe('application status management', () => {
-    before(async () => {
-      await PageObjects.settings.setNavType('individual');
-    });
-
     beforeEach(async () => {
       await PageObjects.common.navigateToApp('app_status_start');
     });
diff --git a/test/plugin_functional/test_suites/core_plugins/applications.ts b/test/plugin_functional/test_suites/core_plugins/applications.ts
index 6567837f65309..f50d460532556 100644
--- a/test/plugin_functional/test_suites/core_plugins/applications.ts
+++ b/test/plugin_functional/test_suites/core_plugins/applications.ts
@@ -122,7 +122,7 @@ export default function({ getService, getPageObjects }: PluginFunctionalProvider
     });
 
     it('can navigate from NP apps to legacy apps', async () => {
-      await appsMenu.clickLink('Stack Management');
+      await appsMenu.clickLink('Management');
       await loadingScreenShown();
       await testSubjects.existOrFail('managementNav');
     });
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index b171863f26c21..47bf2ae634048 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -1449,7 +1449,6 @@
     "kbn.management.indexPatterns.listBreadcrumb": "インデックスパターン",
     "kbn.management.indexPatternTable.createBtn": "インデックスパターンの作成",
     "kbn.management.indexPatternTable.title": "インデックスパターン",
-    "kbn.management.landing.header": "Kibana {version} 管理",
     "kbn.management.landing.subhead": "インデックス、インデックスパターン、保存されたオブジェクト、Kibana の設定、その他を管理します。",
     "kbn.management.landing.text": "すべてのツールの一覧は、左のメニューにあります。",
     "kbn.management.objects.confirmModalOptions.deleteButtonLabel": "削除",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 050e9bd40f58d..a94a602e48d9b 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -1449,7 +1449,6 @@
     "kbn.management.indexPatterns.listBreadcrumb": "索引模式",
     "kbn.management.indexPatternTable.createBtn": "创建索引模式",
     "kbn.management.indexPatternTable.title": "索引模式",
-    "kbn.management.landing.header": "Kibana {version} 管理",
     "kbn.management.landing.subhead": "管理您的索引、索引模式、已保存对象、Kibana 设置等等。",
     "kbn.management.landing.text": "在左侧菜单中可找到完整工具列表",
     "kbn.management.objects.confirmModalOptions.deleteButtonLabel": "删除",
diff --git a/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_security.ts b/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_security.ts
index 261a853387619..0d4c6b2c87666 100644
--- a/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_security.ts
+++ b/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_security.ts
@@ -55,7 +55,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
             expectSpaceSelector: false,
           }
         );
-        await kibanaServer.uiSettings.replace({ pageNavigation: 'individual' });
+        await kibanaServer.uiSettings.replace({});
         await PageObjects.settings.navigateTo();
       });
 
@@ -69,7 +69,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows management navlink', async () => {
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.eql(['Stack Management']);
+        expect(navLinks).to.eql(['Management']);
       });
 
       it(`allows settings to be changed`, async () => {
@@ -125,7 +125,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows Management navlink', async () => {
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.eql(['Stack Management']);
+        expect(navLinks).to.eql(['Management']);
       });
 
       it(`does not allow settings to be changed`, async () => {
@@ -176,7 +176,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows Management navlink', async () => {
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.eql(['Discover', 'Stack Management']);
+        expect(navLinks).to.eql(['Discover', 'Management']);
       });
 
       it(`does not allow navigation to advanced settings; redirects to management home`, async () => {
diff --git a/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_spaces.ts b/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_spaces.ts
index 53202089e8961..fc4f385df3694 100644
--- a/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_spaces.ts
+++ b/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_spaces.ts
@@ -41,9 +41,8 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         await PageObjects.common.navigateToApp('home', {
           basePath: '/s/custom_space',
         });
-        await PageObjects.settings.setNavType('individual');
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.contain('Stack Management');
+        expect(navLinks).to.contain('Management');
       });
 
       it(`allows settings to be changed`, async () => {
diff --git a/x-pack/test/functional/apps/apm/feature_controls/apm_security.ts b/x-pack/test/functional/apps/apm/feature_controls/apm_security.ts
index 7c9c9f9c8c155..e2d5efac4644c 100644
--- a/x-pack/test/functional/apps/apm/feature_controls/apm_security.ts
+++ b/x-pack/test/functional/apps/apm/feature_controls/apm_security.ts
@@ -60,7 +60,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows apm navlink', async () => {
         const navLinks = await appsMenu.readLinks();
-        expect(navLinks.map(link => link.text)).to.eql(['APM', 'Stack Management']);
+        expect(navLinks.map(link => link.text)).to.eql(['APM', 'Management']);
       });
 
       it('can navigate to APM app', async () => {
@@ -109,7 +109,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows apm navlink', async () => {
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.eql(['APM', 'Stack Management']);
+        expect(navLinks).to.eql(['APM', 'Management']);
       });
 
       it('can navigate to APM app', async () => {
diff --git a/x-pack/test/functional/apps/apm/feature_controls/apm_spaces.ts b/x-pack/test/functional/apps/apm/feature_controls/apm_spaces.ts
index 474240b201fac..1ac1784e0e05d 100644
--- a/x-pack/test/functional/apps/apm/feature_controls/apm_spaces.ts
+++ b/x-pack/test/functional/apps/apm/feature_controls/apm_spaces.ts
@@ -8,7 +8,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
 
 export default function({ getPageObjects, getService }: FtrProviderContext) {
   const spacesService = getService('spaces');
-  const PageObjects = getPageObjects(['common', 'error', 'timePicker', 'security', 'settings']);
+  const PageObjects = getPageObjects(['common', 'error', 'timePicker', 'security']);
   const testSubjects = getService('testSubjects');
   const appsMenu = getService('appsMenu');
 
@@ -30,7 +30,6 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         await PageObjects.common.navigateToApp('home', {
           basePath: '/s/custom_space',
         });
-        await PageObjects.settings.setNavType('individual');
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
         expect(navLinks).to.contain('APM');
       });
diff --git a/x-pack/test/functional/apps/canvas/feature_controls/canvas_security.ts b/x-pack/test/functional/apps/canvas/feature_controls/canvas_security.ts
index 71c10bd8248be..d0e37ec8e3f35 100644
--- a/x-pack/test/functional/apps/canvas/feature_controls/canvas_security.ts
+++ b/x-pack/test/functional/apps/canvas/feature_controls/canvas_security.ts
@@ -66,7 +66,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows canvas navlink', async () => {
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.eql(['Canvas', 'Stack Management']);
+        expect(navLinks).to.eql(['Canvas', 'Management']);
       });
 
       it(`landing page shows "Create new workpad" button`, async () => {
@@ -142,7 +142,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows canvas navlink', async () => {
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.eql(['Canvas', 'Stack Management']);
+        expect(navLinks).to.eql(['Canvas', 'Management']);
       });
 
       it(`landing page shows disabled "Create new workpad" button`, async () => {
diff --git a/x-pack/test/functional/apps/canvas/feature_controls/canvas_spaces.ts b/x-pack/test/functional/apps/canvas/feature_controls/canvas_spaces.ts
index 5395f125bbd22..28b572401892b 100644
--- a/x-pack/test/functional/apps/canvas/feature_controls/canvas_spaces.ts
+++ b/x-pack/test/functional/apps/canvas/feature_controls/canvas_spaces.ts
@@ -9,7 +9,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
 export default function({ getPageObjects, getService }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
   const spacesService = getService('spaces');
-  const PageObjects = getPageObjects(['common', 'canvas', 'security', 'spaceSelector', 'settings']);
+  const PageObjects = getPageObjects(['common', 'canvas', 'security', 'spaceSelector']);
   const appsMenu = getService('appsMenu');
 
   describe('spaces feature controls', function() {
@@ -40,7 +40,6 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         await PageObjects.common.navigateToApp('home', {
           basePath: '/s/custom_space',
         });
-        await PageObjects.settings.setNavType('individual');
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
         expect(navLinks).to.contain('Canvas');
       });
diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts
index bfffefaecd94c..b966d37becc3f 100644
--- a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts
+++ b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts
@@ -77,7 +77,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows dashboard navlink', async () => {
         const navLinks = await appsMenu.readLinks();
-        expect(navLinks.map(link => link.text)).to.eql(['Dashboard', 'Stack Management']);
+        expect(navLinks.map(link => link.text)).to.eql(['Dashboard', 'Management']);
       });
 
       it(`landing page shows "Create new Dashboard" button`, async () => {
@@ -261,7 +261,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows dashboard navlink', async () => {
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.eql(['Dashboard', 'Stack Management']);
+        expect(navLinks).to.eql(['Dashboard', 'Management']);
       });
 
       it(`landing page doesn't show "Create new Dashboard" button`, async () => {
diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_spaces.ts b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_spaces.ts
index 1f4f0f33a061e..5ab26e4189096 100644
--- a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_spaces.ts
+++ b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_spaces.ts
@@ -14,13 +14,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
   const config = getService('config');
   const spacesService = getService('spaces');
-  const PageObjects = getPageObjects([
-    'common',
-    'dashboard',
-    'security',
-    'spaceSelector',
-    'settings',
-  ]);
+  const PageObjects = getPageObjects(['common', 'dashboard', 'security', 'spaceSelector']);
   const appsMenu = getService('appsMenu');
   const testSubjects = getService('testSubjects');
 
@@ -50,7 +44,6 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         await PageObjects.common.navigateToApp('home', {
           basePath: '/s/custom_space',
         });
-        await PageObjects.settings.setNavType('individual');
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
         expect(navLinks).to.contain('Dashboard');
       });
diff --git a/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js b/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js
index b521c47585d58..b9c0b0095b96b 100644
--- a/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js
+++ b/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js
@@ -37,10 +37,7 @@ export default function({ getService, getPageObjects }) {
       log.debug('Dashboard View Mode:initTests');
       await esArchiver.loadIfNeeded('logstash_functional');
       await esArchiver.load('dashboard_view_mode');
-      await kibanaServer.uiSettings.replace({
-        defaultIndex: 'logstash-*',
-        pageNavigation: 'individual',
-      });
+      await kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-*' });
       await browser.setWindowSize(1600, 1000);
 
       await PageObjects.common.navigateToApp('discover');
@@ -200,7 +197,7 @@ export default function({ getService, getPageObjects }) {
         await PageObjects.security.forceLogout();
         await PageObjects.security.login('mixeduser', '123456');
 
-        if (await appsMenu.linkExists('Stack Management')) {
+        if (await appsMenu.linkExists('Management')) {
           throw new Error('Expected management nav link to not be shown');
         }
       });
@@ -209,7 +206,7 @@ export default function({ getService, getPageObjects }) {
         await PageObjects.security.forceLogout();
         await PageObjects.security.login('mysuperuser', '123456');
 
-        if (!(await appsMenu.linkExists('Stack Management'))) {
+        if (!(await appsMenu.linkExists('Management'))) {
           throw new Error('Expected management nav link to be shown');
         }
       });
diff --git a/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_security.ts b/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_security.ts
index 162bf23c29490..3d17d235b7f4f 100644
--- a/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_security.ts
+++ b/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_security.ts
@@ -64,7 +64,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows Dev Tools navlink', async () => {
         const navLinks = await appsMenu.readLinks();
-        expect(navLinks.map(link => link.text)).to.eql(['Dev Tools', 'Stack Management']);
+        expect(navLinks.map(link => link.text)).to.eql(['Dev Tools', 'Management']);
       });
 
       describe('console', () => {
@@ -145,7 +145,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it(`shows 'Dev Tools' navlink`, async () => {
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.eql(['Dev Tools', 'Stack Management']);
+        expect(navLinks).to.eql(['Dev Tools', 'Management']);
       });
 
       describe('console', () => {
diff --git a/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_spaces.ts b/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_spaces.ts
index 561b7f64eb77d..d1eddfe89c59e 100644
--- a/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_spaces.ts
+++ b/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_spaces.ts
@@ -10,13 +10,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
   const config = getService('config');
   const spacesService = getService('spaces');
-  const PageObjects = getPageObjects([
-    'common',
-    'dashboard',
-    'security',
-    'spaceSelector',
-    'settings',
-  ]);
+  const PageObjects = getPageObjects(['common', 'dashboard', 'security', 'spaceSelector']);
   const appsMenu = getService('appsMenu');
   const testSubjects = getService('testSubjects');
   const grokDebugger = getService('grokDebugger');
@@ -47,7 +41,6 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         await PageObjects.common.navigateToApp('home', {
           basePath: '/s/custom_space',
         });
-        await PageObjects.settings.setNavType('individual');
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
         expect(navLinks).to.contain('Dev Tools');
       });
diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts
index 53d0872b810fe..87ae5231d1031 100644
--- a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts
+++ b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts
@@ -83,7 +83,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows discover navlink', async () => {
         const navLinks = await appsMenu.readLinks();
-        expect(navLinks.map(link => link.text)).to.eql(['Discover', 'Stack Management']);
+        expect(navLinks.map(link => link.text)).to.eql(['Discover', 'Management']);
       });
 
       it('shows save button', async () => {
@@ -170,7 +170,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows discover navlink', async () => {
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.eql(['Discover', 'Stack Management']);
+        expect(navLinks).to.eql(['Discover', 'Management']);
       });
 
       it(`doesn't show save button`, async () => {
diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_spaces.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_spaces.ts
index ba7d4077b2740..4bedc757f0b57 100644
--- a/x-pack/test/functional/apps/discover/feature_controls/discover_spaces.ts
+++ b/x-pack/test/functional/apps/discover/feature_controls/discover_spaces.ts
@@ -16,7 +16,6 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
     'timePicker',
     'security',
     'spaceSelector',
-    'settings',
   ]);
   const testSubjects = getService('testSubjects');
   const appsMenu = getService('appsMenu');
@@ -51,7 +50,6 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         await PageObjects.common.navigateToApp('home', {
           basePath: '/s/custom_space',
         });
-        await PageObjects.settings.setNavType('individual');
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
         expect(navLinks).to.contain('Discover');
       });
diff --git a/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts b/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts
index 37de93a0a7e91..a2b062e6ef84f 100644
--- a/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts
+++ b/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts
@@ -64,7 +64,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows graph navlink', async () => {
         const navLinks = await appsMenu.readLinks();
-        expect(navLinks.map(link => link.text)).to.eql(['Graph', 'Stack Management']);
+        expect(navLinks.map(link => link.text)).to.eql(['Graph', 'Management']);
       });
 
       it('landing page shows "Create new graph" button', async () => {
@@ -127,7 +127,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows graph navlink', async () => {
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.eql(['Graph', 'Stack Management']);
+        expect(navLinks).to.eql(['Graph', 'Management']);
       });
 
       it('does not show a "Create new Workspace" button', async () => {
diff --git a/x-pack/test/functional/apps/graph/feature_controls/graph_spaces.ts b/x-pack/test/functional/apps/graph/feature_controls/graph_spaces.ts
index d0d0232b5a8b1..a0b0d5bef9668 100644
--- a/x-pack/test/functional/apps/graph/feature_controls/graph_spaces.ts
+++ b/x-pack/test/functional/apps/graph/feature_controls/graph_spaces.ts
@@ -9,7 +9,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
 export default function({ getPageObjects, getService }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
   const spacesService = getService('spaces');
-  const PageObjects = getPageObjects(['common', 'graph', 'security', 'error', 'settings']);
+  const PageObjects = getPageObjects(['common', 'graph', 'security', 'error']);
   const testSubjects = getService('testSubjects');
   const appsMenu = getService('appsMenu');
 
@@ -34,7 +34,6 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         await PageObjects.common.navigateToApp('home', {
           basePath: '/s/custom_space',
         });
-        await PageObjects.settings.setNavType('individual');
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
         expect(navLinks).to.contain('Graph');
       });
diff --git a/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_security.ts b/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_security.ts
index 0783767d8f152..d72c9b970204a 100644
--- a/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_security.ts
+++ b/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_security.ts
@@ -71,7 +71,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows management navlink', async () => {
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.eql(['Stack Management']);
+        expect(navLinks).to.eql(['Management']);
       });
 
       it(`index pattern listing shows create button`, async () => {
@@ -114,7 +114,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
           }
         );
 
-        await kibanaServer.uiSettings.replace({ pageNavigation: 'individual' });
+        await kibanaServer.uiSettings.replace({});
         await PageObjects.settings.navigateTo();
       });
 
@@ -125,7 +125,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows management navlink', async () => {
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.eql(['Stack Management']);
+        expect(navLinks).to.eql(['Management']);
       });
 
       it(`index pattern listing doesn't show create button`, async () => {
@@ -177,7 +177,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows Management navlink', async () => {
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.eql(['Discover', 'Stack Management']);
+        expect(navLinks).to.eql(['Discover', 'Management']);
       });
 
       it(`doesn't show Index Patterns in management side-nav`, async () => {
diff --git a/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_spaces.ts b/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_spaces.ts
index d4422e94d10cf..7d9bee37bbbc4 100644
--- a/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_spaces.ts
+++ b/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_spaces.ts
@@ -41,9 +41,8 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         await PageObjects.common.navigateToApp('home', {
           basePath: '/s/custom_space',
         });
-        await PageObjects.settings.setNavType('individual');
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.contain('Stack Management');
+        expect(navLinks).to.contain('Management');
       });
 
       it(`index pattern listing shows create button`, async () => {
diff --git a/x-pack/test/functional/apps/infra/feature_controls/infrastructure_security.ts b/x-pack/test/functional/apps/infra/feature_controls/infrastructure_security.ts
index ede77b7d9afa7..bf35d4dc06aa2 100644
--- a/x-pack/test/functional/apps/infra/feature_controls/infrastructure_security.ts
+++ b/x-pack/test/functional/apps/infra/feature_controls/infrastructure_security.ts
@@ -61,7 +61,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows metrics navlink', async () => {
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.eql(['Metrics', 'Stack Management']);
+        expect(navLinks).to.eql(['Metrics', 'Management']);
       });
 
       describe('infrastructure landing page without data', () => {
@@ -177,7 +177,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows metrics navlink', async () => {
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.eql(['Metrics', 'Stack Management']);
+        expect(navLinks).to.eql(['Metrics', 'Management']);
       });
 
       describe('infrastructure landing page without data', () => {
diff --git a/x-pack/test/functional/apps/infra/feature_controls/infrastructure_spaces.ts b/x-pack/test/functional/apps/infra/feature_controls/infrastructure_spaces.ts
index 3bbcc1aa7043c..37056c7f17ca1 100644
--- a/x-pack/test/functional/apps/infra/feature_controls/infrastructure_spaces.ts
+++ b/x-pack/test/functional/apps/infra/feature_controls/infrastructure_spaces.ts
@@ -9,13 +9,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
 export default function({ getPageObjects, getService }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
   const spacesService = getService('spaces');
-  const PageObjects = getPageObjects([
-    'common',
-    'infraHome',
-    'security',
-    'spaceSelector',
-    'settings',
-  ]);
+  const PageObjects = getPageObjects(['common', 'infraHome', 'security', 'spaceSelector']);
   const testSubjects = getService('testSubjects');
   const appsMenu = getService('appsMenu');
 
@@ -47,7 +41,6 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         await PageObjects.common.navigateToApp('home', {
           basePath: '/s/custom_space',
         });
-        await PageObjects.settings.setNavType('individual');
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
         expect(navLinks).to.contain('Metrics');
       });
diff --git a/x-pack/test/functional/apps/infra/feature_controls/logs_security.ts b/x-pack/test/functional/apps/infra/feature_controls/logs_security.ts
index 48ad4e90fd413..e5a6e27a0fadb 100644
--- a/x-pack/test/functional/apps/infra/feature_controls/logs_security.ts
+++ b/x-pack/test/functional/apps/infra/feature_controls/logs_security.ts
@@ -58,7 +58,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows logs navlink', async () => {
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.eql(['Logs', 'Stack Management']);
+        expect(navLinks).to.eql(['Logs', 'Management']);
       });
 
       describe('logs landing page without data', () => {
@@ -121,7 +121,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows logs navlink', async () => {
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.eql(['Logs', 'Stack Management']);
+        expect(navLinks).to.eql(['Logs', 'Management']);
       });
 
       describe('logs landing page without data', () => {
diff --git a/x-pack/test/functional/apps/infra/feature_controls/logs_spaces.ts b/x-pack/test/functional/apps/infra/feature_controls/logs_spaces.ts
index 0094d227514c0..985131113c535 100644
--- a/x-pack/test/functional/apps/infra/feature_controls/logs_spaces.ts
+++ b/x-pack/test/functional/apps/infra/feature_controls/logs_spaces.ts
@@ -9,13 +9,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
 export default function({ getPageObjects, getService }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
   const spacesService = getService('spaces');
-  const PageObjects = getPageObjects([
-    'common',
-    'infraHome',
-    'security',
-    'spaceSelector',
-    'settings',
-  ]);
+  const PageObjects = getPageObjects(['common', 'infraHome', 'security', 'spaceSelector']);
   const testSubjects = getService('testSubjects');
   const appsMenu = getService('appsMenu');
 
@@ -42,7 +36,6 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         await PageObjects.common.navigateToApp('home', {
           basePath: '/s/custom_space',
         });
-        await PageObjects.settings.setNavType('individual');
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
         expect(navLinks).to.contain('Logs');
       });
diff --git a/x-pack/test/functional/apps/machine_learning/feature_controls/ml_security.ts b/x-pack/test/functional/apps/machine_learning/feature_controls/ml_security.ts
index c25c1bfe4b731..8fb6f21c778d3 100644
--- a/x-pack/test/functional/apps/machine_learning/feature_controls/ml_security.ts
+++ b/x-pack/test/functional/apps/machine_learning/feature_controls/ml_security.ts
@@ -10,7 +10,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
   const security = getService('security');
   const appsMenu = getService('appsMenu');
-  const PageObjects = getPageObjects(['common', 'security', 'settings']);
+  const PageObjects = getPageObjects(['common', 'security']);
 
   describe('security', () => {
     before(async () => {
@@ -94,7 +94,6 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         });
 
         await PageObjects.security.login('machine_learning_user', 'machine_learning_user-password');
-        await PageObjects.settings.setNavType('individual');
       });
 
       after(async () => {
diff --git a/x-pack/test/functional/apps/machine_learning/feature_controls/ml_spaces.ts b/x-pack/test/functional/apps/machine_learning/feature_controls/ml_spaces.ts
index c633852a2da0a..fc94688e98811 100644
--- a/x-pack/test/functional/apps/machine_learning/feature_controls/ml_spaces.ts
+++ b/x-pack/test/functional/apps/machine_learning/feature_controls/ml_spaces.ts
@@ -9,7 +9,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
 export default function({ getPageObjects, getService }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
   const spacesService = getService('spaces');
-  const PageObjects = getPageObjects(['common', 'dashboard', 'security', 'error', 'settings']);
+  const PageObjects = getPageObjects(['common', 'dashboard', 'security', 'error']);
   const appsMenu = getService('appsMenu');
   const testSubjects = getService('testSubjects');
 
@@ -39,7 +39,6 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         await PageObjects.common.navigateToApp('home', {
           basePath: '/s/custom_space',
         });
-        await PageObjects.settings.setNavType('individual');
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
         expect(navLinks).to.contain('Machine Learning');
       });
diff --git a/x-pack/test/functional/apps/maps/feature_controls/maps_security.ts b/x-pack/test/functional/apps/maps/feature_controls/maps_security.ts
index ece162cbd96cc..804ad5725edfd 100644
--- a/x-pack/test/functional/apps/maps/feature_controls/maps_security.ts
+++ b/x-pack/test/functional/apps/maps/feature_controls/maps_security.ts
@@ -66,7 +66,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows maps navlink', async () => {
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.eql(['Maps', 'Stack Management']);
+        expect(navLinks).to.eql(['Maps', 'Management']);
       });
 
       it(`allows a map to be created`, async () => {
@@ -153,7 +153,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows Maps navlink', async () => {
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.eql(['Maps', 'Stack Management']);
+        expect(navLinks).to.eql(['Maps', 'Management']);
       });
 
       it(`does not show create new button`, async () => {
@@ -248,7 +248,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('does not show Maps navlink', async () => {
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.eql(['Discover', 'Stack Management']);
+        expect(navLinks).to.eql(['Discover', 'Management']);
       });
 
       it(`returns a 404`, async () => {
diff --git a/x-pack/test/functional/apps/monitoring/feature_controls/monitoring_security.ts b/x-pack/test/functional/apps/monitoring/feature_controls/monitoring_security.ts
index 130aefb3cae2a..d985da42ab5ed 100644
--- a/x-pack/test/functional/apps/monitoring/feature_controls/monitoring_security.ts
+++ b/x-pack/test/functional/apps/monitoring/feature_controls/monitoring_security.ts
@@ -10,7 +10,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
   const security = getService('security');
   const appsMenu = getService('appsMenu');
-  const PageObjects = getPageObjects(['common', 'security', 'settings']);
+  const PageObjects = getPageObjects(['common', 'security']);
 
   describe('security', () => {
     before(async () => {
@@ -97,7 +97,6 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
       });
 
       it('shows monitoring navlink', async () => {
-        await PageObjects.settings.setNavType('individual');
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
         expect(navLinks).to.contain('Stack Monitoring');
       });
diff --git a/x-pack/test/functional/apps/monitoring/feature_controls/monitoring_spaces.ts b/x-pack/test/functional/apps/monitoring/feature_controls/monitoring_spaces.ts
index 0465cbcf54541..9e306b074d214 100644
--- a/x-pack/test/functional/apps/monitoring/feature_controls/monitoring_spaces.ts
+++ b/x-pack/test/functional/apps/monitoring/feature_controls/monitoring_spaces.ts
@@ -9,7 +9,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
 export default function({ getPageObjects, getService }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
   const spacesService = getService('spaces');
-  const PageObjects = getPageObjects(['common', 'dashboard', 'security', 'error', 'settings']);
+  const PageObjects = getPageObjects(['common', 'dashboard', 'security', 'error']);
   const appsMenu = getService('appsMenu');
   const find = getService('find');
 
@@ -41,7 +41,6 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         await PageObjects.common.navigateToApp('home', {
           basePath: '/s/custom_space',
         });
-        await PageObjects.settings.setNavType('individual');
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
         expect(navLinks).to.contain('Stack Monitoring');
       });
diff --git a/x-pack/test/functional/apps/spaces/feature_controls/spaces_security.ts b/x-pack/test/functional/apps/spaces/feature_controls/spaces_security.ts
index 9ca314ba5ec18..49b684a37079e 100644
--- a/x-pack/test/functional/apps/spaces/feature_controls/spaces_security.ts
+++ b/x-pack/test/functional/apps/spaces/feature_controls/spaces_security.ts
@@ -16,7 +16,6 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
   describe('security feature controls', () => {
     before(async () => {
       await esArchiver.load('empty_kibana');
-      await PageObjects.settings.setNavType('individual');
     });
 
     after(async () => {
@@ -57,7 +56,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows management navlink', async () => {
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.contain('Stack Management');
+        expect(navLinks).to.contain('Management');
       });
 
       it(`displays Spaces management section`, async () => {
@@ -135,7 +134,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows management navlink', async () => {
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.contain('Stack Management');
+        expect(navLinks).to.contain('Management');
       });
 
       it(`doesn't display Spaces management section`, async () => {
diff --git a/x-pack/test/functional/apps/timelion/feature_controls/timelion_security.ts b/x-pack/test/functional/apps/timelion/feature_controls/timelion_security.ts
index 62483a10552e3..dea45f161e451 100644
--- a/x-pack/test/functional/apps/timelion/feature_controls/timelion_security.ts
+++ b/x-pack/test/functional/apps/timelion/feature_controls/timelion_security.ts
@@ -60,7 +60,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows timelion navlink', async () => {
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.eql(['Timelion', 'Stack Management']);
+        expect(navLinks).to.eql(['Timelion', 'Management']);
       });
 
       it(`allows a timelion sheet to be created`, async () => {
@@ -112,7 +112,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows timelion navlink', async () => {
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.eql(['Timelion', 'Stack Management']);
+        expect(navLinks).to.eql(['Timelion', 'Management']);
       });
 
       it(`does not allow a timelion sheet to be created`, async () => {
diff --git a/x-pack/test/functional/apps/timelion/feature_controls/timelion_spaces.ts b/x-pack/test/functional/apps/timelion/feature_controls/timelion_spaces.ts
index 7e0fe731301a6..fb203a23359bd 100644
--- a/x-pack/test/functional/apps/timelion/feature_controls/timelion_spaces.ts
+++ b/x-pack/test/functional/apps/timelion/feature_controls/timelion_spaces.ts
@@ -9,13 +9,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
 export default function({ getPageObjects, getService }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
   const spacesService = getService('spaces');
-  const PageObjects = getPageObjects([
-    'common',
-    'timelion',
-    'security',
-    'spaceSelector',
-    'settings',
-  ]);
+  const PageObjects = getPageObjects(['common', 'timelion', 'security', 'spaceSelector']);
   const appsMenu = getService('appsMenu');
 
   describe('timelion', () => {
@@ -44,7 +38,6 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         await PageObjects.common.navigateToApp('home', {
           basePath: '/s/custom_space',
         });
-        await PageObjects.settings.setNavType('individual');
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
         expect(navLinks).to.contain('Timelion');
       });
diff --git a/x-pack/test/functional/apps/uptime/feature_controls/uptime_security.ts b/x-pack/test/functional/apps/uptime/feature_controls/uptime_security.ts
index 4ff82484db91c..a004f8db66823 100644
--- a/x-pack/test/functional/apps/uptime/feature_controls/uptime_security.ts
+++ b/x-pack/test/functional/apps/uptime/feature_controls/uptime_security.ts
@@ -64,7 +64,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows uptime navlink', async () => {
         const navLinks = await appsMenu.readLinks();
-        expect(navLinks.map(link => link.text)).to.eql(['Uptime', 'Stack Management']);
+        expect(navLinks.map(link => link.text)).to.eql(['Uptime', 'Management']);
       });
 
       it('can navigate to Uptime app', async () => {
@@ -115,7 +115,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows uptime navlink', async () => {
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.eql(['Uptime', 'Stack Management']);
+        expect(navLinks).to.eql(['Uptime', 'Management']);
       });
 
       it('can navigate to Uptime app', async () => {
diff --git a/x-pack/test/functional/apps/uptime/feature_controls/uptime_spaces.ts b/x-pack/test/functional/apps/uptime/feature_controls/uptime_spaces.ts
index c3dcb1b27771f..77c5b323340bf 100644
--- a/x-pack/test/functional/apps/uptime/feature_controls/uptime_spaces.ts
+++ b/x-pack/test/functional/apps/uptime/feature_controls/uptime_spaces.ts
@@ -8,7 +8,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
 
 export default function({ getPageObjects, getService }: FtrProviderContext) {
   const spacesService = getService('spaces');
-  const PageObjects = getPageObjects(['common', 'error', 'timePicker', 'security', 'settings']);
+  const PageObjects = getPageObjects(['common', 'error', 'timePicker', 'security']);
   const testSubjects = getService('testSubjects');
   const appsMenu = getService('appsMenu');
 
@@ -30,7 +30,6 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         await PageObjects.common.navigateToApp('home', {
           basePath: '/s/custom_space',
         });
-        await PageObjects.settings.setNavType('individual');
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
         expect(navLinks).to.contain('Uptime');
       });
diff --git a/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts b/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts
index 1876f46038326..e5b6512d1c1b0 100644
--- a/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts
+++ b/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts
@@ -76,7 +76,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows visualize navlink', async () => {
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.eql(['Visualize', 'Stack Management']);
+        expect(navLinks).to.eql(['Visualize', 'Management']);
       });
 
       it(`landing page shows "Create new Visualization" button`, async () => {
@@ -200,7 +200,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
 
       it('shows visualize navlink', async () => {
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
-        expect(navLinks).to.eql(['Visualize', 'Stack Management']);
+        expect(navLinks).to.eql(['Visualize', 'Management']);
       });
 
       it(`landing page shows "Create new Visualization" button`, async () => {
diff --git a/x-pack/test/functional/apps/visualize/feature_controls/visualize_spaces.ts b/x-pack/test/functional/apps/visualize/feature_controls/visualize_spaces.ts
index b1cb156caad90..4f12dd16247f6 100644
--- a/x-pack/test/functional/apps/visualize/feature_controls/visualize_spaces.ts
+++ b/x-pack/test/functional/apps/visualize/feature_controls/visualize_spaces.ts
@@ -11,13 +11,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
   const config = getService('config');
   const spacesService = getService('spaces');
-  const PageObjects = getPageObjects([
-    'common',
-    'visualize',
-    'security',
-    'spaceSelector',
-    'settings',
-  ]);
+  const PageObjects = getPageObjects(['common', 'visualize', 'security', 'spaceSelector']);
   const testSubjects = getService('testSubjects');
   const appsMenu = getService('appsMenu');
 
@@ -47,7 +41,6 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
         await PageObjects.common.navigateToApp('home', {
           basePath: '/s/custom_space',
         });
-        await PageObjects.settings.setNavType('individual');
         const navLinks = (await appsMenu.readLinks()).map(link => link.text);
         expect(navLinks).to.contain('Visualize');
       });

From 3cba05128e21772307bce491cebbedcd34ff6b2b Mon Sep 17 00:00:00 2001
From: Maggie Ghamry <46542915+maggieghamry@users.noreply.github.com>
Date: Thu, 20 Feb 2020 10:12:19 -0500
Subject: [PATCH 098/174] [Canvas] Adds argument to open all links in new tab
 within markdown element (#57017)

* Adds toggle to open links in new tab within markdown element

* Updating test for markdown function

* Switch to using Markdown Component

* Update ui.ts

Update to change toggle verbiage per Ryan's request, and reuse the Kibana Markdown per Corey's help and recommendation. Still working on updating the styles (consulting Ryan)

* Update toggle.js

Update to prevent text for "Open links in new tab from wrapping" - the example from the horizontal bar chart is quite differently, and reads from "axisConfig" - when I changed the argType to "axisConfig", the layout was the same, but I'll need some input on which specific styles to add to the "ToggleArgInput" - I think this is where the style updates need to occur to get the toggle to stay on the same line, but may be wrong.

* Update ui.ts

Update to original message string per Ryan's feedback, now that there is no wrapping

* Update to UI styles per Ryan's feedback

* updating message per Ryan's request

* Update ui.ts

update to fix internationalization issues

Co-authored-by: Corey Robertson <crob611@gmail.com>
---
 .../functions/browser/markdown.test.js        | 29 ++++++++++++++++-
 .../functions/browser/markdown.ts             |  8 +++++
 .../renderers/markdown/index.js               | 14 +++------
 .../canvas_plugin_src/uis/arguments/toggle.js | 31 +++++++++++--------
 .../canvas_plugin_src/uis/views/markdown.js   | 11 +++++++
 .../canvas/i18n/functions/dict/markdown.ts    |  4 +++
 x-pack/legacy/plugins/canvas/i18n/ui.ts       | 12 +++++++
 7 files changed, 86 insertions(+), 23 deletions(-)

diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/markdown.test.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/markdown.test.js
index f9912c270a46a..27ea290fb4dcc 100644
--- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/markdown.test.js
+++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/markdown.test.js
@@ -19,7 +19,7 @@ describe('markdown', () => {
   });
 
   describe('args', () => {
-    describe('expression', () => {
+    describe('content', () => {
       it('sets the content to all strings in expression concatenated', () => {
         const result = fn(null, {
           content: ['# this ', 'is ', 'some ', 'markdown'],
@@ -54,5 +54,32 @@ describe('markdown', () => {
       // TODO: write test when using an instance of the interpreter
       // it("defaults to the expression '{font}'", () => {});
     });
+    describe('openLinksInNewTab', () => {
+      it('sets the value of openLinksInNewTab to true ', () => {
+        const result = fn(null, {
+          content: ['some ', 'markdown'],
+          openLinksInNewTab: true,
+        });
+
+        expect(result.value).toHaveProperty('openLinksInNewTab', true);
+      });
+
+      it('sets the value of openLinksInNewTab to false ', () => {
+        const result = fn(null, {
+          content: ['some ', 'markdown'],
+          openLinksInNewTab: false,
+        });
+
+        expect(result.value).toHaveProperty('openLinksInNewTab', false);
+      });
+
+      it('defaults the value of openLinksInNewTab to false ', () => {
+        const result = fn(null, {
+          content: ['some ', 'markdown'],
+        });
+
+        expect(result.value).toHaveProperty('openLinksInNewTab', false);
+      });
+    });
   });
 });
diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/markdown.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/markdown.ts
index 95859feeed5f3..e94b9f201a174 100644
--- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/markdown.ts
+++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/markdown.ts
@@ -19,11 +19,13 @@ type Context = Datatable | null;
 interface Arguments {
   content: string[];
   font: Style;
+  openLinksInNewTab: boolean;
 }
 
 interface Return {
   content: string;
   font: Style;
+  openLinksInNewTab: boolean;
 }
 
 export function markdown(): ExpressionFunctionDefinition<
@@ -53,6 +55,11 @@ export function markdown(): ExpressionFunctionDefinition<
         help: argHelp.font,
         default: '{font}',
       },
+      openLinksInNewTab: {
+        types: ['boolean'],
+        help: argHelp.openLinksInNewTab,
+        default: false,
+      },
     },
     fn: (input, args) => {
       const compileFunctions = args.content.map(str =>
@@ -71,6 +78,7 @@ export function markdown(): ExpressionFunctionDefinition<
         value: {
           content: compileFunctions.map(fn => fn(ctx)).join(''),
           font: args.font,
+          openLinksInNewTab: args.openLinksInNewTab,
         },
       };
     },
diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/markdown/index.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/markdown/index.js
index 82c63d5e7d529..c1bfd7c99ac41 100644
--- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/markdown/index.js
+++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/markdown/index.js
@@ -6,33 +6,29 @@
 
 import React from 'react';
 import ReactDOM from 'react-dom';
-import Markdown from 'markdown-it';
 import { RendererStrings } from '../../../i18n';
+import { Markdown } from '../../../../../../../src/legacy/core_plugins/kibana_react/public';
 
 const { markdown: strings } = RendererStrings;
 
-const md = new Markdown();
-
 export const markdown = () => ({
   name: 'markdown',
   displayName: strings.getDisplayName(),
   help: strings.getHelpDescription(),
   reuseDomNode: true,
   render(domNode, config, handlers) {
-    const html = { __html: md.render(String(config.content)) };
     const fontStyle = config.font ? config.font.spec : {};
 
-    /* eslint-disable react/no-danger */
     ReactDOM.render(
-      <div
-        className="kbnMarkdown__body canvasMarkdown"
+      <Markdown
+        className="canvasMarkdown"
         style={fontStyle}
-        dangerouslySetInnerHTML={html}
+        markdown={config.content}
+        openLinksInNewTab={config.openLinksInNewTab}
       />,
       domNode,
       () => handlers.done()
     );
-    /* eslint-enable */
 
     handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode));
   },
diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/toggle.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/toggle.js
index bcad4678e0b6a..299f96ff1b4e8 100644
--- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/toggle.js
+++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/toggle.js
@@ -19,18 +19,21 @@ const ToggleArgInput = ({ onValueChange, argValue, argId, renderError, typeInsta
     return null;
   }
   return (
-    <EuiFormRow display="columnCompressedSwitch">
-      <EuiSwitch
-        compressed
-        id={argId}
-        checked={argValue}
-        onChange={handleChange}
-        className="canvasArg__switch"
-        aria-label={typeInstance.displayName}
-        label=""
-        showLabel={false}
-      />
-    </EuiFormRow>
+    <div>
+      <EuiFormRow display="rowCompressed">
+        <EuiSwitch
+          compressed
+          id={argId}
+          checked={argValue}
+          onChange={handleChange}
+          className="canvasArg__form"
+          aria-label={typeInstance.displayName}
+          resize="none"
+          label={typeInstance.options.labelValue}
+          showLabel
+        />
+      </EuiFormRow>
+    </div>
   );
 };
 
@@ -38,6 +41,8 @@ ToggleArgInput.propTypes = {
   onValueChange: PropTypes.func.isRequired,
   argValue: PropTypes.oneOfType([PropTypes.bool, PropTypes.string, PropTypes.object]).isRequired,
   argId: PropTypes.string.isRequired,
+  labelValue: PropTypes.string,
+  showLabelValue: PropTypes.bool,
   renderError: PropTypes.func.isRequired,
 };
 
@@ -45,6 +50,6 @@ export const toggle = () => ({
   name: 'toggle',
   displayName: strings.getDisplayName(),
   help: strings.getHelp(),
-  simpleTemplate: templateFromReactComponent(ToggleArgInput),
+  template: templateFromReactComponent(ToggleArgInput),
   default: 'false',
 });
diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/markdown.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/markdown.js
index 1c46bc6dd57c2..edae739ee0d3d 100644
--- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/markdown.js
+++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/markdown.js
@@ -29,5 +29,16 @@ export const markdown = () => ({
       name: 'font',
       argType: 'font',
     },
+    {
+      name: 'openLinksInNewTab',
+      displayName: strings.getOpenLinksInNewTabDisplayName(),
+      help: strings.getOpenLinksInNewTabHelp(),
+      label: strings.getOpenLinksInNewTabLabelName(),
+      argType: 'toggle',
+      default: false,
+      options: {
+        labelValue: 'Open all links in a new tab',
+      },
+    },
   ],
 });
diff --git a/x-pack/legacy/plugins/canvas/i18n/functions/dict/markdown.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/markdown.ts
index d5271e14436e2..aa2845ba4ec3a 100644
--- a/x-pack/legacy/plugins/canvas/i18n/functions/dict/markdown.ts
+++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/markdown.ts
@@ -37,5 +37,9 @@ export const help: FunctionHelp<FunctionFactory<typeof markdown>> = {
         fontWeight: 'font-weight',
       },
     }),
+    openLinksInNewTab: i18n.translate('xpack.canvas.functions.markdown.args.openLinkHelpText', {
+      defaultMessage:
+        'A true/false value for opening links in a new tab. Default value is false. Setting to true will open all links in a new tab.',
+    }),
   },
 };
diff --git a/x-pack/legacy/plugins/canvas/i18n/ui.ts b/x-pack/legacy/plugins/canvas/i18n/ui.ts
index 323a6c97fd967..5b94cb0435b31 100644
--- a/x-pack/legacy/plugins/canvas/i18n/ui.ts
+++ b/x-pack/legacy/plugins/canvas/i18n/ui.ts
@@ -628,6 +628,18 @@ export const ViewStrings = {
           markdown: MARKDOWN,
         },
       }),
+    getOpenLinksInNewTabDisplayName: () =>
+      i18n.translate('xpack.canvas.uis.views.openLinksInNewTabTitle', {
+        defaultMessage: 'Markdown link settings',
+      }),
+    getOpenLinksInNewTabLabelName: () =>
+      i18n.translate('xpack.canvas.uis.views.openLinksInNewTabLabel', {
+        defaultMessage: 'Open all links in a new tab',
+      }),
+    getOpenLinksInNewTabHelp: () =>
+      i18n.translate('xpack.canvas.uis.views.openLinksInNewTabHelpLabel', {
+        defaultMessage: 'Set links to open in new tab',
+      }),
   },
   Metric: {
     getDisplayName: () =>

From 602aca338780c20269e59d113d5d63f6840b2797 Mon Sep 17 00:00:00 2001
From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com>
Date: Thu, 20 Feb 2020 10:39:43 -0500
Subject: [PATCH 099/174] [SIEM] Detections container/rules unit tests (#58055)

* add unit test for rules api

* add unit test for useFetchIndexPatterns

* fix useFetchIndexPatterns and add unit test for usePersistRule

* add more unit test for container/rules

* review

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../detection_engine/rules/__mocks__/api.ts   |  81 ++
 .../detection_engine/rules/api.test.ts        | 860 ++++++++++++++++++
 .../rules/fetch_index_patterns.test.tsx       | 460 ++++++++++
 .../rules/fetch_index_patterns.tsx            |   2 +-
 .../containers/detection_engine/rules/mock.ts | 139 +++
 .../rules/persist_rule.test.tsx               |  44 +
 .../detection_engine/rules/persist_rule.tsx   |   5 +-
 .../detection_engine/rules/types.ts           |   2 +-
 .../rules/use_pre_packaged_rules.test.tsx     | 267 ++++++
 .../rules/use_pre_packaged_rules.tsx          |   8 +-
 .../detection_engine/rules/use_rule.test.tsx  |  84 ++
 .../detection_engine/rules/use_rule.tsx       |   5 +-
 .../rules/use_rule_status.test.tsx            |  65 ++
 .../rules/use_rule_status.tsx                 |   5 +-
 .../detection_engine/rules/use_rules.test.tsx | 216 +++++
 .../detection_engine/rules/use_rules.tsx      |   7 +-
 .../detection_engine/rules/use_tags.test.tsx  |  29 +
 .../detection_engine/rules/use_tags.tsx       |   4 +-
 .../plugins/siem/public/mock/hook_wrapper.tsx |   1 +
 19 files changed, 2267 insertions(+), 17 deletions(-)
 create mode 100644 x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/__mocks__/api.ts
 create mode 100644 x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.test.ts
 create mode 100644 x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/fetch_index_patterns.test.tsx
 create mode 100644 x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/mock.ts
 create mode 100644 x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/persist_rule.test.tsx
 create mode 100644 x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_pre_packaged_rules.test.tsx
 create mode 100644 x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rule.test.tsx
 create mode 100644 x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rule_status.test.tsx
 create mode 100644 x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rules.test.tsx
 create mode 100644 x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_tags.test.tsx

diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/__mocks__/api.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/__mocks__/api.ts
new file mode 100644
index 0000000000000..9f37f3fecd508
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/__mocks__/api.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 {
+  AddRulesProps,
+  NewRule,
+  PrePackagedRulesStatusResponse,
+  BasicFetchProps,
+  RuleStatusResponse,
+  Rule,
+  FetchRuleProps,
+  FetchRulesResponse,
+  FetchRulesProps,
+} from '../types';
+import { ruleMock, savedRuleMock, rulesMock } from '../mock';
+
+export const addRule = async ({ rule, signal }: AddRulesProps): Promise<NewRule> =>
+  Promise.resolve(ruleMock);
+
+export const getPrePackagedRulesStatus = async ({
+  signal,
+}: {
+  signal: AbortSignal;
+}): Promise<PrePackagedRulesStatusResponse> =>
+  Promise.resolve({
+    rules_custom_installed: 33,
+    rules_installed: 12,
+    rules_not_installed: 0,
+    rules_not_updated: 0,
+  });
+
+export const createPrepackagedRules = async ({ signal }: BasicFetchProps): Promise<boolean> =>
+  Promise.resolve(true);
+
+export const getRuleStatusById = async ({
+  id,
+  signal,
+}: {
+  id: string;
+  signal: AbortSignal;
+}): Promise<RuleStatusResponse> =>
+  Promise.resolve({
+    myOwnRuleID: {
+      current_status: {
+        alert_id: 'alertId',
+        status_date: 'mm/dd/yyyyTHH:MM:sssz',
+        status: 'succeeded',
+        last_failure_at: null,
+        last_success_at: 'mm/dd/yyyyTHH:MM:sssz',
+        last_failure_message: null,
+        last_success_message: 'it is a success',
+      },
+      failures: [],
+    },
+  });
+
+export const fetchRuleById = async ({ id, signal }: FetchRuleProps): Promise<Rule> =>
+  Promise.resolve(savedRuleMock);
+
+export const fetchRules = async ({
+  filterOptions = {
+    filter: '',
+    sortField: 'enabled',
+    sortOrder: 'desc',
+    showCustomRules: false,
+    showElasticRules: false,
+    tags: [],
+  },
+  pagination = {
+    page: 1,
+    perPage: 20,
+    total: 0,
+  },
+  signal,
+}: FetchRulesProps): Promise<FetchRulesResponse> => Promise.resolve(rulesMock);
+
+export const fetchTags = async ({ signal }: { signal: AbortSignal }): Promise<string[]> =>
+  Promise.resolve(['elastic', 'love', 'quality', 'code']);
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.test.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.test.ts
new file mode 100644
index 0000000000000..b348678e789f8
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.test.ts
@@ -0,0 +1,860 @@
+/*
+ * 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 { KibanaServices } from '../../../lib/kibana';
+import {
+  addRule,
+  fetchRules,
+  fetchRuleById,
+  enableRules,
+  deleteRules,
+  duplicateRules,
+  createPrepackagedRules,
+  importRules,
+  exportRules,
+  getRuleStatusById,
+  fetchTags,
+  getPrePackagedRulesStatus,
+} from './api';
+import { ruleMock, rulesMock } from './mock';
+import { ToasterErrors } from '../../../components/ml/api/throw_if_not_ok';
+
+const abortCtrl = new AbortController();
+const mockKibanaServices = KibanaServices.get as jest.Mock;
+jest.mock('../../../lib/kibana');
+
+const mockfetchSuccess = (body: unknown, fetchMock?: jest.Mock) => {
+  if (fetchMock) {
+    mockKibanaServices.mockImplementation(() => ({
+      http: {
+        fetch: fetchMock,
+      },
+    }));
+  } else {
+    mockKibanaServices.mockImplementation(() => ({
+      http: {
+        fetch: () => ({
+          response: {
+            ok: true,
+            message: 'success',
+            text: 'success',
+          },
+          body,
+        }),
+      },
+    }));
+  }
+};
+
+const mockfetchError = () => {
+  mockKibanaServices.mockImplementation(() => ({
+    http: {
+      fetch: () => ({
+        response: {
+          ok: false,
+          text: () =>
+            JSON.stringify({
+              message: 'super mega error, it is not that bad',
+            }),
+        },
+        body: null,
+      }),
+    },
+  }));
+};
+
+describe('Detections Rules API', () => {
+  const fetchMock = jest.fn();
+  describe('addRule', () => {
+    beforeEach(() => {
+      mockKibanaServices.mockClear();
+      fetchMock.mockClear();
+      fetchMock.mockImplementation(() => ({
+        response: {
+          ok: true,
+          message: 'success',
+          text: 'success',
+        },
+        body: ruleMock,
+      }));
+    });
+    test('check parameter url, body', async () => {
+      mockfetchSuccess(null, fetchMock);
+
+      await addRule({ rule: ruleMock, signal: abortCtrl.signal });
+      expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules', {
+        asResponse: true,
+        body:
+          '{"description":"some desc","enabled":true,"false_positives":[],"filters":[],"from":"now-360s","index":["apm-*-transaction*","auditbeat-*","endgame-*","filebeat-*","packetbeat-*","winlogbeat-*"],"interval":"5m","rule_id":"bbd3106e-b4b5-4d7c-a1a2-47531d6a2baf","language":"kuery","risk_score":75,"name":"Test rule","query":"user.email: \'root@elastic.co\'","references":[],"severity":"high","tags":["APM"],"to":"now","type":"query","threat":[]}',
+        method: 'POST',
+        signal: abortCtrl.signal,
+      });
+    });
+    test('happy path', async () => {
+      mockfetchSuccess(ruleMock);
+      const ruleResp = await addRule({ rule: ruleMock, signal: abortCtrl.signal });
+      expect(ruleResp).toEqual(ruleMock);
+    });
+    test('unhappy path', async () => {
+      mockfetchError();
+      try {
+        await addRule({ rule: ruleMock, signal: abortCtrl.signal });
+      } catch (exp) {
+        expect(exp).toBeInstanceOf(ToasterErrors);
+        expect(exp.message).toEqual('super mega error, it is not that bad');
+      }
+    });
+  });
+
+  describe('fetchRules', () => {
+    beforeEach(() => {
+      mockKibanaServices.mockClear();
+      fetchMock.mockClear();
+      fetchMock.mockImplementation(() => ({
+        response: {
+          ok: true,
+          message: 'success',
+          text: 'success',
+        },
+        body: rulesMock,
+      }));
+    });
+
+    test('check parameter url, query without any options', async () => {
+      mockfetchSuccess(null, fetchMock);
+
+      await fetchRules({ signal: abortCtrl.signal });
+      expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_find', {
+        asResponse: true,
+        method: 'GET',
+        query: {
+          page: 1,
+          per_page: 20,
+          sort_field: 'enabled',
+          sort_order: 'desc',
+        },
+        signal: abortCtrl.signal,
+      });
+    });
+
+    test('check parameter url, query with a filter', async () => {
+      mockfetchSuccess(null, fetchMock);
+
+      await fetchRules({
+        filterOptions: {
+          filter: 'hello world',
+          sortField: 'enabled',
+          sortOrder: 'desc',
+          showCustomRules: false,
+          showElasticRules: false,
+          tags: [],
+        },
+        signal: abortCtrl.signal,
+      });
+      expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_find', {
+        asResponse: true,
+        method: 'GET',
+        query: {
+          filter: 'alert.attributes.name: hello world',
+          page: 1,
+          per_page: 20,
+          sort_field: 'enabled',
+          sort_order: 'desc',
+        },
+        signal: abortCtrl.signal,
+      });
+    });
+
+    test('check parameter url, query with showCustomRules', async () => {
+      mockfetchSuccess(null, fetchMock);
+
+      await fetchRules({
+        filterOptions: {
+          filter: '',
+          sortField: 'enabled',
+          sortOrder: 'desc',
+          showCustomRules: true,
+          showElasticRules: false,
+          tags: [],
+        },
+        signal: abortCtrl.signal,
+      });
+      expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_find', {
+        asResponse: true,
+        method: 'GET',
+        query: {
+          filter: 'alert.attributes.tags: "__internal_immutable:false"',
+          page: 1,
+          per_page: 20,
+          sort_field: 'enabled',
+          sort_order: 'desc',
+        },
+        signal: abortCtrl.signal,
+      });
+    });
+
+    test('check parameter url, query with showElasticRules', async () => {
+      mockfetchSuccess(null, fetchMock);
+
+      await fetchRules({
+        filterOptions: {
+          filter: '',
+          sortField: 'enabled',
+          sortOrder: 'desc',
+          showCustomRules: false,
+          showElasticRules: true,
+          tags: [],
+        },
+        signal: abortCtrl.signal,
+      });
+      expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_find', {
+        asResponse: true,
+        method: 'GET',
+        query: {
+          filter: 'alert.attributes.tags: "__internal_immutable:true"',
+          page: 1,
+          per_page: 20,
+          sort_field: 'enabled',
+          sort_order: 'desc',
+        },
+        signal: abortCtrl.signal,
+      });
+    });
+
+    test('check parameter url, query with tags', async () => {
+      mockfetchSuccess(null, fetchMock);
+
+      await fetchRules({
+        filterOptions: {
+          filter: '',
+          sortField: 'enabled',
+          sortOrder: 'desc',
+          showCustomRules: false,
+          showElasticRules: false,
+          tags: ['hello', 'world'],
+        },
+        signal: abortCtrl.signal,
+      });
+      expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_find', {
+        asResponse: true,
+        method: 'GET',
+        query: {
+          filter: 'alert.attributes.tags: hello AND alert.attributes.tags: world',
+          page: 1,
+          per_page: 20,
+          sort_field: 'enabled',
+          sort_order: 'desc',
+        },
+        signal: abortCtrl.signal,
+      });
+    });
+
+    test('check parameter url, query with all options', async () => {
+      mockfetchSuccess(null, fetchMock);
+
+      await fetchRules({
+        filterOptions: {
+          filter: 'ruleName',
+          sortField: 'enabled',
+          sortOrder: 'desc',
+          showCustomRules: true,
+          showElasticRules: true,
+          tags: ['hello', 'world'],
+        },
+        signal: abortCtrl.signal,
+      });
+      expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_find', {
+        asResponse: true,
+        method: 'GET',
+        query: {
+          filter:
+            'alert.attributes.name: ruleName AND alert.attributes.tags: "__internal_immutable:false" AND alert.attributes.tags: "__internal_immutable:true" AND alert.attributes.tags: hello AND alert.attributes.tags: world',
+          page: 1,
+          per_page: 20,
+          sort_field: 'enabled',
+          sort_order: 'desc',
+        },
+        signal: abortCtrl.signal,
+      });
+    });
+
+    test('happy path', async () => {
+      mockfetchSuccess(rulesMock);
+
+      const rulesResp = await fetchRules({ signal: abortCtrl.signal });
+      expect(rulesResp).toEqual(rulesMock);
+    });
+    test('unhappy path', async () => {
+      mockfetchError();
+      try {
+        await fetchRules({ signal: abortCtrl.signal });
+      } catch (exp) {
+        expect(exp).toBeInstanceOf(ToasterErrors);
+        expect(exp.message).toEqual('super mega error, it is not that bad');
+      }
+    });
+  });
+
+  describe('fetchRuleById', () => {
+    beforeEach(() => {
+      mockKibanaServices.mockClear();
+      fetchMock.mockClear();
+      fetchMock.mockImplementation(() => ({
+        response: {
+          ok: true,
+          message: 'success',
+          text: 'success',
+        },
+        body: ruleMock,
+      }));
+    });
+    test('check parameter url, query', async () => {
+      mockfetchSuccess(null, fetchMock);
+
+      await fetchRuleById({ id: 'mySuperRuleId', signal: abortCtrl.signal });
+      expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules', {
+        asResponse: true,
+        query: {
+          id: 'mySuperRuleId',
+        },
+        method: 'GET',
+        signal: abortCtrl.signal,
+      });
+    });
+    test('happy path', async () => {
+      mockfetchSuccess(ruleMock);
+      const ruleResp = await fetchRuleById({ id: 'mySuperRuleId', signal: abortCtrl.signal });
+      expect(ruleResp).toEqual(ruleMock);
+    });
+    test('unhappy path', async () => {
+      mockfetchError();
+      try {
+        await fetchRuleById({ id: 'mySuperRuleId', signal: abortCtrl.signal });
+      } catch (exp) {
+        expect(exp).toBeInstanceOf(ToasterErrors);
+        expect(exp.message).toEqual('super mega error, it is not that bad');
+      }
+    });
+  });
+
+  describe('enableRules', () => {
+    beforeEach(() => {
+      mockKibanaServices.mockClear();
+      fetchMock.mockClear();
+      fetchMock.mockImplementation(() => ({
+        response: {
+          ok: true,
+          message: 'success',
+          text: 'success',
+        },
+        body: ruleMock,
+      }));
+    });
+    test('check parameter url, body when enabling rules', async () => {
+      mockfetchSuccess(null, fetchMock);
+
+      await enableRules({ ids: ['mySuperRuleId', 'mySuperRuleId_II'], enabled: true });
+      expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_bulk_update', {
+        asResponse: true,
+        body: '[{"id":"mySuperRuleId","enabled":true},{"id":"mySuperRuleId_II","enabled":true}]',
+        method: 'PATCH',
+      });
+    });
+    test('check parameter url, body when disabling rules', async () => {
+      mockfetchSuccess(null, fetchMock);
+
+      await enableRules({ ids: ['mySuperRuleId', 'mySuperRuleId_II'], enabled: false });
+      expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_bulk_update', {
+        asResponse: true,
+        body: '[{"id":"mySuperRuleId","enabled":false},{"id":"mySuperRuleId_II","enabled":false}]',
+        method: 'PATCH',
+      });
+    });
+    test('happy path', async () => {
+      mockfetchSuccess(rulesMock.data);
+      const ruleResp = await enableRules({
+        ids: ['mySuperRuleId', 'mySuperRuleId_II'],
+        enabled: true,
+      });
+      expect(ruleResp).toEqual(rulesMock.data);
+    });
+    test('unhappy path', async () => {
+      mockfetchError();
+      try {
+        await enableRules({ ids: ['mySuperRuleId', 'mySuperRuleId_II'], enabled: true });
+      } catch (exp) {
+        expect(exp).toBeInstanceOf(ToasterErrors);
+        expect(exp.message).toEqual('super mega error, it is not that bad');
+      }
+    });
+  });
+
+  describe('deleteRules', () => {
+    beforeEach(() => {
+      mockKibanaServices.mockClear();
+      fetchMock.mockClear();
+      fetchMock.mockImplementation(() => ({
+        response: {
+          ok: true,
+          message: 'success',
+          text: 'success',
+        },
+        body: ruleMock,
+      }));
+    });
+    test('check parameter url, body when deleting rules', async () => {
+      mockfetchSuccess(null, fetchMock);
+
+      await deleteRules({ ids: ['mySuperRuleId', 'mySuperRuleId_II'] });
+      expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_bulk_delete', {
+        asResponse: true,
+        body: '[{"id":"mySuperRuleId"},{"id":"mySuperRuleId_II"}]',
+        method: 'DELETE',
+      });
+    });
+    test('happy path', async () => {
+      mockfetchSuccess(ruleMock);
+      const ruleResp = await deleteRules({
+        ids: ['mySuperRuleId', 'mySuperRuleId_II'],
+      });
+      expect(ruleResp).toEqual(ruleMock);
+    });
+    test('unhappy path', async () => {
+      mockfetchError();
+      try {
+        await deleteRules({ ids: ['mySuperRuleId', 'mySuperRuleId_II'] });
+      } catch (exp) {
+        expect(exp).toBeInstanceOf(ToasterErrors);
+        expect(exp.message).toEqual('super mega error, it is not that bad');
+      }
+    });
+  });
+
+  describe('duplicateRules', () => {
+    beforeEach(() => {
+      mockKibanaServices.mockClear();
+      fetchMock.mockClear();
+      fetchMock.mockImplementation(() => ({
+        response: {
+          ok: true,
+          message: 'success',
+          text: 'success',
+        },
+        body: ruleMock,
+      }));
+    });
+    test('check parameter url, body when duplicating rules', async () => {
+      mockfetchSuccess(null, fetchMock);
+
+      await duplicateRules({ rules: rulesMock.data });
+      expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_bulk_create', {
+        asResponse: true,
+        body:
+          '[{"description":"Elastic Endpoint detected Credential Dumping. Click the Elastic Endpoint icon in the event.module column or the link in the rule.reference column in the External Alerts tab of the SIEM Detections page for additional information.","enabled":false,"false_positives":[],"from":"now-660s","index":["endgame-*"],"interval":"10m","language":"kuery","output_index":".siem-signals-default","max_signals":100,"risk_score":73,"name":"Credential Dumping - Detected - Elastic Endpoint [Duplicate]","query":"event.kind:alert and event.module:endgame and event.action:cred_theft_event and endgame.metadata.type:detection","filters":[],"references":[],"severity":"high","tags":["Elastic","Endpoint"],"to":"now","type":"query","threat":[],"version":1},{"description":"Elastic Endpoint detected an Adversary Behavior. Click the Elastic Endpoint icon in the event.module column or the link in the rule.reference column in the External Alerts tab of the SIEM Detections page for additional information.","enabled":false,"false_positives":[],"from":"now-660s","index":["endgame-*"],"interval":"10m","language":"kuery","output_index":".siem-signals-default","max_signals":100,"risk_score":47,"name":"Adversary Behavior - Detected - Elastic Endpoint [Duplicate]","query":"event.kind:alert and event.module:endgame and event.action:rules_engine_event","filters":[],"references":[],"severity":"medium","tags":["Elastic","Endpoint"],"to":"now","type":"query","threat":[],"version":1}]',
+        method: 'POST',
+      });
+    });
+    test('happy path', async () => {
+      mockfetchSuccess(rulesMock.data);
+      const ruleResp = await duplicateRules({ rules: rulesMock.data });
+      expect(ruleResp).toEqual(rulesMock.data);
+    });
+    test('unhappy path', async () => {
+      mockfetchError();
+      try {
+        await duplicateRules({ rules: rulesMock.data });
+      } catch (exp) {
+        expect(exp).toBeInstanceOf(ToasterErrors);
+        expect(exp.message).toEqual('super mega error, it is not that bad');
+      }
+    });
+  });
+
+  describe('createPrepackagedRules', () => {
+    beforeEach(() => {
+      mockKibanaServices.mockClear();
+      fetchMock.mockClear();
+      fetchMock.mockImplementation(() => ({
+        response: {
+          ok: true,
+          message: 'success',
+          text: 'success',
+        },
+        body: ruleMock,
+      }));
+    });
+    test('check parameter url when creating pre-packaged rules', async () => {
+      mockfetchSuccess(null, fetchMock);
+
+      await createPrepackagedRules({ signal: abortCtrl.signal });
+      expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/prepackaged', {
+        asResponse: true,
+        signal: abortCtrl.signal,
+        method: 'PUT',
+      });
+    });
+    test('happy path', async () => {
+      mockfetchSuccess(true);
+      const resp = await createPrepackagedRules({ signal: abortCtrl.signal });
+      expect(resp).toEqual(true);
+    });
+    test('unhappy path', async () => {
+      mockfetchError();
+      try {
+        await createPrepackagedRules({ signal: abortCtrl.signal });
+      } catch (exp) {
+        expect(exp).toBeInstanceOf(ToasterErrors);
+        expect(exp.message).toEqual('super mega error, it is not that bad');
+      }
+    });
+  });
+
+  describe('importRules', () => {
+    const fileToImport: File = {
+      lastModified: 33,
+      name: 'fileToImport',
+      size: 89,
+      type: 'json',
+      arrayBuffer: jest.fn(),
+      slice: jest.fn(),
+      stream: jest.fn(),
+      text: jest.fn(),
+    } as File;
+    const formData = new FormData();
+    formData.append('file', fileToImport);
+    beforeEach(() => {
+      mockKibanaServices.mockClear();
+      fetchMock.mockClear();
+      fetchMock.mockImplementation(() => ({
+        response: {
+          ok: true,
+          message: 'success',
+          text: 'success',
+        },
+        body: ruleMock,
+      }));
+    });
+    test('check parameter url, body and query when importing rules', async () => {
+      mockfetchSuccess(null, fetchMock);
+      await importRules({ fileToImport, signal: abortCtrl.signal });
+      expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_import', {
+        asResponse: true,
+        signal: abortCtrl.signal,
+        method: 'POST',
+        body: formData,
+        headers: {
+          'Content-Type': undefined,
+        },
+        query: {
+          overwrite: false,
+        },
+      });
+    });
+
+    test('check parameter url, body and query when importing rules with overwrite', async () => {
+      mockfetchSuccess(null, fetchMock);
+
+      await importRules({ fileToImport, overwrite: true, signal: abortCtrl.signal });
+      expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_import', {
+        asResponse: true,
+        signal: abortCtrl.signal,
+        method: 'POST',
+        body: formData,
+        headers: {
+          'Content-Type': undefined,
+        },
+        query: {
+          overwrite: true,
+        },
+      });
+    });
+
+    test('happy path', async () => {
+      mockfetchSuccess({
+        success: true,
+        success_count: 33,
+        errors: [],
+      });
+      const resp = await importRules({ fileToImport, signal: abortCtrl.signal });
+      expect(resp).toEqual({
+        success: true,
+        success_count: 33,
+        errors: [],
+      });
+    });
+
+    test('unhappy path', async () => {
+      mockfetchError();
+      try {
+        await importRules({ fileToImport, signal: abortCtrl.signal });
+      } catch (exp) {
+        expect(exp).toBeInstanceOf(ToasterErrors);
+        expect(exp.message).toEqual('super mega error, it is not that bad');
+      }
+    });
+  });
+
+  describe('exportRules', () => {
+    beforeEach(() => {
+      mockKibanaServices.mockClear();
+      fetchMock.mockClear();
+      fetchMock.mockImplementation(() => ({
+        response: {
+          ok: true,
+          message: 'success',
+          text: 'success',
+        },
+        body: ruleMock,
+      }));
+    });
+
+    test('check parameter url, body and query when exporting rules', async () => {
+      mockfetchSuccess(null, fetchMock);
+      await exportRules({
+        ruleIds: ['mySuperRuleId', 'mySuperRuleId_II'],
+        signal: abortCtrl.signal,
+      });
+      expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_export', {
+        asResponse: true,
+        signal: abortCtrl.signal,
+        method: 'POST',
+        body: '{"objects":[{"rule_id":"mySuperRuleId"},{"rule_id":"mySuperRuleId_II"}]}',
+        query: {
+          exclude_export_details: false,
+          file_name: 'rules_export.ndjson',
+        },
+      });
+    });
+
+    test('check parameter url, body and query when exporting rules with excludeExportDetails', async () => {
+      mockfetchSuccess(null, fetchMock);
+      await exportRules({
+        excludeExportDetails: true,
+        ruleIds: ['mySuperRuleId', 'mySuperRuleId_II'],
+        signal: abortCtrl.signal,
+      });
+      expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_export', {
+        asResponse: true,
+        signal: abortCtrl.signal,
+        method: 'POST',
+        body: '{"objects":[{"rule_id":"mySuperRuleId"},{"rule_id":"mySuperRuleId_II"}]}',
+        query: {
+          exclude_export_details: true,
+          file_name: 'rules_export.ndjson',
+        },
+      });
+    });
+
+    test('check parameter url, body and query when exporting rules with fileName', async () => {
+      mockfetchSuccess(null, fetchMock);
+      await exportRules({
+        filename: 'myFileName.ndjson',
+        ruleIds: ['mySuperRuleId', 'mySuperRuleId_II'],
+        signal: abortCtrl.signal,
+      });
+      expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_export', {
+        asResponse: true,
+        signal: abortCtrl.signal,
+        method: 'POST',
+        body: '{"objects":[{"rule_id":"mySuperRuleId"},{"rule_id":"mySuperRuleId_II"}]}',
+        query: {
+          exclude_export_details: false,
+          file_name: 'myFileName.ndjson',
+        },
+      });
+    });
+
+    test('check parameter url, body and query when exporting rules with all options', async () => {
+      mockfetchSuccess(null, fetchMock);
+      await exportRules({
+        excludeExportDetails: true,
+        filename: 'myFileName.ndjson',
+        ruleIds: ['mySuperRuleId', 'mySuperRuleId_II'],
+        signal: abortCtrl.signal,
+      });
+      expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_export', {
+        asResponse: true,
+        signal: abortCtrl.signal,
+        method: 'POST',
+        body: '{"objects":[{"rule_id":"mySuperRuleId"},{"rule_id":"mySuperRuleId_II"}]}',
+        query: {
+          exclude_export_details: true,
+          file_name: 'myFileName.ndjson',
+        },
+      });
+    });
+
+    test('happy path', async () => {
+      const blob: Blob = {
+        size: 89,
+        type: 'json',
+        arrayBuffer: jest.fn(),
+        slice: jest.fn(),
+        stream: jest.fn(),
+        text: jest.fn(),
+      } as Blob;
+      mockfetchSuccess(blob);
+      const resp = await exportRules({
+        ruleIds: ['mySuperRuleId', 'mySuperRuleId_II'],
+        signal: abortCtrl.signal,
+      });
+      expect(resp).toEqual(blob);
+    });
+
+    test('unhappy path', async () => {
+      mockfetchError();
+      try {
+        await exportRules({
+          ruleIds: ['mySuperRuleId', 'mySuperRuleId_II'],
+          signal: abortCtrl.signal,
+        });
+      } catch (exp) {
+        expect(exp).toBeInstanceOf(ToasterErrors);
+        expect(exp.message).toEqual('super mega error, it is not that bad');
+      }
+    });
+  });
+
+  describe('getRuleStatusById', () => {
+    beforeEach(() => {
+      mockKibanaServices.mockClear();
+      fetchMock.mockClear();
+      fetchMock.mockImplementation(() => ({
+        response: {
+          ok: true,
+          message: 'success',
+          text: 'success',
+        },
+        body: ruleMock,
+      }));
+    });
+    test('check parameter url, query', async () => {
+      mockfetchSuccess(null, fetchMock);
+
+      await getRuleStatusById({ id: 'mySuperRuleId', signal: abortCtrl.signal });
+      expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_find_statuses', {
+        asResponse: true,
+        query: {
+          ids: '["mySuperRuleId"]',
+        },
+        method: 'GET',
+        signal: abortCtrl.signal,
+      });
+    });
+    test('happy path', async () => {
+      const statusMock = {
+        myRule: {
+          current_status: {
+            alert_id: 'alertId',
+            status_date: 'mm/dd/yyyyTHH:MM:sssz',
+            status: 'succeeded',
+            last_failure_at: null,
+            last_success_at: 'mm/dd/yyyyTHH:MM:sssz',
+            last_failure_message: null,
+            last_success_message: 'it is a success',
+          },
+          failures: [],
+        },
+      };
+      mockfetchSuccess(statusMock);
+      const ruleResp = await getRuleStatusById({ id: 'mySuperRuleId', signal: abortCtrl.signal });
+      expect(ruleResp).toEqual(statusMock);
+    });
+    test('unhappy path', async () => {
+      mockfetchError();
+      try {
+        await getRuleStatusById({ id: 'mySuperRuleId', signal: abortCtrl.signal });
+      } catch (exp) {
+        expect(exp).toBeInstanceOf(ToasterErrors);
+        expect(exp.message).toEqual('super mega error, it is not that bad');
+      }
+    });
+  });
+
+  describe('fetchTags', () => {
+    beforeEach(() => {
+      mockKibanaServices.mockClear();
+      fetchMock.mockClear();
+      fetchMock.mockImplementation(() => ({
+        response: {
+          ok: true,
+          message: 'success',
+          text: 'success',
+        },
+        body: ruleMock,
+      }));
+    });
+    test('check parameter url when fetching tags', async () => {
+      mockfetchSuccess(null, fetchMock);
+
+      await fetchTags({ signal: abortCtrl.signal });
+      expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/tags', {
+        asResponse: true,
+        signal: abortCtrl.signal,
+        method: 'GET',
+      });
+    });
+    test('happy path', async () => {
+      mockfetchSuccess(['hello', 'tags']);
+      const resp = await fetchTags({ signal: abortCtrl.signal });
+      expect(resp).toEqual(['hello', 'tags']);
+    });
+    test('unhappy path', async () => {
+      mockfetchError();
+      try {
+        await fetchTags({ signal: abortCtrl.signal });
+      } catch (exp) {
+        expect(exp).toBeInstanceOf(ToasterErrors);
+        expect(exp.message).toEqual('super mega error, it is not that bad');
+      }
+    });
+  });
+
+  describe('getPrePackagedRulesStatus', () => {
+    beforeEach(() => {
+      mockKibanaServices.mockClear();
+      fetchMock.mockClear();
+      fetchMock.mockImplementation(() => ({
+        response: {
+          ok: true,
+          message: 'success',
+          text: 'success',
+        },
+        body: ruleMock,
+      }));
+    });
+    test('check parameter url when fetching tags', async () => {
+      mockfetchSuccess(null, fetchMock);
+
+      await getPrePackagedRulesStatus({ signal: abortCtrl.signal });
+      expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/prepackaged/_status', {
+        asResponse: true,
+        signal: abortCtrl.signal,
+        method: 'GET',
+      });
+    });
+    test('happy path', async () => {
+      const prePackagesRulesStatus = {
+        rules_custom_installed: 33,
+        rules_installed: 12,
+        rules_not_installed: 0,
+        rules_not_updated: 2,
+      };
+      mockfetchSuccess(prePackagesRulesStatus);
+      const resp = await getPrePackagedRulesStatus({ signal: abortCtrl.signal });
+      expect(resp).toEqual(prePackagesRulesStatus);
+    });
+    test('unhappy path', async () => {
+      mockfetchError();
+      try {
+        await getPrePackagedRulesStatus({ signal: abortCtrl.signal });
+      } catch (exp) {
+        expect(exp).toBeInstanceOf(ToasterErrors);
+        expect(exp.message).toEqual('super mega error, it is not that bad');
+      }
+    });
+  });
+});
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/fetch_index_patterns.test.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/fetch_index_patterns.test.tsx
new file mode 100644
index 0000000000000..cad78ac565903
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/fetch_index_patterns.test.tsx
@@ -0,0 +1,460 @@
+/*
+ * 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 { renderHook, act } from '@testing-library/react-hooks';
+
+import { defaultIndexPattern } from '../../../../default_index_pattern';
+import { useApolloClient } from '../../../utils/apollo_context';
+import { mocksSource } from '../../source/mock';
+
+import { useFetchIndexPatterns, Return } from './fetch_index_patterns';
+
+const mockUseApolloClient = useApolloClient as jest.Mock;
+jest.mock('../../../utils/apollo_context');
+
+describe('useFetchIndexPatterns', () => {
+  beforeEach(() => {
+    mockUseApolloClient.mockClear();
+  });
+  test('happy path', async () => {
+    await act(async () => {
+      mockUseApolloClient.mockImplementation(() => ({
+        query: () => Promise.resolve(mocksSource[0].result),
+      }));
+      const { result, waitForNextUpdate } = renderHook<unknown, Return>(() =>
+        useFetchIndexPatterns(defaultIndexPattern)
+      );
+      await waitForNextUpdate();
+      await waitForNextUpdate();
+
+      expect(result.current).toEqual([
+        {
+          browserFields: {
+            base: {
+              fields: {
+                '@timestamp': {
+                  category: 'base',
+                  description:
+                    'Date/time when the event originated. For log events this is the date/time when the event was generated, and not when it was read. Required field for all events.',
+                  example: '2016-05-23T08:05:34.853Z',
+                  format: '',
+                  indexes: ['auditbeat', 'filebeat', 'packetbeat'],
+                  name: '@timestamp',
+                  searchable: true,
+                  type: 'date',
+                  aggregatable: true,
+                },
+              },
+            },
+            agent: {
+              fields: {
+                'agent.ephemeral_id': {
+                  category: 'agent',
+                  description:
+                    'Ephemeral identifier of this agent (if one exists). This id normally changes across restarts, but `agent.id` does not.',
+                  example: '8a4f500f',
+                  format: '',
+                  indexes: ['auditbeat', 'filebeat', 'packetbeat'],
+                  name: 'agent.ephemeral_id',
+                  searchable: true,
+                  type: 'string',
+                  aggregatable: true,
+                },
+                'agent.hostname': {
+                  category: 'agent',
+                  description: null,
+                  example: null,
+                  format: '',
+                  indexes: ['auditbeat', 'filebeat', 'packetbeat'],
+                  name: 'agent.hostname',
+                  searchable: true,
+                  type: 'string',
+                  aggregatable: true,
+                },
+                'agent.id': {
+                  category: 'agent',
+                  description:
+                    'Unique identifier of this agent (if one exists). Example: For Beats this would be beat.id.',
+                  example: '8a4f500d',
+                  format: '',
+                  indexes: ['auditbeat', 'filebeat', 'packetbeat'],
+                  name: 'agent.id',
+                  searchable: true,
+                  type: 'string',
+                  aggregatable: true,
+                },
+                'agent.name': {
+                  category: 'agent',
+                  description:
+                    'Name of the agent. This is a name that can be given to an agent. This can be helpful if for example two Filebeat instances are running on the same host but a human readable separation is needed on which Filebeat instance data is coming from. If no name is given, the name is often left empty.',
+                  example: 'foo',
+                  format: '',
+                  indexes: ['auditbeat', 'filebeat', 'packetbeat'],
+                  name: 'agent.name',
+                  searchable: true,
+                  type: 'string',
+                  aggregatable: true,
+                },
+              },
+            },
+            auditd: {
+              fields: {
+                'auditd.data.a0': {
+                  category: 'auditd',
+                  description: null,
+                  example: null,
+                  format: '',
+                  indexes: ['auditbeat'],
+                  name: 'auditd.data.a0',
+                  searchable: true,
+                  type: 'string',
+                  aggregatable: true,
+                },
+                'auditd.data.a1': {
+                  category: 'auditd',
+                  description: null,
+                  example: null,
+                  format: '',
+                  indexes: ['auditbeat'],
+                  name: 'auditd.data.a1',
+                  searchable: true,
+                  type: 'string',
+                  aggregatable: true,
+                },
+                'auditd.data.a2': {
+                  category: 'auditd',
+                  description: null,
+                  example: null,
+                  format: '',
+                  indexes: ['auditbeat'],
+                  name: 'auditd.data.a2',
+                  searchable: true,
+                  type: 'string',
+                  aggregatable: true,
+                },
+              },
+            },
+            client: {
+              fields: {
+                'client.address': {
+                  category: 'client',
+                  description:
+                    'Some event client addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket.  You should always store the raw address in the `.address` field. Then it should be duplicated to `.ip` or `.domain`, depending on which one it is.',
+                  example: null,
+                  format: '',
+                  indexes: ['auditbeat', 'filebeat', 'packetbeat'],
+                  name: 'client.address',
+                  searchable: true,
+                  type: 'string',
+                  aggregatable: true,
+                },
+                'client.bytes': {
+                  category: 'client',
+                  description: 'Bytes sent from the client to the server.',
+                  example: '184',
+                  format: '',
+                  indexes: ['auditbeat', 'filebeat', 'packetbeat'],
+                  name: 'client.bytes',
+                  searchable: true,
+                  type: 'number',
+                  aggregatable: true,
+                },
+                'client.domain': {
+                  category: 'client',
+                  description: 'Client domain.',
+                  example: null,
+                  format: '',
+                  indexes: ['auditbeat', 'filebeat', 'packetbeat'],
+                  name: 'client.domain',
+                  searchable: true,
+                  type: 'string',
+                  aggregatable: true,
+                },
+                'client.geo.country_iso_code': {
+                  category: 'client',
+                  description: 'Country ISO code.',
+                  example: 'CA',
+                  format: '',
+                  indexes: ['auditbeat', 'filebeat', 'packetbeat'],
+                  name: 'client.geo.country_iso_code',
+                  searchable: true,
+                  type: 'string',
+                  aggregatable: true,
+                },
+              },
+            },
+            cloud: {
+              fields: {
+                'cloud.account.id': {
+                  category: 'cloud',
+                  description:
+                    'The cloud account or organization id used to identify different entities in a multi-tenant environment. Examples: AWS account id, Google Cloud ORG Id, or other unique identifier.',
+                  example: '666777888999',
+                  format: '',
+                  indexes: ['auditbeat', 'filebeat', 'packetbeat'],
+                  name: 'cloud.account.id',
+                  searchable: true,
+                  type: 'string',
+                  aggregatable: true,
+                },
+                'cloud.availability_zone': {
+                  category: 'cloud',
+                  description: 'Availability zone in which this host is running.',
+                  example: 'us-east-1c',
+                  format: '',
+                  indexes: ['auditbeat', 'filebeat', 'packetbeat'],
+                  name: 'cloud.availability_zone',
+                  searchable: true,
+                  type: 'string',
+                  aggregatable: true,
+                },
+              },
+            },
+            container: {
+              fields: {
+                'container.id': {
+                  category: 'container',
+                  description: 'Unique container id.',
+                  example: null,
+                  format: '',
+                  indexes: ['auditbeat', 'filebeat', 'packetbeat'],
+                  name: 'container.id',
+                  searchable: true,
+                  type: 'string',
+                  aggregatable: true,
+                },
+                'container.image.name': {
+                  category: 'container',
+                  description: 'Name of the image the container was built on.',
+                  example: null,
+                  format: '',
+                  indexes: ['auditbeat', 'filebeat', 'packetbeat'],
+                  name: 'container.image.name',
+                  searchable: true,
+                  type: 'string',
+                  aggregatable: true,
+                },
+                'container.image.tag': {
+                  category: 'container',
+                  description: 'Container image tag.',
+                  example: null,
+                  format: '',
+                  indexes: ['auditbeat', 'filebeat', 'packetbeat'],
+                  name: 'container.image.tag',
+                  searchable: true,
+                  type: 'string',
+                  aggregatable: true,
+                },
+              },
+            },
+            destination: {
+              fields: {
+                'destination.address': {
+                  category: 'destination',
+                  description:
+                    'Some event destination addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket.  You should always store the raw address in the `.address` field. Then it should be duplicated to `.ip` or `.domain`, depending on which one it is.',
+                  example: null,
+                  format: '',
+                  indexes: ['auditbeat', 'filebeat', 'packetbeat'],
+                  name: 'destination.address',
+                  searchable: true,
+                  type: 'string',
+                  aggregatable: true,
+                },
+                'destination.bytes': {
+                  category: 'destination',
+                  description: 'Bytes sent from the destination to the source.',
+                  example: '184',
+                  format: '',
+                  indexes: ['auditbeat', 'filebeat', 'packetbeat'],
+                  name: 'destination.bytes',
+                  searchable: true,
+                  type: 'number',
+                  aggregatable: true,
+                },
+                'destination.domain': {
+                  category: 'destination',
+                  description: 'Destination domain.',
+                  example: null,
+                  format: '',
+                  indexes: ['auditbeat', 'filebeat', 'packetbeat'],
+                  name: 'destination.domain',
+                  searchable: true,
+                  type: 'string',
+                  aggregatable: true,
+                },
+                'destination.ip': {
+                  aggregatable: true,
+                  category: 'destination',
+                  description:
+                    'IP address of the destination. Can be one or multiple IPv4 or IPv6 addresses.',
+                  example: '',
+                  format: '',
+                  indexes: ['auditbeat', 'filebeat', 'packetbeat'],
+                  name: 'destination.ip',
+                  searchable: true,
+                  type: 'ip',
+                },
+                'destination.port': {
+                  aggregatable: true,
+                  category: 'destination',
+                  description: 'Port of the destination.',
+                  example: '',
+                  format: '',
+                  indexes: ['auditbeat', 'filebeat', 'packetbeat'],
+                  name: 'destination.port',
+                  searchable: true,
+                  type: 'long',
+                },
+              },
+            },
+            source: {
+              fields: {
+                'source.ip': {
+                  aggregatable: true,
+                  category: 'source',
+                  description:
+                    'IP address of the source. Can be one or multiple IPv4 or IPv6 addresses.',
+                  example: '',
+                  format: '',
+                  indexes: ['auditbeat', 'filebeat', 'packetbeat'],
+                  name: 'source.ip',
+                  searchable: true,
+                  type: 'ip',
+                },
+                'source.port': {
+                  aggregatable: true,
+                  category: 'source',
+                  description: 'Port of the source.',
+                  example: '',
+                  format: '',
+                  indexes: ['auditbeat', 'filebeat', 'packetbeat'],
+                  name: 'source.port',
+                  searchable: true,
+                  type: 'long',
+                },
+              },
+            },
+            event: {
+              fields: {
+                'event.end': {
+                  aggregatable: true,
+                  category: 'event',
+                  description:
+                    'event.end contains the date when the event ended or when the activity was last observed.',
+                  example: null,
+                  format: '',
+                  indexes: [
+                    'apm-*-transaction*',
+                    'auditbeat-*',
+                    'endgame-*',
+                    'filebeat-*',
+                    'packetbeat-*',
+                    'winlogbeat-*',
+                  ],
+                  name: 'event.end',
+                  searchable: true,
+                  type: 'date',
+                },
+              },
+            },
+          },
+          isLoading: false,
+          indices: [
+            'apm-*-transaction*',
+            'auditbeat-*',
+            'endgame-*',
+            'filebeat-*',
+            'packetbeat-*',
+            'winlogbeat-*',
+          ],
+          indicesExists: true,
+          indexPatterns: {
+            fields: [
+              { name: '@timestamp', searchable: true, type: 'date', aggregatable: true },
+              { name: 'agent.ephemeral_id', searchable: true, type: 'string', aggregatable: true },
+              { name: 'agent.hostname', searchable: true, type: 'string', aggregatable: true },
+              { name: 'agent.id', searchable: true, type: 'string', aggregatable: true },
+              { name: 'agent.name', searchable: true, type: 'string', aggregatable: true },
+              { name: 'auditd.data.a0', searchable: true, type: 'string', aggregatable: true },
+              { name: 'auditd.data.a1', searchable: true, type: 'string', aggregatable: true },
+              { name: 'auditd.data.a2', searchable: true, type: 'string', aggregatable: true },
+              { name: 'client.address', searchable: true, type: 'string', aggregatable: true },
+              { name: 'client.bytes', searchable: true, type: 'number', aggregatable: true },
+              { name: 'client.domain', searchable: true, type: 'string', aggregatable: true },
+              {
+                name: 'client.geo.country_iso_code',
+                searchable: true,
+                type: 'string',
+                aggregatable: true,
+              },
+              { name: 'cloud.account.id', searchable: true, type: 'string', aggregatable: true },
+              {
+                name: 'cloud.availability_zone',
+                searchable: true,
+                type: 'string',
+                aggregatable: true,
+              },
+              { name: 'container.id', searchable: true, type: 'string', aggregatable: true },
+              {
+                name: 'container.image.name',
+                searchable: true,
+                type: 'string',
+                aggregatable: true,
+              },
+              { name: 'container.image.tag', searchable: true, type: 'string', aggregatable: true },
+              { name: 'destination.address', searchable: true, type: 'string', aggregatable: true },
+              { name: 'destination.bytes', searchable: true, type: 'number', aggregatable: true },
+              { name: 'destination.domain', searchable: true, type: 'string', aggregatable: true },
+              { name: 'destination.ip', searchable: true, type: 'ip', aggregatable: true },
+              { name: 'destination.port', searchable: true, type: 'long', aggregatable: true },
+              { name: 'source.ip', searchable: true, type: 'ip', aggregatable: true },
+              { name: 'source.port', searchable: true, type: 'long', aggregatable: true },
+              { name: 'event.end', searchable: true, type: 'date', aggregatable: true },
+            ],
+            title: 'apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,packetbeat-*,winlogbeat-*',
+          },
+        },
+        result.current[1],
+      ]);
+    });
+  });
+
+  test('unhappy path', async () => {
+    await act(async () => {
+      mockUseApolloClient.mockImplementation(() => ({
+        query: () => Promise.reject(new Error('Something went wrong')),
+      }));
+      const { result, waitForNextUpdate } = renderHook<unknown, Return>(() =>
+        useFetchIndexPatterns(defaultIndexPattern)
+      );
+
+      await waitForNextUpdate();
+      await waitForNextUpdate();
+
+      expect(result.current).toEqual([
+        {
+          browserFields: {},
+          indexPatterns: {
+            fields: [],
+            title: '',
+          },
+          indices: [
+            'apm-*-transaction*',
+            'auditbeat-*',
+            'endgame-*',
+            'filebeat-*',
+            'packetbeat-*',
+            'winlogbeat-*',
+          ],
+          indicesExists: false,
+          isLoading: false,
+        },
+        result.current[1],
+      ]);
+    });
+  });
+});
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/fetch_index_patterns.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/fetch_index_patterns.tsx
index d376a1d6ad178..b7ad41b8ba1bb 100644
--- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/fetch_index_patterns.tsx
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/fetch_index_patterns.tsx
@@ -29,7 +29,7 @@ interface FetchIndexPatternReturn {
   indexPatterns: IIndexPattern;
 }
 
-type Return = [FetchIndexPatternReturn, Dispatch<SetStateAction<string[]>>];
+export type Return = [FetchIndexPatternReturn, Dispatch<SetStateAction<string[]>>];
 
 export const useFetchIndexPatterns = (defaultIndices: string[] = []): Return => {
   const apolloClient = useApolloClient();
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/mock.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/mock.ts
new file mode 100644
index 0000000000000..51526c0ab9949
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/mock.ts
@@ -0,0 +1,139 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { NewRule, FetchRulesResponse, Rule } from './types';
+
+export const ruleMock: NewRule = {
+  description: 'some desc',
+  enabled: true,
+  false_positives: [],
+  filters: [],
+  from: 'now-360s',
+  index: [
+    'apm-*-transaction*',
+    'auditbeat-*',
+    'endgame-*',
+    'filebeat-*',
+    'packetbeat-*',
+    'winlogbeat-*',
+  ],
+  interval: '5m',
+  rule_id: 'bbd3106e-b4b5-4d7c-a1a2-47531d6a2baf',
+  language: 'kuery',
+  risk_score: 75,
+  name: 'Test rule',
+  query: "user.email: 'root@elastic.co'",
+  references: [],
+  severity: 'high',
+  tags: ['APM'],
+  to: 'now',
+  type: 'query',
+  threat: [],
+};
+
+export const savedRuleMock: Rule = {
+  created_at: 'mm/dd/yyyyTHH:MM:sssz',
+  created_by: 'mockUser',
+  description: 'some desc',
+  enabled: true,
+  false_positives: [],
+  filters: [],
+  from: 'now-360s',
+  id: '12345678987654321',
+  index: [
+    'apm-*-transaction*',
+    'auditbeat-*',
+    'endgame-*',
+    'filebeat-*',
+    'packetbeat-*',
+    'winlogbeat-*',
+  ],
+  interval: '5m',
+  immutable: false,
+  rule_id: 'bbd3106e-b4b5-4d7c-a1a2-47531d6a2baf',
+  language: 'kuery',
+  risk_score: 75,
+  name: 'Test rule',
+  max_signals: 100,
+  query: "user.email: 'root@elastic.co'",
+  references: [],
+  severity: 'high',
+  tags: ['APM'],
+  to: 'now',
+  type: 'query',
+  threat: [],
+  updated_at: 'mm/dd/yyyyTHH:MM:sssz',
+  updated_by: 'mockUser',
+};
+
+export const rulesMock: FetchRulesResponse = {
+  page: 1,
+  perPage: 2,
+  total: 2,
+  data: [
+    {
+      created_at: '2020-02-14T19:49:28.178Z',
+      updated_at: '2020-02-14T19:49:28.320Z',
+      created_by: 'elastic',
+      description:
+        'Elastic Endpoint detected Credential Dumping. Click the Elastic Endpoint icon in the event.module column or the link in the rule.reference column in the External Alerts tab of the SIEM Detections page for additional information.',
+      enabled: false,
+      false_positives: [],
+      from: 'now-660s',
+      id: '80c59768-8e1f-400e-908e-7b25c4ce29c3',
+      immutable: true,
+      index: ['endgame-*'],
+      interval: '10m',
+      rule_id: '571afc56-5ed9-465d-a2a9-045f099f6e7e',
+      language: 'kuery',
+      output_index: '.siem-signals-default',
+      max_signals: 100,
+      risk_score: 73,
+      name: 'Credential Dumping - Detected - Elastic Endpoint',
+      query:
+        'event.kind:alert and event.module:endgame and event.action:cred_theft_event and endgame.metadata.type:detection',
+      filters: [],
+      references: [],
+      severity: 'high',
+      updated_by: 'elastic',
+      tags: ['Elastic', 'Endpoint'],
+      to: 'now',
+      type: 'query',
+      threat: [],
+      version: 1,
+    },
+    {
+      created_at: '2020-02-14T19:49:28.189Z',
+      updated_at: '2020-02-14T19:49:28.326Z',
+      created_by: 'elastic',
+      description:
+        'Elastic Endpoint detected an Adversary Behavior. Click the Elastic Endpoint icon in the event.module column or the link in the rule.reference column in the External Alerts tab of the SIEM Detections page for additional information.',
+      enabled: false,
+      false_positives: [],
+      from: 'now-660s',
+      id: '2e846086-bd64-4dbc-9c56-42b46b5b2c8c',
+      immutable: true,
+      index: ['endgame-*'],
+      interval: '10m',
+      rule_id: '77a3c3df-8ec4-4da4-b758-878f551dee69',
+      language: 'kuery',
+      output_index: '.siem-signals-default',
+      max_signals: 100,
+      risk_score: 47,
+      name: 'Adversary Behavior - Detected - Elastic Endpoint',
+      query: 'event.kind:alert and event.module:endgame and event.action:rules_engine_event',
+      filters: [],
+      references: [],
+      severity: 'medium',
+      updated_by: 'elastic',
+      tags: ['Elastic', 'Endpoint'],
+      to: 'now',
+      type: 'query',
+      threat: [],
+      version: 1,
+    },
+  ],
+};
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/persist_rule.test.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/persist_rule.test.tsx
new file mode 100644
index 0000000000000..1bf21623992e6
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/persist_rule.test.tsx
@@ -0,0 +1,44 @@
+/*
+ * 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 { renderHook, act } from '@testing-library/react-hooks';
+
+import { usePersistRule, ReturnPersistRule } from './persist_rule';
+import { ruleMock } from './mock';
+
+jest.mock('./api');
+
+describe('usePersistRule', () => {
+  test('init', async () => {
+    const { result } = renderHook<unknown, ReturnPersistRule>(() => usePersistRule());
+
+    expect(result.current).toEqual([{ isLoading: false, isSaved: false }, result.current[1]]);
+  });
+
+  test('saving rule with isLoading === true', async () => {
+    await act(async () => {
+      const { result, rerender, waitForNextUpdate } = renderHook<void, ReturnPersistRule>(() =>
+        usePersistRule()
+      );
+      await waitForNextUpdate();
+      result.current[1](ruleMock);
+      rerender();
+      expect(result.current).toEqual([{ isLoading: true, isSaved: false }, result.current[1]]);
+    });
+  });
+
+  test('saved rule with isSaved === true', async () => {
+    await act(async () => {
+      const { result, waitForNextUpdate } = renderHook<void, ReturnPersistRule>(() =>
+        usePersistRule()
+      );
+      await waitForNextUpdate();
+      result.current[1](ruleMock);
+      await waitForNextUpdate();
+      expect(result.current).toEqual([{ isLoading: false, isSaved: true }, result.current[1]]);
+    });
+  });
+});
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/persist_rule.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/persist_rule.tsx
index ea03c34ec31ba..e720a1e70f153 100644
--- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/persist_rule.tsx
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/persist_rule.tsx
@@ -18,9 +18,9 @@ interface PersistRuleReturn {
   isSaved: boolean;
 }
 
-type Return = [PersistRuleReturn, Dispatch<NewRule | null>];
+export type ReturnPersistRule = [PersistRuleReturn, Dispatch<NewRule | null>];
 
-export const usePersistRule = (): Return => {
+export const usePersistRule = (): ReturnPersistRule => {
   const [rule, setRule] = useState<NewRule | null>(null);
   const [isSaved, setIsSaved] = useState(false);
   const [isLoading, setIsLoading] = useState(false);
@@ -35,7 +35,6 @@ export const usePersistRule = (): Return => {
         try {
           setIsLoading(true);
           await persistRule({ rule, signal: abortCtrl.signal });
-
           if (isSubscribed) {
             setIsSaved(true);
           }
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts
index 0aaffb7b86b28..ff49bb8a8c3a2 100644
--- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts
@@ -64,7 +64,6 @@ export const RuleSchema = t.intersection([
     language: t.string,
     name: t.string,
     max_signals: t.number,
-    meta: MetaRule,
     query: t.string,
     references: t.array(t.string),
     risk_score: t.number,
@@ -80,6 +79,7 @@ export const RuleSchema = t.intersection([
   t.partial({
     last_failure_at: t.string,
     last_failure_message: t.string,
+    meta: MetaRule,
     output_index: t.string,
     saved_id: t.string,
     status: t.string,
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_pre_packaged_rules.test.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_pre_packaged_rules.test.tsx
new file mode 100644
index 0000000000000..426a1ab9238dc
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_pre_packaged_rules.test.tsx
@@ -0,0 +1,267 @@
+/*
+ * 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 { renderHook, act } from '@testing-library/react-hooks';
+import { ReturnPrePackagedRules, usePrePackagedRules } from './use_pre_packaged_rules';
+import * as api from './api';
+
+jest.mock('./api');
+
+describe('usePersistRule', () => {
+  beforeEach(() => {
+    jest.clearAllMocks();
+    jest.restoreAllMocks();
+  });
+
+  test('init', async () => {
+    await act(async () => {
+      const { result, waitForNextUpdate } = renderHook<unknown, ReturnPrePackagedRules>(() =>
+        usePrePackagedRules({
+          canUserCRUD: null,
+          hasIndexWrite: null,
+          hasManageApiKey: null,
+          isAuthenticated: null,
+          hasEncryptionKey: null,
+          isSignalIndexExists: null,
+        })
+      );
+
+      await waitForNextUpdate();
+
+      expect(result.current).toEqual({
+        createPrePackagedRules: null,
+        loading: true,
+        loadingCreatePrePackagedRules: false,
+        refetchPrePackagedRulesStatus: null,
+        rulesCustomInstalled: null,
+        rulesInstalled: null,
+        rulesNotInstalled: null,
+        rulesNotUpdated: null,
+      });
+    });
+  });
+
+  test('fetch getPrePackagedRulesStatus', async () => {
+    await act(async () => {
+      const { result, waitForNextUpdate } = renderHook<unknown, ReturnPrePackagedRules>(() =>
+        usePrePackagedRules({
+          canUserCRUD: null,
+          hasIndexWrite: null,
+          hasManageApiKey: null,
+          isAuthenticated: null,
+          hasEncryptionKey: null,
+          isSignalIndexExists: null,
+        })
+      );
+      await waitForNextUpdate();
+      await waitForNextUpdate();
+
+      expect(result.current).toEqual({
+        createPrePackagedRules: result.current.createPrePackagedRules,
+        loading: false,
+        loadingCreatePrePackagedRules: false,
+        refetchPrePackagedRulesStatus: result.current.refetchPrePackagedRulesStatus,
+        rulesCustomInstalled: 33,
+        rulesInstalled: 12,
+        rulesNotInstalled: 0,
+        rulesNotUpdated: 0,
+      });
+    });
+  });
+
+  test('happy path to createPrePackagedRules', async () => {
+    const spyOnCreatePrepackagedRules = jest.spyOn(api, 'createPrepackagedRules');
+    await act(async () => {
+      const { result, waitForNextUpdate } = renderHook<unknown, ReturnPrePackagedRules>(() =>
+        usePrePackagedRules({
+          canUserCRUD: true,
+          hasIndexWrite: true,
+          hasManageApiKey: true,
+          isAuthenticated: true,
+          hasEncryptionKey: true,
+          isSignalIndexExists: true,
+        })
+      );
+      await waitForNextUpdate();
+      await waitForNextUpdate();
+      let resp = null;
+      if (result.current.createPrePackagedRules) {
+        resp = await result.current.createPrePackagedRules();
+      }
+      expect(resp).toEqual(true);
+      expect(spyOnCreatePrepackagedRules).toHaveBeenCalled();
+      expect(result.current).toEqual({
+        createPrePackagedRules: result.current.createPrePackagedRules,
+        loading: false,
+        loadingCreatePrePackagedRules: false,
+        refetchPrePackagedRulesStatus: result.current.refetchPrePackagedRulesStatus,
+        rulesCustomInstalled: 33,
+        rulesInstalled: 12,
+        rulesNotInstalled: 0,
+        rulesNotUpdated: 0,
+      });
+    });
+  });
+
+  test('unhappy path to createPrePackagedRules', async () => {
+    const spyOnCreatePrepackagedRules = jest.spyOn(api, 'createPrepackagedRules');
+    spyOnCreatePrepackagedRules.mockImplementation(() => {
+      throw new Error('Something went wrong');
+    });
+    await act(async () => {
+      const { result, waitForNextUpdate } = renderHook<unknown, ReturnPrePackagedRules>(() =>
+        usePrePackagedRules({
+          canUserCRUD: true,
+          hasIndexWrite: true,
+          hasManageApiKey: true,
+          isAuthenticated: true,
+          hasEncryptionKey: true,
+          isSignalIndexExists: true,
+        })
+      );
+      await waitForNextUpdate();
+      await waitForNextUpdate();
+      let resp = null;
+      if (result.current.createPrePackagedRules) {
+        resp = await result.current.createPrePackagedRules();
+      }
+      expect(resp).toEqual(false);
+      expect(spyOnCreatePrepackagedRules).toHaveBeenCalled();
+    });
+  });
+
+  test('can NOT createPrePackagedRules because canUserCrud === false', async () => {
+    await act(async () => {
+      const { result, waitForNextUpdate } = renderHook<unknown, ReturnPrePackagedRules>(() =>
+        usePrePackagedRules({
+          canUserCRUD: false,
+          hasIndexWrite: true,
+          hasManageApiKey: true,
+          isAuthenticated: true,
+          hasEncryptionKey: true,
+          isSignalIndexExists: true,
+        })
+      );
+      await waitForNextUpdate();
+      await waitForNextUpdate();
+      let resp = null;
+      if (result.current.createPrePackagedRules) {
+        resp = await result.current.createPrePackagedRules();
+      }
+      expect(resp).toEqual(false);
+    });
+  });
+
+  test('can NOT createPrePackagedRules because hasIndexWrite === false', async () => {
+    await act(async () => {
+      const { result, waitForNextUpdate } = renderHook<unknown, ReturnPrePackagedRules>(() =>
+        usePrePackagedRules({
+          canUserCRUD: true,
+          hasIndexWrite: false,
+          hasManageApiKey: true,
+          isAuthenticated: true,
+          hasEncryptionKey: true,
+          isSignalIndexExists: true,
+        })
+      );
+      await waitForNextUpdate();
+      await waitForNextUpdate();
+      let resp = null;
+      if (result.current.createPrePackagedRules) {
+        resp = await result.current.createPrePackagedRules();
+      }
+      expect(resp).toEqual(false);
+    });
+  });
+
+  test('can NOT createPrePackagedRules because hasManageApiKey === false', async () => {
+    await act(async () => {
+      const { result, waitForNextUpdate } = renderHook<unknown, ReturnPrePackagedRules>(() =>
+        usePrePackagedRules({
+          canUserCRUD: true,
+          hasIndexWrite: true,
+          hasManageApiKey: false,
+          isAuthenticated: true,
+          hasEncryptionKey: true,
+          isSignalIndexExists: true,
+        })
+      );
+      await waitForNextUpdate();
+      await waitForNextUpdate();
+      let resp = null;
+      if (result.current.createPrePackagedRules) {
+        resp = await result.current.createPrePackagedRules();
+      }
+      expect(resp).toEqual(false);
+    });
+  });
+
+  test('can NOT createPrePackagedRules because isAuthenticated === false', async () => {
+    await act(async () => {
+      const { result, waitForNextUpdate } = renderHook<unknown, ReturnPrePackagedRules>(() =>
+        usePrePackagedRules({
+          canUserCRUD: true,
+          hasIndexWrite: true,
+          hasManageApiKey: true,
+          isAuthenticated: false,
+          hasEncryptionKey: true,
+          isSignalIndexExists: true,
+        })
+      );
+      await waitForNextUpdate();
+      await waitForNextUpdate();
+      let resp = null;
+      if (result.current.createPrePackagedRules) {
+        resp = await result.current.createPrePackagedRules();
+      }
+      expect(resp).toEqual(false);
+    });
+  });
+
+  test('can NOT createPrePackagedRules because hasEncryptionKey === false', async () => {
+    await act(async () => {
+      const { result, waitForNextUpdate } = renderHook<unknown, ReturnPrePackagedRules>(() =>
+        usePrePackagedRules({
+          canUserCRUD: true,
+          hasIndexWrite: true,
+          hasManageApiKey: true,
+          isAuthenticated: true,
+          hasEncryptionKey: false,
+          isSignalIndexExists: true,
+        })
+      );
+      await waitForNextUpdate();
+      await waitForNextUpdate();
+      let resp = null;
+      if (result.current.createPrePackagedRules) {
+        resp = await result.current.createPrePackagedRules();
+      }
+      expect(resp).toEqual(false);
+    });
+  });
+
+  test('can NOT createPrePackagedRules because isSignalIndexExists === false', async () => {
+    await act(async () => {
+      const { result, waitForNextUpdate } = renderHook<unknown, ReturnPrePackagedRules>(() =>
+        usePrePackagedRules({
+          canUserCRUD: true,
+          hasIndexWrite: true,
+          hasManageApiKey: true,
+          isAuthenticated: true,
+          hasEncryptionKey: true,
+          isSignalIndexExists: false,
+        })
+      );
+      await waitForNextUpdate();
+      await waitForNextUpdate();
+      let resp = null;
+      if (result.current.createPrePackagedRules) {
+        resp = await result.current.createPrePackagedRules();
+      }
+      expect(resp).toEqual(false);
+    });
+  });
+});
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_pre_packaged_rules.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_pre_packaged_rules.tsx
index d77d6283692a2..04d7e3ef67da4 100644
--- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_pre_packaged_rules.tsx
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_pre_packaged_rules.tsx
@@ -13,7 +13,7 @@ import * as i18n from './translations';
 
 type Func = () => void;
 export type CreatePreBuiltRules = () => Promise<boolean>;
-interface Return {
+export interface ReturnPrePackagedRules {
   createPrePackagedRules: null | CreatePreBuiltRules;
   loading: boolean;
   loadingCreatePrePackagedRules: boolean;
@@ -50,10 +50,10 @@ export const usePrePackagedRules = ({
   isAuthenticated,
   hasEncryptionKey,
   isSignalIndexExists,
-}: UsePrePackagedRuleProps): Return => {
+}: UsePrePackagedRuleProps): ReturnPrePackagedRules => {
   const [rulesStatus, setRuleStatus] = useState<
     Pick<
-      Return,
+      ReturnPrePackagedRules,
       | 'createPrePackagedRules'
       | 'refetchPrePackagedRulesStatus'
       | 'rulesCustomInstalled'
@@ -167,6 +167,8 @@ export const usePrePackagedRules = ({
                 }, 300);
               timeoutId = reFetch();
             }
+          } else {
+            resolve(false);
           }
         } catch (error) {
           if (isSubscribed) {
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rule.test.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rule.test.tsx
new file mode 100644
index 0000000000000..e0bf2c4907370
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rule.test.tsx
@@ -0,0 +1,84 @@
+/*
+ * 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 { renderHook, act } from '@testing-library/react-hooks';
+import { useRule, ReturnRule } from './use_rule';
+import * as api from './api';
+
+jest.mock('./api');
+
+describe('useRule', () => {
+  test('init', async () => {
+    await act(async () => {
+      const { result, waitForNextUpdate } = renderHook<string, ReturnRule>(() =>
+        useRule('myOwnRuleID')
+      );
+      await waitForNextUpdate();
+      expect(result.current).toEqual([true, null]);
+    });
+  });
+
+  test('fetch rule', async () => {
+    await act(async () => {
+      const { result, waitForNextUpdate } = renderHook<string, ReturnRule>(() =>
+        useRule('myOwnRuleID')
+      );
+      await waitForNextUpdate();
+      await waitForNextUpdate();
+      expect(result.current).toEqual([
+        false,
+        {
+          created_at: 'mm/dd/yyyyTHH:MM:sssz',
+          created_by: 'mockUser',
+          description: 'some desc',
+          enabled: true,
+          false_positives: [],
+          filters: [],
+          from: 'now-360s',
+          id: '12345678987654321',
+          immutable: false,
+          index: [
+            'apm-*-transaction*',
+            'auditbeat-*',
+            'endgame-*',
+            'filebeat-*',
+            'packetbeat-*',
+            'winlogbeat-*',
+          ],
+          interval: '5m',
+          language: 'kuery',
+          name: 'Test rule',
+          max_signals: 100,
+          query: "user.email: 'root@elastic.co'",
+          references: [],
+          risk_score: 75,
+          rule_id: 'bbd3106e-b4b5-4d7c-a1a2-47531d6a2baf',
+          severity: 'high',
+          tags: ['APM'],
+          threat: [],
+          to: 'now',
+          type: 'query',
+          updated_at: 'mm/dd/yyyyTHH:MM:sssz',
+          updated_by: 'mockUser',
+        },
+      ]);
+    });
+  });
+
+  test('fetch a new rule', async () => {
+    const spyOnfetchRuleById = jest.spyOn(api, 'fetchRuleById');
+    await act(async () => {
+      const { rerender, waitForNextUpdate } = renderHook<string, ReturnRule>(id => useRule(id), {
+        initialProps: 'myOwnRuleID',
+      });
+      await waitForNextUpdate();
+      await waitForNextUpdate();
+      rerender('newRuleId');
+      await waitForNextUpdate();
+      expect(spyOnfetchRuleById).toHaveBeenCalledTimes(2);
+    });
+  });
+});
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rule.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rule.tsx
index 22ba86cd09f74..ab08bd39688ce 100644
--- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rule.tsx
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rule.tsx
@@ -12,7 +12,7 @@ import { fetchRuleById } from './api';
 import * as i18n from './translations';
 import { Rule } from './types';
 
-type Return = [boolean, Rule | null];
+export type ReturnRule = [boolean, Rule | null];
 
 /**
  * Hook for using to get a Rule from the Detection Engine API
@@ -20,7 +20,7 @@ type Return = [boolean, Rule | null];
  * @param id desired Rule ID's (not rule_id)
  *
  */
-export const useRule = (id: string | undefined): Return => {
+export const useRule = (id: string | undefined): ReturnRule => {
   const [rule, setRule] = useState<Rule | null>(null);
   const [loading, setLoading] = useState(true);
   const [, dispatchToaster] = useStateToaster();
@@ -36,7 +36,6 @@ export const useRule = (id: string | undefined): Return => {
           id: idToFetch,
           signal: abortCtrl.signal,
         });
-
         if (isSubscribed) {
           setRule(ruleResponse);
         }
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rule_status.test.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rule_status.test.tsx
new file mode 100644
index 0000000000000..25011adcfe98b
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rule_status.test.tsx
@@ -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 { renderHook, act } from '@testing-library/react-hooks';
+import { useRuleStatus, ReturnRuleStatus } from './use_rule_status';
+import * as api from './api';
+
+jest.mock('./api');
+
+describe('useRuleStatus', () => {
+  test('init', async () => {
+    await act(async () => {
+      const { result, waitForNextUpdate } = renderHook<string, ReturnRuleStatus>(() =>
+        useRuleStatus('myOwnRuleID')
+      );
+      await waitForNextUpdate();
+      expect(result.current).toEqual([true, null, null]);
+    });
+  });
+
+  test('fetch rule status', async () => {
+    await act(async () => {
+      const { result, waitForNextUpdate } = renderHook<string, ReturnRuleStatus>(() =>
+        useRuleStatus('myOwnRuleID')
+      );
+      await waitForNextUpdate();
+      await waitForNextUpdate();
+      expect(result.current).toEqual([
+        false,
+        {
+          current_status: {
+            alert_id: 'alertId',
+            last_failure_at: null,
+            last_failure_message: null,
+            last_success_at: 'mm/dd/yyyyTHH:MM:sssz',
+            last_success_message: 'it is a success',
+            status: 'succeeded',
+            status_date: 'mm/dd/yyyyTHH:MM:sssz',
+          },
+          failures: [],
+        },
+        result.current[2],
+      ]);
+    });
+  });
+
+  test('re-fetch rule status', async () => {
+    const spyOngetRuleStatusById = jest.spyOn(api, 'getRuleStatusById');
+    await act(async () => {
+      const { result, waitForNextUpdate } = renderHook<string, ReturnRuleStatus>(() =>
+        useRuleStatus('myOwnRuleID')
+      );
+      await waitForNextUpdate();
+      await waitForNextUpdate();
+      if (result.current[2]) {
+        result.current[2]('myOwnRuleID');
+      }
+      await waitForNextUpdate();
+      expect(spyOngetRuleStatusById).toHaveBeenCalledTimes(2);
+    });
+  });
+});
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rule_status.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rule_status.tsx
index 466c2cddac97d..fcf95ac061ba3 100644
--- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rule_status.tsx
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rule_status.tsx
@@ -13,7 +13,7 @@ import * as i18n from './translations';
 import { RuleStatus } from './types';
 
 type Func = (ruleId: string) => void;
-type Return = [boolean, RuleStatus | null, Func | null];
+export type ReturnRuleStatus = [boolean, RuleStatus | null, Func | null];
 
 /**
  * Hook for using to get a Rule from the Detection Engine API
@@ -21,7 +21,7 @@ type Return = [boolean, RuleStatus | null, Func | null];
  * @param id desired Rule ID's (not rule_id)
  *
  */
-export const useRuleStatus = (id: string | undefined | null): Return => {
+export const useRuleStatus = (id: string | undefined | null): ReturnRuleStatus => {
   const [ruleStatus, setRuleStatus] = useState<RuleStatus | null>(null);
   const fetchRuleStatus = useRef<Func | null>(null);
   const [loading, setLoading] = useState(true);
@@ -34,6 +34,7 @@ export const useRuleStatus = (id: string | undefined | null): Return => {
     const fetchData = async (idToFetch: string) => {
       try {
         setLoading(true);
+
         const ruleStatusResponse = await getRuleStatusById({
           id: idToFetch,
           signal: abortCtrl.signal,
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rules.test.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rules.test.tsx
new file mode 100644
index 0000000000000..b369d3a50730d
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rules.test.tsx
@@ -0,0 +1,216 @@
+/*
+ * 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 { renderHook, act } from '@testing-library/react-hooks';
+import { useRules, ReturnRules } from './use_rules';
+import * as api from './api';
+import { PaginationOptions, FilterOptions } from '.';
+
+jest.mock('./api');
+
+describe('useRules', () => {
+  beforeEach(() => {
+    jest.resetAllMocks();
+  });
+  test('init', async () => {
+    await act(async () => {
+      const { result, waitForNextUpdate } = renderHook<
+        [PaginationOptions, FilterOptions],
+        ReturnRules
+      >(props =>
+        useRules(
+          {
+            page: 1,
+            perPage: 10,
+            total: 100,
+          },
+          {
+            filter: '',
+            sortField: 'created_at',
+            sortOrder: 'desc',
+          }
+        )
+      );
+      await waitForNextUpdate();
+      expect(result.current).toEqual([
+        true,
+        {
+          data: [],
+          page: 1,
+          perPage: 20,
+          total: 0,
+        },
+        null,
+      ]);
+    });
+  });
+
+  test('fetch rules', async () => {
+    await act(async () => {
+      const { result, waitForNextUpdate } = renderHook<
+        [PaginationOptions, FilterOptions],
+        ReturnRules
+      >(() =>
+        useRules(
+          {
+            page: 1,
+            perPage: 10,
+            total: 100,
+          },
+          {
+            filter: '',
+            sortField: 'created_at',
+            sortOrder: 'desc',
+          }
+        )
+      );
+      await waitForNextUpdate();
+      await waitForNextUpdate();
+      expect(result.current).toEqual([
+        false,
+        {
+          data: [
+            {
+              created_at: '2020-02-14T19:49:28.178Z',
+              created_by: 'elastic',
+              description:
+                'Elastic Endpoint detected Credential Dumping. Click the Elastic Endpoint icon in the event.module column or the link in the rule.reference column in the External Alerts tab of the SIEM Detections page for additional information.',
+              enabled: false,
+              false_positives: [],
+              filters: [],
+              from: 'now-660s',
+              id: '80c59768-8e1f-400e-908e-7b25c4ce29c3',
+              immutable: true,
+              index: ['endgame-*'],
+              interval: '10m',
+              language: 'kuery',
+              max_signals: 100,
+              name: 'Credential Dumping - Detected - Elastic Endpoint',
+              output_index: '.siem-signals-default',
+              query:
+                'event.kind:alert and event.module:endgame and event.action:cred_theft_event and endgame.metadata.type:detection',
+              references: [],
+              risk_score: 73,
+              rule_id: '571afc56-5ed9-465d-a2a9-045f099f6e7e',
+              severity: 'high',
+              tags: ['Elastic', 'Endpoint'],
+              threat: [],
+              to: 'now',
+              type: 'query',
+              updated_at: '2020-02-14T19:49:28.320Z',
+              updated_by: 'elastic',
+              version: 1,
+            },
+            {
+              created_at: '2020-02-14T19:49:28.189Z',
+              created_by: 'elastic',
+              description:
+                'Elastic Endpoint detected an Adversary Behavior. Click the Elastic Endpoint icon in the event.module column or the link in the rule.reference column in the External Alerts tab of the SIEM Detections page for additional information.',
+              enabled: false,
+              false_positives: [],
+              filters: [],
+              from: 'now-660s',
+              id: '2e846086-bd64-4dbc-9c56-42b46b5b2c8c',
+              immutable: true,
+              index: ['endgame-*'],
+              interval: '10m',
+              language: 'kuery',
+              max_signals: 100,
+              name: 'Adversary Behavior - Detected - Elastic Endpoint',
+              output_index: '.siem-signals-default',
+              query:
+                'event.kind:alert and event.module:endgame and event.action:rules_engine_event',
+              references: [],
+              risk_score: 47,
+              rule_id: '77a3c3df-8ec4-4da4-b758-878f551dee69',
+              severity: 'medium',
+              tags: ['Elastic', 'Endpoint'],
+              threat: [],
+              to: 'now',
+              type: 'query',
+              updated_at: '2020-02-14T19:49:28.326Z',
+              updated_by: 'elastic',
+              version: 1,
+            },
+          ],
+          page: 1,
+          perPage: 2,
+          total: 2,
+        },
+        result.current[2],
+      ]);
+    });
+  });
+
+  test('re-fetch rules', async () => {
+    const spyOnfetchRules = jest.spyOn(api, 'fetchRules');
+    await act(async () => {
+      const { result, waitForNextUpdate } = renderHook<
+        [PaginationOptions, FilterOptions],
+        ReturnRules
+      >(id =>
+        useRules(
+          {
+            page: 1,
+            perPage: 10,
+            total: 100,
+          },
+          {
+            filter: '',
+            sortField: 'created_at',
+            sortOrder: 'desc',
+          }
+        )
+      );
+      await waitForNextUpdate();
+      await waitForNextUpdate();
+      if (result.current[2]) {
+        result.current[2]();
+      }
+      await waitForNextUpdate();
+      expect(spyOnfetchRules).toHaveBeenCalledTimes(2);
+    });
+  });
+
+  test('fetch rules if props changes', async () => {
+    const spyOnfetchRules = jest.spyOn(api, 'fetchRules');
+    await act(async () => {
+      const { rerender, waitForNextUpdate } = renderHook<
+        [PaginationOptions, FilterOptions],
+        ReturnRules
+      >(args => useRules(args[0], args[1]), {
+        initialProps: [
+          {
+            page: 1,
+            perPage: 10,
+            total: 100,
+          },
+          {
+            filter: '',
+            sortField: 'created_at',
+            sortOrder: 'desc',
+          },
+        ],
+      });
+      await waitForNextUpdate();
+      await waitForNextUpdate();
+      rerender([
+        {
+          page: 1,
+          perPage: 10,
+          total: 100,
+        },
+        {
+          filter: 'hello world',
+          sortField: 'created_at',
+          sortOrder: 'desc',
+        },
+      ]);
+      await waitForNextUpdate();
+      expect(spyOnfetchRules).toHaveBeenCalledTimes(2);
+    });
+  });
+});
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rules.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rules.tsx
index af6e437255acd..301a68dc6f445 100644
--- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rules.tsx
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rules.tsx
@@ -13,7 +13,7 @@ import { errorToToaster } from '../../../components/ml/api/error_to_toaster';
 import * as i18n from './translations';
 
 type Func = () => void;
-type Return = [boolean, FetchRulesResponse, Func | null];
+export type ReturnRules = [boolean, FetchRulesResponse, Func | null];
 
 /**
  * Hook for using the list of Rules from the Detection Engine API
@@ -21,7 +21,10 @@ type Return = [boolean, FetchRulesResponse, Func | null];
  * @param pagination desired pagination options (e.g. page/perPage)
  * @param filterOptions desired filters (e.g. filter/sortField/sortOrder)
  */
-export const useRules = (pagination: PaginationOptions, filterOptions: FilterOptions): Return => {
+export const useRules = (
+  pagination: PaginationOptions,
+  filterOptions: FilterOptions
+): ReturnRules => {
   const [rules, setRules] = useState<FetchRulesResponse>({
     page: 1,
     perPage: 20,
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_tags.test.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_tags.test.tsx
new file mode 100644
index 0000000000000..4a796efa5b0cb
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_tags.test.tsx
@@ -0,0 +1,29 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { renderHook, act } from '@testing-library/react-hooks';
+import { useTags, ReturnTags } from './use_tags';
+
+jest.mock('./api');
+
+describe('useTags', () => {
+  test('init', async () => {
+    await act(async () => {
+      const { result, waitForNextUpdate } = renderHook<unknown, ReturnTags>(() => useTags());
+      await waitForNextUpdate();
+      expect(result.current).toEqual([true, []]);
+    });
+  });
+
+  test('fetch tags', async () => {
+    await act(async () => {
+      const { result, waitForNextUpdate } = renderHook<unknown, ReturnTags>(() => useTags());
+      await waitForNextUpdate();
+      await waitForNextUpdate();
+      expect(result.current).toEqual([false, ['elastic', 'love', 'quality', 'code']]);
+    });
+  });
+});
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_tags.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_tags.tsx
index 1c961d530422a..196d4b1420561 100644
--- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_tags.tsx
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_tags.tsx
@@ -10,13 +10,13 @@ import { fetchTags } from './api';
 import { errorToToaster } from '../../../components/ml/api/error_to_toaster';
 import * as i18n from './translations';
 
-type Return = [boolean, string[]];
+export type ReturnTags = [boolean, string[]];
 
 /**
  * Hook for using the list of Tags from the Detection Engine API
  *
  */
-export const useTags = (): Return => {
+export const useTags = (): ReturnTags => {
   const [tags, setTags] = useState<string[]>([]);
   const [loading, setLoading] = useState(true);
   const [, dispatchToaster] = useStateToaster();
diff --git a/x-pack/legacy/plugins/siem/public/mock/hook_wrapper.tsx b/x-pack/legacy/plugins/siem/public/mock/hook_wrapper.tsx
index 292ddc036dcaf..70c76de01e95a 100644
--- a/x-pack/legacy/plugins/siem/public/mock/hook_wrapper.tsx
+++ b/x-pack/legacy/plugins/siem/public/mock/hook_wrapper.tsx
@@ -12,6 +12,7 @@ interface HookWrapperProps {
   // eslint-disable-next-line @typescript-eslint/no-explicit-any
   hookProps?: any;
 }
+
 export const HookWrapper = ({ hook, hookProps }: HookWrapperProps) => {
   const myHook = hook ? (hookProps ? hook(hookProps) : hook()) : null;
   return <div>{JSON.stringify(myHook)}</div>;

From 783663fa523b70329c3094e2703613f9a68ebfa7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mike=20C=C3=B4t=C3=A9?= <mikecote@users.noreply.github.com>
Date: Thu, 20 Feb 2020 10:46:28 -0500
Subject: [PATCH 100/174] Skip flaky alert details test (#58120)

* Skip flaky test

* Skip suite

* Skip suite
---
 .../functional_with_es_ssl/apps/triggers_actions_ui/details.ts | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts
index 3db4731f0adfb..86fc3d6cd6a6c 100644
--- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts
+++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts
@@ -18,7 +18,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
   const alerting = getService('alerting');
   const retry = getService('retry');
 
-  describe('Alert Details', function() {
+  // FLAKY: https://github.com/elastic/kibana/issues/57426
+  describe.skip('Alert Details', function() {
     describe('Header', function() {
       const testRunUuid = uuid.v4();
       before(async () => {

From 289b2fa611503ab198339f2bae26966a1aeffdfa Mon Sep 17 00:00:00 2001
From: Rachel Shen <rachelwshen@gmail.com>
Date: Thu, 20 Feb 2020 08:59:44 -0700
Subject: [PATCH 101/174] Clarify Precision function in Timelion Kibana
 (#58031)

* Closes issue 26100
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 src/plugins/timelion/server/series_functions/precision.js | 4 ++--
 x-pack/plugins/translations/translations/ja-JP.json       | 2 --
 x-pack/plugins/translations/translations/zh-CN.json       | 2 --
 3 files changed, 2 insertions(+), 6 deletions(-)

diff --git a/src/plugins/timelion/server/series_functions/precision.js b/src/plugins/timelion/server/series_functions/precision.js
index 756fb067f2335..71c15fdd46fdd 100644
--- a/src/plugins/timelion/server/series_functions/precision.js
+++ b/src/plugins/timelion/server/series_functions/precision.js
@@ -32,12 +32,12 @@ export default new Chainable('precision', {
       name: 'precision',
       types: ['number'],
       help: i18n.translate('timelion.help.functions.precision.args.precisionHelpText', {
-        defaultMessage: 'Number of digits to round each value to',
+        defaultMessage: 'The number of digits to truncate each value to',
       }),
     },
   ],
   help: i18n.translate('timelion.help.functions.precisionHelpText', {
-    defaultMessage: 'number of digits to round the decimal portion of the value to',
+    defaultMessage: 'The number of digits to truncate the decimal portion of the value to',
   }),
   fn: async function precisionFn(args) {
     await alter(args, function(eachSeries, precision) {
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 47bf2ae634048..380e78cf6fa19 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -2697,8 +2697,6 @@
     "timelion.help.functions.points.args.symbolHelpText": "点のシンボルです。{validSymbols} の 1 つ",
     "timelion.help.functions.points.args.weightHelpText": "点の周りの太さです",
     "timelion.help.functions.pointsHelpText": "数列を点として表示します",
-    "timelion.help.functions.precision.args.precisionHelpText": "各値を四捨五入する桁数です",
-    "timelion.help.functions.precisionHelpText": "値の小数点以下の四捨五入する桁数です",
     "timelion.help.functions.props.args.globalHelpText": "各数列に対し、seriesList にプロップを設定します",
     "timelion.help.functions.propsHelpText": "数列に任意のプロパティを設定するため、自己責任で行ってください。例: {example}",
     "timelion.help.functions.quandl.args.codeHelpText": "プロットする Quandl コードです。これらは quandl.com に掲載されています。",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index a94a602e48d9b..23e822821fea4 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -2697,8 +2697,6 @@
     "timelion.help.functions.points.args.symbolHelpText": "点符号。以下选项之一:{validSymbols}",
     "timelion.help.functions.points.args.weightHelpText": "围绕点的线条粗细",
     "timelion.help.functions.pointsHelpText": "将序列显示为点",
-    "timelion.help.functions.precision.args.precisionHelpText": "将每个值舍入到的小数位数",
-    "timelion.help.functions.precisionHelpText": "将值的小数部分舍入到的小数位数",
     "timelion.help.functions.props.args.globalHelpText": "在 seriesList 与每个序列上设置属性",
     "timelion.help.functions.propsHelpText": "在序列上可设置任意属性,但请自担风险。例如 {example}",
     "timelion.help.functions.quandl.args.codeHelpText": "要绘图的 quandl 代码。可以在 quandl.com 找到这些内容。",

From c3001c4469e015f5942d5fdaa6a37872129f4aa4 Mon Sep 17 00:00:00 2001
From: CJ Cenizal <cj@cenizal.com>
Date: Thu, 20 Feb 2020 08:27:53 -0800
Subject: [PATCH 102/174] Add filter for ILM phase to Index Management (revert
 #45486) (#57402)

---
 .../extend_index_management.test.js.snap      | 24 +++++++++++++
 .../np_ready/extend_index_management/index.js | 34 +++++++++++++++++++
 2 files changed, 58 insertions(+)

diff --git a/x-pack/legacy/plugins/index_lifecycle_management/__jest__/__snapshots__/extend_index_management.test.js.snap b/x-pack/legacy/plugins/index_lifecycle_management/__jest__/__snapshots__/extend_index_management.test.js.snap
index 92aaa171551a0..74c3e7408fe7c 100644
--- a/x-pack/legacy/plugins/index_lifecycle_management/__jest__/__snapshots__/extend_index_management.test.js.snap
+++ b/x-pack/legacy/plugins/index_lifecycle_management/__jest__/__snapshots__/extend_index_management.test.js.snap
@@ -63,6 +63,30 @@ Array [
     ],
     "type": "field_value_selection",
   },
+  Object {
+    "field": "ilm.phase",
+    "multiSelect": "or",
+    "name": "Lifecycle phase",
+    "options": Array [
+      Object {
+        "value": "hot",
+        "view": "Hot",
+      },
+      Object {
+        "value": "warm",
+        "view": "Warm",
+      },
+      Object {
+        "value": "cold",
+        "view": "Cold",
+      },
+      Object {
+        "value": "delete",
+        "view": "Delete",
+      },
+    ],
+    "type": "field_value_selection",
+  },
 ]
 `;
 
diff --git a/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/extend_index_management/index.js b/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/extend_index_management/index.js
index 6958c4ecce0cc..0e662b78b2c18 100644
--- a/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/extend_index_management/index.js
+++ b/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/extend_index_management/index.js
@@ -200,6 +200,40 @@ export const ilmFilterExtension = indices => {
           },
         ],
       },
+      {
+        type: 'field_value_selection',
+        field: 'ilm.phase',
+        name: i18n.translate('xpack.indexLifecycleMgmt.indexMgmtFilter.lifecyclePhaseLabel', {
+          defaultMessage: 'Lifecycle phase',
+        }),
+        multiSelect: 'or',
+        options: [
+          {
+            value: 'hot',
+            view: i18n.translate('xpack.indexLifecycleMgmt.indexMgmtFilter.hotLabel', {
+              defaultMessage: 'Hot',
+            }),
+          },
+          {
+            value: 'warm',
+            view: i18n.translate('xpack.indexLifecycleMgmt.indexMgmtFilter.warmLabel', {
+              defaultMessage: 'Warm',
+            }),
+          },
+          {
+            value: 'cold',
+            view: i18n.translate('xpack.indexLifecycleMgmt.indexMgmtFilter.coldLabel', {
+              defaultMessage: 'Cold',
+            }),
+          },
+          {
+            value: 'delete',
+            view: i18n.translate('xpack.indexLifecycleMgmt.indexMgmtFilter.deleteLabel', {
+              defaultMessage: 'Delete',
+            }),
+          },
+        ],
+      },
     ];
   }
 };

From dfd19596e1aa39af9d9ea6acc30316d8823b20a9 Mon Sep 17 00:00:00 2001
From: Matthew Kime <matt@mattki.me>
Date: Thu, 20 Feb 2020 10:40:35 -0600
Subject: [PATCH 103/174] Fix browser date format (#57714)

* fix browser date formatter
---
 .../constants/base_formatters.ts              |  2 --
 .../common/field_formats/converters/index.ts  |  1 -
 .../field_formats/field_formats_registry.ts   |  4 +++
 .../data/common/field_formats/index.ts        |  1 -
 .../data/common/field_formats/types.ts        |  1 +
 src/plugins/data/common/types.ts              |  1 +
 .../data/public/field_formats/constants.ts    | 23 +++++++++++++
 .../field_formats/converters/date.test.ts     |  0
 .../field_formats/converters/date.ts          |  9 +++--
 .../public/field_formats/converters/index.ts  | 20 +++++++++++
 .../field_formats_service.test.ts             | 34 +++++++++++++++++++
 .../field_formats/field_formats_service.ts    | 19 +++++++----
 .../data/public/field_formats/index.ts        |  2 ++
 src/plugins/data/public/index.ts              |  4 ++-
 src/plugins/data/public/mocks.ts              |  1 +
 .../query_string_input.test.tsx.snap          |  6 ++++
 .../field_formats/converters/date_server.ts   |  6 ++--
 .../server/field_formats/converters/index.ts  | 20 +++++++++++
 .../field_formats_service.test.ts             | 34 +++++++++++++++++++
 .../field_formats/field_formats_service.ts    |  3 +-
 src/plugins/data/server/index.ts              |  4 ---
 src/test_utils/public/stub_field_formats.ts   |  3 +-
 22 files changed, 174 insertions(+), 24 deletions(-)
 create mode 100644 src/plugins/data/public/field_formats/constants.ts
 rename src/plugins/data/{common => public}/field_formats/converters/date.test.ts (100%)
 rename src/plugins/data/{common => public}/field_formats/converters/date.ts (92%)
 create mode 100644 src/plugins/data/public/field_formats/converters/index.ts
 create mode 100644 src/plugins/data/public/field_formats/field_formats_service.test.ts
 rename src/plugins/data/{common => server}/field_formats/converters/date_server.ts (95%)
 create mode 100644 src/plugins/data/server/field_formats/converters/index.ts
 create mode 100644 src/plugins/data/server/field_formats/field_formats_service.test.ts

diff --git a/src/plugins/data/common/field_formats/constants/base_formatters.ts b/src/plugins/data/common/field_formats/constants/base_formatters.ts
index 95aedd02d16d6..6befe8cea71f5 100644
--- a/src/plugins/data/common/field_formats/constants/base_formatters.ts
+++ b/src/plugins/data/common/field_formats/constants/base_formatters.ts
@@ -23,7 +23,6 @@ import {
   BoolFormat,
   BytesFormat,
   ColorFormat,
-  DateFormat,
   DateNanosFormat,
   DurationFormat,
   IpFormat,
@@ -41,7 +40,6 @@ export const baseFormatters: IFieldFormatType[] = [
   BoolFormat,
   BytesFormat,
   ColorFormat,
-  DateFormat,
   DateNanosFormat,
   DurationFormat,
   IpFormat,
diff --git a/src/plugins/data/common/field_formats/converters/index.ts b/src/plugins/data/common/field_formats/converters/index.ts
index f7e50539b44d8..cc9fae7fc9965 100644
--- a/src/plugins/data/common/field_formats/converters/index.ts
+++ b/src/plugins/data/common/field_formats/converters/index.ts
@@ -19,7 +19,6 @@
 
 export { UrlFormat } from './url';
 export { BytesFormat } from './bytes';
-export { DateFormat } from './date_server';
 export { DateNanosFormat } from './date_nanos';
 export { RelativeDateFormat } from './relative_date';
 export { DurationFormat } from './duration';
diff --git a/src/plugins/data/common/field_formats/field_formats_registry.ts b/src/plugins/data/common/field_formats/field_formats_registry.ts
index 9fe9a31307b6a..9fdf1ad9c80fb 100644
--- a/src/plugins/data/common/field_formats/field_formats_registry.ts
+++ b/src/plugins/data/common/field_formats/field_formats_registry.ts
@@ -95,6 +95,10 @@ export class FieldFormatsRegistry {
     return undefined;
   };
 
+  getTypeWithoutMetaParams = (formatId: FieldFormatId): IFieldFormatType | undefined => {
+    return this.fieldFormats.get(formatId);
+  };
+
   /**
    * Get the default FieldFormat type (class) for
    * a field type, using the format:defaultTypeMap.
diff --git a/src/plugins/data/common/field_formats/index.ts b/src/plugins/data/common/field_formats/index.ts
index d7858966f2620..13d3d9d73d43a 100644
--- a/src/plugins/data/common/field_formats/index.ts
+++ b/src/plugins/data/common/field_formats/index.ts
@@ -27,7 +27,6 @@ export {
   BoolFormat,
   BytesFormat,
   ColorFormat,
-  DateFormat,
   DateNanosFormat,
   DurationFormat,
   IpFormat,
diff --git a/src/plugins/data/common/field_formats/types.ts b/src/plugins/data/common/field_formats/types.ts
index 24aa92c67b694..0c16d9f1ac8bf 100644
--- a/src/plugins/data/common/field_formats/types.ts
+++ b/src/plugins/data/common/field_formats/types.ts
@@ -18,6 +18,7 @@
  */
 
 import { FieldFormat } from './field_format';
+export { FieldFormat };
 
 /** @public **/
 export type FieldFormatsContentType = 'html' | 'text';
diff --git a/src/plugins/data/common/types.ts b/src/plugins/data/common/types.ts
index be0d3230b3a0e..93629c3dbaf62 100644
--- a/src/plugins/data/common/types.ts
+++ b/src/plugins/data/common/types.ts
@@ -21,3 +21,4 @@ export * from './timefilter/types';
 export * from './query/types';
 export * from './kbn_field_types/types';
 export * from './index_patterns/types';
+export { TextContextTypeConvert, IFieldFormatMetaParams } from './field_formats/types';
diff --git a/src/plugins/data/public/field_formats/constants.ts b/src/plugins/data/public/field_formats/constants.ts
new file mode 100644
index 0000000000000..a5c2b4e379908
--- /dev/null
+++ b/src/plugins/data/public/field_formats/constants.ts
@@ -0,0 +1,23 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { baseFormatters } from '../../common';
+import { DateFormat } from './converters/date';
+
+export const baseFormattersPublic = [DateFormat, ...baseFormatters];
diff --git a/src/plugins/data/common/field_formats/converters/date.test.ts b/src/plugins/data/public/field_formats/converters/date.test.ts
similarity index 100%
rename from src/plugins/data/common/field_formats/converters/date.test.ts
rename to src/plugins/data/public/field_formats/converters/date.test.ts
diff --git a/src/plugins/data/common/field_formats/converters/date.ts b/src/plugins/data/public/field_formats/converters/date.ts
similarity index 92%
rename from src/plugins/data/common/field_formats/converters/date.ts
rename to src/plugins/data/public/field_formats/converters/date.ts
index 3888df051b118..3e1efdc69dec8 100644
--- a/src/plugins/data/common/field_formats/converters/date.ts
+++ b/src/plugins/data/public/field_formats/converters/date.ts
@@ -20,9 +20,12 @@
 import { i18n } from '@kbn/i18n';
 import { memoize, noop } from 'lodash';
 import moment from 'moment';
-import { KBN_FIELD_TYPES } from '../../kbn_field_types/types';
-import { FieldFormat } from '../field_format';
-import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types';
+import {
+  FieldFormat,
+  KBN_FIELD_TYPES,
+  TextContextTypeConvert,
+  FIELD_FORMAT_IDS,
+} from '../../../common';
 
 export class DateFormat extends FieldFormat {
   static id = FIELD_FORMAT_IDS.DATE;
diff --git a/src/plugins/data/public/field_formats/converters/index.ts b/src/plugins/data/public/field_formats/converters/index.ts
new file mode 100644
index 0000000000000..c51111092beca
--- /dev/null
+++ b/src/plugins/data/public/field_formats/converters/index.ts
@@ -0,0 +1,20 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export { DateFormat } from './date';
diff --git a/src/plugins/data/public/field_formats/field_formats_service.test.ts b/src/plugins/data/public/field_formats/field_formats_service.test.ts
new file mode 100644
index 0000000000000..e066af28f4699
--- /dev/null
+++ b/src/plugins/data/public/field_formats/field_formats_service.test.ts
@@ -0,0 +1,34 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { FieldFormatsService } from './field_formats_service';
+import { coreMock } from '../../../../../src/core/public/mocks';
+import { DateFormat } from './converters/date';
+
+describe('FieldFormatService', () => {
+  test('DateFormat is public version', () => {
+    const mockCore = coreMock.createSetup();
+    const service = new FieldFormatsService();
+    service.setup(mockCore);
+    const fieldFormatsRegistry = service.start();
+    const DateFormatFromRegsitry = fieldFormatsRegistry.getTypeWithoutMetaParams('date');
+
+    expect(DateFormatFromRegsitry).toEqual(DateFormat);
+  });
+});
diff --git a/src/plugins/data/public/field_formats/field_formats_service.ts b/src/plugins/data/public/field_formats/field_formats_service.ts
index 785bedf9b35d3..22c7e90c06130 100644
--- a/src/plugins/data/public/field_formats/field_formats_service.ts
+++ b/src/plugins/data/public/field_formats/field_formats_service.ts
@@ -18,9 +18,10 @@
  */
 
 import { CoreSetup } from 'src/core/public';
-import { FieldFormatsRegistry } from '../../common/field_formats';
+import { FieldFormatsRegistry } from '../../common';
 import { deserializeFieldFormat } from './utils/deserialize';
 import { FormatFactory } from '../../common/field_formats/utils';
+import { baseFormattersPublic } from './constants';
 
 export class FieldFormatsService {
   private readonly fieldFormatsRegistry: FieldFormatsRegistry = new FieldFormatsRegistry();
@@ -34,13 +35,17 @@ export class FieldFormatsService {
 
     const getConfig = core.uiSettings.get.bind(core.uiSettings);
 
-    this.fieldFormatsRegistry.init(getConfig, {
-      parsedUrl: {
-        origin: window.location.origin,
-        pathname: window.location.pathname,
-        basePath: core.http.basePath.get(),
+    this.fieldFormatsRegistry.init(
+      getConfig,
+      {
+        parsedUrl: {
+          origin: window.location.origin,
+          pathname: window.location.pathname,
+          basePath: core.http.basePath.get(),
+        },
       },
-    });
+      baseFormattersPublic
+    );
 
     return this.fieldFormatsRegistry as FieldFormatsSetup;
   }
diff --git a/src/plugins/data/public/field_formats/index.ts b/src/plugins/data/public/field_formats/index.ts
index 4550a5781535f..015d5b39561bb 100644
--- a/src/plugins/data/public/field_formats/index.ts
+++ b/src/plugins/data/public/field_formats/index.ts
@@ -18,3 +18,5 @@
  */
 
 export { FieldFormatsService, FieldFormatsSetup, FieldFormatsStart } from './field_formats_service';
+export { DateFormat } from './converters';
+export { baseFormattersPublic } from './constants';
diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts
index cdc4167f545af..cbd4bfd348797 100644
--- a/src/plugins/data/public/index.ts
+++ b/src/plugins/data/public/index.ts
@@ -156,7 +156,6 @@ import {
   BoolFormat,
   BytesFormat,
   ColorFormat,
-  DateFormat,
   DateNanosFormat,
   DurationFormat,
   IpFormat,
@@ -171,6 +170,9 @@ import {
   serializeFieldFormat,
 } from '../common/field_formats';
 
+import { DateFormat } from './field_formats';
+export { baseFormattersPublic } from './field_formats';
+
 // Field formats helpers namespace:
 export const fieldFormats = {
   FieldFormat,
diff --git a/src/plugins/data/public/mocks.ts b/src/plugins/data/public/mocks.ts
index 2d5cc72597ec4..a2a1a2424fc90 100644
--- a/src/plugins/data/public/mocks.ts
+++ b/src/plugins/data/public/mocks.ts
@@ -50,6 +50,7 @@ const fieldFormatsMock: IFieldFormatsRegistry = {
   register: jest.fn(),
   parseDefaultTypeMap: jest.fn(),
   deserialize: jest.fn(),
+  getTypeWithoutMetaParams: jest.fn(),
 };
 
 const createSetupContract = (): Setup => {
diff --git a/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap b/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap
index 06e56aaf3eb0a..93af543fba1a8 100644
--- a/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap
+++ b/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap
@@ -184,6 +184,7 @@ exports[`QueryStringInput Should disable autoFocus on EuiFieldText when disableA
                 "getInstance": [MockFunction],
                 "getType": [MockFunction],
                 "getTypeNameByEsTypes": [MockFunction],
+                "getTypeWithoutMetaParams": [MockFunction],
                 "init": [MockFunction],
                 "parseDefaultTypeMap": [MockFunction],
                 "register": [MockFunction],
@@ -839,6 +840,7 @@ exports[`QueryStringInput Should disable autoFocus on EuiFieldText when disableA
                         "getInstance": [MockFunction],
                         "getType": [MockFunction],
                         "getTypeNameByEsTypes": [MockFunction],
+                        "getTypeWithoutMetaParams": [MockFunction],
                         "init": [MockFunction],
                         "parseDefaultTypeMap": [MockFunction],
                         "register": [MockFunction],
@@ -1476,6 +1478,7 @@ exports[`QueryStringInput Should pass the query language to the language switche
                 "getInstance": [MockFunction],
                 "getType": [MockFunction],
                 "getTypeNameByEsTypes": [MockFunction],
+                "getTypeWithoutMetaParams": [MockFunction],
                 "init": [MockFunction],
                 "parseDefaultTypeMap": [MockFunction],
                 "register": [MockFunction],
@@ -2128,6 +2131,7 @@ exports[`QueryStringInput Should pass the query language to the language switche
                         "getInstance": [MockFunction],
                         "getType": [MockFunction],
                         "getTypeNameByEsTypes": [MockFunction],
+                        "getTypeWithoutMetaParams": [MockFunction],
                         "init": [MockFunction],
                         "parseDefaultTypeMap": [MockFunction],
                         "register": [MockFunction],
@@ -2765,6 +2769,7 @@ exports[`QueryStringInput Should render the given query 1`] = `
                 "getInstance": [MockFunction],
                 "getType": [MockFunction],
                 "getTypeNameByEsTypes": [MockFunction],
+                "getTypeWithoutMetaParams": [MockFunction],
                 "init": [MockFunction],
                 "parseDefaultTypeMap": [MockFunction],
                 "register": [MockFunction],
@@ -3417,6 +3422,7 @@ exports[`QueryStringInput Should render the given query 1`] = `
                         "getInstance": [MockFunction],
                         "getType": [MockFunction],
                         "getTypeNameByEsTypes": [MockFunction],
+                        "getTypeWithoutMetaParams": [MockFunction],
                         "init": [MockFunction],
                         "parseDefaultTypeMap": [MockFunction],
                         "register": [MockFunction],
diff --git a/src/plugins/data/common/field_formats/converters/date_server.ts b/src/plugins/data/server/field_formats/converters/date_server.ts
similarity index 95%
rename from src/plugins/data/common/field_formats/converters/date_server.ts
rename to src/plugins/data/server/field_formats/converters/date_server.ts
index 216af133bb5f5..f4e6296259196 100644
--- a/src/plugins/data/common/field_formats/converters/date_server.ts
+++ b/src/plugins/data/server/field_formats/converters/date_server.ts
@@ -20,14 +20,14 @@
 import { i18n } from '@kbn/i18n';
 import { memoize, noop } from 'lodash';
 import moment from 'moment-timezone';
-import { KBN_FIELD_TYPES } from '../../kbn_field_types/types';
-import { FieldFormat } from '../field_format';
 import {
+  FieldFormat,
+  KBN_FIELD_TYPES,
   TextContextTypeConvert,
   FIELD_FORMAT_IDS,
   FieldFormatsGetConfigFn,
   IFieldFormatMetaParams,
-} from '../types';
+} from '../../../common';
 
 export class DateFormat extends FieldFormat {
   static id = FIELD_FORMAT_IDS.DATE;
diff --git a/src/plugins/data/server/field_formats/converters/index.ts b/src/plugins/data/server/field_formats/converters/index.ts
new file mode 100644
index 0000000000000..f5c69df972869
--- /dev/null
+++ b/src/plugins/data/server/field_formats/converters/index.ts
@@ -0,0 +1,20 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export { DateFormat } from './date_server';
diff --git a/src/plugins/data/server/field_formats/field_formats_service.test.ts b/src/plugins/data/server/field_formats/field_formats_service.test.ts
new file mode 100644
index 0000000000000..2e7ce0fa435a7
--- /dev/null
+++ b/src/plugins/data/server/field_formats/field_formats_service.test.ts
@@ -0,0 +1,34 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { FieldFormatsService } from './field_formats_service';
+import { DateFormat } from './converters/date_server';
+import { coreMock } from '../../../../core/server/mocks';
+
+describe('FieldFormatService', () => {
+  test('DateFormat is server version', async () => {
+    const service = new FieldFormatsService();
+    const fieldFormatsService = await service.start();
+    const uiSettings = coreMock.createStart().uiSettings.asScopedToClient({} as any);
+    const fieldFormatsRegistry = await fieldFormatsService.fieldFormatServiceFactory(uiSettings);
+    const DateFormatFromRegsitry = fieldFormatsRegistry.getTypeWithoutMetaParams('date');
+
+    expect(DateFormatFromRegsitry).toEqual(DateFormat);
+  });
+});
diff --git a/src/plugins/data/server/field_formats/field_formats_service.ts b/src/plugins/data/server/field_formats/field_formats_service.ts
index a31e5927ab800..0dac64fb5dc1d 100644
--- a/src/plugins/data/server/field_formats/field_formats_service.ts
+++ b/src/plugins/data/server/field_formats/field_formats_service.ts
@@ -19,9 +19,10 @@
 import { has } from 'lodash';
 import { FieldFormatsRegistry, IFieldFormatType, baseFormatters } from '../../common/field_formats';
 import { IUiSettingsClient } from '../../../../core/server';
+import { DateFormat } from './converters';
 
 export class FieldFormatsService {
-  private readonly fieldFormatClasses: IFieldFormatType[] = baseFormatters;
+  private readonly fieldFormatClasses: IFieldFormatType[] = [DateFormat, ...baseFormatters];
 
   public setup() {
     return {
diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts
index 3ee98a318de35..40d367138b60d 100644
--- a/src/plugins/data/server/index.ts
+++ b/src/plugins/data/server/index.ts
@@ -83,7 +83,6 @@ import {
   BoolFormat,
   BytesFormat,
   ColorFormat,
-  DateFormat,
   DateNanosFormat,
   DurationFormat,
   IpFormat,
@@ -101,13 +100,10 @@ import {
 export const fieldFormats = {
   FieldFormatsRegistry,
   FieldFormat,
-
   serializeFieldFormat,
-
   BoolFormat,
   BytesFormat,
   ColorFormat,
-  DateFormat,
   DateNanosFormat,
   DurationFormat,
   IpFormat,
diff --git a/src/test_utils/public/stub_field_formats.ts b/src/test_utils/public/stub_field_formats.ts
index 5a20823134ebd..589e93fd600c2 100644
--- a/src/test_utils/public/stub_field_formats.ts
+++ b/src/test_utils/public/stub_field_formats.ts
@@ -19,12 +19,13 @@
 import { CoreSetup } from 'kibana/public';
 import { DataPublicPluginStart, fieldFormats } from '../../plugins/data/public';
 import { deserializeFieldFormat } from '../../plugins/data/public/field_formats/utils/deserialize';
+import { baseFormattersPublic } from '../../plugins/data/public';
 
 export const getFieldFormatsRegistry = (core: CoreSetup) => {
   const fieldFormatsRegistry = new fieldFormats.FieldFormatsRegistry();
   const getConfig = core.uiSettings.get.bind(core.uiSettings);
 
-  fieldFormatsRegistry.init(getConfig, {});
+  fieldFormatsRegistry.init(getConfig, {}, baseFormattersPublic);
 
   fieldFormatsRegistry.deserialize = deserializeFieldFormat.bind(
     fieldFormatsRegistry as DataPublicPluginStart['fieldFormats']

From 5953e62a726bacd5b49976a3e0863e1d2cb5aa06 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez?=
 <alejandro.fernandez@elastic.co>
Date: Thu, 20 Feb 2020 17:57:00 +0100
Subject: [PATCH 104/174] [Logs UI]  Fix column reordering in settings page
 (#58104)

* Ensure only one element has scroll

Apparently having multiple elements with scroll confuses the
`react-beautiful-dnd` mechanism to determine the position of the
elements. Adding `overflowY` to the app root fixes it.

* Fix upper bound check for log column reordering

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 x-pack/plugins/infra/public/apps/start_app.tsx                  | 1 +
 .../log_columns_configuration_form_state.tsx                    | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/x-pack/plugins/infra/public/apps/start_app.tsx b/x-pack/plugins/infra/public/apps/start_app.tsx
index 300d97d3c45b1..66e699abd22b4 100644
--- a/x-pack/plugins/infra/public/apps/start_app.tsx
+++ b/x-pack/plugins/infra/public/apps/start_app.tsx
@@ -79,6 +79,7 @@ export async function startApp(
   // expected.
   element.style.height = '100%';
   element.style.display = 'flex';
+  element.style.overflowY = 'hidden'; // Prevent having scroll within a container having scroll. It messes up with drag-n-drop elements
   element.className += ` ${CONTAINER_CLASSNAME}`;
 
   ReactDOM.render(<App />, element);
diff --git a/x-pack/plugins/infra/public/components/source_configuration/log_columns_configuration_form_state.tsx b/x-pack/plugins/infra/public/components/source_configuration/log_columns_configuration_form_state.tsx
index c5398cf79ef43..0b6a92ed98507 100644
--- a/x-pack/plugins/infra/public/components/source_configuration/log_columns_configuration_form_state.tsx
+++ b/x-pack/plugins/infra/public/components/source_configuration/log_columns_configuration_form_state.tsx
@@ -108,7 +108,7 @@ export const useLogColumnsConfigurationFormState = ({
 
   const moveLogColumn = useCallback(
     (sourceIndex, destinationIndex) => {
-      if (destinationIndex >= 0 && sourceIndex < formState.logColumns.length - 1) {
+      if (destinationIndex >= 0 && sourceIndex <= formState.logColumns.length - 1) {
         const newLogColumns = [...formState.logColumns];
         newLogColumns.splice(destinationIndex, 0, newLogColumns.splice(sourceIndex, 1)[0]);
         setFormStateChanges(changes => ({

From 857f9f8379632c74f10b64f16d375ba2cd1ddd66 Mon Sep 17 00:00:00 2001
From: Maryia Lapata <mary.lopato@gmail.com>
Date: Thu, 20 Feb 2020 21:20:23 +0300
Subject: [PATCH 105/174] [NP] Move ui/saved_objects to NP (#57452)

* Move saved_objects to NP

* Update path for imports

* Remove ui/saved_objects

* Update i18n IDs

* Convert test

* Replace Bluebird to Promise; fix unit tests

* Mock openConfirm in test

* Add kibana.json

* Check unit test

* Update unit tests
---
 src/core/MIGRATION.md                         |   2 +-
 .../simple_saved_object.test.ts}              |  35 +-
 .../kibana/public/dashboard/legacy_imports.ts |   2 -
 .../public/dashboard/np_ready/application.ts  |   2 +-
 .../np_ready/dashboard_app_controller.tsx     |   3 +-
 .../dashboard/np_ready/lib/save_dashboard.ts  |   2 +-
 .../saved_dashboard/saved_dashboard.ts        |   7 +-
 .../saved_dashboard/saved_dashboards.ts       |   6 +-
 .../discover/saved_searches/_saved_search.ts  |   7 +-
 .../discover/saved_searches/saved_searches.ts |   7 +-
 .../management/saved_object_registry.ts       |   2 +-
 .../render.test.js                            |   1 +
 .../saved_object_save_as_checkbox.html        |   2 +-
 .../timelion/public/services/_saved_sheet.ts  |   6 +-
 .../timelion/public/services/saved_sheets.ts  |   2 +-
 .../public/embeddable/visualize_embeddable.ts |   2 +-
 .../public/saved_visualizations/_saved_vis.ts |   7 +-
 .../saved_visualizations.ts                   |   6 +-
 src/plugins/data/public/mocks.ts              |   1 +
 .../query_string_input.test.tsx.snap          |   6 +
 src/plugins/saved_objects/kibana.json         |   7 +
 .../saved_objects/public}/constants.ts        |  11 +-
 src/plugins/saved_objects/public/index.ts     |   6 +
 src/plugins/saved_objects/public/plugin.ts    |  25 +
 .../saved_object}/helpers/apply_es_resp.ts    |   8 +-
 .../helpers/build_saved_object.ts             |   4 +-
 .../helpers/check_for_duplicate_title.ts      |   4 +-
 .../helpers/confirm_modal_promise.tsx         |  11 +-
 .../saved_object}/helpers/create_source.ts    |  17 +-
 .../display_duplicate_title_confirm_modal.ts  |  17 +-
 .../helpers/find_object_by_title.test.ts}     |  28 +-
 .../helpers/find_object_by_title.ts           |   8 +-
 .../helpers/hydrate_index_pattern.ts          |   4 +-
 .../helpers/initialize_saved_object.ts        |   2 +-
 .../helpers/parse_search_source.ts            |   6 +-
 .../helpers/save_saved_object.ts              |   4 +-
 .../helpers/serialize_saved_object.ts         |   4 +-
 .../helpers/string_utils.test.ts              |   1 +
 .../saved_object}/helpers/string_utils.ts     |   0
 .../public/saved_object}/index.ts             |   2 +-
 .../public/saved_object/saved_object.test.ts} | 569 +++++++++---------
 .../public/saved_object}/saved_object.ts      |   2 +-
 .../saved_object}/saved_object_loader.ts      |   2 +-
 .../saved_objects/public}/types.ts            |   6 +-
 .../plugins/graph/public/legacy_imports.ts    |   2 -
 .../services/persistence/saved_workspace.ts   |   4 +-
 .../persistence/saved_workspace_loader.ts     |   2 +-
 .../plugins/graph/public/types/persistence.ts |   2 +-
 .../services/gis_map_saved_object_loader.js   |   2 +-
 .../public/angular/services/saved_gis_map.js  |   2 +-
 .../translations/translations/ja-JP.json      |  14 +-
 .../translations/translations/zh-CN.json      |  14 +-
 52 files changed, 467 insertions(+), 431 deletions(-)
 rename src/{legacy/ui/public/saved_objects/__tests__/simple_saved_object.js => core/public/saved_objects/simple_saved_object.test.ts} (67%)
 create mode 100644 src/plugins/saved_objects/kibana.json
 rename src/{legacy/ui/public/saved_objects => plugins/saved_objects/public}/constants.ts (83%)
 create mode 100644 src/plugins/saved_objects/public/plugin.ts
 rename src/{legacy/ui/public/saved_objects => plugins/saved_objects/public/saved_object}/helpers/apply_es_resp.ts (91%)
 rename src/{legacy/ui/public/saved_objects => plugins/saved_objects/public/saved_object}/helpers/build_saved_object.ts (98%)
 rename src/{legacy/ui/public/saved_objects => plugins/saved_objects/public/saved_object}/helpers/check_for_duplicate_title.ts (95%)
 rename src/{legacy/ui/public/saved_objects => plugins/saved_objects/public/saved_object}/helpers/confirm_modal_promise.tsx (86%)
 rename src/{legacy/ui/public/saved_objects => plugins/saved_objects/public/saved_object}/helpers/create_source.ts (85%)
 rename src/{legacy/ui/public/saved_objects => plugins/saved_objects/public/saved_object}/helpers/display_duplicate_title_confirm_modal.ts (79%)
 rename src/{legacy/ui/public/saved_objects/__tests__/find_object_by_title.js => plugins/saved_objects/public/saved_object/helpers/find_object_by_title.test.ts} (72%)
 rename src/{legacy/ui/public/saved_objects => plugins/saved_objects/public/saved_object}/helpers/find_object_by_title.ts (90%)
 rename src/{legacy/ui/public/saved_objects => plugins/saved_objects/public/saved_object}/helpers/hydrate_index_pattern.ts (92%)
 rename src/{legacy/ui/public/saved_objects => plugins/saved_objects/public/saved_object}/helpers/initialize_saved_object.ts (96%)
 rename src/{legacy/ui/public/saved_objects => plugins/saved_objects/public/saved_object}/helpers/parse_search_source.ts (94%)
 rename src/{legacy/ui/public/saved_objects => plugins/saved_objects/public/saved_object}/helpers/save_saved_object.ts (99%)
 rename src/{legacy/ui/public/saved_objects => plugins/saved_objects/public/saved_object}/helpers/serialize_saved_object.ts (96%)
 rename src/{legacy/ui/public/saved_objects => plugins/saved_objects/public/saved_object}/helpers/string_utils.test.ts (99%)
 rename src/{legacy/ui/public/saved_objects => plugins/saved_objects/public/saved_object}/helpers/string_utils.ts (100%)
 rename src/{legacy/ui/public/saved_objects => plugins/saved_objects/public/saved_object}/index.ts (92%)
 rename src/{legacy/ui/public/saved_objects/__tests__/saved_object.js => plugins/saved_objects/public/saved_object/saved_object.test.ts} (52%)
 rename src/{legacy/ui/public/saved_objects => plugins/saved_objects/public/saved_object}/saved_object.ts (98%)
 rename src/{legacy/ui/public/saved_objects => plugins/saved_objects/public/saved_object}/saved_object_loader.ts (98%)
 rename src/{legacy/ui/public/saved_objects => plugins/saved_objects/public}/types.ts (96%)

diff --git a/src/core/MIGRATION.md b/src/core/MIGRATION.md
index 9e57fc4c36876..19f62a7d48923 100644
--- a/src/core/MIGRATION.md
+++ b/src/core/MIGRATION.md
@@ -1170,7 +1170,7 @@ import { setup, start } from '../core_plugins/visualizations/public/legacy';
 | `import 'ui/query_bar'`                           | `import { QueryStringInput } from '../data/public'`          | Directives are deprecated.                                                                                                           |
 | `import 'ui/search_bar'`                          | `import { SearchBar } from '../data/public'`                 | Directive is deprecated.                                                                                                             |
 | `import 'ui/kbn_top_nav'`                         | `import { TopNavMenu } from '../navigation/public'`          | Directive is still available in `ui/kbn_top_nav`.                                                                                    |
-| `ui/saved_objects/components/saved_object_finder` | `import { SavedObjectFinder } from '../kibana_react/public'` |                                                                                                                                      |
+| `ui/saved_objects/components/saved_object_finder` | `import { SavedObjectFinder } from '../saved_objects/public'` |                                                                                                                                      |
 | `core_plugins/interpreter`                        | `data.expressions`                                           | still in progress                                                                                                                    |
 | `ui/courier`                                      | `data.search`                                                | still in progress                                                                                                                    |
 | `ui/embeddable`                                   | `embeddables`                                                | still in progress                                                                                                                    |
diff --git a/src/legacy/ui/public/saved_objects/__tests__/simple_saved_object.js b/src/core/public/saved_objects/simple_saved_object.test.ts
similarity index 67%
rename from src/legacy/ui/public/saved_objects/__tests__/simple_saved_object.js
rename to src/core/public/saved_objects/simple_saved_object.test.ts
index f2fc9bfe232e2..99676f6b78d42 100644
--- a/src/legacy/ui/public/saved_objects/__tests__/simple_saved_object.js
+++ b/src/core/public/saved_objects/simple_saved_object.test.ts
@@ -17,36 +17,43 @@
  * under the License.
  */
 
-import sinon from 'sinon';
-import expect from '@kbn/expect';
-import { SimpleSavedObject } from '../../../../../core/public';
+import { SavedObject } from '../../server';
+import { SimpleSavedObject } from './simple_saved_object';
+import { SavedObjectsClientContract } from './saved_objects_client';
 
 describe('SimpleSavedObject', () => {
+  let client: SavedObjectsClientContract;
+
+  beforeEach(() => {
+    client = {
+      update: jest.fn(),
+      create: jest.fn(),
+      delete: jest.fn(),
+    } as any;
+  });
+
   it('persists type and id', () => {
     const id = 'logstash-*';
     const type = 'index-pattern';
 
-    const client = sinon.stub();
-    const savedObject = new SimpleSavedObject(client, { id, type });
+    const savedObject = new SimpleSavedObject(client, { id, type } as SavedObject);
 
-    expect(savedObject.id).to.be(id);
-    expect(savedObject.type).to.be(type);
+    expect(savedObject.id).toEqual(id);
+    expect(savedObject.type).toEqual(type);
   });
 
   it('persists attributes', () => {
     const attributes = { title: 'My title' };
 
-    const client = sinon.stub();
-    const savedObject = new SimpleSavedObject(client, { attributes });
+    const savedObject = new SimpleSavedObject(client, { attributes } as SavedObject);
 
-    expect(savedObject.attributes).to.be(attributes);
+    expect(savedObject.attributes).toEqual(attributes);
   });
 
   it('persists version', () => {
-    const version = 2;
+    const version = '2';
 
-    const client = sinon.stub();
-    const savedObject = new SimpleSavedObject(client, { version });
-    expect(savedObject._version).to.be(version);
+    const savedObject = new SimpleSavedObject(client, { version } as SavedObject);
+    expect(savedObject._version).toEqual(version);
   });
 });
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts b/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts
index d5198dc557f04..c1f679e9eb7ac 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts
+++ b/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts
@@ -24,7 +24,6 @@
  * directly where they are needed.
  */
 
-export { SavedObjectSaveOpts } from 'ui/saved_objects/types';
 export { npSetup, npStart } from 'ui/new_platform';
 export { subscribeWithScope } from 'ui/utils/subscribe_with_scope';
 export { KbnUrl } from 'ui/url/kbn_url';
@@ -33,7 +32,6 @@ export { createTopNavDirective, createTopNavHelper } from 'ui/kbn_top_nav/kbn_to
 // @ts-ignore
 export { KbnUrlProvider, RedirectWhenMissingProvider } from 'ui/url/index';
 export { IInjector } from 'ui/chrome';
-export { SavedObjectLoader } from 'ui/saved_objects';
 export { absoluteToParsedUrl } from 'ui/url/absolute_to_parsed_url';
 export {
   configureAppAngularModule,
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts
index cc104c1a931d0..7239d8f2258a7 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts
+++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts
@@ -38,7 +38,6 @@ import {
   PrivateProvider,
   PromiseServiceCreator,
   RedirectWhenMissingProvider,
-  SavedObjectLoader,
 } from '../legacy_imports';
 // @ts-ignore
 import { initDashboardApp } from './legacy_app';
@@ -47,6 +46,7 @@ import { NavigationPublicPluginStart as NavigationStart } from '../../../../../.
 import { DataPublicPluginStart } from '../../../../../../plugins/data/public';
 import { SharePluginStart } from '../../../../../../plugins/share/public';
 import { KibanaLegacyStart } from '../../../../../../plugins/kibana_legacy/public';
+import { SavedObjectLoader } from '../../../../../../plugins/saved_objects/public';
 
 export interface RenderDeps {
   pluginInitializerContext: PluginInitializerContext;
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx
index 465203be0d34c..075516d52bab6 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx
+++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx
@@ -26,9 +26,10 @@ import angular from 'angular';
 import { Subscription } from 'rxjs';
 import { map } from 'rxjs/operators';
 import { History } from 'history';
+import { SavedObjectSaveOpts } from 'src/plugins/saved_objects/public';
 import { DashboardEmptyScreen, DashboardEmptyScreenProps } from './dashboard_empty_screen';
 
-import { migrateLegacyQuery, SavedObjectSaveOpts, subscribeWithScope } from '../legacy_imports';
+import { migrateLegacyQuery, subscribeWithScope } from '../legacy_imports';
 import {
   esFilters,
   IndexPattern,
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/lib/save_dashboard.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/lib/save_dashboard.ts
index d80208ce27ffe..db2b1f15247de 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/lib/save_dashboard.ts
+++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/lib/save_dashboard.ts
@@ -18,7 +18,7 @@
  */
 
 import { TimefilterContract } from 'src/plugins/data/public';
-import { SavedObjectSaveOpts } from '../../legacy_imports';
+import { SavedObjectSaveOpts } from '../../../../../../../plugins/saved_objects/public';
 import { updateSavedDashboard } from './update_saved_dashboard';
 import { DashboardStateManager } from '../dashboard_state_manager';
 
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/saved_dashboard/saved_dashboard.ts b/src/legacy/core_plugins/kibana/public/dashboard/saved_dashboard/saved_dashboard.ts
index 5babaf8061de9..c5ac05b5a77eb 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/saved_dashboard/saved_dashboard.ts
+++ b/src/legacy/core_plugins/kibana/public/dashboard/saved_dashboard/saved_dashboard.ts
@@ -16,8 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { SavedObject, SavedObjectKibanaServices } from 'ui/saved_objects/types';
-import { createSavedObjectClass } from 'ui/saved_objects/saved_object';
+import {
+  createSavedObjectClass,
+  SavedObject,
+  SavedObjectKibanaServices,
+} from '../../../../../../plugins/saved_objects/public';
 import { extractReferences, injectReferences } from './saved_dashboard_references';
 
 import {
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/saved_dashboard/saved_dashboards.ts b/src/legacy/core_plugins/kibana/public/dashboard/saved_dashboard/saved_dashboards.ts
index 4ece5d46358ba..2ff76da9c5ca6 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/saved_dashboard/saved_dashboards.ts
+++ b/src/legacy/core_plugins/kibana/public/dashboard/saved_dashboard/saved_dashboards.ts
@@ -17,8 +17,10 @@
  * under the License.
  */
 
-import { SavedObjectLoader } from 'ui/saved_objects';
-import { SavedObjectKibanaServices } from 'ui/saved_objects/types';
+import {
+  SavedObjectLoader,
+  SavedObjectKibanaServices,
+} from '../../../../../../plugins/saved_objects/public';
 import { createSavedDashboardClass } from './saved_dashboard';
 
 export function createSavedDashboardLoader(services: SavedObjectKibanaServices) {
diff --git a/src/legacy/core_plugins/kibana/public/discover/saved_searches/_saved_search.ts b/src/legacy/core_plugins/kibana/public/discover/saved_searches/_saved_search.ts
index 113d13287bd12..7bd0eef8c19af 100644
--- a/src/legacy/core_plugins/kibana/public/discover/saved_searches/_saved_search.ts
+++ b/src/legacy/core_plugins/kibana/public/discover/saved_searches/_saved_search.ts
@@ -16,8 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { SavedObjectKibanaServices } from 'ui/saved_objects/types';
-import { createSavedObjectClass } from 'ui/saved_objects/saved_object';
+
+import {
+  createSavedObjectClass,
+  SavedObjectKibanaServices,
+} from '../../../../../../plugins/saved_objects/public';
 
 export function createSavedSearchClass(services: SavedObjectKibanaServices) {
   const SavedObjectClass = createSavedObjectClass(services);
diff --git a/src/legacy/core_plugins/kibana/public/discover/saved_searches/saved_searches.ts b/src/legacy/core_plugins/kibana/public/discover/saved_searches/saved_searches.ts
index 0b34652461026..ebd341eba99fd 100644
--- a/src/legacy/core_plugins/kibana/public/discover/saved_searches/saved_searches.ts
+++ b/src/legacy/core_plugins/kibana/public/discover/saved_searches/saved_searches.ts
@@ -16,8 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { SavedObjectLoader } from 'ui/saved_objects';
-import { SavedObjectKibanaServices } from 'ui/saved_objects/types';
+
+import {
+  SavedObjectLoader,
+  SavedObjectKibanaServices,
+} from '../../../../../../plugins/saved_objects/public';
 import { createSavedSearchClass } from './_saved_search';
 
 export function createSavedSearchesLoader(services: SavedObjectKibanaServices) {
diff --git a/src/legacy/core_plugins/kibana/public/management/saved_object_registry.ts b/src/legacy/core_plugins/kibana/public/management/saved_object_registry.ts
index 0a6ac20502669..e0756b2e78e25 100644
--- a/src/legacy/core_plugins/kibana/public/management/saved_object_registry.ts
+++ b/src/legacy/core_plugins/kibana/public/management/saved_object_registry.ts
@@ -20,7 +20,7 @@
 import _ from 'lodash';
 import { i18n } from '@kbn/i18n';
 import { npStart } from 'ui/new_platform';
-import { SavedObjectLoader } from 'ui/saved_objects';
+import { SavedObjectLoader } from '../../../../../plugins/saved_objects/public';
 import { createSavedDashboardLoader } from '../dashboard';
 import { createSavedSearchesLoader } from '../discover';
 import { TypesService, createSavedVisLoader } from '../../../visualizations/public';
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/render.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/render.test.js
index af580547b11ed..1b9dafb6daf23 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/render.test.js
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/render.test.js
@@ -50,6 +50,7 @@ describe('CreateIndexPatternWizardRender', () => {
       config: {},
       changeUrl: () => {},
       indexPatternCreationType: {},
+      openConfirm: jest.fn(),
     });
 
     expect(render.mock.calls.length).toBe(1);
diff --git a/src/legacy/core_plugins/timelion/public/directives/saved_object_save_as_checkbox.html b/src/legacy/core_plugins/timelion/public/directives/saved_object_save_as_checkbox.html
index 3e4a1526113c3..5adce4286010a 100644
--- a/src/legacy/core_plugins/timelion/public/directives/saved_object_save_as_checkbox.html
+++ b/src/legacy/core_plugins/timelion/public/directives/saved_object_save_as_checkbox.html
@@ -5,7 +5,7 @@
     i18n-id="timelion.savedObjects.howToSaveAsNewDescription"
     i18n-default-message="In previous versions of Kibana, changing the name of a {savedObjectName} would make a copy with the new name. Use the 'Save as a new {savedObjectName}' checkbox to do this now."
     i18n-values="{ savedObjectName: savedObject.getDisplayName() }"
-    i18n-description="'Save as a new {savedObjectName}' refers to common.ui.savedObjects.saveAsNewLabel and should be the same text."
+    i18n-description="'Save as a new {savedObjectName}' refers to timelion.savedObjects.saveAsNewLabel and should be the same text."
   ></div>
 
   <label class="kuiCheckBoxLabel kuiVerticalRhythmSmall">
diff --git a/src/legacy/core_plugins/timelion/public/services/_saved_sheet.ts b/src/legacy/core_plugins/timelion/public/services/_saved_sheet.ts
index 1e956cbd3e5ac..4e5aa8d445e7d 100644
--- a/src/legacy/core_plugins/timelion/public/services/_saved_sheet.ts
+++ b/src/legacy/core_plugins/timelion/public/services/_saved_sheet.ts
@@ -17,9 +17,11 @@
  * under the License.
  */
 
-import { createSavedObjectClass } from 'ui/saved_objects/saved_object';
-import { SavedObjectKibanaServices } from 'ui/saved_objects/types';
 import { IUiSettingsClient } from 'kibana/public';
+import {
+  createSavedObjectClass,
+  SavedObjectKibanaServices,
+} from '../../../../../plugins/saved_objects/public';
 
 // Used only by the savedSheets service, usually no reason to change this
 export function createSavedSheetClass(
diff --git a/src/legacy/core_plugins/timelion/public/services/saved_sheets.ts b/src/legacy/core_plugins/timelion/public/services/saved_sheets.ts
index 074431bf28da8..201b21f932988 100644
--- a/src/legacy/core_plugins/timelion/public/services/saved_sheets.ts
+++ b/src/legacy/core_plugins/timelion/public/services/saved_sheets.ts
@@ -17,9 +17,9 @@
  * under the License.
  */
 import { npStart } from 'ui/new_platform';
-import { SavedObjectLoader } from 'ui/saved_objects';
 // @ts-ignore
 import { uiModules } from 'ui/modules';
+import { SavedObjectLoader } from '../../../../../plugins/saved_objects/public';
 import { createSavedSheetClass } from './_saved_sheet';
 
 const module = uiModules.get('app/sheet');
diff --git a/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts
index e7ca5ea803701..4c6c12f825609 100644
--- a/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts
+++ b/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts
@@ -22,7 +22,6 @@ import { PersistedState } from 'ui/persisted_state';
 import { Subscription } from 'rxjs';
 import * as Rx from 'rxjs';
 import { buildPipeline } from 'ui/visualize/loader/pipeline_helpers';
-import { SavedObject } from 'ui/saved_objects/types';
 import { npStart } from 'ui/new_platform';
 import { IExpressionLoaderParams } from 'src/plugins/expressions/public';
 import { VISUALIZE_EMBEDDABLE_TYPE } from './constants';
@@ -44,6 +43,7 @@ import {
   SELECT_RANGE_TRIGGER,
 } from '../../../../../plugins/embeddable/public';
 import { dispatchRenderComplete } from '../../../../../plugins/kibana_utils/public';
+import { SavedObject } from '../../../../../plugins/saved_objects/public';
 import { SavedSearch } from '../../../kibana/public/discover/np_ready/types';
 import { Vis } from '../np_ready/public';
 
diff --git a/src/legacy/core_plugins/visualizations/public/saved_visualizations/_saved_vis.ts b/src/legacy/core_plugins/visualizations/public/saved_visualizations/_saved_vis.ts
index 8b8c5fe94cc95..f4548da375216 100644
--- a/src/legacy/core_plugins/visualizations/public/saved_visualizations/_saved_vis.ts
+++ b/src/legacy/core_plugins/visualizations/public/saved_visualizations/_saved_vis.ts
@@ -24,8 +24,11 @@
  *
  * NOTE: It's a type of SavedObject, but specific to visualizations.
  */
-import { SavedObject, SavedObjectKibanaServices } from 'ui/saved_objects/types';
-import { createSavedObjectClass } from 'ui/saved_objects/saved_object';
+import {
+  createSavedObjectClass,
+  SavedObject,
+  SavedObjectKibanaServices,
+} from '../../../../../plugins/saved_objects/public';
 import { updateOldState } from '../index';
 import { extractReferences, injectReferences } from './saved_visualization_references';
 import { IIndexPattern } from '../../../../../plugins/data/public';
diff --git a/src/legacy/core_plugins/visualizations/public/saved_visualizations/saved_visualizations.ts b/src/legacy/core_plugins/visualizations/public/saved_visualizations/saved_visualizations.ts
index 4b2a8e27c0208..7d0d6a10ff66f 100644
--- a/src/legacy/core_plugins/visualizations/public/saved_visualizations/saved_visualizations.ts
+++ b/src/legacy/core_plugins/visualizations/public/saved_visualizations/saved_visualizations.ts
@@ -16,8 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { SavedObjectLoader } from 'ui/saved_objects';
-import { SavedObjectKibanaServices } from 'ui/saved_objects/types';
+import {
+  SavedObjectLoader,
+  SavedObjectKibanaServices,
+} from '../../../../../plugins/saved_objects/public';
 
 // @ts-ignore
 import { findListItems } from './find_list_items';
diff --git a/src/plugins/data/public/mocks.ts b/src/plugins/data/public/mocks.ts
index a2a1a2424fc90..6a0a33096eaac 100644
--- a/src/plugins/data/public/mocks.ts
+++ b/src/plugins/data/public/mocks.ts
@@ -104,6 +104,7 @@ const createStartContract = (): Start => {
           fetchForWildcard: jest.fn(),
         },
       }),
+      get: jest.fn().mockReturnValue(Promise.resolve({})),
     } as unknown) as IndexPatternsContract,
   };
   return startContract;
diff --git a/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap b/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap
index 93af543fba1a8..c3cb36b7743b4 100644
--- a/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap
+++ b/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap
@@ -191,6 +191,7 @@ exports[`QueryStringInput Should disable autoFocus on EuiFieldText when disableA
               },
               "getSuggestions": [MockFunction],
               "indexPatterns": Object {
+                "get": [MockFunction],
                 "make": [Function],
               },
               "query": Object {
@@ -847,6 +848,7 @@ exports[`QueryStringInput Should disable autoFocus on EuiFieldText when disableA
                       },
                       "getSuggestions": [MockFunction],
                       "indexPatterns": Object {
+                        "get": [MockFunction],
                         "make": [Function],
                       },
                       "query": Object {
@@ -1485,6 +1487,7 @@ exports[`QueryStringInput Should pass the query language to the language switche
               },
               "getSuggestions": [MockFunction],
               "indexPatterns": Object {
+                "get": [MockFunction],
                 "make": [Function],
               },
               "query": Object {
@@ -2138,6 +2141,7 @@ exports[`QueryStringInput Should pass the query language to the language switche
                       },
                       "getSuggestions": [MockFunction],
                       "indexPatterns": Object {
+                        "get": [MockFunction],
                         "make": [Function],
                       },
                       "query": Object {
@@ -2776,6 +2780,7 @@ exports[`QueryStringInput Should render the given query 1`] = `
               },
               "getSuggestions": [MockFunction],
               "indexPatterns": Object {
+                "get": [MockFunction],
                 "make": [Function],
               },
               "query": Object {
@@ -3429,6 +3434,7 @@ exports[`QueryStringInput Should render the given query 1`] = `
                       },
                       "getSuggestions": [MockFunction],
                       "indexPatterns": Object {
+                        "get": [MockFunction],
                         "make": [Function],
                       },
                       "query": Object {
diff --git a/src/plugins/saved_objects/kibana.json b/src/plugins/saved_objects/kibana.json
new file mode 100644
index 0000000000000..4081c9a4b21b9
--- /dev/null
+++ b/src/plugins/saved_objects/kibana.json
@@ -0,0 +1,7 @@
+{
+  "id": "savedObjects",
+  "version": "kibana",
+  "server": false,
+  "ui": true,
+  "requiredPlugins": []
+}
diff --git a/src/legacy/ui/public/saved_objects/constants.ts b/src/plugins/saved_objects/public/constants.ts
similarity index 83%
rename from src/legacy/ui/public/saved_objects/constants.ts
rename to src/plugins/saved_objects/public/constants.ts
index e1684aa1d19d5..59b8d058bd570 100644
--- a/src/legacy/ui/public/saved_objects/constants.ts
+++ b/src/plugins/saved_objects/public/constants.ts
@@ -22,18 +22,15 @@ import { i18n } from '@kbn/i18n';
  * An error message to be used when the user rejects a confirm overwrite.
  * @type {string}
  */
-export const OVERWRITE_REJECTED = i18n.translate(
-  'common.ui.savedObjects.overwriteRejectedDescription',
-  {
-    defaultMessage: 'Overwrite confirmation was rejected',
-  }
-);
+export const OVERWRITE_REJECTED = i18n.translate('savedObjects.overwriteRejectedDescription', {
+  defaultMessage: 'Overwrite confirmation was rejected',
+});
 /**
  * An error message to be used when the user rejects a confirm save with duplicate title.
  * @type {string}
  */
 export const SAVE_DUPLICATE_REJECTED = i18n.translate(
-  'common.ui.savedObjects.saveDuplicateRejectedDescription',
+  'savedObjects.saveDuplicateRejectedDescription',
   {
     defaultMessage: 'Save with duplicate title confirmation was rejected',
   }
diff --git a/src/plugins/saved_objects/public/index.ts b/src/plugins/saved_objects/public/index.ts
index 5dae8b055d2db..ea92c921efad0 100644
--- a/src/plugins/saved_objects/public/index.ts
+++ b/src/plugins/saved_objects/public/index.ts
@@ -17,5 +17,11 @@
  * under the License.
  */
 
+import { SavedObjectsPublicPlugin } from './plugin';
+
 export { OnSaveProps, SavedObjectSaveModal, SaveResult, showSaveModal } from './save_modal';
 export { getSavedObjectFinder, SavedObjectFinderUi, SavedObjectMetaData } from './finder';
+export { SavedObjectLoader, createSavedObjectClass } from './saved_object';
+export { SavedObjectSaveOpts, SavedObjectKibanaServices, SavedObject } from './types';
+
+export const plugin = () => new SavedObjectsPublicPlugin();
diff --git a/src/plugins/saved_objects/public/plugin.ts b/src/plugins/saved_objects/public/plugin.ts
new file mode 100644
index 0000000000000..e937c271b6faf
--- /dev/null
+++ b/src/plugins/saved_objects/public/plugin.ts
@@ -0,0 +1,25 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { Plugin } from 'src/core/public';
+
+export class SavedObjectsPublicPlugin implements Plugin {
+  public setup() {}
+  public start() {}
+}
diff --git a/src/legacy/ui/public/saved_objects/helpers/apply_es_resp.ts b/src/plugins/saved_objects/public/saved_object/helpers/apply_es_resp.ts
similarity index 91%
rename from src/legacy/ui/public/saved_objects/helpers/apply_es_resp.ts
rename to src/plugins/saved_objects/public/saved_object/helpers/apply_es_resp.ts
index ee49b7495f2af..2e965eaf1989b 100644
--- a/src/legacy/ui/public/saved_objects/helpers/apply_es_resp.ts
+++ b/src/plugins/saved_objects/public/saved_object/helpers/apply_es_resp.ts
@@ -17,10 +17,10 @@
  * under the License.
  */
 import _ from 'lodash';
-import { EsResponse, SavedObject, SavedObjectConfig } from 'ui/saved_objects/types';
-import { parseSearchSource } from 'ui/saved_objects/helpers/parse_search_source';
-import { expandShorthand, SavedObjectNotFound } from '../../../../../plugins/kibana_utils/public';
-import { IndexPattern } from '../../../../../plugins/data/public';
+import { EsResponse, SavedObject, SavedObjectConfig } from '../../types';
+import { parseSearchSource } from './parse_search_source';
+import { expandShorthand, SavedObjectNotFound } from '../../../../kibana_utils/public';
+import { IndexPattern } from '../../../../data/public';
 
 /**
  * A given response of and ElasticSearch containing a plain saved object is applied to the given
diff --git a/src/legacy/ui/public/saved_objects/helpers/build_saved_object.ts b/src/plugins/saved_objects/public/saved_object/helpers/build_saved_object.ts
similarity index 98%
rename from src/legacy/ui/public/saved_objects/helpers/build_saved_object.ts
rename to src/plugins/saved_objects/public/saved_object/helpers/build_saved_object.ts
index 7cd9a151a443d..b9043890e2775 100644
--- a/src/legacy/ui/public/saved_objects/helpers/build_saved_object.ts
+++ b/src/plugins/saved_objects/public/saved_object/helpers/build_saved_object.ts
@@ -17,7 +17,7 @@
  * under the License.
  */
 import _ from 'lodash';
-import { SearchSource } from '../../../../../plugins/data/public';
+import { SearchSource } from '../../../../data/public';
 import { hydrateIndexPattern } from './hydrate_index_pattern';
 import { intializeSavedObject } from './initialize_saved_object';
 import { serializeSavedObject } from './serialize_saved_object';
@@ -28,7 +28,7 @@ import {
   SavedObjectConfig,
   SavedObjectKibanaServices,
   SavedObjectSaveOpts,
-} from '../types';
+} from '../../types';
 import { applyESResp } from './apply_es_resp';
 import { saveSavedObject } from './save_saved_object';
 
diff --git a/src/legacy/ui/public/saved_objects/helpers/check_for_duplicate_title.ts b/src/plugins/saved_objects/public/saved_object/helpers/check_for_duplicate_title.ts
similarity index 95%
rename from src/legacy/ui/public/saved_objects/helpers/check_for_duplicate_title.ts
rename to src/plugins/saved_objects/public/saved_object/helpers/check_for_duplicate_title.ts
index 5c1c1d0d9a851..8895336aa9ffa 100644
--- a/src/legacy/ui/public/saved_objects/helpers/check_for_duplicate_title.ts
+++ b/src/plugins/saved_objects/public/saved_object/helpers/check_for_duplicate_title.ts
@@ -16,9 +16,9 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { SavedObject, SavedObjectKibanaServices } from '../types';
+import { SavedObject, SavedObjectKibanaServices } from '../../types';
 import { findObjectByTitle } from './find_object_by_title';
-import { SAVE_DUPLICATE_REJECTED } from '../constants';
+import { SAVE_DUPLICATE_REJECTED } from '../../constants';
 import { displayDuplicateTitleConfirmModal } from './display_duplicate_title_confirm_modal';
 
 /**
diff --git a/src/legacy/ui/public/saved_objects/helpers/confirm_modal_promise.tsx b/src/plugins/saved_objects/public/saved_object/helpers/confirm_modal_promise.tsx
similarity index 86%
rename from src/legacy/ui/public/saved_objects/helpers/confirm_modal_promise.tsx
rename to src/plugins/saved_objects/public/saved_object/helpers/confirm_modal_promise.tsx
index 1e0a4f4ebe47f..bb16acad1aa06 100644
--- a/src/legacy/ui/public/saved_objects/helpers/confirm_modal_promise.tsx
+++ b/src/plugins/saved_objects/public/saved_object/helpers/confirm_modal_promise.tsx
@@ -20,7 +20,7 @@ import React from 'react';
 import { OverlayStart } from 'kibana/public';
 import { i18n } from '@kbn/i18n';
 import { EuiConfirmModal } from '@elastic/eui';
-import { toMountPoint } from '../../../../../plugins/kibana_react/public';
+import { toMountPoint } from '../../../../kibana_react/public';
 
 export function confirmModalPromise(
   message = '',
@@ -29,12 +29,9 @@ export function confirmModalPromise(
   overlays: OverlayStart
 ): Promise<true> {
   return new Promise((resolve, reject) => {
-    const cancelButtonText = i18n.translate(
-      'common.ui.savedObjects.confirmModal.cancelButtonLabel',
-      {
-        defaultMessage: 'Cancel',
-      }
-    );
+    const cancelButtonText = i18n.translate('savedObjects.confirmModal.cancelButtonLabel', {
+      defaultMessage: 'Cancel',
+    });
 
     const modal = overlays.openModal(
       toMountPoint(
diff --git a/src/legacy/ui/public/saved_objects/helpers/create_source.ts b/src/plugins/saved_objects/public/saved_object/helpers/create_source.ts
similarity index 85%
rename from src/legacy/ui/public/saved_objects/helpers/create_source.ts
rename to src/plugins/saved_objects/public/saved_object/helpers/create_source.ts
index 1818671cecebe..25ed4d527b833 100644
--- a/src/legacy/ui/public/saved_objects/helpers/create_source.ts
+++ b/src/plugins/saved_objects/public/saved_object/helpers/create_source.ts
@@ -18,9 +18,9 @@
  */
 import _ from 'lodash';
 import { i18n } from '@kbn/i18n';
-import { SavedObject, SavedObjectKibanaServices } from 'ui/saved_objects/types';
 import { SavedObjectAttributes } from 'kibana/public';
-import { OVERWRITE_REJECTED } from 'ui/saved_objects/constants';
+import { SavedObject, SavedObjectKibanaServices } from '../../types';
+import { OVERWRITE_REJECTED } from '../../constants';
 import { confirmModalPromise } from './confirm_modal_promise';
 
 /**
@@ -52,23 +52,20 @@ export async function createSource(
     // record exists, confirm overwriting
     if (_.get(err, 'res.status') === 409) {
       const confirmMessage = i18n.translate(
-        'common.ui.savedObjects.confirmModal.overwriteConfirmationMessage',
+        'savedObjects.confirmModal.overwriteConfirmationMessage',
         {
           defaultMessage: 'Are you sure you want to overwrite {title}?',
           values: { title: savedObject.title },
         }
       );
 
-      const title = i18n.translate('common.ui.savedObjects.confirmModal.overwriteTitle', {
+      const title = i18n.translate('savedObjects.confirmModal.overwriteTitle', {
         defaultMessage: 'Overwrite {name}?',
         values: { name: savedObject.getDisplayName() },
       });
-      const confirmButtonText = i18n.translate(
-        'common.ui.savedObjects.confirmModal.overwriteButtonLabel',
-        {
-          defaultMessage: 'Overwrite',
-        }
-      );
+      const confirmButtonText = i18n.translate('savedObjects.confirmModal.overwriteButtonLabel', {
+        defaultMessage: 'Overwrite',
+      });
 
       return confirmModalPromise(confirmMessage, title, confirmButtonText, overlays)
         .then(() =>
diff --git a/src/legacy/ui/public/saved_objects/helpers/display_duplicate_title_confirm_modal.ts b/src/plugins/saved_objects/public/saved_object/helpers/display_duplicate_title_confirm_modal.ts
similarity index 79%
rename from src/legacy/ui/public/saved_objects/helpers/display_duplicate_title_confirm_modal.ts
rename to src/plugins/saved_objects/public/saved_object/helpers/display_duplicate_title_confirm_modal.ts
index 36882db72d56c..0b02977830fda 100644
--- a/src/legacy/ui/public/saved_objects/helpers/display_duplicate_title_confirm_modal.ts
+++ b/src/plugins/saved_objects/public/saved_object/helpers/display_duplicate_title_confirm_modal.ts
@@ -18,29 +18,26 @@
  */
 import { i18n } from '@kbn/i18n';
 import { OverlayStart } from 'kibana/public';
-import { SAVE_DUPLICATE_REJECTED } from '../constants';
+import { SAVE_DUPLICATE_REJECTED } from '../../constants';
 import { confirmModalPromise } from './confirm_modal_promise';
-import { SavedObject } from '../types';
+import { SavedObject } from '../../types';
 
 export function displayDuplicateTitleConfirmModal(
   savedObject: SavedObject,
   overlays: OverlayStart
 ): Promise<true> {
   const confirmMessage = i18n.translate(
-    'common.ui.savedObjects.confirmModal.saveDuplicateConfirmationMessage',
+    'savedObjects.confirmModal.saveDuplicateConfirmationMessage',
     {
       defaultMessage: `A {name} with the title '{title}' already exists. Would you like to save anyway?`,
       values: { title: savedObject.title, name: savedObject.getDisplayName() },
     }
   );
 
-  const confirmButtonText = i18n.translate(
-    'common.ui.savedObjects.confirmModal.saveDuplicateButtonLabel',
-    {
-      defaultMessage: 'Save {name}',
-      values: { name: savedObject.getDisplayName() },
-    }
-  );
+  const confirmButtonText = i18n.translate('savedObjects.confirmModal.saveDuplicateButtonLabel', {
+    defaultMessage: 'Save {name}',
+    values: { name: savedObject.getDisplayName() },
+  });
   try {
     return confirmModalPromise(confirmMessage, '', confirmButtonText, overlays);
   } catch (_) {
diff --git a/src/legacy/ui/public/saved_objects/__tests__/find_object_by_title.js b/src/plugins/saved_objects/public/saved_object/helpers/find_object_by_title.test.ts
similarity index 72%
rename from src/legacy/ui/public/saved_objects/__tests__/find_object_by_title.js
rename to src/plugins/saved_objects/public/saved_object/helpers/find_object_by_title.test.ts
index b0e110ede2c80..c88e06ce2119e 100644
--- a/src/legacy/ui/public/saved_objects/__tests__/find_object_by_title.js
+++ b/src/plugins/saved_objects/public/saved_object/helpers/find_object_by_title.test.ts
@@ -17,37 +17,35 @@
  * under the License.
  */
 
-import sinon from 'sinon';
-import expect from '@kbn/expect';
-import { findObjectByTitle } from '../helpers/find_object_by_title';
-import { SimpleSavedObject } from '../../../../../core/public';
+import { findObjectByTitle } from './find_object_by_title';
+import {
+  SimpleSavedObject,
+  SavedObjectsClientContract,
+  SavedObject,
+} from '../../../../../core/public';
 
 describe('findObjectByTitle', () => {
-  const sandbox = sinon.createSandbox();
-  const savedObjectsClient = {};
+  const savedObjectsClient: SavedObjectsClientContract = {} as SavedObjectsClientContract;
 
   beforeEach(() => {
-    savedObjectsClient.find = sandbox.stub();
+    savedObjectsClient.find = jest.fn();
   });
 
-  afterEach(() => sandbox.restore());
-
   it('returns undefined if title is not provided', async () => {
-    const match = await findObjectByTitle(savedObjectsClient, 'index-pattern');
-    expect(match).to.be(undefined);
+    const match = await findObjectByTitle(savedObjectsClient, 'index-pattern', '');
+    expect(match).toBeUndefined();
   });
 
   it('matches any case', async () => {
     const indexPattern = new SimpleSavedObject(savedObjectsClient, {
       attributes: { title: 'foo' },
-    });
-    savedObjectsClient.find.returns(
+    } as SavedObject);
+    savedObjectsClient.find = jest.fn().mockImplementation(() =>
       Promise.resolve({
         savedObjects: [indexPattern],
       })
     );
-
     const match = await findObjectByTitle(savedObjectsClient, 'index-pattern', 'FOO');
-    expect(match).to.eql(indexPattern);
+    expect(match).toEqual(indexPattern);
   });
 });
diff --git a/src/legacy/ui/public/saved_objects/helpers/find_object_by_title.ts b/src/plugins/saved_objects/public/saved_object/helpers/find_object_by_title.ts
similarity index 90%
rename from src/legacy/ui/public/saved_objects/helpers/find_object_by_title.ts
rename to src/plugins/saved_objects/public/saved_object/helpers/find_object_by_title.ts
index 373800f576627..0f1c08930db5e 100644
--- a/src/legacy/ui/public/saved_objects/helpers/find_object_by_title.ts
+++ b/src/plugins/saved_objects/public/saved_object/helpers/find_object_by_title.ts
@@ -17,9 +17,11 @@
  * under the License.
  */
 
-import { SavedObjectAttributes } from 'src/core/server';
-import { SavedObjectsClientContract } from 'src/core/public';
-import { SimpleSavedObject } from 'src/core/public';
+import {
+  SavedObjectsClientContract,
+  SimpleSavedObject,
+  SavedObjectAttributes,
+} from '../../../../../core/public';
 
 /**
  * Returns an object matching a given title
diff --git a/src/legacy/ui/public/saved_objects/helpers/hydrate_index_pattern.ts b/src/plugins/saved_objects/public/saved_object/helpers/hydrate_index_pattern.ts
similarity index 92%
rename from src/legacy/ui/public/saved_objects/helpers/hydrate_index_pattern.ts
rename to src/plugins/saved_objects/public/saved_object/helpers/hydrate_index_pattern.ts
index a78b3f97e884b..b55538e4073ba 100644
--- a/src/legacy/ui/public/saved_objects/helpers/hydrate_index_pattern.ts
+++ b/src/plugins/saved_objects/public/saved_object/helpers/hydrate_index_pattern.ts
@@ -16,8 +16,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { SavedObject, SavedObjectConfig } from '../types';
-import { IndexPatternsContract } from '../../../../../plugins/data/public';
+import { SavedObject, SavedObjectConfig } from '../../types';
+import { IndexPatternsContract } from '../../../../data/public';
 
 /**
  * After creation or fetching from ES, ensure that the searchSources index indexPattern
diff --git a/src/legacy/ui/public/saved_objects/helpers/initialize_saved_object.ts b/src/plugins/saved_objects/public/saved_object/helpers/initialize_saved_object.ts
similarity index 96%
rename from src/legacy/ui/public/saved_objects/helpers/initialize_saved_object.ts
rename to src/plugins/saved_objects/public/saved_object/helpers/initialize_saved_object.ts
index a9d4b416add1e..ae8d7ac8a6526 100644
--- a/src/legacy/ui/public/saved_objects/helpers/initialize_saved_object.ts
+++ b/src/plugins/saved_objects/public/saved_object/helpers/initialize_saved_object.ts
@@ -18,7 +18,7 @@
  */
 import _ from 'lodash';
 import { SavedObjectsClientContract } from 'kibana/public';
-import { SavedObject, SavedObjectConfig } from '../types';
+import { SavedObject, SavedObjectConfig } from '../../types';
 
 /**
  * Initialize saved object
diff --git a/src/legacy/ui/public/saved_objects/helpers/parse_search_source.ts b/src/plugins/saved_objects/public/saved_object/helpers/parse_search_source.ts
similarity index 94%
rename from src/legacy/ui/public/saved_objects/helpers/parse_search_source.ts
rename to src/plugins/saved_objects/public/saved_object/helpers/parse_search_source.ts
index b194420fc1cd0..cdb191f9e7df8 100644
--- a/src/legacy/ui/public/saved_objects/helpers/parse_search_source.ts
+++ b/src/plugins/saved_objects/public/saved_object/helpers/parse_search_source.ts
@@ -17,9 +17,9 @@
  * under the License.
  */
 import _ from 'lodash';
-import { migrateLegacyQuery } from '../../../../../plugins/kibana_legacy/public';
-import { SavedObject } from '../types';
-import { InvalidJSONProperty } from '../../../../../plugins/kibana_utils/public';
+import { migrateLegacyQuery } from '../../../../kibana_legacy/public';
+import { SavedObject } from '../../types';
+import { InvalidJSONProperty } from '../../../../kibana_utils/public';
 
 export function parseSearchSource(
   savedObject: SavedObject,
diff --git a/src/legacy/ui/public/saved_objects/helpers/save_saved_object.ts b/src/plugins/saved_objects/public/saved_object/helpers/save_saved_object.ts
similarity index 99%
rename from src/legacy/ui/public/saved_objects/helpers/save_saved_object.ts
rename to src/plugins/saved_objects/public/saved_object/helpers/save_saved_object.ts
index bd6daa1832a25..e43619c2692a5 100644
--- a/src/legacy/ui/public/saved_objects/helpers/save_saved_object.ts
+++ b/src/plugins/saved_objects/public/saved_object/helpers/save_saved_object.ts
@@ -21,8 +21,8 @@ import {
   SavedObjectConfig,
   SavedObjectKibanaServices,
   SavedObjectSaveOpts,
-} from '../types';
-import { OVERWRITE_REJECTED, SAVE_DUPLICATE_REJECTED } from '../constants';
+} from '../../types';
+import { OVERWRITE_REJECTED, SAVE_DUPLICATE_REJECTED } from '../../constants';
 import { createSource } from './create_source';
 import { checkForDuplicateTitle } from './check_for_duplicate_title';
 
diff --git a/src/legacy/ui/public/saved_objects/helpers/serialize_saved_object.ts b/src/plugins/saved_objects/public/saved_object/helpers/serialize_saved_object.ts
similarity index 96%
rename from src/legacy/ui/public/saved_objects/helpers/serialize_saved_object.ts
rename to src/plugins/saved_objects/public/saved_object/helpers/serialize_saved_object.ts
index ca780f8f9584d..8a020ca03aea3 100644
--- a/src/legacy/ui/public/saved_objects/helpers/serialize_saved_object.ts
+++ b/src/plugins/saved_objects/public/saved_object/helpers/serialize_saved_object.ts
@@ -18,8 +18,8 @@
  */
 import _ from 'lodash';
 import angular from 'angular';
-import { SavedObject, SavedObjectConfig } from '../types';
-import { expandShorthand } from '../../../../../plugins/kibana_utils/public';
+import { SavedObject, SavedObjectConfig } from '../../types';
+import { expandShorthand } from '../../../../kibana_utils/public';
 
 export function serializeSavedObject(savedObject: SavedObject, config: SavedObjectConfig) {
   // mapping definition for the fields that this object will expose
diff --git a/src/legacy/ui/public/saved_objects/helpers/string_utils.test.ts b/src/plugins/saved_objects/public/saved_object/helpers/string_utils.test.ts
similarity index 99%
rename from src/legacy/ui/public/saved_objects/helpers/string_utils.test.ts
rename to src/plugins/saved_objects/public/saved_object/helpers/string_utils.test.ts
index 5b108a4cc0180..b71173f3a089d 100644
--- a/src/legacy/ui/public/saved_objects/helpers/string_utils.test.ts
+++ b/src/plugins/saved_objects/public/saved_object/helpers/string_utils.test.ts
@@ -16,6 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 import { StringUtils } from './string_utils';
 
 describe('StringUtils class', () => {
diff --git a/src/legacy/ui/public/saved_objects/helpers/string_utils.ts b/src/plugins/saved_objects/public/saved_object/helpers/string_utils.ts
similarity index 100%
rename from src/legacy/ui/public/saved_objects/helpers/string_utils.ts
rename to src/plugins/saved_objects/public/saved_object/helpers/string_utils.ts
diff --git a/src/legacy/ui/public/saved_objects/index.ts b/src/plugins/saved_objects/public/saved_object/index.ts
similarity index 92%
rename from src/legacy/ui/public/saved_objects/index.ts
rename to src/plugins/saved_objects/public/saved_object/index.ts
index 03c5a64fdd45e..d3be5ea6df617 100644
--- a/src/legacy/ui/public/saved_objects/index.ts
+++ b/src/plugins/saved_objects/public/saved_object/index.ts
@@ -17,5 +17,5 @@
  * under the License.
  */
 
+export { createSavedObjectClass } from './saved_object';
 export { SavedObjectLoader } from './saved_object_loader';
-export { findObjectByTitle } from './helpers/find_object_by_title';
diff --git a/src/legacy/ui/public/saved_objects/__tests__/saved_object.js b/src/plugins/saved_objects/public/saved_object/saved_object.test.ts
similarity index 52%
rename from src/legacy/ui/public/saved_objects/__tests__/saved_object.js
rename to src/plugins/saved_objects/public/saved_object/saved_object.test.ts
index d027d8b6c99da..08389e9e3c97f 100644
--- a/src/legacy/ui/public/saved_objects/__tests__/saved_object.js
+++ b/src/plugins/saved_objects/public/saved_object/saved_object.test.ts
@@ -17,25 +17,32 @@
  * under the License.
  */
 
-import ngMock from 'ng_mock';
-import expect from '@kbn/expect';
-import sinon from 'sinon';
 import Bluebird from 'bluebird';
-
-import { createSavedObjectClass } from '../saved_object';
+import { createSavedObjectClass } from './saved_object';
+import {
+  SavedObject,
+  SavedObjectConfig,
+  SavedObjectKibanaServices,
+  SavedObjectSaveOpts,
+} from '../types';
+
+// @ts-ignore
 import StubIndexPattern from 'test_utils/stub_index_pattern';
-import { npStart } from 'ui/new_platform';
-import { InvalidJSONProperty } from '../../../../../plugins/kibana_utils/public';
-import { npSetup, npStart as npStartMock } from '../../new_platform/new_platform.karma_mock';
+import { InvalidJSONProperty } from '../../../kibana_utils/public';
+import { coreMock } from '../../../../core/public/mocks';
+import { dataPluginMock } from '../../../../plugins/data/public/mocks';
+import { SavedObjectAttributes, SimpleSavedObject } from 'kibana/public';
+import { IIndexPattern } from '../../../data/common/index_patterns';
 
-const getConfig = cfg => cfg;
+const getConfig = (cfg: any) => cfg;
 
-describe('Saved Object', function() {
-  require('test_utils/no_digest_promises').activateForSuite();
+describe('Saved Object', () => {
+  const startMock = coreMock.createStart();
+  const dataStartMock = dataPluginMock.createStartContract();
+  const saveOptionsMock = {} as SavedObjectSaveOpts;
 
-  let SavedObject;
-  let savedObjectsClientStub;
-  let window;
+  let SavedObjectClass: new (config: SavedObjectConfig) => SavedObject;
+  const savedObjectsClientStub = startMock.savedObjects.client;
 
   /**
    * Returns a fake doc response with the given index and id, of type dashboard
@@ -44,31 +51,41 @@ describe('Saved Object', function() {
    * @param additionalOptions - object that will be assigned to the mocked doc response.
    * @returns {{attributes: {}, type: string, id: *, _version: string}}
    */
-  function getMockedDocResponse(indexPatternId, additionalOptions = {}) {
+  function getMockedDocResponse(indexPatternId: string, additionalOptions = {}) {
     return {
       type: 'dashboard',
       id: indexPatternId,
       _version: 'foo',
       attributes: {},
       ...additionalOptions,
-    };
+    } as SimpleSavedObject<SavedObjectAttributes>;
   }
 
   /**
    * Stubs some of the es retrieval calls so it returns the given response.
    * @param {Object} mockDocResponse
    */
-  function stubESResponse(mockDocResponse) {
+  function stubESResponse(mockDocResponse: SimpleSavedObject<SavedObjectAttributes>) {
     // Stub out search for duplicate title:
-    sinon.stub(savedObjectsClientStub, 'get').returns(Bluebird.resolve(mockDocResponse));
-    sinon.stub(savedObjectsClientStub, 'update').returns(Bluebird.resolve(mockDocResponse));
-
-    sinon
-      .stub(savedObjectsClientStub, 'find')
-      .returns(Bluebird.resolve({ savedObjects: [], total: 0 }));
-    sinon
-      .stub(savedObjectsClientStub, 'bulkGet')
-      .returns(Bluebird.resolve({ savedObjects: [mockDocResponse] }));
+    savedObjectsClientStub.get = jest.fn().mockReturnValue(Bluebird.resolve(mockDocResponse));
+    savedObjectsClientStub.update = jest.fn().mockReturnValue(Bluebird.resolve(mockDocResponse));
+
+    savedObjectsClientStub.find = jest
+      .fn()
+      .mockReturnValue(Bluebird.resolve({ savedObjects: [], total: 0 }));
+
+    savedObjectsClientStub.bulkGet = jest
+      .fn()
+      .mockReturnValue(Bluebird.resolve({ savedObjects: [mockDocResponse] }));
+  }
+
+  function stubSavedObjectsClientCreate(
+    resp: SimpleSavedObject<SavedObjectAttributes> | string,
+    resolve = true
+  ) {
+    savedObjectsClientStub.create = jest
+      .fn()
+      .mockReturnValue(resolve ? Bluebird.resolve(resp) : Bluebird.reject(resp));
   }
 
   /**
@@ -79,168 +96,138 @@ describe('Saved Object', function() {
    * @returns {Promise<SavedObject>} A promise that resolves with an instance of
    * SavedObject
    */
-  function createInitializedSavedObject(config = {}) {
-    const savedObject = new SavedObject(config);
+  function createInitializedSavedObject(config: SavedObjectConfig = {}) {
+    const savedObject = new SavedObjectClass(config);
     savedObject.title = 'my saved object';
-    return savedObject.init();
-  }
-
-  function restoreIfWrapped(obj, fName) {
-    obj[fName].restore && obj[fName].restore();
+    return savedObject.init!();
   }
 
   beforeEach(() => {
-    // Use the native window.confirm instead of our specialized version to make testing
-    // this easier.
-    npStartMock.core.overlays.openModal = message =>
-      window.confirm(message) ? Promise.resolve() : Promise.reject();
+    SavedObjectClass = createSavedObjectClass({
+      savedObjectsClient: savedObjectsClientStub,
+      indexPatterns: dataStartMock.indexPatterns,
+    } as SavedObjectKibanaServices);
   });
 
-  beforeEach(
-    ngMock.inject(function($window) {
-      savedObjectsClientStub = npStart.core.savedObjects.client;
-      SavedObject = createSavedObjectClass({ savedObjectsClient: savedObjectsClientStub });
-      window = $window;
-    })
-  );
-
-  afterEach(
-    ngMock.inject(function() {
-      restoreIfWrapped(savedObjectsClientStub, 'create');
-      restoreIfWrapped(savedObjectsClientStub, 'get');
-      restoreIfWrapped(savedObjectsClientStub, 'update');
-      restoreIfWrapped(savedObjectsClientStub, 'find');
-      restoreIfWrapped(savedObjectsClientStub, 'bulkGet');
-    })
-  );
-
-  describe('save', function() {
-    describe('with confirmOverwrite', function() {
-      function stubConfirmOverwrite() {
-        window.confirm = sinon.stub().returns(true);
-      }
-
-      it('when false does not request overwrite', function() {
-        const mockDocResponse = getMockedDocResponse('myId');
-        stubESResponse(mockDocResponse);
+  describe('save', () => {
+    describe('with confirmOverwrite', () => {
+      it('when false does not request overwrite', () => {
+        stubESResponse(getMockedDocResponse('myId'));
 
         return createInitializedSavedObject({ type: 'dashboard', id: 'myId' }).then(savedObject => {
-          stubConfirmOverwrite();
-
-          sinon.stub(savedObjectsClientStub, 'create').returns(Bluebird.resolve({ id: 'myId' }));
+          stubSavedObjectsClientCreate({ id: 'myId' } as SimpleSavedObject<SavedObjectAttributes>);
 
           return savedObject.save({ confirmOverwrite: false }).then(() => {
-            expect(window.confirm.called).to.be(false);
+            expect(startMock.overlays.openModal).not.toHaveBeenCalled();
           });
         });
       });
     });
 
-    describe('with copyOnSave', function() {
-      it('as true creates a copy on save success', function() {
-        const mockDocResponse = getMockedDocResponse('myId');
-        stubESResponse(mockDocResponse);
-        return createInitializedSavedObject({ type: 'dashboard', id: 'myId' }).then(savedObject => {
-          sinon.stub(savedObjectsClientStub, 'create').callsFake(() => {
-            return Bluebird.resolve({ type: 'dashboard', id: 'newUniqueId' });
-          });
+    describe('with copyOnSave', () => {
+      it('as true creates a copy on save success', () => {
+        stubESResponse(getMockedDocResponse('myId'));
 
+        return createInitializedSavedObject({ type: 'dashboard', id: 'myId' }).then(savedObject => {
+          stubSavedObjectsClientCreate({
+            type: 'dashboard',
+            id: 'newUniqueId',
+          } as SimpleSavedObject<SavedObjectAttributes>);
           savedObject.copyOnSave = true;
-          return savedObject.save().then(id => {
-            expect(id).to.be('newUniqueId');
+
+          return savedObject.save(saveOptionsMock).then(id => {
+            expect(id).toBe('newUniqueId');
           });
         });
       });
 
-      it('as true does not create a copy when save fails', function() {
+      it('as true does not create a copy when save fails', () => {
         const originalId = 'id1';
-        const mockDocResponse = getMockedDocResponse(originalId);
-        stubESResponse(mockDocResponse);
+        stubESResponse(getMockedDocResponse(originalId));
+
         return createInitializedSavedObject({ type: 'dashboard', id: originalId }).then(
           savedObject => {
-            sinon.stub(savedObjectsClientStub, 'create').callsFake(() => {
-              return Bluebird.reject('simulated error');
-            });
+            stubSavedObjectsClientCreate('simulated error', false);
             savedObject.copyOnSave = true;
+
             return savedObject
-              .save()
+              .save(saveOptionsMock)
               .then(() => {
-                throw new Error('Expected a rejection');
+                expect(false).toBe(true);
               })
               .catch(() => {
-                expect(savedObject.id).to.be(originalId);
+                expect(savedObject.id).toBe(originalId);
               });
           }
         );
       });
 
-      it('as false does not create a copy', function() {
-        const id = 'myId';
-        const mockDocResponse = getMockedDocResponse(id);
-        stubESResponse(mockDocResponse);
+      it('as false does not create a copy', () => {
+        const myId = 'myId';
+        stubESResponse(getMockedDocResponse(myId));
 
-        return createInitializedSavedObject({ type: 'dashboard', id: id }).then(savedObject => {
-          sinon.stub(savedObjectsClientStub, 'create').callsFake(() => {
-            expect(savedObject.id).to.be(id);
-            return Bluebird.resolve(id);
+        return createInitializedSavedObject({ type: 'dashboard', id: myId }).then(savedObject => {
+          savedObjectsClientStub.create = jest.fn().mockImplementation(() => {
+            expect(savedObject.id).toBe(myId);
+            return Bluebird.resolve({ id: myId });
           });
           savedObject.copyOnSave = false;
-          return savedObject.save().then(id => {
-            expect(id).to.be(id);
+
+          return savedObject.save(saveOptionsMock).then(id => {
+            expect(id).toBe(myId);
           });
         });
       });
     });
 
-    it('returns id from server on success', function() {
+    it('returns id from server on success', () => {
       return createInitializedSavedObject({ type: 'dashboard' }).then(savedObject => {
-        const mockDocResponse = getMockedDocResponse('myId');
-        sinon.stub(savedObjectsClientStub, 'create').callsFake(() => {
-          return Bluebird.resolve({
-            type: 'dashboard',
-            id: 'myId',
-            _version: 'foo',
-          });
-        });
+        stubESResponse(getMockedDocResponse('myId'));
+        stubSavedObjectsClientCreate({
+          type: 'dashboard',
+          id: 'myId',
+          _version: 'foo',
+        } as SimpleSavedObject<SavedObjectAttributes>);
 
-        stubESResponse(mockDocResponse);
-        return savedObject.save().then(id => {
-          expect(id).to.be('myId');
+        return savedObject.save(saveOptionsMock).then(id => {
+          expect(id).toBe('myId');
         });
       });
     });
 
-    describe('updates isSaving variable', function() {
-      it('on success', function() {
+    describe('updates isSaving variable', () => {
+      it('on success', () => {
         const id = 'id';
         stubESResponse(getMockedDocResponse(id));
 
-        return createInitializedSavedObject({ type: 'dashboard', id: id }).then(savedObject => {
-          sinon.stub(savedObjectsClientStub, 'create').callsFake(() => {
-            expect(savedObject.isSaving).to.be(true);
+        return createInitializedSavedObject({ type: 'dashboard', id }).then(savedObject => {
+          savedObjectsClientStub.create = jest.fn().mockImplementation(() => {
+            expect(savedObject.isSaving).toBe(true);
             return Bluebird.resolve({
               type: 'dashboard',
               id,
-              version: 'foo',
+              _version: 'foo',
             });
           });
-          expect(savedObject.isSaving).to.be(false);
-          return savedObject.save().then(() => {
-            expect(savedObject.isSaving).to.be(false);
+
+          expect(savedObject.isSaving).toBe(false);
+          return savedObject.save(saveOptionsMock).then(() => {
+            expect(savedObject.isSaving).toBe(false);
           });
         });
       });
 
-      it('on failure', function() {
+      it('on failure', () => {
         stubESResponse(getMockedDocResponse('id'));
         return createInitializedSavedObject({ type: 'dashboard' }).then(savedObject => {
-          sinon.stub(savedObjectsClientStub, 'create').callsFake(() => {
-            expect(savedObject.isSaving).to.be(true);
-            return Bluebird.reject();
+          savedObjectsClientStub.create = jest.fn().mockImplementation(() => {
+            expect(savedObject.isSaving).toBe(true);
+            return Bluebird.reject('');
           });
-          expect(savedObject.isSaving).to.be(false);
-          return savedObject.save().catch(() => {
-            expect(savedObject.isSaving).to.be(false);
+
+          expect(savedObject.isSaving).toBe(false);
+          return savedObject.save(saveOptionsMock).catch(() => {
+            expect(savedObject.isSaving).toBe(false);
           });
         });
       });
@@ -250,7 +237,10 @@ describe('Saved Object', function() {
       it('when "extractReferences" function when passed in', async () => {
         const id = '123';
         stubESResponse(getMockedDocResponse(id));
-        const extractReferences = ({ attributes, references }) => {
+        const extractReferences: SavedObjectConfig['extractReferences'] = ({
+          attributes,
+          references,
+        }) => {
           references.push({
             name: 'test',
             type: 'index-pattern',
@@ -260,17 +250,16 @@ describe('Saved Object', function() {
         };
         return createInitializedSavedObject({ type: 'dashboard', extractReferences }).then(
           savedObject => {
-            sinon.stub(savedObjectsClientStub, 'create').callsFake(() => {
-              return Bluebird.resolve({
-                id,
-                version: 'foo',
-                type: 'dashboard',
-              });
-            });
-            return savedObject.save().then(() => {
-              const { references } = savedObjectsClientStub.create.getCall(0).args[2];
-              expect(references).to.have.length(1);
-              expect(references[0]).to.eql({
+            stubSavedObjectsClientCreate({
+              id,
+              _version: 'foo',
+              type: 'dashboard',
+            } as SimpleSavedObject<SavedObjectAttributes>);
+
+            return savedObject.save(saveOptionsMock).then(() => {
+              const { references } = (savedObjectsClientStub.create as jest.Mock).mock.calls[0][2];
+              expect(references).toHaveLength(1);
+              expect(references[0]).toEqual({
                 name: 'test',
                 type: 'index-pattern',
                 id: 'my-index',
@@ -285,33 +274,33 @@ describe('Saved Object', function() {
         stubESResponse(getMockedDocResponse(id));
         return createInitializedSavedObject({ type: 'dashboard', searchSource: true }).then(
           savedObject => {
-            sinon.stub(savedObjectsClientStub, 'create').callsFake(() => {
-              return Bluebird.resolve({
-                id,
-                version: 2,
-                type: 'dashboard',
-              });
-            });
+            stubSavedObjectsClientCreate({
+              id,
+              _version: '2',
+              type: 'dashboard',
+            } as SimpleSavedObject<SavedObjectAttributes>);
+
             const indexPattern = new StubIndexPattern(
               'my-index',
               getConfig,
               null,
               [],
-              npSetup.core
+              coreMock.createSetup()
             );
             indexPattern.title = indexPattern.id;
-            savedObject.searchSource.setField('index', indexPattern);
-            return savedObject.save().then(() => {
-              expect(savedObjectsClientStub.create.getCall(0).args[1]).to.eql({
+            savedObject.searchSource!.setField('index', indexPattern);
+            return savedObject.save(saveOptionsMock).then(() => {
+              const args = (savedObjectsClientStub.create as jest.Mock).mock.calls[0];
+              expect(args[1]).toEqual({
                 kibanaSavedObjectMeta: {
                   searchSourceJSON: JSON.stringify({
                     indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.index',
                   }),
                 },
               });
-              const { references } = savedObjectsClientStub.create.getCall(0).args[2];
-              expect(references).to.have.length(1);
-              expect(references[0]).to.eql({
+
+              expect(args[2].references).toHaveLength(1);
+              expect(args[2].references[0]).toEqual({
                 name: 'kibanaSavedObjectMeta.searchSourceJSON.index',
                 type: 'index-pattern',
                 id: 'my-index',
@@ -326,25 +315,31 @@ describe('Saved Object', function() {
         stubESResponse(getMockedDocResponse(id));
         return createInitializedSavedObject({ type: 'dashboard', searchSource: true }).then(
           savedObject => {
-            sinon.stub(savedObjectsClientStub, 'create').callsFake(() => {
-              return Bluebird.resolve({
-                id,
-                version: 2,
-                type: 'dashboard',
-              });
-            });
-            savedObject.searchSource.setFields({ index: 'non-existant-index' });
-            return savedObject.save().then(() => {
-              expect(savedObjectsClientStub.create.getCall(0).args[1]).to.eql({
+            stubSavedObjectsClientCreate({
+              id,
+              _version: '2',
+              type: 'dashboard',
+            } as SimpleSavedObject<SavedObjectAttributes>);
+
+            const indexPattern = new StubIndexPattern(
+              'non-existant-index',
+              getConfig,
+              null,
+              [],
+              coreMock.createSetup()
+            );
+            savedObject.searchSource!.setFields({ index: indexPattern });
+            return savedObject.save(saveOptionsMock).then(() => {
+              const args = (savedObjectsClientStub.create as jest.Mock).mock.calls[0];
+              expect(args[1]).toEqual({
                 kibanaSavedObjectMeta: {
                   searchSourceJSON: JSON.stringify({
                     indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.index',
                   }),
                 },
               });
-              const { references } = savedObjectsClientStub.create.getCall(0).args[2];
-              expect(references).to.have.length(1);
-              expect(references[0]).to.eql({
+              expect(args[2].references).toHaveLength(1);
+              expect(args[2].references[0]).toEqual({
                 name: 'kibanaSavedObjectMeta.searchSourceJSON.index',
                 type: 'index-pattern',
                 id: 'non-existant-index',
@@ -359,22 +354,22 @@ describe('Saved Object', function() {
         stubESResponse(getMockedDocResponse(id));
         return createInitializedSavedObject({ type: 'dashboard', searchSource: true }).then(
           savedObject => {
-            sinon.stub(savedObjectsClientStub, 'create').callsFake(() => {
-              return Bluebird.resolve({
-                id,
-                version: 2,
-                type: 'dashboard',
-              });
-            });
-            savedObject.searchSource.setField('filter', [
+            stubSavedObjectsClientCreate({
+              id,
+              _version: '2',
+              type: 'dashboard',
+            } as SimpleSavedObject<SavedObjectAttributes>);
+
+            savedObject.searchSource!.setField('filter', [
               {
                 meta: {
                   index: 'my-index',
                 },
               },
-            ]);
-            return savedObject.save().then(() => {
-              expect(savedObjectsClientStub.create.getCall(0).args[1]).to.eql({
+            ] as any);
+            return savedObject.save(saveOptionsMock).then(() => {
+              const args = (savedObjectsClientStub.create as jest.Mock).mock.calls[0];
+              expect(args[1]).toEqual({
                 kibanaSavedObjectMeta: {
                   searchSourceJSON: JSON.stringify({
                     filter: [
@@ -388,9 +383,8 @@ describe('Saved Object', function() {
                   }),
                 },
               });
-              const { references } = savedObjectsClientStub.create.getCall(0).args[2];
-              expect(references).to.have.length(1);
-              expect(references[0]).to.eql({
+              expect(args[2].references).toHaveLength(1);
+              expect(args[2].references[0]).toEqual({
                 name: 'kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index',
                 type: 'index-pattern',
                 id: 'my-index',
@@ -402,20 +396,20 @@ describe('Saved Object', function() {
     });
   });
 
-  describe('applyESResp', function() {
-    it('throws error if not found', function() {
+  describe('applyESResp', () => {
+    it('throws error if not found', () => {
       return createInitializedSavedObject({ type: 'dashboard' }).then(savedObject => {
-        const response = {};
+        const response = { _source: {} };
         try {
           savedObject.applyESResp(response);
-          expect(true).to.be(false);
+          expect(true).toBe(false);
         } catch (err) {
-          expect(!!err).to.be(true);
+          expect(!!err).toBe(true);
         }
       });
     });
 
-    it('throws error invalid JSON is detected', async function() {
+    it('throws error invalid JSON is detected', async () => {
       const savedObject = await createInitializedSavedObject({
         type: 'dashboard',
         searchSource: true,
@@ -433,11 +427,11 @@ describe('Saved Object', function() {
         await savedObject.applyESResp(response);
         throw new Error('applyESResp should have failed, but did not.');
       } catch (err) {
-        expect(err instanceof InvalidJSONProperty).to.be(true);
+        expect(err instanceof InvalidJSONProperty).toBe(true);
       }
     });
 
-    it('preserves original defaults if not overridden', function() {
+    it('preserves original defaults if not overridden', () => {
       const id = 'anid';
       const preserveMeValue = 'here to stay!';
       const config = {
@@ -445,43 +439,40 @@ describe('Saved Object', function() {
           preserveMe: preserveMeValue,
         },
         type: 'dashboard',
-        id: id,
+        id,
       };
 
       const mockDocResponse = getMockedDocResponse(id);
       stubESResponse(mockDocResponse);
 
-      const savedObject = new SavedObject(config);
-      return savedObject
-        .init()
+      const savedObject = new SavedObjectClass(config);
+      return savedObject.init!()
         .then(() => {
-          expect(savedObject._source.preserveMe).to.equal(preserveMeValue);
+          expect(savedObject._source.preserveMe).toEqual(preserveMeValue);
           const response = { found: true, _source: {} };
           return savedObject.applyESResp(response);
         })
         .then(() => {
-          expect(savedObject._source.preserveMe).to.equal(preserveMeValue);
+          expect(savedObject._source.preserveMe).toEqual(preserveMeValue);
         });
     });
 
-    it('overrides defaults', function() {
+    it('overrides defaults', () => {
       const id = 'anid';
       const config = {
         defaults: {
           flower: 'rose',
         },
         type: 'dashboard',
-        id: id,
+        id,
       };
 
-      const mockDocResponse = getMockedDocResponse(id);
-      stubESResponse(mockDocResponse);
+      stubESResponse(getMockedDocResponse(id));
 
-      const savedObject = new SavedObject(config);
-      return savedObject
-        .init()
+      const savedObject = new SavedObjectClass(config);
+      return savedObject.init!()
         .then(() => {
-          expect(savedObject._source.flower).to.equal('rose');
+          expect(savedObject._source.flower).toEqual('rose');
           const response = {
             found: true,
             _source: {
@@ -491,11 +482,11 @@ describe('Saved Object', function() {
           return savedObject.applyESResp(response);
         })
         .then(() => {
-          expect(savedObject._source.flower).to.equal('orchid');
+          expect(savedObject._source.flower).toEqual('orchid');
         });
     });
 
-    it('overrides previous _source and default values', function() {
+    it('overrides previous _source and default values', () => {
       const id = 'anid';
       const config = {
         defaults: {
@@ -504,7 +495,7 @@ describe('Saved Object', function() {
           },
         },
         type: 'dashboard',
-        id: id,
+        id,
       };
 
       const mockDocResponse = getMockedDocResponse(id, {
@@ -512,9 +503,8 @@ describe('Saved Object', function() {
       });
       stubESResponse(mockDocResponse);
 
-      const savedObject = new SavedObject(config);
-      return savedObject
-        .init()
+      const savedObject = new SavedObjectClass(config);
+      return savedObject.init!()
         .then(() => {
           const response = {
             found: true,
@@ -524,19 +514,18 @@ describe('Saved Object', function() {
           return savedObject.applyESResp(response);
         })
         .then(() => {
-          expect(savedObject._source.dinosaurs.tRex).to.equal('has big teeth');
+          expect((savedObject._source as any).dinosaurs.tRex).toEqual('has big teeth');
         });
     });
 
     it('does not inject references when references array is missing', async () => {
-      const injectReferences = sinon.stub();
+      const injectReferences = jest.fn();
       const config = {
         type: 'dashboard',
         injectReferences,
       };
-      const savedObject = new SavedObject(config);
-      return savedObject
-        .init()
+      const savedObject = new SavedObjectClass(config);
+      return savedObject.init!()
         .then(() => {
           const response = {
             found: true,
@@ -547,19 +536,18 @@ describe('Saved Object', function() {
           return savedObject.applyESResp(response);
         })
         .then(() => {
-          expect(injectReferences).to.have.property('notCalled', true);
+          expect(injectReferences).not.toHaveBeenCalled();
         });
     });
 
     it('does not inject references when references array is empty', async () => {
-      const injectReferences = sinon.stub();
+      const injectReferences = jest.fn();
       const config = {
         type: 'dashboard',
         injectReferences,
       };
-      const savedObject = new SavedObject(config);
-      return savedObject
-        .init()
+      const savedObject = new SavedObjectClass(config);
+      return savedObject.init!()
         .then(() => {
           const response = {
             found: true,
@@ -571,19 +559,18 @@ describe('Saved Object', function() {
           return savedObject.applyESResp(response);
         })
         .then(() => {
-          expect(injectReferences).to.have.property('notCalled', true);
+          expect(injectReferences).not.toHaveBeenCalled();
         });
     });
 
     it('injects references when function is provided and references exist', async () => {
-      const injectReferences = sinon.stub();
+      const injectReferences = jest.fn();
       const config = {
         type: 'dashboard',
         injectReferences,
       };
-      const savedObject = new SavedObject(config);
-      return savedObject
-        .init()
+      const savedObject = new SavedObjectClass(config);
+      return savedObject.init!()
         .then(() => {
           const response = {
             found: true,
@@ -595,13 +582,13 @@ describe('Saved Object', function() {
           return savedObject.applyESResp(response);
         })
         .then(() => {
-          expect(injectReferences).to.have.property('calledOnce', true);
+          expect(injectReferences).toHaveBeenCalledTimes(1);
         });
     });
 
     it('injects references from searchSourceJSON', async () => {
-      const savedObject = new SavedObject({ type: 'dashboard', searchSource: true });
-      return savedObject.init().then(() => {
+      const savedObject = new SavedObjectClass({ type: 'dashboard', searchSource: true });
+      return savedObject.init!().then(() => {
         const response = {
           found: true,
           _source: {
@@ -632,7 +619,7 @@ describe('Saved Object', function() {
           ],
         };
         savedObject.applyESResp(response);
-        expect(savedObject.searchSource.getFields()).to.eql({
+        expect(savedObject.searchSource!.getFields()).toEqual({
           index: 'my-index-1',
           filter: [
             {
@@ -646,119 +633,115 @@ describe('Saved Object', function() {
     });
   });
 
-  describe('config', function() {
-    it('afterESResp is called', function() {
-      const afterESRespCallback = sinon.spy();
+  describe('config', () => {
+    it('afterESResp is called', () => {
+      const afterESRespCallback = jest.fn();
       const config = {
         type: 'dashboard',
         afterESResp: afterESRespCallback,
       };
 
       return createInitializedSavedObject(config).then(() => {
-        expect(afterESRespCallback.called).to.be(true);
+        expect(afterESRespCallback).toHaveBeenCalled();
       });
     });
 
-    describe('searchSource', function() {
-      it('when true, creates index', function() {
+    describe('searchSource', () => {
+      it('when true, creates index', () => {
         const indexPatternId = 'testIndexPattern';
-        const afterESRespCallback = sinon.spy();
+        const afterESRespCallback = jest.fn();
 
-        const config = {
+        const config: SavedObjectConfig = {
           type: 'dashboard',
           afterESResp: afterESRespCallback,
           searchSource: true,
-          indexPattern: indexPatternId,
+          indexPattern: { id: indexPatternId } as IIndexPattern,
         };
 
-        stubESResponse({
-          id: indexPatternId,
-          type: 'dashboard',
-          attributes: {
-            title: 'testIndexPattern',
-          },
-          _version: 'foo',
-        });
+        stubESResponse(
+          getMockedDocResponse(indexPatternId, {
+            attributes: {
+              title: 'testIndexPattern',
+            },
+          })
+        );
 
-        const savedObject = new SavedObject(config);
-        sinon.stub(savedObject, 'hydrateIndexPattern').callsFake(() => {
+        const savedObject = new SavedObjectClass(config);
+        savedObject.hydrateIndexPattern = jest.fn().mockImplementation(() => {
           const indexPattern = new StubIndexPattern(
             indexPatternId,
             getConfig,
             null,
             [],
-            npSetup.core
+            coreMock.createSetup()
           );
           indexPattern.title = indexPattern.id;
-          savedObject.searchSource.setField('index', indexPattern);
-          return Promise.resolve(indexPattern);
+          savedObject.searchSource!.setField('index', indexPattern);
+          return Bluebird.resolve(indexPattern);
         });
-        expect(!!savedObject.searchSource.getField('index')).to.be(false);
+        expect(!!savedObject.searchSource!.getField('index')).toBe(false);
 
-        return savedObject.init().then(() => {
-          expect(afterESRespCallback.called).to.be(true);
-          const index = savedObject.searchSource.getField('index');
-          expect(index instanceof StubIndexPattern).to.be(true);
-          expect(index.id).to.equal(indexPatternId);
+        return savedObject.init!().then(() => {
+          expect(afterESRespCallback).toHaveBeenCalled();
+          const index = savedObject.searchSource!.getField('index');
+          expect(index instanceof StubIndexPattern).toBe(true);
+          expect(index!.id).toEqual(indexPatternId);
         });
       });
 
-      it('when false, does not create index', function() {
+      it('when false, does not create index', () => {
         const indexPatternId = 'testIndexPattern';
-        const afterESRespCallback = sinon.spy();
+        const afterESRespCallback = jest.fn();
 
-        const config = {
+        const config: SavedObjectConfig = {
           type: 'dashboard',
           afterESResp: afterESRespCallback,
           searchSource: false,
-          indexPattern: indexPatternId,
+          indexPattern: { id: indexPatternId } as IIndexPattern,
         };
 
         stubESResponse(getMockedDocResponse(indexPatternId));
 
-        const savedObject = new SavedObject(config);
-        expect(!!savedObject.searchSource).to.be(false);
+        const savedObject = new SavedObjectClass(config);
+        expect(!!savedObject.searchSource).toBe(false);
 
-        return savedObject.init().then(() => {
-          expect(afterESRespCallback.called).to.be(true);
-          expect(!!savedObject.searchSource).to.be(false);
+        return savedObject.init!().then(() => {
+          expect(afterESRespCallback).toHaveBeenCalled();
+          expect(!!savedObject.searchSource).toBe(false);
         });
       });
     });
 
-    describe('type', function() {
-      it('that is not specified throws an error', function() {
+    describe('type', () => {
+      it('that is not specified throws an error', done => {
         const config = {};
 
-        const savedObject = new SavedObject(config);
-        try {
-          savedObject.init();
-          expect(false).to.be(true);
-        } catch (err) {
-          expect(err).to.not.be(null);
-        }
+        const savedObject = new SavedObjectClass(config);
+        savedObject.init!().catch(() => {
+          done();
+        });
       });
 
-      it('that is invalid invalid throws an error', function() {
+      it('that is invalid invalid throws an error', () => {
         const config = { type: 'notypeexists' };
 
-        const savedObject = new SavedObject(config);
+        const savedObject = new SavedObjectClass(config);
         try {
-          savedObject.init();
-          expect(false).to.be(true);
+          savedObject.init!();
+          expect(false).toBe(true);
         } catch (err) {
-          expect(err).to.not.be(null);
+          expect(err).not.toBeNull();
         }
       });
 
-      it('that is valid passes', function() {
+      it('that is valid passes', () => {
         const config = { type: 'dashboard' };
-        return new SavedObject(config).init();
+        return new SavedObjectClass(config).init!();
       });
     });
 
-    describe('defaults', function() {
-      function getTestDefaultConfig(extraOptions) {
+    describe('defaults', () => {
+      function getTestDefaultConfig(extraOptions: object = {}) {
         return {
           defaults: { testDefault: 'hi' },
           type: 'dashboard',
@@ -766,31 +749,31 @@ describe('Saved Object', function() {
         };
       }
 
-      function expectDefaultApplied(config) {
+      function expectDefaultApplied(config: SavedObjectConfig) {
         return createInitializedSavedObject(config).then(savedObject => {
-          expect(savedObject.defaults).to.be(config.defaults);
+          expect(savedObject.defaults).toBe(config.defaults);
         });
       }
 
-      describe('applied to object when id', function() {
-        it('is not specified', function() {
+      describe('applied to object when id', () => {
+        it('is not specified', () => {
           expectDefaultApplied(getTestDefaultConfig());
         });
 
-        it('is undefined', function() {
+        it('is undefined', () => {
           expectDefaultApplied(getTestDefaultConfig({ id: undefined }));
         });
 
-        it('is 0', function() {
+        it('is 0', () => {
           expectDefaultApplied(getTestDefaultConfig({ id: 0 }));
         });
 
-        it('is false', function() {
+        it('is false', () => {
           expectDefaultApplied(getTestDefaultConfig({ id: false }));
         });
       });
 
-      it('applied to source if an id is given', function() {
+      it('applied to source if an id is given', () => {
         const myId = 'myid';
         const customDefault = 'hi';
         const initialOverwriteMeValue = 'this should get overwritten by the server response';
@@ -798,7 +781,7 @@ describe('Saved Object', function() {
         const config = {
           defaults: {
             overwriteMe: initialOverwriteMeValue,
-            customDefault: customDefault,
+            customDefault,
           },
           type: 'dashboard',
           id: myId,
@@ -813,10 +796,10 @@ describe('Saved Object', function() {
         stubESResponse(mockDocResponse);
 
         return createInitializedSavedObject(config).then(savedObject => {
-          expect(!!savedObject._source).to.be(true);
-          expect(savedObject.defaults).to.be(config.defaults);
-          expect(savedObject._source.overwriteMe).to.be(serverValue);
-          expect(savedObject._source.customDefault).to.be(customDefault);
+          expect(!!savedObject._source).toBe(true);
+          expect(savedObject.defaults).toBe(config.defaults);
+          expect(savedObject._source.overwriteMe).toBe(serverValue);
+          expect(savedObject._source.customDefault).toBe(customDefault);
         });
       });
     });
diff --git a/src/legacy/ui/public/saved_objects/saved_object.ts b/src/plugins/saved_objects/public/saved_object/saved_object.ts
similarity index 98%
rename from src/legacy/ui/public/saved_objects/saved_object.ts
rename to src/plugins/saved_objects/public/saved_object/saved_object.ts
index ca0746410a7dd..45711cb6b3498 100644
--- a/src/legacy/ui/public/saved_objects/saved_object.ts
+++ b/src/plugins/saved_objects/public/saved_object/saved_object.ts
@@ -27,7 +27,7 @@
  * This class seems to interface with ES primarily through the es Angular
  * service and the saved object api.
  */
-import { SavedObject, SavedObjectConfig, SavedObjectKibanaServices } from './types';
+import { SavedObject, SavedObjectConfig, SavedObjectKibanaServices } from '../types';
 import { buildSavedObject } from './helpers/build_saved_object';
 
 export function createSavedObjectClass(services: SavedObjectKibanaServices) {
diff --git a/src/legacy/ui/public/saved_objects/saved_object_loader.ts b/src/plugins/saved_objects/public/saved_object/saved_object_loader.ts
similarity index 98%
rename from src/legacy/ui/public/saved_objects/saved_object_loader.ts
rename to src/plugins/saved_objects/public/saved_object/saved_object_loader.ts
index 7784edc9ad528..36a169d20e51c 100644
--- a/src/legacy/ui/public/saved_objects/saved_object_loader.ts
+++ b/src/plugins/saved_objects/public/saved_object/saved_object_loader.ts
@@ -16,8 +16,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { SavedObject } from 'ui/saved_objects/types';
 import { ChromeStart, SavedObjectsClientContract, SavedObjectsFindOptions } from 'kibana/public';
+import { SavedObject } from '../types';
 import { StringUtils } from './helpers/string_utils';
 
 /**
diff --git a/src/legacy/ui/public/saved_objects/types.ts b/src/plugins/saved_objects/public/types.ts
similarity index 96%
rename from src/legacy/ui/public/saved_objects/types.ts
rename to src/plugins/saved_objects/public/types.ts
index e44c323aebb87..99088df84ec36 100644
--- a/src/legacy/ui/public/saved_objects/types.ts
+++ b/src/plugins/saved_objects/public/types.ts
@@ -24,11 +24,7 @@ import {
   SavedObjectAttributes,
   SavedObjectReference,
 } from 'kibana/public';
-import {
-  IIndexPattern,
-  IndexPatternsContract,
-  ISearchSource,
-} from '../../../../plugins/data/public';
+import { IIndexPattern, IndexPatternsContract, ISearchSource } from '../../data/public';
 
 export interface SavedObject {
   _serialize: () => { attributes: SavedObjectAttributes; references: SavedObjectReference[] };
diff --git a/x-pack/legacy/plugins/graph/public/legacy_imports.ts b/x-pack/legacy/plugins/graph/public/legacy_imports.ts
index 27184f5701235..ac518d34551db 100644
--- a/x-pack/legacy/plugins/graph/public/legacy_imports.ts
+++ b/x-pack/legacy/plugins/graph/public/legacy_imports.ts
@@ -6,10 +6,8 @@
 
 import 'ace';
 
-export { SavedObject, SavedObjectKibanaServices } from 'ui/saved_objects/types';
 // @ts-ignore
 export { createTopNavDirective, createTopNavHelper } from 'ui/kbn_top_nav/kbn_top_nav';
 // @ts-ignore
 export { addAppRedirectMessageToUrl } from 'ui/notify';
-export { createSavedObjectClass } from 'ui/saved_objects/saved_object';
 export { configureAppAngularModule } from '../../../../../src/plugins/kibana_legacy/public';
diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace.ts b/x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace.ts
index d25fcc89d6a1f..025d5e6935902 100644
--- a/x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace.ts
+++ b/x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace.ts
@@ -6,10 +6,10 @@
 import { i18n } from '@kbn/i18n';
 import { extractReferences, injectReferences } from './saved_workspace_references';
 import {
-  createSavedObjectClass,
   SavedObject,
+  createSavedObjectClass,
   SavedObjectKibanaServices,
-} from '../../legacy_imports';
+} from '../../../../../../../src/plugins/saved_objects/public';
 
 export interface SavedWorkspace extends SavedObject {
   wsState?: string;
diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace_loader.ts b/x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace_loader.ts
index 8ddff0be0d06d..184fbc01eb96f 100644
--- a/x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace_loader.ts
+++ b/x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace_loader.ts
@@ -7,8 +7,8 @@
 import { IBasePath } from 'kibana/public';
 import { i18n } from '@kbn/i18n';
 
+import { SavedObjectKibanaServices } from '../../../../../../../src/plugins/saved_objects/public';
 import { createSavedWorkspaceClass } from './saved_workspace';
-import { SavedObjectKibanaServices } from '../../legacy_imports';
 
 export function createSavedWorkspacesLoader(
   services: SavedObjectKibanaServices & { basePath: IBasePath }
diff --git a/x-pack/legacy/plugins/graph/public/types/persistence.ts b/x-pack/legacy/plugins/graph/public/types/persistence.ts
index adb07605b61c4..cdaee5db202d8 100644
--- a/x-pack/legacy/plugins/graph/public/types/persistence.ts
+++ b/x-pack/legacy/plugins/graph/public/types/persistence.ts
@@ -4,9 +4,9 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+import { SavedObject } from '../../../../../../src/plugins/saved_objects/public';
 import { AdvancedSettings, UrlTemplate, WorkspaceField } from './app_state';
 import { WorkspaceNode, WorkspaceEdge } from './workspace_state';
-import { SavedObject } from '../legacy_imports';
 
 type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
 
diff --git a/x-pack/legacy/plugins/maps/public/angular/services/gis_map_saved_object_loader.js b/x-pack/legacy/plugins/maps/public/angular/services/gis_map_saved_object_loader.js
index bd496f44aa8ac..252d602e8f564 100644
--- a/x-pack/legacy/plugins/maps/public/angular/services/gis_map_saved_object_loader.js
+++ b/x-pack/legacy/plugins/maps/public/angular/services/gis_map_saved_object_loader.js
@@ -6,7 +6,7 @@
 
 import { createSavedGisMapClass } from './saved_gis_map';
 import { uiModules } from 'ui/modules';
-import { SavedObjectLoader } from 'ui/saved_objects';
+import { SavedObjectLoader } from '../../../../../../../src/plugins/saved_objects/public';
 import { npStart } from '../../../../../../../src/legacy/ui/public/new_platform';
 
 const module = uiModules.get('app/maps');
diff --git a/x-pack/legacy/plugins/maps/public/angular/services/saved_gis_map.js b/x-pack/legacy/plugins/maps/public/angular/services/saved_gis_map.js
index 035ae4886920c..490ab16a1799c 100644
--- a/x-pack/legacy/plugins/maps/public/angular/services/saved_gis_map.js
+++ b/x-pack/legacy/plugins/maps/public/angular/services/saved_gis_map.js
@@ -5,7 +5,7 @@
  */
 
 import _ from 'lodash';
-import { createSavedObjectClass } from 'ui/saved_objects/saved_object';
+import { createSavedObjectClass } from '../../../../../../../src/plugins/saved_objects/public';
 import {
   getTimeFilters,
   getMapZoom,
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 380e78cf6fa19..4b06645cdfe04 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -250,13 +250,6 @@
     "common.ui.flotCharts.wedLabel": "水",
     "common.ui.paginateControls.pageSizeLabel": "ページサイズ",
     "common.ui.paginateControls.scrollTopButtonLabel": "最上部に移動",
-    "common.ui.savedObjects.confirmModal.overwriteButtonLabel": "上書き",
-    "common.ui.savedObjects.confirmModal.overwriteConfirmationMessage": "{title} を上書きしてよろしいですか?",
-    "common.ui.savedObjects.confirmModal.overwriteTitle": "{name} を上書きしますか?",
-    "common.ui.savedObjects.confirmModal.saveDuplicateButtonLabel": "{name} を保存",
-    "common.ui.savedObjects.confirmModal.saveDuplicateConfirmationMessage": "「{title}」というタイトルの {name} が既に存在します。保存を続けますか?",
-    "common.ui.savedObjects.overwriteRejectedDescription": "上書き確認が拒否されました",
-    "common.ui.savedObjects.saveDuplicateRejectedDescription": "重複ファイルの保存確認が拒否されました",
     "common.ui.scriptingLanguages.errorFetchingToastDescription": "Elasticsearch から利用可能なスクリプト言語の取得中にエラーが発生しました",
     "common.ui.stateManagement.unableToParseUrlErrorMessage": "URL をパースできません",
     "common.ui.stateManagement.unableToRestoreUrlErrorMessage": "URL を完全に復元できません。共有機能を使用していることを確認してください。",
@@ -2439,12 +2432,19 @@
     "regionMap.visParams.vectorMapLabel": "ベクトルマップ",
     "regionMap.visualization.unableToShowMismatchesWarningText": "次の各用語がシェイプの結合フィールドのシェイプと一致することを確認してください: {mismatches}",
     "regionMap.visualization.unableToShowMismatchesWarningTitle": "{mismatchesLength} {oneMismatch, plural, one { 件の結果} other { 件の結果}}をマップに表示できません",
+    "savedObjects.confirmModal.overwriteButtonLabel": "上書き",
+    "savedObjects.confirmModal.overwriteConfirmationMessage": "{title} を上書きしてよろしいですか?",
+    "savedObjects.confirmModal.overwriteTitle": "{name} を上書きしますか?",
+    "savedObjects.confirmModal.saveDuplicateButtonLabel": "{name} を保存",
+    "savedObjects.confirmModal.saveDuplicateConfirmationMessage": "「{title}」というタイトルの {name} が既に存在します。保存を続けますか?",
     "savedObjects.finder.filterButtonLabel": "タイプ",
     "savedObjects.finder.searchPlaceholder": "検索...",
     "savedObjects.finder.sortAsc": "昇順",
     "savedObjects.finder.sortAuto": "ベストマッチ",
     "savedObjects.finder.sortButtonLabel": "並べ替え",
     "savedObjects.finder.sortDesc": "降順",
+    "savedObjects.overwriteRejectedDescription": "上書き確認が拒否されました",
+    "savedObjects.saveDuplicateRejectedDescription": "重複ファイルの保存確認が拒否されました",
     "savedObjects.saveModal.cancelButtonLabel": "キャンセル",
     "savedObjects.saveModal.descriptionLabel": "説明",
     "savedObjects.saveModal.duplicateTitleDescription": "{confirmSaveLabel} をクリックすると、既存の {objectType} が上書きされます。",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 23e822821fea4..ecf4dfbb33be6 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -250,13 +250,6 @@
     "common.ui.flotCharts.wedLabel": "周三",
     "common.ui.paginateControls.pageSizeLabel": "页面大小",
     "common.ui.paginateControls.scrollTopButtonLabel": "滚动至顶部",
-    "common.ui.savedObjects.confirmModal.overwriteButtonLabel": "覆盖",
-    "common.ui.savedObjects.confirmModal.overwriteConfirmationMessage": "确定要覆盖 “{title}”?",
-    "common.ui.savedObjects.confirmModal.overwriteTitle": "覆盖“{name}”?",
-    "common.ui.savedObjects.confirmModal.saveDuplicateButtonLabel": "保存“{name}”",
-    "common.ui.savedObjects.confirmModal.saveDuplicateConfirmationMessage": "具有标题 “{title}” 的 “{name}” 已存在。是否确定要保存?",
-    "common.ui.savedObjects.overwriteRejectedDescription": "已拒绝覆盖确认",
-    "common.ui.savedObjects.saveDuplicateRejectedDescription": "已拒绝使用重复标题保存确认",
     "common.ui.scriptingLanguages.errorFetchingToastDescription": "从 Elasticsearch 获取可用的脚本语言时出错",
     "common.ui.stateManagement.unableToParseUrlErrorMessage": "无法解析 URL",
     "common.ui.stateManagement.unableToRestoreUrlErrorMessage": "无法完整还原 URL,确保使用共享功能。",
@@ -2439,12 +2432,19 @@
     "regionMap.visParams.vectorMapLabel": "矢量地图",
     "regionMap.visualization.unableToShowMismatchesWarningText": "确保每个字词与该形状的联接字段匹配:{mismatches}",
     "regionMap.visualization.unableToShowMismatchesWarningTitle": "无法在地图上显示 {mismatchesLength} {oneMismatch, plural, one { 个结果} other { 个结果}}",
+    "savedObjects.confirmModal.overwriteButtonLabel": "覆盖",
+    "savedObjects.confirmModal.overwriteConfirmationMessage": "确定要覆盖 “{title}”?",
+    "savedObjects.confirmModal.overwriteTitle": "覆盖“{name}”?",
+    "savedObjects.confirmModal.saveDuplicateButtonLabel": "保存“{name}”",
+    "savedObjects.confirmModal.saveDuplicateConfirmationMessage": "具有标题 “{title}” 的 “{name}” 已存在。是否确定要保存?",
     "savedObjects.finder.filterButtonLabel": "类型",
     "savedObjects.finder.searchPlaceholder": "搜索……",
     "savedObjects.finder.sortAsc": "升序",
     "savedObjects.finder.sortAuto": "最佳匹配",
     "savedObjects.finder.sortButtonLabel": "排序",
     "savedObjects.finder.sortDesc": "降序",
+    "savedObjects.overwriteRejectedDescription": "已拒绝覆盖确认",
+    "savedObjects.saveDuplicateRejectedDescription": "已拒绝使用重复标题保存确认",
     "savedObjects.saveModal.cancelButtonLabel": "取消",
     "savedObjects.saveModal.descriptionLabel": "描述",
     "savedObjects.saveModal.duplicateTitleDescription": "单击“{confirmSaveLabel}”可覆盖现有 {objectType}。",

From d96cf70cd4843c4a0f7dea78b8611a0a680c29f8 Mon Sep 17 00:00:00 2001
From: Zacqary Adam Xeper <Zacqary@users.noreply.github.com>
Date: Thu, 20 Feb 2020 13:45:58 -0600
Subject: [PATCH 106/174] Add throttle param to Alerting readme (#57609)

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 x-pack/plugins/alerting/README.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/x-pack/plugins/alerting/README.md b/x-pack/plugins/alerting/README.md
index 32ca804198ebd..6b04b3ba500b0 100644
--- a/x-pack/plugins/alerting/README.md
+++ b/x-pack/plugins/alerting/README.md
@@ -249,6 +249,7 @@ Payload:
 |tags|A list of keywords to reference and search in the future.|string[]|
 |alertTypeId|The id value of the alert type you want to call when the alert is scheduled to execute.|string|
 |schedule|The schedule specifying when this alert should be run, using one of the available schedule formats specified under _Schedule Formats_ below|object|
+|throttle|A Duration specifying how often this alert should fire the same actions. This will prevent the alert from sending out the same notification over and over. For example, if an alert with a `schedule` of 1 minute stays in a triggered state for 90 minutes, setting a `throttle` of `10m` or `1h` will prevent it from sending 90 notifications over this period.|string|
 |params|The parameters to pass in to the alert type executor `params` value. This will also validate against the alert type params validator if defined.|object|
 |actions|Array of the following:<br> - `group` (string): We support grouping actions in the scenario of escalations or different types of alert instances. If you don't need this, feel free to use `default` as a value.<br>- `id` (string): The id of the action saved object to execute.<br>- `params` (object): The map to the `params` the action type will receive. In order to help apply context to strings, we handle them as mustache templates and pass in a default set of context. (see templating actions).|array|
 
@@ -299,6 +300,7 @@ Payload:
 |Property|Description|Type|
 |---|---|---|
 |schedule|The schedule specifying when this alert should be run, using one of the available schedule formats specified under _Schedule Formats_ below|object|
+|throttle|A Duration specifying how often this alert should fire the same actions. This will prevent the alert from sending out the same notification over and over. For example, if an alert with a `schedule` of 1 minute stays in a triggered state for 90 minutes, setting a `throttle` of `10m` or `1h` will prevent it from sending 90 notifications over this period.|string|
 |name|A name to reference and search in the future.|string|
 |tags|A list of keywords to reference and search in the future.|string[]|
 |params|The parameters to pass in to the alert type executor `params` value. This will also validate against the alert type params validator if defined.|object|

From 3bd639dd685fe98d6b744e48440c74b4226917a3 Mon Sep 17 00:00:00 2001
From: Frank Hassanabad <frank.hassanabad@elastic.co>
Date: Thu, 20 Feb 2020 12:56:03 -0700
Subject: [PATCH 107/174] [SIEM] Let us try out code owners for a little while
 and see what happens

## Summary

Code owners for SIEM team.
---
 .github/CODEOWNERS | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index bea10a1c8b31c..dbac07c5e093c 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -185,3 +185,10 @@
 /x-pack/test/api_integration/apis/endpoint/ @elastic/endpoint-app-team
 /x-pack/test/functional/apps/endpoint/ @elastic/endpoint-app-team
 /x-pack/test/functional/es_archives/endpoint/ @elastic/endpoint-app-team
+
+# SIEM
+/x-pack/legacy/plugins/siem/ @elastic/siem
+/x-pack/plugins/siem/ @elastic/siem
+/x-pack/test/detection_engine_api_integration @elastic/siem
+/x-pack/test/api_integration/apis/siem @elastic/siem
+/x-pack/plugins/case @elastic/siem

From 4aedf2b75bc397be7c4c2cdf4eb26f2783fccb4c Mon Sep 17 00:00:00 2001
From: Spencer <email@spalger.com>
Date: Thu, 20 Feb 2020 12:58:42 -0700
Subject: [PATCH 108/174] add monaco to kbn/ui-shared-deps and load required
 features for all uses (#58075)

* add monaco to kbn/ui-shared-deps and load required features for all uses

* forgot to save a change

* remove unused imports

* include a cache buster to counteract issue #58077

* include monaco.ts in ts project
---
 packages/kbn-optimizer/src/index.ts           |  1 +
 packages/kbn-ui-shared-deps/entry.js          |  2 +
 packages/kbn-ui-shared-deps/index.js          |  3 ++
 packages/kbn-ui-shared-deps/monaco.ts         | 32 ++++++++++++
 packages/kbn-ui-shared-deps/package.json      |  1 +
 packages/kbn-ui-shared-deps/tsconfig.json     |  3 +-
 packages/kbn-ui-shared-deps/webpack.config.js | 11 ++++
 .../components/timelion_expression_input.tsx  | 10 ++--
 .../timelion_expression_input_helpers.ts      | 21 ++++----
 .../public/code_editor/code_editor.test.tsx   | 43 +++++++--------
 .../public/code_editor/code_editor.tsx        | 52 +++++++++----------
 .../public/code_editor/editor_theme.ts        |  4 +-
 .../expression_input.examples.tsx             |  2 +-
 .../expression_input/expression_input.tsx     | 31 +++++------
 .../canvas/public/lib/monaco_language_def.ts  |  2 +-
 15 files changed, 128 insertions(+), 90 deletions(-)
 create mode 100644 packages/kbn-ui-shared-deps/monaco.ts

diff --git a/packages/kbn-optimizer/src/index.ts b/packages/kbn-optimizer/src/index.ts
index 48777f1d54aaf..9798391d47da4 100644
--- a/packages/kbn-optimizer/src/index.ts
+++ b/packages/kbn-optimizer/src/index.ts
@@ -17,6 +17,7 @@
  * under the License.
  */
 
+// cache buster - https://github.com/elastic/kibana/issues/58077 - 1
 export { OptimizerConfig } from './optimizer';
 export * from './run_optimizer';
 export * from './log_optimizer_state';
diff --git a/packages/kbn-ui-shared-deps/entry.js b/packages/kbn-ui-shared-deps/entry.js
index 9a5fb479276f7..5028c6efdb40e 100644
--- a/packages/kbn-ui-shared-deps/entry.js
+++ b/packages/kbn-ui-shared-deps/entry.js
@@ -40,6 +40,8 @@ export const ReactDom = require('react-dom');
 export const ReactIntl = require('react-intl');
 export const ReactRouter = require('react-router'); // eslint-disable-line
 export const ReactRouterDom = require('react-router-dom');
+export const Monaco = require('./monaco.ts');
+export const MonacoBare = require('monaco-editor/esm/vs/editor/editor.api');
 
 // load timezone data into moment-timezone
 Moment.tz.load(require('moment-timezone/data/packed/latest.json'));
diff --git a/packages/kbn-ui-shared-deps/index.js b/packages/kbn-ui-shared-deps/index.js
index 5f5ac3f1c9c2f..c7c004bd55794 100644
--- a/packages/kbn-ui-shared-deps/index.js
+++ b/packages/kbn-ui-shared-deps/index.js
@@ -41,4 +41,7 @@ exports.externals = {
   'react-intl': '__kbnSharedDeps__.ReactIntl',
   'react-router': '__kbnSharedDeps__.ReactRouter',
   'react-router-dom': '__kbnSharedDeps__.ReactRouterDom',
+  '@kbn/ui-shared-deps/monaco': '__kbnSharedDeps__.Monaco',
+  // this is how plugins/consumers from npm load monaco
+  'monaco-editor/esm/vs/editor/editor.api': '__kbnSharedDeps__.MonacoBare',
 };
diff --git a/packages/kbn-ui-shared-deps/monaco.ts b/packages/kbn-ui-shared-deps/monaco.ts
new file mode 100644
index 0000000000000..570aca86c484c
--- /dev/null
+++ b/packages/kbn-ui-shared-deps/monaco.ts
@@ -0,0 +1,32 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
+
+import 'monaco-editor/esm/vs/base/common/worker/simpleWorker';
+import 'monaco-editor/esm/vs/base/worker/defaultWorkerFactory';
+
+import 'monaco-editor/esm/vs/editor/browser/controller/coreCommands.js';
+import 'monaco-editor/esm/vs/editor/browser/widget/codeEditorWidget.js';
+
+import 'monaco-editor/esm/vs/editor/contrib/suggest/suggestController.js'; // Needed for suggestions
+import 'monaco-editor/esm/vs/editor/contrib/hover/hover.js'; // Needed for hover
+import 'monaco-editor/esm/vs/editor/contrib/parameterHints/parameterHints.js'; // Needed for signature
+
+export { monaco };
diff --git a/packages/kbn-ui-shared-deps/package.json b/packages/kbn-ui-shared-deps/package.json
index 6795d363a9f1d..acb2b48e12278 100644
--- a/packages/kbn-ui-shared-deps/package.json
+++ b/packages/kbn-ui-shared-deps/package.json
@@ -12,6 +12,7 @@
     "@elastic/charts": "^17.0.2",
     "abortcontroller-polyfill": "^1.4.0",
     "@elastic/eui": "19.0.0",
+    "@kbn/babel-preset": "1.0.0",
     "@kbn/dev-utils": "1.0.0",
     "@kbn/i18n": "1.0.0",
     "@yarnpkg/lockfile": "^1.1.0",
diff --git a/packages/kbn-ui-shared-deps/tsconfig.json b/packages/kbn-ui-shared-deps/tsconfig.json
index c5c3cba147fcf..5d981c73f1d21 100644
--- a/packages/kbn-ui-shared-deps/tsconfig.json
+++ b/packages/kbn-ui-shared-deps/tsconfig.json
@@ -1,6 +1,7 @@
 {
   "extends": "../../tsconfig.json",
   "include": [
-    "index.d.ts"
+    "index.d.ts",
+    "monaco.ts"
   ]
 }
diff --git a/packages/kbn-ui-shared-deps/webpack.config.js b/packages/kbn-ui-shared-deps/webpack.config.js
index 87cca2cc897f8..dc6e7ae33dbec 100644
--- a/packages/kbn-ui-shared-deps/webpack.config.js
+++ b/packages/kbn-ui-shared-deps/webpack.config.js
@@ -59,6 +59,17 @@ exports.getWebpackConfig = ({ dev = false } = {}) => ({
         test: /\.css$/,
         use: [MiniCssExtractPlugin.loader, 'css-loader'],
       },
+      {
+        include: [require.resolve('./monaco.ts')],
+        use: [
+          {
+            loader: 'babel-loader',
+            options: {
+              presets: [require.resolve('@kbn/babel-preset/webpack_preset')],
+            },
+          },
+        ],
+      },
     ],
   },
 
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input.tsx b/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input.tsx
index e0aa7f6dad2fa..c317451b8201e 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input.tsx
+++ b/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input.tsx
@@ -20,7 +20,7 @@
 import React, { useEffect, useCallback, useRef, useMemo } from 'react';
 import { EuiFormLabel } from '@elastic/eui';
 import { FormattedMessage } from '@kbn/i18n/react';
-import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api';
+import { monaco } from '@kbn/ui-shared-deps/monaco';
 
 import { CodeEditor, useKibana } from '../../../../../plugins/kibana_react/public';
 import { suggest, getSuggestion } from './timelion_expression_input_helpers';
@@ -31,7 +31,7 @@ import {
 } from '../../../../../plugins/timelion/common/types';
 
 const LANGUAGE_ID = 'timelion_expression';
-monacoEditor.languages.register({ id: LANGUAGE_ID });
+monaco.languages.register({ id: LANGUAGE_ID });
 
 interface TimelionExpressionInputProps {
   value: string;
@@ -44,10 +44,10 @@ function TimelionExpressionInput({ value, setValue }: TimelionExpressionInputPro
   const argValueSuggestions = useMemo(getArgValueSuggestions, []);
 
   const provideCompletionItems = useCallback(
-    async (model: monacoEditor.editor.ITextModel, position: monacoEditor.Position) => {
+    async (model: monaco.editor.ITextModel, position: monaco.Position) => {
       const text = model.getValue();
       const wordUntil = model.getWordUntilPosition(position);
-      const wordRange = new monacoEditor.Range(
+      const wordRange = new monaco.Range(
         position.lineNumber,
         wordUntil.startColumn,
         position.lineNumber,
@@ -75,7 +75,7 @@ function TimelionExpressionInput({ value, setValue }: TimelionExpressionInputPro
   );
 
   const provideHover = useCallback(
-    async (model: monacoEditor.editor.ITextModel, position: monacoEditor.Position) => {
+    async (model: monaco.editor.ITextModel, position: monaco.Position) => {
       const suggestions = await suggest(
         model.getValue(),
         functionList.current,
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.ts b/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.ts
index 93b6a0d463c01..6f23c864419eb 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.ts
+++ b/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.ts
@@ -19,7 +19,7 @@
 
 import { get, startsWith } from 'lodash';
 import { i18n } from '@kbn/i18n';
-import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api';
+import { monaco } from '@kbn/ui-shared-deps/monaco';
 
 import { Parser } from 'pegjs';
 
@@ -215,14 +215,13 @@ export async function suggest(
 export function getSuggestion(
   suggestion: ITimelionFunction | TimelionFunctionArgs,
   type: SUGGESTION_TYPE,
-  range: monacoEditor.Range
-): monacoEditor.languages.CompletionItem {
-  let kind: monacoEditor.languages.CompletionItemKind =
-    monacoEditor.languages.CompletionItemKind.Method;
+  range: monaco.Range
+): monaco.languages.CompletionItem {
+  let kind: monaco.languages.CompletionItemKind = monaco.languages.CompletionItemKind.Method;
   let insertText: string = suggestion.name;
-  let insertTextRules: monacoEditor.languages.CompletionItem['insertTextRules'];
+  let insertTextRules: monaco.languages.CompletionItem['insertTextRules'];
   let detail: string = '';
-  let command: monacoEditor.languages.CompletionItem['command'];
+  let command: monaco.languages.CompletionItem['command'];
 
   switch (type) {
     case SUGGESTION_TYPE.ARGUMENTS:
@@ -230,7 +229,7 @@ export function getSuggestion(
         title: 'Trigger Suggestion Dialog',
         id: 'editor.action.triggerSuggest',
       };
-      kind = monacoEditor.languages.CompletionItemKind.Property;
+      kind = monaco.languages.CompletionItemKind.Property;
       insertText = `${insertText}=`;
       detail = `${i18n.translate(
         'timelion.expressionSuggestions.argument.description.acceptsText',
@@ -245,9 +244,9 @@ export function getSuggestion(
         title: 'Trigger Suggestion Dialog',
         id: 'editor.action.triggerSuggest',
       };
-      kind = monacoEditor.languages.CompletionItemKind.Function;
+      kind = monaco.languages.CompletionItemKind.Function;
       insertText = `${insertText}($0)`;
-      insertTextRules = monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet;
+      insertTextRules = monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet;
       detail = `(${
         (suggestion as ITimelionFunction).chainable
           ? i18n.translate('timelion.expressionSuggestions.func.description.chainableHelpText', {
@@ -270,7 +269,7 @@ export function getSuggestion(
         title: 'Trigger Suggestion Dialog',
         id: 'editor.action.triggerSuggest',
       };
-      kind = monacoEditor.languages.CompletionItemKind.Property;
+      kind = monaco.languages.CompletionItemKind.Property;
       detail = suggestion.help || '';
 
       break;
diff --git a/src/plugins/kibana_react/public/code_editor/code_editor.test.tsx b/src/plugins/kibana_react/public/code_editor/code_editor.test.tsx
index 2c305c4ea97da..bcb46fac36856 100644
--- a/src/plugins/kibana_react/public/code_editor/code_editor.test.tsx
+++ b/src/plugins/kibana_react/public/code_editor/code_editor.test.tsx
@@ -19,13 +19,13 @@
 
 import React from 'react';
 import { CodeEditor } from './code_editor';
-import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api';
+import { monaco } from '@kbn/ui-shared-deps/monaco';
 import { shallow } from 'enzyme';
 
 import 'monaco-editor/esm/vs/basic-languages/html/html.contribution.js';
 
 // A sample language definition with a few example tokens
-const simpleLogLang: monacoEditor.languages.IMonarchLanguage = {
+const simpleLogLang: monaco.languages.IMonarchLanguage = {
   tokenizer: {
     root: [
       [/\[error.*/, 'constant'],
@@ -36,8 +36,8 @@ const simpleLogLang: monacoEditor.languages.IMonarchLanguage = {
   },
 };
 
-monacoEditor.languages.register({ id: 'loglang' });
-monacoEditor.languages.setMonarchTokensProvider('loglang', simpleLogLang);
+monaco.languages.register({ id: 'loglang' });
+monaco.languages.setMonarchTokensProvider('loglang', simpleLogLang);
 
 const logs = `
 [Sun Mar 7 20:54:27 2004] [notice] [client xx.xx.xx.xx] This is a notice!
@@ -55,23 +55,22 @@ test('is rendered', () => {
 
 test('editor mount setup', () => {
   const suggestionProvider = {
-    provideCompletionItems: (
-      model: monacoEditor.editor.ITextModel,
-      position: monacoEditor.Position
-    ) => ({ suggestions: [] }),
+    provideCompletionItems: (model: monaco.editor.ITextModel, position: monaco.Position) => ({
+      suggestions: [],
+    }),
   };
   const signatureProvider = {
     provideSignatureHelp: () => ({ signatures: [], activeParameter: 0, activeSignature: 0 }),
   };
   const hoverProvider = {
-    provideHover: (model: monacoEditor.editor.ITextModel, position: monacoEditor.Position) => ({
+    provideHover: (model: monaco.editor.ITextModel, position: monaco.Position) => ({
       contents: [],
     }),
   };
 
   const editorWillMount = jest.fn();
 
-  monacoEditor.languages.onLanguage = jest.fn((languageId, func) => {
+  monaco.languages.onLanguage = jest.fn((languageId, func) => {
     expect(languageId).toBe('loglang');
 
     // Call the function immediately so we can see our providers
@@ -79,11 +78,11 @@ test('editor mount setup', () => {
     func();
   }) as any;
 
-  monacoEditor.languages.registerCompletionItemProvider = jest.fn();
-  monacoEditor.languages.registerSignatureHelpProvider = jest.fn();
-  monacoEditor.languages.registerHoverProvider = jest.fn();
+  monaco.languages.registerCompletionItemProvider = jest.fn();
+  monaco.languages.registerSignatureHelpProvider = jest.fn();
+  monaco.languages.registerHoverProvider = jest.fn();
 
-  monacoEditor.editor.defineTheme = jest.fn();
+  monaco.editor.defineTheme = jest.fn();
 
   const wrapper = shallow(
     <CodeEditor
@@ -98,21 +97,17 @@ test('editor mount setup', () => {
   );
 
   const instance = wrapper.instance() as CodeEditor;
-  instance._editorWillMount(monacoEditor);
+  instance._editorWillMount(monaco);
 
   // Verify our mount callback will be called
   expect(editorWillMount.mock.calls.length).toBe(1);
 
   // Verify our theme will be setup
-  expect((monacoEditor.editor.defineTheme as jest.Mock).mock.calls.length).toBe(1);
+  expect((monaco.editor.defineTheme as jest.Mock).mock.calls.length).toBe(1);
 
   // Verify our language features have been registered
-  expect((monacoEditor.languages.onLanguage as jest.Mock).mock.calls.length).toBe(1);
-  expect(
-    (monacoEditor.languages.registerCompletionItemProvider as jest.Mock).mock.calls.length
-  ).toBe(1);
-  expect(
-    (monacoEditor.languages.registerSignatureHelpProvider as jest.Mock).mock.calls.length
-  ).toBe(1);
-  expect((monacoEditor.languages.registerHoverProvider as jest.Mock).mock.calls.length).toBe(1);
+  expect((monaco.languages.onLanguage as jest.Mock).mock.calls.length).toBe(1);
+  expect((monaco.languages.registerCompletionItemProvider as jest.Mock).mock.calls.length).toBe(1);
+  expect((monaco.languages.registerSignatureHelpProvider as jest.Mock).mock.calls.length).toBe(1);
+  expect((monaco.languages.registerHoverProvider as jest.Mock).mock.calls.length).toBe(1);
 });
diff --git a/src/plugins/kibana_react/public/code_editor/code_editor.tsx b/src/plugins/kibana_react/public/code_editor/code_editor.tsx
index 62440f12c6d84..37707fdec2140 100644
--- a/src/plugins/kibana_react/public/code_editor/code_editor.tsx
+++ b/src/plugins/kibana_react/public/code_editor/code_editor.tsx
@@ -19,18 +19,9 @@
 
 import React from 'react';
 import ReactResizeDetector from 'react-resize-detector';
-import MonacoEditor, { EditorDidMount, EditorWillMount } from 'react-monaco-editor';
+import MonacoEditor from 'react-monaco-editor';
 
-import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api';
-import 'monaco-editor/esm/vs/base/common/worker/simpleWorker';
-import 'monaco-editor/esm/vs/base/worker/defaultWorkerFactory';
-
-import 'monaco-editor/esm/vs/editor/browser/controller/coreCommands.js';
-import 'monaco-editor/esm/vs/editor/browser/widget/codeEditorWidget.js';
-
-import 'monaco-editor/esm/vs/editor/contrib/suggest/suggestController.js'; // Needed for suggestions
-import 'monaco-editor/esm/vs/editor/contrib/hover/hover.js'; // Needed for hover
-import 'monaco-editor/esm/vs/editor/contrib/parameterHints/parameterHints.js'; // Needed for signature
+import { monaco } from '@kbn/ui-shared-deps/monaco';
 
 import { LIGHT_THEME, DARK_THEME } from './editor_theme';
 
@@ -55,50 +46,50 @@ export interface Props {
    * Documentation of options can be found here:
    * https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.ieditorconstructionoptions.html
    */
-  options?: monacoEditor.editor.IEditorConstructionOptions;
+  options?: monaco.editor.IEditorConstructionOptions;
 
   /**
    * Suggestion provider for autocompletion
    * Documentation for the provider can be found here:
    * https://microsoft.github.io/monaco-editor/api/interfaces/monaco.languages.completionitemprovider.html
    */
-  suggestionProvider?: monacoEditor.languages.CompletionItemProvider;
+  suggestionProvider?: monaco.languages.CompletionItemProvider;
 
   /**
    * Signature provider for function parameter info
    * Documentation for the provider can be found here:
    * https://microsoft.github.io/monaco-editor/api/interfaces/monaco.languages.signaturehelpprovider.html
    */
-  signatureProvider?: monacoEditor.languages.SignatureHelpProvider;
+  signatureProvider?: monaco.languages.SignatureHelpProvider;
 
   /**
    * Hover provider for hover documentation
    * Documentation for the provider can be found here:
    * https://microsoft.github.io/monaco-editor/api/interfaces/monaco.languages.hoverprovider.html
    */
-  hoverProvider?: monacoEditor.languages.HoverProvider;
+  hoverProvider?: monaco.languages.HoverProvider;
 
   /**
    * Language config provider for bracket
    * Documentation for the provider can be found here:
    * https://microsoft.github.io/monaco-editor/api/interfaces/monaco.languages.languageconfiguration.html
    */
-  languageConfiguration?: monacoEditor.languages.LanguageConfiguration;
+  languageConfiguration?: monaco.languages.LanguageConfiguration;
 
   /**
    * Function called before the editor is mounted in the view
    */
-  editorWillMount?: EditorWillMount;
+  editorWillMount?: () => void;
   /**
    * Function called before the editor is mounted in the view
    * and completely replaces the setup behavior called by the component
    */
-  overrideEditorWillMount?: EditorWillMount;
+  overrideEditorWillMount?: () => void;
 
   /**
    * Function called after the editor is mounted in the view
    */
-  editorDidMount?: EditorDidMount;
+  editorDidMount?: (editor: monaco.editor.IStandaloneCodeEditor) => void;
 
   /**
    * Should the editor use the dark theme
@@ -107,16 +98,20 @@ export interface Props {
 }
 
 export class CodeEditor extends React.Component<Props, {}> {
-  _editor: monacoEditor.editor.IStandaloneCodeEditor | null = null;
+  _editor: monaco.editor.IStandaloneCodeEditor | null = null;
+
+  _editorWillMount = (__monaco: unknown) => {
+    if (__monaco !== monaco) {
+      throw new Error('react-monaco-editor is using a different version of monaco');
+    }
 
-  _editorWillMount = (monaco: typeof monacoEditor) => {
     if (this.props.overrideEditorWillMount) {
-      this.props.overrideEditorWillMount(monaco);
+      this.props.overrideEditorWillMount();
       return;
     }
 
     if (this.props.editorWillMount) {
-      this.props.editorWillMount(monaco);
+      this.props.editorWillMount();
     }
 
     monaco.languages.onLanguage(this.props.languageId, () => {
@@ -150,14 +145,15 @@ export class CodeEditor extends React.Component<Props, {}> {
     monaco.editor.defineTheme('euiColors', this.props.useDarkTheme ? DARK_THEME : LIGHT_THEME);
   };
 
-  _editorDidMount = (
-    editor: monacoEditor.editor.IStandaloneCodeEditor,
-    monaco: typeof monacoEditor
-  ) => {
+  _editorDidMount = (editor: monaco.editor.IStandaloneCodeEditor, __monaco: unknown) => {
+    if (__monaco !== monaco) {
+      throw new Error('react-monaco-editor is using a different version of monaco');
+    }
+
     this._editor = editor;
 
     if (this.props.editorDidMount) {
-      this.props.editorDidMount(editor, monaco);
+      this.props.editorDidMount(editor);
     }
   };
 
diff --git a/src/plugins/kibana_react/public/code_editor/editor_theme.ts b/src/plugins/kibana_react/public/code_editor/editor_theme.ts
index 41702f1b3fc35..6e30135686797 100644
--- a/src/plugins/kibana_react/public/code_editor/editor_theme.ts
+++ b/src/plugins/kibana_react/public/code_editor/editor_theme.ts
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api';
+import { monaco } from '@kbn/ui-shared-deps/monaco';
 
 import darkTheme from '@elastic/eui/dist/eui_theme_dark.json';
 import lightTheme from '@elastic/eui/dist/eui_theme_light.json';
@@ -27,7 +27,7 @@ import lightTheme from '@elastic/eui/dist/eui_theme_light.json';
 export function createTheme(
   euiTheme: typeof darkTheme | typeof lightTheme,
   selectionBackgroundColor: string
-): monacoEditor.editor.IStandaloneThemeData {
+): monaco.editor.IStandaloneThemeData {
   return {
     base: 'vs',
     inherit: true,
diff --git a/x-pack/legacy/plugins/canvas/public/components/expression_input/__examples__/expression_input.examples.tsx b/x-pack/legacy/plugins/canvas/public/components/expression_input/__examples__/expression_input.examples.tsx
index 880269385d12f..51caf1db196bc 100644
--- a/x-pack/legacy/plugins/canvas/public/components/expression_input/__examples__/expression_input.examples.tsx
+++ b/x-pack/legacy/plugins/canvas/public/components/expression_input/__examples__/expression_input.examples.tsx
@@ -7,7 +7,7 @@
 import { action } from '@storybook/addon-actions';
 import { storiesOf } from '@storybook/react';
 import React from 'react';
-import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
+import { monaco } from '@kbn/ui-shared-deps/monaco';
 
 import { ExpressionInput } from '../expression_input';
 import { language, LANGUAGE_ID } from '../../../lib/monaco_language_def';
diff --git a/x-pack/legacy/plugins/canvas/public/components/expression_input/expression_input.tsx b/x-pack/legacy/plugins/canvas/public/components/expression_input/expression_input.tsx
index 9653decb6db97..faac2ae883c13 100644
--- a/x-pack/legacy/plugins/canvas/public/components/expression_input/expression_input.tsx
+++ b/x-pack/legacy/plugins/canvas/public/components/expression_input/expression_input.tsx
@@ -8,7 +8,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { EuiFormRow } from '@elastic/eui';
 import { debounce } from 'lodash';
-import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api';
+import { monaco } from '@kbn/ui-shared-deps/monaco';
 import { ExpressionFunction } from '../../../../../../../src/plugins/expressions';
 import { CodeEditor } from '../../../../../../../src/plugins/kibana_react/public';
 import {
@@ -46,7 +46,7 @@ export class ExpressionInput extends React.Component<Props> {
     onChange: PropTypes.func.isRequired,
   };
 
-  editor: monacoEditor.editor.IStandaloneCodeEditor | null;
+  editor: monaco.editor.IStandaloneCodeEditor | null;
 
   undoHistory: string[];
   redoHistory: string[];
@@ -114,9 +114,9 @@ export class ExpressionInput extends React.Component<Props> {
   };
 
   provideSuggestions = (
-    model: monacoEditor.editor.ITextModel,
-    position: monacoEditor.Position,
-    context: monacoEditor.languages.CompletionContext
+    model: monaco.editor.ITextModel,
+    position: monaco.Position,
+    context: monaco.languages.CompletionContext
   ) => {
     const text = model.getValue();
     const textRange = model.getFullModelRange();
@@ -128,13 +128,13 @@ export class ExpressionInput extends React.Component<Props> {
       endColumn: textRange.endColumn,
     });
 
-    let wordRange: monacoEditor.Range;
+    let wordRange: monaco.Range;
     let aSuggestions;
 
     if (context.triggerCharacter === '{') {
       const wordUntil = model.getWordAtPosition(position.delta(0, -3));
       if (wordUntil) {
-        wordRange = new monacoEditor.Range(
+        wordRange = new monaco.Range(
           position.lineNumber,
           position.column,
           position.lineNumber,
@@ -151,7 +151,7 @@ export class ExpressionInput extends React.Component<Props> {
       }
     } else {
       const wordUntil = model.getWordUntilPosition(position);
-      wordRange = new monacoEditor.Range(
+      wordRange = new monaco.Range(
         position.lineNumber,
         wordUntil.startColumn,
         position.lineNumber,
@@ -173,7 +173,7 @@ export class ExpressionInput extends React.Component<Props> {
       if (s.type === 'argument') {
         return {
           label: s.argDef.name,
-          kind: monacoEditor.languages.CompletionItemKind.Variable,
+          kind: monaco.languages.CompletionItemKind.Variable,
           documentation: { value: getArgReferenceStr(s.argDef), isTrusted: true },
           insertText: s.text,
           command: {
@@ -186,7 +186,7 @@ export class ExpressionInput extends React.Component<Props> {
       } else if (s.type === 'value') {
         return {
           label: s.text,
-          kind: monacoEditor.languages.CompletionItemKind.Value,
+          kind: monaco.languages.CompletionItemKind.Value,
           insertText: s.text,
           command: {
             title: 'Trigger Suggestion Dialog',
@@ -198,7 +198,7 @@ export class ExpressionInput extends React.Component<Props> {
       } else {
         return {
           label: s.fnDef.name,
-          kind: monacoEditor.languages.CompletionItemKind.Function,
+          kind: monaco.languages.CompletionItemKind.Function,
           documentation: {
             value: getFunctionReferenceStr(s.fnDef),
             isTrusted: true,
@@ -219,7 +219,7 @@ export class ExpressionInput extends React.Component<Props> {
     };
   };
 
-  providerHover = (model: monacoEditor.editor.ITextModel, position: monacoEditor.Position) => {
+  providerHover = (model: monaco.editor.ITextModel, position: monaco.Position) => {
     const text = model.getValue();
     const word = model.getWordAtPosition(position);
 
@@ -248,7 +248,7 @@ export class ExpressionInput extends React.Component<Props> {
       const startPos = model.getPositionAt(argStart);
       const endPos = model.getPositionAt(argEnd);
 
-      const argRange = new monacoEditor.Range(
+      const argRange = new monaco.Range(
         startPos.lineNumber,
         startPos.column,
         endPos.lineNumber,
@@ -275,10 +275,7 @@ export class ExpressionInput extends React.Component<Props> {
     };
   };
 
-  editorDidMount = (
-    editor: monacoEditor.editor.IStandaloneCodeEditor,
-    monaco: typeof monacoEditor
-  ) => {
+  editorDidMount = (editor: monaco.editor.IStandaloneCodeEditor) => {
     // Updating tab size for the editor
     const model = editor.getModel();
     if (model) {
diff --git a/x-pack/legacy/plugins/canvas/public/lib/monaco_language_def.ts b/x-pack/legacy/plugins/canvas/public/lib/monaco_language_def.ts
index e15be9a90beb0..cd054bff3b7d2 100644
--- a/x-pack/legacy/plugins/canvas/public/lib/monaco_language_def.ts
+++ b/x-pack/legacy/plugins/canvas/public/lib/monaco_language_def.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
+import { monaco } from '@kbn/ui-shared-deps/monaco';
 import { npSetup } from 'ui/new_platform';
 
 export const LANGUAGE_ID = 'canvas-expression';

From 262397623725531b6d0c62f01cd241872289ce38 Mon Sep 17 00:00:00 2001
From: Poff Poffenberger <poffdeluxe@gmail.com>
Date: Thu, 20 Feb 2020 14:01:31 -0600
Subject: [PATCH 109/174] Add flag for building static storybook site (#58050)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Adding storybook static site generation and percy storybook script

* feat: 🎸 build each Storybook in own folder, add --site flag doc

* Add flags site tag for exiting process

Co-Authored-By: Vadim Dalecky <streamich@users.noreply.github.com>

* Back out of percy-storybook integration

Co-authored-by: Vadim Dalecky <streamich@users.noreply.github.com>
---
 packages/kbn-storybook/index.js                    | 14 +++++++++-----
 .../storybook_config/preview-head.html             |  4 ++--
 src/dev/storybook/run_storybook_cli.ts             |  4 +++-
 3 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/packages/kbn-storybook/index.js b/packages/kbn-storybook/index.js
index 78e2cf7f5073b..353a273881343 100644
--- a/packages/kbn-storybook/index.js
+++ b/packages/kbn-storybook/index.js
@@ -24,7 +24,7 @@ const { first } = require('rxjs/operators');
 const storybook = require('@storybook/react/standalone');
 const { run } = require('@kbn/dev-utils');
 const { generateStorybookEntry } = require('./lib/storybook_entry');
-const { REPO_ROOT, CURRENT_CONFIG } = require('./lib/constants');
+const { REPO_ROOT, ASSET_DIR, CURRENT_CONFIG } = require('./lib/constants');
 const { buildDll } = require('./lib/dll');
 
 exports.runStorybookCli = config => {
@@ -62,21 +62,25 @@ exports.runStorybookCli = config => {
         // route errors
         subj.toPromise(),
 
-        new Promise(() => {
+        new Promise(async () => {
           // storybook never completes, so neither will this promise
           const configDir = join(__dirname, 'storybook_config');
           log.debug('Config dir:', configDir);
-          storybook({
-            mode: 'dev',
+          await storybook({
+            mode: flags.site ? 'static' : 'dev',
             port: 9001,
             configDir,
+            outputDir: flags.site ? join(ASSET_DIR, name) : undefined,
           });
+
+          // Line is only reached when building the static version
+          if (flags.site) process.exit();
         }),
       ]);
     },
     {
       flags: {
-        boolean: ['rebuildDll'],
+        boolean: ['rebuildDll', 'site'],
       },
       description: `
         Run the storybook examples for ${name}
diff --git a/packages/kbn-storybook/storybook_config/preview-head.html b/packages/kbn-storybook/storybook_config/preview-head.html
index bef08a5120a36..16754ad550da0 100644
--- a/packages/kbn-storybook/storybook_config/preview-head.html
+++ b/packages/kbn-storybook/storybook_config/preview-head.html
@@ -2,5 +2,5 @@
   This file is looked for by Storybook and included in the HEAD element
   if it exists.  This is how we load the DLL content into the Storybook UI.
 -->
-<script src="/dll.js"></script>
-<link href="/dll.css" rel="stylesheet" />
+<script src="./dll.js"></script>
+<link href="./dll.css" rel="stylesheet" />
diff --git a/src/dev/storybook/run_storybook_cli.ts b/src/dev/storybook/run_storybook_cli.ts
index 0f7dc40ceef0b..efb618a48cd6e 100644
--- a/src/dev/storybook/run_storybook_cli.ts
+++ b/src/dev/storybook/run_storybook_cli.ts
@@ -52,6 +52,7 @@ run(
 
     log.verbose('Loading Storybook:', absolute);
     process.chdir(join(absolute, '..', '..'));
+
     require(absolute);
   },
   {
@@ -69,9 +70,10 @@ run(
     flags: {
       default: {},
       string: [],
-      boolean: ['clean'],
+      boolean: ['clean', 'site'],
       help: `
       --clean            Clean Storybook build folder.
+      --site             Build static version of Storybook.
     `,
     },
   }

From 3f4640b13d55d1b208e6c93610ca3e2cbcb61c1a Mon Sep 17 00:00:00 2001
From: Melissa Alvarez <melissa.alvarez@elastic.co>
Date: Thu, 20 Feb 2020 15:50:12 -0500
Subject: [PATCH 110/174] [ML] New Platform server shim: update datafeed routes
 (#57739)

* convert datafeed routes to new platfrom

* update datafeed schema

* consolidate datafeedConfig schema for datafeed + job validation
---
 .../server/new_platform/datafeeds_schema.ts   |  31 ++
 .../new_platform/job_validation_schema.ts     |  17 +-
 .../plugins/ml/server/new_platform/plugin.ts  |   1 -
 .../plugins/ml/server/routes/apidoc.json      |  13 +-
 .../plugins/ml/server/routes/datafeeds.js     | 152 ---------
 .../plugins/ml/server/routes/datafeeds.ts     | 320 ++++++++++++++++++
 6 files changed, 364 insertions(+), 170 deletions(-)
 create mode 100644 x-pack/legacy/plugins/ml/server/new_platform/datafeeds_schema.ts
 delete mode 100644 x-pack/legacy/plugins/ml/server/routes/datafeeds.js
 create mode 100644 x-pack/legacy/plugins/ml/server/routes/datafeeds.ts

diff --git a/x-pack/legacy/plugins/ml/server/new_platform/datafeeds_schema.ts b/x-pack/legacy/plugins/ml/server/new_platform/datafeeds_schema.ts
new file mode 100644
index 0000000000000..02677dcb107c2
--- /dev/null
+++ b/x-pack/legacy/plugins/ml/server/new_platform/datafeeds_schema.ts
@@ -0,0 +1,31 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { schema } from '@kbn/config-schema';
+
+export const startDatafeedSchema = schema.object({
+  start: schema.maybe(schema.oneOf([schema.number(), schema.string()])),
+  end: schema.maybe(schema.oneOf([schema.number(), schema.string()])),
+  timeout: schema.maybe(schema.any()),
+});
+
+export const datafeedConfigSchema = schema.object({
+  datafeed_id: schema.maybe(schema.string()),
+  feed_id: schema.maybe(schema.string()),
+  aggregations: schema.maybe(schema.any()),
+  aggs: schema.maybe(schema.any()),
+  chunking_config: schema.maybe(schema.any()),
+  frequency: schema.maybe(schema.string()),
+  indices: schema.arrayOf(schema.string()),
+  indexes: schema.maybe(schema.arrayOf(schema.string())),
+  job_id: schema.maybe(schema.string()),
+  query: schema.maybe(schema.any()),
+  max_empty_searches: schema.maybe(schema.number()),
+  query_delay: schema.maybe(schema.string()),
+  script_fields: schema.maybe(schema.any()),
+  scroll_size: schema.maybe(schema.number()),
+  delayed_data_check_config: schema.maybe(schema.any()),
+});
diff --git a/x-pack/legacy/plugins/ml/server/new_platform/job_validation_schema.ts b/x-pack/legacy/plugins/ml/server/new_platform/job_validation_schema.ts
index 5917ec50884d8..5da825a905e8d 100644
--- a/x-pack/legacy/plugins/ml/server/new_platform/job_validation_schema.ts
+++ b/x-pack/legacy/plugins/ml/server/new_platform/job_validation_schema.ts
@@ -6,6 +6,7 @@
 
 import { schema } from '@kbn/config-schema';
 import { anomalyDetectionJobSchema } from './anomaly_detectors_schema';
+import { datafeedConfigSchema } from './datafeeds_schema';
 
 export const estimateBucketSpanSchema = schema.object({
   aggTypes: schema.arrayOf(schema.nullable(schema.string())),
@@ -38,22 +39,6 @@ export const validateJobSchema = schema.object({
   job: schema.object(anomalyDetectionJobSchema),
 });
 
-const datafeedConfigSchema = schema.object({
-  datafeed_id: schema.string(),
-  aggregations: schema.maybe(schema.any()),
-  aggs: schema.maybe(schema.any()),
-  chunking_config: schema.maybe(schema.any()),
-  frequency: schema.maybe(schema.string()),
-  indices: schema.arrayOf(schema.string()),
-  indexes: schema.maybe(schema.arrayOf(schema.string())),
-  job_id: schema.string(),
-  query: schema.any(),
-  query_delay: schema.maybe(schema.string()),
-  script_fields: schema.maybe(schema.any()),
-  scroll_size: schema.maybe(schema.number()),
-  delayed_data_check_config: schema.maybe(schema.any()),
-});
-
 export const validateCardinalitySchema = {
   ...anomalyDetectionJobSchema,
   datafeed_config: datafeedConfigSchema,
diff --git a/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts b/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts
index e006ad3d3718f..10961182be841 100644
--- a/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts
+++ b/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts
@@ -27,7 +27,6 @@ import { mirrorPluginStatus } from '../../../../server/lib/mirror_plugin_status'
 import { LICENSE_TYPE } from '../../common/constants/license';
 import { annotationRoutes } from '../routes/annotations';
 import { jobRoutes } from '../routes/anomaly_detectors';
-// @ts-ignore: could not find declaration file for module
 import { dataFeedRoutes } from '../routes/datafeeds';
 // @ts-ignore: could not find declaration file for module
 import { indicesRoutes } from '../routes/indices';
diff --git a/x-pack/legacy/plugins/ml/server/routes/apidoc.json b/x-pack/legacy/plugins/ml/server/routes/apidoc.json
index 89751abdbe20d..7d1f13ead3fef 100644
--- a/x-pack/legacy/plugins/ml/server/routes/apidoc.json
+++ b/x-pack/legacy/plugins/ml/server/routes/apidoc.json
@@ -94,6 +94,17 @@
     "ValidateCardinality",
     "ValidateJob",
     "NotificationSettings",
-    "GetNotificationSettings"
+    "GetNotificationSettings",
+    "DatafeedService",
+    "GetDatafeeds",
+    "GetDatafeed",
+    "GetDatafeedsStats",
+    "GetDatafeedStats",
+    "CreateDatafeed",
+    "UpdateDatafeed",
+    "DeleteDatafeed",
+    "StartDatafeed",
+    "StopDatafeed",
+    "PreviewDatafeed"
   ]
 }
diff --git a/x-pack/legacy/plugins/ml/server/routes/datafeeds.js b/x-pack/legacy/plugins/ml/server/routes/datafeeds.js
deleted file mode 100644
index daa83795ff7d2..0000000000000
--- a/x-pack/legacy/plugins/ml/server/routes/datafeeds.js
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { callWithRequestFactory } from '../client/call_with_request_factory';
-import { wrapError } from '../client/errors';
-
-export function dataFeedRoutes({ commonRouteConfig, elasticsearchPlugin, route }) {
-  route({
-    method: 'GET',
-    path: '/api/ml/datafeeds',
-    handler(request) {
-      const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
-      return callWithRequest('ml.datafeeds').catch(resp => wrapError(resp));
-    },
-    config: {
-      ...commonRouteConfig,
-    },
-  });
-
-  route({
-    method: 'GET',
-    path: '/api/ml/datafeeds/{datafeedId}',
-    handler(request) {
-      const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
-      const datafeedId = request.params.datafeedId;
-      return callWithRequest('ml.datafeeds', { datafeedId }).catch(resp => wrapError(resp));
-    },
-    config: {
-      ...commonRouteConfig,
-    },
-  });
-
-  route({
-    method: 'GET',
-    path: '/api/ml/datafeeds/_stats',
-    handler(request) {
-      const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
-      return callWithRequest('ml.datafeedStats').catch(resp => wrapError(resp));
-    },
-    config: {
-      ...commonRouteConfig,
-    },
-  });
-
-  route({
-    method: 'GET',
-    path: '/api/ml/datafeeds/{datafeedId}/_stats',
-    handler(request) {
-      const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
-      const datafeedId = request.params.datafeedId;
-      return callWithRequest('ml.datafeedStats', { datafeedId }).catch(resp => wrapError(resp));
-    },
-    config: {
-      ...commonRouteConfig,
-    },
-  });
-
-  route({
-    method: 'PUT',
-    path: '/api/ml/datafeeds/{datafeedId}',
-    handler(request) {
-      const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
-      const datafeedId = request.params.datafeedId;
-      const body = request.payload;
-      return callWithRequest('ml.addDatafeed', { datafeedId, body }).catch(resp => wrapError(resp));
-    },
-    config: {
-      ...commonRouteConfig,
-    },
-  });
-
-  route({
-    method: 'POST',
-    path: '/api/ml/datafeeds/{datafeedId}/_update',
-    handler(request) {
-      const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
-      const datafeedId = request.params.datafeedId;
-      const body = request.payload;
-      return callWithRequest('ml.updateDatafeed', { datafeedId, body }).catch(resp =>
-        wrapError(resp)
-      );
-    },
-    config: {
-      ...commonRouteConfig,
-    },
-  });
-
-  route({
-    method: 'DELETE',
-    path: '/api/ml/datafeeds/{datafeedId}',
-    handler(request) {
-      const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
-      const options = {
-        datafeedId: request.params.datafeedId,
-      };
-      const force = request.query.force;
-      if (force !== undefined) {
-        options.force = force;
-      }
-      return callWithRequest('ml.deleteDatafeed', options).catch(resp => wrapError(resp));
-    },
-    config: {
-      ...commonRouteConfig,
-    },
-  });
-
-  route({
-    method: 'POST',
-    path: '/api/ml/datafeeds/{datafeedId}/_start',
-    handler(request) {
-      const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
-      const datafeedId = request.params.datafeedId;
-      const start = request.payload.start;
-      const end = request.payload.end;
-      return callWithRequest('ml.startDatafeed', { datafeedId, start, end }).catch(resp =>
-        wrapError(resp)
-      );
-    },
-    config: {
-      ...commonRouteConfig,
-    },
-  });
-
-  route({
-    method: 'POST',
-    path: '/api/ml/datafeeds/{datafeedId}/_stop',
-    handler(request) {
-      const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
-      const datafeedId = request.params.datafeedId;
-      return callWithRequest('ml.stopDatafeed', { datafeedId }).catch(resp => wrapError(resp));
-    },
-    config: {
-      ...commonRouteConfig,
-    },
-  });
-
-  route({
-    method: 'GET',
-    path: '/api/ml/datafeeds/{datafeedId}/_preview',
-    handler(request) {
-      const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
-      const datafeedId = request.params.datafeedId;
-      return callWithRequest('ml.datafeedPreview', { datafeedId }).catch(resp => wrapError(resp));
-    },
-    config: {
-      ...commonRouteConfig,
-    },
-  });
-}
diff --git a/x-pack/legacy/plugins/ml/server/routes/datafeeds.ts b/x-pack/legacy/plugins/ml/server/routes/datafeeds.ts
new file mode 100644
index 0000000000000..9335403616cf7
--- /dev/null
+++ b/x-pack/legacy/plugins/ml/server/routes/datafeeds.ts
@@ -0,0 +1,320 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { schema } from '@kbn/config-schema';
+import { licensePreRoutingFactory } from '../new_platform/licence_check_pre_routing_factory';
+import { wrapError } from '../client/error_wrapper';
+import { RouteInitialization } from '../new_platform/plugin';
+import { startDatafeedSchema, datafeedConfigSchema } from '../new_platform/datafeeds_schema';
+
+/**
+ * Routes for datafeed service
+ */
+export function dataFeedRoutes({ xpackMainPlugin, router }: RouteInitialization) {
+  /**
+   * @apiGroup DatafeedService
+   *
+   * @api {get} /api/ml/datafeeds Get all datafeeds
+   * @apiName GetDatafeeds
+   * @apiDescription Retrieves configuration information for datafeeds
+   */
+  router.get(
+    {
+      path: '/api/ml/datafeeds',
+      validate: false,
+    },
+    licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
+      try {
+        const resp = await context.ml!.mlClient.callAsCurrentUser('ml.datafeeds');
+
+        return response.ok({
+          body: resp,
+        });
+      } catch (e) {
+        return response.customError(wrapError(e));
+      }
+    })
+  );
+
+  /**
+   * @apiGroup DatafeedService
+   *
+   * @api {get} /api/ml/datafeeds/:datafeedId Get datafeed for given datafeed id
+   * @apiName GetDatafeed
+   * @apiDescription Retrieves configuration information for datafeed
+   */
+  router.get(
+    {
+      path: '/api/ml/datafeeds/{datafeedId}',
+      validate: {
+        params: schema.object({ datafeedId: schema.string() }),
+      },
+    },
+    licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
+      try {
+        const datafeedId = request.params.datafeedId;
+        const resp = await context.ml!.mlClient.callAsCurrentUser('ml.datafeeds', { datafeedId });
+
+        return response.ok({
+          body: resp,
+        });
+      } catch (e) {
+        return response.customError(wrapError(e));
+      }
+    })
+  );
+
+  /**
+   * @apiGroup DatafeedService
+   *
+   * @api {get} /api/ml/datafeeds/_stats Get stats for all datafeeds
+   * @apiName GetDatafeedsStats
+   * @apiDescription Retrieves usage information for datafeeds
+   */
+  router.get(
+    {
+      path: '/api/ml/datafeeds/_stats',
+      validate: false,
+    },
+    licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
+      try {
+        const resp = await context.ml!.mlClient.callAsCurrentUser('ml.datafeedStats');
+
+        return response.ok({
+          body: resp,
+        });
+      } catch (e) {
+        return response.customError(wrapError(e));
+      }
+    })
+  );
+
+  /**
+   * @apiGroup DatafeedService
+   *
+   * @api {get} /api/ml/datafeeds/:datafeedId/_stats Get datafeed stats for given datafeed id
+   * @apiName GetDatafeedStats
+   * @apiDescription Retrieves usage information for datafeed
+   */
+  router.get(
+    {
+      path: '/api/ml/datafeeds/{datafeedId}/_stats',
+      validate: {
+        params: schema.object({ datafeedId: schema.string() }),
+      },
+    },
+    licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
+      try {
+        const datafeedId = request.params.datafeedId;
+        const resp = await context.ml!.mlClient.callAsCurrentUser('ml.datafeedStats', {
+          datafeedId,
+        });
+
+        return response.ok({
+          body: resp,
+        });
+      } catch (e) {
+        return response.customError(wrapError(e));
+      }
+    })
+  );
+
+  /**
+   * @apiGroup DatafeedService
+   *
+   * @api {put} /api/ml/datafeeds/:datafeedId Creates datafeed
+   * @apiName CreateDatafeed
+   * @apiDescription Instantiates a datafeed
+   */
+  router.put(
+    {
+      path: '/api/ml/datafeeds/{datafeedId}',
+      validate: {
+        params: schema.object({ datafeedId: schema.string() }),
+        body: datafeedConfigSchema,
+      },
+    },
+    licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
+      try {
+        const datafeedId = request.params.datafeedId;
+        const resp = await context.ml!.mlClient.callAsCurrentUser('ml.addDatafeed', {
+          datafeedId,
+          body: request.body,
+        });
+
+        return response.ok({
+          body: resp,
+        });
+      } catch (e) {
+        return response.customError(wrapError(e));
+      }
+    })
+  );
+
+  /**
+   * @apiGroup DatafeedService
+   *
+   * @api {post} /api/ml/datafeeds/:datafeedId/_update Updates datafeed for given datafeed id
+   * @apiName UpdateDatafeed
+   * @apiDescription Updates certain properties of a datafeed
+   */
+  router.post(
+    {
+      path: '/api/ml/datafeeds/{datafeedId}/_update',
+      validate: {
+        params: schema.object({ datafeedId: schema.string() }),
+        body: datafeedConfigSchema,
+      },
+    },
+    licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
+      try {
+        const datafeedId = request.params.datafeedId;
+        const resp = await context.ml!.mlClient.callAsCurrentUser('ml.updateDatafeed', {
+          datafeedId,
+          body: request.body,
+        });
+
+        return response.ok({
+          body: resp,
+        });
+      } catch (e) {
+        return response.customError(wrapError(e));
+      }
+    })
+  );
+
+  /**
+   * @apiGroup DatafeedService
+   *
+   * @api {delete} /api/ml/datafeeds/:datafeedId Deletes datafeed
+   * @apiName DeleteDatafeed
+   * @apiDescription Deletes an existing datafeed
+   */
+  router.delete(
+    {
+      path: '/api/ml/datafeeds/{datafeedId}',
+      validate: {
+        params: schema.object({ datafeedId: schema.string() }),
+        query: schema.maybe(schema.object({ force: schema.maybe(schema.any()) })),
+      },
+    },
+    licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
+      try {
+        const options: { datafeedId: string; force?: boolean } = {
+          datafeedId: request.params.jobId,
+        };
+        const force = request.query.force;
+        if (force !== undefined) {
+          options.force = force;
+        }
+
+        const resp = await context.ml!.mlClient.callAsCurrentUser('ml.deleteDatafeed', options);
+
+        return response.ok({
+          body: resp,
+        });
+      } catch (e) {
+        return response.customError(wrapError(e));
+      }
+    })
+  );
+
+  /**
+   * @apiGroup DatafeedService
+   *
+   * @api {post} /api/ml/datafeeds/:datafeedId/_start Starts datafeed for given datafeed id(s)
+   * @apiName StartDatafeed
+   * @apiDescription Starts one or more datafeeds
+   */
+  router.post(
+    {
+      path: '/api/ml/datafeeds/{datafeedId}/_start',
+      validate: {
+        params: schema.object({ datafeedId: schema.string() }),
+        body: startDatafeedSchema,
+      },
+    },
+    licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
+      try {
+        const datafeedId = request.params.datafeedId;
+        const { start, end } = request.body;
+
+        const resp = await context.ml!.mlClient.callAsCurrentUser('ml.startDatafeed', {
+          datafeedId,
+          start,
+          end,
+        });
+
+        return response.ok({
+          body: resp,
+        });
+      } catch (e) {
+        return response.customError(wrapError(e));
+      }
+    })
+  );
+
+  /**
+   * @apiGroup DatafeedService
+   *
+   * @api {post} /api/ml/datafeeds/:datafeedId/_stop Stops datafeed for given datafeed id(s)
+   * @apiName StopDatafeed
+   * @apiDescription Stops one or more datafeeds
+   */
+  router.post(
+    {
+      path: '/api/ml/datafeeds/{datafeedId}/_stop',
+      validate: {
+        params: schema.object({ datafeedId: schema.string() }),
+      },
+    },
+    licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
+      try {
+        const datafeedId = request.params.datafeedId;
+
+        const resp = await context.ml!.mlClient.callAsCurrentUser('ml.stopDatafeed', {
+          datafeedId,
+        });
+
+        return response.ok({
+          body: resp,
+        });
+      } catch (e) {
+        return response.customError(wrapError(e));
+      }
+    })
+  );
+
+  /**
+   * @apiGroup DatafeedService
+   *
+   * @api {get} /api/ml/datafeeds/:datafeedId/_preview Preview datafeed for given datafeed id
+   * @apiName PreviewDatafeed
+   * @apiDescription Previews a datafeed
+   */
+  router.get(
+    {
+      path: '/api/ml/datafeeds/{datafeedId}/_preview',
+      validate: {
+        params: schema.object({ datafeedId: schema.string() }),
+      },
+    },
+    licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
+      try {
+        const datafeedId = request.params.datafeedId;
+        const resp = await context.ml!.mlClient.callAsCurrentUser('ml.datafeedPreview', {
+          datafeedId,
+        });
+
+        return response.ok({
+          body: resp,
+        });
+      } catch (e) {
+        return response.customError(wrapError(e));
+      }
+    })
+  );
+}

From 970bb75b84e0c13afc735e67c1bb459d4938e81b Mon Sep 17 00:00:00 2001
From: Josh Dover <me@joshdover.com>
Date: Thu, 20 Feb 2020 13:57:10 -0700
Subject: [PATCH 111/174] [skip-ci] Fix broken links to saved objects APIs in
 MIGRATION.md (#58033)

---
 src/core/MIGRATION.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/core/MIGRATION.md b/src/core/MIGRATION.md
index 19f62a7d48923..d33fd9bcce7a0 100644
--- a/src/core/MIGRATION.md
+++ b/src/core/MIGRATION.md
@@ -1200,9 +1200,9 @@ In server code, `core` can be accessed from either `server.newPlatform` or `kbnS
 | `server.plugins.elasticsearch.getCluster('data')`                             | [`context.core.elasticsearch.dataClient`](/docs/development/core/server/kibana-plugin-server.iscopedclusterclient.md)                                                                                                                                                                                            |                                                                             |
 | `server.plugins.elasticsearch.getCluster('admin')`                            | [`context.core.elasticsearch.adminClient`](/docs/development/core/server/kibana-plugin-server.iscopedclusterclient.md)                                                                                                                                                                                           |                                                                             |
 | `server.plugins.elasticsearch.createCluster(...)`                             | [`core.elasticsearch.createClient`](/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.createclient.md)                                                                                                                                                                           |                                                                             |
-| `server.savedObjects.setScopedSavedObjectsClientFactory`                      | [`core.savedObjects.setClientFactory`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.setclientfactory.md)                                                                                                                                                                     |                                                                             |
+| `server.savedObjects.setScopedSavedObjectsClientFactory`                      | [`core.savedObjects.setClientFactoryProvider`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.setclientfactoryprovider.md)                                                                                                                                                                     |                                                                             |
 | `server.savedObjects.addScopedSavedObjectsClientWrapperFactory`               | [`core.savedObjects.addClientWrapper`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.addclientwrapper.md)                                                                                                                                                                     |                                                                             |
-| `server.savedObjects.getSavedObjectsRepository`                               | [`core.savedObjects.createInternalRepository`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.createinternalrepository.md) [`core.savedObjects.createScopedRepository`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.createscopedrepository.md) |                                                                             |
+| `server.savedObjects.getSavedObjectsRepository`                               | [`core.savedObjects.createInternalRepository`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicestart.createinternalrepository.md) [`core.savedObjects.createScopedRepository`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicestart.createscopedrepository.md) |                                                                             |
 | `server.savedObjects.getScopedSavedObjectsClient`                             | [`core.savedObjects.getScopedClient`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicestart.getscopedclient.md)                                                                                                                                                                       |                                                                             |
 | `request.getSavedObjectsClient`                                               | [`context.core.savedObjects.client`](/docs/development/core/server/kibana-plugin-server.requesthandlercontext.core.md)                                                                                                                                                                                      |                                                                             |
 | `request.getUiSettingsService`                                                | [`context.uiSettings.client`](/docs/development/core/server/kibana-plugin-server.iuisettingsclient.md)                                                                                                                                                                                                      |                                                                             |

From 4cd809aa7d7241133cb79774c1a62c994f0b47dd Mon Sep 17 00:00:00 2001
From: Vadim Dalecky <streamich@users.noreply.github.com>
Date: Thu, 20 Feb 2020 21:59:29 +0100
Subject: [PATCH 112/174] Drilldown plugin (#58097)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat: 🎸 add <PanelOptionsMenu> component

* feat: 🎸 use presentational <PanelOptionsMenu> component

* feat: 🎸 create stubs for Drilldown components

* feat: 🎸 open new drilldown flyout from panel's context menu

* feat: 🎸 setup Drilldowns plugin in X-Pack

* feat: 🎸 add Storybook to drilldowns plugin

* refactor: 💡 move drilldown components to x-pack

* feat: 🎸 add stub action to open drilldown flyout

* feat: 🎸 add drilldowns plugin to translation index

* fix: 🐛 correct TypeScript type check

* fix: 🐛 use correct i18n namespace

* ci: 🎡 add drilldowns plugin to CODEOWNERS file

* fix: 🐛 revert back <PanelOptionsMenu> change

* fix: 🐛 type must not be empty
---
 .github/CODEOWNERS                            |  1 +
 src/dev/storybook/aliases.ts                  |  1 +
 .../components/embeddable_panel/index.tsx     | 29 ------
 .../panel_options_menu.examples.tsx}          | 33 ++++++-
 .../components/panel_options_menu/index.tsx   | 93 +++++++++++++++++++
 .../public/lib/panel/_embeddable_panel.scss   |  1 -
 x-pack/.i18nrc.json                           |  5 +-
 x-pack/plugins/drilldowns/README.md           |  3 +
 x-pack/plugins/drilldowns/kibana.json         | 10 ++
 .../drilldowns/public/actions/index.ts        |  7 ++
 .../open_flyout_add_drilldown/index.tsx       | 50 ++++++++++
 .../drilldown_hello_bar.examples.tsx          | 13 +++
 .../components/drilldown_hello_bar/index.tsx  | 27 ++++++
 .../drilldown_picker.examples.tsx             | 13 +++
 .../components/drilldown_picker/index.tsx     | 21 +++++
 .../form_create_drilldown.examples.tsx        | 13 +++
 .../components/form_create_drilldown/i18n.ts  | 28 ++++++
 .../form_create_drilldown/index.tsx           | 30 ++++++
 x-pack/plugins/drilldowns/public/index.ts     | 18 ++++
 x-pack/plugins/drilldowns/public/mocks.ts     | 28 ++++++
 x-pack/plugins/drilldowns/public/plugin.ts    | 45 +++++++++
 .../public/service/drilldown_service.ts       | 28 ++++++
 .../drilldowns/public/service/index.ts        |  7 ++
 .../plugins/drilldowns/scripts/storybook.js   | 13 +++
 24 files changed, 483 insertions(+), 34 deletions(-)
 delete mode 100644 src/plugins/embeddable/public/components/embeddable_panel/index.tsx
 rename src/plugins/embeddable/public/components/{embeddable_panel/__examples__/embeddable_panel.examples.tsx => panel_options_menu/__examples__/panel_options_menu.examples.tsx} (53%)
 create mode 100644 src/plugins/embeddable/public/components/panel_options_menu/index.tsx
 create mode 100644 x-pack/plugins/drilldowns/README.md
 create mode 100644 x-pack/plugins/drilldowns/kibana.json
 create mode 100644 x-pack/plugins/drilldowns/public/actions/index.ts
 create mode 100644 x-pack/plugins/drilldowns/public/actions/open_flyout_add_drilldown/index.tsx
 create mode 100644 x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/__examples__/drilldown_hello_bar.examples.tsx
 create mode 100644 x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/index.tsx
 create mode 100644 x-pack/plugins/drilldowns/public/components/drilldown_picker/__examples__/drilldown_picker.examples.tsx
 create mode 100644 x-pack/plugins/drilldowns/public/components/drilldown_picker/index.tsx
 create mode 100644 x-pack/plugins/drilldowns/public/components/form_create_drilldown/__examples__/form_create_drilldown.examples.tsx
 create mode 100644 x-pack/plugins/drilldowns/public/components/form_create_drilldown/i18n.ts
 create mode 100644 x-pack/plugins/drilldowns/public/components/form_create_drilldown/index.tsx
 create mode 100644 x-pack/plugins/drilldowns/public/index.ts
 create mode 100644 x-pack/plugins/drilldowns/public/mocks.ts
 create mode 100644 x-pack/plugins/drilldowns/public/plugin.ts
 create mode 100644 x-pack/plugins/drilldowns/public/service/drilldown_service.ts
 create mode 100644 x-pack/plugins/drilldowns/public/service/index.ts
 create mode 100644 x-pack/plugins/drilldowns/scripts/storybook.js

diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index dbac07c5e093c..51433f598ac16 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -54,6 +54,7 @@
 /src/plugins/ui_actions/ @elastic/kibana-app-arch
 /src/plugins/visualizations/ @elastic/kibana-app-arch
 /x-pack/plugins/advanced_ui_actions/ @elastic/kibana-app-arch
+/x-pack/plugins/drilldowns/ @elastic/kibana-app-arch
 
 # APM
 /x-pack/legacy/plugins/apm/  @elastic/apm-ui
diff --git a/src/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts
index 1dce53b6c2a84..fb91b865097fa 100644
--- a/src/dev/storybook/aliases.ts
+++ b/src/dev/storybook/aliases.ts
@@ -20,6 +20,7 @@
 export const storybookAliases = {
   apm: 'x-pack/legacy/plugins/apm/scripts/storybook.js',
   canvas: 'x-pack/legacy/plugins/canvas/scripts/storybook_new.js',
+  drilldowns: 'x-pack/plugins/drilldowns/scripts/storybook.js',
   embeddable: 'src/plugins/embeddable/scripts/storybook.js',
   infra: 'x-pack/legacy/plugins/infra/scripts/storybook.js',
   siem: 'x-pack/legacy/plugins/siem/scripts/storybook.js',
diff --git a/src/plugins/embeddable/public/components/embeddable_panel/index.tsx b/src/plugins/embeddable/public/components/embeddable_panel/index.tsx
deleted file mode 100644
index 7089efa4bca88..0000000000000
--- a/src/plugins/embeddable/public/components/embeddable_panel/index.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { EuiPanel } from '@elastic/eui';
-import * as React from 'react';
-
-export const EmbeddablePanel = () => {
-  return (
-    <EuiPanel data-test-subj="embeddablePanel" paddingSize="none" role="figure">
-      Hello world
-    </EuiPanel>
-  );
-};
diff --git a/src/plugins/embeddable/public/components/embeddable_panel/__examples__/embeddable_panel.examples.tsx b/src/plugins/embeddable/public/components/panel_options_menu/__examples__/panel_options_menu.examples.tsx
similarity index 53%
rename from src/plugins/embeddable/public/components/embeddable_panel/__examples__/embeddable_panel.examples.tsx
rename to src/plugins/embeddable/public/components/panel_options_menu/__examples__/panel_options_menu.examples.tsx
index 7ec8848b8cebd..33724068a6ba8 100644
--- a/src/plugins/embeddable/public/components/embeddable_panel/__examples__/embeddable_panel.examples.tsx
+++ b/src/plugins/embeddable/public/components/panel_options_menu/__examples__/panel_options_menu.examples.tsx
@@ -19,6 +19,35 @@
 
 import * as React from 'react';
 import { storiesOf } from '@storybook/react';
-import { EmbeddablePanel } from '..';
+import { action } from '@storybook/addon-actions';
+import { withKnobs, boolean } from '@storybook/addon-knobs';
+import { PanelOptionsMenu } from '..';
 
-storiesOf('components/EmbeddablePanel', module).add('default', () => <EmbeddablePanel />);
+const euiContextDescriptors = {
+  id: 'mainMenu',
+  title: 'Options',
+  items: [
+    {
+      name: 'Inspect',
+      icon: 'inspect',
+      onClick: action('onClick(inspect)'),
+    },
+    {
+      name: 'Full screen',
+      icon: 'expand',
+      onClick: action('onClick(expand)'),
+    },
+  ],
+};
+
+storiesOf('components/PanelOptionsMenu', module)
+  .addDecorator(withKnobs)
+  .add('default', () => {
+    const isViewMode = boolean('isViewMode', false);
+
+    return (
+      <div style={{ height: 150 }}>
+        <PanelOptionsMenu panelDescriptor={euiContextDescriptors} isViewMode={isViewMode} />
+      </div>
+    );
+  });
diff --git a/src/plugins/embeddable/public/components/panel_options_menu/index.tsx b/src/plugins/embeddable/public/components/panel_options_menu/index.tsx
new file mode 100644
index 0000000000000..4a95027269587
--- /dev/null
+++ b/src/plugins/embeddable/public/components/panel_options_menu/index.tsx
@@ -0,0 +1,93 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { i18n } from '@kbn/i18n';
+import React, { useState, useEffect } from 'react';
+import {
+  EuiButtonIcon,
+  EuiContextMenu,
+  EuiContextMenuPanelDescriptor,
+  EuiPopover,
+} from '@elastic/eui';
+
+export interface PanelOptionsMenuProps {
+  panelDescriptor?: EuiContextMenuPanelDescriptor;
+  close?: boolean;
+  isViewMode?: boolean;
+  title?: string;
+}
+
+export const PanelOptionsMenu: React.FC<PanelOptionsMenuProps> = ({
+  panelDescriptor,
+  close,
+  isViewMode,
+  title,
+}) => {
+  const [open, setOpen] = useState(false);
+  useEffect(() => {
+    if (!close) setOpen(false);
+  }, [close]);
+
+  const handleContextMenuClick = () => {
+    setOpen(isOpen => !isOpen);
+  };
+
+  const handlePopoverClose = () => {
+    setOpen(false);
+  };
+
+  const enhancedAriaLabel = i18n.translate(
+    'embeddableApi.panel.optionsMenu.panelOptionsButtonEnhancedAriaLabel',
+    {
+      defaultMessage: 'Panel options for {title}',
+      values: { title },
+    }
+  );
+  const ariaLabelWithoutTitle = i18n.translate(
+    'embeddableApi.panel.optionsMenu.panelOptionsButtonAriaLabel',
+    {
+      defaultMessage: 'Panel options',
+    }
+  );
+
+  const button = (
+    <EuiButtonIcon
+      iconType={isViewMode ? 'boxesHorizontal' : 'gear'}
+      color="text"
+      className="embPanel__optionsMenuButton"
+      aria-label={title ? enhancedAriaLabel : ariaLabelWithoutTitle}
+      data-test-subj="embeddablePanelToggleMenuIcon"
+      onClick={handleContextMenuClick}
+    />
+  );
+
+  return (
+    <EuiPopover
+      button={button}
+      isOpen={open}
+      closePopover={handlePopoverClose}
+      panelPaddingSize="none"
+      anchorPosition="downRight"
+      data-test-subj={open ? 'embeddablePanelContextMenuOpen' : 'embeddablePanelContextMenuClosed'}
+      withTitle
+    >
+      <EuiContextMenu initialPanelId="mainMenu" panels={panelDescriptor ? [panelDescriptor] : []} />
+    </EuiPopover>
+  );
+};
diff --git a/src/plugins/embeddable/public/lib/panel/_embeddable_panel.scss b/src/plugins/embeddable/public/lib/panel/_embeddable_panel.scss
index 52a9ea594ff1d..9de20b73af0f8 100644
--- a/src/plugins/embeddable/public/lib/panel/_embeddable_panel.scss
+++ b/src/plugins/embeddable/public/lib/panel/_embeddable_panel.scss
@@ -100,7 +100,6 @@
   }
 }
 
-.embPanel__optionsMenuPopover[class*='-isOpen'],
 .embPanel:hover {
   .embPanel__optionsMenuButton {
     opacity: 1;
diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json
index f0b590f7ffd6c..f22f7e98d3b8a 100644
--- a/x-pack/.i18nrc.json
+++ b/x-pack/.i18nrc.json
@@ -4,12 +4,13 @@
     "xpack.actions": "plugins/actions",
     "xpack.advancedUiActions": "plugins/advanced_ui_actions",
     "xpack.alerting": "plugins/alerting",
-    "xpack.triggersActionsUI": "plugins/triggers_actions_ui",
     "xpack.apm": ["legacy/plugins/apm", "plugins/apm"],
     "xpack.beatsManagement": "legacy/plugins/beats_management",
     "xpack.canvas": "legacy/plugins/canvas",
     "xpack.crossClusterReplication": "legacy/plugins/cross_cluster_replication",
     "xpack.dashboardMode": "legacy/plugins/dashboard_mode",
+    "xpack.data": "plugins/data_enhanced",
+    "xpack.drilldowns": "plugins/drilldowns",
     "xpack.endpoint": "plugins/endpoint",
     "xpack.features": "plugins/features",
     "xpack.fileUpload": "legacy/plugins/file_upload",
@@ -19,7 +20,6 @@
     "xpack.indexLifecycleMgmt": "legacy/plugins/index_lifecycle_management",
     "xpack.infra": "plugins/infra",
     "xpack.ingestManager": "plugins/ingest_manager",
-    "xpack.data": "plugins/data_enhanced",
     "xpack.lens": "legacy/plugins/lens",
     "xpack.licenseMgmt": "legacy/plugins/license_management",
     "xpack.licensing": "plugins/licensing",
@@ -39,6 +39,7 @@
     "xpack.spaces": ["legacy/plugins/spaces", "plugins/spaces"],
     "xpack.taskManager": "legacy/plugins/task_manager",
     "xpack.transform": "legacy/plugins/transform",
+    "xpack.triggersActionsUI": "plugins/triggers_actions_ui",
     "xpack.upgradeAssistant": "legacy/plugins/upgrade_assistant",
     "xpack.uptime": "legacy/plugins/uptime",
     "xpack.watcher": "plugins/watcher"
diff --git a/x-pack/plugins/drilldowns/README.md b/x-pack/plugins/drilldowns/README.md
new file mode 100644
index 0000000000000..701b6082d4985
--- /dev/null
+++ b/x-pack/plugins/drilldowns/README.md
@@ -0,0 +1,3 @@
+# Drilldowns
+
+Provides functionality to navigate between Kibana apps with context information.
diff --git a/x-pack/plugins/drilldowns/kibana.json b/x-pack/plugins/drilldowns/kibana.json
new file mode 100644
index 0000000000000..b951c7dc1fc87
--- /dev/null
+++ b/x-pack/plugins/drilldowns/kibana.json
@@ -0,0 +1,10 @@
+{
+  "id": "drilldowns",
+  "version": "kibana",
+  "server": false,
+  "ui": true,
+  "requiredPlugins": [
+    "uiActions",
+    "embeddable"
+  ]
+}
diff --git a/x-pack/plugins/drilldowns/public/actions/index.ts b/x-pack/plugins/drilldowns/public/actions/index.ts
new file mode 100644
index 0000000000000..c0ca7fac22049
--- /dev/null
+++ b/x-pack/plugins/drilldowns/public/actions/index.ts
@@ -0,0 +1,7 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export * from './open_flyout_add_drilldown';
diff --git a/x-pack/plugins/drilldowns/public/actions/open_flyout_add_drilldown/index.tsx b/x-pack/plugins/drilldowns/public/actions/open_flyout_add_drilldown/index.tsx
new file mode 100644
index 0000000000000..06f134b10a4b7
--- /dev/null
+++ b/x-pack/plugins/drilldowns/public/actions/open_flyout_add_drilldown/index.tsx
@@ -0,0 +1,50 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { i18n } from '@kbn/i18n';
+import { CoreStart } from 'src/core/public';
+import { Action } from '../../../../../../src/plugins/ui_actions/public';
+import { toMountPoint } from '../../../../../../src/plugins/kibana_react/public';
+import { IEmbeddable } from '../../../../../../src/plugins/embeddable/public';
+import { FormCreateDrilldown } from '../../components/form_create_drilldown';
+
+export const OPEN_FLYOUT_ADD_DRILLDOWN = 'OPEN_FLYOUT_ADD_DRILLDOWN';
+
+interface ActionContext {
+  embeddable: IEmbeddable;
+}
+
+export interface OpenFlyoutAddDrilldownParams {
+  overlays: () => Promise<CoreStart['overlays']>;
+}
+
+export class OpenFlyoutAddDrilldown implements Action<ActionContext> {
+  public readonly type = OPEN_FLYOUT_ADD_DRILLDOWN;
+  public readonly id = OPEN_FLYOUT_ADD_DRILLDOWN;
+  public order = 100;
+
+  constructor(protected readonly params: OpenFlyoutAddDrilldownParams) {}
+
+  public getDisplayName() {
+    return i18n.translate('xpack.drilldowns.panel.openFlyoutAddDrilldown.displayName', {
+      defaultMessage: 'Add drilldown',
+    });
+  }
+
+  public getIconType() {
+    return 'empty';
+  }
+
+  public async isCompatible({ embeddable }: ActionContext) {
+    return true;
+  }
+
+  public async execute({ embeddable }: ActionContext) {
+    const overlays = await this.params.overlays();
+    overlays.openFlyout(toMountPoint(<FormCreateDrilldown />));
+  }
+}
diff --git a/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/__examples__/drilldown_hello_bar.examples.tsx b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/__examples__/drilldown_hello_bar.examples.tsx
new file mode 100644
index 0000000000000..afa82f5e74c16
--- /dev/null
+++ b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/__examples__/drilldown_hello_bar.examples.tsx
@@ -0,0 +1,13 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import * as React from 'react';
+import { storiesOf } from '@storybook/react';
+import { DrilldownHelloBar } from '..';
+
+storiesOf('components/DrilldownHelloBar', module).add('default', () => {
+  return <DrilldownHelloBar />;
+});
diff --git a/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/index.tsx b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/index.tsx
new file mode 100644
index 0000000000000..895a100df3ac5
--- /dev/null
+++ b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/index.tsx
@@ -0,0 +1,27 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+
+export interface DrilldownHelloBarProps {
+  docsLink?: string;
+}
+
+export const DrilldownHelloBar: React.FC<DrilldownHelloBarProps> = ({ docsLink }) => {
+  return (
+    <div>
+      <p>
+        Drilldowns provide the ability to define a new behavior when interacting with a panel. You
+        can add multiple options or simply override the default filtering behavior.
+      </p>
+      <a href={docsLink}>View docs</a>
+      <img
+        src="https://user-images.githubusercontent.com/9773803/72729009-e5803180-3b8e-11ea-8330-b86089bf5f0a.png"
+        alt=""
+      />
+    </div>
+  );
+};
diff --git a/x-pack/plugins/drilldowns/public/components/drilldown_picker/__examples__/drilldown_picker.examples.tsx b/x-pack/plugins/drilldowns/public/components/drilldown_picker/__examples__/drilldown_picker.examples.tsx
new file mode 100644
index 0000000000000..dfdd9627ab5cd
--- /dev/null
+++ b/x-pack/plugins/drilldowns/public/components/drilldown_picker/__examples__/drilldown_picker.examples.tsx
@@ -0,0 +1,13 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import * as React from 'react';
+import { storiesOf } from '@storybook/react';
+import { DrilldownPicker } from '..';
+
+storiesOf('components/DrilldownPicker', module).add('default', () => {
+  return <DrilldownPicker />;
+});
diff --git a/x-pack/plugins/drilldowns/public/components/drilldown_picker/index.tsx b/x-pack/plugins/drilldowns/public/components/drilldown_picker/index.tsx
new file mode 100644
index 0000000000000..3748fc666c81c
--- /dev/null
+++ b/x-pack/plugins/drilldowns/public/components/drilldown_picker/index.tsx
@@ -0,0 +1,21 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+
+// eslint-disable-next-line
+export interface DrilldownPickerProps {}
+
+export const DrilldownPicker: React.FC<DrilldownPickerProps> = () => {
+  return (
+    <img
+      src={
+        'https://user-images.githubusercontent.com/9773803/72725665-9e8e3e00-3b86-11ea-9314-8724c521b41f.png'
+      }
+      alt=""
+    />
+  );
+};
diff --git a/x-pack/plugins/drilldowns/public/components/form_create_drilldown/__examples__/form_create_drilldown.examples.tsx b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/__examples__/form_create_drilldown.examples.tsx
new file mode 100644
index 0000000000000..34f6932b41dac
--- /dev/null
+++ b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/__examples__/form_create_drilldown.examples.tsx
@@ -0,0 +1,13 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import * as React from 'react';
+import { storiesOf } from '@storybook/react';
+import { FormCreateDrilldown } from '..';
+
+storiesOf('components/FormCreateDrilldown', module).add('default', () => {
+  return <FormCreateDrilldown />;
+});
diff --git a/x-pack/plugins/drilldowns/public/components/form_create_drilldown/i18n.ts b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/i18n.ts
new file mode 100644
index 0000000000000..922131ba4b901
--- /dev/null
+++ b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/i18n.ts
@@ -0,0 +1,28 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+export const txtNameOfDrilldown = i18n.translate(
+  'xpack.drilldowns.components.form_create_drilldown.nameOfDrilldown',
+  {
+    defaultMessage: 'Name of drilldown',
+  }
+);
+
+export const txtUntitledDrilldown = i18n.translate(
+  'xpack.drilldowns.components.form_create_drilldown.untitledDrilldown',
+  {
+    defaultMessage: 'Untitled drilldown',
+  }
+);
+
+export const txtDrilldownAction = i18n.translate(
+  'xpack.drilldowns.components.form_create_drilldown.drilldownAction',
+  {
+    defaultMessage: 'Drilldown action',
+  }
+);
diff --git a/x-pack/plugins/drilldowns/public/components/form_create_drilldown/index.tsx b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/index.tsx
new file mode 100644
index 0000000000000..40cd4cf2b210b
--- /dev/null
+++ b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/index.tsx
@@ -0,0 +1,30 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { EuiForm, EuiFormRow, EuiFieldText } from '@elastic/eui';
+import { DrilldownHelloBar } from '../drilldown_hello_bar';
+import { txtNameOfDrilldown, txtUntitledDrilldown, txtDrilldownAction } from './i18n';
+import { DrilldownPicker } from '../drilldown_picker';
+
+// eslint-disable-next-line
+export interface FormCreateDrilldownProps {}
+
+export const FormCreateDrilldown: React.FC<FormCreateDrilldownProps> = () => {
+  return (
+    <div>
+      <DrilldownHelloBar />
+      <EuiForm>
+        <EuiFormRow label={txtNameOfDrilldown}>
+          <EuiFieldText name="drilldown_name" placeholder={txtUntitledDrilldown} />
+        </EuiFormRow>
+        <EuiFormRow label={txtDrilldownAction}>
+          <DrilldownPicker />
+        </EuiFormRow>
+      </EuiForm>
+    </div>
+  );
+};
diff --git a/x-pack/plugins/drilldowns/public/index.ts b/x-pack/plugins/drilldowns/public/index.ts
new file mode 100644
index 0000000000000..63e7a12235462
--- /dev/null
+++ b/x-pack/plugins/drilldowns/public/index.ts
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { DrilldownsPlugin } from './plugin';
+
+export {
+  DrilldownsSetupContract,
+  DrilldownsSetupDependencies,
+  DrilldownsStartContract,
+  DrilldownsStartDependencies,
+} from './plugin';
+
+export function plugin() {
+  return new DrilldownsPlugin();
+}
diff --git a/x-pack/plugins/drilldowns/public/mocks.ts b/x-pack/plugins/drilldowns/public/mocks.ts
new file mode 100644
index 0000000000000..bfade1674072a
--- /dev/null
+++ b/x-pack/plugins/drilldowns/public/mocks.ts
@@ -0,0 +1,28 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { DrilldownsSetupContract, DrilldownsStartContract } from '.';
+
+export type Setup = jest.Mocked<DrilldownsSetupContract>;
+export type Start = jest.Mocked<DrilldownsStartContract>;
+
+const createSetupContract = (): Setup => {
+  const setupContract: Setup = {
+    registerDrilldown: jest.fn(),
+  };
+  return setupContract;
+};
+
+const createStartContract = (): Start => {
+  const startContract: Start = {};
+
+  return startContract;
+};
+
+export const bfetchPluginMock = {
+  createSetupContract,
+  createStartContract,
+};
diff --git a/x-pack/plugins/drilldowns/public/plugin.ts b/x-pack/plugins/drilldowns/public/plugin.ts
new file mode 100644
index 0000000000000..6c8555fa55a11
--- /dev/null
+++ b/x-pack/plugins/drilldowns/public/plugin.ts
@@ -0,0 +1,45 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { CoreStart, CoreSetup, Plugin } from 'src/core/public';
+import { UiActionsSetup, UiActionsStart } from '../../../../src/plugins/ui_actions/public';
+import { DrilldownService } from './service';
+
+export interface DrilldownsSetupDependencies {
+  uiActions: UiActionsSetup;
+}
+
+export interface DrilldownsStartDependencies {
+  uiActions: UiActionsStart;
+}
+
+export type DrilldownsSetupContract = Pick<DrilldownService, 'registerDrilldown'>;
+
+// eslint-disable-next-line
+export interface DrilldownsStartContract {}
+
+export class DrilldownsPlugin
+  implements
+    Plugin<
+      DrilldownsSetupContract,
+      DrilldownsStartContract,
+      DrilldownsSetupDependencies,
+      DrilldownsStartDependencies
+    > {
+  private readonly service = new DrilldownService();
+
+  public setup(core: CoreSetup, plugins: DrilldownsSetupDependencies): DrilldownsSetupContract {
+    this.service.bootstrap(core, plugins);
+
+    return this.service;
+  }
+
+  public start(core: CoreStart, plugins: DrilldownsStartDependencies): DrilldownsStartContract {
+    return {};
+  }
+
+  public stop() {}
+}
diff --git a/x-pack/plugins/drilldowns/public/service/drilldown_service.ts b/x-pack/plugins/drilldowns/public/service/drilldown_service.ts
new file mode 100644
index 0000000000000..f22f452181648
--- /dev/null
+++ b/x-pack/plugins/drilldowns/public/service/drilldown_service.ts
@@ -0,0 +1,28 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { CoreSetup } from 'src/core/public';
+import { OpenFlyoutAddDrilldown } from '../actions/open_flyout_add_drilldown';
+import { DrilldownsSetupDependencies } from '../plugin';
+
+export class DrilldownService {
+  bootstrap(core: CoreSetup, { uiActions }: DrilldownsSetupDependencies) {
+    const actionOpenFlyoutAddDrilldown = new OpenFlyoutAddDrilldown({
+      overlays: async () => (await core.getStartServices())[0].overlays,
+    });
+
+    uiActions.registerAction(actionOpenFlyoutAddDrilldown);
+    uiActions.attachAction('CONTEXT_MENU_TRIGGER', actionOpenFlyoutAddDrilldown.id);
+  }
+
+  /**
+   * Convenience method to register a drilldown. (It should set-up all the
+   * necessary triggers and actions.)
+   */
+  registerDrilldown = (): void => {
+    throw new Error('not implemented');
+  };
+}
diff --git a/x-pack/plugins/drilldowns/public/service/index.ts b/x-pack/plugins/drilldowns/public/service/index.ts
new file mode 100644
index 0000000000000..44472b18a5317
--- /dev/null
+++ b/x-pack/plugins/drilldowns/public/service/index.ts
@@ -0,0 +1,7 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export * from './drilldown_service';
diff --git a/x-pack/plugins/drilldowns/scripts/storybook.js b/x-pack/plugins/drilldowns/scripts/storybook.js
new file mode 100644
index 0000000000000..9b0f57746e584
--- /dev/null
+++ b/x-pack/plugins/drilldowns/scripts/storybook.js
@@ -0,0 +1,13 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { join } from 'path';
+
+// eslint-disable-next-line
+require('@kbn/storybook').runStorybookCli({
+  name: 'drilldowns',
+  storyGlobs: [join(__dirname, '..', 'public', 'components', '**', '*.examples.tsx')],
+});

From 6b77c88e98af2bb06140e74758b2ebc18e302c34 Mon Sep 17 00:00:00 2001
From: Nick Partridge <nick.ryan.partridge@gmail.com>
Date: Thu, 20 Feb 2020 15:07:03 -0600
Subject: [PATCH 113/174] Fix legend sizing on area charts (#58083)

---
 .../vislib/components/legend/legend.tsx       | 63 ++++++++++---------
 1 file changed, 34 insertions(+), 29 deletions(-)

diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend.tsx b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend.tsx
index c1563625c3b8c..b9d218b089c31 100644
--- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend.tsx
+++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend.tsx
@@ -53,6 +53,7 @@ export interface VisLegendState {
   open: boolean;
   labels: any[];
   tableAggs: any[];
+  filterableLabels: Set<string>;
   selectedLabel: string | null;
 }
 
@@ -68,6 +69,7 @@ export class VisLegend extends PureComponent<VisLegendProps, VisLegendState> {
       open,
       labels: [],
       tableAggs: [],
+      filterableLabels: new Set(),
       selectedLabel: null,
     };
   }
@@ -133,40 +135,43 @@ export class VisLegend extends PureComponent<VisLegendProps, VisLegendState> {
     }));
   };
 
-  // Most of these functions were moved directly from the old Legend class. Not a fan of this.
-  setLabels = (data: any, type: string): Promise<void> =>
+  setFilterableLabels = (items: LegendItem[]): Promise<void> =>
     new Promise(async resolve => {
-      let labels = [];
-      if (CUSTOM_LEGEND_VIS_TYPES.includes(type)) {
-        const legendLabels = this.props.vislibVis.getLegendLabels();
-        if (legendLabels) {
-          labels = map(legendLabels, label => {
-            return { label };
-          });
+      const filterableLabels = new Set<string>();
+      items.forEach(async item => {
+        const canFilter = await this.canFilter(item);
+        if (canFilter) {
+          filterableLabels.add(item.label);
         }
-      } else {
-        if (!data) return [];
-        data = data.columns || data.rows || [data];
+      });
+
+      this.setState({ filterableLabels }, resolve);
+    });
 
-        labels = type === 'pie' ? getPieNames(data) : this.getSeriesLabels(data);
+  setLabels = (data: any, type: string) => {
+    let labels = [];
+    if (CUSTOM_LEGEND_VIS_TYPES.includes(type)) {
+      const legendLabels = this.props.vislibVis.getLegendLabels();
+      if (legendLabels) {
+        labels = map(legendLabels, label => {
+          return { label };
+        });
       }
+    } else {
+      if (!data) return [];
+      data = data.columns || data.rows || [data];
 
-      const labelsConfig = await Promise.all(
-        labels.map(async label => ({
-          ...label,
-          canFilter: await this.canFilter(label),
-        }))
-      );
-
-      this.setState(
-        {
-          labels: labelsConfig,
-        },
-        resolve
-      );
+      labels = type === 'pie' ? getPieNames(data) : this.getSeriesLabels(data);
+    }
+
+    this.setFilterableLabels(labels);
+
+    this.setState({
+      labels,
     });
+  };
 
-  refresh = async () => {
+  refresh = () => {
     const vislibVis = this.props.vislibVis;
     if (!vislibVis || !vislibVis.visConfig) {
       this.setState({
@@ -193,7 +198,7 @@ export class VisLegend extends PureComponent<VisLegendProps, VisLegendState> {
     }
 
     this.setState({ tableAggs: getTableAggs(this.props.vis) });
-    await this.setLabels(this.props.visData, vislibVis.visConfigArgs.type);
+    this.setLabels(this.props.visData, vislibVis.visConfigArgs.type);
   };
 
   highlight = (event: BaseSyntheticEvent) => {
@@ -241,7 +246,7 @@ export class VisLegend extends PureComponent<VisLegendProps, VisLegendState> {
           key={item.label}
           anchorPosition={anchorPosition}
           selected={this.state.selectedLabel === item.label}
-          canFilter={item.canFilter}
+          canFilter={this.state.filterableLabels.has(item.label)}
           onFilter={this.filter}
           onSelect={this.toggleDetails}
           legendId={this.legendId}

From 523849338167e484ae7ef7fa80423618f90ef45d Mon Sep 17 00:00:00 2001
From: Josh Dover <me@joshdover.com>
Date: Thu, 20 Feb 2020 14:43:15 -0700
Subject: [PATCH 114/174] Expose serverBasePath on client-side (#58070)

---
 .../core/public/kibana-plugin-public.ibasepath.md |  1 +
 ...bana-plugin-public.ibasepath.serverbasepath.md | 15 +++++++++++++++
 .../kibana-plugin-public.ihttpfetcherror.name.md  | 11 +++++++++++
 src/core/public/http/base_path.test.ts            | 10 ++++++++++
 src/core/public/http/base_path.ts                 |  5 ++++-
 src/core/public/http/http_service.ts              |  5 ++++-
 src/core/public/http/types.ts                     |  7 +++++++
 .../injected_metadata_service.mock.ts             |  1 +
 .../injected_metadata_service.ts                  |  6 ++++++
 src/core/public/public.api.md                     |  2 ++
 .../__snapshots__/rendering_service.test.ts.snap  | 10 ++++++++++
 src/core/server/rendering/rendering_service.tsx   |  2 ++
 src/core/server/rendering/types.ts                |  1 +
 .../dashboard_empty_screen.test.tsx.snap          |  3 +++
 .../query_string_input.test.tsx.snap              |  6 ++++++
 .../api_keys/api_keys_management_app.test.tsx     |  2 +-
 .../role_mappings_management_app.test.tsx         |  6 +++---
 .../roles/roles_management_app.test.tsx           |  8 ++++----
 .../users/users_management_app.test.tsx           |  6 +++---
 .../management/spaces_management_app.test.tsx     |  6 +++---
 20 files changed, 97 insertions(+), 16 deletions(-)
 create mode 100644 docs/development/core/public/kibana-plugin-public.ibasepath.serverbasepath.md
 create mode 100644 docs/development/core/public/kibana-plugin-public.ihttpfetcherror.name.md

diff --git a/docs/development/core/public/kibana-plugin-public.ibasepath.md b/docs/development/core/public/kibana-plugin-public.ibasepath.md
index ca4c4b7ad3be7..7f2070eb1fd6d 100644
--- a/docs/development/core/public/kibana-plugin-public.ibasepath.md
+++ b/docs/development/core/public/kibana-plugin-public.ibasepath.md
@@ -19,4 +19,5 @@ export interface IBasePath
 |  [get](./kibana-plugin-public.ibasepath.get.md) | <code>() =&gt; string</code> | Gets the <code>basePath</code> string. |
 |  [prepend](./kibana-plugin-public.ibasepath.prepend.md) | <code>(url: string) =&gt; string</code> | Prepends <code>path</code> with the basePath. |
 |  [remove](./kibana-plugin-public.ibasepath.remove.md) | <code>(url: string) =&gt; string</code> | Removes the prepended basePath from the <code>path</code>. |
+|  [serverBasePath](./kibana-plugin-public.ibasepath.serverbasepath.md) | <code>string</code> | Returns the server's root basePath as configured, without any namespace prefix.<!-- -->See  for getting the basePath value for a specific request |
 
diff --git a/docs/development/core/public/kibana-plugin-public.ibasepath.serverbasepath.md b/docs/development/core/public/kibana-plugin-public.ibasepath.serverbasepath.md
new file mode 100644
index 0000000000000..0c2b5451767c7
--- /dev/null
+++ b/docs/development/core/public/kibana-plugin-public.ibasepath.serverbasepath.md
@@ -0,0 +1,15 @@
+<!-- Do not edit this file. It is automatically generated by API Documenter. -->
+
+[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [IBasePath](./kibana-plugin-public.ibasepath.md) &gt; [serverBasePath](./kibana-plugin-public.ibasepath.serverbasepath.md)
+
+## IBasePath.serverBasePath property
+
+Returns the server's root basePath as configured, without any namespace prefix.
+
+See  for getting the basePath value for a specific request
+
+<b>Signature:</b>
+
+```typescript
+readonly serverBasePath: string;
+```
diff --git a/docs/development/core/public/kibana-plugin-public.ihttpfetcherror.name.md b/docs/development/core/public/kibana-plugin-public.ihttpfetcherror.name.md
new file mode 100644
index 0000000000000..ba986b75503ed
--- /dev/null
+++ b/docs/development/core/public/kibana-plugin-public.ihttpfetcherror.name.md
@@ -0,0 +1,11 @@
+<!-- Do not edit this file. It is automatically generated by API Documenter. -->
+
+[Home](./index.md) &gt; [kibana-plugin-public](./kibana-plugin-public.md) &gt; [IHttpFetchError](./kibana-plugin-public.ihttpfetcherror.md) &gt; [name](./kibana-plugin-public.ihttpfetcherror.name.md)
+
+## IHttpFetchError.name property
+
+<b>Signature:</b>
+
+```typescript
+readonly name: string;
+```
diff --git a/src/core/public/http/base_path.test.ts b/src/core/public/http/base_path.test.ts
index 63b7fa61cee84..6468e674d5e78 100644
--- a/src/core/public/http/base_path.test.ts
+++ b/src/core/public/http/base_path.test.ts
@@ -88,4 +88,14 @@ describe('BasePath', () => {
       });
     });
   });
+
+  describe('serverBasePath', () => {
+    it('defaults to basePath', () => {
+      expect(new BasePath('/foo/bar').serverBasePath).toEqual('/foo/bar');
+    });
+
+    it('returns value when passed into constructor', () => {
+      expect(new BasePath('/foo/bar', '/foo').serverBasePath).toEqual('/foo');
+    });
+  });
 });
diff --git a/src/core/public/http/base_path.ts b/src/core/public/http/base_path.ts
index 6352327c41625..67464a6196b02 100644
--- a/src/core/public/http/base_path.ts
+++ b/src/core/public/http/base_path.ts
@@ -38,7 +38,10 @@
 import { modifyUrl } from '../../utils';
 
 export class BasePath {
-  constructor(private readonly basePath: string = '') {}
+  constructor(
+    private readonly basePath: string = '',
+    public readonly serverBasePath: string = basePath
+  ) {}
 
   public get = () => {
     return this.basePath;
diff --git a/src/core/public/http/http_service.ts b/src/core/public/http/http_service.ts
index 8965747ba6837..44fc9d65565d4 100644
--- a/src/core/public/http/http_service.ts
+++ b/src/core/public/http/http_service.ts
@@ -39,7 +39,10 @@ export class HttpService implements CoreService<HttpSetup, HttpStart> {
 
   public setup({ injectedMetadata, fatalErrors }: HttpDeps): HttpSetup {
     const kibanaVersion = injectedMetadata.getKibanaVersion();
-    const basePath = new BasePath(injectedMetadata.getBasePath());
+    const basePath = new BasePath(
+      injectedMetadata.getBasePath(),
+      injectedMetadata.getServerBasePath()
+    );
     const fetchService = new Fetch({ basePath, kibanaVersion });
     const loadingCount = this.loadingCount.setup({ fatalErrors });
 
diff --git a/src/core/public/http/types.ts b/src/core/public/http/types.ts
index 5909572c7e545..6370ae165282b 100644
--- a/src/core/public/http/types.ts
+++ b/src/core/public/http/types.ts
@@ -94,6 +94,13 @@ export interface IBasePath {
    * Removes the prepended basePath from the `path`.
    */
   remove: (url: string) => string;
+
+  /**
+   * Returns the server's root basePath as configured, without any namespace prefix.
+   *
+   * See {@link BasePath.get} for getting the basePath value for a specific request
+   */
+  readonly serverBasePath: string;
 }
 
 /**
diff --git a/src/core/public/injected_metadata/injected_metadata_service.mock.ts b/src/core/public/injected_metadata/injected_metadata_service.mock.ts
index 3c06f40d976db..5caa9830a643d 100644
--- a/src/core/public/injected_metadata/injected_metadata_service.mock.ts
+++ b/src/core/public/injected_metadata/injected_metadata_service.mock.ts
@@ -21,6 +21,7 @@ import { InjectedMetadataService, InjectedMetadataSetup } from './injected_metad
 const createSetupContractMock = () => {
   const setupContract: jest.Mocked<InjectedMetadataSetup> = {
     getBasePath: jest.fn(),
+    getServerBasePath: jest.fn(),
     getKibanaVersion: jest.fn(),
     getKibanaBranch: jest.fn(),
     getCspConfig: jest.fn(),
diff --git a/src/core/public/injected_metadata/injected_metadata_service.ts b/src/core/public/injected_metadata/injected_metadata_service.ts
index 64a8b8a855fb4..75abdd6d87d5a 100644
--- a/src/core/public/injected_metadata/injected_metadata_service.ts
+++ b/src/core/public/injected_metadata/injected_metadata_service.ts
@@ -54,6 +54,7 @@ export interface InjectedMetadataParams {
     buildNumber: number;
     branch: string;
     basePath: string;
+    serverBasePath: string;
     category?: AppCategory;
     csp: {
       warnLegacyBrowsers: boolean;
@@ -115,6 +116,10 @@ export class InjectedMetadataService {
         return this.state.basePath;
       },
 
+      getServerBasePath: () => {
+        return this.state.serverBasePath;
+      },
+
       getKibanaVersion: () => {
         return this.state.version;
       },
@@ -161,6 +166,7 @@ export class InjectedMetadataService {
  */
 export interface InjectedMetadataSetup {
   getBasePath: () => string;
+  getServerBasePath: () => string;
   getKibanaBuildNumber: () => number;
   getKibanaBranch: () => string;
   getKibanaVersion: () => string;
diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md
index f0289cc2b8355..ca2f6789bebee 100644
--- a/src/core/public/public.api.md
+++ b/src/core/public/public.api.md
@@ -718,6 +718,8 @@ export interface IBasePath {
     get: () => string;
     prepend: (url: string) => string;
     remove: (url: string) => string;
+    // Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "BasePath"
+    readonly serverBasePath: string;
 }
 
 // @public
diff --git a/src/core/server/rendering/__snapshots__/rendering_service.test.ts.snap b/src/core/server/rendering/__snapshots__/rendering_service.test.ts.snap
index 5e6e977663bc4..3b11313367d9c 100644
--- a/src/core/server/rendering/__snapshots__/rendering_service.test.ts.snap
+++ b/src/core/server/rendering/__snapshots__/rendering_service.test.ts.snap
@@ -65,6 +65,7 @@ Object {
     "version": Any<String>,
   },
   "legacyMode": false,
+  "serverBasePath": "/mock-server-basepath",
   "uiPlugins": Array [],
   "vars": Object {},
   "version": Any<String>,
@@ -136,6 +137,7 @@ Object {
     "version": Any<String>,
   },
   "legacyMode": false,
+  "serverBasePath": "/mock-server-basepath",
   "uiPlugins": Array [],
   "vars": Object {},
   "version": Any<String>,
@@ -211,6 +213,7 @@ Object {
     "version": Any<String>,
   },
   "legacyMode": false,
+  "serverBasePath": "/mock-server-basepath",
   "uiPlugins": Array [],
   "vars": Object {},
   "version": Any<String>,
@@ -282,6 +285,7 @@ Object {
     "version": Any<String>,
   },
   "legacyMode": false,
+  "serverBasePath": "/mock-server-basepath",
   "uiPlugins": Array [],
   "vars": Object {},
   "version": Any<String>,
@@ -353,6 +357,7 @@ Object {
     "version": Any<String>,
   },
   "legacyMode": false,
+  "serverBasePath": "/mock-server-basepath",
   "uiPlugins": Array [],
   "vars": Object {},
   "version": Any<String>,
@@ -424,6 +429,7 @@ Object {
     "version": Any<String>,
   },
   "legacyMode": true,
+  "serverBasePath": "/mock-server-basepath",
   "uiPlugins": Array [],
   "vars": Object {},
   "version": Any<String>,
@@ -495,6 +501,7 @@ Object {
     "version": Any<String>,
   },
   "legacyMode": true,
+  "serverBasePath": "/mock-server-basepath",
   "uiPlugins": Array [],
   "vars": Object {},
   "version": Any<String>,
@@ -566,6 +573,7 @@ Object {
     "version": Any<String>,
   },
   "legacyMode": true,
+  "serverBasePath": "/mock-server-basepath",
   "uiPlugins": Array [],
   "vars": Object {
     "fake": "__TEST_TOKEN__",
@@ -639,6 +647,7 @@ Object {
     "version": Any<String>,
   },
   "legacyMode": true,
+  "serverBasePath": "/mock-server-basepath",
   "uiPlugins": Array [],
   "vars": Object {},
   "version": Any<String>,
@@ -710,6 +719,7 @@ Object {
     "version": Any<String>,
   },
   "legacyMode": true,
+  "serverBasePath": "/mock-server-basepath",
   "uiPlugins": Array [],
   "vars": Object {
     "fake": "__TEST_TOKEN__",
diff --git a/src/core/server/rendering/rendering_service.tsx b/src/core/server/rendering/rendering_service.tsx
index 11d1fb271c81d..dbafd5806bd74 100644
--- a/src/core/server/rendering/rendering_service.tsx
+++ b/src/core/server/rendering/rendering_service.tsx
@@ -60,6 +60,7 @@ export class RenderingService implements CoreService<RenderingServiceSetup> {
       ) => {
         const { env } = this.coreContext;
         const basePath = http.basePath.get(request);
+        const serverBasePath = http.basePath.serverBasePath;
         const settings = {
           defaults: uiSettings.getRegistered(),
           user: includeUserSettings ? await uiSettings.getUserProvided() : {},
@@ -79,6 +80,7 @@ export class RenderingService implements CoreService<RenderingServiceSetup> {
             buildNumber: env.packageInfo.buildNum,
             branch: env.packageInfo.branch,
             basePath,
+            serverBasePath,
             env,
             legacyMode: appId !== 'core',
             i18n: {
diff --git a/src/core/server/rendering/types.ts b/src/core/server/rendering/types.ts
index 3f9f6ff294909..cfaa23d491139 100644
--- a/src/core/server/rendering/types.ts
+++ b/src/core/server/rendering/types.ts
@@ -39,6 +39,7 @@ export interface RenderingMetadata {
     buildNumber: number;
     branch: string;
     basePath: string;
+    serverBasePath: string;
     env: Env;
     legacyMode: boolean;
     i18n: {
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/__snapshots__/dashboard_empty_screen.test.tsx.snap b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/__snapshots__/dashboard_empty_screen.test.tsx.snap
index f7fc3b0891fef..c9f56dc898381 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/__snapshots__/dashboard_empty_screen.test.tsx.snap
+++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/__snapshots__/dashboard_empty_screen.test.tsx.snap
@@ -14,6 +14,7 @@ exports[`DashboardEmptyScreen renders correctly with readonly mode 1`] = `
         "get": [Function],
         "prepend": [Function],
         "remove": [Function],
+        "serverBasePath": "",
       },
       "delete": [MockFunction],
       "fetch": [MockFunction],
@@ -376,6 +377,7 @@ exports[`DashboardEmptyScreen renders correctly with visualize paragraph 1`] = `
         "get": [Function],
         "prepend": [Function],
         "remove": [Function],
+        "serverBasePath": "",
       },
       "delete": [MockFunction],
       "fetch": [MockFunction],
@@ -735,6 +737,7 @@ exports[`DashboardEmptyScreen renders correctly without visualize paragraph 1`]
         "get": [Function],
         "prepend": [Function],
         "remove": [Function],
+        "serverBasePath": "",
       },
       "delete": [MockFunction],
       "fetch": [MockFunction],
diff --git a/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap b/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap
index c3cb36b7743b4..b411d27a2a965 100644
--- a/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap
+++ b/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap
@@ -346,6 +346,7 @@ exports[`QueryStringInput Should disable autoFocus on EuiFieldText when disableA
                 "get": [Function],
                 "prepend": [Function],
                 "remove": [Function],
+                "serverBasePath": "",
               },
               "delete": [MockFunction],
               "fetch": [MockFunction],
@@ -1003,6 +1004,7 @@ exports[`QueryStringInput Should disable autoFocus on EuiFieldText when disableA
                         "get": [Function],
                         "prepend": [Function],
                         "remove": [Function],
+                        "serverBasePath": "",
                       },
                       "delete": [MockFunction],
                       "fetch": [MockFunction],
@@ -1642,6 +1644,7 @@ exports[`QueryStringInput Should pass the query language to the language switche
                 "get": [Function],
                 "prepend": [Function],
                 "remove": [Function],
+                "serverBasePath": "",
               },
               "delete": [MockFunction],
               "fetch": [MockFunction],
@@ -2296,6 +2299,7 @@ exports[`QueryStringInput Should pass the query language to the language switche
                         "get": [Function],
                         "prepend": [Function],
                         "remove": [Function],
+                        "serverBasePath": "",
                       },
                       "delete": [MockFunction],
                       "fetch": [MockFunction],
@@ -2935,6 +2939,7 @@ exports[`QueryStringInput Should render the given query 1`] = `
                 "get": [Function],
                 "prepend": [Function],
                 "remove": [Function],
+                "serverBasePath": "",
               },
               "delete": [MockFunction],
               "fetch": [MockFunction],
@@ -3589,6 +3594,7 @@ exports[`QueryStringInput Should render the given query 1`] = `
                         "get": [Function],
                         "prepend": [Function],
                         "remove": [Function],
+                        "serverBasePath": "",
                       },
                       "delete": [MockFunction],
                       "fetch": [MockFunction],
diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.test.tsx b/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.test.tsx
index 05427960d42b5..11e65f14e2708 100644
--- a/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.test.tsx
+++ b/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.test.tsx
@@ -43,7 +43,7 @@ describe('apiKeysManagementApp', () => {
     expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: '#/some-base-path', text: 'API Keys' }]);
     expect(container).toMatchInlineSnapshot(`
       <div>
-        Page: {"notifications":{"toasts":{}},"docLinks":{"esDocBasePath":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/"},"apiKeysAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}}}
+        Page: {"notifications":{"toasts":{}},"docLinks":{"esDocBasePath":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/"},"apiKeysAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}}}}
       </div>
     `);
 
diff --git a/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.test.tsx b/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.test.tsx
index 86f54bca88dbd..9c41d6624065e 100644
--- a/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.test.tsx
+++ b/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.test.tsx
@@ -53,7 +53,7 @@ describe('roleMappingsManagementApp', () => {
     expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: `#${basePath}`, text: 'Role Mappings' }]);
     expect(container).toMatchInlineSnapshot(`
       <div>
-        Role Mappings Page: {"notifications":{"toasts":{}},"roleMappingsAPI":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"docLinks":{"esDocBasePath":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/"}}
+        Role Mappings Page: {"notifications":{"toasts":{}},"roleMappingsAPI":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}}},"docLinks":{"esDocBasePath":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/"}}
       </div>
     `);
 
@@ -75,7 +75,7 @@ describe('roleMappingsManagementApp', () => {
     ]);
     expect(container).toMatchInlineSnapshot(`
       <div>
-        Role Mapping Edit Page: {"roleMappingsAPI":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"notifications":{"toasts":{}},"docLinks":{"esDocBasePath":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/"}}
+        Role Mapping Edit Page: {"roleMappingsAPI":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}}},"notifications":{"toasts":{}},"docLinks":{"esDocBasePath":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/"}}
       </div>
     `);
 
@@ -98,7 +98,7 @@ describe('roleMappingsManagementApp', () => {
     ]);
     expect(container).toMatchInlineSnapshot(`
       <div>
-        Role Mapping Edit Page: {"name":"someRoleMappingName","roleMappingsAPI":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"notifications":{"toasts":{}},"docLinks":{"esDocBasePath":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/"}}
+        Role Mapping Edit Page: {"name":"someRoleMappingName","roleMappingsAPI":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}}},"notifications":{"toasts":{}},"docLinks":{"esDocBasePath":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/"}}
       </div>
     `);
 
diff --git a/x-pack/plugins/security/public/management/roles/roles_management_app.test.tsx b/x-pack/plugins/security/public/management/roles/roles_management_app.test.tsx
index 48bc1a6580a93..5936409eb6e8b 100644
--- a/x-pack/plugins/security/public/management/roles/roles_management_app.test.tsx
+++ b/x-pack/plugins/security/public/management/roles/roles_management_app.test.tsx
@@ -64,7 +64,7 @@ describe('rolesManagementApp', () => {
     expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: `#${basePath}`, text: 'Roles' }]);
     expect(container).toMatchInlineSnapshot(`
       <div>
-        Roles Page: {"notifications":{"toasts":{}},"rolesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}}}
+        Roles Page: {"notifications":{"toasts":{}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}}}}
       </div>
     `);
 
@@ -86,7 +86,7 @@ describe('rolesManagementApp', () => {
     ]);
     expect(container).toMatchInlineSnapshot(`
       <div>
-        Role Edit Page: {"action":"edit","rolesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"userAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"http":{"basePath":{"basePath":""},"anonymousPaths":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{"esDocBasePath":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/"},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}}}
+        Role Edit Page: {"action":"edit","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{"esDocBasePath":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/"},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}}}
       </div>
     `);
 
@@ -109,7 +109,7 @@ describe('rolesManagementApp', () => {
     ]);
     expect(container).toMatchInlineSnapshot(`
       <div>
-        Role Edit Page: {"action":"edit","roleName":"someRoleName","rolesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"userAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"http":{"basePath":{"basePath":""},"anonymousPaths":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{"esDocBasePath":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/"},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}}}
+        Role Edit Page: {"action":"edit","roleName":"someRoleName","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{"esDocBasePath":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/"},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}}}
       </div>
     `);
 
@@ -132,7 +132,7 @@ describe('rolesManagementApp', () => {
     ]);
     expect(container).toMatchInlineSnapshot(`
       <div>
-        Role Edit Page: {"action":"clone","roleName":"someRoleName","rolesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"userAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"http":{"basePath":{"basePath":""},"anonymousPaths":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{"esDocBasePath":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/"},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}}}
+        Role Edit Page: {"action":"clone","roleName":"someRoleName","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{"esDocBasePath":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/"},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}}}
       </div>
     `);
 
diff --git a/x-pack/plugins/security/public/management/users/users_management_app.test.tsx b/x-pack/plugins/security/public/management/users/users_management_app.test.tsx
index 48ffcfc550a84..fd81756f176f7 100644
--- a/x-pack/plugins/security/public/management/users/users_management_app.test.tsx
+++ b/x-pack/plugins/security/public/management/users/users_management_app.test.tsx
@@ -58,7 +58,7 @@ describe('usersManagementApp', () => {
     expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: `#${basePath}`, text: 'Users' }]);
     expect(container).toMatchInlineSnapshot(`
       <div>
-        Users Page: {"notifications":{"toasts":{}},"apiClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}}}
+        Users Page: {"notifications":{"toasts":{}},"apiClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}}}}
       </div>
     `);
 
@@ -80,7 +80,7 @@ describe('usersManagementApp', () => {
     ]);
     expect(container).toMatchInlineSnapshot(`
       <div>
-        User Edit Page: {"authc":{},"apiClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"notifications":{"toasts":{}}}
+        User Edit Page: {"authc":{},"apiClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}}},"notifications":{"toasts":{}}}
       </div>
     `);
 
@@ -103,7 +103,7 @@ describe('usersManagementApp', () => {
     ]);
     expect(container).toMatchInlineSnapshot(`
       <div>
-        User Edit Page: {"authc":{},"apiClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":""},"anonymousPaths":{}}},"notifications":{"toasts":{}},"username":"someUserName"}
+        User Edit Page: {"authc":{},"apiClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}}},"notifications":{"toasts":{}},"username":"someUserName"}
       </div>
     `);
 
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 b19ef995283da..2e274e08ee13b 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
@@ -81,7 +81,7 @@ describe('spacesManagementApp', () => {
     expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: `#${basePath}`, text: 'Spaces' }]);
     expect(container).toMatchInlineSnapshot(`
       <div>
-        Spaces Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"http":{"basePath":{"basePath":""},"anonymousPaths":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{"_isScalar":false}},"securityEnabled":true}
+        Spaces Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{"_isScalar":false}},"securityEnabled":true}
       </div>
     `);
 
@@ -103,7 +103,7 @@ describe('spacesManagementApp', () => {
     ]);
     expect(container).toMatchInlineSnapshot(`
       <div>
-        Spaces Edit Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"http":{"basePath":{"basePath":""},"anonymousPaths":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{"_isScalar":false}},"securityEnabled":true}
+        Spaces Edit Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{"_isScalar":false}},"securityEnabled":true}
       </div>
     `);
 
@@ -126,7 +126,7 @@ describe('spacesManagementApp', () => {
     ]);
     expect(container).toMatchInlineSnapshot(`
       <div>
-        Spaces Edit Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"http":{"basePath":{"basePath":""},"anonymousPaths":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{"_isScalar":false}},"spaceId":"some-space","securityEnabled":true}
+        Spaces Edit Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{"_isScalar":false}},"spaceId":"some-space","securityEnabled":true}
       </div>
     `);
 

From 2121b8c1b29af168273f6d7bd7aae07fd9f80caa Mon Sep 17 00:00:00 2001
From: Ryland Herrick <ryalnd@gmail.com>
Date: Thu, 20 Feb 2020 20:08:55 -0600
Subject: [PATCH 115/174] Prevent core savedObjects plugin from being
 overridden (#58193)

PR #57452 added an empty savedObjects plugin with the same name as the
core plugin. Due to the way we were spreading into our context coupled
with the fact that we don't get NP's whitelisting of plugins on legacy,
we were overriding the core plugin here.

If this happens again, we should perhaps whitelist our plugins here.
---
 x-pack/legacy/plugins/siem/public/app/app.tsx | 1 +
 1 file changed, 1 insertion(+)

diff --git a/x-pack/legacy/plugins/siem/public/app/app.tsx b/x-pack/legacy/plugins/siem/public/app/app.tsx
index 030cb72750649..7413aeab549db 100644
--- a/x-pack/legacy/plugins/siem/public/app/app.tsx
+++ b/x-pack/legacy/plugins/siem/public/app/app.tsx
@@ -106,6 +106,7 @@ const SiemAppComponent: React.FC<SiemAppComponentProps> = ({ core, plugins }) =>
       storage: new Storage(localStorage),
       ...core,
       ...plugins,
+      savedObjects: core.savedObjects,
     }}
   >
     <StartApp {...compose(core)} />

From cdda82ec1444decb12faace0841d112ba41cc8f7 Mon Sep 17 00:00:00 2001
From: Vadim Dalecky <streamich@users.noreply.github.com>
Date: Fri, 21 Feb 2020 07:47:58 +0100
Subject: [PATCH 116/174] =?UTF-8?q?fix:=20=F0=9F=90=9B=20make=20dev=20serv?=
 =?UTF-8?q?er=20Storybook=20builds=20work=20again=20(#58188)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/kbn-storybook/index.js | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/packages/kbn-storybook/index.js b/packages/kbn-storybook/index.js
index 353a273881343..b595de8ea1c07 100644
--- a/packages/kbn-storybook/index.js
+++ b/packages/kbn-storybook/index.js
@@ -66,12 +66,17 @@ exports.runStorybookCli = config => {
           // storybook never completes, so neither will this promise
           const configDir = join(__dirname, 'storybook_config');
           log.debug('Config dir:', configDir);
-          await storybook({
+
+          const config = {
             mode: flags.site ? 'static' : 'dev',
             port: 9001,
             configDir,
-            outputDir: flags.site ? join(ASSET_DIR, name) : undefined,
-          });
+          };
+          if (flags.site) {
+            config.outputDir = join(ASSET_DIR, name);
+          }
+
+          await storybook(config);
 
           // Line is only reached when building the static version
           if (flags.site) process.exit();

From 61e35eab81e80e5290691685d3f127249700fc90 Mon Sep 17 00:00:00 2001
From: MadameSheema <snootchie.boochies@gmail.com>
Date: Fri, 21 Feb 2020 09:10:34 +0100
Subject: [PATCH 117/174] [SIEM] Cleans Cypress tests code (#58134)

* deletes 'events viewer' lib

* deletes 'fields browser' lib

* deletes 'inspect' lib

* deletes 'ml conditional links' lib

* deletes 'navigate' lib

* delete 'overview' lib

* deletes 'pagination' lib

* deletes 'timeline' lib

* deletes 'url state' lib

* deletes the rest of lib folders

* reorders tasks and screens

* reorders 'integration' folder

* orders integration files

* extracts defeault timeout to cypress configuration

* orders screens files

* cleans tasks files

* fixes type check
---
 x-pack/legacy/plugins/siem/cypress.json       |   1 +
 .../events_viewer => }/events_viewer.spec.ts  |  48 +++---
 .../fields_browser.spec.ts                    |  49 +++---
 .../{smoke_tests/inspect => }/inspect.spec.ts |  24 +--
 .../integration/lib/drag_n_drop/helpers.ts    |  50 ------
 .../integration/lib/events_viewer/helpers.ts  |  31 ----
 .../lib/events_viewer/selectors.ts            |  31 ----
 .../integration/lib/fields_browser/helpers.ts |  44 ------
 .../lib/fields_browser/selectors.ts           |  40 -----
 .../integration/lib/fixtures/helpers.ts       |  19 ---
 .../cypress/integration/lib/hosts/helpers.ts  |  16 --
 .../integration/lib/hosts/selectors.ts        |  21 ---
 .../integration/lib/inspect/helpers.ts        |  29 ----
 .../integration/lib/inspect/selectors.ts      |  89 -----------
 .../cypress/integration/lib/login/helpers.ts  | 115 --------------
 .../integration/lib/login/selectors.ts        |  14 --
 .../lib/ml_conditional_links/index.ts         |  76 ---------
 .../integration/lib/navigation/selectors.ts   |  25 ---
 .../integration/lib/overview/selectors.ts     | 144 ------------------
 .../integration/lib/pagination/selectors.ts   |  14 --
 .../integration/lib/timeline/helpers.ts       |  59 -------
 .../integration/lib/timeline/selectors.ts     |  47 ------
 .../integration/lib/url_state/index.ts        |  47 ------
 .../cypress/integration/lib/urls/index.ts     |  33 ----
 .../cypress/integration/lib/util/helpers.ts   |  27 ----
 .../ml_conditional_links.spec.ts              |  46 +++---
 .../navigation => }/navigation.spec.ts        |   9 +-
 .../overview => }/overview.spec.ts            |  10 +-
 .../pagination => }/pagination.spec.ts        |  20 +--
 ...pec.ts => timeline_data_providers.spec.ts} |  31 ++--
 ...spec.ts => timeline_flyout_button.spec.ts} |  17 +--
 ...c.ts => timeline_search_or_filter.spec.ts} |  14 +-
 ...spec.ts => timeline_toggle_column.spec.ts} |  32 ++--
 .../url_state => }/url_state.spec.ts          |  65 ++++----
 .../screens/{calendar.ts => date_picker.ts}   |  22 ++-
 .../screens/{timeline => }/fields_browser.ts  |  67 ++++----
 .../siem/cypress/screens/hosts/all_hosts.ts   |   4 +-
 .../screens/{ => hosts}/authentications.ts    |   0
 .../siem/cypress/screens/hosts/events.ts      |  36 +++--
 .../cypress/screens/hosts/fields_browser.ts   |  20 ---
 .../siem/cypress/screens/hosts/main.ts        |  12 +-
 .../screens/hosts/uncommon_processes.ts       |   4 +-
 .../screens/{header.ts => siem_header.ts}     |   8 +-
 .../screens/{timeline/main.ts => timeline.ts} |  39 +++--
 .../cypress/screens/uncommon_processes.ts     |   8 -
 .../plugins/siem/cypress/tasks/common.ts      |  14 +-
 .../tasks/{calendar.ts => date_picker.ts}     |  58 ++++---
 .../tasks/{timeline => }/fields_browser.ts    |  51 +++----
 .../siem/cypress/tasks/hosts/all_hosts.ts     |  26 ++--
 .../tasks/{ => hosts}/authentications.ts      |   5 +-
 .../siem/cypress/tasks/hosts/events.ts        |  59 ++++---
 .../cypress/tasks/hosts/fields_browsers.ts    |  12 --
 .../plugins/siem/cypress/tasks/hosts/main.ts  |  19 +--
 .../tasks/{ => hosts}/uncommon_processes.ts   |   5 +-
 .../plugins/siem/cypress/tasks/inspect.ts     |  12 +-
 .../plugins/siem/cypress/tasks/login.ts       | 113 +++++++++++++-
 .../siem/cypress/tasks/network/flows.ts       |   3 +-
 .../tasks/{header.ts => siem_header.ts}       |  23 ++-
 .../plugins/siem/cypress/tasks/siem_main.ts   |   9 +-
 .../tasks/{timeline/main.ts => timeline.ts}   |  82 +++++-----
 .../plugins/siem/cypress/urls/navigation.ts   |   5 +-
 61 files changed, 562 insertions(+), 1491 deletions(-)
 rename x-pack/legacy/plugins/siem/cypress/integration/{smoke_tests/events_viewer => }/events_viewer.spec.ts (83%)
 rename x-pack/legacy/plugins/siem/cypress/integration/{smoke_tests/fields_browser => }/fields_browser.spec.ts (87%)
 rename x-pack/legacy/plugins/siem/cypress/integration/{smoke_tests/inspect => }/inspect.spec.ts (70%)
 delete mode 100644 x-pack/legacy/plugins/siem/cypress/integration/lib/drag_n_drop/helpers.ts
 delete mode 100644 x-pack/legacy/plugins/siem/cypress/integration/lib/events_viewer/helpers.ts
 delete mode 100644 x-pack/legacy/plugins/siem/cypress/integration/lib/events_viewer/selectors.ts
 delete mode 100644 x-pack/legacy/plugins/siem/cypress/integration/lib/fields_browser/helpers.ts
 delete mode 100644 x-pack/legacy/plugins/siem/cypress/integration/lib/fields_browser/selectors.ts
 delete mode 100644 x-pack/legacy/plugins/siem/cypress/integration/lib/fixtures/helpers.ts
 delete mode 100644 x-pack/legacy/plugins/siem/cypress/integration/lib/hosts/helpers.ts
 delete mode 100644 x-pack/legacy/plugins/siem/cypress/integration/lib/hosts/selectors.ts
 delete mode 100644 x-pack/legacy/plugins/siem/cypress/integration/lib/inspect/helpers.ts
 delete mode 100644 x-pack/legacy/plugins/siem/cypress/integration/lib/inspect/selectors.ts
 delete mode 100644 x-pack/legacy/plugins/siem/cypress/integration/lib/login/helpers.ts
 delete mode 100644 x-pack/legacy/plugins/siem/cypress/integration/lib/login/selectors.ts
 delete mode 100644 x-pack/legacy/plugins/siem/cypress/integration/lib/ml_conditional_links/index.ts
 delete mode 100644 x-pack/legacy/plugins/siem/cypress/integration/lib/navigation/selectors.ts
 delete mode 100644 x-pack/legacy/plugins/siem/cypress/integration/lib/overview/selectors.ts
 delete mode 100644 x-pack/legacy/plugins/siem/cypress/integration/lib/pagination/selectors.ts
 delete mode 100644 x-pack/legacy/plugins/siem/cypress/integration/lib/timeline/helpers.ts
 delete mode 100644 x-pack/legacy/plugins/siem/cypress/integration/lib/timeline/selectors.ts
 delete mode 100644 x-pack/legacy/plugins/siem/cypress/integration/lib/url_state/index.ts
 delete mode 100644 x-pack/legacy/plugins/siem/cypress/integration/lib/urls/index.ts
 delete mode 100644 x-pack/legacy/plugins/siem/cypress/integration/lib/util/helpers.ts
 rename x-pack/legacy/plugins/siem/cypress/integration/{smoke_tests/ml_conditional_links => }/ml_conditional_links.spec.ts (93%)
 rename x-pack/legacy/plugins/siem/cypress/integration/{smoke_tests/navigation => }/navigation.spec.ts (78%)
 rename x-pack/legacy/plugins/siem/cypress/integration/{smoke_tests/overview => }/overview.spec.ts (74%)
 rename x-pack/legacy/plugins/siem/cypress/integration/{smoke_tests/pagination => }/pagination.spec.ts (80%)
 rename x-pack/legacy/plugins/siem/cypress/integration/{smoke_tests/timeline/data_providers.spec.ts => timeline_data_providers.spec.ts} (81%)
 rename x-pack/legacy/plugins/siem/cypress/integration/{smoke_tests/timeline/flyout_button.spec.ts => timeline_flyout_button.spec.ts} (68%)
 rename x-pack/legacy/plugins/siem/cypress/integration/{smoke_tests/timeline/search_or_filter.spec.ts => timeline_search_or_filter.spec.ts} (58%)
 rename x-pack/legacy/plugins/siem/cypress/integration/{smoke_tests/timeline/toggle_column.spec.ts => timeline_toggle_column.spec.ts} (82%)
 rename x-pack/legacy/plugins/siem/cypress/integration/{smoke_tests/url_state => }/url_state.spec.ts (84%)
 rename x-pack/legacy/plugins/siem/cypress/screens/{calendar.ts => date_picker.ts} (99%)
 rename x-pack/legacy/plugins/siem/cypress/screens/{timeline => }/fields_browser.ts (92%)
 rename x-pack/legacy/plugins/siem/cypress/screens/{ => hosts}/authentications.ts (100%)
 delete mode 100644 x-pack/legacy/plugins/siem/cypress/screens/hosts/fields_browser.ts
 rename x-pack/legacy/plugins/siem/cypress/screens/{header.ts => siem_header.ts} (100%)
 rename x-pack/legacy/plugins/siem/cypress/screens/{timeline/main.ts => timeline.ts} (89%)
 delete mode 100644 x-pack/legacy/plugins/siem/cypress/screens/uncommon_processes.ts
 rename x-pack/legacy/plugins/siem/cypress/tasks/{calendar.ts => date_picker.ts} (70%)
 rename x-pack/legacy/plugins/siem/cypress/tasks/{timeline => }/fields_browser.ts (81%)
 rename x-pack/legacy/plugins/siem/cypress/tasks/{ => hosts}/authentications.ts (60%)
 delete mode 100644 x-pack/legacy/plugins/siem/cypress/tasks/hosts/fields_browsers.ts
 rename x-pack/legacy/plugins/siem/cypress/tasks/{ => hosts}/uncommon_processes.ts (59%)
 rename x-pack/legacy/plugins/siem/cypress/tasks/{header.ts => siem_header.ts} (68%)
 rename x-pack/legacy/plugins/siem/cypress/tasks/{timeline/main.ts => timeline.ts} (79%)

diff --git a/x-pack/legacy/plugins/siem/cypress.json b/x-pack/legacy/plugins/siem/cypress.json
index ae73965f8f10f..d2397e1ec90dd 100644
--- a/x-pack/legacy/plugins/siem/cypress.json
+++ b/x-pack/legacy/plugins/siem/cypress.json
@@ -1,5 +1,6 @@
 {
   "baseUrl": "http://localhost:5601",
+  "defaultCommandTimeout": 30000,
   "screenshotsFolder": "../../../../target/kibana-siem/cypress/screenshots",
   "trashAssetsBeforeRuns": false,
   "video": false,
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/events_viewer/events_viewer.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/events_viewer.spec.ts
similarity index 83%
rename from x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/events_viewer/events_viewer.spec.ts
rename to x-pack/legacy/plugins/siem/cypress/integration/events_viewer.spec.ts
index 7bb7b9f4da5d1..e44c8f4459ba9 100644
--- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/events_viewer/events_viewer.spec.ts
+++ b/x-pack/legacy/plugins/siem/cypress/integration/events_viewer.spec.ts
@@ -4,38 +4,36 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { filterFieldsBrowser } from '../../lib/fields_browser/helpers';
 import {
+  FIELDS_BROWSER_CHECKBOX,
   FIELDS_BROWSER_CONTAINER,
   FIELDS_BROWSER_SELECTED_CATEGORY_TITLE,
   FIELDS_BROWSER_TITLE,
-  FIELDS_BROWSER_CHECKBOX,
-} from '../../../screens/hosts/fields_browser';
-import { HOSTS_PAGE } from '../../lib/urls';
-import { loginAndWaitForPage } from '../../../tasks/login';
-import { openEventsViewerFieldsBrowser, filterSearchBar } from '../../lib/events_viewer/helpers';
-import { closeFieldsBrowser } from '../../../tasks/hosts/fields_browsers';
-import { openEvents } from '../../../tasks/hosts/main';
-import {
-  closeModal,
-  opensInspectQueryModal,
-  waitsForEventsToBeLoaded,
-  addsHostGeoCityNameToHeader,
-  addsHostGeoCountryNameToHeader,
-  resetFields,
-} from '../../../tasks/hosts/events';
-
+} from '../screens/fields_browser';
 import {
   HEADER_SUBTITLE,
+  HOST_GEO_CITY_NAME_HEADER,
+  HOST_GEO_COUNTRY_NAME_HEADER,
   INSPECT_MODAL,
   LOAD_MORE,
   LOCAL_EVENTS_COUNT,
-  HOST_GEO_CITY_NAME_HEADER,
-  HOST_GEO_COUNTRY_NAME_HEADER,
-} from '../../../screens/hosts/events';
-import { DEFAULT_TIMEOUT } from '../../lib/util/helpers';
+} from '../screens/hosts/events';
+
+import { closeFieldsBrowser, filterFieldsBrowser } from '../tasks/fields_browser';
+import { loginAndWaitForPage } from '../tasks/login';
+import { openEvents } from '../tasks/hosts/main';
+import {
+  addsHostGeoCityNameToHeader,
+  addsHostGeoCountryNameToHeader,
+  closeModal,
+  openEventsViewerFieldsBrowser,
+  opensInspectQueryModal,
+  resetFields,
+  waitsForEventsToBeLoaded,
+} from '../tasks/hosts/events';
+import { clearSearchBar, kqlSearch } from '../tasks/siem_header';
 
-import { clearSearchBar } from '../../../tasks/header';
+import { HOSTS_PAGE } from '../urls/navigation';
 
 const defaultHeadersInDefaultEcsCategory = [
   { id: '@timestamp' },
@@ -90,13 +88,13 @@ describe('Events Viewer', () => {
 
     after(() => {
       closeModal();
-      cy.get(INSPECT_MODAL, { timeout: DEFAULT_TIMEOUT }).should('not.exist');
+      cy.get(INSPECT_MODAL).should('not.exist');
     });
 
     it('launches the inspect query modal when the inspect button is clicked', () => {
       waitsForEventsToBeLoaded();
       opensInspectQueryModal();
-      cy.get(INSPECT_MODAL, { timeout: DEFAULT_TIMEOUT }).should('exist');
+      cy.get(INSPECT_MODAL).should('exist');
     });
   });
 
@@ -147,7 +145,7 @@ describe('Events Viewer', () => {
       cy.get(HEADER_SUBTITLE)
         .invoke('text')
         .then(initialNumberOfEvents => {
-          filterSearchBar(filterInput);
+          kqlSearch(`${filterInput}{enter}`);
           cy.get(HEADER_SUBTITLE)
             .invoke('text')
             .should('not.equal', initialNumberOfEvents);
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/fields_browser/fields_browser.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/fields_browser.spec.ts
similarity index 87%
rename from x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/fields_browser/fields_browser.spec.ts
rename to x-pack/legacy/plugins/siem/cypress/integration/fields_browser.spec.ts
index 6e8ef93a54016..095fc30356fd4 100644
--- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/fields_browser/fields_browser.spec.ts
+++ b/x-pack/legacy/plugins/siem/cypress/integration/fields_browser.spec.ts
@@ -4,38 +4,35 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { HOSTS_PAGE } from '../../lib/urls';
-
-import { loginAndWaitForPage, DEFAULT_TIMEOUT } from '../../../tasks/login';
-
 import {
-  FIELDS_BROWSER_TITLE,
-  FIELDS_BROWSER_SELECTED_CATEGORY_TITLE,
-  FIELDS_BROWSER_SELECTED_CATEGORY_COUNT,
   FIELDS_BROWSER_CATEGORIES_COUNT,
-  FIELDS_BROWSER_HOST_CATEGORIES_COUNT,
-  FIELDS_BROWSER_SYSTEM_CATEGORIES_COUNT,
   FIELDS_BROWSER_FIELDS_COUNT,
-  FIELDS_BROWSER_MESSAGE_HEADER,
+  FIELDS_BROWSER_HOST_CATEGORIES_COUNT,
   FIELDS_BROWSER_HOST_GEO_CITY_NAME_HEADER,
   FIELDS_BROWSER_HOST_GEO_COUNTRY_NAME_HEADER,
   FIELDS_BROWSER_HEADER_HOST_GEO_CONTINENT_NAME_HEADER,
-} from '../../../screens/timeline/fields_browser';
-
-import { populateTimeline, openTimelineFieldsBrowser } from '../../../tasks/timeline/main';
-
-import { openTimeline } from '../../../tasks/siem_main';
+  FIELDS_BROWSER_MESSAGE_HEADER,
+  FIELDS_BROWSER_SELECTED_CATEGORY_TITLE,
+  FIELDS_BROWSER_SELECTED_CATEGORY_COUNT,
+  FIELDS_BROWSER_SYSTEM_CATEGORIES_COUNT,
+  FIELDS_BROWSER_TITLE,
+} from '../screens/fields_browser';
 
 import {
+  addsHostGeoCityNameToTimeline,
+  addsHostGeoContinentNameToTimeline,
+  addsHostGeoCountryNameToTimelineDraggingIt,
   clearFieldsBrowser,
-  filterFieldsBrowser,
   closeFieldsBrowser,
+  filterFieldsBrowser,
   removesMessageField,
-  addsHostGeoCityNameToTimeline,
-  addsHostGeoCountryNameToTimelineDraggingIt,
-  addsHostGeoContinentNameToTimeline,
   resetFields,
-} from '../../../tasks/timeline/fields_browser';
+} from '../tasks/fields_browser';
+import { loginAndWaitForPage } from '../tasks/login';
+import { openTimeline } from '../tasks/siem_main';
+import { openTimelineFieldsBrowser, populateTimeline } from '../tasks/timeline';
+
+import { HOSTS_PAGE } from '../urls/navigation';
 
 const defaultHeaders = [
   { id: '@timestamp' },
@@ -90,7 +87,7 @@ describe('Fields Browser', () => {
 
       filterFieldsBrowser(filterInput);
 
-      cy.get(FIELDS_BROWSER_CATEGORIES_COUNT, { timeout: DEFAULT_TIMEOUT })
+      cy.get(FIELDS_BROWSER_CATEGORIES_COUNT)
         .invoke('text')
         .should('eq', '2 categories');
     });
@@ -106,7 +103,7 @@ describe('Fields Browser', () => {
           cy.get(FIELDS_BROWSER_SYSTEM_CATEGORIES_COUNT)
             .invoke('text')
             .then(systemCategoriesCount => {
-              cy.get(FIELDS_BROWSER_FIELDS_COUNT, { timeout: DEFAULT_TIMEOUT })
+              cy.get(FIELDS_BROWSER_FIELDS_COUNT)
                 .invoke('text')
                 .should('eq', `${+hostCategoriesCount + +systemCategoriesCount} fields`);
             });
@@ -163,9 +160,7 @@ describe('Fields Browser', () => {
       addsHostGeoCityNameToTimeline();
       closeFieldsBrowser();
 
-      cy.get(FIELDS_BROWSER_HOST_GEO_CITY_NAME_HEADER, {
-        timeout: DEFAULT_TIMEOUT,
-      }).should('exist');
+      cy.get(FIELDS_BROWSER_HOST_GEO_CITY_NAME_HEADER).should('exist');
     });
 
     it('adds a field to the timeline when the user drags and drops a field', () => {
@@ -177,9 +172,7 @@ describe('Fields Browser', () => {
 
       addsHostGeoCountryNameToTimelineDraggingIt();
 
-      cy.get(FIELDS_BROWSER_HOST_GEO_COUNTRY_NAME_HEADER, {
-        timeout: DEFAULT_TIMEOUT,
-      }).should('exist');
+      cy.get(FIELDS_BROWSER_HOST_GEO_COUNTRY_NAME_HEADER).should('exist');
     });
 
     it('resets all fields in the timeline when `Reset Fields` is clicked', () => {
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/inspect/inspect.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/inspect.spec.ts
similarity index 70%
rename from x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/inspect/inspect.spec.ts
rename to x-pack/legacy/plugins/siem/cypress/integration/inspect.spec.ts
index 1555470f5eee7..b6b4e7a72b8f6 100644
--- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/inspect/inspect.spec.ts
+++ b/x-pack/legacy/plugins/siem/cypress/integration/inspect.spec.ts
@@ -4,20 +4,22 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { HOSTS_PAGE, NETWORK_PAGE } from '../../lib/urls';
 import {
+  INSPECT_HOSTS_BUTTONS_IN_SIEM,
   INSPECT_MODAL,
   INSPECT_NETWORK_BUTTONS_IN_SIEM,
-  INSPECT_HOSTS_BUTTONS_IN_SIEM,
-} from '../../../screens/inspect';
+} from '../screens/inspect';
+
+import { closesModal, openStatsAndTables } from '../tasks/inspect';
+import { loginAndWaitForPage } from '../tasks/login';
+import { openTimeline } from '../tasks/siem_main';
 import {
   executeTimelineKQL,
-  openTimelineSettings,
   openTimelineInspectButton,
-} from '../../../tasks/timeline/main';
-import { openTimeline } from '../../../tasks/siem_main';
-import { DEFAULT_TIMEOUT, loginAndWaitForPage } from '../../../tasks/login';
-import { closesModal, openStatsAndTables } from '../../../tasks/inspect';
+  openTimelineSettings,
+} from '../tasks/timeline';
+
+import { HOSTS_PAGE, NETWORK_PAGE } from '../urls/navigation';
 
 describe('Inspect', () => {
   context('Hosts stats and tables', () => {
@@ -31,7 +33,7 @@ describe('Inspect', () => {
     INSPECT_HOSTS_BUTTONS_IN_SIEM.forEach(table =>
       it(`inspects the ${table.title}`, () => {
         openStatsAndTables(table);
-        cy.get(INSPECT_MODAL, { timeout: DEFAULT_TIMEOUT }).should('be.visible');
+        cy.get(INSPECT_MODAL).should('be.visible');
       })
     );
   });
@@ -47,7 +49,7 @@ describe('Inspect', () => {
     INSPECT_NETWORK_BUTTONS_IN_SIEM.forEach(table =>
       it(`inspects the ${table.title}`, () => {
         openStatsAndTables(table);
-        cy.get(INSPECT_MODAL, { timeout: DEFAULT_TIMEOUT }).should('be.visible');
+        cy.get(INSPECT_MODAL).should('be.visible');
       })
     );
   });
@@ -60,7 +62,7 @@ describe('Inspect', () => {
       executeTimelineKQL(hostExistsQuery);
       openTimelineSettings();
       openTimelineInspectButton();
-      cy.get(INSPECT_MODAL, { timeout: DEFAULT_TIMEOUT }).should('be.visible');
+      cy.get(INSPECT_MODAL).should('be.visible');
     });
   });
 });
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/drag_n_drop/helpers.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/drag_n_drop/helpers.ts
deleted file mode 100644
index 39a61401c15b3..0000000000000
--- a/x-pack/legacy/plugins/siem/cypress/integration/lib/drag_n_drop/helpers.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-const primaryButton = 0;
-
-/**
- * To overcome the React Beautiful DND sloppy click detection threshold:
- * https://github.com/atlassian/react-beautiful-dnd/blob/67b96c8d04f64af6b63ae1315f74fc02b5db032b/docs/sensors/mouse.md#sloppy-clicks-and-click-prevention-
- */
-const dndSloppyClickDetectionThreshold = 5;
-
-/** Starts dragging the subject */
-export const drag = (subject: JQuery<HTMLElement>) => {
-  const subjectLocation = subject[0].getBoundingClientRect();
-
-  cy.wrap(subject)
-    .trigger('mousedown', {
-      button: primaryButton,
-      clientX: subjectLocation.left,
-      clientY: subjectLocation.top,
-      force: true,
-    })
-    .wait(1)
-    .trigger('mousemove', {
-      button: primaryButton,
-      clientX: subjectLocation.left + dndSloppyClickDetectionThreshold,
-      clientY: subjectLocation.top,
-      force: true,
-    })
-    .wait(1);
-};
-
-/** "Drops" the subject being dragged on the specified drop target  */
-export const drop = (dropTarget: JQuery<HTMLElement>) => {
-  cy.wrap(dropTarget)
-    .trigger('mousemove', { button: primaryButton, force: true })
-    .wait(1)
-    .trigger('mouseup', { force: true })
-    .wait(1);
-};
-
-/** Drags the subject being dragged on the specified drop target, but does not drop it  */
-export const dragWithoutDrop = (dropTarget: JQuery<HTMLElement>) => {
-  cy.wrap(dropTarget).trigger('mousemove', 'center', {
-    button: primaryButton,
-  });
-};
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/events_viewer/helpers.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/events_viewer/helpers.ts
deleted file mode 100644
index 02a80f6c0329c..0000000000000
--- a/x-pack/legacy/plugins/siem/cypress/integration/lib/events_viewer/helpers.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { EVENTS_VIEWER_FIELDS_BUTTON, KQL_SEARCH_BAR } from './selectors';
-import { FIELDS_BROWSER_CONTAINER } from '../fields_browser/selectors';
-import { SERVER_SIDE_EVENT_COUNT } from '../timeline/selectors';
-import { DEFAULT_TIMEOUT } from '../util/helpers';
-
-/** Opens the eventsViewer Field Browser */
-export const openEventsViewerFieldsBrowser = () => {
-  cy.get(EVENTS_VIEWER_FIELDS_BUTTON, { timeout: DEFAULT_TIMEOUT }).click({ force: true });
-
-  cy.get(SERVER_SIDE_EVENT_COUNT, { timeout: DEFAULT_TIMEOUT })
-    .invoke('text')
-    .should('not.equal', '0');
-
-  cy.get(FIELDS_BROWSER_CONTAINER).should('exist');
-};
-
-/** Clicks an arbitrary UI element that's not part of the fields browser (to dismiss it) */
-export const clickOutsideFieldsBrowser = () => {
-  cy.get(KQL_SEARCH_BAR, { timeout: DEFAULT_TIMEOUT }).click();
-};
-
-/** Filters the search bar at the top of most pages with the specified KQL */
-export const filterSearchBar = (kql: string) => {
-  cy.get(KQL_SEARCH_BAR).type(`${kql} {enter}`);
-};
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/events_viewer/selectors.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/events_viewer/selectors.ts
deleted file mode 100644
index 6f7906d7fd791..0000000000000
--- a/x-pack/legacy/plugins/siem/cypress/integration/lib/events_viewer/selectors.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-/** The panel containing the events viewer */
-export const EVENTS_VIEWER_PANEL = '[data-test-subj="events-viewer-panel"]';
-
-/** Clicking this button in the timeline opens the Fields browser in the Events Viewer */
-export const EVENTS_VIEWER_FIELDS_BUTTON = `${EVENTS_VIEWER_PANEL} [data-test-subj="show-field-browser-gear"]`;
-
-/** The KQL search bar that exists at the top of most pages */
-export const KQL_SEARCH_BAR = '[data-test-subj="queryInput"]';
-
-/** The Events Viewer Showing N events header subtitle */
-export const HEADER_SUBTITLE = `${EVENTS_VIEWER_PANEL} [data-test-subj="header-panel-subtitle"]`;
-
-/** The inspect query modal */
-export const INSPECT_MODAL = '[data-test-subj="modal-inspect-euiModal"]';
-
-export const CLOSE_MODAL = '[data-test-subj="modal-inspect-close"]';
-
-/** The inspect query button that launches the inspect query modal */
-export const INSPECT_QUERY = `${EVENTS_VIEWER_PANEL} [data-test-subj="inspect-icon-button"]`;
-
-/** A count of the events loaded in the table */
-export const LOCAL_EVENTS_COUNT = `${EVENTS_VIEWER_PANEL} [data-test-subj="local-events-count"]`;
-
-/** The events viewer Load More button */
-export const LOAD_MORE = `${EVENTS_VIEWER_PANEL} [data-test-subj="TimelineMoreButton"]`;
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/fields_browser/helpers.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/fields_browser/helpers.ts
deleted file mode 100644
index b02eda513cba3..0000000000000
--- a/x-pack/legacy/plugins/siem/cypress/integration/lib/fields_browser/helpers.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { FIELDS_BROWSER_CONTAINER, FIELDS_BROWSER_FILTER_INPUT } from './selectors';
-import {
-  assertAtLeastOneEventMatchesSearch,
-  executeKQL,
-  hostExistsQuery,
-  toggleTimelineVisibility,
-} from '../timeline/helpers';
-import { TIMELINE_DATA_PROVIDERS, TIMELINE_FIELDS_BUTTON } from '../timeline/selectors';
-
-/** Opens the timeline's Field Browser */
-export const openTimelineFieldsBrowser = () => {
-  cy.get(TIMELINE_FIELDS_BUTTON).click({ force: true });
-
-  cy.get(FIELDS_BROWSER_CONTAINER).should('exist');
-};
-
-/** Populates the timeline with a host from the hosts page */
-export const populateTimeline = () => {
-  toggleTimelineVisibility();
-
-  executeKQL(hostExistsQuery);
-
-  assertAtLeastOneEventMatchesSearch();
-};
-
-/** Clicks an arbitrary UI element that's not part of the fields browser (to dismiss it) */
-export const clickOutsideFieldsBrowser = () => {
-  cy.get(TIMELINE_DATA_PROVIDERS).click();
-};
-
-/** Filters the Field Browser by typing `fieldName` in the input */
-export const filterFieldsBrowser = (fieldName: string) => {
-  cy.get(FIELDS_BROWSER_FILTER_INPUT).type(fieldName);
-};
-
-export const clearFieldsBrowser = () => {
-  cy.get(FIELDS_BROWSER_FILTER_INPUT).type('{selectall}{backspace}');
-};
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/fields_browser/selectors.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/fields_browser/selectors.ts
deleted file mode 100644
index 039e38aaf3ee7..0000000000000
--- a/x-pack/legacy/plugins/siem/cypress/integration/lib/fields_browser/selectors.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-/** Clicking this button in the timeline opens the Fields browser */
-export const TIMELINE_FIELDS_BUTTON =
-  '[data-test-subj="timeline"] [data-test-subj="show-field-browser"]';
-
-/** The title displayed in the fields browser (i.e. Customize Columns) */
-export const FIELDS_BROWSER_TITLE = '[data-test-subj="field-browser-title"]';
-
-/** Contains the body of the fields browser */
-export const FIELDS_BROWSER_CONTAINER = '[data-test-subj="fields-browser-container"]';
-
-/** The title of the selected category in the right-hand side of the fields browser */
-export const FIELDS_BROWSER_SELECTED_CATEGORY_TITLE = '[data-test-subj="selected-category-title"]';
-
-/** A count of the fields in the selected category in the right-hand side of the fields browser */
-export const FIELDS_BROWSER_SELECTED_CATEGORY_COUNT =
-  '[data-test-subj="selected-category-count-badge"]';
-
-/** Typing in this input filters the Field Browser */
-export const FIELDS_BROWSER_FILTER_INPUT = '[data-test-subj="field-search"]';
-
-/**
- * This label displays a count of the categories containing (one or more)
- * fields that match the filter criteria
- */
-export const FIELDS_BROWSER_CATEGORIES_COUNT = '[data-test-subj="categories-count"]';
-
-export const FIELDS_BROWSER_HOST_CATEGORIES_COUNT = '[data-test-subj="host-category-count"]';
-
-export const FIELDS_BROWSER_SYSTEM_CATEGORIES_COUNT = '[data-test-subj="system-category-count"]';
-
-/**
- * This label displays a count of the fields that match the filter criteria
- */
-export const FIELDS_BROWSER_FIELDS_COUNT = '[data-test-subj="fields-count"]';
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/fixtures/helpers.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/fixtures/helpers.ts
deleted file mode 100644
index e66199e0419ec..0000000000000
--- a/x-pack/legacy/plugins/siem/cypress/integration/lib/fixtures/helpers.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-// Cypress workaround to hijack XHR
-// https://github.com/cypress-io/cypress/issues/687
-export const clearFetch = () =>
-  cy.on('window:before:load', win => {
-    // @ts-ignore no null, this is a temp hack see issue above
-    win.fetch = null;
-  });
-
-export const stubApi = (dataFileName: string) => {
-  cy.server();
-  cy.fixture(dataFileName).as(`${dataFileName}JSON`);
-  cy.route('POST', 'api/siem/graphql', `@${dataFileName}JSON`);
-};
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/hosts/helpers.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/hosts/helpers.ts
deleted file mode 100644
index 8f4efbe0c09c2..0000000000000
--- a/x-pack/legacy/plugins/siem/cypress/integration/lib/hosts/helpers.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { DEFAULT_TIMEOUT } from '../util/helpers';
-
-import { ALL_HOSTS_WIDGET, EVENTS_TAB_BUTTON } from './selectors';
-
-/** Wait for the for the `All Hosts` widget on the `Hosts` page to load */
-export const waitForAllHostsWidget = () => cy.get(ALL_HOSTS_WIDGET, { timeout: DEFAULT_TIMEOUT });
-
-/** Clicks the Events tab on the hosts page */
-export const clickEventsTab = () =>
-  cy.get(EVENTS_TAB_BUTTON, { timeout: DEFAULT_TIMEOUT }).click({ force: true });
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/hosts/selectors.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/hosts/selectors.ts
deleted file mode 100644
index ab2502676d6f7..0000000000000
--- a/x-pack/legacy/plugins/siem/cypress/integration/lib/hosts/selectors.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-/** The `All Hosts` widget on the `Hosts` page */
-export const ALL_HOSTS_WIDGET = '[data-test-subj="table-allHosts-loading-false"]';
-
-/** A single draggable host in the `All Hosts` widget on the `Hosts` page */
-export const ALL_HOSTS_WIDGET_HOST = '[data-test-subj="draggable-content-host.name"]';
-
-/** All the draggable hosts in the `All Hosts` widget on the `Hosts` page */
-export const ALL_HOSTS_WIDGET_DRAGGABLE_HOSTS = `${ALL_HOSTS_WIDGET} ${ALL_HOSTS_WIDGET_HOST}`;
-
-/** Clicking this button displays the `Events` tab */
-export const EVENTS_TAB_BUTTON = '[data-test-subj="navigation-events"]';
-
-export const NAVIGATION_HOSTS_ALL_HOSTS = '[data-test-subj="navigation-allHosts"]';
-
-export const NAVIGATION_HOSTS_ANOMALIES = '[data-test-subj="navigation-anomalies"]';
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/inspect/helpers.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/inspect/helpers.ts
deleted file mode 100644
index c431d0551b29a..0000000000000
--- a/x-pack/legacy/plugins/siem/cypress/integration/lib/inspect/helpers.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { DEFAULT_TIMEOUT } from '../util/helpers';
-
-import { INSPECT_BUTTON_ICON, InspectButtonMetadata } from './selectors';
-
-export const openStatsAndTables = (table: InspectButtonMetadata) => {
-  if (table.tabId) {
-    cy.get(table.tabId).click({ force: true });
-  }
-  cy.get(table.id, { timeout: DEFAULT_TIMEOUT });
-  if (table.altInspectId) {
-    cy.get(table.altInspectId, { timeout: DEFAULT_TIMEOUT }).trigger('click', {
-      force: true,
-    });
-  } else {
-    cy.get(`${table.id} ${INSPECT_BUTTON_ICON}`, {
-      timeout: DEFAULT_TIMEOUT,
-    }).trigger('click', { force: true });
-  }
-};
-
-export const closesModal = () => {
-  cy.get('[data-test-subj="modal-inspect-close"]', { timeout: DEFAULT_TIMEOUT }).click();
-};
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/inspect/selectors.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/inspect/selectors.ts
deleted file mode 100644
index a6d4b37be9f00..0000000000000
--- a/x-pack/legacy/plugins/siem/cypress/integration/lib/inspect/selectors.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-export const INSPECT_BUTTON_ICON = '[data-test-subj="inspect-icon-button"]';
-export const INSPECT_MODAL = '[data-test-subj="modal-inspect-euiModal"]';
-export const TIMELINE_SETTINGS_ICON = '[data-test-subj="settings-gear"]';
-export const TIMELINE_INSPECT_BUTTON = '[data-test-subj="inspect-empty-button"]';
-
-export interface InspectButtonMetadata {
-  altInspectId?: string;
-  id: string;
-  title: string;
-  tabId?: string;
-}
-
-export const INSPECT_HOSTS_BUTTONS_IN_SIEM: InspectButtonMetadata[] = [
-  {
-    id: '[data-test-subj="stat-hosts"]',
-    title: 'Hosts Stat',
-  },
-  {
-    id: '[data-test-subj="stat-authentication"]',
-    title: 'User Authentications Stat',
-  },
-  {
-    id: '[data-test-subj="stat-uniqueIps"]',
-    title: 'Unique IPs Stat',
-  },
-  {
-    id: '[data-test-subj="table-allHosts-loading-false"]',
-    title: 'All Hosts Table',
-    tabId: '[data-test-subj="navigation-allHosts"]',
-  },
-  {
-    id: '[data-test-subj="table-authentications-loading-false"]',
-    title: 'Authentications Table',
-    tabId: '[data-test-subj="navigation-authentications"]',
-  },
-  {
-    id: '[data-test-subj="table-uncommonProcesses-loading-false"]',
-    title: 'Uncommon processes Table',
-    tabId: '[data-test-subj="navigation-uncommonProcesses"]',
-  },
-  {
-    altInspectId: `[data-test-subj="events-viewer-panel"] ${INSPECT_BUTTON_ICON}`,
-    id: '[data-test-subj="events-container-loading-false"]',
-    title: 'Events Table',
-    tabId: '[data-test-subj="navigation-events"]',
-  },
-];
-
-export const INSPECT_NETWORK_BUTTONS_IN_SIEM: InspectButtonMetadata[] = [
-  {
-    id: '[data-test-subj="stat-networkEvents"]',
-    title: 'Network events Stat',
-  },
-  {
-    id: '[data-test-subj="stat-dnsQueries"]',
-    title: 'DNS queries Stat',
-  },
-  {
-    id: '[data-test-subj="stat-uniqueFlowId"]',
-    title: 'Unique flow IDs Stat',
-  },
-  {
-    id: '[data-test-subj="stat-tlsHandshakes"]',
-    title: 'TLS handshakes Stat',
-  },
-  {
-    id: '[data-test-subj="stat-UniqueIps"]',
-    title: 'Unique private IPs Stat',
-  },
-  {
-    id: '[data-test-subj="table-topNFlowSource-loading-false"]',
-    title: 'Source IPs Table',
-  },
-  {
-    id: '[data-test-subj="table-topNFlowDestination-loading-false"]',
-    title: 'Destination IPs Table',
-  },
-  {
-    id: '[data-test-subj="table-dns-loading-false"]',
-    title: 'Top DNS Domains Table',
-    tabId: '[data-test-subj="navigation-dns"]',
-  },
-];
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/login/helpers.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/login/helpers.ts
deleted file mode 100644
index b2b8ce7b9c000..0000000000000
--- a/x-pack/legacy/plugins/siem/cypress/integration/lib/login/helpers.ts
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import * as yaml from 'js-yaml';
-
-/**
- * Credentials in the `kibana.dev.yml` config file will be used to authenticate
- * with Kibana when credentials are not provided via environment variables
- */
-const KIBANA_DEV_YML_PATH = '../../../../config/kibana.dev.yml';
-
-/**
- * The configuration path in `kibana.dev.yml` to the username to be used when
- * authenticating with Kibana.
- */
-const ELASTICSEARCH_USERNAME_CONFIG_PATH = 'config.elasticsearch.username';
-
-/**
- * The configuration path in `kibana.dev.yml` to the password to be used when
- * authenticating with Kibana.
- */
-const ELASTICSEARCH_PASSWORD_CONFIG_PATH = 'config.elasticsearch.password';
-
-/**
- * The `CYPRESS_ELASTICSEARCH_USERNAME` environment variable specifies the
- * username to be used when authenticating with Kibana
- */
-const ELASTICSEARCH_USERNAME = 'ELASTICSEARCH_USERNAME';
-
-/**
- * The `CYPRESS_ELASTICSEARCH_PASSWORD` environment variable specifies the
- * username to be used when authenticating with Kibana
- */
-const ELASTICSEARCH_PASSWORD = 'ELASTICSEARCH_PASSWORD';
-
-/**
- * The Kibana server endpoint used for authentication
- */
-const LOGIN_API_ENDPOINT = '/internal/security/login';
-
-/**
- * Authenticates with Kibana using, if specified, credentials specified by
- * environment variables. The credentials in `kibana.dev.yml` will be used
- * for authentication when the environment variables are unset.
- *
- * To speed the execution of tests, prefer this non-interactive authentication,
- * which is faster than authentication via Kibana's interactive login page.
- */
-export const login = () => {
-  if (credentialsProvidedByEnvironment()) {
-    loginViaEnvironmentCredentials();
-  } else {
-    loginViaConfig();
-  }
-};
-
-/**
- * Returns `true` if the credentials used to login to Kibana are provided
- * via environment variables
- */
-const credentialsProvidedByEnvironment = (): boolean =>
-  Cypress.env(ELASTICSEARCH_USERNAME) != null && Cypress.env(ELASTICSEARCH_PASSWORD) != null;
-
-/**
- * Authenticates with Kibana by reading credentials from the
- * `CYPRESS_ELASTICSEARCH_USERNAME` and `CYPRESS_ELASTICSEARCH_PASSWORD`
- * environment variables, and POSTing the username and password directly to
- * Kibana's `/internal/security/login` endpoint, bypassing the login page (for speed).
- */
-const loginViaEnvironmentCredentials = () => {
-  cy.log(
-    `Authenticating via environment credentials from the \`CYPRESS_${ELASTICSEARCH_USERNAME}\` and \`CYPRESS_${ELASTICSEARCH_PASSWORD}\` environment variables`
-  );
-
-  // programmatically authenticate without interacting with the Kibana login page
-  cy.request({
-    body: {
-      username: Cypress.env(ELASTICSEARCH_USERNAME),
-      password: Cypress.env(ELASTICSEARCH_PASSWORD),
-    },
-    headers: { 'kbn-xsrf': 'cypress-creds-via-env' },
-    method: 'POST',
-    url: `${Cypress.config().baseUrl}${LOGIN_API_ENDPOINT}`,
-  });
-};
-
-/**
- * Authenticates with Kibana by reading credentials from the
- * `kibana.dev.yml` file and POSTing the username and password directly to
- * Kibana's `/internal/security/login` endpoint, bypassing the login page (for speed).
- */
-const loginViaConfig = () => {
-  cy.log(
-    `Authenticating via config credentials \`${ELASTICSEARCH_USERNAME_CONFIG_PATH}\` and \`${ELASTICSEARCH_PASSWORD_CONFIG_PATH}\` from \`${KIBANA_DEV_YML_PATH}\``
-  );
-
-  // read the login details from `kibana.dev.yaml`
-  cy.readFile(KIBANA_DEV_YML_PATH).then(kibanaDevYml => {
-    const config = yaml.safeLoad(kibanaDevYml);
-
-    // programmatically authenticate without interacting with the Kibana login page
-    cy.request({
-      body: {
-        username: config.elasticsearch.username,
-        password: config.elasticsearch.password,
-      },
-      headers: { 'kbn-xsrf': 'cypress-creds-via-config' },
-      method: 'POST',
-      url: `${Cypress.config().baseUrl}${LOGIN_API_ENDPOINT}`,
-    });
-  });
-};
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/login/selectors.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/login/selectors.ts
deleted file mode 100644
index ac028fa91216c..0000000000000
--- a/x-pack/legacy/plugins/siem/cypress/integration/lib/login/selectors.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-/** The Username field in the Kibana login page */
-export const USERNAME = '[data-test-subj="loginUsername"]';
-
-/** The Password field in the Kibana login page */
-export const PASSWORD = '[data-test-subj="loginPassword"]';
-
-/** The `Default` space button on the `Select your space` page */
-export const DEFAULT_SPACE_BUTTON = '[data-test-subj="space-card-default"]';
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/ml_conditional_links/index.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/ml_conditional_links/index.ts
deleted file mode 100644
index 655418fc98bf8..0000000000000
--- a/x-pack/legacy/plugins/siem/cypress/integration/lib/ml_conditional_links/index.ts
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-/*
- * These links are for different test scenarios that try and capture different drill downs into
- * ml-network and ml-hosts and are of the flavor of testing:
- * A filter being null: (query:!n)
- * A filter being set with single values: query=(query:%27process.name%20:%20%22conhost.exe%22%27,language:kuery)
- * A filter being set with multiple values:  query=(query:%27process.name%20:%20%22conhost.exe,sc.exe%22%27,language:kuery)
- * A filter containing variables not replaced:  query=(query:%27process.name%20:%20%$process.name$%22%27,language:kuery)
- *
- * In different combination with:
- * network not being set: $ip$
- * host not being set: $host.name$
- * ...or...
- * network being set normally: 127.0.0.1
- * host being set normally: suricata-iowa
- * ...or...
- * network having multiple values: 127.0.0.1,127.0.0.2
- * host having multiple values: suricata-iowa,siem-windows
- */
-
-// Single IP with a null for the Query:
-export const mlNetworkSingleIpNullKqlQuery =
-  "/app/siem#/ml-network/ip/127.0.0.1?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))";
-
-// Single IP with a value for the Query:
-export const mlNetworkSingleIpKqlQuery =
-  "/app/siem#/ml-network/ip/127.0.0.1?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))";
-
-// Multiple IPs with a null for the Query:
-export const mlNetworkMultipleIpNullKqlQuery =
-  "/app/siem#/ml-network/ip/127.0.0.1,127.0.0.2?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))";
-
-// Multiple IPs with a value for the Query:
-export const mlNetworkMultipleIpKqlQuery =
-  "/app/siem#/ml-network/ip/127.0.0.1,127.0.0.2?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))";
-
-// $ip$ with a null Query:
-export const mlNetworkNullKqlQuery =
-  "/app/siem#/ml-network/ip/$ip$?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))";
-
-// $ip$ with a value for the Query:
-export const mlNetworkKqlQuery =
-  "/app/siem#/ml-network/ip/$ip$?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))";
-
-// Single host name with a null for the Query:
-export const mlHostSingleHostNullKqlQuery =
-  "/app/siem#/ml-hosts/siem-windows?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))";
-
-// Single host name with a variable in the Query:
-export const mlHostSingleHostKqlQueryVariable =
-  "/app/siem#/ml-hosts/siem-windows?query=(language:kuery,query:'process.name%20:%20%22$process.name$%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))";
-
-// Single host name with a value for Query:
-export const mlHostSingleHostKqlQuery =
-  "/app/siem#/ml-hosts/siem-windows?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))";
-
-// Multiple host names with null for Query:
-export const mlHostMultiHostNullKqlQuery =
-  "/app/siem#/ml-hosts/siem-windows,siem-suricata?query=!n&&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))";
-
-// Multiple host names with a value for Query:
-export const mlHostMultiHostKqlQuery =
-  "/app/siem#/ml-hosts/siem-windows,siem-suricata?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))";
-
-// Undefined/null host name with a null for the KQL:
-export const mlHostVariableHostNullKqlQuery =
-  "/app/siem#/ml-hosts/$host.name$?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))";
-
-// Undefined/null host name but with a value for Query:
-export const mlHostVariableHostKqlQuery =
-  "/app/siem#/ml-hosts/$host.name$?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))";
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/navigation/selectors.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/navigation/selectors.ts
deleted file mode 100644
index 0d5f40ae53966..0000000000000
--- a/x-pack/legacy/plugins/siem/cypress/integration/lib/navigation/selectors.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-/** Top-level (global) navigation link to the `Hosts` page */
-export const NAVIGATION_HOSTS = '[data-test-subj="navigation-hosts"]';
-
-/** Top-level (global) navigation link to the `Network` page */
-export const NAVIGATION_NETWORK = '[data-test-subj="navigation-network"]';
-
-/** Top-level (global) navigation link to the `Overview` page */
-export const NAVIGATION_OVERVIEW = '[data-test-subj="navigation-overview"]';
-
-/** Top-level (global) navigation link to the `Timelines` page */
-export const NAVIGATION_TIMELINES = '[data-test-subj="navigation-timelines"]';
-
-export const HOSTS_PAGE_TABS = {
-  allHosts: '[data-test-subj="navigation-allHosts"]',
-  anomalies: '[data-test-subj="navigation-anomalies"]',
-  authentications: '[data-test-subj="navigation-authentications"]',
-  events: '[data-test-subj="navigation-events"]',
-  uncommonProcesses: '[data-test-subj="navigation-uncommonProcesses"]',
-};
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/overview/selectors.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/overview/selectors.ts
deleted file mode 100644
index 95facc8974400..0000000000000
--- a/x-pack/legacy/plugins/siem/cypress/integration/lib/overview/selectors.ts
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-// Host Stats
-export const STAT_AUDITD = {
-  value: '123',
-  domId: '[data-test-subj="host-stat-auditbeatAuditd"]',
-};
-export const ENDGAME_DNS = {
-  value: '391',
-  domId: '[data-test-subj="host-stat-endgameDns"]',
-};
-export const ENDGAME_FILE = {
-  value: '392',
-  domId: '[data-test-subj="host-stat-endgameFile"]',
-};
-export const ENDGAME_IMAGE_LOAD = {
-  value: '393',
-  domId: '[data-test-subj="host-stat-endgameImageLoad"]',
-};
-export const ENDGAME_NETWORK = {
-  value: '394',
-  domId: '[data-test-subj="host-stat-endgameNetwork"]',
-};
-export const ENDGAME_PROCESS = {
-  value: '395',
-  domId: '[data-test-subj="host-stat-endgameProcess"]',
-};
-export const ENDGAME_REGISTRY = {
-  value: '396',
-  domId: '[data-test-subj="host-stat-endgameRegistry"]',
-};
-export const ENDGAME_SECURITY = {
-  value: '397',
-  domId: '[data-test-subj="host-stat-endgameSecurity"]',
-};
-export const STAT_FILEBEAT = {
-  value: '890',
-  domId: '[data-test-subj="host-stat-filebeatSystemModule"]',
-};
-export const STAT_FIM = {
-  value: '345',
-  domId: '[data-test-subj="host-stat-auditbeatFIM"]',
-};
-export const STAT_LOGIN = {
-  value: '456',
-  domId: '[data-test-subj="host-stat-auditbeatLogin"]',
-};
-export const STAT_PACKAGE = {
-  value: '567',
-  domId: '[data-test-subj="host-stat-auditbeatPackage"]',
-};
-export const STAT_PROCESS = {
-  value: '678',
-  domId: '[data-test-subj="host-stat-auditbeatProcess"]',
-};
-export const STAT_USER = {
-  value: '789',
-  domId: '[data-test-subj="host-stat-auditbeatUser"]',
-};
-export const STAT_WINLOGBEAT_SECURITY = {
-  value: '70',
-  domId: '[data-test-subj="host-stat-winlogbeatSecurity"]',
-};
-export const STAT_WINLOGBEAT_MWSYSMON_OPERATIONAL = {
-  value: '30',
-  domId: '[data-test-subj="host-stat-winlogbeatMWSysmonOperational"]',
-};
-
-export const HOST_STATS = [
-  STAT_AUDITD,
-  ENDGAME_DNS,
-  ENDGAME_FILE,
-  ENDGAME_IMAGE_LOAD,
-  ENDGAME_NETWORK,
-  ENDGAME_PROCESS,
-  ENDGAME_REGISTRY,
-  ENDGAME_SECURITY,
-  STAT_FILEBEAT,
-  STAT_FIM,
-  STAT_LOGIN,
-  STAT_PACKAGE,
-  STAT_PROCESS,
-  STAT_USER,
-  STAT_WINLOGBEAT_SECURITY,
-  STAT_WINLOGBEAT_MWSYSMON_OPERATIONAL,
-];
-
-// Network Stats
-export const STAT_SOCKET = {
-  value: '578,502',
-  domId: '[data-test-subj="network-stat-auditbeatSocket"]',
-};
-export const STAT_CISCO = {
-  value: '999',
-  domId: '[data-test-subj="network-stat-filebeatCisco"]',
-};
-export const STAT_NETFLOW = {
-  value: '2,544',
-  domId: '[data-test-subj="network-stat-filebeatNetflow"]',
-};
-export const STAT_PANW = {
-  value: '678',
-  domId: '[data-test-subj="network-stat-filebeatPanw"]',
-};
-export const STAT_SURICATA = {
-  value: '303,699',
-  domId: '[data-test-subj="network-stat-filebeatSuricata"]',
-};
-export const STAT_ZEEK = {
-  value: '71,129',
-  domId: '[data-test-subj="network-stat-filebeatZeek"]',
-};
-export const STAT_DNS = {
-  value: '1,090',
-  domId: '[data-test-subj="network-stat-packetbeatDNS"]',
-};
-export const STAT_FLOW = {
-  value: '722,153',
-  domId: '[data-test-subj="network-stat-packetbeatFlow"]',
-};
-export const STAT_TLS = {
-  value: '340',
-  domId: '[data-test-subj="network-stat-packetbeatTLS"]',
-};
-
-export const NETWORK_STATS = [
-  STAT_SOCKET,
-  STAT_CISCO,
-  STAT_NETFLOW,
-  STAT_PANW,
-  STAT_SURICATA,
-  STAT_ZEEK,
-  STAT_DNS,
-  STAT_FLOW,
-  STAT_TLS,
-];
-
-export const OVERVIEW_HOST_STATS = '[data-test-subj="overview-hosts-stats"]';
-
-export const OVERVIEW_NETWORK_STATS = '[data-test-subj="overview-network-stats"]';
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/pagination/selectors.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/pagination/selectors.ts
deleted file mode 100644
index 8e05f3e54568e..0000000000000
--- a/x-pack/legacy/plugins/siem/cypress/integration/lib/pagination/selectors.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-export const AUTHENTICATIONS_TABLE = '[data-test-subj="table-authentications-loading-false"]';
-export const getDraggableField = (field: string) => `[data-test-subj="draggable-content-${field}"]`;
-export const getPageButtonSelector = (num: number) => `[data-test-subj="pagination-button-${num}"]`;
-export const NAVIGATION_AUTHENTICATIONS = '[data-test-subj="navigation-authentications"]';
-export const NAVIGATION_UNCOMMON_PROCESSES = '[data-test-subj="navigation-uncommonProcesses"]';
-export const NUMBERED_PAGINATION = '[data-test-subj="numberedPagination"]';
-export const SUPER_DATE_PICKER_APPLY_BUTTON = '[data-test-subj="superDatePickerApplyTimeButton"]';
-export const UNCOMMON_PROCCESSES_TABLE = '[data-test-subj="table-uncommonProcesses-loading-false"]';
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/timeline/helpers.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/timeline/helpers.ts
deleted file mode 100644
index ef2c19bd7e737..0000000000000
--- a/x-pack/legacy/plugins/siem/cypress/integration/lib/timeline/helpers.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { drag, drop } from '../drag_n_drop/helpers';
-import { ALL_HOSTS_WIDGET_DRAGGABLE_HOSTS } from '../hosts/selectors';
-import {
-  CLOSE_TIMELINE_BTN,
-  CREATE_NEW_TIMELINE,
-  SEARCH_OR_FILTER_CONTAINER,
-  SERVER_SIDE_EVENT_COUNT,
-  TIMELINE_DATA_PROVIDERS,
-  TIMELINE_SETTINGS,
-  TIMELINE_TOGGLE_BUTTON,
-  TOGGLE_TIMELINE_EXPAND_EVENT,
-} from './selectors';
-import { DEFAULT_TIMEOUT } from '../util/helpers';
-
-/** Toggles the timeline's open / closed state by clicking the `T I M E L I N E` button */
-export const toggleTimelineVisibility = () =>
-  cy.get(TIMELINE_TOGGLE_BUTTON, { timeout: DEFAULT_TIMEOUT }).click();
-
-export const createNewTimeline = () => {
-  cy.get(TIMELINE_SETTINGS).click();
-  cy.get(CREATE_NEW_TIMELINE).click();
-  cy.get(CLOSE_TIMELINE_BTN).click({ force: true });
-};
-
-/** Drags and drops a host from the `All Hosts` widget on the `Hosts` page to the timeline */
-export const dragFromAllHostsToTimeline = () => {
-  cy.get(ALL_HOSTS_WIDGET_DRAGGABLE_HOSTS)
-    .first()
-    .then(host => drag(host));
-  cy.get(TIMELINE_DATA_PROVIDERS).then(dataProvidersDropArea => drop(dataProvidersDropArea));
-};
-
-/** Executes the specified KQL query in the timeline */
-export const executeKQL = (query: string) => {
-  cy.get(`${SEARCH_OR_FILTER_CONTAINER} input`).type(`${query} {enter}`);
-};
-
-/** A sample KQL query that finds any documents where the `host.name` field exists */
-export const hostExistsQuery = 'host.name: *';
-
-/** Asserts that at least one event matches the timeline's search criteria */
-export const assertAtLeastOneEventMatchesSearch = () =>
-  cy
-    .get(SERVER_SIDE_EVENT_COUNT, { timeout: DEFAULT_TIMEOUT })
-    .invoke('text')
-    .should('be.above', 0);
-
-/** Toggles open or closed the first event in the timeline */
-export const toggleFirstTimelineEventDetails = () => {
-  cy.get(TOGGLE_TIMELINE_EXPAND_EVENT, { timeout: DEFAULT_TIMEOUT })
-    .first()
-    .click({ force: true });
-};
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/timeline/selectors.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/timeline/selectors.ts
deleted file mode 100644
index 5515c1f7d58e2..0000000000000
--- a/x-pack/legacy/plugins/siem/cypress/integration/lib/timeline/selectors.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-/** A data provider rendered in the timeline's data providers drop area */
-export const DATA_PROVIDER = '[data-test-subj="providerContainer"]';
-
-export const TIMELINE_FIELDS_BUTTON =
-  '[data-test-subj="timeline"] [data-test-subj="show-field-browser"]';
-
-/** Data providers are dropped and rendered in this area of the timeline */
-export const TIMELINE_DATA_PROVIDERS = '[data-test-subj="dataProviders"]';
-
-/** The empty data providers area when no data providers have been added to the timeline */
-export const TIMELINE_DATA_PROVIDERS_EMPTY =
-  '[data-test-subj="dataProviders"] [data-test-subj="empty"]';
-
-/** Data providers that were dropped on a timeline */
-export const TIMELINE_DROPPED_DATA_PROVIDERS = `${TIMELINE_DATA_PROVIDERS} ${DATA_PROVIDER}`;
-
-/** The `Timeline ^` button that toggles visibility of the Timeline */
-export const TIMELINE_TOGGLE_BUTTON = '[data-test-subj="flyoutOverlay"]';
-
-/** The flyout button shown when a data provider is not ready to be dropped on the timeline */
-export const TIMELINE_NOT_READY_TO_DROP_BUTTON =
-  '[data-test-subj="flyout-button-not-ready-to-drop"]';
-
-/** Contains the KQL bar for searching or filtering in the timeline */
-export const SEARCH_OR_FILTER_CONTAINER =
-  '[data-test-subj="timeline-search-or-filter-search-container"]';
-
-/** The total server-side count of the events matching the timeline's search criteria */
-export const SERVER_SIDE_EVENT_COUNT = '[data-test-subj="server-side-event-count"]';
-
-/** Expands or collapses an event in the timeline */
-export const TOGGLE_TIMELINE_EXPAND_EVENT = '[data-test-subj="expand-event"]';
-
-/** The body of the timeline flyout */
-export const TIMELINE_FLYOUT_BODY = '[data-test-subj="eui-flyout-body"]';
-
-export const CLOSE_TIMELINE_BTN = '[data-test-subj="close-timeline"]';
-
-export const TIMELINE_SETTINGS = '[data-test-subj="settings-gear"]';
-
-export const CREATE_NEW_TIMELINE = '[data-test-subj="timeline-new"]';
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/url_state/index.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/url_state/index.ts
deleted file mode 100644
index fa754cd4b8db4..0000000000000
--- a/x-pack/legacy/plugins/siem/cypress/integration/lib/url_state/index.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-/*
- * Be Careful here by using iso date and epoch date
- * because the conversion might not what you expect
- * for different timezone better to calculate
- * them on the fly
- */
-
-export const ABSOLUTE_DATE_RANGE = {
-  url:
-    '/app/siem#/network/?timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))',
-
-  urlUnlinked:
-    '/app/siem#/network/?timerange=(global:(linkTo:!(),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(),timerange:(from:1564776209186,kind:absolute,to:1564779809186)))',
-  urlKqlNetworkNetwork: `/app/siem#/network/?query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`,
-  urlKqlNetworkHosts: `/app/siem#/network/?query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`,
-  urlKqlHostsNetwork: `/app/siem#/hosts/allHosts?query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`,
-  urlKqlHostsHosts: `/app/siem#/hosts/allHosts?query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`,
-  urlHost:
-    '/app/siem#/hosts/authentications?timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))',
-  urlHostNew:
-    '/app/siem#/hosts/authentications?timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))',
-};
-export const DATE_PICKER_START_DATE_POPOVER_BUTTON =
-  'div[data-test-subj="globalDatePicker"] button[data-test-subj="superDatePickerstartDatePopoverButton"]';
-export const DATE_PICKER_END_DATE_POPOVER_BUTTON =
-  '[data-test-subj="globalDatePicker"] [data-test-subj="superDatePickerendDatePopoverButton"]';
-export const DATE_PICKER_START_DATE_POPOVER_BUTTON_TIMELINE =
-  '[data-test-subj="timeline-properties"] [data-test-subj="superDatePickerstartDatePopoverButton"]';
-export const DATE_PICKER_END_DATE_POPOVER_BUTTON_TIMELINE =
-  '[data-test-subj="timeline-properties"] [data-test-subj="superDatePickerendDatePopoverButton"]';
-export const DATE_PICKER_ABSOLUTE_TAB = '[data-test-subj="superDatePickerAbsoluteTab"]';
-export const DATE_PICKER_APPLY_BUTTON =
-  '[data-test-subj="globalDatePicker"] button[data-test-subj="querySubmitButton"]';
-export const DATE_PICKER_APPLY_BUTTON_TIMELINE =
-  '[data-test-subj="timeline-properties"] button[data-test-subj="superDatePickerApplyTimeButton"]';
-export const DATE_PICKER_ABSOLUTE_INPUT = '[data-test-subj="superDatePickerAbsoluteDateInput"]';
-export const KQL_INPUT = '[data-test-subj="queryInput"]';
-export const TIMELINE_TITLE = '[data-test-subj="timeline-title"]';
-
-export const HOST_DETAIL_SIEM_KIBANA = '[data-test-subj="table-allHosts-loading-false"] a.euiLink';
-export const BREADCRUMBS = '[data-test-subj="breadcrumbs"] a';
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/urls/index.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/urls/index.ts
deleted file mode 100644
index 18276580289c7..0000000000000
--- a/x-pack/legacy/plugins/siem/cypress/integration/lib/urls/index.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-/** The SIEM app's Hosts page */
-export const HOSTS_PAGE = '/app/siem#/hosts/allHosts';
-export const HOSTS_PAGE_TAB_URLS = {
-  allHosts: '/app/siem#/hosts/allHosts',
-  anomalies: '/app/siem#/hosts/anomalies',
-  authentications: '/app/siem#/hosts/authentications',
-  events: '/app/siem#/hosts/events',
-  uncommonProcesses: '/app/siem#/hosts/uncommonProcesses',
-};
-
-/** Kibana's login page */
-export const LOGIN_PAGE = '/login';
-
-/** The SIEM app's Network page */
-export const NETWORK_PAGE = '/app/siem#/network';
-export const NETWORK_TAB_URLS = {
-  dns: `${NETWORK_PAGE}/dns`,
-};
-
-/** The SIEM app's Overview page */
-export const OVERVIEW_PAGE = '/app/siem#/overview';
-
-/** The SIEM app's Timelines page */
-export const TIMELINES_PAGE = '/app/siem#/timelines';
-
-/** Visit this URL to logout of Kibana */
-export const LOGOUT = '/logout';
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/util/helpers.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/util/helpers.ts
deleted file mode 100644
index 4f96430bb381e..0000000000000
--- a/x-pack/legacy/plugins/siem/cypress/integration/lib/util/helpers.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { login } from '../login/helpers';
-
-/** The default time in ms to wait for a Cypress command to complete */
-export const DEFAULT_TIMEOUT = 30 * 1000;
-
-/**
- * Authenticates with Kibana, visits the specified `url`, and waits for the
- * Kibana logo to be displayed before continuing
- */
-export const loginAndWaitForPage = (url: string) => {
-  login();
-
-  cy.visit(`${Cypress.config().baseUrl}${url}`);
-
-  cy.viewport('macbook-15');
-
-  cy.contains('a', 'SIEM', { timeout: DEFAULT_TIMEOUT });
-};
-
-export const waitForTableLoad = (dataTestSubj: string) =>
-  cy.get(dataTestSubj, { timeout: DEFAULT_TIMEOUT });
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/ml_conditional_links/ml_conditional_links.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/ml_conditional_links.spec.ts
similarity index 93%
rename from x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/ml_conditional_links/ml_conditional_links.spec.ts
rename to x-pack/legacy/plugins/siem/cypress/integration/ml_conditional_links.spec.ts
index fabd86cf51f91..b02ed1a5e4c94 100644
--- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/ml_conditional_links/ml_conditional_links.spec.ts
+++ b/x-pack/legacy/plugins/siem/cypress/integration/ml_conditional_links.spec.ts
@@ -4,28 +4,30 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+import { KQL_INPUT } from '../screens/siem_header';
+
+import { loginAndWaitForPage } from '../tasks/login';
+
 import {
-  mlNetworkSingleIpNullKqlQuery,
-  mlNetworkSingleIpKqlQuery,
-  mlNetworkMultipleIpNullKqlQuery,
-  mlNetworkMultipleIpKqlQuery,
-  mlNetworkNullKqlQuery,
-  mlNetworkKqlQuery,
-  mlHostSingleHostNullKqlQuery,
-  mlHostSingleHostKqlQueryVariable,
-  mlHostSingleHostKqlQuery,
-  mlHostMultiHostNullKqlQuery,
   mlHostMultiHostKqlQuery,
-  mlHostVariableHostNullKqlQuery,
+  mlHostMultiHostNullKqlQuery,
+  mlHostSingleHostKqlQuery,
+  mlHostSingleHostKqlQueryVariable,
+  mlHostSingleHostNullKqlQuery,
   mlHostVariableHostKqlQuery,
-} from '../../../urls/ml_conditional_links';
-import { loginAndWaitForPage, DEFAULT_TIMEOUT } from '../../../tasks/login';
-import { KQL_INPUT } from '../../../screens/header';
+  mlHostVariableHostNullKqlQuery,
+  mlNetworkKqlQuery,
+  mlNetworkMultipleIpKqlQuery,
+  mlNetworkMultipleIpNullKqlQuery,
+  mlNetworkNullKqlQuery,
+  mlNetworkSingleIpKqlQuery,
+  mlNetworkSingleIpNullKqlQuery,
+} from '../urls/ml_conditional_links';
 
 describe('ml conditional links', () => {
   it('sets the KQL from a single IP with a value for the query', () => {
     loginAndWaitForPage(mlNetworkSingleIpKqlQuery);
-    cy.get(KQL_INPUT, { timeout: DEFAULT_TIMEOUT }).should(
+    cy.get(KQL_INPUT).should(
       'have.attr',
       'value',
       '(process.name: "conhost.exe" or process.name: "sc.exe")'
@@ -34,7 +36,7 @@ describe('ml conditional links', () => {
 
   it('sets the KQL from a multiple IPs with a null for the query', () => {
     loginAndWaitForPage(mlNetworkMultipleIpNullKqlQuery);
-    cy.get(KQL_INPUT, { timeout: DEFAULT_TIMEOUT }).should(
+    cy.get(KQL_INPUT).should(
       'have.attr',
       'value',
       '((source.ip: "127.0.0.1" or destination.ip: "127.0.0.1") or (source.ip: "127.0.0.2" or destination.ip: "127.0.0.2"))'
@@ -43,7 +45,7 @@ describe('ml conditional links', () => {
 
   it('sets the KQL from a multiple IPs with a value for the query', () => {
     loginAndWaitForPage(mlNetworkMultipleIpKqlQuery);
-    cy.get(KQL_INPUT, { timeout: DEFAULT_TIMEOUT }).should(
+    cy.get(KQL_INPUT).should(
       'have.attr',
       'value',
       '((source.ip: "127.0.0.1" or destination.ip: "127.0.0.1") or (source.ip: "127.0.0.2" or destination.ip: "127.0.0.2")) and ((process.name: "conhost.exe" or process.name: "sc.exe"))'
@@ -52,7 +54,7 @@ describe('ml conditional links', () => {
 
   it('sets the KQL from a $ip$ with a value for the query', () => {
     loginAndWaitForPage(mlNetworkKqlQuery);
-    cy.get(KQL_INPUT, { timeout: DEFAULT_TIMEOUT }).should(
+    cy.get(KQL_INPUT).should(
       'have.attr',
       'value',
       '(process.name: "conhost.exe" or process.name: "sc.exe")'
@@ -61,7 +63,7 @@ describe('ml conditional links', () => {
 
   it('sets the KQL from a single host name with a value for query', () => {
     loginAndWaitForPage(mlHostSingleHostKqlQuery);
-    cy.get(KQL_INPUT, { timeout: DEFAULT_TIMEOUT }).should(
+    cy.get(KQL_INPUT).should(
       'have.attr',
       'value',
       '(process.name: "conhost.exe" or process.name: "sc.exe")'
@@ -70,7 +72,7 @@ describe('ml conditional links', () => {
 
   it('sets the KQL from a multiple host names with null for query', () => {
     loginAndWaitForPage(mlHostMultiHostNullKqlQuery);
-    cy.get(KQL_INPUT, { timeout: DEFAULT_TIMEOUT }).should(
+    cy.get(KQL_INPUT).should(
       'have.attr',
       'value',
       '(host.name: "siem-windows" or host.name: "siem-suricata")'
@@ -79,7 +81,7 @@ describe('ml conditional links', () => {
 
   it('sets the KQL from a multiple host names with a value for query', () => {
     loginAndWaitForPage(mlHostMultiHostKqlQuery);
-    cy.get(KQL_INPUT, { timeout: DEFAULT_TIMEOUT }).should(
+    cy.get(KQL_INPUT).should(
       'have.attr',
       'value',
       '(host.name: "siem-windows" or host.name: "siem-suricata") and ((process.name: "conhost.exe" or process.name: "sc.exe"))'
@@ -88,7 +90,7 @@ describe('ml conditional links', () => {
 
   it('sets the KQL from a undefined/null host name but with a value for query', () => {
     loginAndWaitForPage(mlHostVariableHostKqlQuery);
-    cy.get(KQL_INPUT, { timeout: DEFAULT_TIMEOUT }).should(
+    cy.get(KQL_INPUT).should(
       'have.attr',
       'value',
       '(process.name: "conhost.exe" or process.name: "sc.exe")'
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/navigation/navigation.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/navigation.spec.ts
similarity index 78%
rename from x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/navigation/navigation.spec.ts
rename to x-pack/legacy/plugins/siem/cypress/integration/navigation.spec.ts
index 364864b395d41..2c5a0e5eeea8a 100644
--- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/navigation/navigation.spec.ts
+++ b/x-pack/legacy/plugins/siem/cypress/integration/navigation.spec.ts
@@ -3,11 +3,12 @@
  * or more contributor license agreements. Licensed under the Elastic License;
  * you may not use this file except in compliance with the Elastic License.
  */
+import { HOSTS, NETWORK, OVERVIEW, TIMELINES } from '../screens/siem_header';
 
-import { TIMELINES_PAGE } from '../../../urls/navigation';
-import { HOSTS, NETWORK, OVERVIEW, TIMELINES } from '../../../screens/header';
-import { loginAndWaitForPage } from '../../../tasks/login';
-import { navigateFromHeaderTo } from '../../../tasks/header';
+import { loginAndWaitForPage } from '../tasks/login';
+import { navigateFromHeaderTo } from '../tasks/siem_header';
+
+import { TIMELINES_PAGE } from '../urls/navigation';
 
 describe('top-level navigation common to all pages in the SIEM app', () => {
   before(() => {
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/overview/overview.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/overview.spec.ts
similarity index 74%
rename from x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/overview/overview.spec.ts
rename to x-pack/legacy/plugins/siem/cypress/integration/overview.spec.ts
index 64002aadc86d8..cadb4beca0f9e 100644
--- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/overview/overview.spec.ts
+++ b/x-pack/legacy/plugins/siem/cypress/integration/overview.spec.ts
@@ -4,10 +4,12 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { OVERVIEW_PAGE } from '../../../urls/navigation';
-import { HOST_STATS, NETWORK_STATS } from '../../../screens/overview';
-import { expandHostStats, expandNetworkStats } from '../../../tasks/overview';
-import { loginAndWaitForPage } from '../../lib/util/helpers';
+import { HOST_STATS, NETWORK_STATS } from '../screens/overview';
+
+import { expandHostStats, expandNetworkStats } from '../tasks/overview';
+import { loginAndWaitForPage } from '../tasks/login';
+
+import { OVERVIEW_PAGE } from '../urls/navigation';
 
 describe('Overview Page', () => {
   before(() => {
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/pagination/pagination.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/pagination.spec.ts
similarity index 80%
rename from x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/pagination/pagination.spec.ts
rename to x-pack/legacy/plugins/siem/cypress/integration/pagination.spec.ts
index d8ad75322b889..482c97fe29c3b 100644
--- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/pagination/pagination.spec.ts
+++ b/x-pack/legacy/plugins/siem/cypress/integration/pagination.spec.ts
@@ -4,15 +4,17 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { HOSTS_PAGE_TAB_URLS } from '../../../urls/navigation';
-import { loginAndWaitForPage } from '../../../tasks/login';
-import { refreshPage } from '../../../tasks/header';
-import { goToFirstPage, goToThirdPage } from '../../../tasks/pagination';
-import { FIRST_PAGE_SELECTOR, THIRD_PAGE_SELECTOR } from '../../../screens/pagination';
-import { PROCESS_NAME_FIELD } from '../../../screens/uncommon_processes';
-import { waitForUncommonProcessesToBeLoaded } from '../../../tasks/uncommon_processes';
-import { waitForAuthenticationsToBeLoaded } from '../../../tasks/authentications';
-import { openAuthentications, openUncommonProcesses } from '../../../tasks/hosts/main';
+import { PROCESS_NAME_FIELD } from '../screens/hosts/uncommon_processes';
+import { FIRST_PAGE_SELECTOR, THIRD_PAGE_SELECTOR } from '../screens/pagination';
+
+import { waitForAuthenticationsToBeLoaded } from '../tasks/hosts/authentications';
+import { openAuthentications, openUncommonProcesses } from '../tasks/hosts/main';
+import { waitForUncommonProcessesToBeLoaded } from '../tasks/hosts/uncommon_processes';
+import { loginAndWaitForPage } from '../tasks/login';
+import { goToFirstPage, goToThirdPage } from '../tasks/pagination';
+import { refreshPage } from '../tasks/siem_header';
+
+import { HOSTS_PAGE_TAB_URLS } from '../urls/navigation';
 
 describe('Pagination', () => {
   before(() => {
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/data_providers.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/timeline_data_providers.spec.ts
similarity index 81%
rename from x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/data_providers.spec.ts
rename to x-pack/legacy/plugins/siem/cypress/integration/timeline_data_providers.spec.ts
index 7d1ee43b1b509..4889d40ae7d39 100644
--- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/data_providers.spec.ts
+++ b/x-pack/legacy/plugins/siem/cypress/integration/timeline_data_providers.spec.ts
@@ -4,22 +4,25 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { HOSTS_PAGE } from '../../../urls/navigation';
 import {
-  waitForAllHostsToBeLoaded,
-  dragAndDropFirstHostToTimeline,
-  dragFirstHostToTimeline,
-  dragFirstHostToEmptyTimelineDataProviders,
-} from '../../../tasks/hosts/all_hosts';
-import { HOSTS_NAMES_DRAGGABLE } from '../../../screens/hosts/all_hosts';
-import { DEFAULT_TIMEOUT, loginAndWaitForPage } from '../../../tasks/login';
-import { createNewTimeline } from '../../../tasks/timeline/main';
-import { openTimeline } from '../../../tasks/siem_main';
-import {
-  TIMELINE_DATA_PROVIDERS_EMPTY,
   TIMELINE_DATA_PROVIDERS,
+  TIMELINE_DATA_PROVIDERS_EMPTY,
   TIMELINE_DROPPED_DATA_PROVIDERS,
-} from '../../../screens/timeline/main';
+} from '../screens/timeline';
+import { HOSTS_NAMES_DRAGGABLE } from '../screens/hosts/all_hosts';
+
+import {
+  dragAndDropFirstHostToTimeline,
+  dragFirstHostToEmptyTimelineDataProviders,
+  dragFirstHostToTimeline,
+  waitForAllHostsToBeLoaded,
+} from '../tasks/hosts/all_hosts';
+
+import { loginAndWaitForPage } from '../tasks/login';
+import { openTimeline } from '../tasks/siem_main';
+import { createNewTimeline } from '../tasks/timeline';
+
+import { HOSTS_PAGE } from '../urls/navigation';
 
 describe('timeline data providers', () => {
   before(() => {
@@ -38,7 +41,7 @@ describe('timeline data providers', () => {
   it('renders the data provider of a host dragged from the All Hosts widget on the hosts page', () => {
     dragAndDropFirstHostToTimeline();
 
-    cy.get(TIMELINE_DROPPED_DATA_PROVIDERS, { timeout: DEFAULT_TIMEOUT })
+    cy.get(TIMELINE_DROPPED_DATA_PROVIDERS)
       .first()
       .invoke('text')
       .then(dataProviderText => {
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/flyout_button.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/timeline_flyout_button.spec.ts
similarity index 68%
rename from x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/flyout_button.spec.ts
rename to x-pack/legacy/plugins/siem/cypress/integration/timeline_flyout_button.spec.ts
index b7faaaac1c06c..1a94a4abbe5bf 100644
--- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/flyout_button.spec.ts
+++ b/x-pack/legacy/plugins/siem/cypress/integration/timeline_flyout_button.spec.ts
@@ -4,15 +4,14 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { HOSTS_PAGE } from '../../../urls/navigation';
-import { waitForAllHostsToBeLoaded, dragFirstHostToTimeline } from '../../../tasks/hosts/all_hosts';
-import { loginAndWaitForPage } from '../../../tasks/login';
-import { openTimelineIfClosed, openTimeline } from '../../../tasks/siem_main';
-import {
-  TIMELINE_FLYOUT_BODY,
-  TIMELINE_NOT_READY_TO_DROP_BUTTON,
-} from '../../../screens/timeline/main';
-import { createNewTimeline } from '../../../tasks/timeline/main';
+import { TIMELINE_FLYOUT_BODY, TIMELINE_NOT_READY_TO_DROP_BUTTON } from '../screens/timeline';
+
+import { dragFirstHostToTimeline, waitForAllHostsToBeLoaded } from '../tasks/hosts/all_hosts';
+import { loginAndWaitForPage } from '../tasks/login';
+import { openTimeline, openTimelineIfClosed } from '../tasks/siem_main';
+import { createNewTimeline } from '../tasks/timeline';
+
+import { HOSTS_PAGE } from '../urls/navigation';
 
 describe('timeline flyout button', () => {
   before(() => {
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/search_or_filter.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/timeline_search_or_filter.spec.ts
similarity index 58%
rename from x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/search_or_filter.spec.ts
rename to x-pack/legacy/plugins/siem/cypress/integration/timeline_search_or_filter.spec.ts
index 28cc4a6e8827d..c06fd69a558a4 100644
--- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/search_or_filter.spec.ts
+++ b/x-pack/legacy/plugins/siem/cypress/integration/timeline_search_or_filter.spec.ts
@@ -4,11 +4,13 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { SERVER_SIDE_EVENT_COUNT } from '../../../screens/timeline/main';
-import { HOSTS_PAGE } from '../../../urls/navigation';
-import { loginAndWaitForPage, DEFAULT_TIMEOUT } from '../../../tasks/login';
-import { openTimeline } from '../../../tasks/siem_main';
-import { executeTimelineKQL } from '../../../tasks/timeline/main';
+import { SERVER_SIDE_EVENT_COUNT } from '../screens/timeline';
+
+import { loginAndWaitForPage } from '../tasks/login';
+import { openTimeline } from '../tasks/siem_main';
+import { executeTimelineKQL } from '../tasks/timeline';
+
+import { HOSTS_PAGE } from '../urls/navigation';
 
 describe('timeline search or filter KQL bar', () => {
   beforeEach(() => {
@@ -20,7 +22,7 @@ describe('timeline search or filter KQL bar', () => {
     openTimeline();
     executeTimelineKQL(hostExistsQuery);
 
-    cy.get(SERVER_SIDE_EVENT_COUNT, { timeout: DEFAULT_TIMEOUT })
+    cy.get(SERVER_SIDE_EVENT_COUNT)
       .invoke('text')
       .should('be.above', 0);
   });
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/toggle_column.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/timeline_toggle_column.spec.ts
similarity index 82%
rename from x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/toggle_column.spec.ts
rename to x-pack/legacy/plugins/siem/cypress/integration/timeline_toggle_column.spec.ts
index ccd6ae818c6cd..7b2c6f3b55b2e 100644
--- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/toggle_column.spec.ts
+++ b/x-pack/legacy/plugins/siem/cypress/integration/timeline_toggle_column.spec.ts
@@ -4,23 +4,25 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { HOSTS_PAGE } from '../../../urls/navigation';
-import { loginAndWaitForPage, DEFAULT_TIMEOUT } from '../../../tasks/login';
 import {
+  ID_HEADER_FIELD,
+  ID_TOGGLE_FIELD,
+  TIMESTAMP_HEADER_FIELD,
+  TIMESTAMP_TOGGLE_FIELD,
+} from '../screens/timeline';
+
+import { loginAndWaitForPage } from '../tasks/login';
+import { openTimeline } from '../tasks/siem_main';
+import {
+  checkIdToggleField,
   createNewTimeline,
-  populateTimeline,
+  dragAndDropIdToggleFieldToTimeline,
   expandFirstTimelineEventDetails,
+  populateTimeline,
   uncheckTimestampToggleField,
-  checkIdToggleField,
-  dragAndDropIdToggleFieldToTimeline,
-} from '../../../tasks/timeline/main';
-import { openTimeline } from '../../../tasks/siem_main';
-import {
-  TIMESTAMP_TOGGLE_FIELD,
-  ID_TOGGLE_FIELD,
-  TIMESTAMP_HEADER_FIELD,
-  ID_HEADER_FIELD,
-} from '../../../screens/timeline/main';
+} from '../tasks/timeline';
+
+import { HOSTS_PAGE } from '../urls/navigation';
 
 describe('toggle column in timeline', () => {
   before(() => {
@@ -63,8 +65,6 @@ describe('toggle column in timeline', () => {
     expandFirstTimelineEventDetails();
     dragAndDropIdToggleFieldToTimeline();
 
-    cy.get(ID_HEADER_FIELD, {
-      timeout: DEFAULT_TIMEOUT,
-    }).should('exist');
+    cy.get(ID_HEADER_FIELD).should('exist');
   });
 });
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/url_state/url_state.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/url_state.spec.ts
similarity index 84%
rename from x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/url_state/url_state.spec.ts
rename to x-pack/legacy/plugins/siem/cypress/integration/url_state.spec.ts
index 4345938c8867e..cabdb98fa5b67 100644
--- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/url_state/url_state.spec.ts
+++ b/x-pack/legacy/plugins/siem/cypress/integration/url_state.spec.ts
@@ -4,9 +4,18 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { ABSOLUTE_DATE_RANGE } from '../../../urls/state';
-import { DEFAULT_TIMEOUT, loginAndWaitForPage } from '../../../tasks/login';
-import { HOSTS_PAGE } from '../../../urls/navigation';
+import {
+  DATE_PICKER_END_DATE_POPOVER_BUTTON,
+  DATE_PICKER_END_DATE_POPOVER_BUTTON_TIMELINE,
+  DATE_PICKER_START_DATE_POPOVER_BUTTON,
+  DATE_PICKER_START_DATE_POPOVER_BUTTON_TIMELINE,
+} from '../screens/date_picker';
+import { HOSTS_NAMES } from '../screens/hosts/all_hosts';
+import { ANOMALIES_TAB } from '../screens/hosts/main';
+import { BREADCRUMBS, HOSTS, KQL_INPUT, NETWORK } from '../screens/siem_header';
+import { SERVER_SIDE_EVENT_COUNT, TIMELINE_TITLE } from '../screens/timeline';
+
+import { loginAndWaitForPage } from '../tasks/login';
 import {
   setStartDate,
   setEndDate,
@@ -14,23 +23,17 @@ import {
   setTimelineStartDate,
   setTimelineEndDate,
   updateTimelineDates,
-} from '../../../tasks/calendar';
-import { waitForIpsTableToBeLoaded } from '../../../tasks/network/flows';
-import { openTimeline } from '../../../tasks/siem_main';
-import {
-  DATE_PICKER_START_DATE_POPOVER_BUTTON_TIMELINE,
-  DATE_PICKER_END_DATE_POPOVER_BUTTON_TIMELINE,
-  DATE_PICKER_START_DATE_POPOVER_BUTTON,
-  DATE_PICKER_END_DATE_POPOVER_BUTTON,
-} from '../../../screens/calendar';
-import { kqlSearch, navigateFromHeaderTo, clearSearchBar } from '../../../tasks/header';
-import { HOSTS, NETWORK, KQL_INPUT, BREADCRUMBS } from '../../../screens/header';
-import { openAllHosts } from '../../../tasks/hosts/main';
-import { ANOMALIES_TAB } from '../../../screens/hosts/main';
-import { waitForAllHostsToBeLoaded, openFirstHostDetails } from '../../../tasks/hosts/all_hosts';
-import { HOSTS_NAMES } from '../../../screens/hosts/all_hosts';
-import { executeTimelineKQL, addNameToTimeline } from '../../../tasks/timeline/main';
-import { SERVER_SIDE_EVENT_COUNT, TIMELINE_TITLE } from '../../../screens/timeline/main';
+} from '../tasks/date_picker';
+import { openFirstHostDetails, waitForAllHostsToBeLoaded } from '../tasks/hosts/all_hosts';
+import { openAllHosts } from '../tasks/hosts/main';
+
+import { waitForIpsTableToBeLoaded } from '../tasks/network/flows';
+import { clearSearchBar, kqlSearch, navigateFromHeaderTo } from '../tasks/siem_header';
+import { openTimeline } from '../tasks/siem_main';
+import { addNameToTimeline, executeTimelineKQL } from '../tasks/timeline';
+
+import { HOSTS_PAGE } from '../urls/navigation';
+import { ABSOLUTE_DATE_RANGE } from '../urls/state';
 
 const ABSOLUTE_DATE = {
   endTime: '1564691609186',
@@ -146,20 +149,12 @@ describe('url state', () => {
 
   it('sets kql on network page', () => {
     loginAndWaitForPage(ABSOLUTE_DATE_RANGE.urlKqlNetworkNetwork);
-    cy.get(KQL_INPUT, { timeout: DEFAULT_TIMEOUT }).should(
-      'have.attr',
-      'value',
-      'source.ip: "10.142.0.9"'
-    );
+    cy.get(KQL_INPUT).should('have.attr', 'value', 'source.ip: "10.142.0.9"');
   });
 
   it('sets kql on hosts page', () => {
     loginAndWaitForPage(ABSOLUTE_DATE_RANGE.urlKqlHostsHosts);
-    cy.get(KQL_INPUT, { timeout: DEFAULT_TIMEOUT }).should(
-      'have.attr',
-      'value',
-      'source.ip: "10.142.0.9"'
-    );
+    cy.get(KQL_INPUT).should('have.attr', 'value', 'source.ip: "10.142.0.9"');
   });
 
   it('sets the url state when kql is set', () => {
@@ -197,7 +192,7 @@ describe('url state', () => {
       'href',
       "#/link-to/network?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))"
     );
-    cy.get(HOSTS_NAMES, { timeout: DEFAULT_TIMEOUT })
+    cy.get(HOSTS_NAMES)
       .first()
       .invoke('text')
       .should('eq', 'siem-kibana');
@@ -231,11 +226,7 @@ describe('url state', () => {
     loginAndWaitForPage(ABSOLUTE_DATE_RANGE.urlKqlHostsHosts);
     navigateFromHeaderTo(NETWORK);
 
-    cy.get(KQL_INPUT, { timeout: DEFAULT_TIMEOUT }).should(
-      'have.attr',
-      'value',
-      'source.ip: "10.142.0.9"'
-    );
+    cy.get(KQL_INPUT).should('have.attr', 'value', 'source.ip: "10.142.0.9"');
   });
 
   it('sets and reads the url state for timeline by id', () => {
@@ -243,7 +234,7 @@ describe('url state', () => {
     openTimeline();
     executeTimelineKQL('host.name: *');
 
-    cy.get(SERVER_SIDE_EVENT_COUNT, { timeout: DEFAULT_TIMEOUT })
+    cy.get(SERVER_SIDE_EVENT_COUNT)
       .invoke('text')
       .should('be.above', 0);
 
diff --git a/x-pack/legacy/plugins/siem/cypress/screens/calendar.ts b/x-pack/legacy/plugins/siem/cypress/screens/date_picker.ts
similarity index 99%
rename from x-pack/legacy/plugins/siem/cypress/screens/calendar.ts
rename to x-pack/legacy/plugins/siem/cypress/screens/date_picker.ts
index caff721c683e9..e49f5afa7bd0c 100644
--- a/x-pack/legacy/plugins/siem/cypress/screens/calendar.ts
+++ b/x-pack/legacy/plugins/siem/cypress/screens/date_picker.ts
@@ -4,18 +4,24 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-export const DATE_PICKER_START_DATE_POPOVER_BUTTON =
-  'div[data-test-subj="globalDatePicker"] button[data-test-subj="superDatePickerstartDatePopoverButton"]';
-export const DATE_PICKER_END_DATE_POPOVER_BUTTON =
-  '[data-test-subj="globalDatePicker"] [data-test-subj="superDatePickerendDatePopoverButton"]';
-export const DATE_PICKER_ABSOLUTE_TAB = '[data-test-subj="superDatePickerAbsoluteTab"]';
+export const DATE_PICKER_ABSOLUTE_INPUT = '[data-test-subj="superDatePickerAbsoluteDateInput"]';
+
 export const DATE_PICKER_APPLY_BUTTON =
   '[data-test-subj="globalDatePicker"] button[data-test-subj="querySubmitButton"]';
+
 export const DATE_PICKER_APPLY_BUTTON_TIMELINE =
   '[data-test-subj="timeline-properties"] button[data-test-subj="superDatePickerApplyTimeButton"]';
-export const DATE_PICKER_ABSOLUTE_INPUT = '[data-test-subj="superDatePickerAbsoluteDateInput"]';
-export const DATE_PICKER_START_DATE_POPOVER_BUTTON_TIMELINE =
-  '[data-test-subj="timeline-properties"] [data-test-subj="superDatePickerstartDatePopoverButton"]';
+
+export const DATE_PICKER_ABSOLUTE_TAB = '[data-test-subj="superDatePickerAbsoluteTab"]';
+
+export const DATE_PICKER_END_DATE_POPOVER_BUTTON =
+  '[data-test-subj="globalDatePicker"] [data-test-subj="superDatePickerendDatePopoverButton"]';
 
 export const DATE_PICKER_END_DATE_POPOVER_BUTTON_TIMELINE =
   '[data-test-subj="timeline-properties"] [data-test-subj="superDatePickerendDatePopoverButton"]';
+
+export const DATE_PICKER_START_DATE_POPOVER_BUTTON =
+  'div[data-test-subj="globalDatePicker"] button[data-test-subj="superDatePickerstartDatePopoverButton"]';
+
+export const DATE_PICKER_START_DATE_POPOVER_BUTTON_TIMELINE =
+  '[data-test-subj="timeline-properties"] [data-test-subj="superDatePickerstartDatePopoverButton"]';
diff --git a/x-pack/legacy/plugins/siem/cypress/screens/timeline/fields_browser.ts b/x-pack/legacy/plugins/siem/cypress/screens/fields_browser.ts
similarity index 92%
rename from x-pack/legacy/plugins/siem/cypress/screens/timeline/fields_browser.ts
rename to x-pack/legacy/plugins/siem/cypress/screens/fields_browser.ts
index aa63aaf89f98b..f15096bd874bc 100644
--- a/x-pack/legacy/plugins/siem/cypress/screens/timeline/fields_browser.ts
+++ b/x-pack/legacy/plugins/siem/cypress/screens/fields_browser.ts
@@ -4,57 +4,58 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-export const FIELDS_BROWSER_TITLE = '[data-test-subj="field-browser-title"]';
+export const FIELDS_BROWSER_CATEGORIES_COUNT = '[data-test-subj="categories-count"]';
 
-/** Typing in this input filters the Field Browser */
-export const FIELDS_BROWSER_FILTER_INPUT = '[data-test-subj="field-search"]';
+export const FIELDS_BROWSER_CHECKBOX = (id: string) => {
+  return `[data-test-subj="field-${id}-checkbox`;
+};
 
-/** The title of the selected category in the right-hand side of the fields browser */
-export const FIELDS_BROWSER_SELECTED_CATEGORY_TITLE = '[data-test-subj="selected-category-title"]';
+export const FIELDS_BROWSER_CONTAINER = '[data-test-subj="fields-browser-container"]';
 
-export const FIELDS_BROWSER_SELECTED_CATEGORY_COUNT =
-  '[data-test-subj="selected-category-count-badge"]';
+export const FIELDS_BROWSER_DRAGGABLE_HOST_GEO_COUNTRY_NAME_HEADER =
+  '[data-test-subj="timeline"] [data-test-subj="field-name-host.geo.country_name"]';
 
-export const FIELDS_BROWSER_CATEGORIES_COUNT = '[data-test-subj="categories-count"]';
+export const FIELDS_BROWSER_FIELDS_COUNT = '[data-test-subj="fields-count"]';
 
-export const FIELDS_BROWSER_HOST_CATEGORIES_COUNT = '[data-test-subj="host-category-count"]';
+export const FIELDS_BROWSER_FILTER_INPUT = '[data-test-subj="field-search"]';
 
-export const FIELDS_BROWSER_SYSTEM_CATEGORIES_COUNT = '[data-test-subj="system-category-count"]';
+export const FIELDS_BROWSER_HEADER_DROP_AREA =
+  '[data-test-subj="timeline"] [data-test-subj="headers-group"]';
 
-export const FIELDS_BROWSER_FIELDS_COUNT = '[data-test-subj="fields-count"]';
+export const FIELDS_BROWSER_HOST_CATEGORIES_COUNT = '[data-test-subj="host-category-count"]';
 
-/** Contains the body of the fields browser */
-export const FIELDS_BROWSER_CONTAINER = '[data-test-subj="fields-browser-container"]';
+export const FIELDS_BROWSER_HOST_GEO_CITY_NAME_CHECKBOX =
+  '[data-test-subj="field-host.geo.city_name-checkbox"]';
 
-export const FIELDS_BROWSER_MESSAGE_HEADER =
-  '[data-test-subj="timeline"] [data-test-subj="header-text-message"]';
+export const FIELDS_BROWSER_HOST_GEO_CITY_NAME_HEADER =
+  '[data-test-subj="header-text-host.geo.city_name"]';
 
-export const FIELDS_BROWSER_MESSAGE_CHECKBOX =
-  '[data-test-subj="timeline"] [data-test-subj="field-message-checkbox"]';
+export const FIELDS_BROWSER_HOST_GEO_CONTINENT_NAME_CHECKBOX =
+  '[data-test-subj="field-host.geo.continent_name-checkbox"]';
 
-export const FIELDS_BROWSER_HOST_GEO_COUNTRY_NAME_HEADER =
-  '[data-test-subj="header-text-host.geo.country_name"]';
+export const FIELDS_BROWSER_HEADER_HOST_GEO_CONTINENT_NAME_HEADER =
+  '[data-test-subj="header-text-host.geo.continent_name"]';
 
 export const FIELDS_BROWSER_HOST_GEO_COUNTRY_NAME_CHECKBOX =
   '[data-test-subj="field-host.geo.country_name-checkbox"]';
 
-export const FIELDS_BROWSER_DRAGGABLE_HOST_GEO_COUNTRY_NAME_HEADER =
-  '[data-test-subj="timeline"] [data-test-subj="field-name-host.geo.country_name"]';
+export const FIELDS_BROWSER_HOST_GEO_COUNTRY_NAME_HEADER =
+  '[data-test-subj="header-text-host.geo.country_name"]';
 
-export const FIELDS_BROWSER_HOST_GEO_CITY_NAME_HEADER =
-  '[data-test-subj="header-text-host.geo.city_name"]';
+export const FIELDS_BROWSER_MESSAGE_CHECKBOX =
+  '[data-test-subj="timeline"] [data-test-subj="field-message-checkbox"]';
 
-export const FIELDS_BROWSER_HOST_GEO_CITY_NAME_CHECKBOX =
-  '[data-test-subj="field-host.geo.city_name-checkbox"]';
+export const FIELDS_BROWSER_MESSAGE_HEADER =
+  '[data-test-subj="timeline"] [data-test-subj="header-text-message"]';
 
-export const FIELDS_BROWSER_HEADER_DROP_AREA =
-  '[data-test-subj="timeline"] [data-test-subj="headers-group"]';
+export const FIELDS_BROWSER_RESET_FIELDS =
+  '[data-test-subj="timeline"] [data-test-subj="reset-fields"]';
 
-export const FIELDS_BROWSER_HEADER_HOST_GEO_CONTINENT_NAME_HEADER =
-  '[data-test-subj="header-text-host.geo.continent_name"]';
+export const FIELDS_BROWSER_TITLE = '[data-test-subj="field-browser-title"]';
 
-export const FIELDS_BROWSER_HOST_GEO_CONTINENT_NAME_CHECKBOX =
-  '[data-test-subj="field-host.geo.continent_name-checkbox"]';
+export const FIELDS_BROWSER_SELECTED_CATEGORY_COUNT =
+  '[data-test-subj="selected-category-count-badge"]';
 
-export const FIELDS_BROWSER_RESET_FIELDS =
-  '[data-test-subj="timeline"] [data-test-subj="reset-fields"]';
+export const FIELDS_BROWSER_SELECTED_CATEGORY_TITLE = '[data-test-subj="selected-category-title"]';
+
+export const FIELDS_BROWSER_SYSTEM_CATEGORIES_COUNT = '[data-test-subj="system-category-count"]';
diff --git a/x-pack/legacy/plugins/siem/cypress/screens/hosts/all_hosts.ts b/x-pack/legacy/plugins/siem/cypress/screens/hosts/all_hosts.ts
index 61f39ca7a8b0c..ca123a8afc6c9 100644
--- a/x-pack/legacy/plugins/siem/cypress/screens/hosts/all_hosts.ts
+++ b/x-pack/legacy/plugins/siem/cypress/screens/hosts/all_hosts.ts
@@ -6,6 +6,6 @@
 
 export const ALL_HOSTS_TABLE = '[data-test-subj="table-allHosts-loading-false"]';
 
-export const HOSTS_NAMES_DRAGGABLE = '[data-test-subj="draggable-content-host.name"]';
-
 export const HOSTS_NAMES = '[data-test-subj="draggable-content-host.name"] a.euiLink';
+
+export const HOSTS_NAMES_DRAGGABLE = '[data-test-subj="draggable-content-host.name"]';
diff --git a/x-pack/legacy/plugins/siem/cypress/screens/authentications.ts b/x-pack/legacy/plugins/siem/cypress/screens/hosts/authentications.ts
similarity index 100%
rename from x-pack/legacy/plugins/siem/cypress/screens/authentications.ts
rename to x-pack/legacy/plugins/siem/cypress/screens/hosts/authentications.ts
diff --git a/x-pack/legacy/plugins/siem/cypress/screens/hosts/events.ts b/x-pack/legacy/plugins/siem/cypress/screens/hosts/events.ts
index 034c1453fc979..ed46a90c872c8 100644
--- a/x-pack/legacy/plugins/siem/cypress/screens/hosts/events.ts
+++ b/x-pack/legacy/plugins/siem/cypress/screens/hosts/events.ts
@@ -4,33 +4,39 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-export const EVENTS_VIEWER_PANEL = '[data-test-subj="events-viewer-panel"]';
-
 export const CLOSE_MODAL = '[data-test-subj="modal-inspect-close"]';
 
-export const HEADER_SUBTITLE = `${EVENTS_VIEWER_PANEL} [data-test-subj="header-panel-subtitle"]`;
+export const EVENTS_VIEWER_FIELDS_BUTTON =
+  '[data-test-subj="events-viewer-panel"] [data-test-subj="show-field-browser-gear"]';
 
-export const INSPECT_MODAL = '[data-test-subj="modal-inspect-euiModal"]';
-
-export const INSPECT_QUERY = `${EVENTS_VIEWER_PANEL} [data-test-subj="inspect-icon-button"]`;
+export const EVENTS_VIEWER_PANEL = '[data-test-subj="events-viewer-panel"]';
 
-export const LOAD_MORE = `${EVENTS_VIEWER_PANEL} [data-test-subj="TimelineMoreButton"]`;
+export const FIELDS_BROWSER_CONTAINER = '[data-test-subj="fields-browser-container"]';
 
-export const LOCAL_EVENTS_COUNT = `${EVENTS_VIEWER_PANEL} [data-test-subj="local-events-count"]`;
+export const HEADER_SUBTITLE =
+  '[data-test-subj="events-viewer-panel"] [data-test-subj="header-panel-subtitle"]';
 
-export const SERVER_SIDE_EVENT_COUNT = '[data-test-subj="server-side-event-count"]';
+export const HOST_GEO_CITY_NAME_CHECKBOX = '[data-test-subj="field-host.geo.city_name-checkbox"]';
 
 export const HOST_GEO_CITY_NAME_HEADER = '[data-test-subj="header-text-host.geo.city_name"]';
 
-export const HOST_GEO_CITY_NAME_CHECKBOX = '[data-test-subj="field-host.geo.city_name-checkbox"]';
+export const HOST_GEO_COUNTRY_NAME_CHECKBOX =
+  '[data-test-subj="field-host.geo.country_name-checkbox"]';
 
 export const HOST_GEO_COUNTRY_NAME_HEADER = '[data-test-subj="header-text-host.geo.country_name"]';
 
-export const HOST_GEO_COUNTRY_NAME_CHECKBOX =
-  '[data-test-subj="field-host.geo.country_name-checkbox"]';
+export const INSPECT_MODAL = '[data-test-subj="modal-inspect-euiModal"]';
 
-export const FIELDS_BROWSER_CONTAINER = '[data-test-subj="fields-browser-container"]';
+export const INSPECT_QUERY =
+  '[data-test-subj="events-viewer-panel"] [data-test-subj="inspect-icon-button"]';
+
+export const LOCAL_EVENTS_COUNT =
+  '[data-test-subj="events-viewer-panel"] [data-test-subj="local-events-count"';
 
-export const EVENTS_VIEWER_FIELDS_BUTTON = `${EVENTS_VIEWER_PANEL} [data-test-subj="show-field-browser-gear"]`;
+export const LOAD_MORE =
+  '[data-test-subj="events-viewer-panel"] [data-test-subj="TimelineMoreButton"';
 
-export const RESET_FIELDS = `${EVENTS_VIEWER_PANEL} [data-test-subj="reset-fields"]`;
+export const RESET_FIELDS =
+  '[data-test-subj="events-viewer-panel"] [data-test-subj="reset-fields"]';
+
+export const SERVER_SIDE_EVENT_COUNT = '[data-test-subj="server-side-event-count"]';
diff --git a/x-pack/legacy/plugins/siem/cypress/screens/hosts/fields_browser.ts b/x-pack/legacy/plugins/siem/cypress/screens/hosts/fields_browser.ts
deleted file mode 100644
index 252fa7d44a7c7..0000000000000
--- a/x-pack/legacy/plugins/siem/cypress/screens/hosts/fields_browser.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-/** Clicking this button in the timeline opens the Fields browser */
-
-/** The title displayed in the fields browser (i.e. Customize Columns) */
-export const FIELDS_BROWSER_TITLE = '[data-test-subj="field-browser-title"]';
-
-/** Contains the body of the fields browser */
-export const FIELDS_BROWSER_CONTAINER = '[data-test-subj="fields-browser-container"]';
-
-/** The title of the selected category in the right-hand side of the fields browser */
-export const FIELDS_BROWSER_SELECTED_CATEGORY_TITLE = '[data-test-subj="selected-category-title"]';
-
-export const FIELDS_BROWSER_CHECKBOX = (id: string) => {
-  return `[data-test-subj="field-${id}-checkbox`;
-};
diff --git a/x-pack/legacy/plugins/siem/cypress/screens/hosts/main.ts b/x-pack/legacy/plugins/siem/cypress/screens/hosts/main.ts
index 25696be526e5f..3c30562680e86 100644
--- a/x-pack/legacy/plugins/siem/cypress/screens/hosts/main.ts
+++ b/x-pack/legacy/plugins/siem/cypress/screens/hosts/main.ts
@@ -4,14 +4,14 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-export const EVENTS_TAB = '[data-test-subj="navigation-events"]';
-
-export const AUTHENTICATIONS_TAB = '[data-test-subj="navigation-authentications"]';
-
-export const UNCOMMON_PROCESSES_TAB = '[data-test-subj="navigation-uncommonProcesses"]';
-
 export const ALL_HOSTS_TAB = '[data-test-subj="navigation-allHosts';
 
 export const ANOMALIES_TAB = '[data-test-subj="navigation-anomalies"]';
 
+export const AUTHENTICATIONS_TAB = '[data-test-subj="navigation-authentications"]';
+
+export const EVENTS_TAB = '[data-test-subj="navigation-events"]';
+
 export const KQL_SEARCH_BAR = '[data-test-subj="queryInput"]';
+
+export const UNCOMMON_PROCESSES_TAB = '[data-test-subj="navigation-uncommonProcesses"]';
diff --git a/x-pack/legacy/plugins/siem/cypress/screens/hosts/uncommon_processes.ts b/x-pack/legacy/plugins/siem/cypress/screens/hosts/uncommon_processes.ts
index 9e15bea79eae0..68ea8e008bfd3 100644
--- a/x-pack/legacy/plugins/siem/cypress/screens/hosts/uncommon_processes.ts
+++ b/x-pack/legacy/plugins/siem/cypress/screens/hosts/uncommon_processes.ts
@@ -4,4 +4,6 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-export const PROCESS_NAME = '[data-test-subj="draggable-content-process.name"]';
+export const PROCESS_NAME_FIELD = '[data-test-subj="draggable-content-process.name"]';
+
+export const UNCOMMON_PROCESSES_TABLE = '[data-test-subj="table-uncommonProcesses-loading-false"]';
diff --git a/x-pack/legacy/plugins/siem/cypress/screens/header.ts b/x-pack/legacy/plugins/siem/cypress/screens/siem_header.ts
similarity index 100%
rename from x-pack/legacy/plugins/siem/cypress/screens/header.ts
rename to x-pack/legacy/plugins/siem/cypress/screens/siem_header.ts
index 4ffb497a62432..cf1059269393a 100644
--- a/x-pack/legacy/plugins/siem/cypress/screens/header.ts
+++ b/x-pack/legacy/plugins/siem/cypress/screens/siem_header.ts
@@ -4,16 +4,16 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-export const KQL_INPUT = '[data-test-subj="queryInput"]';
+export const BREADCRUMBS = '[data-test-subj="breadcrumbs"] a';
 
 export const HOSTS = '[data-test-subj="navigation-hosts"]';
 
+export const KQL_INPUT = '[data-test-subj="queryInput"]';
+
 export const NETWORK = '[data-test-subj="navigation-network"]';
 
 export const OVERVIEW = '[data-test-subj="navigation-overview"]';
 
-export const TIMELINES = '[data-test-subj="navigation-timelines"]';
-
 export const REFRESH_BUTTON = '[data-test-subj="querySubmitButton"]';
 
-export const BREADCRUMBS = '[data-test-subj="breadcrumbs"] a';
+export const TIMELINES = '[data-test-subj="navigation-timelines"]';
diff --git a/x-pack/legacy/plugins/siem/cypress/screens/timeline/main.ts b/x-pack/legacy/plugins/siem/cypress/screens/timeline.ts
similarity index 89%
rename from x-pack/legacy/plugins/siem/cypress/screens/timeline/main.ts
rename to x-pack/legacy/plugins/siem/cypress/screens/timeline.ts
index 6df269b7691a8..1640647b45427 100644
--- a/x-pack/legacy/plugins/siem/cypress/screens/timeline/main.ts
+++ b/x-pack/legacy/plugins/siem/cypress/screens/timeline.ts
@@ -4,26 +4,20 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-/** The `Timeline ^` button that toggles visibility of the Timeline */
-export const TIMELINE_TOGGLE_BUTTON = '[data-test-subj="flyoutOverlay"]';
-
-/** Contains the KQL bar for searching or filtering in the timeline */
-export const SEARCH_OR_FILTER_CONTAINER =
-  '[data-test-subj="timeline-search-or-filter-search-container"]';
+export const CLOSE_TIMELINE_BTN = '[data-test-subj="close-timeline"]';
 
-export const TIMELINE_FIELDS_BUTTON =
-  '[data-test-subj="timeline"] [data-test-subj="show-field-browser"]';
+export const CREATE_NEW_TIMELINE = '[data-test-subj="timeline-new"]';
 
-/** The total server-side count of the events matching the timeline's search criteria */
-export const SERVER_SIDE_EVENT_COUNT = '[data-test-subj="server-side-event-count"]';
+export const ID_HEADER_FIELD = '[data-test-subj="timeline"] [data-test-subj="header-text-_id"]';
 
-export const TIMELINE_SETTINGS_ICON = '[data-test-subj="settings-gear"]';
+export const ID_FIELD = '[data-test-subj="timeline"] [data-test-subj="field-name-_id"]';
 
-export const TIMELINE_INSPECT_BUTTON = '[data-test-subj="inspect-empty-button"]';
+export const ID_TOGGLE_FIELD = '[data-test-subj="toggle-field-_id"]';
 
-export const CLOSE_TIMELINE_BTN = '[data-test-subj="close-timeline"]';
+export const SEARCH_OR_FILTER_CONTAINER =
+  '[data-test-subj="timeline-search-or-filter-search-container"]';
 
-export const CREATE_NEW_TIMELINE = '[data-test-subj="timeline-new"]';
+export const SERVER_SIDE_EVENT_COUNT = '[data-test-subj="server-side-event-count"]';
 
 export const TIMELINE_DATA_PROVIDERS = '[data-test-subj="dataProviders"]';
 
@@ -33,21 +27,24 @@ export const TIMELINE_DATA_PROVIDERS_EMPTY =
 export const TIMELINE_DROPPED_DATA_PROVIDERS =
   '[data-test-subj="dataProviders"] [data-test-subj="providerContainer"]';
 
+export const TIMELINE_FIELDS_BUTTON =
+  '[data-test-subj="timeline"] [data-test-subj="show-field-browser"]';
+
 export const TIMELINE_FLYOUT_BODY = '[data-test-subj="eui-flyout-body"]';
 
+export const TIMELINE_INSPECT_BUTTON = '[data-test-subj="inspect-empty-button"]';
+
 export const TIMELINE_NOT_READY_TO_DROP_BUTTON =
   '[data-test-subj="flyout-button-not-ready-to-drop"]';
 
-export const TOGGLE_TIMELINE_EXPAND_EVENT = '[data-test-subj="expand-event"]';
-
-export const TIMESTAMP_TOGGLE_FIELD = '[data-test-subj="toggle-field-@timestamp"]';
+export const TIMELINE_SETTINGS_ICON = '[data-test-subj="settings-gear"]';
 
-export const ID_TOGGLE_FIELD = '[data-test-subj="toggle-field-_id"]';
+export const TIMELINE_TITLE = '[data-test-subj="timeline-title"]';
 
 export const TIMESTAMP_HEADER_FIELD = '[data-test-subj="header-text-@timestamp"]';
 
-export const ID_HEADER_FIELD = '[data-test-subj="timeline"] [data-test-subj="header-text-_id"]';
+export const TIMELINE_TOGGLE_BUTTON = '[data-test-subj="flyoutOverlay"]';
 
-export const ID_FIELD = '[data-test-subj="timeline"] [data-test-subj="field-name-_id"]';
+export const TIMESTAMP_TOGGLE_FIELD = '[data-test-subj="toggle-field-@timestamp"]';
 
-export const TIMELINE_TITLE = '[data-test-subj="timeline-title"]';
+export const TOGGLE_TIMELINE_EXPAND_EVENT = '[data-test-subj="expand-event"]';
diff --git a/x-pack/legacy/plugins/siem/cypress/screens/uncommon_processes.ts b/x-pack/legacy/plugins/siem/cypress/screens/uncommon_processes.ts
deleted file mode 100644
index 71abaa21bf6bd..0000000000000
--- a/x-pack/legacy/plugins/siem/cypress/screens/uncommon_processes.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-export const PROCESS_NAME_FIELD = '[data-test-subj="draggable-content-process.name"]';
-export const UNCOMMON_PROCESSES_TABLE = '[data-test-subj="table-uncommonProcesses-loading-false"]';
diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/common.ts b/x-pack/legacy/plugins/siem/cypress/tasks/common.ts
index 39a61401c15b3..e02d3506b33bc 100644
--- a/x-pack/legacy/plugins/siem/cypress/tasks/common.ts
+++ b/x-pack/legacy/plugins/siem/cypress/tasks/common.ts
@@ -33,6 +33,13 @@ export const drag = (subject: JQuery<HTMLElement>) => {
     .wait(1);
 };
 
+/** Drags the subject being dragged on the specified drop target, but does not drop it  */
+export const dragWithoutDrop = (dropTarget: JQuery<HTMLElement>) => {
+  cy.wrap(dropTarget).trigger('mousemove', 'center', {
+    button: primaryButton,
+  });
+};
+
 /** "Drops" the subject being dragged on the specified drop target  */
 export const drop = (dropTarget: JQuery<HTMLElement>) => {
   cy.wrap(dropTarget)
@@ -41,10 +48,3 @@ export const drop = (dropTarget: JQuery<HTMLElement>) => {
     .trigger('mouseup', { force: true })
     .wait(1);
 };
-
-/** Drags the subject being dragged on the specified drop target, but does not drop it  */
-export const dragWithoutDrop = (dropTarget: JQuery<HTMLElement>) => {
-  cy.wrap(dropTarget).trigger('mousemove', 'center', {
-    button: primaryButton,
-  });
-};
diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/calendar.ts b/x-pack/legacy/plugins/siem/cypress/tasks/date_picker.ts
similarity index 70%
rename from x-pack/legacy/plugins/siem/cypress/tasks/calendar.ts
rename to x-pack/legacy/plugins/siem/cypress/tasks/date_picker.ts
index 16231317d6aef..9d79b73a52b08 100644
--- a/x-pack/legacy/plugins/siem/cypress/tasks/calendar.ts
+++ b/x-pack/legacy/plugins/siem/cypress/tasks/date_picker.ts
@@ -5,73 +5,67 @@
  */
 
 import {
-  DATE_PICKER_START_DATE_POPOVER_BUTTON_TIMELINE,
-  DATE_PICKER_END_DATE_POPOVER_BUTTON_TIMELINE,
-  DATE_PICKER_APPLY_BUTTON_TIMELINE,
-  DATE_PICKER_START_DATE_POPOVER_BUTTON,
   DATE_PICKER_ABSOLUTE_TAB,
   DATE_PICKER_ABSOLUTE_INPUT,
   DATE_PICKER_APPLY_BUTTON,
+  DATE_PICKER_APPLY_BUTTON_TIMELINE,
   DATE_PICKER_END_DATE_POPOVER_BUTTON,
-} from '../screens/calendar';
-
-import { DEFAULT_TIMEOUT } from '../tasks/login';
+  DATE_PICKER_END_DATE_POPOVER_BUTTON_TIMELINE,
+  DATE_PICKER_START_DATE_POPOVER_BUTTON,
+  DATE_PICKER_START_DATE_POPOVER_BUTTON_TIMELINE,
+} from '../screens/date_picker';
 
-export const setStartDate = (date: string) => {
-  cy.get(DATE_PICKER_START_DATE_POPOVER_BUTTON).click({ force: true });
+export const setEndDate = (date: string) => {
+  cy.get(DATE_PICKER_END_DATE_POPOVER_BUTTON).click({ force: true });
 
   cy.get(DATE_PICKER_ABSOLUTE_TAB)
     .first()
     .click({ force: true });
 
-  cy.get(DATE_PICKER_ABSOLUTE_INPUT, { timeout: DEFAULT_TIMEOUT })
+  cy.get(DATE_PICKER_ABSOLUTE_INPUT)
     .clear()
     .type(date);
 };
 
-export const setEndDate = (date: string) => {
-  cy.get(DATE_PICKER_END_DATE_POPOVER_BUTTON).click({ force: true });
+export const setStartDate = (date: string) => {
+  cy.get(DATE_PICKER_START_DATE_POPOVER_BUTTON).click({ force: true });
 
   cy.get(DATE_PICKER_ABSOLUTE_TAB)
     .first()
     .click({ force: true });
 
-  cy.get(DATE_PICKER_ABSOLUTE_INPUT, { timeout: DEFAULT_TIMEOUT })
+  cy.get(DATE_PICKER_ABSOLUTE_INPUT)
     .clear()
     .type(date);
 };
 
-export const updateDates = () => {
-  cy.get(DATE_PICKER_APPLY_BUTTON, { timeout: DEFAULT_TIMEOUT })
-    .click({ force: true })
-    .invoke('text', { timeout: DEFAULT_TIMEOUT })
-    .should('not.equal', 'Updating');
-};
-
-export const setTimelineStartDate = (date: string) => {
-  cy.get(DATE_PICKER_START_DATE_POPOVER_BUTTON_TIMELINE, { timeout: DEFAULT_TIMEOUT }).click({
-    force: true,
-  });
+export const setTimelineEndDate = (date: string) => {
+  cy.get(DATE_PICKER_END_DATE_POPOVER_BUTTON_TIMELINE).click({ force: true });
 
   cy.get(DATE_PICKER_ABSOLUTE_TAB)
     .first()
     .click({ force: true });
 
-  cy.get(DATE_PICKER_ABSOLUTE_INPUT, { timeout: DEFAULT_TIMEOUT }).type(
-    `{selectall}{backspace}${date}{enter}`
-  );
+  cy.get(DATE_PICKER_ABSOLUTE_INPUT).type(`{selectall}{backspace}${date}{enter}`);
 };
 
-export const setTimelineEndDate = (date: string) => {
-  cy.get(DATE_PICKER_END_DATE_POPOVER_BUTTON_TIMELINE).click({ force: true });
+export const setTimelineStartDate = (date: string) => {
+  cy.get(DATE_PICKER_START_DATE_POPOVER_BUTTON_TIMELINE).click({
+    force: true,
+  });
 
   cy.get(DATE_PICKER_ABSOLUTE_TAB)
     .first()
     .click({ force: true });
 
-  cy.get(DATE_PICKER_ABSOLUTE_INPUT, { timeout: DEFAULT_TIMEOUT }).type(
-    `{selectall}{backspace}${date}{enter}`
-  );
+  cy.get(DATE_PICKER_ABSOLUTE_INPUT).type(`{selectall}{backspace}${date}{enter}`);
+};
+
+export const updateDates = () => {
+  cy.get(DATE_PICKER_APPLY_BUTTON)
+    .click({ force: true })
+    .invoke('text')
+    .should('not.equal', 'Updating');
 };
 
 export const updateTimelineDates = () => {
diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/timeline/fields_browser.ts b/x-pack/legacy/plugins/siem/cypress/tasks/fields_browser.ts
similarity index 81%
rename from x-pack/legacy/plugins/siem/cypress/tasks/timeline/fields_browser.ts
rename to x-pack/legacy/plugins/siem/cypress/tasks/fields_browser.ts
index d30e49a25bab0..e1d2f24da424c 100644
--- a/x-pack/legacy/plugins/siem/cypress/tasks/timeline/fields_browser.ts
+++ b/x-pack/legacy/plugins/siem/cypress/tasks/fields_browser.ts
@@ -3,42 +3,27 @@
  * or more contributor license agreements. Licensed under the Elastic License;
  * you may not use this file except in compliance with the Elastic License.
  */
-import { drag, drop } from '../../integration/lib/drag_n_drop/helpers';
+import { drag, drop } from '../tasks/common';
 
 import {
   FIELDS_BROWSER_FILTER_INPUT,
-  FIELDS_BROWSER_MESSAGE_CHECKBOX,
-  FIELDS_BROWSER_HOST_GEO_CITY_NAME_CHECKBOX,
   FIELDS_BROWSER_DRAGGABLE_HOST_GEO_COUNTRY_NAME_HEADER,
   FIELDS_BROWSER_HEADER_DROP_AREA,
+  FIELDS_BROWSER_HOST_GEO_CITY_NAME_CHECKBOX,
   FIELDS_BROWSER_HOST_GEO_CONTINENT_NAME_CHECKBOX,
+  FIELDS_BROWSER_MESSAGE_CHECKBOX,
   FIELDS_BROWSER_RESET_FIELDS,
-} from '../../screens/timeline/fields_browser';
-import { DEFAULT_TIMEOUT } from '../../integration/lib/util/helpers';
-import { KQL_SEARCH_BAR } from '../../screens/hosts/main';
-
-export const clearFieldsBrowser = () => {
-  cy.get(FIELDS_BROWSER_FILTER_INPUT).type('{selectall}{backspace}');
-};
-
-export const filterFieldsBrowser = (fieldName: string) => {
-  cy.get(FIELDS_BROWSER_FILTER_INPUT, { timeout: DEFAULT_TIMEOUT })
-    .type(fieldName)
-    .should('not.have.class', 'euiFieldSearch-isLoading');
-};
-
-export const closeFieldsBrowser = () => {
-  cy.get(KQL_SEARCH_BAR, { timeout: DEFAULT_TIMEOUT }).click({ force: true });
-};
+} from '../screens/fields_browser';
+import { KQL_SEARCH_BAR } from '../screens/hosts/main';
 
-export const removesMessageField = () => {
-  cy.get(FIELDS_BROWSER_MESSAGE_CHECKBOX).uncheck({
+export const addsHostGeoCityNameToTimeline = () => {
+  cy.get(FIELDS_BROWSER_HOST_GEO_CITY_NAME_CHECKBOX).check({
     force: true,
   });
 };
 
-export const addsHostGeoCityNameToTimeline = () => {
-  cy.get(FIELDS_BROWSER_HOST_GEO_CITY_NAME_CHECKBOX).check({
+export const addsHostGeoContinentNameToTimeline = () => {
+  cy.get(FIELDS_BROWSER_HOST_GEO_CONTINENT_NAME_CHECKBOX).check({
     force: true,
   });
 };
@@ -50,8 +35,22 @@ export const addsHostGeoCountryNameToTimelineDraggingIt = () => {
   cy.get(FIELDS_BROWSER_HEADER_DROP_AREA).then(headersDropArea => drop(headersDropArea));
 };
 
-export const addsHostGeoContinentNameToTimeline = () => {
-  cy.get(FIELDS_BROWSER_HOST_GEO_CONTINENT_NAME_CHECKBOX).check({
+export const clearFieldsBrowser = () => {
+  cy.get(FIELDS_BROWSER_FILTER_INPUT).type('{selectall}{backspace}');
+};
+
+export const closeFieldsBrowser = () => {
+  cy.get(KQL_SEARCH_BAR).click({ force: true });
+};
+
+export const filterFieldsBrowser = (fieldName: string) => {
+  cy.get(FIELDS_BROWSER_FILTER_INPUT)
+    .type(fieldName)
+    .should('not.have.class', 'euiFieldSearch-isLoading');
+};
+
+export const removesMessageField = () => {
+  cy.get(FIELDS_BROWSER_MESSAGE_CHECKBOX).uncheck({
     force: true,
   });
 };
diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/hosts/all_hosts.ts b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/all_hosts.ts
index 7146c132db4a0..312df96df1ddf 100644
--- a/x-pack/legacy/plugins/siem/cypress/tasks/hosts/all_hosts.ts
+++ b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/all_hosts.ts
@@ -5,16 +5,9 @@
  */
 
 import { ALL_HOSTS_TABLE, HOSTS_NAMES_DRAGGABLE, HOSTS_NAMES } from '../../screens/hosts/all_hosts';
-import {
-  TIMELINE_DATA_PROVIDERS,
-  TIMELINE_DATA_PROVIDERS_EMPTY,
-} from '../../screens/timeline/main';
-import { DEFAULT_TIMEOUT } from '../../tasks/login';
-import { drag, drop, dragWithoutDrop } from '../../tasks/common';
+import { TIMELINE_DATA_PROVIDERS, TIMELINE_DATA_PROVIDERS_EMPTY } from '../../screens/timeline';
 
-export const waitForAllHostsToBeLoaded = () => {
-  cy.get(ALL_HOSTS_TABLE, { timeout: DEFAULT_TIMEOUT }).should('exist');
-};
+import { drag, dragWithoutDrop, drop } from '../../tasks/common';
 
 export const dragAndDropFirstHostToTimeline = () => {
   cy.get(HOSTS_NAMES_DRAGGABLE)
@@ -23,12 +16,6 @@ export const dragAndDropFirstHostToTimeline = () => {
   cy.get(TIMELINE_DATA_PROVIDERS).then(dataProvidersDropArea => drop(dataProvidersDropArea));
 };
 
-export const dragFirstHostToTimeline = () => {
-  cy.get(HOSTS_NAMES_DRAGGABLE)
-    .first()
-    .then(host => drag(host));
-};
-
 export const dragFirstHostToEmptyTimelineDataProviders = () => {
   cy.get(HOSTS_NAMES_DRAGGABLE)
     .first()
@@ -39,8 +26,17 @@ export const dragFirstHostToEmptyTimelineDataProviders = () => {
   );
 };
 
+export const dragFirstHostToTimeline = () => {
+  cy.get(HOSTS_NAMES_DRAGGABLE)
+    .first()
+    .then(host => drag(host));
+};
 export const openFirstHostDetails = () => {
   cy.get(HOSTS_NAMES)
     .first()
     .click({ force: true });
 };
+
+export const waitForAllHostsToBeLoaded = () => {
+  cy.get(ALL_HOSTS_TABLE).should('exist');
+};
diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/authentications.ts b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/authentications.ts
similarity index 60%
rename from x-pack/legacy/plugins/siem/cypress/tasks/authentications.ts
rename to x-pack/legacy/plugins/siem/cypress/tasks/hosts/authentications.ts
index 6fa4bf72ca2b2..f5f15150e8ac3 100644
--- a/x-pack/legacy/plugins/siem/cypress/tasks/authentications.ts
+++ b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/authentications.ts
@@ -4,9 +4,8 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { AUTHENTICATIONS_TABLE } from '../screens/authentications';
-import { DEFAULT_TIMEOUT } from '../tasks/login';
+import { AUTHENTICATIONS_TABLE } from '../../screens/hosts/authentications';
 
 export const waitForAuthenticationsToBeLoaded = () => {
-  cy.get(AUTHENTICATIONS_TABLE, { timeout: DEFAULT_TIMEOUT }).should('exist');
+  cy.get(AUTHENTICATIONS_TABLE).should('exist');
 };
diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/hosts/events.ts b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/events.ts
index d84d62f82d2e6..de0c99bd31dff 100644
--- a/x-pack/legacy/plugins/siem/cypress/tasks/hosts/events.ts
+++ b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/events.ts
@@ -4,37 +4,18 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { DEFAULT_TIMEOUT } from '../../integration/lib/util/helpers';
 import {
-  EVENTS_VIEWER_FIELDS_BUTTON,
   CLOSE_MODAL,
-  INSPECT_QUERY,
-  SERVER_SIDE_EVENT_COUNT,
+  EVENTS_VIEWER_FIELDS_BUTTON,
+  FIELDS_BROWSER_CONTAINER,
   HOST_GEO_CITY_NAME_CHECKBOX,
   HOST_GEO_COUNTRY_NAME_CHECKBOX,
-  FIELDS_BROWSER_CONTAINER,
-  RESET_FIELDS,
+  INSPECT_QUERY,
   LOAD_MORE,
+  RESET_FIELDS,
+  SERVER_SIDE_EVENT_COUNT,
 } from '../../screens/hosts/events';
 
-export const closeModal = () => {
-  cy.get(CLOSE_MODAL, { timeout: DEFAULT_TIMEOUT }).click();
-};
-
-export const opensInspectQueryModal = () => {
-  cy.get(INSPECT_QUERY, { timeout: DEFAULT_TIMEOUT })
-    .should('exist')
-    .trigger('mousemove', { force: true })
-    .click({ force: true });
-};
-
-export const waitsForEventsToBeLoaded = () => {
-  cy.get(SERVER_SIDE_EVENT_COUNT, { timeout: DEFAULT_TIMEOUT })
-    .should('exist')
-    .invoke('text', { timeout: DEFAULT_TIMEOUT })
-    .should('not.equal', '0');
-};
-
 export const addsHostGeoCityNameToHeader = () => {
   cy.get(HOST_GEO_CITY_NAME_CHECKBOX).check({
     force: true,
@@ -47,20 +28,38 @@ export const addsHostGeoCountryNameToHeader = () => {
   });
 };
 
-export const resetFields = () => {
-  cy.get(RESET_FIELDS).click({ force: true });
+export const closeModal = () => {
+  cy.get(CLOSE_MODAL).click();
+};
+
+export const loadMoreEvents = () => {
+  cy.get(LOAD_MORE).click({ force: true });
 };
 
 export const openEventsViewerFieldsBrowser = () => {
-  cy.get(EVENTS_VIEWER_FIELDS_BUTTON, { timeout: DEFAULT_TIMEOUT }).click({ force: true });
+  cy.get(EVENTS_VIEWER_FIELDS_BUTTON).click({ force: true });
 
-  cy.get(SERVER_SIDE_EVENT_COUNT, { timeout: DEFAULT_TIMEOUT })
+  cy.get(SERVER_SIDE_EVENT_COUNT)
     .invoke('text')
     .should('not.equal', '0');
 
   cy.get(FIELDS_BROWSER_CONTAINER).should('exist');
 };
 
-export const loadMoreEvents = () => {
-  cy.get(LOAD_MORE).click({ force: true });
+export const opensInspectQueryModal = () => {
+  cy.get(INSPECT_QUERY)
+    .should('exist')
+    .trigger('mousemove', { force: true })
+    .click({ force: true });
+};
+
+export const resetFields = () => {
+  cy.get(RESET_FIELDS).click({ force: true });
+};
+
+export const waitsForEventsToBeLoaded = () => {
+  cy.get(SERVER_SIDE_EVENT_COUNT)
+    .should('exist')
+    .invoke('text')
+    .should('not.equal', '0');
 };
diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/hosts/fields_browsers.ts b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/fields_browsers.ts
deleted file mode 100644
index ae3ed2010a0ae..0000000000000
--- a/x-pack/legacy/plugins/siem/cypress/tasks/hosts/fields_browsers.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { DEFAULT_TIMEOUT } from '../../integration/lib/util/helpers';
-import { KQL_SEARCH_BAR } from '../../screens/hosts/main';
-
-export const closeFieldsBrowser = () => {
-  cy.get(KQL_SEARCH_BAR, { timeout: DEFAULT_TIMEOUT }).click();
-};
diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/hosts/main.ts b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/main.ts
index cbf410b4bc232..eba0c353fbf37 100644
--- a/x-pack/legacy/plugins/siem/cypress/tasks/hosts/main.ts
+++ b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/main.ts
@@ -4,24 +4,17 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { DEFAULT_TIMEOUT } from '../../integration/lib/util/helpers';
-
 import {
-  EVENTS_TAB,
+  ALL_HOSTS_TAB,
   AUTHENTICATIONS_TAB,
+  EVENTS_TAB,
   UNCOMMON_PROCESSES_TAB,
-  ALL_HOSTS_TAB,
 } from '../../screens/hosts/main';
 
-/** Clicks the Events tab on the hosts page */
-export const openEvents = () =>
-  cy.get(EVENTS_TAB, { timeout: DEFAULT_TIMEOUT }).click({ force: true });
+export const openAllHosts = () => cy.get(ALL_HOSTS_TAB).click({ force: true });
 
-export const openAuthentications = () =>
-  cy.get(AUTHENTICATIONS_TAB, { timeout: DEFAULT_TIMEOUT }).click({ force: true });
+export const openAuthentications = () => cy.get(AUTHENTICATIONS_TAB).click({ force: true });
 
-export const openUncommonProcesses = () =>
-  cy.get(UNCOMMON_PROCESSES_TAB, { timeout: DEFAULT_TIMEOUT }).click({ force: true });
+export const openEvents = () => cy.get(EVENTS_TAB).click({ force: true });
 
-export const openAllHosts = () =>
-  cy.get(ALL_HOSTS_TAB, { timeout: DEFAULT_TIMEOUT }).click({ force: true });
+export const openUncommonProcesses = () => cy.get(UNCOMMON_PROCESSES_TAB).click({ force: true });
diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/uncommon_processes.ts b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/uncommon_processes.ts
similarity index 59%
rename from x-pack/legacy/plugins/siem/cypress/tasks/uncommon_processes.ts
rename to x-pack/legacy/plugins/siem/cypress/tasks/hosts/uncommon_processes.ts
index 007a20c770ca0..c44249acdd964 100644
--- a/x-pack/legacy/plugins/siem/cypress/tasks/uncommon_processes.ts
+++ b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/uncommon_processes.ts
@@ -4,9 +4,8 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { UNCOMMON_PROCESSES_TABLE } from '../screens/uncommon_processes';
-import { DEFAULT_TIMEOUT } from '../tasks/login';
+import { UNCOMMON_PROCESSES_TABLE } from '../../screens/hosts/uncommon_processes';
 
 export const waitForUncommonProcessesToBeLoaded = () => {
-  cy.get(UNCOMMON_PROCESSES_TABLE, { timeout: DEFAULT_TIMEOUT }).should('exist');
+  cy.get(UNCOMMON_PROCESSES_TABLE).should('exist');
 };
diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/inspect.ts b/x-pack/legacy/plugins/siem/cypress/tasks/inspect.ts
index 26b1c0f7e4e39..109cd9c477968 100644
--- a/x-pack/legacy/plugins/siem/cypress/tasks/inspect.ts
+++ b/x-pack/legacy/plugins/siem/cypress/tasks/inspect.ts
@@ -6,24 +6,20 @@
 
 import { INSPECT_BUTTON_ICON, InspectButtonMetadata } from '../screens/inspect';
 
-import { DEFAULT_TIMEOUT } from '../tasks/login';
-
 export const closesModal = () => {
-  cy.get('[data-test-subj="modal-inspect-close"]', { timeout: DEFAULT_TIMEOUT }).click();
+  cy.get('[data-test-subj="modal-inspect-close"]').click();
 };
 
 export const openStatsAndTables = (table: InspectButtonMetadata) => {
   if (table.tabId) {
     cy.get(table.tabId).click({ force: true });
   }
-  cy.get(table.id, { timeout: DEFAULT_TIMEOUT });
+  cy.get(table.id);
   if (table.altInspectId) {
-    cy.get(table.altInspectId, { timeout: DEFAULT_TIMEOUT }).trigger('click', {
+    cy.get(table.altInspectId).trigger('click', {
       force: true,
     });
   } else {
-    cy.get(`${table.id} ${INSPECT_BUTTON_ICON}`, {
-      timeout: DEFAULT_TIMEOUT,
-    }).trigger('click', { force: true });
+    cy.get(`${table.id} ${INSPECT_BUTTON_ICON}`).trigger('click', { force: true });
   }
 };
diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/login.ts b/x-pack/legacy/plugins/siem/cypress/tasks/login.ts
index d5bf1178d5bdd..1b982d56d79a4 100644
--- a/x-pack/legacy/plugins/siem/cypress/tasks/login.ts
+++ b/x-pack/legacy/plugins/siem/cypress/tasks/login.ts
@@ -4,10 +4,115 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { login } from '../integration/lib/login/helpers';
+import * as yaml from 'js-yaml';
 
-/** The default time in ms to wait for a Cypress command to complete */
-export const DEFAULT_TIMEOUT = 30 * 1000;
+/**
+ * Credentials in the `kibana.dev.yml` config file will be used to authenticate
+ * with Kibana when credentials are not provided via environment variables
+ */
+const KIBANA_DEV_YML_PATH = '../../../../config/kibana.dev.yml';
+
+/**
+ * The configuration path in `kibana.dev.yml` to the username to be used when
+ * authenticating with Kibana.
+ */
+const ELASTICSEARCH_USERNAME_CONFIG_PATH = 'config.elasticsearch.username';
+
+/**
+ * The configuration path in `kibana.dev.yml` to the password to be used when
+ * authenticating with Kibana.
+ */
+const ELASTICSEARCH_PASSWORD_CONFIG_PATH = 'config.elasticsearch.password';
+
+/**
+ * The `CYPRESS_ELASTICSEARCH_USERNAME` environment variable specifies the
+ * username to be used when authenticating with Kibana
+ */
+const ELASTICSEARCH_USERNAME = 'ELASTICSEARCH_USERNAME';
+
+/**
+ * The `CYPRESS_ELASTICSEARCH_PASSWORD` environment variable specifies the
+ * username to be used when authenticating with Kibana
+ */
+const ELASTICSEARCH_PASSWORD = 'ELASTICSEARCH_PASSWORD';
+
+/**
+ * The Kibana server endpoint used for authentication
+ */
+const LOGIN_API_ENDPOINT = '/internal/security/login';
+
+/**
+ * Authenticates with Kibana using, if specified, credentials specified by
+ * environment variables. The credentials in `kibana.dev.yml` will be used
+ * for authentication when the environment variables are unset.
+ *
+ * To speed the execution of tests, prefer this non-interactive authentication,
+ * which is faster than authentication via Kibana's interactive login page.
+ */
+export const login = () => {
+  if (credentialsProvidedByEnvironment()) {
+    loginViaEnvironmentCredentials();
+  } else {
+    loginViaConfig();
+  }
+};
+
+/**
+ * Returns `true` if the credentials used to login to Kibana are provided
+ * via environment variables
+ */
+const credentialsProvidedByEnvironment = (): boolean =>
+  Cypress.env(ELASTICSEARCH_USERNAME) != null && Cypress.env(ELASTICSEARCH_PASSWORD) != null;
+
+/**
+ * Authenticates with Kibana by reading credentials from the
+ * `CYPRESS_ELASTICSEARCH_USERNAME` and `CYPRESS_ELASTICSEARCH_PASSWORD`
+ * environment variables, and POSTing the username and password directly to
+ * Kibana's `/internal/security/login` endpoint, bypassing the login page (for speed).
+ */
+const loginViaEnvironmentCredentials = () => {
+  cy.log(
+    `Authenticating via environment credentials from the \`CYPRESS_${ELASTICSEARCH_USERNAME}\` and \`CYPRESS_${ELASTICSEARCH_PASSWORD}\` environment variables`
+  );
+
+  // programmatically authenticate without interacting with the Kibana login page
+  cy.request({
+    body: {
+      username: Cypress.env(ELASTICSEARCH_USERNAME),
+      password: Cypress.env(ELASTICSEARCH_PASSWORD),
+    },
+    headers: { 'kbn-xsrf': 'cypress-creds-via-env' },
+    method: 'POST',
+    url: `${Cypress.config().baseUrl}${LOGIN_API_ENDPOINT}`,
+  });
+};
+
+/**
+ * Authenticates with Kibana by reading credentials from the
+ * `kibana.dev.yml` file and POSTing the username and password directly to
+ * Kibana's `/internal/security/login` endpoint, bypassing the login page (for speed).
+ */
+const loginViaConfig = () => {
+  cy.log(
+    `Authenticating via config credentials \`${ELASTICSEARCH_USERNAME_CONFIG_PATH}\` and \`${ELASTICSEARCH_PASSWORD_CONFIG_PATH}\` from \`${KIBANA_DEV_YML_PATH}\``
+  );
+
+  // read the login details from `kibana.dev.yaml`
+  cy.readFile(KIBANA_DEV_YML_PATH).then(kibanaDevYml => {
+    const config = yaml.safeLoad(kibanaDevYml);
+
+    // programmatically authenticate without interacting with the Kibana login page
+    cy.request({
+      body: {
+        username: config.elasticsearch.username,
+        password: config.elasticsearch.password,
+      },
+      headers: { 'kbn-xsrf': 'cypress-creds-via-config' },
+      method: 'POST',
+      url: `${Cypress.config().baseUrl}${LOGIN_API_ENDPOINT}`,
+    });
+  });
+};
 
 /**
  * Authenticates with Kibana, visits the specified `url`, and waits for the
@@ -20,5 +125,5 @@ export const loginAndWaitForPage = (url: string) => {
 
   cy.viewport('macbook-15');
 
-  cy.contains('a', 'SIEM', { timeout: DEFAULT_TIMEOUT });
+  cy.contains('a', 'SIEM');
 };
diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/network/flows.ts b/x-pack/legacy/plugins/siem/cypress/tasks/network/flows.ts
index c7b031aabc8cd..c7255f3925d9e 100644
--- a/x-pack/legacy/plugins/siem/cypress/tasks/network/flows.ts
+++ b/x-pack/legacy/plugins/siem/cypress/tasks/network/flows.ts
@@ -5,8 +5,7 @@
  */
 
 import { IPS_TABLE_LOADED } from '../../screens/network/flows';
-import { DEFAULT_TIMEOUT } from '../../tasks/login';
 
 export const waitForIpsTableToBeLoaded = () => {
-  cy.get(IPS_TABLE_LOADED, { timeout: DEFAULT_TIMEOUT }).should('exist');
+  cy.get(IPS_TABLE_LOADED).should('exist');
 };
diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/header.ts b/x-pack/legacy/plugins/siem/cypress/tasks/siem_header.ts
similarity index 68%
rename from x-pack/legacy/plugins/siem/cypress/tasks/header.ts
rename to x-pack/legacy/plugins/siem/cypress/tasks/siem_header.ts
index 4067779136d9e..4c43948445c58 100644
--- a/x-pack/legacy/plugins/siem/cypress/tasks/header.ts
+++ b/x-pack/legacy/plugins/siem/cypress/tasks/siem_header.ts
@@ -4,26 +4,25 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { DEFAULT_TIMEOUT } from '../tasks/login';
-import { KQL_INPUT, REFRESH_BUTTON } from '../screens/header';
-
-export const navigateFromHeaderTo = (page: string) => {
-  cy.get(page).click({ force: true });
-};
+import { KQL_INPUT, REFRESH_BUTTON } from '../screens/siem_header';
 
 export const clearSearchBar = () => {
-  cy.get(KQL_INPUT, { timeout: DEFAULT_TIMEOUT })
+  cy.get(KQL_INPUT)
     .clear()
     .type('{enter}');
 };
 
+export const kqlSearch = (search: string) => {
+  cy.get(KQL_INPUT).type(search);
+};
+
+export const navigateFromHeaderTo = (page: string) => {
+  cy.get(page).click({ force: true });
+};
+
 export const refreshPage = () => {
   cy.get(REFRESH_BUTTON)
     .click({ force: true })
-    .invoke('text', { timeout: DEFAULT_TIMEOUT })
+    .invoke('text')
     .should('not.equal', 'Updating');
 };
-
-export const kqlSearch = (search: string) => {
-  cy.get(KQL_INPUT, { timeout: DEFAULT_TIMEOUT }).type(search);
-};
diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/siem_main.ts b/x-pack/legacy/plugins/siem/cypress/tasks/siem_main.ts
index 8501bb3d94e26..2bdc62ecbdc03 100644
--- a/x-pack/legacy/plugins/siem/cypress/tasks/siem_main.ts
+++ b/x-pack/legacy/plugins/siem/cypress/tasks/siem_main.ts
@@ -5,7 +5,10 @@
  */
 
 import { MAIN_PAGE, TIMELINE_TOGGLE_BUTTON } from '../screens/siem_main';
-import { DEFAULT_TIMEOUT } from '../tasks/login';
+
+export const openTimeline = () => {
+  cy.get(TIMELINE_TOGGLE_BUTTON).click();
+};
 
 export const openTimelineIfClosed = () => {
   cy.get(MAIN_PAGE).then($page => {
@@ -14,7 +17,3 @@ export const openTimelineIfClosed = () => {
     }
   });
 };
-
-export const openTimeline = () => {
-  cy.get(TIMELINE_TOGGLE_BUTTON, { timeout: DEFAULT_TIMEOUT }).click();
-};
diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/timeline/main.ts b/x-pack/legacy/plugins/siem/cypress/tasks/timeline.ts
similarity index 79%
rename from x-pack/legacy/plugins/siem/cypress/tasks/timeline/main.ts
rename to x-pack/legacy/plugins/siem/cypress/tasks/timeline.ts
index 80b5e4379212c..76acdad990a7e 100644
--- a/x-pack/legacy/plugins/siem/cypress/tasks/timeline/main.ts
+++ b/x-pack/legacy/plugins/siem/cypress/tasks/timeline.ts
@@ -4,78 +4,74 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { DEFAULT_TIMEOUT } from '../../integration/lib/util/helpers';
-
 import {
+  CLOSE_TIMELINE_BTN,
+  CREATE_NEW_TIMELINE,
+  ID_FIELD,
+  ID_HEADER_FIELD,
+  ID_TOGGLE_FIELD,
   SEARCH_OR_FILTER_CONTAINER,
-  TIMELINE_FIELDS_BUTTON,
   SERVER_SIDE_EVENT_COUNT,
-  TIMELINE_SETTINGS_ICON,
+  TIMELINE_FIELDS_BUTTON,
   TIMELINE_INSPECT_BUTTON,
-  CREATE_NEW_TIMELINE,
-  CLOSE_TIMELINE_BTN,
-  TOGGLE_TIMELINE_EXPAND_EVENT,
-  TIMESTAMP_TOGGLE_FIELD,
-  ID_TOGGLE_FIELD,
-  ID_HEADER_FIELD,
-  ID_FIELD,
+  TIMELINE_SETTINGS_ICON,
   TIMELINE_TITLE,
-} from '../../screens/timeline/main';
+  TIMESTAMP_TOGGLE_FIELD,
+  TOGGLE_TIMELINE_EXPAND_EVENT,
+} from '../screens/timeline';
 
-import { drag, drop } from '../../tasks/common';
+import { drag, drop } from '../tasks/common';
 
 export const hostExistsQuery = 'host.name: *';
 
-export const populateTimeline = () => {
-  executeTimelineKQL(hostExistsQuery);
-  cy.get(SERVER_SIDE_EVENT_COUNT, { timeout: DEFAULT_TIMEOUT })
-    .invoke('text')
-    .should('be.above', 0);
+export const checkIdToggleField = () => {
+  cy.get(ID_TOGGLE_FIELD).should('not.exist');
+
+  cy.get(ID_TOGGLE_FIELD).check({
+    force: true,
+  });
 };
 
-export const openTimelineFieldsBrowser = () => {
-  cy.get(TIMELINE_FIELDS_BUTTON).click({ force: true });
+export const createNewTimeline = () => {
+  cy.get(TIMELINE_SETTINGS_ICON).click({ force: true });
+  cy.get(CREATE_NEW_TIMELINE).click();
+  cy.get(CLOSE_TIMELINE_BTN).click({ force: true });
 };
 
 export const executeTimelineKQL = (query: string) => {
   cy.get(`${SEARCH_OR_FILTER_CONTAINER} input`).type(`${query} {enter}`);
 };
 
-export const openTimelineSettings = () => {
-  cy.get(TIMELINE_SETTINGS_ICON).trigger('click', { force: true });
+export const expandFirstTimelineEventDetails = () => {
+  cy.get(TOGGLE_TIMELINE_EXPAND_EVENT)
+    .first()
+    .click({ force: true });
+};
+
+export const openTimelineFieldsBrowser = () => {
+  cy.get(TIMELINE_FIELDS_BUTTON).click({ force: true });
 };
 
 export const openTimelineInspectButton = () => {
-  cy.get(TIMELINE_INSPECT_BUTTON, { timeout: DEFAULT_TIMEOUT }).should('not.be.disabled');
+  cy.get(TIMELINE_INSPECT_BUTTON).should('not.be.disabled');
   cy.get(TIMELINE_INSPECT_BUTTON).trigger('click', { force: true });
 };
 
-export const createNewTimeline = () => {
-  cy.get(TIMELINE_SETTINGS_ICON).click({ force: true });
-  cy.get(CREATE_NEW_TIMELINE).click();
-  cy.get(CLOSE_TIMELINE_BTN).click({ force: true });
+export const openTimelineSettings = () => {
+  cy.get(TIMELINE_SETTINGS_ICON).trigger('click', { force: true });
 };
 
-export const expandFirstTimelineEventDetails = () => {
-  cy.get(TOGGLE_TIMELINE_EXPAND_EVENT, { timeout: DEFAULT_TIMEOUT })
-    .first()
-    .click({ force: true });
+export const populateTimeline = () => {
+  executeTimelineKQL(hostExistsQuery);
+  cy.get(SERVER_SIDE_EVENT_COUNT)
+    .invoke('text')
+    .should('be.above', 0);
 };
 
 export const uncheckTimestampToggleField = () => {
   cy.get(TIMESTAMP_TOGGLE_FIELD).should('exist');
 
-  cy.get(TIMESTAMP_TOGGLE_FIELD, {
-    timeout: DEFAULT_TIMEOUT,
-  }).uncheck({ force: true });
-};
-
-export const checkIdToggleField = () => {
-  cy.get(ID_TOGGLE_FIELD).should('not.exist');
-
-  cy.get(ID_TOGGLE_FIELD).check({
-    force: true,
-  });
+  cy.get(TIMESTAMP_TOGGLE_FIELD).uncheck({ force: true });
 };
 
 export const dragAndDropIdToggleFieldToTimeline = () => {
@@ -89,5 +85,5 @@ export const dragAndDropIdToggleFieldToTimeline = () => {
 };
 
 export const addNameToTimeline = (name: string) => {
-  cy.get(TIMELINE_TITLE, { timeout: DEFAULT_TIMEOUT }).type(name);
+  cy.get(TIMELINE_TITLE).type(name);
 };
diff --git a/x-pack/legacy/plugins/siem/cypress/urls/navigation.ts b/x-pack/legacy/plugins/siem/cypress/urls/navigation.ts
index 164a117b82475..8fdc939e7ee51 100644
--- a/x-pack/legacy/plugins/siem/cypress/urls/navigation.ts
+++ b/x-pack/legacy/plugins/siem/cypress/urls/navigation.ts
@@ -4,8 +4,6 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-export const TIMELINES_PAGE = '/app/siem#/timelines';
-export const OVERVIEW_PAGE = '/app/siem#/overview';
 export const HOSTS_PAGE = '/app/siem#/hosts/allHosts';
 export const HOSTS_PAGE_TAB_URLS = {
   allHosts: '/app/siem#/hosts/allHosts',
@@ -14,3 +12,6 @@ export const HOSTS_PAGE_TAB_URLS = {
   events: '/app/siem#/hosts/events',
   uncommonProcesses: '/app/siem#/hosts/uncommonProcesses',
 };
+export const NETWORK_PAGE = '/app/siem#/network';
+export const OVERVIEW_PAGE = '/app/siem#/overview';
+export const TIMELINES_PAGE = '/app/siem#/timelines';

From 32a4a91776c8a9a0075c7ea63f93c06ddde80af2 Mon Sep 17 00:00:00 2001
From: Alexey Antonov <alexwizp@gmail.com>
Date: Fri, 21 Feb 2020 11:24:00 +0300
Subject: [PATCH 118/174] [ui/agg_response/tabify] update types for
 search/expressions/build_tabular_inspector_data.ts (#58130)

Part of #57660
---
 .../expressions/build_tabular_inspector_data.ts | 16 ++--------------
 .../public/search/tabify/get_columns.test.ts    |  5 +++--
 .../data/public/search/tabify/get_columns.ts    | 14 ++++----------
 .../data/public/search/tabify/index.ts          |  2 ++
 .../public/search/tabify/response_writer.ts     | 16 +++++++---------
 .../data/public/search/tabify/types.ts          | 17 +++++++++++++++++
 6 files changed, 35 insertions(+), 35 deletions(-)

diff --git a/src/legacy/core_plugins/data/public/search/expressions/build_tabular_inspector_data.ts b/src/legacy/core_plugins/data/public/search/expressions/build_tabular_inspector_data.ts
index 8f7953c408a97..e85e9deff6ddf 100644
--- a/src/legacy/core_plugins/data/public/search/expressions/build_tabular_inspector_data.ts
+++ b/src/legacy/core_plugins/data/public/search/expressions/build_tabular_inspector_data.ts
@@ -22,20 +22,8 @@ import { set } from 'lodash';
 import { FormattedData } from '../../../../../../plugins/inspector/public';
 // @ts-ignore
 import { createFilter } from './create_filter';
-interface Column {
-  id: string;
-  name: string;
-  aggConfig: any;
-}
-
-interface Row {
-  [key: string]: any;
-}
 
-interface Table {
-  columns: Column[];
-  rows: Row[];
-}
+import { TabbedTable } from '../tabify';
 
 /**
  * @deprecated
@@ -52,7 +40,7 @@ interface Table {
  * inspector. It will only be called when the data view in the inspector is opened.
  */
 export async function buildTabularInspectorData(
-  table: Table,
+  table: TabbedTable,
   queryFilter: { addFilters: (filter: any) => void }
 ) {
   const aggConfigs = table.columns.map(column => column.aggConfig);
diff --git a/src/legacy/core_plugins/data/public/search/tabify/get_columns.test.ts b/src/legacy/core_plugins/data/public/search/tabify/get_columns.test.ts
index 0328e87d8b832..cfd4cd7de640b 100644
--- a/src/legacy/core_plugins/data/public/search/tabify/get_columns.test.ts
+++ b/src/legacy/core_plugins/data/public/search/tabify/get_columns.test.ts
@@ -17,8 +17,9 @@
  * under the License.
  */
 
-import { tabifyGetColumns, AggColumn } from './get_columns';
+import { tabifyGetColumns } from './get_columns';
 import { AggConfigs, AggGroupNames, Schemas } from '../aggs';
+import { TabbedAggColumn } from './types';
 
 jest.mock('ui/new_platform');
 
@@ -140,7 +141,7 @@ describe('get columns', () => {
       false
     );
 
-    function checkColumns(column: AggColumn, i: number) {
+    function checkColumns(column: TabbedAggColumn, i: number) {
       expect(column).toHaveProperty('aggConfig');
 
       switch (i) {
diff --git a/src/legacy/core_plugins/data/public/search/tabify/get_columns.ts b/src/legacy/core_plugins/data/public/search/tabify/get_columns.ts
index 54f09f6c6364f..8bffca65b4ae2 100644
--- a/src/legacy/core_plugins/data/public/search/tabify/get_columns.ts
+++ b/src/legacy/core_plugins/data/public/search/tabify/get_columns.ts
@@ -19,14 +19,8 @@
 
 import { groupBy } from 'lodash';
 import { IAggConfig } from '../aggs';
-
-export interface AggColumn {
-  aggConfig: IAggConfig;
-  id: string;
-  name: string;
-}
-
-const getColumn = (agg: IAggConfig, i: number): AggColumn => {
+import { TabbedAggColumn } from './types';
+const getColumn = (agg: IAggConfig, i: number): TabbedAggColumn => {
   return {
     aggConfig: agg,
     id: `col-${i}-${agg.id}`,
@@ -40,14 +34,14 @@ const getColumn = (agg: IAggConfig, i: number): AggColumn => {
  * @param {AggConfigs} aggs - the agg configs object to which the aggregation response correlates
  * @param {boolean} minimalColumns - setting to true will only return a column for the last bucket/metric instead of one for each level
  */
-export function tabifyGetColumns(aggs: IAggConfig[], minimalColumns: boolean): AggColumn[] {
+export function tabifyGetColumns(aggs: IAggConfig[], minimalColumns: boolean): TabbedAggColumn[] {
   // pick the columns
   if (minimalColumns) {
     return aggs.map((agg, i) => getColumn(agg, i));
   }
 
   // supposed to be bucket,...metrics,bucket,...metrics
-  const columns: AggColumn[] = [];
+  const columns: TabbedAggColumn[] = [];
 
   // separate the metrics
   const grouped = groupBy(aggs, agg => {
diff --git a/src/legacy/core_plugins/data/public/search/tabify/index.ts b/src/legacy/core_plugins/data/public/search/tabify/index.ts
index be8d64510033c..90ac3f2fb730b 100644
--- a/src/legacy/core_plugins/data/public/search/tabify/index.ts
+++ b/src/legacy/core_plugins/data/public/search/tabify/index.ts
@@ -19,3 +19,5 @@
 
 export { tabifyAggResponse } from './tabify';
 export { tabifyGetColumns } from './get_columns';
+
+export { TabbedTable, TabbedAggRow, TabbedAggColumn } from './types';
diff --git a/src/legacy/core_plugins/data/public/search/tabify/response_writer.ts b/src/legacy/core_plugins/data/public/search/tabify/response_writer.ts
index 4c4578e505b71..c910eda024540 100644
--- a/src/legacy/core_plugins/data/public/search/tabify/response_writer.ts
+++ b/src/legacy/core_plugins/data/public/search/tabify/response_writer.ts
@@ -19,26 +19,24 @@
 
 import { isEmpty } from 'lodash';
 import { IAggConfigs } from '../aggs/agg_configs';
-import { AggColumn, tabifyGetColumns } from './get_columns';
+import { tabifyGetColumns } from './get_columns';
 
-import { TabbedResponseWriterOptions } from './types';
+import { TabbedResponseWriterOptions, TabbedAggColumn, TabbedAggRow, TabbedTable } from './types';
 
-interface TabbedAggColumn {
+interface BufferColumn {
   id: string;
   value: string | number;
 }
 
-type TabbedAggRow = Record<TabbedAggColumn['id'], TabbedAggColumn['value']>;
-
 /**
  * Writer class that collects information about an aggregation response and
  * produces a table, or a series of tables.
  */
 export class TabbedAggResponseWriter {
-  columns: AggColumn[];
+  columns: TabbedAggColumn[];
   rows: TabbedAggRow[] = [];
-  bucketBuffer: TabbedAggColumn[] = [];
-  metricBuffer: TabbedAggColumn[] = [];
+  bucketBuffer: BufferColumn[] = [];
+  metricBuffer: BufferColumn[] = [];
 
   private readonly partialRows: boolean;
 
@@ -79,7 +77,7 @@ export class TabbedAggResponseWriter {
     }
   }
 
-  response() {
+  response(): TabbedTable {
     return {
       columns: this.columns,
       rows: this.rows,
diff --git a/src/legacy/core_plugins/data/public/search/tabify/types.ts b/src/legacy/core_plugins/data/public/search/tabify/types.ts
index 3a02a2b64f0c3..964a9d2080e7b 100644
--- a/src/legacy/core_plugins/data/public/search/tabify/types.ts
+++ b/src/legacy/core_plugins/data/public/search/tabify/types.ts
@@ -18,6 +18,7 @@
  */
 
 import { RangeFilterParams } from '../../../../../../plugins/data/public';
+import { IAggConfig } from '../aggs';
 
 /** @internal **/
 export interface TabbedRangeFilterParams extends RangeFilterParams {
@@ -30,3 +31,19 @@ export interface TabbedResponseWriterOptions {
   partialRows: boolean;
   timeRange?: { [key: string]: RangeFilterParams };
 }
+
+/** @public **/
+export interface TabbedAggColumn {
+  aggConfig: IAggConfig;
+  id: string;
+  name: string;
+}
+
+/** @public **/
+export type TabbedAggRow = Record<TabbedAggColumn['id'], string | number>;
+
+/** @public **/
+export interface TabbedTable {
+  columns: TabbedAggColumn[];
+  rows: TabbedAggRow[];
+}

From a08070ec1a75466f8913b0bfd0c5acea6ffb1226 Mon Sep 17 00:00:00 2001
From: Mikhail Shustov <restrry@gmail.com>
Date: Fri, 21 Feb 2020 09:32:23 +0100
Subject: [PATCH 119/174] Expose elasticsearch config schema (#57655)

* expose elasticsearch config schema

* update docs

* mark export as alpha since it can be deleted

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 ...erver.elasticsearchconfig._constructor_.md | 20 ++++++
 ...n-server.elasticsearchconfig.apiversion.md | 13 ++++
 ...erver.elasticsearchconfig.customheaders.md | 13 ++++
 ...er.elasticsearchconfig.healthcheckdelay.md | 13 ++++
 ...plugin-server.elasticsearchconfig.hosts.md | 13 ++++
 ...asticsearchconfig.ignoreversionmismatch.md | 13 ++++
 ...n-server.elasticsearchconfig.logqueries.md | 13 ++++
 ...ibana-plugin-server.elasticsearchconfig.md | 41 +++++++++++
 ...gin-server.elasticsearchconfig.password.md | 13 ++++
 ...-server.elasticsearchconfig.pingtimeout.md | 13 ++++
 ...ticsearchconfig.requestheaderswhitelist.md | 13 ++++
 ...rver.elasticsearchconfig.requesttimeout.md | 13 ++++
 ...server.elasticsearchconfig.shardtimeout.md | 13 ++++
 ...erver.elasticsearchconfig.sniffinterval.md | 13 ++++
 ...sticsearchconfig.sniffonconnectionfault.md | 13 ++++
 ...server.elasticsearchconfig.sniffonstart.md | 13 ++++
 ...a-plugin-server.elasticsearchconfig.ssl.md | 15 ++++
 ...gin-server.elasticsearchconfig.username.md | 13 ++++
 .../core/server/kibana-plugin-server.md       |  1 +
 .../elasticsearch/elasticsearch_config.ts     | 11 ++-
 src/core/server/elasticsearch/index.ts        |  2 +-
 src/core/server/index.ts                      | 19 ++++-
 src/core/server/server.api.md                 | 71 ++++++++++++++++++-
 23 files changed, 369 insertions(+), 6 deletions(-)
 create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig._constructor_.md
 create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.apiversion.md
 create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.customheaders.md
 create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.healthcheckdelay.md
 create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.hosts.md
 create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.ignoreversionmismatch.md
 create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.logqueries.md
 create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.md
 create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.password.md
 create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.pingtimeout.md
 create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.requestheaderswhitelist.md
 create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.requesttimeout.md
 create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.shardtimeout.md
 create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.sniffinterval.md
 create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.sniffonconnectionfault.md
 create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.sniffonstart.md
 create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.ssl.md
 create mode 100644 docs/development/core/server/kibana-plugin-server.elasticsearchconfig.username.md

diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig._constructor_.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig._constructor_.md
new file mode 100644
index 0000000000000..55e5cf74fc512
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig._constructor_.md
@@ -0,0 +1,20 @@
+<!-- Do not edit this file. It is automatically generated by API Documenter. -->
+
+[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) &gt; [(constructor)](./kibana-plugin-server.elasticsearchconfig._constructor_.md)
+
+## ElasticsearchConfig.(constructor)
+
+Constructs a new instance of the `ElasticsearchConfig` class
+
+<b>Signature:</b>
+
+```typescript
+constructor(rawConfig: ElasticsearchConfigType);
+```
+
+## Parameters
+
+|  Parameter | Type | Description |
+|  --- | --- | --- |
+|  rawConfig | <code>ElasticsearchConfigType</code> |  |
+
diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.apiversion.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.apiversion.md
new file mode 100644
index 0000000000000..097654f1fb090
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.apiversion.md
@@ -0,0 +1,13 @@
+<!-- Do not edit this file. It is automatically generated by API Documenter. -->
+
+[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) &gt; [apiVersion](./kibana-plugin-server.elasticsearchconfig.apiversion.md)
+
+## ElasticsearchConfig.apiVersion property
+
+Version of the Elasticsearch (6.7, 7.1 or `master`<!-- -->) client will be connecting to.
+
+<b>Signature:</b>
+
+```typescript
+readonly apiVersion: string;
+```
diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.customheaders.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.customheaders.md
new file mode 100644
index 0000000000000..0b3998e59c5df
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.customheaders.md
@@ -0,0 +1,13 @@
+<!-- Do not edit this file. It is automatically generated by API Documenter. -->
+
+[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) &gt; [customHeaders](./kibana-plugin-server.elasticsearchconfig.customheaders.md)
+
+## ElasticsearchConfig.customHeaders property
+
+Header names and values to send to Elasticsearch with every request. These headers cannot be overwritten by client-side headers and aren't affected by `requestHeadersWhitelist` configuration.
+
+<b>Signature:</b>
+
+```typescript
+readonly customHeaders: ElasticsearchConfigType['customHeaders'];
+```
diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.healthcheckdelay.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.healthcheckdelay.md
new file mode 100644
index 0000000000000..b5589727d80aa
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.healthcheckdelay.md
@@ -0,0 +1,13 @@
+<!-- Do not edit this file. It is automatically generated by API Documenter. -->
+
+[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) &gt; [healthCheckDelay](./kibana-plugin-server.elasticsearchconfig.healthcheckdelay.md)
+
+## ElasticsearchConfig.healthCheckDelay property
+
+The interval between health check requests Kibana sends to the Elasticsearch.
+
+<b>Signature:</b>
+
+```typescript
+readonly healthCheckDelay: Duration;
+```
diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.hosts.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.hosts.md
new file mode 100644
index 0000000000000..29770ba5e0795
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.hosts.md
@@ -0,0 +1,13 @@
+<!-- Do not edit this file. It is automatically generated by API Documenter. -->
+
+[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) &gt; [hosts](./kibana-plugin-server.elasticsearchconfig.hosts.md)
+
+## ElasticsearchConfig.hosts property
+
+Hosts that the client will connect to. If sniffing is enabled, this list will be used as seeds to discover the rest of your cluster.
+
+<b>Signature:</b>
+
+```typescript
+readonly hosts: string[];
+```
diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.ignoreversionmismatch.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.ignoreversionmismatch.md
new file mode 100644
index 0000000000000..42e32f920c1db
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.ignoreversionmismatch.md
@@ -0,0 +1,13 @@
+<!-- Do not edit this file. It is automatically generated by API Documenter. -->
+
+[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) &gt; [ignoreVersionMismatch](./kibana-plugin-server.elasticsearchconfig.ignoreversionmismatch.md)
+
+## ElasticsearchConfig.ignoreVersionMismatch property
+
+Whether to allow kibana to connect to a non-compatible elasticsearch node.
+
+<b>Signature:</b>
+
+```typescript
+readonly ignoreVersionMismatch: boolean;
+```
diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.logqueries.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.logqueries.md
new file mode 100644
index 0000000000000..64de7f6504450
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.logqueries.md
@@ -0,0 +1,13 @@
+<!-- Do not edit this file. It is automatically generated by API Documenter. -->
+
+[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) &gt; [logQueries](./kibana-plugin-server.elasticsearchconfig.logqueries.md)
+
+## ElasticsearchConfig.logQueries property
+
+Specifies whether all queries to the client should be logged (status code, method, query etc.).
+
+<b>Signature:</b>
+
+```typescript
+readonly logQueries: boolean;
+```
diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.md
new file mode 100644
index 0000000000000..e478dc7b966a2
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.md
@@ -0,0 +1,41 @@
+<!-- Do not edit this file. It is automatically generated by API Documenter. -->
+
+[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md)
+
+## ElasticsearchConfig class
+
+Wrapper of config schema.
+
+<b>Signature:</b>
+
+```typescript
+export declare class ElasticsearchConfig 
+```
+
+## Constructors
+
+|  Constructor | Modifiers | Description |
+|  --- | --- | --- |
+|  [(constructor)(rawConfig)](./kibana-plugin-server.elasticsearchconfig._constructor_.md) |  | Constructs a new instance of the <code>ElasticsearchConfig</code> class |
+
+## Properties
+
+|  Property | Modifiers | Type | Description |
+|  --- | --- | --- | --- |
+|  [apiVersion](./kibana-plugin-server.elasticsearchconfig.apiversion.md) |  | <code>string</code> | Version of the Elasticsearch (6.7, 7.1 or <code>master</code>) client will be connecting to. |
+|  [customHeaders](./kibana-plugin-server.elasticsearchconfig.customheaders.md) |  | <code>ElasticsearchConfigType['customHeaders']</code> | Header names and values to send to Elasticsearch with every request. These headers cannot be overwritten by client-side headers and aren't affected by <code>requestHeadersWhitelist</code> configuration. |
+|  [healthCheckDelay](./kibana-plugin-server.elasticsearchconfig.healthcheckdelay.md) |  | <code>Duration</code> | The interval between health check requests Kibana sends to the Elasticsearch. |
+|  [hosts](./kibana-plugin-server.elasticsearchconfig.hosts.md) |  | <code>string[]</code> | Hosts that the client will connect to. If sniffing is enabled, this list will be used as seeds to discover the rest of your cluster. |
+|  [ignoreVersionMismatch](./kibana-plugin-server.elasticsearchconfig.ignoreversionmismatch.md) |  | <code>boolean</code> | Whether to allow kibana to connect to a non-compatible elasticsearch node. |
+|  [logQueries](./kibana-plugin-server.elasticsearchconfig.logqueries.md) |  | <code>boolean</code> | Specifies whether all queries to the client should be logged (status code, method, query etc.). |
+|  [password](./kibana-plugin-server.elasticsearchconfig.password.md) |  | <code>string</code> | If Elasticsearch is protected with basic authentication, this setting provides the password that the Kibana server uses to perform its administrative functions. |
+|  [pingTimeout](./kibana-plugin-server.elasticsearchconfig.pingtimeout.md) |  | <code>Duration</code> | Timeout after which PING HTTP request will be aborted and retried. |
+|  [requestHeadersWhitelist](./kibana-plugin-server.elasticsearchconfig.requestheaderswhitelist.md) |  | <code>string[]</code> | List of Kibana client-side headers to send to Elasticsearch when request scoped cluster client is used. If this is an empty array then \*no\* client-side will be sent. |
+|  [requestTimeout](./kibana-plugin-server.elasticsearchconfig.requesttimeout.md) |  | <code>Duration</code> | Timeout after which HTTP request will be aborted and retried. |
+|  [shardTimeout](./kibana-plugin-server.elasticsearchconfig.shardtimeout.md) |  | <code>Duration</code> | Timeout for Elasticsearch to wait for responses from shards. Set to 0 to disable. |
+|  [sniffInterval](./kibana-plugin-server.elasticsearchconfig.sniffinterval.md) |  | <code>false &#124; Duration</code> | Interval to perform a sniff operation and make sure the list of nodes is complete. If <code>false</code> then sniffing is disabled. |
+|  [sniffOnConnectionFault](./kibana-plugin-server.elasticsearchconfig.sniffonconnectionfault.md) |  | <code>boolean</code> | Specifies whether the client should immediately sniff for a more current list of nodes when a connection dies. |
+|  [sniffOnStart](./kibana-plugin-server.elasticsearchconfig.sniffonstart.md) |  | <code>boolean</code> | Specifies whether the client should attempt to detect the rest of the cluster when it is first instantiated. |
+|  [ssl](./kibana-plugin-server.elasticsearchconfig.ssl.md) |  | <code>Pick&lt;SslConfigSchema, Exclude&lt;keyof SslConfigSchema, 'certificateAuthorities' &#124; 'keystore' &#124; 'truststore'&gt;&gt; &amp; {</code><br/><code>        certificateAuthorities?: string[];</code><br/><code>    }</code> | Set of settings configure SSL connection between Kibana and Elasticsearch that are required when <code>xpack.ssl.verification_mode</code> in Elasticsearch is set to either <code>certificate</code> or <code>full</code>. |
+|  [username](./kibana-plugin-server.elasticsearchconfig.username.md) |  | <code>string</code> | If Elasticsearch is protected with basic authentication, this setting provides the username that the Kibana server uses to perform its administrative functions. |
+
diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.password.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.password.md
new file mode 100644
index 0000000000000..ffe6f75e9874d
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.password.md
@@ -0,0 +1,13 @@
+<!-- Do not edit this file. It is automatically generated by API Documenter. -->
+
+[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) &gt; [password](./kibana-plugin-server.elasticsearchconfig.password.md)
+
+## ElasticsearchConfig.password property
+
+If Elasticsearch is protected with basic authentication, this setting provides the password that the Kibana server uses to perform its administrative functions.
+
+<b>Signature:</b>
+
+```typescript
+readonly password?: string;
+```
diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.pingtimeout.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.pingtimeout.md
new file mode 100644
index 0000000000000..09123f0969b60
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.pingtimeout.md
@@ -0,0 +1,13 @@
+<!-- Do not edit this file. It is automatically generated by API Documenter. -->
+
+[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) &gt; [pingTimeout](./kibana-plugin-server.elasticsearchconfig.pingtimeout.md)
+
+## ElasticsearchConfig.pingTimeout property
+
+Timeout after which PING HTTP request will be aborted and retried.
+
+<b>Signature:</b>
+
+```typescript
+readonly pingTimeout: Duration;
+```
diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.requestheaderswhitelist.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.requestheaderswhitelist.md
new file mode 100644
index 0000000000000..eeced56e3103f
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.requestheaderswhitelist.md
@@ -0,0 +1,13 @@
+<!-- Do not edit this file. It is automatically generated by API Documenter. -->
+
+[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) &gt; [requestHeadersWhitelist](./kibana-plugin-server.elasticsearchconfig.requestheaderswhitelist.md)
+
+## ElasticsearchConfig.requestHeadersWhitelist property
+
+List of Kibana client-side headers to send to Elasticsearch when request scoped cluster client is used. If this is an empty array then \*no\* client-side will be sent.
+
+<b>Signature:</b>
+
+```typescript
+readonly requestHeadersWhitelist: string[];
+```
diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.requesttimeout.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.requesttimeout.md
new file mode 100644
index 0000000000000..dbd5ecb939673
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.requesttimeout.md
@@ -0,0 +1,13 @@
+<!-- Do not edit this file. It is automatically generated by API Documenter. -->
+
+[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) &gt; [requestTimeout](./kibana-plugin-server.elasticsearchconfig.requesttimeout.md)
+
+## ElasticsearchConfig.requestTimeout property
+
+Timeout after which HTTP request will be aborted and retried.
+
+<b>Signature:</b>
+
+```typescript
+readonly requestTimeout: Duration;
+```
diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.shardtimeout.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.shardtimeout.md
new file mode 100644
index 0000000000000..aa923042bf64f
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.shardtimeout.md
@@ -0,0 +1,13 @@
+<!-- Do not edit this file. It is automatically generated by API Documenter. -->
+
+[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) &gt; [shardTimeout](./kibana-plugin-server.elasticsearchconfig.shardtimeout.md)
+
+## ElasticsearchConfig.shardTimeout property
+
+Timeout for Elasticsearch to wait for responses from shards. Set to 0 to disable.
+
+<b>Signature:</b>
+
+```typescript
+readonly shardTimeout: Duration;
+```
diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.sniffinterval.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.sniffinterval.md
new file mode 100644
index 0000000000000..37fd2a7439535
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.sniffinterval.md
@@ -0,0 +1,13 @@
+<!-- Do not edit this file. It is automatically generated by API Documenter. -->
+
+[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) &gt; [sniffInterval](./kibana-plugin-server.elasticsearchconfig.sniffinterval.md)
+
+## ElasticsearchConfig.sniffInterval property
+
+Interval to perform a sniff operation and make sure the list of nodes is complete. If `false` then sniffing is disabled.
+
+<b>Signature:</b>
+
+```typescript
+readonly sniffInterval: false | Duration;
+```
diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.sniffonconnectionfault.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.sniffonconnectionfault.md
new file mode 100644
index 0000000000000..c703be548d34b
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.sniffonconnectionfault.md
@@ -0,0 +1,13 @@
+<!-- Do not edit this file. It is automatically generated by API Documenter. -->
+
+[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) &gt; [sniffOnConnectionFault](./kibana-plugin-server.elasticsearchconfig.sniffonconnectionfault.md)
+
+## ElasticsearchConfig.sniffOnConnectionFault property
+
+Specifies whether the client should immediately sniff for a more current list of nodes when a connection dies.
+
+<b>Signature:</b>
+
+```typescript
+readonly sniffOnConnectionFault: boolean;
+```
diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.sniffonstart.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.sniffonstart.md
new file mode 100644
index 0000000000000..26a7d9cc11a80
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.sniffonstart.md
@@ -0,0 +1,13 @@
+<!-- Do not edit this file. It is automatically generated by API Documenter. -->
+
+[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) &gt; [sniffOnStart](./kibana-plugin-server.elasticsearchconfig.sniffonstart.md)
+
+## ElasticsearchConfig.sniffOnStart property
+
+Specifies whether the client should attempt to detect the rest of the cluster when it is first instantiated.
+
+<b>Signature:</b>
+
+```typescript
+readonly sniffOnStart: boolean;
+```
diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.ssl.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.ssl.md
new file mode 100644
index 0000000000000..4d23c410f59fa
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.ssl.md
@@ -0,0 +1,15 @@
+<!-- Do not edit this file. It is automatically generated by API Documenter. -->
+
+[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) &gt; [ssl](./kibana-plugin-server.elasticsearchconfig.ssl.md)
+
+## ElasticsearchConfig.ssl property
+
+Set of settings configure SSL connection between Kibana and Elasticsearch that are required when `xpack.ssl.verification_mode` in Elasticsearch is set to either `certificate` or `full`<!-- -->.
+
+<b>Signature:</b>
+
+```typescript
+readonly ssl: Pick<SslConfigSchema, Exclude<keyof SslConfigSchema, 'certificateAuthorities' | 'keystore' | 'truststore'>> & {
+        certificateAuthorities?: string[];
+    };
+```
diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.username.md b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.username.md
new file mode 100644
index 0000000000000..d0098d656befb
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.elasticsearchconfig.username.md
@@ -0,0 +1,13 @@
+<!-- Do not edit this file. It is automatically generated by API Documenter. -->
+
+[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) &gt; [username](./kibana-plugin-server.elasticsearchconfig.username.md)
+
+## ElasticsearchConfig.username property
+
+If Elasticsearch is protected with basic authentication, this setting provides the username that the Kibana server uses to perform its administrative functions.
+
+<b>Signature:</b>
+
+```typescript
+readonly username?: string;
+```
diff --git a/docs/development/core/server/kibana-plugin-server.md b/docs/development/core/server/kibana-plugin-server.md
index 482f014b226b9..9ec443d6482e8 100644
--- a/docs/development/core/server/kibana-plugin-server.md
+++ b/docs/development/core/server/kibana-plugin-server.md
@@ -19,6 +19,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
 |  [BasePath](./kibana-plugin-server.basepath.md) | Access or manipulate the Kibana base path |
 |  [ClusterClient](./kibana-plugin-server.clusterclient.md) | Represents an Elasticsearch cluster API client created by the platform. It allows to call API on behalf of the internal Kibana user and the actual user that is derived from the request headers (via <code>asScoped(...)</code>).<!-- -->See [ClusterClient](./kibana-plugin-server.clusterclient.md)<!-- -->. |
 |  [CspConfig](./kibana-plugin-server.cspconfig.md) | CSP configuration for use in Kibana. |
+|  [ElasticsearchConfig](./kibana-plugin-server.elasticsearchconfig.md) | Wrapper of config schema. |
 |  [ElasticsearchErrorHelpers](./kibana-plugin-server.elasticsearcherrorhelpers.md) | Helpers for working with errors returned from the Elasticsearch service.Since the internal data of errors are subject to change, consumers of the Elasticsearch service should always use these helpers to classify errors instead of checking error internals such as <code>body.error.header[WWW-Authenticate]</code> |
 |  [KibanaRequest](./kibana-plugin-server.kibanarequest.md) | Kibana specific abstraction for an incoming request. |
 |  [RouteValidationError](./kibana-plugin-server.routevalidationerror.md) | Error to return when the validation is not successful. |
diff --git a/src/core/server/elasticsearch/elasticsearch_config.ts b/src/core/server/elasticsearch/elasticsearch_config.ts
index 50866e5550d8e..b2f4e388b337d 100644
--- a/src/core/server/elasticsearch/elasticsearch_config.ts
+++ b/src/core/server/elasticsearch/elasticsearch_config.ts
@@ -31,7 +31,12 @@ export const DEFAULT_API_VERSION = 'master';
 export type ElasticsearchConfigType = TypeOf<typeof configSchema>;
 type SslConfigSchema = ElasticsearchConfigType['ssl'];
 
-const configSchema = schema.object({
+/**
+ * Validation schema for elasticsearch service config. It can be reused when plugins allow users
+ * to specify a local elasticsearch config.
+ * @public
+ */
+export const configSchema = schema.object({
   sniffOnStart: schema.boolean({ defaultValue: false }),
   sniffInterval: schema.oneOf([schema.duration(), schema.literal(false)], {
     defaultValue: false,
@@ -148,6 +153,10 @@ export const config: ServiceConfigDescriptor<ElasticsearchConfigType> = {
   deprecations,
 };
 
+/**
+ * Wrapper of config schema.
+ * @public
+ */
 export class ElasticsearchConfig {
   /**
    * The interval between health check requests Kibana sends to the Elasticsearch.
diff --git a/src/core/server/elasticsearch/index.ts b/src/core/server/elasticsearch/index.ts
index 5d64fadfaa184..cfd72a6fd5e47 100644
--- a/src/core/server/elasticsearch/index.ts
+++ b/src/core/server/elasticsearch/index.ts
@@ -27,7 +27,7 @@ export {
 } from './cluster_client';
 export { IScopedClusterClient, ScopedClusterClient, Headers } from './scoped_cluster_client';
 export { ElasticsearchClientConfig } from './elasticsearch_client_config';
-export { config } from './elasticsearch_config';
+export { config, configSchema, ElasticsearchConfig } from './elasticsearch_config';
 export { ElasticsearchError, ElasticsearchErrorHelpers } from './errors';
 export * from './api_types';
 export * from './types';
diff --git a/src/core/server/index.ts b/src/core/server/index.ts
index cc838ddd1351d..52827b72ee0cc 100644
--- a/src/core/server/index.ts
+++ b/src/core/server/index.ts
@@ -39,7 +39,12 @@
  * @packageDocumentation
  */
 
-import { ElasticsearchServiceSetup, IScopedClusterClient } from './elasticsearch';
+import {
+  ElasticsearchServiceSetup,
+  IScopedClusterClient,
+  configSchema as elasticsearchConfigSchema,
+} from './elasticsearch';
+
 import { HttpServiceSetup } from './http';
 import { IScopedRenderingClient } from './rendering';
 import { PluginsServiceSetup, PluginsServiceStart, PluginOpaqueId } from './plugins';
@@ -78,6 +83,7 @@ export {
   Headers,
   ScopedClusterClient,
   IScopedClusterClient,
+  ElasticsearchConfig,
   ElasticsearchClientConfig,
   ElasticsearchError,
   ElasticsearchErrorHelpers,
@@ -347,3 +353,14 @@ export {
   PluginOpaqueId,
   UuidServiceSetup,
 };
+
+/**
+ * Config schemas for the platform services.
+ *
+ * @alpha
+ */
+export const config = {
+  elasticsearch: {
+    schema: elasticsearchConfigSchema,
+  },
+};
diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md
index ad1907df571fb..053a60028fc5f 100644
--- a/src/core/server/server.api.md
+++ b/src/core/server/server.api.md
@@ -503,6 +503,49 @@ export class ClusterClient implements IClusterClient {
     close(): void;
     }
 
+// @alpha
+export const config: {
+    elasticsearch: {
+        schema: import("@kbn/config-schema").ObjectType<{
+            sniffOnStart: import("@kbn/config-schema").Type<boolean>;
+            sniffInterval: import("@kbn/config-schema").Type<false | import("moment").Duration>;
+            sniffOnConnectionFault: import("@kbn/config-schema").Type<boolean>;
+            hosts: import("@kbn/config-schema").Type<string | string[]>;
+            preserveHost: import("@kbn/config-schema").Type<boolean>;
+            username: import("@kbn/config-schema").Type<string | undefined>;
+            password: import("@kbn/config-schema").Type<string | undefined>;
+            requestHeadersWhitelist: import("@kbn/config-schema").Type<string | string[]>;
+            customHeaders: import("@kbn/config-schema").Type<Record<string, string>>;
+            shardTimeout: import("@kbn/config-schema").Type<import("moment").Duration>;
+            requestTimeout: import("@kbn/config-schema").Type<import("moment").Duration>;
+            pingTimeout: import("@kbn/config-schema").Type<import("moment").Duration>;
+            startupTimeout: import("@kbn/config-schema").Type<import("moment").Duration>;
+            logQueries: import("@kbn/config-schema").Type<boolean>;
+            ssl: import("@kbn/config-schema").ObjectType<{
+                verificationMode: import("@kbn/config-schema").Type<"none" | "full" | "certificate">;
+                certificateAuthorities: import("@kbn/config-schema").Type<string | string[] | undefined>;
+                certificate: import("@kbn/config-schema").Type<string | undefined>;
+                key: import("@kbn/config-schema").Type<string | undefined>;
+                keyPassphrase: import("@kbn/config-schema").Type<string | undefined>;
+                keystore: import("@kbn/config-schema").ObjectType<{
+                    path: import("@kbn/config-schema").Type<string | undefined>;
+                    password: import("@kbn/config-schema").Type<string | undefined>;
+                }>;
+                truststore: import("@kbn/config-schema").ObjectType<{
+                    path: import("@kbn/config-schema").Type<string | undefined>;
+                    password: import("@kbn/config-schema").Type<string | undefined>;
+                }>;
+                alwaysPresentCertificate: import("@kbn/config-schema").Type<boolean>;
+            }>;
+            apiVersion: import("@kbn/config-schema").Type<string>;
+            healthCheck: import("@kbn/config-schema").ObjectType<{
+                delay: import("@kbn/config-schema").Type<import("moment").Duration>;
+            }>;
+            ignoreVersionMismatch: import("@kbn/config-schema/target/types/types").ConditionalType<false, boolean, boolean>;
+        }>;
+    };
+};
+
 // @public
 export type ConfigDeprecation = (config: Record<string, any>, fromPath: string, logger: ConfigDeprecationLogger) => Record<string, any>;
 
@@ -650,8 +693,6 @@ export interface DiscoveredPlugin {
     readonly requiredPlugins: readonly PluginName[];
 }
 
-// Warning: (ae-forgotten-export) The symbol "ElasticsearchConfig" needs to be exported by the entry point index.d.ts
-//
 // @public (undocumented)
 export type ElasticsearchClientConfig = Pick<ConfigOptions, 'keepAlive' | 'log' | 'plugins'> & Pick<ElasticsearchConfig, 'apiVersion' | 'customHeaders' | 'logQueries' | 'requestHeadersWhitelist' | 'sniffOnStart' | 'sniffOnConnectionFault' | 'hosts' | 'username' | 'password'> & {
     pingTimeout?: ElasticsearchConfig['pingTimeout'] | ConfigOptions['pingTimeout'];
@@ -660,6 +701,31 @@ export type ElasticsearchClientConfig = Pick<ConfigOptions, 'keepAlive' | 'log'
     ssl?: Partial<ElasticsearchConfig['ssl']>;
 };
 
+// @public
+export class ElasticsearchConfig {
+    constructor(rawConfig: ElasticsearchConfigType);
+    readonly apiVersion: string;
+    // Warning: (ae-forgotten-export) The symbol "ElasticsearchConfigType" needs to be exported by the entry point index.d.ts
+    readonly customHeaders: ElasticsearchConfigType['customHeaders'];
+    readonly healthCheckDelay: Duration;
+    readonly hosts: string[];
+    readonly ignoreVersionMismatch: boolean;
+    readonly logQueries: boolean;
+    readonly password?: string;
+    readonly pingTimeout: Duration;
+    readonly requestHeadersWhitelist: string[];
+    readonly requestTimeout: Duration;
+    readonly shardTimeout: Duration;
+    readonly sniffInterval: false | Duration;
+    readonly sniffOnConnectionFault: boolean;
+    readonly sniffOnStart: boolean;
+    // Warning: (ae-forgotten-export) The symbol "SslConfigSchema" needs to be exported by the entry point index.d.ts
+    readonly ssl: Pick<SslConfigSchema, Exclude<keyof SslConfigSchema, 'certificateAuthorities' | 'keystore' | 'truststore'>> & {
+        certificateAuthorities?: string[];
+    };
+    readonly username?: string;
+}
+
 // @public (undocumented)
 export interface ElasticsearchError extends Boom {
     // (undocumented)
@@ -2133,7 +2199,6 @@ export const validBodyOutput: readonly ["data", "stream"];
 // src/core/server/plugins/plugins_service.ts:44:5 - (ae-forgotten-export) The symbol "InternalPluginInfo" needs to be exported by the entry point index.d.ts
 // src/core/server/plugins/types.ts:226:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts
 // src/core/server/plugins/types.ts:226:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts
-// src/core/server/plugins/types.ts:227:3 - (ae-forgotten-export) The symbol "ElasticsearchConfigType" needs to be exported by the entry point index.d.ts
 // src/core/server/plugins/types.ts:228:3 - (ae-forgotten-export) The symbol "PathConfigType" needs to be exported by the entry point index.d.ts
 
 ```

From 5ad4c3d8bf6e9566ab7813ed55f7c01990e9e110 Mon Sep 17 00:00:00 2001
From: Mikhail Shustov <restrry@gmail.com>
Date: Fri, 21 Feb 2020 09:36:27 +0100
Subject: [PATCH 120/174] document difference between log record formats
 (#57798)

* document difference between log record formats

* add more
---
 src/core/server/logging/README.md | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/src/core/server/logging/README.md b/src/core/server/logging/README.md
index 3fbec7a45148d..ed64e7c4ce0b1 100644
--- a/src/core/server/logging/README.md
+++ b/src/core/server/logging/README.md
@@ -7,6 +7,8 @@
   - [JSON layout](#json-layout)
 - [Configuration](#configuration)
 - [Usage](#usage)
+- [Logging config migration](#logging-config-migration)
+- [Log record format changes](#log-record-format-changes)
 
 The way logging works in Kibana is inspired by `log4j 2` logging framework used by [Elasticsearch](https://www.elastic.co/guide/en/elasticsearch/reference/current/settings.html#logging).
 The main idea is to have consistent logging behaviour (configuration, log format etc.) across the entire Elastic Stack 
@@ -321,3 +323,23 @@ Define a custom logger for a specific context.
 
 #### logging.filter
 TBD
+
+### Log record format changes
+
+| Parameter       | Platform log record in **pattern** format  | Legacy Platform log record **text** format |
+| --------------- | ------------------------------------------ | ------------------------------------------ |
+| @timestamp      | ISO8601 `2012-01-31T23:33:22.011Z`         | Absolute `23:33:22.011`                    |
+| context         | `parent.child`                             | `['parent', 'child']`                      |
+| level           | `DEBUG`                                    | `['debug']`                                |
+| meta            | stringified JSON object `{"to": "v8"}`     | N/A                                        |
+| pid             | can be configured as `%pid`                | N/A                                        |
+
+| Parameter       | Platform log record in **json** format     | Legacy Platform log record **json** format   |
+| --------------- | ------------------------------------------ | -------------------------------------------- |
+| @timestamp      | ISO8601_TZ `2012-01-31T23:33:22.011-05:00` | ISO8601 `2012-01-31T23:33:22.011Z`           |
+| context         | `context: parent.child`                    | `tags: ['parent', 'child']`                  |
+| level           | `level: DEBUG`                             | `tags: ['debug']`                            |
+| meta            | separate property `"meta": {"to": "v8"}`   | merged in log record  `{... "to": "v8"}`     |
+| pid             | `pid: 12345`                               | `pid: 12345`                                 |
+| type            | N/A                                        | `type: log`                                  |
+| error           | `{ message, name, stack }`                 | `{ message, name, stack, code, signal }`     |
\ No newline at end of file

From 2aa359a65086191a9fcc3d761851ab6a0f04de4a Mon Sep 17 00:00:00 2001
From: nnamdifrankie <56440728+nnamdifrankie@users.noreply.github.com>
Date: Fri, 21 Feb 2020 08:15:48 -0500
Subject: [PATCH 121/174] [Endpoint] EMT-184: change endpoints to metadata up
 and down the code base. (#58038)

[Endpoint] EMT-184: change endpoints to metadata up and down the code base.
---
 .../store/managing/middleware.test.ts         |  2 +-
 .../endpoint/store/managing/middleware.ts     |  2 +-
 x-pack/plugins/endpoint/server/plugin.ts      |  2 +-
 .../{endpoints.test.ts => metadata.test.ts}   | 14 ++--
 .../routes/{endpoints.ts => metadata.ts}      | 14 ++--
 ...est.ts => metadata_query_builders.test.ts} | 27 ++++---
 ...builders.ts => metadata_query_builders.ts} |  4 +-
 ...oints_data.json => all_metadata_data.json} |  0
 .../api_integration/apis/endpoint/index.ts    |  2 +-
 .../endpoint/{endpoints.ts => metadata.ts}    | 80 +++++++++++++------
 .../functional/apps/endpoint/management.ts    |  4 +-
 .../api_feature/data.json                     |  0
 .../api_feature/mappings.json                 |  0
 13 files changed, 93 insertions(+), 58 deletions(-)
 rename x-pack/plugins/endpoint/server/routes/{endpoints.test.ts => metadata.test.ts} (95%)
 rename x-pack/plugins/endpoint/server/routes/{endpoints.ts => metadata.ts} (90%)
 rename x-pack/plugins/endpoint/server/services/endpoint/{endpoint_query_builders.test.ts => metadata_query_builders.test.ts} (79%)
 rename x-pack/plugins/endpoint/server/services/endpoint/{endpoint_query_builders.ts => metadata_query_builders.ts} (96%)
 rename x-pack/plugins/endpoint/server/test_data/{all_endpoints_data.json => all_metadata_data.json} (100%)
 rename x-pack/test/api_integration/apis/endpoint/{endpoints.ts => metadata.ts} (61%)
 rename x-pack/test/functional/es_archives/endpoint/{endpoints => metadata}/api_feature/data.json (100%)
 rename x-pack/test/functional/es_archives/endpoint/{endpoints => metadata}/api_feature/mappings.json (100%)

diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts
index 250cbc6e312ed..9fb12b77e7252 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts
@@ -72,7 +72,7 @@ describe('endpoint list saga', () => {
     expect(fakeHttpServices.post).not.toHaveBeenCalled();
     dispatch({ type: 'userNavigatedToPage', payload: 'managementPage' });
     await sleep();
-    expect(fakeHttpServices.post).toHaveBeenCalledWith('/api/endpoint/endpoints', {
+    expect(fakeHttpServices.post).toHaveBeenCalledWith('/api/endpoint/metadata', {
       body: JSON.stringify({
         paging_properties: [{ page_index: 0 }, { page_size: 10 }],
       }),
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.ts
index ae756caf5aa35..754a855c171ad 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.ts
@@ -18,7 +18,7 @@ export const managementMiddlewareFactory: MiddlewareFactory<ManagementListState>
     ) {
       const managementPageIndex = pageIndex(getState());
       const managementPageSize = pageSize(getState());
-      const response = await coreStart.http.post('/api/endpoint/endpoints', {
+      const response = await coreStart.http.post('/api/endpoint/metadata', {
         body: JSON.stringify({
           paging_properties: [
             { page_index: managementPageIndex },
diff --git a/x-pack/plugins/endpoint/server/plugin.ts b/x-pack/plugins/endpoint/server/plugin.ts
index afed5199b7d72..aef85f39e0382 100644
--- a/x-pack/plugins/endpoint/server/plugin.ts
+++ b/x-pack/plugins/endpoint/server/plugin.ts
@@ -10,7 +10,7 @@ import { createConfig$, EndpointConfigType } from './config';
 import { EndpointAppContext } from './types';
 
 import { addRoutes } from './routes';
-import { registerEndpointRoutes } from './routes/endpoints';
+import { registerEndpointRoutes } from './routes/metadata';
 import { registerAlertRoutes } from './routes/alerts';
 import { registerResolverRoutes } from './routes/resolver';
 
diff --git a/x-pack/plugins/endpoint/server/routes/endpoints.test.ts b/x-pack/plugins/endpoint/server/routes/metadata.test.ts
similarity index 95%
rename from x-pack/plugins/endpoint/server/routes/endpoints.test.ts
rename to x-pack/plugins/endpoint/server/routes/metadata.test.ts
index 25c4225495a41..ee374bc1b57d6 100644
--- a/x-pack/plugins/endpoint/server/routes/endpoints.test.ts
+++ b/x-pack/plugins/endpoint/server/routes/metadata.test.ts
@@ -20,9 +20,9 @@ import {
 } from '../../../../../src/core/server/mocks';
 import { EndpointMetadata, EndpointResultList } from '../../common/types';
 import { SearchResponse } from 'elasticsearch';
-import { registerEndpointRoutes } from './endpoints';
+import { registerEndpointRoutes } from './metadata';
 import { EndpointConfigSchema } from '../config';
-import * as data from '../test_data/all_endpoints_data.json';
+import * as data from '../test_data/all_metadata_data.json';
 
 describe('test endpoint route', () => {
   let routerMock: jest.Mocked<IRouter>;
@@ -54,7 +54,7 @@ describe('test endpoint route', () => {
     >;
     mockScopedClient.callAsCurrentUser.mockImplementationOnce(() => Promise.resolve(response));
     [routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) =>
-      path.startsWith('/api/endpoint/endpoints')
+      path.startsWith('/api/endpoint/metadata')
     )!;
 
     await routeHandler(
@@ -96,7 +96,7 @@ describe('test endpoint route', () => {
       Promise.resolve((data as unknown) as SearchResponse<EndpointMetadata>)
     );
     [routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) =>
-      path.startsWith('/api/endpoint/endpoints')
+      path.startsWith('/api/endpoint/metadata')
     )!;
 
     await routeHandler(
@@ -143,7 +143,7 @@ describe('test endpoint route', () => {
       Promise.resolve((data as unknown) as SearchResponse<EndpointMetadata>)
     );
     [routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) =>
-      path.startsWith('/api/endpoint/endpoints')
+      path.startsWith('/api/endpoint/metadata')
     )!;
 
     await routeHandler(
@@ -208,7 +208,7 @@ describe('test endpoint route', () => {
         })
       );
       [routeConfig, routeHandler] = routerMock.get.mock.calls.find(([{ path }]) =>
-        path.startsWith('/api/endpoint/endpoints')
+        path.startsWith('/api/endpoint/metadata')
       )!;
 
       await routeHandler(
@@ -239,7 +239,7 @@ describe('test endpoint route', () => {
       >;
       mockScopedClient.callAsCurrentUser.mockImplementationOnce(() => Promise.resolve(response));
       [routeConfig, routeHandler] = routerMock.get.mock.calls.find(([{ path }]) =>
-        path.startsWith('/api/endpoint/endpoints')
+        path.startsWith('/api/endpoint/metadata')
       )!;
 
       await routeHandler(
diff --git a/x-pack/plugins/endpoint/server/routes/endpoints.ts b/x-pack/plugins/endpoint/server/routes/metadata.ts
similarity index 90%
rename from x-pack/plugins/endpoint/server/routes/endpoints.ts
rename to x-pack/plugins/endpoint/server/routes/metadata.ts
index 054172a7f258a..278cfac020a3b 100644
--- a/x-pack/plugins/endpoint/server/routes/endpoints.ts
+++ b/x-pack/plugins/endpoint/server/routes/metadata.ts
@@ -9,9 +9,9 @@ import { SearchResponse } from 'elasticsearch';
 import { schema } from '@kbn/config-schema';
 
 import {
-  kibanaRequestToEndpointListQuery,
-  kibanaRequestToEndpointFetchQuery,
-} from '../services/endpoint/endpoint_query_builders';
+  kibanaRequestToMetadataListESQuery,
+  kibanaRequestToMetadataGetESQuery,
+} from '../services/endpoint/metadata_query_builders';
 import { EndpointMetadata, EndpointResultList } from '../../common/types';
 import { EndpointAppContext } from '../types';
 
@@ -22,7 +22,7 @@ interface HitSource {
 export function registerEndpointRoutes(router: IRouter, endpointAppContext: EndpointAppContext) {
   router.post(
     {
-      path: '/api/endpoint/endpoints',
+      path: '/api/endpoint/metadata',
       validate: {
         body: schema.nullable(
           schema.object({
@@ -53,7 +53,7 @@ export function registerEndpointRoutes(router: IRouter, endpointAppContext: Endp
     },
     async (context, req, res) => {
       try {
-        const queryParams = await kibanaRequestToEndpointListQuery(req, endpointAppContext);
+        const queryParams = await kibanaRequestToMetadataListESQuery(req, endpointAppContext);
         const response = (await context.core.elasticsearch.dataClient.callAsCurrentUser(
           'search',
           queryParams
@@ -67,7 +67,7 @@ export function registerEndpointRoutes(router: IRouter, endpointAppContext: Endp
 
   router.get(
     {
-      path: '/api/endpoint/endpoints/{id}',
+      path: '/api/endpoint/metadata/{id}',
       validate: {
         params: schema.object({ id: schema.string() }),
       },
@@ -75,7 +75,7 @@ export function registerEndpointRoutes(router: IRouter, endpointAppContext: Endp
     },
     async (context, req, res) => {
       try {
-        const query = kibanaRequestToEndpointFetchQuery(req, endpointAppContext);
+        const query = kibanaRequestToMetadataGetESQuery(req, endpointAppContext);
         const response = (await context.core.elasticsearch.dataClient.callAsCurrentUser(
           'search',
           query
diff --git a/x-pack/plugins/endpoint/server/services/endpoint/endpoint_query_builders.test.ts b/x-pack/plugins/endpoint/server/services/endpoint/metadata_query_builders.test.ts
similarity index 79%
rename from x-pack/plugins/endpoint/server/services/endpoint/endpoint_query_builders.test.ts
rename to x-pack/plugins/endpoint/server/services/endpoint/metadata_query_builders.test.ts
index bd9986ecf1f97..a3090361d4965 100644
--- a/x-pack/plugins/endpoint/server/services/endpoint/endpoint_query_builders.test.ts
+++ b/x-pack/plugins/endpoint/server/services/endpoint/metadata_query_builders.test.ts
@@ -6,17 +6,18 @@
 import { httpServerMock, loggingServiceMock } from '../../../../../../src/core/server/mocks';
 import { EndpointConfigSchema } from '../../config';
 import {
-  kibanaRequestToEndpointListQuery,
-  kibanaRequestToEndpointFetchQuery,
-} from './endpoint_query_builders';
+  kibanaRequestToMetadataListESQuery,
+  kibanaRequestToMetadataGetESQuery,
+} from './metadata_query_builders';
+import { EndpointAppConstants } from '../../../common/types';
 
 describe('query builder', () => {
-  describe('EndpointListQuery', () => {
-    it('test default query params for all endpoints when no params or body is provided', async () => {
+  describe('MetadataListESQuery', () => {
+    it('test default query params for all endpoints metadata when no params or body is provided', async () => {
       const mockRequest = httpServerMock.createKibanaRequest({
         body: {},
       });
-      const query = await kibanaRequestToEndpointListQuery(mockRequest, {
+      const query = await kibanaRequestToMetadataListESQuery(mockRequest, {
         logFactory: loggingServiceMock.create(),
         config: () => Promise.resolve(EndpointConfigSchema.validate({})),
       });
@@ -50,19 +51,19 @@ describe('query builder', () => {
         },
         from: 0,
         size: 10,
-        index: 'endpoint-agent*',
+        index: EndpointAppConstants.ENDPOINT_INDEX_NAME,
       } as Record<string, any>);
     });
   });
 
   describe('test query builder with kql filter', () => {
-    it('test default query params for all endpoints when no params or body is provided', async () => {
+    it('test default query params for all endpoints metadata when body filter is provided', async () => {
       const mockRequest = httpServerMock.createKibanaRequest({
         body: {
           filter: 'not host.ip:10.140.73.246',
         },
       });
-      const query = await kibanaRequestToEndpointListQuery(mockRequest, {
+      const query = await kibanaRequestToMetadataListESQuery(mockRequest, {
         logFactory: loggingServiceMock.create(),
         config: () => Promise.resolve(EndpointConfigSchema.validate({})),
       });
@@ -109,12 +110,12 @@ describe('query builder', () => {
         },
         from: 0,
         size: 10,
-        index: 'endpoint-agent*',
+        index: EndpointAppConstants.ENDPOINT_INDEX_NAME,
       } as Record<string, any>);
     });
   });
 
-  describe('EndpointFetchQuery', () => {
+  describe('MetadataGetQuery', () => {
     it('searches for the correct ID', () => {
       const mockID = 'AABBCCDD-0011-2233-AA44-DEADBEEF8899';
       const mockRequest = httpServerMock.createKibanaRequest({
@@ -122,7 +123,7 @@ describe('query builder', () => {
           id: mockID,
         },
       });
-      const query = kibanaRequestToEndpointFetchQuery(mockRequest, {
+      const query = kibanaRequestToMetadataGetESQuery(mockRequest, {
         logFactory: loggingServiceMock.create(),
         config: () => Promise.resolve(EndpointConfigSchema.validate({})),
       });
@@ -132,7 +133,7 @@ describe('query builder', () => {
           sort: [{ 'event.created': { order: 'desc' } }],
           size: 1,
         },
-        index: 'endpoint-agent*',
+        index: EndpointAppConstants.ENDPOINT_INDEX_NAME,
       });
     });
   });
diff --git a/x-pack/plugins/endpoint/server/services/endpoint/endpoint_query_builders.ts b/x-pack/plugins/endpoint/server/services/endpoint/metadata_query_builders.ts
similarity index 96%
rename from x-pack/plugins/endpoint/server/services/endpoint/endpoint_query_builders.ts
rename to x-pack/plugins/endpoint/server/services/endpoint/metadata_query_builders.ts
index c143b09ec453c..300e837c4af1e 100644
--- a/x-pack/plugins/endpoint/server/services/endpoint/endpoint_query_builders.ts
+++ b/x-pack/plugins/endpoint/server/services/endpoint/metadata_query_builders.ts
@@ -8,7 +8,7 @@ import { EndpointAppConstants } from '../../../common/types';
 import { EndpointAppContext } from '../../types';
 import { esKuery } from '../../../../../../src/plugins/data/server';
 
-export const kibanaRequestToEndpointListQuery = async (
+export const kibanaRequestToMetadataListESQuery = async (
   request: KibanaRequest<any, any, any>,
   endpointAppContext: EndpointAppContext
 ): Promise<Record<string, any>> => {
@@ -74,7 +74,7 @@ function buildQueryBody(request: KibanaRequest<any, any, any>): Record<string, a
   };
 }
 
-export const kibanaRequestToEndpointFetchQuery = (
+export const kibanaRequestToMetadataGetESQuery = (
   request: KibanaRequest<any, any, any>,
   endpointAppContext: EndpointAppContext
 ) => {
diff --git a/x-pack/plugins/endpoint/server/test_data/all_endpoints_data.json b/x-pack/plugins/endpoint/server/test_data/all_metadata_data.json
similarity index 100%
rename from x-pack/plugins/endpoint/server/test_data/all_endpoints_data.json
rename to x-pack/plugins/endpoint/server/test_data/all_metadata_data.json
diff --git a/x-pack/test/api_integration/apis/endpoint/index.ts b/x-pack/test/api_integration/apis/endpoint/index.ts
index 238c63640386a..4ffd0c3b6044b 100644
--- a/x-pack/test/api_integration/apis/endpoint/index.ts
+++ b/x-pack/test/api_integration/apis/endpoint/index.ts
@@ -10,7 +10,7 @@ export default function endpointAPIIntegrationTests({ loadTestFile }: FtrProvide
   describe('Endpoint plugin', function() {
     this.tags(['endpoint']);
     loadTestFile(require.resolve('./resolver'));
-    loadTestFile(require.resolve('./endpoints'));
+    loadTestFile(require.resolve('./metadata'));
     loadTestFile(require.resolve('./alerts'));
   });
 }
diff --git a/x-pack/test/api_integration/apis/endpoint/endpoints.ts b/x-pack/test/api_integration/apis/endpoint/metadata.ts
similarity index 61%
rename from x-pack/test/api_integration/apis/endpoint/endpoints.ts
rename to x-pack/test/api_integration/apis/endpoint/metadata.ts
index febe5f523cb6f..4b0cc8d93a395 100644
--- a/x-pack/test/api_integration/apis/endpoint/endpoints.ts
+++ b/x-pack/test/api_integration/apis/endpoint/metadata.ts
@@ -9,12 +9,12 @@ import { FtrProviderContext } from '../../ftr_provider_context';
 export default function({ getService }: FtrProviderContext) {
   const esArchiver = getService('esArchiver');
   const supertest = getService('supertest');
-  describe('test endpoints api', () => {
-    describe('POST /api/endpoint/endpoints when index is empty', () => {
-      it('endpoints api should return empty result when index is empty', async () => {
-        await esArchiver.unload('endpoint/endpoints/api_feature');
+  describe('test metadata api', () => {
+    describe('POST /api/endpoint/metadata when index is empty', () => {
+      it('metadata api should return empty result when index is empty', async () => {
+        await esArchiver.unload('endpoint/metadata/api_feature');
         const { body } = await supertest
-          .post('/api/endpoint/endpoints')
+          .post('/api/endpoint/metadata')
           .set('kbn-xsrf', 'xxx')
           .send()
           .expect(200);
@@ -25,12 +25,12 @@ export default function({ getService }: FtrProviderContext) {
       });
     });
 
-    describe('POST /api/endpoint/endpoints when index is not empty', () => {
-      before(() => esArchiver.load('endpoint/endpoints/api_feature'));
-      after(() => esArchiver.unload('endpoint/endpoints/api_feature'));
-      it('endpoints api should return one entry for each endpoint with default paging', async () => {
+    describe('POST /api/endpoint/metadata when index is not empty', () => {
+      before(() => esArchiver.load('endpoint/metadata/api_feature'));
+      after(() => esArchiver.unload('endpoint/metadata/api_feature'));
+      it('metadata api should return one entry for each endpoint with default paging', async () => {
         const { body } = await supertest
-          .post('/api/endpoint/endpoints')
+          .post('/api/endpoint/metadata')
           .set('kbn-xsrf', 'xxx')
           .send()
           .expect(200);
@@ -40,9 +40,9 @@ export default function({ getService }: FtrProviderContext) {
         expect(body.request_page_index).to.eql(0);
       });
 
-      it('endpoints api should return page based on paging properties passed.', async () => {
+      it('metadata api should return page based on paging properties passed.', async () => {
         const { body } = await supertest
-          .post('/api/endpoint/endpoints')
+          .post('/api/endpoint/metadata')
           .set('kbn-xsrf', 'xxx')
           .send({
             paging_properties: [
@@ -61,12 +61,12 @@ export default function({ getService }: FtrProviderContext) {
         expect(body.request_page_index).to.eql(1);
       });
 
-      /* test that when paging properties produces no result, the total should reflect the actual number of endpoints
+      /* test that when paging properties produces no result, the total should reflect the actual number of metadata
       in the index.
        */
-      it('endpoints api should return accurate total endpoints if page index produces no result', async () => {
+      it('metadata api should return accurate total metadata if page index produces no result', async () => {
         const { body } = await supertest
-          .post('/api/endpoint/endpoints')
+          .post('/api/endpoint/metadata')
           .set('kbn-xsrf', 'xxx')
           .send({
             paging_properties: [
@@ -85,9 +85,9 @@ export default function({ getService }: FtrProviderContext) {
         expect(body.request_page_index).to.eql(30);
       });
 
-      it('endpoints api should return 400 when pagingProperties is below boundaries.', async () => {
+      it('metadata api should return 400 when pagingProperties is below boundaries.', async () => {
         const { body } = await supertest
-          .post('/api/endpoint/endpoints')
+          .post('/api/endpoint/metadata')
           .set('kbn-xsrf', 'xxx')
           .send({
             paging_properties: [
@@ -103,9 +103,9 @@ export default function({ getService }: FtrProviderContext) {
         expect(body.message).to.contain('Value is [0] but it must be equal to or greater than [1]');
       });
 
-      it('endpoints api should return page based on filters passed.', async () => {
+      it('metadata api should return page based on filters passed.', async () => {
         const { body } = await supertest
-          .post('/api/endpoint/endpoints')
+          .post('/api/endpoint/metadata')
           .set('kbn-xsrf', 'xxx')
           .send({ filter: 'not host.ip:10.101.149.26' })
           .expect(200);
@@ -115,10 +115,10 @@ export default function({ getService }: FtrProviderContext) {
         expect(body.request_page_index).to.eql(0);
       });
 
-      it('endpoints api should return page based on filters and paging passed.', async () => {
+      it('metadata api should return page based on filters and paging passed.', async () => {
         const notIncludedIp = '10.101.149.26';
         const { body } = await supertest
-          .post('/api/endpoint/endpoints')
+          .post('/api/endpoint/metadata')
           .set('kbn-xsrf', 'xxx')
           .send({
             paging_properties: [
@@ -143,10 +143,10 @@ export default function({ getService }: FtrProviderContext) {
         expect(body.request_page_index).to.eql(0);
       });
 
-      it('endpoints api should return page based on host.os.variant filter.', async () => {
+      it('metadata api should return page based on host.os.variant filter.', async () => {
         const variantValue = 'Windows Pro';
         const { body } = await supertest
-          .post('/api/endpoint/endpoints')
+          .post('/api/endpoint/metadata')
           .set('kbn-xsrf', 'xxx')
           .send({
             filter: `host.os.variant.keyword:${variantValue}`,
@@ -161,6 +161,40 @@ export default function({ getService }: FtrProviderContext) {
         expect(body.request_page_size).to.eql(10);
         expect(body.request_page_index).to.eql(0);
       });
+
+      it('metadata api should return the latest event for all the events for an endpoint', async () => {
+        const targetEndpointIp = '10.192.213.130';
+        const { body } = await supertest
+          .post('/api/endpoint/metadata')
+          .set('kbn-xsrf', 'xxx')
+          .send({
+            filter: `host.ip:${targetEndpointIp}`,
+          })
+          .expect(200);
+        expect(body.total).to.eql(1);
+        const resultIp: string = body.endpoints[0].host.ip.filter(
+          (ip: string) => ip === targetEndpointIp
+        );
+        expect(resultIp).to.eql([targetEndpointIp]);
+        expect(body.endpoints[0].event.created).to.eql('2020-01-24T16:06:09.541Z');
+        expect(body.endpoints.length).to.eql(1);
+        expect(body.request_page_size).to.eql(10);
+        expect(body.request_page_index).to.eql(0);
+      });
+
+      it('metadata api should return  all endpoints when filter is empty string', async () => {
+        const { body } = await supertest
+          .post('/api/endpoint/metadata')
+          .set('kbn-xsrf', 'xxx')
+          .send({
+            filter: '',
+          })
+          .expect(200);
+        expect(body.total).to.eql(3);
+        expect(body.endpoints.length).to.eql(3);
+        expect(body.request_page_size).to.eql(10);
+        expect(body.request_page_index).to.eql(0);
+      });
     });
   });
 }
diff --git a/x-pack/test/functional/apps/endpoint/management.ts b/x-pack/test/functional/apps/endpoint/management.ts
index bac87f34ceb82..500185182f0d8 100644
--- a/x-pack/test/functional/apps/endpoint/management.ts
+++ b/x-pack/test/functional/apps/endpoint/management.ts
@@ -15,7 +15,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
   describe('Endpoint Management List', function() {
     this.tags('ciGroup7');
     before(async () => {
-      await esArchiver.load('endpoint/endpoints/api_feature');
+      await esArchiver.load('endpoint/metadata/api_feature');
       await pageObjects.common.navigateToUrlWithBrowserHistory('endpoint', '/management');
     });
 
@@ -41,7 +41,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
     });
 
     after(async () => {
-      await esArchiver.unload('endpoint/endpoints/api_feature');
+      await esArchiver.unload('endpoint/metadata/api_feature');
     });
   });
 };
diff --git a/x-pack/test/functional/es_archives/endpoint/endpoints/api_feature/data.json b/x-pack/test/functional/es_archives/endpoint/metadata/api_feature/data.json
similarity index 100%
rename from x-pack/test/functional/es_archives/endpoint/endpoints/api_feature/data.json
rename to x-pack/test/functional/es_archives/endpoint/metadata/api_feature/data.json
diff --git a/x-pack/test/functional/es_archives/endpoint/endpoints/api_feature/mappings.json b/x-pack/test/functional/es_archives/endpoint/metadata/api_feature/mappings.json
similarity index 100%
rename from x-pack/test/functional/es_archives/endpoint/endpoints/api_feature/mappings.json
rename to x-pack/test/functional/es_archives/endpoint/metadata/api_feature/mappings.json

From fe932fce6e6eb3c91c6dcac2279bb21f9fb6b885 Mon Sep 17 00:00:00 2001
From: Melissa Alvarez <melissa.alvarez@elastic.co>
Date: Fri, 21 Feb 2020 09:27:25 -0500
Subject: [PATCH 122/174] [ML] New Platform server shim: update fields service
 routes (#58060)

* update fieldsService routes to use NP router

* fix file name typo

* add routes names for docs
---
 .../models/fields_service/fields_service.d.ts | 21 +++++
 .../models/fields_service/fields_service.js   |  8 +-
 .../fields_service/{index.js => index.ts}     |  0
 .../new_platform/fields_service_schema.ts     | 22 +++++
 .../plugins/ml/server/routes/apidoc.json      |  5 +-
 .../ml/server/routes/fields_service.js        | 49 -----------
 .../ml/server/routes/fields_service.ts        | 86 +++++++++++++++++++
 7 files changed, 137 insertions(+), 54 deletions(-)
 create mode 100644 x-pack/legacy/plugins/ml/server/models/fields_service/fields_service.d.ts
 rename x-pack/legacy/plugins/ml/server/models/fields_service/{index.js => index.ts} (100%)
 create mode 100644 x-pack/legacy/plugins/ml/server/new_platform/fields_service_schema.ts
 delete mode 100644 x-pack/legacy/plugins/ml/server/routes/fields_service.js
 create mode 100644 x-pack/legacy/plugins/ml/server/routes/fields_service.ts

diff --git a/x-pack/legacy/plugins/ml/server/models/fields_service/fields_service.d.ts b/x-pack/legacy/plugins/ml/server/models/fields_service/fields_service.d.ts
new file mode 100644
index 0000000000000..38f7dce93546d
--- /dev/null
+++ b/x-pack/legacy/plugins/ml/server/models/fields_service/fields_service.d.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 { APICaller } from 'src/core/server';
+
+export function fieldsServiceProvider(
+  callAsCurrentUser: APICaller
+): {
+  getCardinalityOfFields: (
+    index: string[] | string,
+    fieldNames: string[],
+    query: any,
+    timeFieldName: string,
+    earliestMs: number,
+    latestMs: number
+  ) => Promise<any>;
+  getTimeFieldRange: (index: string[] | string, timeFieldName: string, query: any) => Promise<any>;
+};
diff --git a/x-pack/legacy/plugins/ml/server/models/fields_service/fields_service.js b/x-pack/legacy/plugins/ml/server/models/fields_service/fields_service.js
index 3b2de50ee2e63..a538693a92aba 100644
--- a/x-pack/legacy/plugins/ml/server/models/fields_service/fields_service.js
+++ b/x-pack/legacy/plugins/ml/server/models/fields_service/fields_service.js
@@ -7,7 +7,7 @@
 // Service for carrying out queries to obtain data
 // specific to fields in Elasticsearch indices.
 
-export function fieldsServiceProvider(callWithRequest) {
+export function fieldsServiceProvider(callAsCurrentUser) {
   // Obtains the cardinality of one or more fields.
   // Returns an Object whose keys are the names of the fields,
   // with values equal to the cardinality of the field.
@@ -17,7 +17,7 @@ export function fieldsServiceProvider(callWithRequest) {
     // First check that each of the supplied fieldNames are aggregatable,
     // then obtain the cardinality for each of the aggregatable fields.
     return new Promise((resolve, reject) => {
-      callWithRequest('fieldCaps', {
+      callAsCurrentUser('fieldCaps', {
         index,
         fields: fieldNames,
       })
@@ -72,7 +72,7 @@ export function fieldsServiceProvider(callWithRequest) {
               aggs,
             };
 
-            callWithRequest('search', {
+            callAsCurrentUser('search', {
               index,
               body,
             })
@@ -106,7 +106,7 @@ export function fieldsServiceProvider(callWithRequest) {
     return new Promise((resolve, reject) => {
       const obj = { success: true, start: { epoch: 0, string: '' }, end: { epoch: 0, string: '' } };
 
-      callWithRequest('search', {
+      callAsCurrentUser('search', {
         index,
         size: 0,
         body: {
diff --git a/x-pack/legacy/plugins/ml/server/models/fields_service/index.js b/x-pack/legacy/plugins/ml/server/models/fields_service/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/ml/server/models/fields_service/index.js
rename to x-pack/legacy/plugins/ml/server/models/fields_service/index.ts
diff --git a/x-pack/legacy/plugins/ml/server/new_platform/fields_service_schema.ts b/x-pack/legacy/plugins/ml/server/new_platform/fields_service_schema.ts
new file mode 100644
index 0000000000000..e0fba498e0d58
--- /dev/null
+++ b/x-pack/legacy/plugins/ml/server/new_platform/fields_service_schema.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 { schema } from '@kbn/config-schema';
+
+export const getCardinalityOfFieldsSchema = schema.object({
+  index: schema.oneOf([schema.string(), schema.arrayOf(schema.string())]),
+  fieldNames: schema.maybe(schema.arrayOf(schema.string())),
+  query: schema.maybe(schema.any()),
+  timeFieldName: schema.maybe(schema.string()),
+  earliestMs: schema.maybe(schema.oneOf([schema.number(), schema.string()])),
+  latestMs: schema.maybe(schema.oneOf([schema.number(), schema.string()])),
+});
+
+export const getTimeFieldRangeSchema = schema.object({
+  index: schema.oneOf([schema.string(), schema.arrayOf(schema.string())]),
+  timeFieldName: schema.maybe(schema.string()),
+  query: schema.maybe(schema.any()),
+});
diff --git a/x-pack/legacy/plugins/ml/server/routes/apidoc.json b/x-pack/legacy/plugins/ml/server/routes/apidoc.json
index 7d1f13ead3fef..946e3bd71d6c3 100644
--- a/x-pack/legacy/plugins/ml/server/routes/apidoc.json
+++ b/x-pack/legacy/plugins/ml/server/routes/apidoc.json
@@ -105,6 +105,9 @@
     "DeleteDatafeed",
     "StartDatafeed",
     "StopDatafeed",
-    "PreviewDatafeed"
+    "PreviewDatafeed",
+    "FieldsService",
+    "GetCardinalityOfFields",
+    "GetTimeFieldRange"
   ]
 }
diff --git a/x-pack/legacy/plugins/ml/server/routes/fields_service.js b/x-pack/legacy/plugins/ml/server/routes/fields_service.js
deleted file mode 100644
index 7848ffbd8bafe..0000000000000
--- a/x-pack/legacy/plugins/ml/server/routes/fields_service.js
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { callWithRequestFactory } from '../client/call_with_request_factory';
-import { wrapError } from '../client/errors';
-import { fieldsServiceProvider } from '../models/fields_service';
-
-function getCardinalityOfFields(callWithRequest, payload) {
-  const fs = fieldsServiceProvider(callWithRequest);
-  const { index, fieldNames, query, timeFieldName, earliestMs, latestMs } = payload;
-  return fs.getCardinalityOfFields(index, fieldNames, query, timeFieldName, earliestMs, latestMs);
-}
-
-function getTimeFieldRange(callWithRequest, payload) {
-  const fs = fieldsServiceProvider(callWithRequest);
-  const { index, timeFieldName, query } = payload;
-  return fs.getTimeFieldRange(index, timeFieldName, query);
-}
-
-export function fieldsService({ commonRouteConfig, elasticsearchPlugin, route }) {
-  route({
-    method: 'POST',
-    path: '/api/ml/fields_service/field_cardinality',
-    handler(request) {
-      const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
-      return getCardinalityOfFields(callWithRequest, request.payload).catch(resp =>
-        wrapError(resp)
-      );
-    },
-    config: {
-      ...commonRouteConfig,
-    },
-  });
-
-  route({
-    method: 'POST',
-    path: '/api/ml/fields_service/time_field_range',
-    handler(request) {
-      const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
-      return getTimeFieldRange(callWithRequest, request.payload).catch(resp => wrapError(resp));
-    },
-    config: {
-      ...commonRouteConfig,
-    },
-  });
-}
diff --git a/x-pack/legacy/plugins/ml/server/routes/fields_service.ts b/x-pack/legacy/plugins/ml/server/routes/fields_service.ts
new file mode 100644
index 0000000000000..4827adf23d7b4
--- /dev/null
+++ b/x-pack/legacy/plugins/ml/server/routes/fields_service.ts
@@ -0,0 +1,86 @@
+/*
+ * 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 'src/core/server';
+import { licensePreRoutingFactory } from '../new_platform/licence_check_pre_routing_factory';
+import { wrapError } from '../client/error_wrapper';
+import { RouteInitialization } from '../new_platform/plugin';
+import {
+  getCardinalityOfFieldsSchema,
+  getTimeFieldRangeSchema,
+} from '../new_platform/fields_service_schema';
+import { fieldsServiceProvider } from '../models/fields_service';
+
+function getCardinalityOfFields(context: RequestHandlerContext, payload: any) {
+  const fs = fieldsServiceProvider(context.ml!.mlClient.callAsCurrentUser);
+  const { index, fieldNames, query, timeFieldName, earliestMs, latestMs } = payload;
+  return fs.getCardinalityOfFields(index, fieldNames, query, timeFieldName, earliestMs, latestMs);
+}
+
+function getTimeFieldRange(context: RequestHandlerContext, payload: any) {
+  const fs = fieldsServiceProvider(context.ml!.mlClient.callAsCurrentUser);
+  const { index, timeFieldName, query } = payload;
+  return fs.getTimeFieldRange(index, timeFieldName, query);
+}
+
+/**
+ * Routes for fields service
+ */
+export function fieldsService({ xpackMainPlugin, router }: RouteInitialization) {
+  /**
+   * @apiGroup FieldsService
+   *
+   * @api {post} /api/ml/fields_service/field_cardinality Get cardinality of fields
+   * @apiName GetCardinalityOfFields
+   * @apiDescription Returns the cardinality of one or more fields. Returns an Object whose keys are the names of the fields, with values equal to the cardinality of the field
+   */
+  router.post(
+    {
+      path: '/api/ml/fields_service/field_cardinality',
+      validate: {
+        body: getCardinalityOfFieldsSchema,
+      },
+    },
+    licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
+      try {
+        const resp = await getCardinalityOfFields(context, request.body);
+
+        return response.ok({
+          body: resp,
+        });
+      } catch (e) {
+        return response.customError(wrapError(e));
+      }
+    })
+  );
+
+  /**
+   * @apiGroup FieldsService
+   *
+   * @api {post} /api/ml/fields_service/time_field_range Get time field range
+   * @apiName GetTimeFieldRange
+   * @apiDescription Returns the timefield range for the given index
+   */
+  router.post(
+    {
+      path: '/api/ml/fields_service/time_field_range',
+      validate: {
+        body: getTimeFieldRangeSchema,
+      },
+    },
+    licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
+      try {
+        const resp = await getTimeFieldRange(context, request.body);
+
+        return response.ok({
+          body: resp,
+        });
+      } catch (e) {
+        return response.customError(wrapError(e));
+      }
+    })
+  );
+}

From 96000fa425932618211b76643ef6914335beb28e Mon Sep 17 00:00:00 2001
From: Walter Rafelsberger <walter@elastic.co>
Date: Fri, 21 Feb 2020 15:31:57 +0100
Subject: [PATCH 123/174] [ML] Transforms: Adds clone feature to transforms
 list. (#57837)

Adds a Clone action to the transform management list. The action opens the transform wizard with prepopulated configurations based on the selected transform. The fields for the transform name and target index will not be populated to avoid the obvious "already exists" warnings.
---
 .../plugins/transform/public/app/app.tsx      |   5 +
 .../transform/public/app/common/index.ts      |   3 +
 .../public/app/common/request.test.ts         |  11 +-
 .../transform/public/app/common/request.ts    |   6 +
 .../transform/public/app/constants/index.ts   |   1 +
 .../transform/public/app/lib/kibana/common.ts |  34 ++-
 .../transform/public/app/lib/kibana/index.ts  |   1 +
 .../public/app/lib/kibana/kibana_context.tsx  |  18 +-
 .../public/app/lib/kibana/kibana_provider.tsx |  20 +-
 .../clone_transform_section.tsx               | 194 ++++++++++++++++++
 .../app/sections/clone_transform/index.ts     |   7 +
 .../use_source_index_data.ts                  |   3 +-
 .../components/step_define/index.ts           |   3 +-
 .../step_define/step_define_form.tsx          |  72 ++++++-
 .../components/step_details/index.ts          |   6 +-
 .../step_details/step_details_form.tsx        |  16 ++
 .../components/wizard/wizard.tsx              |  26 ++-
 .../transform_list/action_clone.tsx           |  60 ++++++
 .../transform_list/actions.test.tsx           |   3 +-
 .../components/transform_list/actions.tsx     |   6 +
 .../app/services/navigation/breadcrumb.ts     |   8 +
 .../public/app/services/text/text.ts          |   3 +
 22 files changed, 454 insertions(+), 52 deletions(-)
 create mode 100644 x-pack/legacy/plugins/transform/public/app/sections/clone_transform/clone_transform_section.tsx
 create mode 100644 x-pack/legacy/plugins/transform/public/app/sections/clone_transform/index.ts
 create mode 100644 x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_clone.tsx

diff --git a/x-pack/legacy/plugins/transform/public/app/app.tsx b/x-pack/legacy/plugins/transform/public/app/app.tsx
index 825c1761bf619..0f21afbcccca8 100644
--- a/x-pack/legacy/plugins/transform/public/app/app.tsx
+++ b/x-pack/legacy/plugins/transform/public/app/app.tsx
@@ -16,6 +16,7 @@ import { getAppProviders } from './app_dependencies';
 import { AuthorizationContext } from './lib/authorization';
 import { AppDependencies } from '../shim';
 
+import { CloneTransformSection } from './sections/clone_transform';
 import { CreateTransformSection } from './sections/create_transform';
 import { TransformManagementSection } from './sections/transform_management';
 
@@ -39,6 +40,10 @@ export const App: FC = () => {
   return (
     <div data-test-subj="transformApp">
       <Switch>
+        <Route
+          path={`${CLIENT_BASE_PATH}/${SECTION_SLUG.CLONE_TRANSFORM}/:transformId`}
+          component={CloneTransformSection}
+        />
         <Route
           path={`${CLIENT_BASE_PATH}/${SECTION_SLUG.CREATE_TRANSFORM}/:savedObjectId`}
           component={CreateTransformSection}
diff --git a/x-pack/legacy/plugins/transform/public/app/common/index.ts b/x-pack/legacy/plugins/transform/public/app/common/index.ts
index f42b36f2a38e4..3f515db389b45 100644
--- a/x-pack/legacy/plugins/transform/public/app/common/index.ts
+++ b/x-pack/legacy/plugins/transform/public/app/common/index.ts
@@ -76,11 +76,14 @@ export {
   TermsAgg,
 } from './pivot_group_by';
 export {
+  defaultQuery,
   getPreviewRequestBody,
   getCreateRequestBody,
   getPivotQuery,
   isDefaultQuery,
+  isMatchAllQuery,
   isSimpleQuery,
+  matchAllQuery,
   PivotQuery,
   SimpleQuery,
 } from './request';
diff --git a/x-pack/legacy/plugins/transform/public/app/common/request.test.ts b/x-pack/legacy/plugins/transform/public/app/common/request.test.ts
index 0a433df539efe..0720c654d5029 100644
--- a/x-pack/legacy/plugins/transform/public/app/common/request.test.ts
+++ b/x-pack/legacy/plugins/transform/public/app/common/request.test.ts
@@ -12,19 +12,26 @@ import { StepDetailsExposedState } from '../sections/create_transform/components
 import { PIVOT_SUPPORTED_GROUP_BY_AGGS } from './pivot_group_by';
 import { PivotAggsConfig, PIVOT_SUPPORTED_AGGS } from './pivot_aggs';
 import {
+  defaultQuery,
   getPreviewRequestBody,
   getCreateRequestBody,
   getPivotQuery,
   isDefaultQuery,
+  isMatchAllQuery,
   isSimpleQuery,
+  matchAllQuery,
   PivotQuery,
 } from './request';
 
-const defaultQuery: PivotQuery = { query_string: { query: '*' } };
-const matchAllQuery: PivotQuery = { match_all: {} };
 const simpleQuery: PivotQuery = { query_string: { query: 'airline:AAL' } };
 
 describe('Transform: Common', () => {
+  test('isMatchAllQuery()', () => {
+    expect(isMatchAllQuery(defaultQuery)).toBe(false);
+    expect(isMatchAllQuery(matchAllQuery)).toBe(true);
+    expect(isMatchAllQuery(simpleQuery)).toBe(false);
+  });
+
   test('isSimpleQuery()', () => {
     expect(isSimpleQuery(defaultQuery)).toBe(true);
     expect(isSimpleQuery(matchAllQuery)).toBe(false);
diff --git a/x-pack/legacy/plugins/transform/public/app/common/request.ts b/x-pack/legacy/plugins/transform/public/app/common/request.ts
index 5d508f3d245d3..3b740de177ef8 100644
--- a/x-pack/legacy/plugins/transform/public/app/common/request.ts
+++ b/x-pack/legacy/plugins/transform/public/app/common/request.ts
@@ -53,6 +53,12 @@ export function isSimpleQuery(arg: any): arg is SimpleQuery {
   return arg.query_string !== undefined;
 }
 
+export const matchAllQuery = { match_all: {} };
+export function isMatchAllQuery(query: any): boolean {
+  return query.match_all !== undefined && Object.keys(query.match_all).length === 0;
+}
+
+export const defaultQuery: PivotQuery = { query_string: { query: '*' } };
 export function isDefaultQuery(query: PivotQuery): boolean {
   return isSimpleQuery(query) && query.query_string.query === '*';
 }
diff --git a/x-pack/legacy/plugins/transform/public/app/constants/index.ts b/x-pack/legacy/plugins/transform/public/app/constants/index.ts
index 85ffc222f59a2..78b5f018dd782 100644
--- a/x-pack/legacy/plugins/transform/public/app/constants/index.ts
+++ b/x-pack/legacy/plugins/transform/public/app/constants/index.ts
@@ -8,6 +8,7 @@ export const CLIENT_BASE_PATH = '/management/elasticsearch/transform';
 
 export enum SECTION_SLUG {
   HOME = 'transform_management',
+  CLONE_TRANSFORM = 'clone_transform',
   CREATE_TRANSFORM = 'create_transform',
 }
 
diff --git a/x-pack/legacy/plugins/transform/public/app/lib/kibana/common.ts b/x-pack/legacy/plugins/transform/public/app/lib/kibana/common.ts
index 3e55d509a94ab..aba61766b5d2b 100644
--- a/x-pack/legacy/plugins/transform/public/app/lib/kibana/common.ts
+++ b/x-pack/legacy/plugins/transform/public/app/lib/kibana/common.ts
@@ -4,17 +4,19 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { SavedObjectsClientContract, IUiSettingsClient } from 'src/core/public';
+import { SavedObjectsClientContract, SimpleSavedObject, IUiSettingsClient } from 'src/core/public';
 import {
   IndexPattern,
   esQuery,
   IndexPatternsContract,
 } from '../../../../../../../../src/plugins/data/public';
 
+import { matchAllQuery } from '../../common';
+
 type IndexPatternId = string;
 type SavedSearchId = string;
 
-let indexPatternCache = [];
+let indexPatternCache: Array<SimpleSavedObject<Record<string, any>>> = [];
 let fullIndexPatterns;
 let currentIndexPattern = null;
 let currentSavedSearch = null;
@@ -53,6 +55,10 @@ export function loadIndexPatterns(
     });
 }
 
+export function getIndexPatternIdByTitle(indexPatternTitle: string): string | undefined {
+  return indexPatternCache.find(d => d?.attributes?.title === indexPatternTitle)?.id;
+}
+
 type CombinedQuery = Record<'bool', any> | unknown;
 
 export function loadCurrentIndexPattern(
@@ -69,12 +75,20 @@ export function loadCurrentSavedSearch(savedSearches: any, savedSearchId: SavedS
   return currentSavedSearch;
 }
 
+function isIndexPattern(arg: any): arg is IndexPattern {
+  return arg !== undefined;
+}
 // Helper for creating the items used for searching and job creation.
 export function createSearchItems(
   indexPattern: IndexPattern | undefined,
   savedSearch: any,
   config: IUiSettingsClient
-) {
+): {
+  indexPattern: IndexPattern;
+  savedSearch: any;
+  query: any;
+  combinedQuery: CombinedQuery;
+} {
   // query is only used by the data visualizer as it needs
   // a lucene query_string.
   // Using a blank query will cause match_all:{} to be used
@@ -86,17 +100,13 @@ export function createSearchItems(
 
   let combinedQuery: CombinedQuery = {
     bool: {
-      must: [
-        {
-          match_all: {},
-        },
-      ],
+      must: [matchAllQuery],
     },
   };
 
-  if (indexPattern === undefined && savedSearch !== null && savedSearch.id !== undefined) {
+  if (!isIndexPattern(indexPattern) && savedSearch !== null && savedSearch.id !== undefined) {
     const searchSource = savedSearch.searchSource;
-    indexPattern = searchSource.getField('index');
+    indexPattern = searchSource.getField('index') as IndexPattern;
 
     query = searchSource.getField('query');
     const fs = searchSource.getField('filter');
@@ -107,6 +117,10 @@ export function createSearchItems(
     combinedQuery = esQuery.buildEsQuery(indexPattern, [query], filters, esQueryConfigs);
   }
 
+  if (!isIndexPattern(indexPattern)) {
+    throw new Error('Index Pattern is not defined.');
+  }
+
   return {
     indexPattern,
     savedSearch,
diff --git a/x-pack/legacy/plugins/transform/public/app/lib/kibana/index.ts b/x-pack/legacy/plugins/transform/public/app/lib/kibana/index.ts
index 82d5362e21c02..62107cb37ff2c 100644
--- a/x-pack/legacy/plugins/transform/public/app/lib/kibana/index.ts
+++ b/x-pack/legacy/plugins/transform/public/app/lib/kibana/index.ts
@@ -4,6 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+export { getIndexPatternIdByTitle, loadIndexPatterns } from './common';
 export {
   useKibanaContext,
   InitializedKibanaContextValue,
diff --git a/x-pack/legacy/plugins/transform/public/app/lib/kibana/kibana_context.tsx b/x-pack/legacy/plugins/transform/public/app/lib/kibana/kibana_context.tsx
index 5b7702a0193ec..b0a0371d2de86 100644
--- a/x-pack/legacy/plugins/transform/public/app/lib/kibana/kibana_context.tsx
+++ b/x-pack/legacy/plugins/transform/public/app/lib/kibana/kibana_context.tsx
@@ -6,30 +6,26 @@
 
 import React, { createContext, useContext, FC } from 'react';
 
+import { IUiSettingsClient } from 'kibana/public';
+
 import { SavedSearch } from '../../../../../../../../src/legacy/core_plugins/kibana/public/discover/np_ready/types';
 import {
   IndexPattern,
   IndexPatternsContract,
 } from '../../../../../../../../src/plugins/data/public';
-import { KibanaConfig } from '../../../../../../../../src/legacy/server/kbn_server';
-
-// set() method is missing in original d.ts
-interface KibanaConfigTypeFix extends KibanaConfig {
-  set(key: string, value: any): void;
-}
 
 interface UninitializedKibanaContextValue {
-  initialized: boolean;
+  initialized: false;
 }
 
 export interface InitializedKibanaContextValue {
   combinedQuery: any;
-  currentIndexPattern: IndexPattern;
-  currentSavedSearch: SavedSearch;
   indexPatterns: IndexPatternsContract;
-  initialized: boolean;
+  initialized: true;
   kbnBaseUrl: string;
-  kibanaConfig: KibanaConfigTypeFix;
+  kibanaConfig: IUiSettingsClient;
+  currentIndexPattern: IndexPattern;
+  currentSavedSearch?: SavedSearch;
 }
 
 export type KibanaContextValue = UninitializedKibanaContextValue | InitializedKibanaContextValue;
diff --git a/x-pack/legacy/plugins/transform/public/app/lib/kibana/kibana_provider.tsx b/x-pack/legacy/plugins/transform/public/app/lib/kibana/kibana_provider.tsx
index 0a9de49168ad4..d2cf5f2b32910 100644
--- a/x-pack/legacy/plugins/transform/public/app/lib/kibana/kibana_provider.tsx
+++ b/x-pack/legacy/plugins/transform/public/app/lib/kibana/kibana_provider.tsx
@@ -17,7 +17,7 @@ import {
   loadCurrentSavedSearch,
 } from './common';
 
-import { KibanaContext, KibanaContextValue } from './kibana_context';
+import { InitializedKibanaContextValue, KibanaContext, KibanaContextValue } from './kibana_context';
 
 const indexPatterns = npStart.plugins.data.indexPatterns;
 const savedObjectsClient = npStart.core.savedObjects.client;
@@ -52,20 +52,20 @@ export const KibanaProvider: FC<Props> = ({ savedObjectId, children }) => {
 
     const kibanaConfig = npStart.core.uiSettings;
 
-    const { indexPattern, savedSearch, combinedQuery } = createSearchItems(
-      fetchedIndexPattern,
-      fetchedSavedSearch,
-      kibanaConfig
-    );
-
-    const kibanaContext = {
+    const {
+      indexPattern: currentIndexPattern,
+      savedSearch: currentSavedSearch,
       combinedQuery,
-      currentIndexPattern: indexPattern,
-      currentSavedSearch: savedSearch,
+    } = createSearchItems(fetchedIndexPattern, fetchedSavedSearch, kibanaConfig);
+
+    const kibanaContext: InitializedKibanaContextValue = {
       indexPatterns,
       initialized: true,
       kbnBaseUrl: npStart.core.injectedMetadata.getBasePath(),
       kibanaConfig,
+      combinedQuery,
+      currentIndexPattern,
+      currentSavedSearch,
     };
 
     setContextValue(kibanaContext);
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/clone_transform/clone_transform_section.tsx b/x-pack/legacy/plugins/transform/public/app/sections/clone_transform/clone_transform_section.tsx
new file mode 100644
index 0000000000000..de96a4de32962
--- /dev/null
+++ b/x-pack/legacy/plugins/transform/public/app/sections/clone_transform/clone_transform_section.tsx
@@ -0,0 +1,194 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { useEffect, useState, FC } from 'react';
+import { RouteComponentProps } from 'react-router-dom';
+
+import { FormattedMessage } from '@kbn/i18n/react';
+import { i18n } from '@kbn/i18n';
+
+import {
+  EuiBetaBadge,
+  EuiButtonEmpty,
+  EuiCallOut,
+  EuiFlexGroup,
+  EuiFlexItem,
+  EuiPageContent,
+  EuiPageContentBody,
+  EuiSpacer,
+  EuiTitle,
+} from '@elastic/eui';
+
+import { npStart } from 'ui/new_platform';
+
+import { useApi } from '../../hooks/use_api';
+
+import { APP_CREATE_TRANSFORM_CLUSTER_PRIVILEGES } from '../../../../common/constants';
+import { TransformPivotConfig } from '../../common';
+import { breadcrumbService, docTitleService, BREADCRUMB_SECTION } from '../../services/navigation';
+import { documentationLinksService } from '../../services/documentation';
+import { PrivilegesWrapper } from '../../lib/authorization';
+import {
+  getIndexPatternIdByTitle,
+  loadIndexPatterns,
+  KibanaProvider,
+  RenderOnlyWithInitializedKibanaContext,
+} from '../../lib/kibana';
+
+import { Wizard } from '../create_transform/components/wizard';
+
+const indexPatterns = npStart.plugins.data.indexPatterns;
+const savedObjectsClient = npStart.core.savedObjects.client;
+
+interface GetTransformsResponseOk {
+  count: number;
+  transforms: TransformPivotConfig[];
+}
+
+interface GetTransformsResponseError {
+  error: {
+    msg: string;
+    path: string;
+    query: any;
+    statusCode: number;
+    response: string;
+  };
+}
+
+function isGetTransformsResponseError(arg: any): arg is GetTransformsResponseError {
+  return arg.error !== undefined;
+}
+
+type GetTransformsResponse = GetTransformsResponseOk | GetTransformsResponseError;
+
+type Props = RouteComponentProps<{ transformId: string }>;
+export const CloneTransformSection: FC<Props> = ({ match }) => {
+  // Set breadcrumb and page title
+  useEffect(() => {
+    breadcrumbService.setBreadcrumbs(BREADCRUMB_SECTION.CLONE_TRANSFORM);
+    docTitleService.setTitle('createTransform');
+  }, []);
+
+  const api = useApi();
+
+  const transformId = match.params.transformId;
+
+  const [transformConfig, setTransformConfig] = useState<TransformPivotConfig>();
+  const [errorMessage, setErrorMessage] = useState();
+  const [isInitialized, setIsInitialized] = useState(false);
+  const [savedObjectId, setSavedObjectId] = useState<string | undefined>(undefined);
+
+  const fetchTransformConfig = async () => {
+    try {
+      const transformConfigs: GetTransformsResponse = await api.getTransforms(transformId);
+      if (isGetTransformsResponseError(transformConfigs)) {
+        setTransformConfig(undefined);
+        setErrorMessage(transformConfigs.error.msg);
+        setIsInitialized(true);
+        return;
+      }
+
+      await loadIndexPatterns(savedObjectsClient, indexPatterns);
+      const indexPatternTitle = Array.isArray(transformConfigs.transforms[0].source.index)
+        ? transformConfigs.transforms[0].source.index.join(',')
+        : transformConfigs.transforms[0].source.index;
+      const indexPatternId = getIndexPatternIdByTitle(indexPatternTitle);
+
+      if (indexPatternId === undefined) {
+        throw new Error(
+          i18n.translate('xpack.transform.clone.errorPromptText', {
+            defaultMessage: 'Could not fetch the Kibana index pattern ID.',
+          })
+        );
+      }
+
+      setSavedObjectId(indexPatternId);
+
+      setTransformConfig(transformConfigs.transforms[0]);
+      setErrorMessage(undefined);
+      setIsInitialized(true);
+    } catch (e) {
+      setTransformConfig(undefined);
+      if (e.message !== undefined) {
+        setErrorMessage(e.message);
+      } else {
+        setErrorMessage(JSON.stringify(e, null, 2));
+      }
+      setIsInitialized(true);
+    }
+  };
+
+  useEffect(() => {
+    fetchTransformConfig();
+    // The effect should only be called once.
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, []);
+
+  return (
+    <PrivilegesWrapper privileges={APP_CREATE_TRANSFORM_CLUSTER_PRIVILEGES}>
+      <EuiPageContent data-test-subj="transformPageCloneTransform">
+        <EuiTitle size="l">
+          <EuiFlexGroup alignItems="center">
+            <EuiFlexItem grow={true}>
+              <h1>
+                <FormattedMessage
+                  id="xpack.transform.transformsWizard.cloneTransformTitle"
+                  defaultMessage="Clone transform"
+                />
+                <span>&nbsp;</span>
+                <EuiBetaBadge
+                  label={i18n.translate('xpack.transform.transformsWizard.betaBadgeLabel', {
+                    defaultMessage: `Beta`,
+                  })}
+                  tooltipContent={i18n.translate(
+                    'xpack.transform.transformsWizard.betaBadgeTooltipContent',
+                    {
+                      defaultMessage: `Transforms are a beta feature. We'd love to hear your feedback.`,
+                    }
+                  )}
+                />
+              </h1>
+            </EuiFlexItem>
+            <EuiFlexItem grow={false}>
+              <EuiButtonEmpty
+                href={documentationLinksService.getTransformsDocUrl()}
+                target="_blank"
+                iconType="help"
+                data-test-subj="documentationLink"
+              >
+                <FormattedMessage
+                  id="xpack.transform.transformsWizard.transformDocsLinkText"
+                  defaultMessage="Transform docs"
+                />
+              </EuiButtonEmpty>
+            </EuiFlexItem>
+          </EuiFlexGroup>
+        </EuiTitle>
+        <EuiPageContentBody>
+          <EuiSpacer size="l" />
+          {typeof errorMessage !== 'undefined' && (
+            <EuiCallOut
+              title={i18n.translate('xpack.transform.clone.errorPromptTitle', {
+                defaultMessage: 'An error occurred getting the transform configuration.',
+              })}
+              color="danger"
+              iconType="alert"
+            >
+              <pre>{JSON.stringify(errorMessage)}</pre>
+            </EuiCallOut>
+          )}
+          {savedObjectId !== undefined && isInitialized === true && transformConfig !== undefined && (
+            <KibanaProvider savedObjectId={savedObjectId}>
+              <RenderOnlyWithInitializedKibanaContext>
+                <Wizard cloneConfig={transformConfig} />
+              </RenderOnlyWithInitializedKibanaContext>
+            </KibanaProvider>
+          )}
+        </EuiPageContentBody>
+      </EuiPageContent>
+    </PrivilegesWrapper>
+  );
+};
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/clone_transform/index.ts b/x-pack/legacy/plugins/transform/public/app/sections/clone_transform/index.ts
new file mode 100644
index 0000000000000..fef33d50130a7
--- /dev/null
+++ b/x-pack/legacy/plugins/transform/public/app/sections/clone_transform/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 { CloneTransformSection } from './clone_transform_section';
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/use_source_index_data.ts b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/use_source_index_data.ts
index 3fcc3cc15803b..e5c6783db1022 100644
--- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/use_source_index_data.ts
+++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/use_source_index_data.ts
@@ -17,6 +17,7 @@ import {
   getDefaultSelectableFields,
   getFlattenedFields,
   isDefaultQuery,
+  matchAllQuery,
   EsDoc,
   EsDocSource,
   EsFieldName,
@@ -75,7 +76,7 @@ export const useSourceIndexData = (
         index: indexPattern.title,
         size: SEARCH_SIZE,
         // Instead of using the default query (`*`), fall back to a more efficient `match_all` query.
-        body: { query: isDefaultQuery(query) ? { match_all: {} } : query },
+        body: { query: isDefaultQuery(query) ? matchAllQuery : query },
       });
 
       if (isErrorResponse(resp)) {
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/index.ts b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/index.ts
index 7c5b60715961b..881e8c6b26658 100644
--- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/index.ts
+++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/index.ts
@@ -5,8 +5,9 @@
  */
 
 export {
+  applyTransformConfigToDefineState,
+  getDefaultStepDefineState,
   StepDefineExposedState,
   StepDefineForm,
-  getDefaultStepDefineState,
 } from './step_define_form';
 export { StepDefineSummary } from './step_define_summary';
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx
index b8f63ef697e78..675386be8e2a5 100644
--- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx
+++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx
@@ -4,6 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+import { isEqual } from 'lodash';
 import React, { Fragment, FC, useEffect, useState } from 'react';
 
 import { i18n } from '@kbn/i18n';
@@ -27,7 +28,8 @@ import {
   EuiSwitch,
 } from '@elastic/eui';
 
-import { dictionaryToArray } from '../../../../../../common/types/common';
+import { TransformPivotConfig } from '../../../../common';
+import { dictionaryToArray, Dictionary } from '../../../../../../common/types/common';
 import { DropDown } from '../aggregation_dropdown';
 import { AggListForm } from '../aggregation_list';
 import { GroupByListForm } from '../group_by_list';
@@ -43,10 +45,12 @@ import {
 } from '../../../../lib/kibana';
 
 import {
-  AggName,
-  DropDownLabel,
   getPivotQuery,
   getPreviewRequestBody,
+  isMatchAllQuery,
+  matchAllQuery,
+  AggName,
+  DropDownLabel,
   PivotAggDict,
   PivotAggsConfig,
   PivotAggsConfigDict,
@@ -55,6 +59,7 @@ import {
   PivotGroupByConfigDict,
   PivotSupportedGroupByAggs,
   PIVOT_SUPPORTED_AGGS,
+  PIVOT_SUPPORTED_GROUP_BY_AGGS,
 } from '../../../../common';
 
 import { getPivotDropdownOptions } from './common';
@@ -89,6 +94,58 @@ export function getDefaultStepDefineState(
     valid: false,
   };
 }
+
+export function applyTransformConfigToDefineState(
+  state: StepDefineExposedState,
+  transformConfig?: TransformPivotConfig
+): StepDefineExposedState {
+  // apply the transform configuration to wizard DEFINE state
+  if (transformConfig !== undefined) {
+    // transform aggregations config to wizard state
+    state.aggList = Object.keys(transformConfig.pivot.aggregations).reduce((aggList, aggName) => {
+      const aggConfig = transformConfig.pivot.aggregations[aggName] as Dictionary<any>;
+      const agg = Object.keys(aggConfig)[0];
+      aggList[aggName] = {
+        ...aggConfig[agg],
+        agg: agg as PIVOT_SUPPORTED_AGGS,
+        aggName,
+        dropDownName: aggName,
+      } as PivotAggsConfig;
+      return aggList;
+    }, {} as PivotAggsConfigDict);
+
+    // transform group by config to wizard state
+    state.groupByList = Object.keys(transformConfig.pivot.group_by).reduce(
+      (groupByList, groupByName) => {
+        const groupByConfig = transformConfig.pivot.group_by[groupByName] as Dictionary<any>;
+        const groupBy = Object.keys(groupByConfig)[0];
+        groupByList[groupByName] = {
+          agg: groupBy as PIVOT_SUPPORTED_GROUP_BY_AGGS,
+          aggName: groupByName,
+          dropDownName: groupByName,
+          ...groupByConfig[groupBy],
+        } as PivotGroupByConfig;
+        return groupByList;
+      },
+      {} as PivotGroupByConfigDict
+    );
+
+    // only apply the query from the transform config to wizard state if it's not the default query
+    const query = transformConfig.source.query;
+    if (query !== undefined && !isEqual(query, matchAllQuery)) {
+      state.isAdvancedSourceEditorEnabled = true;
+      state.searchString = '';
+      state.searchQuery = query;
+      state.sourceConfigUpdated = true;
+    }
+
+    // applying a transform config to wizard state will always result in a valid configuration
+    state.valid = true;
+  }
+
+  return state;
+}
+
 export function isAggNameConflict(
   aggName: AggName,
   aggList: PivotAggsConfigDict,
@@ -208,10 +265,7 @@ export const StepDefineForm: FC<Props> = React.memo(({ overrides = {}, onChange
   const searchHandler = (d: Record<string, any>) => {
     const { filterQuery, queryString } = d;
     const newSearch = queryString === emptySearch ? defaultSearch : queryString;
-    const newSearchQuery =
-      filterQuery.match_all && Object.keys(filterQuery.match_all).length === 0
-        ? defaultSearch
-        : filterQuery;
+    const newSearchQuery = isMatchAllQuery(filterQuery) ? defaultSearch : filterQuery;
     setSearchString(newSearch);
     setSearchQuery(newSearchQuery);
   };
@@ -363,10 +417,10 @@ export const StepDefineForm: FC<Props> = React.memo(({ overrides = {}, onChange
         const aggConfigKeys = Object.keys(aggConfig);
         const agg = aggConfigKeys[0] as PivotSupportedGroupByAggs;
         newGroupByList[aggName] = {
+          ...aggConfig[agg],
           agg,
           aggName,
           dropDownName: '',
-          ...aggConfig[agg],
         };
       });
     }
@@ -380,10 +434,10 @@ export const StepDefineForm: FC<Props> = React.memo(({ overrides = {}, onChange
         const aggConfigKeys = Object.keys(aggConfig);
         const agg = aggConfigKeys[0] as PIVOT_SUPPORTED_AGGS;
         newAggList[aggName] = {
+          ...aggConfig[agg],
           agg,
           aggName,
           dropDownName: '',
-          ...aggConfig[agg],
         };
       });
     }
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/index.ts b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/index.ts
index e454ea32d76ed..5cbdf4500e3c3 100644
--- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/index.ts
+++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/index.ts
@@ -4,5 +4,9 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-export { StepDetailsForm, getDefaultStepDetailsState } from './step_details_form';
+export {
+  applyTransformConfigToDetailsState,
+  getDefaultStepDetailsState,
+  StepDetailsForm,
+} from './step_details_form';
 export { StepDetailsSummary } from './step_details_summary';
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx
index a01481fde343c..220923f88ed36 100644
--- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx
+++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx
@@ -49,6 +49,22 @@ export function getDefaultStepDetailsState(): StepDetailsExposedState {
   };
 }
 
+export function applyTransformConfigToDetailsState(
+  state: StepDetailsExposedState,
+  transformConfig?: TransformPivotConfig
+): StepDetailsExposedState {
+  // apply the transform configuration to wizard DETAILS state
+  if (transformConfig !== undefined) {
+    const time = transformConfig.sync?.time;
+    if (time !== undefined) {
+      state.continuousModeDateField = time.field;
+      state.continuousModeDelay = time.delay;
+      state.isContinuousModeEnabled = true;
+    }
+  }
+  return state;
+}
+
 interface Props {
   overrides?: StepDetailsExposedState;
   onChange(s: StepDetailsExposedState): void;
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/wizard/wizard.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/wizard/wizard.tsx
index 109cf81da6caa..f1861755d9742 100644
--- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/wizard/wizard.tsx
+++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/wizard/wizard.tsx
@@ -12,16 +12,22 @@ import { EuiSteps, EuiStepStatus } from '@elastic/eui';
 
 import { useKibanaContext } from '../../../../lib/kibana';
 
-import { getCreateRequestBody } from '../../../../common';
+import { getCreateRequestBody, TransformPivotConfig } from '../../../../common';
 
 import {
+  applyTransformConfigToDefineState,
+  getDefaultStepDefineState,
   StepDefineExposedState,
   StepDefineForm,
   StepDefineSummary,
-  getDefaultStepDefineState,
 } from '../step_define';
 import { getDefaultStepCreateState, StepCreateForm, StepCreateSummary } from '../step_create';
-import { getDefaultStepDetailsState, StepDetailsForm, StepDetailsSummary } from '../step_details';
+import {
+  applyTransformConfigToDetailsState,
+  getDefaultStepDetailsState,
+  StepDetailsForm,
+  StepDetailsSummary,
+} from '../step_details';
 import { WizardNav } from '../wizard_nav';
 
 enum KBN_MANAGEMENT_PAGE_CLASSNAME {
@@ -67,17 +73,25 @@ const StepDefine: FC<DefinePivotStepProps> = ({
   );
 };
 
-export const Wizard: FC = React.memo(() => {
+interface WizardProps {
+  cloneConfig?: TransformPivotConfig;
+}
+
+export const Wizard: FC<WizardProps> = React.memo(({ cloneConfig }) => {
   const kibanaContext = useKibanaContext();
 
   // The current WIZARD_STEP
   const [currentStep, setCurrentStep] = useState(WIZARD_STEPS.DEFINE);
 
   // The DEFINE state
-  const [stepDefineState, setStepDefineState] = useState(getDefaultStepDefineState(kibanaContext));
+  const [stepDefineState, setStepDefineState] = useState(
+    applyTransformConfigToDefineState(getDefaultStepDefineState(kibanaContext), cloneConfig)
+  );
 
   // The DETAILS state
-  const [stepDetailsState, setStepDetailsState] = useState(getDefaultStepDetailsState());
+  const [stepDetailsState, setStepDetailsState] = useState(
+    applyTransformConfigToDetailsState(getDefaultStepDetailsState(), cloneConfig)
+  );
 
   const stepDetails =
     currentStep === WIZARD_STEPS.DETAILS ? (
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_clone.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_clone.tsx
new file mode 100644
index 0000000000000..40098ac7ef72a
--- /dev/null
+++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_clone.tsx
@@ -0,0 +1,60 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { FC, useContext } from 'react';
+import { useHistory } from 'react-router-dom';
+import { i18n } from '@kbn/i18n';
+import { EuiButtonEmpty, EuiToolTip } from '@elastic/eui';
+
+import {
+  createCapabilityFailureMessage,
+  AuthorizationContext,
+} from '../../../../lib/authorization';
+
+import { CLIENT_BASE_PATH, SECTION_SLUG } from '../../../../constants';
+
+interface CloneActionProps {
+  itemId: string;
+}
+
+export const CloneAction: FC<CloneActionProps> = ({ itemId }) => {
+  const history = useHistory();
+
+  const { canCreateTransform } = useContext(AuthorizationContext).capabilities;
+
+  const buttonCloneText = i18n.translate('xpack.transform.transformList.cloneActionName', {
+    defaultMessage: 'Clone',
+  });
+
+  function clickHandler() {
+    history.push(`${CLIENT_BASE_PATH}/${SECTION_SLUG.CLONE_TRANSFORM}/${itemId}`);
+  }
+
+  const cloneButton = (
+    <EuiButtonEmpty
+      size="xs"
+      color="text"
+      disabled={!canCreateTransform}
+      iconType="copy"
+      onClick={clickHandler}
+      aria-label={buttonCloneText}
+    >
+      {buttonCloneText}
+    </EuiButtonEmpty>
+  );
+
+  if (!canCreateTransform) {
+    const content = createCapabilityFailureMessage('canStartStopTransform');
+
+    return (
+      <EuiToolTip position="top" content={content}>
+        {cloneButton}
+      </EuiToolTip>
+    );
+  }
+
+  return <>{cloneButton}</>;
+};
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/actions.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/actions.test.tsx
index 3d847890b2bd5..ef92a5e3859d7 100644
--- a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/actions.test.tsx
+++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/actions.test.tsx
@@ -12,9 +12,10 @@ describe('Transform: Transform List Actions', () => {
   test('getActions()', () => {
     const actions = getActions({ forceDisable: false });
 
-    expect(actions).toHaveLength(2);
+    expect(actions).toHaveLength(3);
     expect(actions[0].isPrimary).toBeTruthy();
     expect(typeof actions[0].render).toBe('function');
     expect(typeof actions[1].render).toBe('function');
+    expect(typeof actions[2].render).toBe('function');
   });
 });
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/actions.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/actions.tsx
index 1773405e36e39..3e3829973e328 100644
--- a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/actions.tsx
+++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/actions.tsx
@@ -6,6 +6,7 @@
 
 import React from 'react';
 import { TransformListRow, TRANSFORM_STATE } from '../../../../common';
+import { CloneAction } from './action_clone';
 import { StartAction } from './action_start';
 import { StopAction } from './action_stop';
 import { DeleteAction } from './action_delete';
@@ -21,6 +22,11 @@ export const getActions = ({ forceDisable }: { forceDisable: boolean }) => {
         return <StopAction items={[item]} forceDisable={forceDisable} />;
       },
     },
+    {
+      render: (item: TransformListRow) => {
+        return <CloneAction itemId={item.id} />;
+      },
+    },
     {
       render: (item: TransformListRow) => {
         return <DeleteAction items={[item]} forceDisable={forceDisable} />;
diff --git a/x-pack/legacy/plugins/transform/public/app/services/navigation/breadcrumb.ts b/x-pack/legacy/plugins/transform/public/app/services/navigation/breadcrumb.ts
index 0e0b174f28f99..5a2f698b35154 100644
--- a/x-pack/legacy/plugins/transform/public/app/services/navigation/breadcrumb.ts
+++ b/x-pack/legacy/plugins/transform/public/app/services/navigation/breadcrumb.ts
@@ -10,6 +10,7 @@ import { linkToHome } from './links';
 export enum BREADCRUMB_SECTION {
   MANAGEMENT = 'management',
   HOME = 'home',
+  CLONE_TRANSFORM = 'cloneTransform',
   CREATE_TRANSFORM = 'createTransform',
 }
 
@@ -27,6 +28,7 @@ class BreadcrumbService {
   private breadcrumbs: Breadcrumbs = {
     management: [],
     home: [],
+    cloneTransform: [],
     createTransform: [],
   };
 
@@ -42,6 +44,12 @@ class BreadcrumbService {
         href: linkToHome(),
       },
     ];
+    this.breadcrumbs.cloneTransform = [
+      ...this.breadcrumbs.home,
+      {
+        text: textService.breadcrumbs.cloneTransform,
+      },
+    ];
     this.breadcrumbs.createTransform = [
       ...this.breadcrumbs.home,
       {
diff --git a/x-pack/legacy/plugins/transform/public/app/services/text/text.ts b/x-pack/legacy/plugins/transform/public/app/services/text/text.ts
index df1b07e171c62..af4aea7e8db4e 100644
--- a/x-pack/legacy/plugins/transform/public/app/services/text/text.ts
+++ b/x-pack/legacy/plugins/transform/public/app/services/text/text.ts
@@ -14,6 +14,9 @@ class TextService {
       home: i18n.translate('xpack.transform.home.breadcrumbTitle', {
         defaultMessage: 'Transforms',
       }),
+      cloneTransform: i18n.translate('xpack.transform.cloneTransform.breadcrumbTitle', {
+        defaultMessage: 'Clone transform',
+      }),
       createTransform: i18n.translate('xpack.transform.createTransform.breadcrumbTitle', {
         defaultMessage: 'Create transform',
       }),

From 68e79e45a5f49d749ea6d0ef1f039b914cbc6bf9 Mon Sep 17 00:00:00 2001
From: Vadim Dalecky <streamich@users.noreply.github.com>
Date: Fri, 21 Feb 2020 15:35:20 +0100
Subject: [PATCH 124/174] Trigger context (#57870)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat: 🎸 annotate with comments Trigger and add createContext(

* feat: 🎸 add TriggerContext<T> type

* feat: 🎸 improve trigger setup in embeddable plugin

* feat: 🎸 export trigger vars and types from embeddable

* feat: 🎸 add internal representation of Trigger

* feat: 🎸 use new trigger interface to execute in vis embeddable

* feat: 🎸 add TriggerContextMapping interface

* feat: 🎸 improve trigger types

* refactor: 💡 remove trigger ID getter

* chore: 🤖 remove commented out line

* chore: 🤖 re-export more trigger stuff

* refactor: 💡 simplify Trigger interface

* suggestions

* Remove unused type

Co-authored-by: Stacey Gammon <gammon@elastic.co>
---
 .../public/embeddable/visualize_embeddable.ts | 14 ++--
 src/plugins/embeddable/public/bootstrap.ts    | 71 ++++++++---------
 src/plugins/embeddable/public/index.ts        | 26 ++++---
 .../embeddable/public/lib/triggers/index.ts   |  6 +-
 .../public/lib/triggers/triggers.ts           | 65 ++++++++++++++++
 src/plugins/ui_actions/public/index.ts        |  3 +-
 .../public/service/ui_actions_service.test.ts |  5 +-
 .../public/service/ui_actions_service.ts      | 65 ++++++----------
 .../ui_actions/public/triggers/index.ts       |  4 +-
 .../ui_actions/public/triggers/trigger.ts     | 31 +++++++-
 .../public/triggers/trigger_contract.ts       | 56 ++++++++++++++
 .../public/triggers/trigger_internal.ts       | 76 +++++++++++++++++++
 src/plugins/ui_actions/public/types.ts        | 10 ++-
 13 files changed, 321 insertions(+), 111 deletions(-)
 create mode 100644 src/plugins/embeddable/public/lib/triggers/triggers.ts
 create mode 100644 src/plugins/ui_actions/public/triggers/trigger_contract.ts
 create mode 100644 src/plugins/ui_actions/public/triggers/trigger_internal.ts

diff --git a/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts
index 4c6c12f825609..72a0ef72b5693 100644
--- a/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts
+++ b/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts
@@ -24,6 +24,7 @@ import * as Rx from 'rxjs';
 import { buildPipeline } from 'ui/visualize/loader/pipeline_helpers';
 import { npStart } from 'ui/new_platform';
 import { IExpressionLoaderParams } from 'src/plugins/expressions/public';
+import { EmbeddableVisTriggerContext } from 'src/plugins/embeddable/public';
 import { VISUALIZE_EMBEDDABLE_TYPE } from './constants';
 import {
   IIndexPattern,
@@ -39,8 +40,8 @@ import {
   EmbeddableOutput,
   Embeddable,
   Container,
-  VALUE_CLICK_TRIGGER,
-  SELECT_RANGE_TRIGGER,
+  selectRangeTrigger,
+  valueClickTrigger,
 } from '../../../../../plugins/embeddable/public';
 import { dispatchRenderComplete } from '../../../../../plugins/kibana_utils/public';
 import { SavedObject } from '../../../../../plugins/saved_objects/public';
@@ -301,13 +302,14 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut
         }
 
         if (!this.input.disableTriggers) {
-          const eventName = event.name === 'brush' ? SELECT_RANGE_TRIGGER : VALUE_CLICK_TRIGGER;
-
-          npStart.plugins.uiActions.executeTriggerActions(eventName, {
+          const triggerId: 'SELECT_RANGE_TRIGGER' | 'VALUE_CLICK_TRIGGER' =
+            event.name === 'brush' ? selectRangeTrigger.id : valueClickTrigger.id;
+          const context: EmbeddableVisTriggerContext = {
             embeddable: this,
             timeFieldName: this.vis.indexPattern.timeFieldName,
             data: event.data,
-          });
+          };
+          npStart.plugins.uiActions.getTrigger(triggerId).exec(context);
         }
       })
     );
diff --git a/src/plugins/embeddable/public/bootstrap.ts b/src/plugins/embeddable/public/bootstrap.ts
index 9a364e84092ca..9989345df2796 100644
--- a/src/plugins/embeddable/public/bootstrap.ts
+++ b/src/plugins/embeddable/public/bootstrap.ts
@@ -16,56 +16,49 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
-import { UiActionsSetup, Trigger } from 'src/plugins/ui_actions/public';
+import { UiActionsSetup } from 'src/plugins/ui_actions/public';
+import { Filter } from '../../data/public';
 import {
-  CONTEXT_MENU_TRIGGER,
-  APPLY_FILTER_TRIGGER,
+  applyFilterTrigger,
+  contextMenuTrigger,
   createFilterAction,
-  PANEL_BADGE_TRIGGER,
-  SELECT_RANGE_TRIGGER,
+  panelBadgeTrigger,
+  selectRangeTrigger,
+  valueClickTrigger,
+  EmbeddableVisTriggerContext,
+  IEmbeddable,
+  APPLY_FILTER_TRIGGER,
   VALUE_CLICK_TRIGGER,
+  SELECT_RANGE_TRIGGER,
+  CONTEXT_MENU_TRIGGER,
+  PANEL_BADGE_TRIGGER,
 } from './lib';
 
+declare module '../../ui_actions/public' {
+  export interface TriggerContextMapping {
+    [SELECT_RANGE_TRIGGER]: EmbeddableVisTriggerContext;
+    [VALUE_CLICK_TRIGGER]: EmbeddableVisTriggerContext;
+    [APPLY_FILTER_TRIGGER]: {
+      embeddable: IEmbeddable;
+      filters: Filter[];
+    };
+    [CONTEXT_MENU_TRIGGER]: object;
+    [PANEL_BADGE_TRIGGER]: object;
+  }
+}
+
 /**
  * This method initializes Embeddable plugin with initial set of
  * triggers and actions.
- *
- * @param api
  */
 export const bootstrap = (uiActions: UiActionsSetup) => {
-  const triggerContext: Trigger = {
-    id: CONTEXT_MENU_TRIGGER,
-    title: 'Context menu',
-    description: 'Triggered on top-right corner context-menu select.',
-  };
-  const triggerFilter: Trigger = {
-    id: APPLY_FILTER_TRIGGER,
-    title: 'Filter click',
-    description: 'Triggered when user applies filter to an embeddable.',
-  };
-  const triggerBadge: Trigger = {
-    id: PANEL_BADGE_TRIGGER,
-    title: 'Panel badges',
-    description: 'Actions appear in title bar when an embeddable loads in a panel',
-  };
-  const selectRangeTrigger: Trigger = {
-    id: SELECT_RANGE_TRIGGER,
-    title: 'Select range',
-    description: 'Applies a range filter',
-  };
-  const valueClickTrigger: Trigger = {
-    id: VALUE_CLICK_TRIGGER,
-    title: 'Value clicked',
-    description: 'Value was clicked',
-  };
+  uiActions.registerTrigger(contextMenuTrigger);
+  uiActions.registerTrigger(applyFilterTrigger);
+  uiActions.registerTrigger(panelBadgeTrigger);
+  uiActions.registerTrigger(selectRangeTrigger);
+  uiActions.registerTrigger(valueClickTrigger);
+
   const actionApplyFilter = createFilterAction();
 
-  uiActions.registerTrigger(triggerContext);
-  uiActions.registerTrigger(triggerFilter);
   uiActions.registerAction(actionApplyFilter);
-  uiActions.registerTrigger(triggerBadge);
-  uiActions.registerTrigger(selectRangeTrigger);
-  uiActions.registerTrigger(valueClickTrigger);
-  // uiActions.attachAction(triggerFilter.id, actionApplyFilter.id);
 };
diff --git a/src/plugins/embeddable/public/index.ts b/src/plugins/embeddable/public/index.ts
index b0e14a04a9944..2eafe16442e18 100644
--- a/src/plugins/embeddable/public/index.ts
+++ b/src/plugins/embeddable/public/index.ts
@@ -23,18 +23,17 @@ import { PluginInitializerContext } from 'src/core/public';
 import { EmbeddablePublicPlugin } from './plugin';
 
 export {
+  Adapters,
   ADD_PANEL_ACTION_ID,
+  AddPanelAction,
   APPLY_FILTER_ACTION,
   APPLY_FILTER_TRIGGER,
-  PANEL_BADGE_TRIGGER,
-  SELECT_RANGE_TRIGGER,
-  VALUE_CLICK_TRIGGER,
-  Adapters,
-  AddPanelAction,
-  CONTEXT_MENU_TRIGGER,
+  applyFilterTrigger,
   Container,
   ContainerInput,
   ContainerOutput,
+  CONTEXT_MENU_TRIGGER,
+  contextMenuTrigger,
   EDIT_PANEL_ACTION_ID,
   EditPanelAction,
   Embeddable,
@@ -42,25 +41,32 @@ export {
   EmbeddableChildPanelProps,
   EmbeddableFactory,
   EmbeddableFactoryNotFoundError,
+  EmbeddableFactoryRenderer,
   EmbeddableInput,
   EmbeddableInstanceConfiguration,
   EmbeddableOutput,
   EmbeddablePanel,
+  EmbeddableRoot,
+  EmbeddableVisTriggerContext,
   ErrorEmbeddable,
   GetEmbeddableFactories,
   GetEmbeddableFactory,
   IContainer,
   IEmbeddable,
+  isErrorEmbeddable,
+  openAddPanelFlyout,
   OutputSpec,
+  PANEL_BADGE_TRIGGER,
+  panelBadgeTrigger,
   PanelNotFoundError,
   PanelState,
   PropertySpec,
+  SELECT_RANGE_TRIGGER,
+  selectRangeTrigger,
+  VALUE_CLICK_TRIGGER,
+  valueClickTrigger,
   ViewMode,
-  isErrorEmbeddable,
-  openAddPanelFlyout,
   withEmbeddableSubscription,
-  EmbeddableFactoryRenderer,
-  EmbeddableRoot,
 } from './lib';
 
 export function plugin(initializerContext: PluginInitializerContext) {
diff --git a/src/plugins/embeddable/public/lib/triggers/index.ts b/src/plugins/embeddable/public/lib/triggers/index.ts
index 72565b3f527ad..4f981562a49ba 100644
--- a/src/plugins/embeddable/public/lib/triggers/index.ts
+++ b/src/plugins/embeddable/public/lib/triggers/index.ts
@@ -17,8 +17,4 @@
  * under the License.
  */
 
-export const CONTEXT_MENU_TRIGGER = 'CONTEXT_MENU_TRIGGER';
-export const APPLY_FILTER_TRIGGER = 'FILTER_TRIGGER';
-export const SELECT_RANGE_TRIGGER = 'SELECT_RANGE_TRIGGER';
-export const VALUE_CLICK_TRIGGER = 'VALUE_CLICK_TRIGGER';
-export const PANEL_BADGE_TRIGGER = 'PANEL_BADGE_TRIGGER';
+export * from './triggers';
diff --git a/src/plugins/embeddable/public/lib/triggers/triggers.ts b/src/plugins/embeddable/public/lib/triggers/triggers.ts
new file mode 100644
index 0000000000000..491d9e730eb75
--- /dev/null
+++ b/src/plugins/embeddable/public/lib/triggers/triggers.ts
@@ -0,0 +1,65 @@
+/*
+ * 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 { Trigger } from '../../../../ui_actions/public';
+import { IEmbeddable } from '..';
+
+export interface EmbeddableVisTriggerContext {
+  embeddable: IEmbeddable;
+  timeFieldName: string;
+  data: {
+    e: MouseEvent;
+    data: unknown;
+  };
+}
+
+export const SELECT_RANGE_TRIGGER = 'SELECT_RANGE_TRIGGER';
+export const selectRangeTrigger: Trigger<'SELECT_RANGE_TRIGGER'> = {
+  id: SELECT_RANGE_TRIGGER,
+  title: 'Select range',
+  description: 'Applies a range filter',
+};
+
+export const VALUE_CLICK_TRIGGER = 'VALUE_CLICK_TRIGGER';
+export const valueClickTrigger: Trigger<'VALUE_CLICK_TRIGGER'> = {
+  id: VALUE_CLICK_TRIGGER,
+  title: 'Value clicked',
+  description: 'Value was clicked',
+};
+
+export const CONTEXT_MENU_TRIGGER = 'CONTEXT_MENU_TRIGGER';
+export const contextMenuTrigger: Trigger<'CONTEXT_MENU_TRIGGER'> = {
+  id: CONTEXT_MENU_TRIGGER,
+  title: 'Context menu',
+  description: 'Triggered on top-right corner context-menu select.',
+};
+
+export const APPLY_FILTER_TRIGGER = 'FILTER_TRIGGER';
+export const applyFilterTrigger: Trigger<'FILTER_TRIGGER'> = {
+  id: APPLY_FILTER_TRIGGER,
+  title: 'Filter click',
+  description: 'Triggered when user applies filter to an embeddable.',
+};
+
+export const PANEL_BADGE_TRIGGER = 'PANEL_BADGE_TRIGGER';
+export const panelBadgeTrigger: Trigger<'PANEL_BADGE_TRIGGER'> = {
+  id: PANEL_BADGE_TRIGGER,
+  title: 'Panel badges',
+  description: 'Actions appear in title bar when an embeddable loads in a panel',
+};
diff --git a/src/plugins/ui_actions/public/index.ts b/src/plugins/ui_actions/public/index.ts
index 83a08b11fa4c2..1ce48d5460b2e 100644
--- a/src/plugins/ui_actions/public/index.ts
+++ b/src/plugins/ui_actions/public/index.ts
@@ -29,7 +29,8 @@ export { UiActionsSetup, UiActionsStart } from './plugin';
 export { UiActionsServiceParams, UiActionsService } from './service';
 export { Action, createAction, IncompatibleActionError } from './actions';
 export { buildContextMenuForActions } from './context_menu';
-export { Trigger } from './triggers';
+export { Trigger, TriggerContext } from './triggers';
+export { TriggerContextMapping } from './types';
 
 /**
  * @deprecated
diff --git a/src/plugins/ui_actions/public/service/ui_actions_service.test.ts b/src/plugins/ui_actions/public/service/ui_actions_service.test.ts
index 2bbe106c49a25..8963ba4ddb005 100644
--- a/src/plugins/ui_actions/public/service/ui_actions_service.test.ts
+++ b/src/plugins/ui_actions/public/service/ui_actions_service.test.ts
@@ -68,7 +68,7 @@ describe('UiActionsService', () => {
 
       const trigger = service.getTrigger('bar');
 
-      expect(trigger).toEqual({
+      expect(trigger).toMatchObject({
         description: 'foo',
         id: 'bar',
         title: 'baz',
@@ -345,8 +345,9 @@ describe('UiActionsService', () => {
         id: 'bar',
         title: 'baz',
       });
+      const triggerContract = service.getTrigger('bar');
 
-      expect(triggers.get('bar')).toEqual({
+      expect(triggerContract).toMatchObject({
         description: 'foo',
         id: 'bar',
         title: 'baz',
diff --git a/src/plugins/ui_actions/public/service/ui_actions_service.ts b/src/plugins/ui_actions/public/service/ui_actions_service.ts
index a62d2aa356435..ae409830bbb6e 100644
--- a/src/plugins/ui_actions/public/service/ui_actions_service.ts
+++ b/src/plugins/ui_actions/public/service/ui_actions_service.ts
@@ -17,10 +17,11 @@
  * under the License.
  */
 
-import { TriggerRegistry, ActionRegistry, TriggerToActionsRegistry } from '../types';
+import { TriggerRegistry, ActionRegistry, TriggerToActionsRegistry, TriggerId } from '../types';
 import { Action } from '../actions';
-import { Trigger } from '../triggers/trigger';
-import { buildContextMenuForActions, openContextMenu } from '../context_menu';
+import { Trigger, TriggerContext } from '../triggers/trigger';
+import { TriggerInternal } from '../triggers/trigger_internal';
+import { TriggerContract } from '../triggers/trigger_contract';
 
 export interface UiActionsServiceParams {
   readonly triggers?: TriggerRegistry;
@@ -52,18 +53,20 @@ export class UiActionsService {
       throw new Error(`Trigger [trigger.id = ${trigger.id}] already registered.`);
     }
 
-    this.triggers.set(trigger.id, trigger);
+    const triggerInternal = new TriggerInternal(this, trigger);
+
+    this.triggers.set(trigger.id, triggerInternal);
     this.triggerToActions.set(trigger.id, []);
   };
 
-  public readonly getTrigger = (id: string) => {
-    const trigger = this.triggers.get(id);
+  public readonly getTrigger = <T extends TriggerId>(triggerId: T): TriggerContract<T> => {
+    const trigger = this.triggers.get(triggerId as string);
 
     if (!trigger) {
-      throw new Error(`Trigger [triggerId = ${id}] does not exist.`);
+      throw new Error(`Trigger [triggerId = ${triggerId}] does not exist.`);
     }
 
-    return trigger;
+    return trigger.contract;
   };
 
   public readonly registerAction = (action: Action) => {
@@ -128,41 +131,17 @@ export class UiActionsService {
     );
   };
 
-  private async executeSingleAction<A>(action: Action<A>, actionContext: A) {
-    const href = action.getHref && action.getHref(actionContext);
-
-    if (href) {
-      window.location.href = href;
-      return;
-    }
-
-    await action.execute(actionContext);
-  }
-
-  private async executeMultipleActions<C>(actions: Action[], actionContext: C) {
-    const panel = await buildContextMenuForActions({
-      actions,
-      actionContext,
-      closeMenu: () => session.close(),
-    });
-    const session = openContextMenu([panel]);
-  }
-
-  public readonly executeTriggerActions = async <C>(triggerId: string, actionContext: C) => {
-    const actions = await this.getTriggerCompatibleActions!(triggerId, actionContext);
-
-    if (!actions.length) {
-      throw new Error(
-        `No compatible actions found to execute for trigger [triggerId = ${triggerId}].`
-      );
-    }
-
-    if (actions.length === 1) {
-      await this.executeSingleAction(actions[0], actionContext);
-      return;
-    }
-
-    await this.executeMultipleActions(actions, actionContext);
+  /**
+   * @deprecated
+   *
+   * Use `plugins.uiActions.getTrigger(triggerId).exec(params)` instead.
+   */
+  public readonly executeTriggerActions = async <T extends TriggerId>(
+    triggerId: T,
+    context: TriggerContext<T>
+  ) => {
+    const trigger = this.getTrigger<T>(triggerId);
+    await trigger.exec(context);
   };
 
   /**
diff --git a/src/plugins/ui_actions/public/triggers/index.ts b/src/plugins/ui_actions/public/triggers/index.ts
index a34c6eda61ba0..1ae2a19c4001f 100644
--- a/src/plugins/ui_actions/public/triggers/index.ts
+++ b/src/plugins/ui_actions/public/triggers/index.ts
@@ -17,4 +17,6 @@
  * under the License.
  */
 
-export { Trigger } from './trigger';
+export * from './trigger';
+export * from './trigger_contract';
+export * from './trigger_internal';
diff --git a/src/plugins/ui_actions/public/triggers/trigger.ts b/src/plugins/ui_actions/public/triggers/trigger.ts
index ba83f5619e250..2c019b09881d1 100644
--- a/src/plugins/ui_actions/public/triggers/trigger.ts
+++ b/src/plugins/ui_actions/public/triggers/trigger.ts
@@ -17,8 +17,35 @@
  * under the License.
  */
 
-export interface Trigger {
-  id: string;
+import { TriggerContextMapping, TriggerId } from '../types';
+
+/**
+ * This is a convenience interface used to register a *trigger*.
+ *
+ * `Trigger` specifies a named anchor to which `Action` can be attached. When
+ * `Trigger` is being *called* it creates a `Context` object and passes it to
+ * the `execute` method of an `Action`.
+ *
+ * More than one action can be attached to a single trigger, in which case when
+ * trigger is *called* it first displays a context menu for user to pick a
+ * single action to execute.
+ */
+export interface Trigger<ID extends TriggerId = TriggerId> {
+  /**
+   * Unique name of the trigger as identified in `ui_actions` plugin trigger
+   * registry, such as "SELECT_RANGE_TRIGGER" or "VALUE_CLICK_TRIGGER".
+   */
+  id: ID;
+
+  /**
+   * User friendly name of the trigger.
+   */
   title?: string;
+
+  /**
+   * A longer user friendly description of the trigger.
+   */
   description?: string;
 }
+
+export type TriggerContext<T> = T extends TriggerId ? TriggerContextMapping[T] : never;
diff --git a/src/plugins/ui_actions/public/triggers/trigger_contract.ts b/src/plugins/ui_actions/public/triggers/trigger_contract.ts
new file mode 100644
index 0000000000000..853b83dccabcc
--- /dev/null
+++ b/src/plugins/ui_actions/public/triggers/trigger_contract.ts
@@ -0,0 +1,56 @@
+/*
+ * 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 { TriggerContext } from './trigger';
+import { TriggerInternal } from './trigger_internal';
+import { TriggerId } from '../types';
+
+/**
+ * This is a public representation of a trigger that is provided to other plugins.
+ */
+export class TriggerContract<T extends TriggerId> {
+  /**
+   * Unique name of the trigger as identified in `ui_actions` plugin trigger
+   * registry, such as "SELECT_RANGE_TRIGGER" or "VALUE_CLICK_TRIGGER".
+   */
+  public readonly id: T;
+
+  /**
+   * User friendly name of the trigger.
+   */
+  public readonly title?: string;
+
+  /**
+   * A longer user friendly description of the trigger.
+   */
+  public readonly description?: string;
+
+  constructor(private readonly internal: TriggerInternal<T>) {
+    this.id = this.internal.trigger.id;
+    this.title = this.internal.trigger.title;
+    this.description = this.internal.trigger.description;
+  }
+
+  /**
+   * Use this method to execute action attached to this trigger.
+   */
+  public readonly exec = async (context: TriggerContext<T>) => {
+    await this.internal.execute(context);
+  };
+}
diff --git a/src/plugins/ui_actions/public/triggers/trigger_internal.ts b/src/plugins/ui_actions/public/triggers/trigger_internal.ts
new file mode 100644
index 0000000000000..efcdc72ecad57
--- /dev/null
+++ b/src/plugins/ui_actions/public/triggers/trigger_internal.ts
@@ -0,0 +1,76 @@
+/*
+ * 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 { TriggerContext, Trigger } from './trigger';
+import { TriggerContract } from './trigger_contract';
+import { UiActionsService } from '../service';
+import { Action } from '../actions';
+import { buildContextMenuForActions, openContextMenu } from '../context_menu';
+import { TriggerId } from '../types';
+
+/**
+ * Internal representation of a trigger kept for consumption only internally
+ * within `ui_actions` plugin.
+ */
+export class TriggerInternal<T extends TriggerId> {
+  public readonly contract = new TriggerContract<T>(this);
+
+  constructor(public readonly service: UiActionsService, public readonly trigger: Trigger<T>) {}
+
+  public async execute(context: TriggerContext<T>) {
+    const triggerId = this.trigger.id;
+    const actions = await this.service.getTriggerCompatibleActions!(triggerId, context);
+
+    if (!actions.length) {
+      throw new Error(
+        `No compatible actions found to execute for trigger [triggerId = ${triggerId}].`
+      );
+    }
+
+    if (actions.length === 1) {
+      await this.executeSingleAction(actions[0], context);
+      return;
+    }
+
+    await this.executeMultipleActions(actions, context);
+  }
+
+  private async executeSingleAction(action: Action<TriggerContext<T>>, context: TriggerContext<T>) {
+    const href = action.getHref && action.getHref(context);
+
+    if (href) {
+      window.location.href = href;
+      return;
+    }
+
+    await action.execute(context);
+  }
+
+  private async executeMultipleActions(
+    actions: Array<Action<TriggerContext<T>>>,
+    context: TriggerContext<T>
+  ) {
+    const panel = await buildContextMenuForActions({
+      actions,
+      actionContext: context,
+      closeMenu: () => session.close(),
+    });
+    const session = openContextMenu([panel]);
+  }
+}
diff --git a/src/plugins/ui_actions/public/types.ts b/src/plugins/ui_actions/public/types.ts
index 9bd6ffdef2af3..8daa893eb4347 100644
--- a/src/plugins/ui_actions/public/types.ts
+++ b/src/plugins/ui_actions/public/types.ts
@@ -18,8 +18,14 @@
  */
 
 import { Action } from './actions/action';
-import { Trigger } from './triggers/trigger';
+import { TriggerInternal } from './triggers/trigger_internal';
 
-export type TriggerRegistry = Map<string, Trigger>;
+export type TriggerRegistry = Map<string, TriggerInternal<any>>;
 export type ActionRegistry = Map<string, Action>;
 export type TriggerToActionsRegistry = Map<string, string[]>;
+
+export type TriggerId = string;
+
+export interface TriggerContextMapping {
+  [key: string]: object;
+}

From c91b6ceebcec85cf0991da555ba94605e5fcb029 Mon Sep 17 00:00:00 2001
From: Brandon Kobel <brandon.kobel@elastic.co>
Date: Fri, 21 Feb 2020 07:27:40 -0800
Subject: [PATCH 125/174] Updating to @elastic/lodash@3.10.1-kibana4 (#54662)

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 package.json                           | 2 +-
 packages/kbn-interpreter/package.json  | 2 +-
 packages/kbn-ui-framework/package.json | 2 +-
 x-pack/package.json                    | 2 +-
 yarn.lock                              | 8 ++++----
 5 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/package.json b/package.json
index b11234b6312e9..cb87c48ab7f2a 100644
--- a/package.json
+++ b/package.json
@@ -205,7 +205,7 @@
     "leaflet.heat": "0.2.0",
     "less": "^2.7.3",
     "less-loader": "5.0.0",
-    "lodash": "npm:@elastic/lodash@3.10.1-kibana3",
+    "lodash": "npm:@elastic/lodash@3.10.1-kibana4",
     "lodash.clonedeep": "^4.5.0",
     "lru-cache": "4.1.5",
     "markdown-it": "^10.0.0",
diff --git a/packages/kbn-interpreter/package.json b/packages/kbn-interpreter/package.json
index d2f0b0c358284..5dede7fbf1aaa 100644
--- a/packages/kbn-interpreter/package.json
+++ b/packages/kbn-interpreter/package.json
@@ -11,7 +11,7 @@
   "dependencies": {
     "@babel/runtime": "^7.5.5",
     "@kbn/i18n": "1.0.0",
-    "lodash": "npm:@elastic/lodash@3.10.1-kibana3",
+    "lodash": "npm:@elastic/lodash@3.10.1-kibana4",
     "lodash.clone": "^4.5.0",
     "uuid": "3.3.2"
   },
diff --git a/packages/kbn-ui-framework/package.json b/packages/kbn-ui-framework/package.json
index fc245ca3fe921..0402a83d3d274 100644
--- a/packages/kbn-ui-framework/package.json
+++ b/packages/kbn-ui-framework/package.json
@@ -17,7 +17,7 @@
   "dependencies": {
     "classnames": "2.2.6",
     "focus-trap-react": "^3.1.1",
-    "lodash": "npm:@elastic/lodash@3.10.1-kibana3",
+    "lodash": "npm:@elastic/lodash@3.10.1-kibana4",
     "prop-types": "15.6.0",
     "react": "^16.12.0",
     "react-ace": "^5.9.0",
diff --git a/x-pack/package.json b/x-pack/package.json
index 9d6b5d76a58e7..551e466893f93 100644
--- a/x-pack/package.json
+++ b/x-pack/package.json
@@ -262,7 +262,7 @@
     "json-stable-stringify": "^1.0.1",
     "jsonwebtoken": "^8.5.1",
     "jsts": "^1.6.2",
-    "lodash": "npm:@elastic/lodash@3.10.1-kibana3",
+    "lodash": "npm:@elastic/lodash@3.10.1-kibana4",
     "lodash.keyby": "^4.6.0",
     "lodash.mean": "^4.1.0",
     "lodash.topath": "^4.5.2",
diff --git a/yarn.lock b/yarn.lock
index 6b3370407e3b2..2caaac974dfad 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -19812,10 +19812,10 @@ lodash@^3.10.1:
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
   integrity sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=
 
-"lodash@npm:@elastic/lodash@3.10.1-kibana3":
-  version "3.10.1-kibana3"
-  resolved "https://registry.yarnpkg.com/@elastic/lodash/-/lodash-3.10.1-kibana3.tgz#c0e318245219eeeff535895c429e0cef5058a9ad"
-  integrity sha512-HMfwwT2yAkEQNzHSR1BxgE5YcDMUaZ/skhNyjy1nvM/A4m0Kh940hLZeCqKBCsSaUJz/8A/9cQGd9BaAOCIBLg==
+"lodash@npm:@elastic/lodash@3.10.1-kibana4":
+  version "3.10.1-kibana4"
+  resolved "https://registry.yarnpkg.com/@elastic/lodash/-/lodash-3.10.1-kibana4.tgz#d491228fd659b4a1b0dfa08ba9c67a4979b9746d"
+  integrity sha512-geQqXd9ZedRCL+kq5cpeahYWYaYRV0BMXhCwzq4DpnGCVs430FTMS3Wcot3XChZZhCvkwHm15bpNjB312vPxaA==
 
 log-ok@^0.1.1:
   version "0.1.1"

From 38988dae7118b0e71db6c3cf73d87ca9e816ee5c Mon Sep 17 00:00:00 2001
From: Joe Reuter <johannes.reuter@elastic.co>
Date: Fri, 21 Feb 2020 16:54:53 +0100
Subject: [PATCH 126/174] Do not refresh color scale on each lookup (#57792)

---
 .../public/components/__tests__/tag_cloud.js  | 26 +++++++++----------
 .../public/components/tag_cloud.js            |  7 +++--
 .../components/tag_cloud_visualization.js     |  4 ++-
 .../new_platform/new_platform.karma_mock.js   |  3 +++
 4 files changed, 21 insertions(+), 19 deletions(-)

diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/tag_cloud.js b/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/tag_cloud.js
index 136fe51674bf1..152efe5667f18 100644
--- a/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/tag_cloud.js
+++ b/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/tag_cloud.js
@@ -30,10 +30,6 @@ import simpleloadPng from './simpleload.png';
 // eslint-disable-next-line @kbn/eslint/no-restricted-paths
 import { seedColors } from '../../../../../../plugins/charts/public/services/colors/seed_colors';
 
-const colors = {
-  seedColors,
-};
-
 describe('tag cloud tests', function() {
   const minValue = 1;
   const maxValue = 9;
@@ -102,6 +98,8 @@ describe('tag cloud tests', function() {
   let domNode;
   let tagCloud;
 
+  const colorScale = d3.scale.ordinal().range(seedColors);
+
   function setupDOM() {
     domNode = document.createElement('div');
     domNode.style.top = '0';
@@ -132,7 +130,7 @@ describe('tag cloud tests', function() {
     )}`, function() {
       beforeEach(async function() {
         setupDOM();
-        tagCloud = new TagCloud(domNode, colors);
+        tagCloud = new TagCloud(domNode, colorScale);
         tagCloud.setData(test.data);
         tagCloud.setOptions(test.options);
         await fromNode(cb => tagCloud.once('renderComplete', cb));
@@ -164,7 +162,7 @@ describe('tag cloud tests', function() {
 
         //TagCloud takes at least 600ms to complete (due to d3 animation)
         //renderComplete should only notify at the last one
-        tagCloud = new TagCloud(domNode, colors);
+        tagCloud = new TagCloud(domNode, colorScale);
         tagCloud.setData(baseTest.data);
         tagCloud.setOptions(baseTest.options);
 
@@ -196,7 +194,7 @@ describe('tag cloud tests', function() {
   describe('should use the latest state before notifying (when modifying options multiple times)', function() {
     beforeEach(async function() {
       setupDOM();
-      tagCloud = new TagCloud(domNode, colors);
+      tagCloud = new TagCloud(domNode, colorScale);
       tagCloud.setData(baseTest.data);
       tagCloud.setOptions(baseTest.options);
       tagCloud.setOptions(logScaleTest.options);
@@ -223,7 +221,7 @@ describe('tag cloud tests', function() {
   describe('should use the latest state before notifying (when modifying data multiple times)', function() {
     beforeEach(async function() {
       setupDOM();
-      tagCloud = new TagCloud(domNode, colors);
+      tagCloud = new TagCloud(domNode, colorScale);
       tagCloud.setData(baseTest.data);
       tagCloud.setOptions(baseTest.options);
       tagCloud.setData(trimDataTest.data);
@@ -253,7 +251,7 @@ describe('tag cloud tests', function() {
       counter = 0;
       setupDOM();
       return new Promise((resolve, reject) => {
-        tagCloud = new TagCloud(domNode, colors);
+        tagCloud = new TagCloud(domNode, colorScale);
         tagCloud.setData(baseTest.data);
         tagCloud.setOptions(baseTest.options);
 
@@ -299,7 +297,7 @@ describe('tag cloud tests', function() {
   describe('should show correct data when state-updates are interleaved with resize event', function() {
     beforeEach(async function() {
       setupDOM();
-      tagCloud = new TagCloud(domNode, colors);
+      tagCloud = new TagCloud(domNode, colorScale);
       tagCloud.setData(logScaleTest.data);
       tagCloud.setOptions(logScaleTest.options);
 
@@ -337,7 +335,7 @@ describe('tag cloud tests', function() {
       setupDOM();
       domNode.style.width = '1px';
       domNode.style.height = '1px';
-      tagCloud = new TagCloud(domNode, colors);
+      tagCloud = new TagCloud(domNode, colorScale);
       tagCloud.setData(baseTest.data);
       tagCloud.setOptions(baseTest.options);
       await fromNode(cb => tagCloud.once('renderComplete', cb));
@@ -363,7 +361,7 @@ describe('tag cloud tests', function() {
       domNode.style.width = '1px';
       domNode.style.height = '1px';
 
-      tagCloud = new TagCloud(domNode, colors);
+      tagCloud = new TagCloud(domNode, colorScale);
       tagCloud.setData(baseTest.data);
       tagCloud.setOptions(baseTest.options);
       await fromNode(cb => tagCloud.once('renderComplete', cb));
@@ -388,7 +386,7 @@ describe('tag cloud tests', function() {
   describe(`tags should no longer fit after making container smaller`, function() {
     beforeEach(async function() {
       setupDOM();
-      tagCloud = new TagCloud(domNode, colors);
+      tagCloud = new TagCloud(domNode, colorScale);
       tagCloud.setData(baseTest.data);
       tagCloud.setOptions(baseTest.options);
       await fromNode(cb => tagCloud.once('renderComplete', cb));
@@ -420,7 +418,7 @@ describe('tag cloud tests', function() {
     });
 
     it('should render simple image', async function() {
-      tagCloud = new TagCloud(domNode, colors);
+      tagCloud = new TagCloud(domNode, colorScale);
       tagCloud.setData(baseTest.data);
       tagCloud.setOptions(baseTest.options);
 
diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud.js b/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud.js
index f5084fd92cfee..fae7cdf797958 100644
--- a/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud.js
+++ b/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud.js
@@ -37,7 +37,7 @@ const D3_SCALING_FUNCTIONS = {
 };
 
 export class TagCloud extends EventEmitter {
-  constructor(domNode, colors) {
+  constructor(domNode, colorScale) {
     super();
 
     //DOM
@@ -54,7 +54,6 @@ export class TagCloud extends EventEmitter {
     this._spiral = 'archimedean'; //layout shape
     this._timeInterval = 1000; //time allowed for layout algorithm
     this._padding = 5;
-    this._seedColors = colors.seedColors;
 
     //OPTIONS
     this._orientation = 'single';
@@ -67,6 +66,7 @@ export class TagCloud extends EventEmitter {
     this._words = null;
 
     //UTIL
+    this._colorScale = colorScale;
     this._setTimeoutId = null;
     this._pendingJob = null;
     this._layoutIsUpdating = null;
@@ -371,8 +371,7 @@ export class TagCloud extends EventEmitter {
   }
 
   getFill(tag) {
-    const colorScale = d3.scale.ordinal().range(this._seedColors);
-    return colorScale(tag.text);
+    return this._colorScale(tag.text);
   }
 }
 
diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_visualization.js b/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_visualization.js
index 5528278adf4eb..114643c9a74e0 100644
--- a/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_visualization.js
+++ b/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_visualization.js
@@ -28,10 +28,12 @@ import { getFormat } from '../legacy_imports';
 import { Label } from './label';
 import { TagCloud } from './tag_cloud';
 import { FeedbackMessage } from './feedback_message';
+import d3 from 'd3';
 
 const MAX_TAG_COUNT = 200;
 
 export function createTagCloudVisualization({ colors }) {
+  const colorScale = d3.scale.ordinal().range(colors.seedColors);
   return class TagCloudVisualization {
     constructor(node, vis) {
       this._containerNode = node;
@@ -48,7 +50,7 @@ export function createTagCloudVisualization({ colors }) {
 
       this._vis = vis;
       this._truncated = false;
-      this._tagCloud = new TagCloud(cloudContainer, colors);
+      this._tagCloud = new TagCloud(cloudContainer, colorScale);
       this._tagCloud.on('select', event => {
         if (!this._visParams.bucket) {
           return;
diff --git a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js
index 38b3434ef9c48..cf8537ba7ab3e 100644
--- a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js
+++ b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js
@@ -218,6 +218,9 @@ export const npSetup = {
         chartsTheme$: mockObservable,
         useChartsTheme: sinon.fake(),
       },
+      colors: {
+        seedColors: ['white', 'black'],
+      },
     },
     management: {
       sections: {

From 8686fc99dc841f0c3463cb80da53c83f0d24763c Mon Sep 17 00:00:00 2001
From: Spencer <email@spalger.com>
Date: Fri, 21 Feb 2020 09:14:28 -0700
Subject: [PATCH 127/174] [kbn/optimizer] include bootstrap cache key in
 optimizer cache key (#58176)

* [kbn/optimizer] include bootstrap cache key in optimizer cache key

* remove cache buster

* update cache keys tests

* move mocks
---
 packages/kbn-optimizer/src/index.ts           |  1 -
 .../src/optimizer/cache_keys.test.ts          | 52 ++++++++++++++-----
 .../kbn-optimizer/src/optimizer/cache_keys.ts | 43 ++++++++++++---
 3 files changed, 75 insertions(+), 21 deletions(-)

diff --git a/packages/kbn-optimizer/src/index.ts b/packages/kbn-optimizer/src/index.ts
index 9798391d47da4..48777f1d54aaf 100644
--- a/packages/kbn-optimizer/src/index.ts
+++ b/packages/kbn-optimizer/src/index.ts
@@ -17,7 +17,6 @@
  * under the License.
  */
 
-// cache buster - https://github.com/elastic/kibana/issues/58077 - 1
 export { OptimizerConfig } from './optimizer';
 export * from './run_optimizer';
 export * from './log_optimizer_state';
diff --git a/packages/kbn-optimizer/src/optimizer/cache_keys.test.ts b/packages/kbn-optimizer/src/optimizer/cache_keys.test.ts
index 44234acd897dc..2337017f54ed8 100644
--- a/packages/kbn-optimizer/src/optimizer/cache_keys.test.ts
+++ b/packages/kbn-optimizer/src/optimizer/cache_keys.test.ts
@@ -17,14 +17,35 @@
  * under the License.
  */
 
+import Path from 'path';
+
 import jestDiff from 'jest-diff';
 import { REPO_ROOT, createAbsolutePathSerializer } from '@kbn/dev-utils';
 
 import { reformatJestDiff, getOptimizerCacheKey, diffCacheKey } from './cache_keys';
 import { OptimizerConfig } from './optimizer_config';
 
-jest.mock('./get_changes.ts');
+jest.mock('./get_changes.ts', () => ({
+  getChanges: async () =>
+    new Map([
+      ['/foo/bar/a', 'modified'],
+      ['/foo/bar/b', 'modified'],
+      ['/foo/bar/c', 'deleted'],
+    ]),
+}));
+
+jest.mock('./get_mtimes.ts', () => ({
+  getMtimes: async (paths: string[]) => new Map(paths.map(path => [path, 12345])),
+}));
+
 jest.mock('execa');
+
+jest.mock('fs', () => {
+  const realFs = jest.requireActual('fs');
+  jest.spyOn(realFs, 'readFile');
+  return realFs;
+});
+
 expect.addSnapshotSerializer(createAbsolutePathSerializer());
 
 jest.requireMock('execa').mockImplementation(async (cmd: string, args: string[], opts: object) => {
@@ -46,28 +67,35 @@ jest.requireMock('execa').mockImplementation(async (cmd: string, args: string[],
   };
 });
 
-jest.requireMock('./get_changes.ts').getChanges.mockImplementation(
-  async () =>
-    new Map([
-      ['/foo/bar/a', 'modified'],
-      ['/foo/bar/b', 'modified'],
-      ['/foo/bar/c', 'deleted'],
-    ])
-);
-
 describe('getOptimizerCacheKey()', () => {
-  it('uses latest commit and changes files to create unique value', async () => {
+  it('uses latest commit, bootstrap cache, and changed files to create unique value', async () => {
+    jest
+      .requireMock('fs')
+      .readFile.mockImplementation(
+        (path: string, enc: string, cb: (err: null, file: string) => void) => {
+          expect(path).toBe(
+            Path.resolve(REPO_ROOT, 'packages/kbn-optimizer/target/.bootstrap-cache')
+          );
+          expect(enc).toBe('utf8');
+          cb(null, '<bootstrap cache>');
+        }
+      );
+
     const config = OptimizerConfig.create({
       repoRoot: REPO_ROOT,
     });
 
     await expect(getOptimizerCacheKey(config)).resolves.toMatchInlineSnapshot(`
             Object {
+              "bootstrap": "<bootstrap cache>",
               "deletedPaths": Array [
                 "/foo/bar/c",
               ],
               "lastCommit": "<last commit sha>",
-              "modifiedPaths": Object {},
+              "modifiedTimes": Object {
+                "/foo/bar/a": 12345,
+                "/foo/bar/b": 12345,
+              },
               "workerConfig": Object {
                 "browserslistEnv": "dev",
                 "cache": true,
diff --git a/packages/kbn-optimizer/src/optimizer/cache_keys.ts b/packages/kbn-optimizer/src/optimizer/cache_keys.ts
index 3529ffa587f16..af6a8a648d29c 100644
--- a/packages/kbn-optimizer/src/optimizer/cache_keys.ts
+++ b/packages/kbn-optimizer/src/optimizer/cache_keys.ts
@@ -18,6 +18,8 @@
  */
 
 import Path from 'path';
+import Fs from 'fs';
+import { promisify } from 'util';
 
 import Chalk from 'chalk';
 import execa from 'execa';
@@ -116,9 +118,10 @@ export function reformatJestDiff(diff: string | null) {
 
 export interface OptimizerCacheKey {
   readonly lastCommit: string | undefined;
+  readonly bootstrap: string | undefined;
   readonly workerConfig: WorkerConfig;
   readonly deletedPaths: string[];
-  readonly modifiedPaths: Record<string, number>;
+  readonly modifiedTimes: Record<string, number>;
 }
 
 async function getLastCommit() {
@@ -133,21 +136,45 @@ async function getLastCommit() {
   return stdout.trim() || undefined;
 }
 
+async function getBootstrapCacheKey() {
+  try {
+    return await promisify(Fs.readFile)(
+      Path.resolve(OPTIMIZER_DIR, 'target/.bootstrap-cache'),
+      'utf8'
+    );
+  } catch (error) {
+    if (error?.code !== 'ENOENT') {
+      throw error;
+    }
+    return undefined;
+  }
+}
+
 export async function getOptimizerCacheKey(config: OptimizerConfig) {
-  const changes = Array.from((await getChanges(OPTIMIZER_DIR)).entries());
+  const [changes, lastCommit, bootstrap] = await Promise.all([
+    getChanges(OPTIMIZER_DIR),
+    getLastCommit(),
+    getBootstrapCacheKey(),
+  ] as const);
+
+  const deletedPaths: string[] = [];
+  const modifiedPaths: string[] = [];
+  for (const [path, type] of changes) {
+    (type === 'deleted' ? deletedPaths : modifiedPaths).push(path);
+  }
 
   const cacheKeys: OptimizerCacheKey = {
-    lastCommit: await getLastCommit(),
     workerConfig: config.getWorkerConfig('♻'),
-    deletedPaths: changes.filter(e => e[1] === 'deleted').map(e => e[0]),
-    modifiedPaths: {} as Record<string, number>,
+    lastCommit,
+    bootstrap,
+    deletedPaths,
+    modifiedTimes: {} as Record<string, number>,
   };
 
-  const modified = changes.filter(e => e[1] === 'modified').map(e => e[0]);
-  const mtimes = await getMtimes(modified);
+  const mtimes = await getMtimes(modifiedPaths);
   for (const [path, mtime] of Array.from(mtimes.entries()).sort(ascending(e => e[0]))) {
     if (typeof mtime === 'number') {
-      cacheKeys.modifiedPaths[path] = mtime;
+      cacheKeys.modifiedTimes[path] = mtime;
     }
   }
 

From fb04b7afb4cd644e68fa4bb3a5f1da60f799ae35 Mon Sep 17 00:00:00 2001
From: Charlie Pichette <56399229+charlie-pichette@users.noreply.github.com>
Date: Fri, 21 Feb 2020 11:44:57 -0500
Subject: [PATCH 128/174] [Endpoint] Refactor Management List Tests (#58148)

* endpoint-161-refactor-management-list-test

* fix location of es archive file
---
 .../functional/apps/endpoint/management.ts    | 68 +++++++++++++++----
 .../endpoint/metadata/api_feature/data.json   |  6 +-
 .../functional/page_objects/endpoint_page.ts  | 27 +++++++-
 3 files changed, 82 insertions(+), 19 deletions(-)

diff --git a/x-pack/test/functional/apps/endpoint/management.ts b/x-pack/test/functional/apps/endpoint/management.ts
index 500185182f0d8..4925fa7678ab0 100644
--- a/x-pack/test/functional/apps/endpoint/management.ts
+++ b/x-pack/test/functional/apps/endpoint/management.ts
@@ -25,19 +25,61 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
     });
 
     it('displays table data', async () => {
-      const data = await pageObjects.endpoint.getManagementTableData();
-      [
-        'Hostnamecadmann-4.example.com',
-        'PolicyPolicy Name',
-        'Policy StatusPolicy Status',
-        'Alerts0',
-        'Operating Systemwindows 10.0',
-        'IP Address10.192.213.130, 10.70.28.129',
-        'Sensor Versionversion',
-        'Last Activexxxx',
-      ].forEach((cellValue, index) => {
-        expect(data[1][index]).to.equal(cellValue);
-      });
+      const expectedData = [
+        [
+          'Hostname',
+          'Policy',
+          'Policy Status',
+          'Alerts',
+          'Operating System',
+          'IP Address',
+          'Sensor Version',
+          'Last Active',
+        ],
+        [
+          'cadmann-4.example.com',
+          'Policy Name',
+          'Policy Status',
+          '0',
+          'windows 10.0',
+          '10.192.213.130, 10.70.28.129',
+          'version',
+          'xxxx',
+        ],
+        [
+          'thurlow-9.example.com',
+          'Policy Name',
+          'Policy Status',
+          '0',
+          'windows 10.0',
+          '10.46.229.234',
+          'version',
+          'xxxx',
+        ],
+        [
+          'rezzani-7.example.com',
+          'Policy Name',
+          'Policy Status',
+          '0',
+          'windows 10.0',
+          '10.101.149.26, 2606:a000:ffc0:39:11ef:37b9:3371:578c',
+          'version',
+          'xxxx',
+        ],
+      ];
+      const tableData = await pageObjects.endpoint.getEndpointAppTableData('managementListTable');
+      expect(tableData).to.eql(expectedData);
+    });
+
+    it('displays no items found', async () => {
+      // clear out the data and reload the page
+      await esArchiver.unload('endpoint/metadata/api_feature');
+      await pageObjects.common.navigateToUrlWithBrowserHistory('endpoint', '/management');
+      // get the table data and verify no entries appear
+      const tableData = await pageObjects.endpoint.getEndpointAppTableData('managementListTable');
+      expect(tableData[1][0]).to.equal('No items found');
+      // reload the data so the other tests continue to pass
+      await esArchiver.load('endpoint/metadata/api_feature');
     });
 
     after(async () => {
diff --git a/x-pack/test/functional/es_archives/endpoint/metadata/api_feature/data.json b/x-pack/test/functional/es_archives/endpoint/metadata/api_feature/data.json
index 87720b068f0e8..6a7911b5be61f 100644
--- a/x-pack/test/functional/es_archives/endpoint/metadata/api_feature/data.json
+++ b/x-pack/test/functional/es_archives/endpoint/metadata/api_feature/data.json
@@ -110,7 +110,7 @@
         "id": "fc0ff548-feba-41b6-8367-65e8790d0eaf",
         "ip": [
           "10.101.149.26",
-          "10.12.85.216"
+          "2606:a000:ffc0:39:11ef:37b9:3371:578c"
         ],
         "mac": [
           "e2-6d-f9-0-46-2e"
@@ -238,7 +238,7 @@
         "id": "fc0ff548-feba-41b6-8367-65e8790d0eaf",
         "ip": [
           "10.101.149.26",
-          "10.12.85.216"
+          "2606:a000:ffc0:39:11ef:37b9:3371:578c"
         ],
         "mac": [
           "e2-6d-f9-0-46-2e"
@@ -365,7 +365,7 @@
         "id": "fc0ff548-feba-41b6-8367-65e8790d0eaf",
         "ip": [
           "10.101.149.26",
-          "10.12.85.216"
+          "2606:a000:ffc0:39:11ef:37b9:3371:578c"
         ],
         "mac": [
           "e2-6d-f9-0-46-2e"
diff --git a/x-pack/test/functional/page_objects/endpoint_page.ts b/x-pack/test/functional/page_objects/endpoint_page.ts
index 54f537dd0e8c3..185b95b00527d 100644
--- a/x-pack/test/functional/page_objects/endpoint_page.ts
+++ b/x-pack/test/functional/page_objects/endpoint_page.ts
@@ -4,11 +4,11 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+import { WebElementWrapper } from 'test/functional/services/lib/web_element_wrapper';
 import { FtrProviderContext } from '../ftr_provider_context';
 
 export function EndpointPageProvider({ getService }: FtrProviderContext) {
   const testSubjects = getService('testSubjects');
-  const table = getService('table');
 
   return {
     /**
@@ -34,8 +34,29 @@ export function EndpointPageProvider({ getService }: FtrProviderContext) {
       return await testSubjects.getVisibleText('welcomeTitle');
     },
 
-    async getManagementTableData() {
-      return await table.getDataFromTestSubj('managementListTable');
+    /**
+     * Finds a table and returns the data in a nested array with row 0 is the headers if they exist.
+     * It uses euiTableCellContent to avoid poluting the array data with the euiTableRowCell__mobileHeader data.
+     * @param dataTestSubj
+     * @returns Promise<string[][]>
+     */
+    async getEndpointAppTableData(dataTestSubj: string) {
+      await testSubjects.exists(dataTestSubj);
+      const hostTable: WebElementWrapper = await testSubjects.find(dataTestSubj);
+      const $ = await hostTable.parseDomContent();
+      return $('tr')
+        .toArray()
+        .map(row =>
+          $(row)
+            .find('.euiTableCellContent')
+            .toArray()
+            .map(cell =>
+              $(cell)
+                .text()
+                .replace(/&nbsp;/g, '')
+                .trim()
+            )
+        );
     },
   };
 }

From 3d9eb2a679f6316548f156ed5c9e705ea45a7bf6 Mon Sep 17 00:00:00 2001
From: Dima Arnautov <dmitrii.arnautov@elastic.co>
Date: Fri, 21 Feb 2020 17:45:42 +0100
Subject: [PATCH 129/174] [ML] Transform: Support multi-line JSON notation in
 advanced editor (#58015)

* [ML] use xJsonMode

* [ML] remove commented code

* [ML] move use_x_json_mode hook, disable json formatting

* [ML] mocks for shared_imports

* [ML] ts-ignore worker import
---
 .../public/__mocks__/shared_imports.ts        | 10 +++++++++
 .../public/app/hooks/use_x_json_mode.ts       | 20 ++++++++++++++++++
 .../source_index_preview.test.tsx             |  2 ++
 .../step_create/step_create_form.test.tsx     |  2 ++
 .../step_define/pivot_preview.test.tsx        |  2 ++
 .../step_define/step_define_form.test.tsx     |  2 ++
 .../step_define/step_define_form.tsx          | 21 +++++++++++--------
 .../step_define/step_define_summary.test.tsx  |  2 ++
 .../create_transform_button.test.tsx          |  2 ++
 .../transform_list/action_delete.test.tsx     |  2 ++
 .../transform_list/action_start.test.tsx      |  2 ++
 .../transform_list/action_stop.test.tsx       |  2 ++
 .../transform_list/actions.test.tsx           |  2 ++
 .../transform_list/columns.test.tsx           |  2 ++
 .../transform_list/expanded_row.test.tsx      |  2 ++
 .../transform_list/transform_list.test.tsx    |  2 ++
 .../transform_management_section.test.tsx     |  2 ++
 .../transform/public/shared_imports.ts        |  6 ++++++
 .../ace/modes/x_json/worker/index.ts          |  1 +
 19 files changed, 77 insertions(+), 9 deletions(-)
 create mode 100644 x-pack/legacy/plugins/transform/public/__mocks__/shared_imports.ts
 create mode 100644 x-pack/legacy/plugins/transform/public/app/hooks/use_x_json_mode.ts

diff --git a/x-pack/legacy/plugins/transform/public/__mocks__/shared_imports.ts b/x-pack/legacy/plugins/transform/public/__mocks__/shared_imports.ts
new file mode 100644
index 0000000000000..b55a4cd5c7bd6
--- /dev/null
+++ b/x-pack/legacy/plugins/transform/public/__mocks__/shared_imports.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 function XJsonMode() {}
+export function setDependencyCache() {}
+export { mlInMemoryTableBasicFactory } from '../../../ml/public/application/components/ml_in_memory_table';
+export const SORT_DIRECTION = { ASC: 'asc' };
diff --git a/x-pack/legacy/plugins/transform/public/app/hooks/use_x_json_mode.ts b/x-pack/legacy/plugins/transform/public/app/hooks/use_x_json_mode.ts
new file mode 100644
index 0000000000000..1017ce198ff29
--- /dev/null
+++ b/x-pack/legacy/plugins/transform/public/app/hooks/use_x_json_mode.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 { useState } from 'react';
+import { collapseLiteralStrings, expandLiteralStrings, XJsonMode } from '../../shared_imports';
+
+export const xJsonMode = new XJsonMode();
+
+export const useXJsonMode = (json: string) => {
+  const [xJson, setXJson] = useState(expandLiteralStrings(json));
+
+  return {
+    xJson,
+    setXJson,
+    xJsonMode,
+    convertToJson: collapseLiteralStrings,
+  };
+};
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/source_index_preview.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/source_index_preview.test.tsx
index f326199271592..d7f1d9d099cc3 100644
--- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/source_index_preview.test.tsx
+++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/source_index_preview.test.tsx
@@ -20,6 +20,8 @@ jest.mock('react', () => {
   return { ...r, memo: (x: any) => x };
 });
 
+jest.mock('../../../../../shared_imports');
+
 describe('Transform: <SourceIndexPreview />', () => {
   test('Minimal initialization', () => {
     const props = {
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.test.tsx
index 055f0613e4e44..a0c91c070844b 100644
--- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.test.tsx
+++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.test.tsx
@@ -19,6 +19,8 @@ jest.mock('react', () => {
   return { ...r, memo: (x: any) => x };
 });
 
+jest.mock('../../../../../shared_imports');
+
 describe('Transform: <StepCreateForm />', () => {
   test('Minimal initialization', () => {
     const props = {
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/pivot_preview.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/pivot_preview.test.tsx
index 4ff1190415dba..a2aa056c1634d 100644
--- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/pivot_preview.test.tsx
+++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/pivot_preview.test.tsx
@@ -27,6 +27,8 @@ jest.mock('react', () => {
   return { ...r, memo: (x: any) => x };
 });
 
+jest.mock('../../../../../shared_imports');
+
 describe('Transform: <PivotPreview />', () => {
   test('Minimal initialization', () => {
     const groupBy: PivotGroupByConfig = {
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.test.tsx
index 48df371e87664..0311b26304c30 100644
--- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.test.tsx
+++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.test.tsx
@@ -25,6 +25,8 @@ jest.mock('react', () => {
   return { ...r, memo: (x: any) => x };
 });
 
+jest.mock('../../../../../shared_imports');
+
 describe('Transform: <DefinePivotForm />', () => {
   test('Minimal initialization', () => {
     // Using a wrapping <div> element because shallow() would fail
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx
index 675386be8e2a5..1499f99f82824 100644
--- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx
+++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx
@@ -28,6 +28,7 @@ import {
   EuiSwitch,
 } from '@elastic/eui';
 
+import { useXJsonMode, xJsonMode } from '../../../../hooks/use_x_json_mode';
 import { TransformPivotConfig } from '../../../../common';
 import { dictionaryToArray, Dictionary } from '../../../../../../common/types/common';
 import { DropDown } from '../aggregation_dropdown';
@@ -383,7 +384,13 @@ export const StepDefineForm: FC<Props> = React.memo(({ overrides = {}, onChange
   const [advancedEditorConfigLastApplied, setAdvancedEditorConfigLastApplied] = useState(
     stringifiedPivotConfig
   );
-  const [advancedEditorConfig, setAdvancedEditorConfig] = useState(stringifiedPivotConfig);
+
+  const {
+    convertToJson,
+    setXJson: setAdvancedEditorConfig,
+    xJson: advancedEditorConfig,
+  } = useXJsonMode(stringifiedPivotConfig);
+
   // source config
   const stringifiedSourceConfig = JSON.stringify(previewRequest.source.query, null, 2);
   const [
@@ -407,7 +414,7 @@ export const StepDefineForm: FC<Props> = React.memo(({ overrides = {}, onChange
   };
 
   const applyAdvancedPivotEditorChanges = () => {
-    const pivotConfig = JSON.parse(advancedEditorConfig);
+    const pivotConfig = JSON.parse(convertToJson(advancedEditorConfig));
 
     const newGroupByList: PivotGroupByConfigDict = {};
     if (pivotConfig !== undefined && pivotConfig.group_by !== undefined) {
@@ -442,10 +449,8 @@ export const StepDefineForm: FC<Props> = React.memo(({ overrides = {}, onChange
       });
     }
     setAggList(newAggList);
-    const prettyPivotConfig = JSON.stringify(pivotConfig, null, 2);
 
-    setAdvancedEditorConfig(prettyPivotConfig);
-    setAdvancedEditorConfigLastApplied(prettyPivotConfig);
+    setAdvancedEditorConfigLastApplied(advancedEditorConfig);
     setAdvancedPivotEditorApplyButtonEnabled(false);
   };
 
@@ -513,13 +518,11 @@ export const StepDefineForm: FC<Props> = React.memo(({ overrides = {}, onChange
       pivotAggsArr
     );
 
-    const stringifiedPivotConfigUpdate = JSON.stringify(previewRequestUpdate.pivot, null, 2);
     const stringifiedSourceConfigUpdate = JSON.stringify(
       previewRequestUpdate.source.query,
       null,
       2
     );
-    setAdvancedEditorConfig(stringifiedPivotConfigUpdate);
     setAdvancedEditorSourceConfig(stringifiedSourceConfigUpdate);
 
     onChange({
@@ -784,7 +787,7 @@ export const StepDefineForm: FC<Props> = React.memo(({ overrides = {}, onChange
                 >
                   <EuiPanel grow={false} paddingSize="none">
                     <EuiCodeEditor
-                      mode="json"
+                      mode={xJsonMode}
                       width="100%"
                       value={advancedEditorConfig}
                       onChange={(d: string) => {
@@ -799,7 +802,7 @@ export const StepDefineForm: FC<Props> = React.memo(({ overrides = {}, onChange
                         // Try to parse the string passed on from the editor.
                         // If parsing fails, the "Apply"-Button will be disabled
                         try {
-                          JSON.parse(d);
+                          JSON.parse(convertToJson(d));
                           setAdvancedPivotEditorApplyButtonEnabled(true);
                         } catch (e) {
                           setAdvancedPivotEditorApplyButtonEnabled(false);
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.test.tsx
index 2d9895e8ddcf1..aae366e6008d5 100644
--- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.test.tsx
+++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.test.tsx
@@ -26,6 +26,8 @@ jest.mock('react', () => {
   return { ...r, memo: (x: any) => x };
 });
 
+jest.mock('../../../../../shared_imports');
+
 describe('Transform: <DefinePivotSummary />', () => {
   test('Minimal initialization', () => {
     const groupBy: PivotGroupByConfig = {
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/create_transform_button/create_transform_button.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/create_transform_button/create_transform_button.test.tsx
index 673e60de54572..288630333615a 100644
--- a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/create_transform_button/create_transform_button.test.tsx
+++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/create_transform_button/create_transform_button.test.tsx
@@ -11,6 +11,8 @@ import { CreateTransformButton } from './create_transform_button';
 
 jest.mock('ui/new_platform');
 
+jest.mock('../../../../../shared_imports');
+
 describe('Transform: Transform List <CreateTransformButton />', () => {
   test('Minimal initialization', () => {
     const wrapper = shallow(<CreateTransformButton onClick={jest.fn()} />);
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_delete.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_delete.test.tsx
index 979da13b1f83a..4795a2eb7d7bc 100644
--- a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_delete.test.tsx
+++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_delete.test.tsx
@@ -17,6 +17,8 @@ import transformListRow from '../../../../common/__mocks__/transform_list_row.js
 
 jest.mock('ui/new_platform');
 
+jest.mock('../../../../../shared_imports');
+
 describe('Transform: Transform List Actions <DeleteAction />', () => {
   test('Minimal initialization', () => {
     const Providers = getAppProviders(createPublicShim());
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_start.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_start.test.tsx
index 71a2eff39506d..5f4d4a71c71eb 100644
--- a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_start.test.tsx
+++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_start.test.tsx
@@ -17,6 +17,8 @@ import transformListRow from '../../../../common/__mocks__/transform_list_row.js
 
 jest.mock('ui/new_platform');
 
+jest.mock('../../../../../shared_imports');
+
 describe('Transform: Transform List Actions <StartAction />', () => {
   test('Minimal initialization', () => {
     const Providers = getAppProviders(createPublicShim());
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_stop.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_stop.test.tsx
index c3b67f7661a1a..f6bb1c8b60667 100644
--- a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_stop.test.tsx
+++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_stop.test.tsx
@@ -17,6 +17,8 @@ import transformListRow from '../../../../common/__mocks__/transform_list_row.js
 
 jest.mock('ui/new_platform');
 
+jest.mock('../../../../../shared_imports');
+
 describe('Transform: Transform List Actions <StopAction />', () => {
   test('Minimal initialization', () => {
     const Providers = getAppProviders(createPublicShim());
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/actions.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/actions.test.tsx
index ef92a5e3859d7..12e1ba5528c43 100644
--- a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/actions.test.tsx
+++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/actions.test.tsx
@@ -8,6 +8,8 @@ import { getActions } from './actions';
 
 jest.mock('ui/new_platform');
 
+jest.mock('../../../../../shared_imports');
+
 describe('Transform: Transform List Actions', () => {
   test('getActions()', () => {
     const actions = getActions({ forceDisable: false });
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/columns.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/columns.test.tsx
index f16130bfe618b..42f04ed101ad6 100644
--- a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/columns.test.tsx
+++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/columns.test.tsx
@@ -8,6 +8,8 @@ import { getColumns } from './columns';
 
 jest.mock('ui/new_platform');
 
+jest.mock('../../../../../shared_imports');
+
 describe('Transform: Job List Columns', () => {
   test('getColumns()', () => {
     const columns = getColumns([], () => {}, []);
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.test.tsx
index 4f992707cbf1a..7fcaf5e6048f6 100644
--- a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.test.tsx
+++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.test.tsx
@@ -13,6 +13,8 @@ import { ExpandedRow } from './expanded_row';
 
 import transformListRow from '../../../../common/__mocks__/transform_list_row.json';
 
+jest.mock('../../../../../shared_imports');
+
 describe('Transform: Transform List <ExpandedRow />', () => {
   // Set timezone to US/Eastern for consistent test results.
   beforeEach(() => {
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.test.tsx
index 303de6b86ac53..e1a19ddd3c742 100644
--- a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.test.tsx
+++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.test.tsx
@@ -12,6 +12,8 @@ import { TransformList } from './transform_list';
 
 jest.mock('ui/new_platform');
 
+jest.mock('../../../../../shared_imports');
+
 describe('Transform: Transform List <TransformList />', () => {
   test('Minimal initialization', () => {
     const wrapper = shallow(
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/transform_management_section.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/transform_management_section.test.tsx
index c94f5c1d57d49..f68670f0b38b2 100644
--- a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/transform_management_section.test.tsx
+++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/transform_management_section.test.tsx
@@ -14,6 +14,8 @@ jest.mock('ui/timefilter', () => {
   return {};
 });
 
+jest.mock('../../../shared_imports');
+
 describe('Transform: <TransformManagementSection />', () => {
   test('Minimal initialization', () => {
     const wrapper = shallow(<TransformManagementSection />);
diff --git a/x-pack/legacy/plugins/transform/public/shared_imports.ts b/x-pack/legacy/plugins/transform/public/shared_imports.ts
index 74e0c9a3878db..248eb00c67dff 100644
--- a/x-pack/legacy/plugins/transform/public/shared_imports.ts
+++ b/x-pack/legacy/plugins/transform/public/shared_imports.ts
@@ -4,6 +4,12 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+export { XJsonMode } from '../../../../plugins/es_ui_shared/console_lang/ace/modes/x_json';
+export {
+  collapseLiteralStrings,
+  expandLiteralStrings,
+} from '../../../../../src/plugins/es_ui_shared/console_lang/lib';
+
 export {
   SendRequestConfig,
   SendRequestResponse,
diff --git a/x-pack/plugins/es_ui_shared/console_lang/ace/modes/x_json/worker/index.ts b/x-pack/plugins/es_ui_shared/console_lang/ace/modes/x_json/worker/index.ts
index af50562bd3242..0e40fd335dd31 100644
--- a/x-pack/plugins/es_ui_shared/console_lang/ace/modes/x_json/worker/index.ts
+++ b/x-pack/plugins/es_ui_shared/console_lang/ace/modes/x_json/worker/index.ts
@@ -4,6 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+// @ts-ignore
 import src from '!!raw-loader!./worker.js';
 
 export const workerModule = {

From 0adfdcafe137692750118277927f9a3ccbf26faa Mon Sep 17 00:00:00 2001
From: Corey Robertson <corey.robertson@elastic.co>
Date: Fri, 21 Feb 2020 12:16:22 -0500
Subject: [PATCH 130/174] Sanitize workpad before sending to API (#57704)

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../canvas/public/lib/workpad_service.js      | 38 +++++++++++++++++--
 1 file changed, 35 insertions(+), 3 deletions(-)

diff --git a/x-pack/legacy/plugins/canvas/public/lib/workpad_service.js b/x-pack/legacy/plugins/canvas/public/lib/workpad_service.js
index b678a532ec084..f3681f50c56a5 100644
--- a/x-pack/legacy/plugins/canvas/public/lib/workpad_service.js
+++ b/x-pack/legacy/plugins/canvas/public/lib/workpad_service.js
@@ -12,6 +12,35 @@ import {
 } from '../../common/lib/constants';
 import { fetch } from '../../common/lib/fetch';
 import { getCoreStart } from '../legacy';
+/*
+  Remove any top level keys from the workpad which will be rejected by validation
+*/
+const validKeys = [
+  '@created',
+  '@timestamp',
+  'assets',
+  'colors',
+  'css',
+  'height',
+  'id',
+  'isWriteable',
+  'name',
+  'page',
+  'pages',
+  'width',
+];
+
+const sanitizeWorkpad = function(workpad) {
+  const workpadKeys = Object.keys(workpad);
+
+  for (const key of workpadKeys) {
+    if (!validKeys.includes(key)) {
+      delete workpad[key];
+    }
+  }
+
+  return workpad;
+};
 
 const getApiPath = function() {
   const basePath = getCoreStart().http.basePath.get();
@@ -29,7 +58,10 @@ const getApiPathAssets = function() {
 };
 
 export function create(workpad) {
-  return fetch.post(getApiPath(), { ...workpad, assets: workpad.assets || {} });
+  return fetch.post(getApiPath(), {
+    ...sanitizeWorkpad({ ...workpad }),
+    assets: workpad.assets || {},
+  });
 }
 
 export function get(workpadId) {
@@ -41,11 +73,11 @@ export function get(workpadId) {
 
 // TODO: I think this function is never used.  Look into and remove the corresponding route as well
 export function update(id, workpad) {
-  return fetch.put(`${getApiPath()}/${id}`, workpad);
+  return fetch.put(`${getApiPath()}/${id}`, sanitizeWorkpad({ ...workpad }));
 }
 
 export function updateWorkpad(id, workpad) {
-  return fetch.put(`${getApiPathStructures()}/${id}`, workpad);
+  return fetch.put(`${getApiPathStructures()}/${id}`, sanitizeWorkpad({ ...workpad }));
 }
 
 export function updateAssets(id, workpadAssets) {

From cca7555c4b028a5d2a4bcd22241404aa547d6bef Mon Sep 17 00:00:00 2001
From: Aaron Caldwell <aaron.caldwell@elastic.co>
Date: Fri, 21 Feb 2020 12:41:35 -0700
Subject: [PATCH 131/174] Make sure index pattern has fields before parsing
 (#58242)

---
 .../plugins/maps/server/maps_telemetry/maps_telemetry.ts    | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_telemetry.ts b/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_telemetry.ts
index 87642d9f8bea8..863dbee7b5c8b 100644
--- a/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_telemetry.ts
+++ b/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_telemetry.ts
@@ -83,7 +83,11 @@ function getUniqueLayerCounts(layerCountsList: ILayerTypeCount[], mapsCount: num
 }
 
 function getIndexPatternsWithGeoFieldCount(indexPatterns: IIndexPattern[]) {
-  const fieldLists = indexPatterns.map(indexPattern => JSON.parse(indexPattern.attributes.fields));
+  const fieldLists = indexPatterns.map(indexPattern =>
+    indexPattern.attributes && indexPattern.attributes.fields
+      ? JSON.parse(indexPattern.attributes.fields)
+      : []
+  );
   const fieldListsWithGeoFields = fieldLists.filter(fields =>
     fields.some(
       (field: IFieldType) =>

From 02efb01c481f9f24d8d707f06dfc68b2fb805001 Mon Sep 17 00:00:00 2001
From: Robert Austin <robert.austin@elastic.co>
Date: Fri, 21 Feb 2020 14:44:02 -0500
Subject: [PATCH 132/174] [Endpoint] Add a flyout to alert list. (#57926)

* Filter alert API so it shows only Alerts instead of all documents in the index
* Clicking an item in the alert list will open a flyout
---
 x-pack/plugins/endpoint/common/types.ts       |   2 +-
 .../public/applications/endpoint/index.tsx    |  15 +-
 .../endpoint/store/alerts/alert_list.test.ts  |  33 +--
 .../alerts/alert_list_pagination.test.ts      |  63 +++---
 .../endpoint/store/alerts/middleware.ts       |   5 +-
 .../store/alerts/mock_alert_result_list.ts    |  60 ++++++
 .../endpoint/store/alerts/selectors.ts        | 110 +++++-----
 .../applications/endpoint/store/index.ts      |  43 ++--
 .../public/applications/endpoint/types.ts     |  37 +++-
 .../endpoint/view/alerts/index.test.tsx       | 189 ++++++++++++++++++
 .../endpoint/view/alerts/index.tsx            | 159 +++++++++++----
 .../view/alerts/url_from_query_params.ts      |  31 +++
 .../endpoint/view/route_capture.tsx           |  21 ++
 .../embeddables/resolver/store/selectors.ts   |   1 -
 .../resolver/view/use_camera.test.tsx         |   3 -
 .../endpoint/alert_query_builders.test.ts     |  88 ++++----
 .../services/endpoint/alert_query_builders.ts |  14 +-
 17 files changed, 650 insertions(+), 224 deletions(-)
 create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/mock_alert_result_list.ts
 create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.test.tsx
 create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/url_from_query_params.ts
 create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/view/route_capture.tsx

diff --git a/x-pack/plugins/endpoint/common/types.ts b/x-pack/plugins/endpoint/common/types.ts
index f0fd9dc610e4e..78be98b7805ba 100644
--- a/x-pack/plugins/endpoint/common/types.ts
+++ b/x-pack/plugins/endpoint/common/types.ts
@@ -71,7 +71,7 @@ export interface EndpointResultList {
 }
 
 export interface AlertData {
-  '@timestamp': Date;
+  '@timestamp': string;
   agent: {
     id: string;
     version: string;
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx
index 8530d6206d398..c6c032c273543 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx
@@ -8,16 +8,14 @@ import * as React from 'react';
 import ReactDOM from 'react-dom';
 import { CoreStart, AppMountParameters } from 'kibana/public';
 import { I18nProvider, FormattedMessage } from '@kbn/i18n/react';
-import { Route, Switch, BrowserRouter, useLocation } from 'react-router-dom';
-import { Provider, useDispatch } from 'react-redux';
+import { Route, Switch, BrowserRouter } from 'react-router-dom';
+import { Provider } from 'react-redux';
 import { Store } from 'redux';
-import { memo } from 'react';
+import { RouteCapture } from './view/route_capture';
 import { appStoreFactory } from './store';
 import { AlertIndex } from './view/alerts';
 import { ManagementList } from './view/managing';
 import { PolicyList } from './view/policy';
-import { AppAction } from './store/action';
-import { EndpointAppLocation } from './types';
 
 /**
  * This module will be loaded asynchronously to reduce the bundle size of your plugin's main bundle.
@@ -33,13 +31,6 @@ export function renderApp(coreStart: CoreStart, { appBasePath, element }: AppMou
   };
 }
 
-const RouteCapture = memo(({ children }) => {
-  const location: EndpointAppLocation = useLocation();
-  const dispatch: (action: AppAction) => unknown = useDispatch();
-  dispatch({ type: 'userChangedUrl', payload: location });
-  return <>{children}</>;
-});
-
 interface RouterProps {
   basename: string;
   store: Store;
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/alert_list.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/alert_list.test.ts
index 6ba7a34ae81d1..0aeeb6881ad96 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/alert_list.test.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/alert_list.test.ts
@@ -14,6 +14,7 @@ import { coreMock } from 'src/core/public/mocks';
 import { AlertResultList } from '../../../../../common/types';
 import { isOnAlertPage } from './selectors';
 import { createBrowserHistory } from 'history';
+import { mockAlertResultList } from './mock_alert_result_list';
 
 describe('alert list tests', () => {
   let store: Store<AlertListState, AppAction>;
@@ -28,37 +29,7 @@ describe('alert list tests', () => {
   describe('when the user navigates to the alert list page', () => {
     beforeEach(() => {
       coreStart.http.get.mockImplementation(async () => {
-        const response: AlertResultList = {
-          alerts: [
-            {
-              '@timestamp': new Date(1542341895000),
-              agent: {
-                id: 'ced9c68e-b94a-4d66-bb4c-6106514f0a2f',
-                version: '3.0.0',
-              },
-              event: {
-                action: 'open',
-              },
-              file_classification: {
-                malware_classification: {
-                  score: 3,
-                },
-              },
-              host: {
-                hostname: 'HD-c15-bc09190a',
-                ip: '10.179.244.14',
-                os: {
-                  name: 'Windows',
-                },
-              },
-              thread: {},
-            },
-          ],
-          total: 1,
-          request_page_size: 10,
-          request_page_index: 0,
-          result_from_index: 0,
-        };
+        const response: AlertResultList = mockAlertResultList();
         return response;
       });
 
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/alert_list_pagination.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/alert_list_pagination.test.ts
index 77708a3c77e2b..5c257c3d65fdc 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/alert_list_pagination.test.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/alert_list_pagination.test.ts
@@ -7,37 +7,47 @@
 import { Store, createStore, applyMiddleware } from 'redux';
 import { History } from 'history';
 import { alertListReducer } from './reducer';
-import { AlertListState } from '../../types';
+import { AlertListState, AlertingIndexUIQueryParams } from '../../types';
 import { alertMiddlewareFactory } from './middleware';
 import { AppAction } from '../action';
 import { coreMock } from 'src/core/public/mocks';
 import { createBrowserHistory } from 'history';
-import {
-  urlFromNewPageSizeParam,
-  paginationDataFromUrl,
-  urlFromNewPageIndexParam,
-} from './selectors';
+import { uiQueryParams } from './selectors';
+import { urlFromQueryParams } from '../../view/alerts/url_from_query_params';
 
 describe('alert list pagination', () => {
   let store: Store<AlertListState, AppAction>;
   let coreStart: ReturnType<typeof coreMock.createStart>;
   let history: History<never>;
+  let queryParams: () => AlertingIndexUIQueryParams;
+  /**
+   * Update the history with a new `AlertingIndexUIQueryParams`
+   */
+  let historyPush: (params: AlertingIndexUIQueryParams) => void;
   beforeEach(() => {
     coreStart = coreMock.createStart();
     history = createBrowserHistory();
+
     const middleware = alertMiddlewareFactory(coreStart);
     store = createStore(alertListReducer, applyMiddleware(middleware));
+
+    history.listen(location => {
+      store.dispatch({ type: 'userChangedUrl', payload: location });
+    });
+
+    queryParams = () => uiQueryParams(store.getState());
+
+    historyPush = (nextQueryParams: AlertingIndexUIQueryParams): void => {
+      return history.push(urlFromQueryParams(nextQueryParams));
+    };
   });
   describe('when the user navigates to the alert list page', () => {
     describe('when a new page size is passed', () => {
       beforeEach(() => {
-        const urlPageSizeSelector = urlFromNewPageSizeParam(store.getState());
-        history.push(urlPageSizeSelector(1));
-        store.dispatch({ type: 'userChangedUrl', payload: history.location });
+        historyPush({ ...queryParams(), page_size: '1' });
       });
       it('should modify the url correctly', () => {
-        const actualPaginationQuery = paginationDataFromUrl(store.getState());
-        expect(actualPaginationQuery).toMatchInlineSnapshot(`
+        expect(queryParams()).toMatchInlineSnapshot(`
           Object {
             "page_size": "1",
           }
@@ -46,13 +56,10 @@ describe('alert list pagination', () => {
 
       describe('and then a new page index is passed', () => {
         beforeEach(() => {
-          const urlPageIndexSelector = urlFromNewPageIndexParam(store.getState());
-          history.push(urlPageIndexSelector(1));
-          store.dispatch({ type: 'userChangedUrl', payload: history.location });
+          historyPush({ ...queryParams(), page_index: '1' });
         });
         it('should modify the url in the correct order', () => {
-          const actualPaginationQuery = paginationDataFromUrl(store.getState());
-          expect(actualPaginationQuery).toMatchInlineSnapshot(`
+          expect(queryParams()).toMatchInlineSnapshot(`
             Object {
               "page_index": "1",
               "page_size": "1",
@@ -64,35 +71,15 @@ describe('alert list pagination', () => {
 
     describe('when a new page index is passed', () => {
       beforeEach(() => {
-        const urlPageIndexSelector = urlFromNewPageIndexParam(store.getState());
-        history.push(urlPageIndexSelector(1));
-        store.dispatch({ type: 'userChangedUrl', payload: history.location });
+        historyPush({ ...queryParams(), page_index: '1' });
       });
       it('should modify the url correctly', () => {
-        const actualPaginationQuery = paginationDataFromUrl(store.getState());
-        expect(actualPaginationQuery).toMatchInlineSnapshot(`
+        expect(queryParams()).toMatchInlineSnapshot(`
           Object {
             "page_index": "1",
           }
         `);
       });
-
-      describe('and then a new page size is passed', () => {
-        beforeEach(() => {
-          const urlPageSizeSelector = urlFromNewPageSizeParam(store.getState());
-          history.push(urlPageSizeSelector(1));
-          store.dispatch({ type: 'userChangedUrl', payload: history.location });
-        });
-        it('should modify the url correctly and reset index to `0`', () => {
-          const actualPaginationQuery = paginationDataFromUrl(store.getState());
-          expect(actualPaginationQuery).toMatchInlineSnapshot(`
-            Object {
-              "page_index": "0",
-              "page_size": "1",
-            }
-          `);
-        });
-      });
     });
   });
 });
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts
index 059507c8f0658..76a6867418bd8 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts
@@ -4,11 +4,10 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { HttpFetchQuery } from 'kibana/public';
 import { AlertResultList } from '../../../../../common/types';
 import { AppAction } from '../action';
 import { MiddlewareFactory, AlertListState } from '../../types';
-import { isOnAlertPage, paginationDataFromUrl } from './selectors';
+import { isOnAlertPage, apiQueryParams } from './selectors';
 
 export const alertMiddlewareFactory: MiddlewareFactory<AlertListState> = coreStart => {
   return api => next => async (action: AppAction) => {
@@ -16,7 +15,7 @@ export const alertMiddlewareFactory: MiddlewareFactory<AlertListState> = coreSta
     const state = api.getState();
     if (action.type === 'userChangedUrl' && isOnAlertPage(state)) {
       const response: AlertResultList = await coreStart.http.get(`/api/endpoint/alerts`, {
-        query: paginationDataFromUrl(state) as HttpFetchQuery,
+        query: apiQueryParams(state),
       });
       api.dispatch({ type: 'serverReturnedAlertsData', payload: response });
     }
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/mock_alert_result_list.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/mock_alert_result_list.ts
new file mode 100644
index 0000000000000..338a1077b58a2
--- /dev/null
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/mock_alert_result_list.ts
@@ -0,0 +1,60 @@
+/*
+ * 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 { AlertResultList } from '../../../../../common/types';
+
+export const mockAlertResultList: (options?: {
+  total?: number;
+  request_page_size?: number;
+  request_page_index?: number;
+}) => AlertResultList = (options = {}) => {
+  const {
+    total = 1,
+    request_page_size: requestPageSize = 10,
+    request_page_index: requestPageIndex = 0,
+  } = options;
+
+  // Skip any that are before the page we're on
+  const numberToSkip = requestPageSize * requestPageIndex;
+
+  // total - numberToSkip is the count of non-skipped ones, but return no more than a pageSize, and no less than 0
+  const actualCountToReturn = Math.max(Math.min(total - numberToSkip, requestPageSize), 0);
+
+  const alerts = [];
+  for (let index = 0; index < actualCountToReturn; index++) {
+    alerts.push({
+      '@timestamp': new Date(1542341895000).toString(),
+      agent: {
+        id: 'ced9c68e-b94a-4d66-bb4c-6106514f0a2f',
+        version: '3.0.0',
+      },
+      event: {
+        action: 'open',
+      },
+      file_classification: {
+        malware_classification: {
+          score: 3,
+        },
+      },
+      host: {
+        hostname: 'HD-c15-bc09190a',
+        ip: '10.179.244.14',
+        os: {
+          name: 'Windows',
+        },
+      },
+      thread: {},
+    });
+  }
+  const mock: AlertResultList = {
+    alerts,
+    total,
+    request_page_size: requestPageSize,
+    request_page_index: requestPageIndex,
+    result_from_index: 0,
+  };
+  return mock;
+};
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts
index 6ad053888729c..3a0461e06538f 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts
@@ -4,9 +4,20 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import qs from 'querystring';
-import { AlertListState } from '../../types';
+import querystring from 'querystring';
+import {
+  createSelector,
+  createStructuredSelector as createStructuredSelectorWithBadType,
+} from 'reselect';
+import { Immutable } from '../../../../../common/types';
+import {
+  AlertListState,
+  AlertingIndexUIQueryParams,
+  AlertsAPIQueryParams,
+  CreateStructuredSelector,
+} from '../../types';
 
+const createStructuredSelector: CreateStructuredSelector = createStructuredSelectorWithBadType;
 /**
  * Returns the Alert Data array from state
  */
@@ -15,14 +26,12 @@ export const alertListData = (state: AlertListState) => state.alerts;
 /**
  * Returns the alert list pagination data from state
  */
-export const alertListPagination = (state: AlertListState) => {
-  return {
-    pageIndex: state.request_page_index,
-    pageSize: state.request_page_size,
-    resultFromIndex: state.result_from_index,
-    total: state.total,
-  };
-};
+export const alertListPagination = createStructuredSelector({
+  pageIndex: (state: AlertListState) => state.request_page_index,
+  pageSize: (state: AlertListState) => state.request_page_size,
+  resultFromIndex: (state: AlertListState) => state.result_from_index,
+  total: (state: AlertListState) => state.total,
+});
 
 /**
  * Returns a boolean based on whether or not the user is on the alerts page
@@ -32,48 +41,55 @@ export const isOnAlertPage = (state: AlertListState): boolean => {
 };
 
 /**
- * Returns the query object received from parsing the URL query params
- */
-export const paginationDataFromUrl = (state: AlertListState): qs.ParsedUrlQuery => {
-  if (state.location) {
-    // Removes the `?` from the beginning of query string if it exists
-    const query = qs.parse(state.location.search.slice(1));
-    return {
-      ...(query.page_size ? { page_size: query.page_size } : {}),
-      ...(query.page_index ? { page_index: query.page_index } : {}),
-    };
-  } else {
-    return {};
-  }
-};
-
-/**
- * Returns a function that takes in a new page size and returns a new query param string
+ * Returns the query object received from parsing the browsers URL query params.
+ * Used to calculate urls for links and such.
  */
-export const urlFromNewPageSizeParam: (
+export const uiQueryParams: (
   state: AlertListState
-) => (newPageSize: number) => string = state => {
-  return newPageSize => {
-    const urlPaginationData = paginationDataFromUrl(state);
-    urlPaginationData.page_size = newPageSize.toString();
+) => Immutable<AlertingIndexUIQueryParams> = createSelector(
+  (state: AlertListState) => state.location,
+  (location: AlertListState['location']) => {
+    const data: AlertingIndexUIQueryParams = {};
+    if (location) {
+      // Removes the `?` from the beginning of query string if it exists
+      const query = querystring.parse(location.search.slice(1));
 
-    // Only set the url back to page zero if the user has changed the page index already
-    if (urlPaginationData.page_index !== undefined) {
-      urlPaginationData.page_index = '0';
+      /**
+       * Build an AlertingIndexUIQueryParams object with keys from the query.
+       * If more than one value exists for a key, use the last.
+       */
+      const keys: Array<keyof AlertingIndexUIQueryParams> = [
+        'page_size',
+        'page_index',
+        'selected_alert',
+      ];
+      for (const key of keys) {
+        const value = query[key];
+        if (typeof value === 'string') {
+          data[key] = value;
+        } else if (Array.isArray(value)) {
+          data[key] = value[value.length - 1];
+        }
+      }
     }
-    return '?' + qs.stringify(urlPaginationData);
-  };
-};
+    return data;
+  }
+);
 
 /**
- * Returns a function that takes in a new page index and returns a new query param string
+ * query params to use when requesting alert data.
  */
-export const urlFromNewPageIndexParam: (
+export const apiQueryParams: (
   state: AlertListState
-) => (newPageIndex: number) => string = state => {
-  return newPageIndex => {
-    const urlPaginationData = paginationDataFromUrl(state);
-    urlPaginationData.page_index = newPageIndex.toString();
-    return '?' + qs.stringify(urlPaginationData);
-  };
-};
+) => Immutable<AlertsAPIQueryParams> = createSelector(
+  uiQueryParams,
+  ({ page_size, page_index }) => ({
+    page_size,
+    page_index,
+  })
+);
+
+export const hasSelectedAlert: (state: AlertListState) => boolean = createSelector(
+  uiQueryParams,
+  ({ selected_alert: selectedAlert }) => selectedAlert !== undefined
+);
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts
index 3aeeeaf1c09e2..b95ff7cb2d45c 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts
@@ -48,25 +48,36 @@ export const substateMiddlewareFactory = <Substate>(
   };
 };
 
-export const appStoreFactory = (coreStart: CoreStart): Store => {
+export const appStoreFactory: (
+  /**
+   * Allow middleware to communicate with Kibana core.
+   */
+  coreStart: CoreStart,
+  /**
+   * Create the store without any middleware. This is useful for testing the store w/o side effects.
+   */
+  disableMiddleware?: boolean
+) => Store = (coreStart, disableMiddleware = false) => {
   const store = createStore(
     appReducer,
-    composeWithReduxDevTools(
-      applyMiddleware(
-        substateMiddlewareFactory(
-          globalState => globalState.managementList,
-          managementMiddlewareFactory(coreStart)
-        ),
-        substateMiddlewareFactory(
-          globalState => globalState.policyList,
-          policyListMiddlewareFactory(coreStart)
-        ),
-        substateMiddlewareFactory(
-          globalState => globalState.alertList,
-          alertMiddlewareFactory(coreStart)
+    disableMiddleware
+      ? undefined
+      : composeWithReduxDevTools(
+          applyMiddleware(
+            substateMiddlewareFactory(
+              globalState => globalState.managementList,
+              managementMiddlewareFactory(coreStart)
+            ),
+            substateMiddlewareFactory(
+              globalState => globalState.policyList,
+              policyListMiddlewareFactory(coreStart)
+            ),
+            substateMiddlewareFactory(
+              globalState => globalState.alertList,
+              alertMiddlewareFactory(coreStart)
+            )
+          )
         )
-      )
-    )
   );
 
   return store;
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/types.ts b/x-pack/plugins/endpoint/public/applications/endpoint/types.ts
index d07521d09a119..bd4838419891d 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/types.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/types.ts
@@ -10,6 +10,7 @@ import { EndpointMetadata } from '../../../common/types';
 import { AppAction } from './store/action';
 import { AlertResultList, Immutable } from '../../../common/types';
 
+export { AppAction };
 export type MiddlewareFactory<S = GlobalState> = (
   coreStart: CoreStart
 ) => (
@@ -63,6 +64,9 @@ export interface GlobalState {
   readonly policyList: PolicyListState;
 }
 
+/**
+ * A better type for createStructuredSelector. This doesn't support the options object.
+ */
 export type CreateStructuredSelector = <
   SelectorMap extends { [key: string]: (...args: never[]) => unknown }
 >(
@@ -76,7 +80,6 @@ export type CreateStructuredSelector = <
 export interface EndpointAppLocation {
   pathname: string;
   search: string;
-  state: never;
   hash: string;
   key?: string;
 }
@@ -85,3 +88,35 @@ export type AlertListData = AlertResultList;
 export type AlertListState = Immutable<AlertResultList> & {
   readonly location?: Immutable<EndpointAppLocation>;
 };
+
+/**
+ * Gotten by parsing the URL from the browser. Used to calculate the new URL when changing views.
+ */
+export interface AlertingIndexUIQueryParams {
+  /**
+   * How many items to show in list.
+   */
+  page_size?: string;
+  /**
+   * Which page to show. If `page_index` is 1, show page 2.
+   */
+  page_index?: string;
+  /**
+   * If any value is present, show the alert detail view for the selected alert. Should be an ID for an alert event.
+   */
+  selected_alert?: string;
+}
+
+/**
+ * Query params to pass to the alert API when fetching new data.
+ */
+export interface AlertsAPIQueryParams {
+  /**
+   * Number of results to return.
+   */
+  page_size?: string;
+  /**
+   * 0-based index of 'page' to return.
+   */
+  page_index?: string;
+}
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.test.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.test.tsx
new file mode 100644
index 0000000000000..37847553d512a
--- /dev/null
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.test.tsx
@@ -0,0 +1,189 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import * as reactTestingLibrary from '@testing-library/react';
+import { Provider } from 'react-redux';
+import { I18nProvider } from '@kbn/i18n/react';
+import { AlertIndex } from './index';
+import { appStoreFactory } from '../../store';
+import { coreMock } from 'src/core/public/mocks';
+import { fireEvent, waitForElement, act } from '@testing-library/react';
+import { RouteCapture } from '../route_capture';
+import { createMemoryHistory, MemoryHistory } from 'history';
+import { Router } from 'react-router-dom';
+import { AppAction } from '../../types';
+import { mockAlertResultList } from '../../store/alerts/mock_alert_result_list';
+
+describe('when on the alerting page', () => {
+  let render: () => reactTestingLibrary.RenderResult;
+  let history: MemoryHistory<never>;
+  let store: ReturnType<typeof appStoreFactory>;
+
+  /**
+   * @testing-library/react provides `queryByTestId`, but that uses the data attribute
+   * 'data-testid' whereas our FTR and EUI's tests all use 'data-test-subj'. While @testing-library/react
+   * could be configured to use 'data-test-subj', it is not currently configured that way.
+   *
+   * This provides an equivalent function to `queryByTestId` but that uses our 'data-test-subj' attribute.
+   */
+  let queryByTestSubjId: (
+    renderResult: reactTestingLibrary.RenderResult,
+    testSubjId: string
+  ) => Promise<Element | null>;
+
+  beforeEach(async () => {
+    /**
+     * Create a 'history' instance that is only in-memory and causes no side effects to the testing environment.
+     */
+    history = createMemoryHistory<never>();
+    /**
+     * Create a store, with the middleware disabled. We don't want side effects being created by our code in this test.
+     */
+    store = appStoreFactory(coreMock.createStart(), true);
+    /**
+     * Render the test component, use this after setting up anything in `beforeEach`.
+     */
+    render = () => {
+      /**
+       * Provide the store via `Provider`, and i18n APIs via `I18nProvider`.
+       * Use react-router via `Router`, passing our in-memory `history` instance.
+       * Use `RouteCapture` to emit url-change actions when the URL is changed.
+       * Finally, render the `AlertIndex` component which we are testing.
+       */
+      return reactTestingLibrary.render(
+        <Provider store={store}>
+          <I18nProvider>
+            <Router history={history}>
+              <RouteCapture>
+                <AlertIndex />
+              </RouteCapture>
+            </Router>
+          </I18nProvider>
+        </Provider>
+      );
+    };
+    queryByTestSubjId = async (renderResult, testSubjId) => {
+      return await waitForElement(
+        /**
+         * Use document.body instead of container because EUI renders things like popover out of the DOM heirarchy.
+         */
+        () => document.body.querySelector(`[data-test-subj="${testSubjId}"]`),
+        {
+          container: renderResult.container,
+        }
+      );
+    };
+  });
+  it('should show a data grid', async () => {
+    await render().findByTestId('alertListGrid');
+  });
+  describe('when there is no selected alert in the url', () => {
+    it('should not show the flyout', () => {
+      expect(render().queryByTestId('alertDetailFlyout')).toBeNull();
+    });
+    describe('when data loads', () => {
+      beforeEach(() => {
+        /**
+         * Dispatch the `serverReturnedAlertsData` action, which is normally dispatched by the middleware
+         * after interacting with the server.
+         */
+        reactTestingLibrary.act(() => {
+          const action: AppAction = {
+            type: 'serverReturnedAlertsData',
+            payload: mockAlertResultList(),
+          };
+          store.dispatch(action);
+        });
+      });
+      it('should render the alert summary row in the grid', async () => {
+        const renderResult = render();
+        const rows = await renderResult.findAllByRole('row');
+
+        /**
+         * There should be a 'row' which is the header, and
+         * row which is the alert item.
+         */
+        expect(rows).toHaveLength(2);
+      });
+      describe('when the user has clicked the alert type in the grid', () => {
+        let renderResult: reactTestingLibrary.RenderResult;
+        beforeEach(async () => {
+          renderResult = render();
+          /**
+           * This is the cell with the alert type, it has a link.
+           */
+          fireEvent.click(await renderResult.findByTestId('alertTypeCellLink'));
+        });
+        it('should show the flyout', async () => {
+          await renderResult.findByTestId('alertDetailFlyout');
+        });
+      });
+    });
+  });
+  describe('when there is a selected alert in the url', () => {
+    beforeEach(() => {
+      reactTestingLibrary.act(() => {
+        history.push({
+          ...history.location,
+          search: '?selected_alert=1',
+        });
+      });
+    });
+    it('should show the flyout', async () => {
+      await render().findByTestId('alertDetailFlyout');
+    });
+    describe('when the user clicks the close button on the flyout', () => {
+      let renderResult: reactTestingLibrary.RenderResult;
+      beforeEach(async () => {
+        renderResult = render();
+        /**
+         * Use our helper function to find the flyout's close button, as it uses a different test ID attribute.
+         */
+        const closeButton = await queryByTestSubjId(renderResult, 'euiFlyoutCloseButton');
+        if (closeButton) {
+          fireEvent.click(closeButton);
+        }
+      });
+      it('should no longer show the flyout', () => {
+        expect(render().queryByTestId('alertDetailFlyout')).toBeNull();
+      });
+    });
+  });
+  describe('when the url has page_size=1 and a page_index=1', () => {
+    beforeEach(() => {
+      reactTestingLibrary.act(() => {
+        history.push({
+          ...history.location,
+          search: '?page_size=1&page_index=1',
+        });
+      });
+    });
+    describe('when the user changes page size to 10', () => {
+      beforeEach(async () => {
+        const renderResult = render();
+        const paginationButton = await queryByTestSubjId(
+          renderResult,
+          'tablePaginationPopoverButton'
+        );
+        if (paginationButton) {
+          act(() => {
+            fireEvent.click(paginationButton);
+          });
+        }
+        const show10RowsButton = await queryByTestSubjId(renderResult, 'tablePagination-10-rows');
+        if (show10RowsButton) {
+          act(() => {
+            fireEvent.click(show10RowsButton);
+          });
+        }
+      });
+      it('should have a page_index of 0', () => {
+        expect(history.location.search).toBe('?page_size=10');
+      });
+    });
+  });
+});
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx
index 045b82200b11b..6f88727575557 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx
@@ -6,16 +6,30 @@
 
 import { memo, useState, useMemo, useCallback } from 'react';
 import React from 'react';
-import { EuiDataGrid, EuiDataGridColumn, EuiPage, EuiPageBody, EuiPageContent } from '@elastic/eui';
+import {
+  EuiDataGrid,
+  EuiDataGridColumn,
+  EuiPage,
+  EuiPageBody,
+  EuiPageContent,
+  EuiFlyout,
+  EuiFlyoutHeader,
+  EuiFlyoutBody,
+  EuiTitle,
+  EuiBadge,
+} from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
-import { useHistory } from 'react-router-dom';
+import { useHistory, Link } from 'react-router-dom';
+import { FormattedDate } from 'react-intl';
+import { urlFromQueryParams } from './url_from_query_params';
+import { AlertData } from '../../../../../common/types';
 import * as selectors from '../../store/alerts/selectors';
 import { useAlertListSelector } from './hooks/use_alerts_selector';
 
 export const AlertIndex = memo(() => {
   const history = useHistory();
 
-  const columns: EuiDataGridColumn[] = useMemo(() => {
+  const columns = useMemo((): EuiDataGridColumn[] => {
     return [
       {
         id: 'alert_type',
@@ -69,22 +83,48 @@ export const AlertIndex = memo(() => {
   }, []);
 
   const { pageIndex, pageSize, total } = useAlertListSelector(selectors.alertListPagination);
-  const urlFromNewPageSizeParam = useAlertListSelector(selectors.urlFromNewPageSizeParam);
-  const urlFromNewPageIndexParam = useAlertListSelector(selectors.urlFromNewPageIndexParam);
   const alertListData = useAlertListSelector(selectors.alertListData);
+  const hasSelectedAlert = useAlertListSelector(selectors.hasSelectedAlert);
+  const queryParams = useAlertListSelector(selectors.uiQueryParams);
 
   const onChangeItemsPerPage = useCallback(
-    newPageSize => history.push(urlFromNewPageSizeParam(newPageSize)),
-    [history, urlFromNewPageSizeParam]
+    newPageSize => {
+      const newQueryParms = { ...queryParams };
+      newQueryParms.page_size = newPageSize;
+      delete newQueryParms.page_index;
+      const relativeURL = urlFromQueryParams(newQueryParms);
+      return history.push(relativeURL);
+    },
+    [history, queryParams]
   );
 
   const onChangePage = useCallback(
-    newPageIndex => history.push(urlFromNewPageIndexParam(newPageIndex)),
-    [history, urlFromNewPageIndexParam]
+    newPageIndex => {
+      return history.push(
+        urlFromQueryParams({
+          ...queryParams,
+          page_index: newPageIndex,
+        })
+      );
+    },
+    [history, queryParams]
   );
 
   const [visibleColumns, setVisibleColumns] = useState(() => columns.map(({ id }) => id));
 
+  const handleFlyoutClose = useCallback(() => {
+    const { selected_alert, ...paramsWithoutSelectedAlert } = queryParams;
+    history.push(urlFromQueryParams(paramsWithoutSelectedAlert));
+  }, [history, queryParams]);
+
+  const datesForRows: Map<AlertData, Date> = useMemo(() => {
+    return new Map(
+      alertListData.map(alertData => {
+        return [alertData, new Date(alertData['@timestamp'])];
+      })
+    );
+  }, [alertListData]);
+
   const renderCellValue = useMemo(() => {
     return ({ rowIndex, columnId }: { rowIndex: number; columnId: string }) => {
       if (rowIndex > total) {
@@ -94,11 +134,18 @@ export const AlertIndex = memo(() => {
       const row = alertListData[rowIndex % pageSize];
 
       if (columnId === 'alert_type') {
-        return i18n.translate(
-          'xpack.endpoint.application.endpoint.alerts.alertType.maliciousFileDescription',
-          {
-            defaultMessage: 'Malicious File',
-          }
+        return (
+          <Link
+            data-testid="alertTypeCellLink"
+            to={urlFromQueryParams({ ...queryParams, selected_alert: 'TODO' })}
+          >
+            {i18n.translate(
+              'xpack.endpoint.application.endpoint.alerts.alertType.maliciousFileDescription',
+              {
+                defaultMessage: 'Malicious File',
+              }
+            )}
+          </Link>
         );
       } else if (columnId === 'event_type') {
         return row.event.action;
@@ -109,7 +156,31 @@ export const AlertIndex = memo(() => {
       } else if (columnId === 'host_name') {
         return row.host.hostname;
       } else if (columnId === 'timestamp') {
-        return row['@timestamp'];
+        const date = datesForRows.get(row)!;
+        if (date && isFinite(date.getTime())) {
+          return (
+            <FormattedDate
+              value={date}
+              year="numeric"
+              month="2-digit"
+              day="2-digit"
+              hour="2-digit"
+              minute="2-digit"
+              second="2-digit"
+            />
+          );
+        } else {
+          return (
+            <EuiBadge color="warning">
+              {i18n.translate(
+                'xpack.endpoint.application.endpoint.alerts.alertDate.timestampInvalidLabel',
+                {
+                  defaultMessage: 'invalid',
+                }
+              )}
+            </EuiBadge>
+          );
+        }
       } else if (columnId === 'archived') {
         return null;
       } else if (columnId === 'malware_score') {
@@ -117,7 +188,7 @@ export const AlertIndex = memo(() => {
       }
       return null;
     };
-  }, [alertListData, pageSize, total]);
+  }, [alertListData, datesForRows, pageSize, queryParams, total]);
 
   const pagination = useMemo(() => {
     return {
@@ -130,23 +201,43 @@ export const AlertIndex = memo(() => {
   }, [onChangeItemsPerPage, onChangePage, pageIndex, pageSize]);
 
   return (
-    <EuiPage data-test-subj="alertListPage">
-      <EuiPageBody>
-        <EuiPageContent>
-          <EuiDataGrid
-            aria-label="Alert List"
-            rowCount={total}
-            columns={columns}
-            columnVisibility={{
-              visibleColumns,
-              setVisibleColumns,
-            }}
-            renderCellValue={renderCellValue}
-            pagination={pagination}
-            data-test-subj="alertListGrid"
-          />
-        </EuiPageContent>
-      </EuiPageBody>
-    </EuiPage>
+    <>
+      {hasSelectedAlert && (
+        <EuiFlyout data-testid="alertDetailFlyout" size="l" onClose={handleFlyoutClose}>
+          <EuiFlyoutHeader hasBorder>
+            <EuiTitle size="m">
+              <h2>
+                {i18n.translate('xpack.endpoint.application.endpoint.alerts.detailsTitle', {
+                  defaultMessage: 'Alert Details',
+                })}
+              </h2>
+            </EuiTitle>
+          </EuiFlyoutHeader>
+          <EuiFlyoutBody />
+        </EuiFlyout>
+      )}
+      <EuiPage data-test-subj="alertListPage" data-testid="alertListPage">
+        <EuiPageBody>
+          <EuiPageContent>
+            <EuiDataGrid
+              aria-label="Alert List"
+              rowCount={total}
+              columns={columns}
+              columnVisibility={useMemo(
+                () => ({
+                  visibleColumns,
+                  setVisibleColumns,
+                }),
+                [setVisibleColumns, visibleColumns]
+              )}
+              renderCellValue={renderCellValue}
+              pagination={pagination}
+              data-test-subj="alertListGrid"
+              data-testid="alertListGrid"
+            />
+          </EuiPageContent>
+        </EuiPageBody>
+      </EuiPage>
+    </>
   );
 });
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/url_from_query_params.ts b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/url_from_query_params.ts
new file mode 100644
index 0000000000000..e037d000e6e8f
--- /dev/null
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/url_from_query_params.ts
@@ -0,0 +1,31 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import querystring from 'querystring';
+import { AlertingIndexUIQueryParams, EndpointAppLocation } from '../../types';
+
+/**
+ * Return a relative URL for `AlertingIndexUIQueryParams`.
+ * usage:
+ *
+ * ```ts
+ * // Replace this with however you get state, e.g. useSelector in react
+ * const queryParams = selectors.uiQueryParams(store.getState())
+ *
+ * // same as current url, but page_index is now 3
+ * const relativeURL = urlFromQueryParams({ ...queryParams, page_index: 3 })
+ *
+ * // now use relativeURL in the 'href' of a link, the 'to' of a react-router-dom 'Link' or history.push, history.replace
+ * ```
+ */
+export function urlFromQueryParams(
+  queryParams: AlertingIndexUIQueryParams
+): Partial<EndpointAppLocation> {
+  const search = querystring.stringify(queryParams);
+  return {
+    search,
+  };
+}
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/route_capture.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/route_capture.tsx
new file mode 100644
index 0000000000000..28d2019b56888
--- /dev/null
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/route_capture.tsx
@@ -0,0 +1,21 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { memo } from 'react';
+import { useLocation } from 'react-router-dom';
+import { useDispatch } from 'react-redux';
+import { EndpointAppLocation, AppAction } from '../types';
+
+/**
+ * This component should be used above all routes, but below the Provider.
+ * It dispatches actions when the URL is changed.
+ */
+export const RouteCapture = memo(({ children }) => {
+  const location: EndpointAppLocation = useLocation();
+  const dispatch: (action: AppAction) => unknown = useDispatch();
+  dispatch({ type: 'userChangedUrl', payload: location });
+  return <>{children}</>;
+});
diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/selectors.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/selectors.ts
index 4d12e656205fa..25d08a8c347ed 100644
--- a/x-pack/plugins/endpoint/public/embeddables/resolver/store/selectors.ts
+++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/selectors.ts
@@ -31,7 +31,6 @@ export const inverseProjectionMatrix = composeSelectors(
 
 /**
  * The scale by which world values are scaled when rendered.
- * TODO make it a number
  */
 export const scale = composeSelectors(cameraStateSelector, cameraSelectors.scale);
 
diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/view/use_camera.test.tsx b/x-pack/plugins/endpoint/public/embeddables/resolver/view/use_camera.test.tsx
index 85e1d4e694b15..f4abb51f062f2 100644
--- a/x-pack/plugins/endpoint/public/embeddables/resolver/view/use_camera.test.tsx
+++ b/x-pack/plugins/endpoint/public/embeddables/resolver/view/use_camera.test.tsx
@@ -4,9 +4,6 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-/**
- * This import must be hoisted as it uses `jest.mock`. Is there a better way? Mocking is not good.
- */
 import React from 'react';
 import { render, act, RenderResult, fireEvent } from '@testing-library/react';
 import { useCamera } from './use_camera';
diff --git a/x-pack/plugins/endpoint/server/services/endpoint/alert_query_builders.test.ts b/x-pack/plugins/endpoint/server/services/endpoint/alert_query_builders.test.ts
index a4d7de8fdcfdb..3ef1142b9ce46 100644
--- a/x-pack/plugins/endpoint/server/services/endpoint/alert_query_builders.test.ts
+++ b/x-pack/plugins/endpoint/server/services/endpoint/alert_query_builders.test.ts
@@ -16,26 +16,36 @@ describe('test query builder', () => {
         config: () => Promise.resolve(EndpointConfigSchema.validate({})),
       };
       const queryParams = await getPagingProperties(mockRequest, mockCtx);
-      const query = await buildAlertListESQuery(queryParams);
+      const query = buildAlertListESQuery(queryParams);
 
-      expect(query).toEqual({
-        body: {
-          query: {
-            match_all: {},
-          },
-          sort: [
-            {
-              '@timestamp': {
-                order: 'desc',
+      expect(query).toMatchInlineSnapshot(`
+        Object {
+          "body": Object {
+            "query": Object {
+              "bool": Object {
+                "must": Array [
+                  Object {
+                    "match": Object {
+                      "event.kind": "alert",
+                    },
+                  },
+                ],
               },
             },
-          ],
-          track_total_hits: 10000,
-        },
-        from: 0,
-        size: 10,
-        index: 'my-index',
-      } as Record<string, any>);
+            "sort": Array [
+              Object {
+                "@timestamp": Object {
+                  "order": "desc",
+                },
+              },
+            ],
+            "track_total_hits": 10000,
+          },
+          "from": 0,
+          "index": "my-index",
+          "size": 10,
+        }
+      `);
     });
     it('should adjust track_total_hits for deep pagination', async () => {
       const mockRequest = httpServerMock.createKibanaRequest({
@@ -49,26 +59,36 @@ describe('test query builder', () => {
         config: () => Promise.resolve(EndpointConfigSchema.validate({})),
       };
       const queryParams = await getPagingProperties(mockRequest, mockCtx);
-      const query = await buildAlertListESQuery(queryParams);
+      const query = buildAlertListESQuery(queryParams);
 
-      expect(query).toEqual({
-        body: {
-          query: {
-            match_all: {},
-          },
-          sort: [
-            {
-              '@timestamp': {
-                order: 'desc',
+      expect(query).toMatchInlineSnapshot(`
+        Object {
+          "body": Object {
+            "query": Object {
+              "bool": Object {
+                "must": Array [
+                  Object {
+                    "match": Object {
+                      "event.kind": "alert",
+                    },
+                  },
+                ],
               },
             },
-          ],
-          track_total_hits: 12000,
-        },
-        from: 10000,
-        size: 1000,
-        index: 'my-index',
-      } as Record<string, any>);
+            "sort": Array [
+              Object {
+                "@timestamp": Object {
+                  "order": "desc",
+                },
+              },
+            ],
+            "track_total_hits": 12000,
+          },
+          "from": 10000,
+          "index": "my-index",
+          "size": 1000,
+        }
+      `);
     });
   });
 });
diff --git a/x-pack/plugins/endpoint/server/services/endpoint/alert_query_builders.ts b/x-pack/plugins/endpoint/server/services/endpoint/alert_query_builders.ts
index a20f2ae1cdecd..e56ae43ef5c76 100644
--- a/x-pack/plugins/endpoint/server/services/endpoint/alert_query_builders.ts
+++ b/x-pack/plugins/endpoint/server/services/endpoint/alert_query_builders.ts
@@ -7,9 +7,9 @@ import { KibanaRequest } from 'kibana/server';
 import { EndpointAppConstants } from '../../../common/types';
 import { EndpointAppContext, AlertRequestParams, JSONish } from '../../types';
 
-export const buildAlertListESQuery = async (
+export const buildAlertListESQuery: (
   pagingProperties: Record<string, number>
-): Promise<JSONish> => {
+) => JSONish = pagingProperties => {
   const DEFAULT_TOTAL_HITS = 10000;
 
   // Calculate minimum total hits set to indicate there's a next page
@@ -22,7 +22,15 @@ export const buildAlertListESQuery = async (
     body: {
       track_total_hits: totalHitsMin,
       query: {
-        match_all: {},
+        bool: {
+          must: [
+            {
+              match: {
+                'event.kind': 'alert',
+              },
+            },
+          ],
+        },
       },
       sort: [
         {

From 8ccbe87069416da68feb87b411fcb6292d3456c5 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Fri, 21 Feb 2020 14:49:48 -0600
Subject: [PATCH 133/174] Update dependency @elastic/charts to ^17.1.1 (#57634)

---
 package.json                             |  2 +-
 packages/kbn-ui-shared-deps/package.json |  2 +-
 yarn.lock                                | 59 +++++-------------------
 3 files changed, 13 insertions(+), 50 deletions(-)

diff --git a/package.json b/package.json
index cb87c48ab7f2a..c3fe290e7934f 100644
--- a/package.json
+++ b/package.json
@@ -117,7 +117,7 @@
     "@babel/core": "^7.5.5",
     "@babel/register": "^7.7.0",
     "@elastic/apm-rum": "^4.6.0",
-    "@elastic/charts": "^17.0.2",
+    "@elastic/charts": "^17.1.1",
     "@elastic/datemath": "5.0.2",
     "@elastic/ems-client": "7.6.0",
     "@elastic/eui": "19.0.0",
diff --git a/packages/kbn-ui-shared-deps/package.json b/packages/kbn-ui-shared-deps/package.json
index acb2b48e12278..e9ad227b235fa 100644
--- a/packages/kbn-ui-shared-deps/package.json
+++ b/packages/kbn-ui-shared-deps/package.json
@@ -9,7 +9,7 @@
     "kbn:watch": "node scripts/build --watch"
   },
   "devDependencies": {
-    "@elastic/charts": "^17.0.2",
+    "@elastic/charts": "^17.1.1",
     "abortcontroller-polyfill": "^1.4.0",
     "@elastic/eui": "19.0.0",
     "@kbn/babel-preset": "1.0.0",
diff --git a/yarn.lock b/yarn.lock
index 2caaac974dfad..8ea23c17b8b8b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1680,7 +1680,7 @@
   dependencies:
     regenerator-runtime "^0.13.2"
 
-"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.1", "@babel/runtime@^7.5.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2":
+"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.1", "@babel/runtime@^7.5.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2":
   version "7.7.6"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.6.tgz#d18c511121aff1b4f2cd1d452f1bac9601dd830f"
   integrity sha512-BWAJxpNVa0QlE5gZdWjSxXtemZyZ9RmrmVozxt3NUXeZhVIJ5ANyqmMc0JDrivBZyxUuQvFxlvH4OWWOogGfUw==
@@ -1880,10 +1880,10 @@
   dependencies:
     "@elastic/apm-rum-core" "^4.7.0"
 
-"@elastic/charts@^17.0.2":
-  version "17.0.2"
-  resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-17.0.2.tgz#0bd2cb7b78bd60255eed2a9b0ce5049a62dc5d8a"
-  integrity sha512-hz31Yma/HSdu9tRS/05xNXY+YuaHOadD9Gd+eh7ankNlJJOFI4IRV6F8BMnloeWYedNXBSTp4susFLwJNQQ+0w==
+"@elastic/charts@^17.1.1":
+  version "17.1.1"
+  resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-17.1.1.tgz#28df3d1be445aa4951fac59dec0831760a1ea034"
+  integrity sha512-zI3ZHy51tqlsEenDbHFWlL6qbDMvrQXeuV+UFleILtVcfI0r4Lk7DtSbx4GQlHcBuVvvAk1MRdYKNGA0IYWP6w==
   dependencies:
     "@types/d3-shape" "^1.3.1"
     classnames "^2.2.6"
@@ -1892,14 +1892,11 @@
     d3-color "^1.4.0"
     d3-scale "^1.0.7"
     d3-shape "^1.3.4"
-    fast-deep-equal "^3.1.1"
-    konva "^4.0.18"
     newtype-ts "^0.2.4"
+    path2d-polyfill "^0.4.2"
     prop-types "^15.7.2"
     re-reselect "^3.4.0"
-    react-konva "16.10.1-0"
     react-redux "^7.1.0"
-    react-spring "^8.0.8"
     redux "^4.0.4"
     reselect "^4.0.0"
     resize-observer-polyfill "^1.5.1"
@@ -19008,11 +19005,6 @@ known-css-properties@^0.3.0:
   resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.3.0.tgz#a3d135bbfc60ee8c6eacf2f7e7e6f2d4755e49a4"
   integrity sha512-QMQcnKAiQccfQTqtBh/qwquGZ2XK/DXND1jrcN9M8gMMy99Gwla7GQjndVUsEqIaRyP6bsFRuhwRj5poafBGJQ==
 
-konva@^4.0.18:
-  version "4.0.18"
-  resolved "https://registry.yarnpkg.com/konva/-/konva-4.0.18.tgz#43e614c9b22827183506d4a6b3b474f90187b469"
-  integrity sha512-Tlq0v7QHr8q73xr1cKjHdQl41oHC06IOldPO+ukjt99G74NgoU0TVouvPIFpW2whA9t3xNk/+/VJcc3XPcboOw==
-
 kopy@^8.2.0:
   version "8.2.5"
   resolved "https://registry.yarnpkg.com/kopy/-/kopy-8.2.5.tgz#6c95f312e981ab917680d7e5de3cdf29a1bf221f"
@@ -22917,6 +22909,11 @@ path-type@^4.0.0:
   resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
   integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
 
+path2d-polyfill@^0.4.2:
+  version "0.4.2"
+  resolved "https://registry.yarnpkg.com/path2d-polyfill/-/path2d-polyfill-0.4.2.tgz#594d3103838ef6b9dd4a7fd498fe9a88f1f28531"
+  integrity sha512-JSeAnUfkFjl+Ml/EZL898ivMSbGHrOH63Mirx5EQ1ycJiryHDmj1Q7Are+uEPvenVGCUN9YbolfGfyUewJfJEg==
+
 pathval@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0"
@@ -24504,14 +24501,6 @@ react-is@~16.3.0:
   resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.3.2.tgz#f4d3d0e2f5fbb6ac46450641eb2e25bf05d36b22"
   integrity sha512-ybEM7YOr4yBgFd6w8dJqwxegqZGJNBZl6U27HnGKuTZmDvVrD5quWOK/wAnMywiZzW+Qsk+l4X2c70+thp/A8Q==
 
-react-konva@16.10.1-0:
-  version "16.10.1-0"
-  resolved "https://registry.yarnpkg.com/react-konva/-/react-konva-16.10.1-0.tgz#f8cc2c95374933069e891a6c714c70d0fdc77e68"
-  integrity sha512-N0Zi3TcWmUxb2d7y1DUDQhRA+WIcqk54DQmmUmJSadj+fS0bg6iZDebQSEQC8dMbjnLHc/338xRT4a4718PEiw==
-  dependencies:
-    react-reconciler "^0.22.1"
-    scheduler "^0.16.1"
-
 react-lib-adler32@^1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/react-lib-adler32/-/react-lib-adler32-1.0.3.tgz#63df1aed274eabcc1c5067077ea281ec30888ba7"
@@ -24647,16 +24636,6 @@ react-portal@^3.2.0:
   dependencies:
     prop-types "^15.5.8"
 
-react-reconciler@^0.22.1:
-  version "0.22.2"
-  resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.22.2.tgz#e8a10374fec8fee7c5cd0cf3cd05626f1b134d3e"
-  integrity sha512-MLX5Y2pNLsdXzWz/GLNhhYkdLOvxEtw2IGqVCzkiRdSFSHRjujI9gfTOQ3rV5z8toTBxSZ2qrRkRUo97mmEdhA==
-  dependencies:
-    loose-envify "^1.1.0"
-    object-assign "^4.1.1"
-    prop-types "^15.6.2"
-    scheduler "^0.16.2"
-
 react-redux@^5.0.7, react-redux@^5.1.2:
   version "5.1.2"
   resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.1.2.tgz#b19cf9e21d694422727bf798e934a916c4080f57"
@@ -24798,14 +24777,6 @@ react-sizeme@^2.6.7:
     shallowequal "^1.1.0"
     throttle-debounce "^2.1.0"
 
-react-spring@^8.0.8:
-  version "8.0.20"
-  resolved "https://registry.yarnpkg.com/react-spring/-/react-spring-8.0.20.tgz#e25967f6059364b09cf0339168d73014e87c9d17"
-  integrity sha512-40ZUQ5uI5YHsoQWLPchWNcEUh6zQ6qvcVDeTI2vW10ldoCN3PvDsII9wBH2xEbMl+BQvYmHzGdfLTQxPxJWGnQ==
-  dependencies:
-    "@babel/runtime" "^7.3.1"
-    prop-types "^15.5.8"
-
 react-sticky@^6.0.3:
   version "6.0.3"
   resolved "https://registry.yarnpkg.com/react-sticky/-/react-sticky-6.0.3.tgz#7a18b643e1863da113d7f7036118d2a75d9ecde4"
@@ -26570,14 +26541,6 @@ saxes@^3.1.9:
   dependencies:
     xmlchars "^2.1.1"
 
-scheduler@^0.16.1, scheduler@^0.16.2:
-  version "0.16.2"
-  resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.16.2.tgz#f74cd9d33eff6fc554edfb79864868e4819132c1"
-  integrity sha512-BqYVWqwz6s1wZMhjFvLfVR5WXP7ZY32M/wYPo04CcuPM7XZEbV2TBNW7Z0UkguPTl0dWMA59VbNXxK6q+pHItg==
-  dependencies:
-    loose-envify "^1.1.0"
-    object-assign "^4.1.1"
-
 scheduler@^0.18.0:
   version "0.18.0"
   resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.18.0.tgz#5901ad6659bc1d8f3fdaf36eb7a67b0d6746b1c4"

From d61ef267d530554cd048b5e602a0b6c2e362185f Mon Sep 17 00:00:00 2001
From: Mikhail Shustov <restrry@gmail.com>
Date: Fri, 21 Feb 2020 22:11:05 +0100
Subject: [PATCH 134/174] force savedObject API consumers to define SO type
 explicitly (#58022)

* force savedObject consumers to define type explicitly

* address comments

* regen docs
---
 .../kibana-plugin-public.savedobject.md       |  2 +-
 ...plugin-public.savedobjectsbatchresponse.md |  2 +-
 ...gin-public.savedobjectsbulkcreateobject.md |  2 +-
 ...gin-public.savedobjectsbulkupdateobject.md |  2 +-
 ...in-public.savedobjectsclient.bulkcreate.md |  2 +-
 ...lugin-public.savedobjectsclient.bulkget.md |  2 +-
 ...in-public.savedobjectsclient.bulkupdate.md |  4 +-
 ...plugin-public.savedobjectsclient.create.md |  2 +-
 ...a-plugin-public.savedobjectsclient.find.md |  2 +-
 ...na-plugin-public.savedobjectsclient.get.md |  2 +-
 ...kibana-plugin-public.savedobjectsclient.md | 10 ++---
 ...plugin-public.savedobjectsclient.update.md |  2 +-
 ...n-public.savedobjectsfindresponsepublic.md |  2 +-
 .../kibana-plugin-public.simplesavedobject.md |  2 +-
 .../kibana-plugin-server.savedobject.md       |  2 +-
 ...gin-server.savedobjectsbulkcreateobject.md |  2 +-
 ...-plugin-server.savedobjectsbulkresponse.md |  2 +-
 ...gin-server.savedobjectsbulkupdateobject.md |  2 +-
 ...n-server.savedobjectsbulkupdateresponse.md |  2 +-
 ...in-server.savedobjectsclient.bulkcreate.md |  2 +-
 ...lugin-server.savedobjectsclient.bulkget.md |  2 +-
 ...in-server.savedobjectsclient.bulkupdate.md |  2 +-
 ...plugin-server.savedobjectsclient.create.md |  2 +-
 ...a-plugin-server.savedobjectsclient.find.md |  2 +-
 ...na-plugin-server.savedobjectsclient.get.md |  2 +-
 ...plugin-server.savedobjectsclient.update.md |  2 +-
 ...-plugin-server.savedobjectsfindresponse.md |  2 +-
 ...erver.savedobjectsrepository.bulkcreate.md |  2 +-
 ...n-server.savedobjectsrepository.bulkget.md |  2 +-
 ...erver.savedobjectsrepository.bulkupdate.md |  2 +-
 ...in-server.savedobjectsrepository.create.md |  2 +-
 ...ugin-server.savedobjectsrepository.find.md |  2 +-
 ...lugin-server.savedobjectsrepository.get.md |  2 +-
 ...in-server.savedobjectsrepository.update.md |  2 +-
 ...lugin-server.savedobjectsupdateresponse.md |  2 +-
 src/core/public/public.api.md                 | 26 +++++------
 .../saved_objects/saved_objects_client.ts     | 36 +++++----------
 .../saved_objects/simple_saved_object.ts      |  6 +--
 .../import/collect_saved_objects.ts           |  4 +-
 .../saved_objects/import/extract_errors.ts    |  7 +--
 .../import/validate_references.ts             |  2 +-
 .../saved_objects/management/management.ts    |  6 +--
 .../saved_objects/serialization/types.ts      |  2 +-
 .../saved_objects/service/lib/repository.ts   | 19 ++++----
 .../service/saved_objects_client.ts           | 35 ++++++---------
 src/core/server/saved_objects/types.ts        |  3 +-
 src/core/server/server.api.md                 | 44 +++++++++----------
 .../create_or_upgrade_saved_config.ts         |  5 ++-
 .../server/ui_settings/ui_settings_client.ts  |  2 +-
 .../discover_index_pattern.test.tsx           |  4 +-
 .../field_chooser/discover_index_pattern.tsx  |  5 ++-
 .../step_index_pattern/step_index_pattern.tsx |  3 +-
 .../public/helpers/arg_value_suggestions.ts   |  9 ++--
 .../public/embeddable/get_index_pattern.ts    | 13 ++++--
 .../data/common/index_patterns/types.ts       | 12 +++++
 src/plugins/data/public/index.ts              |  1 +
 .../index_patterns/index_patterns.ts          |  2 +-
 .../lib/get_from_saved_object.ts              |  9 ++--
 .../fetch_index_patterns.ts                   | 11 +++--
 src/plugins/data/server/index.ts              |  1 +
 .../lib/sample_dataset_registry_types.ts      |  4 +-
 .../sample_data/sample_data_registry.ts       |  4 +-
 .../public/finder/saved_object_finder.tsx     | 31 +++++++------
 .../saved_object/saved_object_loader.ts       |  2 +-
 .../routes/lib/short_url_lookup.test.ts       | 28 +++++-------
 .../server/routes/lib/short_url_lookup.ts     | 15 +++++--
 .../plugins/canvas/public/lib/es_service.ts   |  6 ++-
 .../persistence/saved_workspace_loader.ts     |  2 +-
 .../legacy/plugins/ml/common/types/kibana.ts  |  8 ++--
 .../jobs/new_job/recognize/resolvers.ts       |  2 +-
 .../ml/public/application/util/index_utils.ts |  5 ++-
 .../models/data_recognizer/data_recognizer.ts |  9 +++-
 .../models/job_service/new_job_caps/rollup.ts |  5 ++-
 .../detection_engine/signals/get_filter.ts    | 14 +++++-
 .../signals/get_input_output_index.ts         |  4 +-
 .../signals/signal_rule_alert_type.ts         | 30 ++++++++-----
 .../plugins/task_manager/server/migrations.ts |  4 +-
 .../transform/public/app/lib/kibana/common.ts |  3 +-
 .../np_ready/lib/telemetry/usage_collector.ts |  7 ++-
 .../plugins/actions/server/actions_client.ts  | 12 ++---
 .../actions/server/create_execute_function.ts |  4 +-
 .../plugins/alerting/server/alerts_client.ts  |  2 +-
 .../server/task_runner/task_runner.ts         |  3 +-
 .../canvas/server/routes/workpad/update.ts    |  3 +-
 .../case/server/services/tags/read_tags.ts    |  2 +-
 .../encrypted_saved_objects_client_wrapper.ts | 35 +++++++--------
 .../server/saved_objects/index.ts             | 17 +++----
 .../hooks/use_bulk_get_saved_object.tsx       |  5 ++-
 .../server/routes/existing_fields.test.ts     |  2 +
 .../lens/server/routes/existing_fields.ts     | 12 +++--
 .../secure_saved_objects_client_wrapper.ts    | 27 ++++++------
 .../spaces_saved_objects_client.ts            | 27 ++++++------
 .../task_manager/server/task_store.test.ts    | 28 +++++-------
 .../plugins/task_manager/server/task_store.ts |  3 +-
 .../suites/resolve_copy_to_space_conflicts.ts |  4 +-
 95 files changed, 387 insertions(+), 329 deletions(-)

diff --git a/docs/development/core/public/kibana-plugin-public.savedobject.md b/docs/development/core/public/kibana-plugin-public.savedobject.md
index b1bb3e267bf0e..542d7d3a063ec 100644
--- a/docs/development/core/public/kibana-plugin-public.savedobject.md
+++ b/docs/development/core/public/kibana-plugin-public.savedobject.md
@@ -8,7 +8,7 @@
 <b>Signature:</b>
 
 ```typescript
-export interface SavedObject<T extends SavedObjectAttributes = any> 
+export interface SavedObject<T = unknown> 
 ```
 
 ## Properties
diff --git a/docs/development/core/public/kibana-plugin-public.savedobjectsbatchresponse.md b/docs/development/core/public/kibana-plugin-public.savedobjectsbatchresponse.md
index 5a08b3f97f429..97772112ff006 100644
--- a/docs/development/core/public/kibana-plugin-public.savedobjectsbatchresponse.md
+++ b/docs/development/core/public/kibana-plugin-public.savedobjectsbatchresponse.md
@@ -8,7 +8,7 @@
 <b>Signature:</b>
 
 ```typescript
-export interface SavedObjectsBatchResponse<T extends SavedObjectAttributes = SavedObjectAttributes> 
+export interface SavedObjectsBatchResponse<T = unknown> 
 ```
 
 ## Properties
diff --git a/docs/development/core/public/kibana-plugin-public.savedobjectsbulkcreateobject.md b/docs/development/core/public/kibana-plugin-public.savedobjectsbulkcreateobject.md
index c479e9f9f3e3f..ef83acb8fd73d 100644
--- a/docs/development/core/public/kibana-plugin-public.savedobjectsbulkcreateobject.md
+++ b/docs/development/core/public/kibana-plugin-public.savedobjectsbulkcreateobject.md
@@ -7,7 +7,7 @@
 <b>Signature:</b>
 
 ```typescript
-export interface SavedObjectsBulkCreateObject<T extends SavedObjectAttributes = SavedObjectAttributes> extends SavedObjectsCreateOptions 
+export interface SavedObjectsBulkCreateObject<T = unknown> extends SavedObjectsCreateOptions 
 ```
 
 ## Properties
diff --git a/docs/development/core/public/kibana-plugin-public.savedobjectsbulkupdateobject.md b/docs/development/core/public/kibana-plugin-public.savedobjectsbulkupdateobject.md
index ca0eabb265901..a57702c305f50 100644
--- a/docs/development/core/public/kibana-plugin-public.savedobjectsbulkupdateobject.md
+++ b/docs/development/core/public/kibana-plugin-public.savedobjectsbulkupdateobject.md
@@ -8,7 +8,7 @@
 <b>Signature:</b>
 
 ```typescript
-export interface SavedObjectsBulkUpdateObject<T extends SavedObjectAttributes = SavedObjectAttributes> 
+export interface SavedObjectsBulkUpdateObject<T = unknown> 
 ```
 
 ## Properties
diff --git a/docs/development/core/public/kibana-plugin-public.savedobjectsclient.bulkcreate.md b/docs/development/core/public/kibana-plugin-public.savedobjectsclient.bulkcreate.md
index 391b2f57205d5..c5072bea5b50e 100644
--- a/docs/development/core/public/kibana-plugin-public.savedobjectsclient.bulkcreate.md
+++ b/docs/development/core/public/kibana-plugin-public.savedobjectsclient.bulkcreate.md
@@ -9,5 +9,5 @@ Creates multiple documents at once
 <b>Signature:</b>
 
 ```typescript
-bulkCreate: (objects?: SavedObjectsBulkCreateObject<SavedObjectAttributes>[], options?: SavedObjectsBulkCreateOptions) => Promise<SavedObjectsBatchResponse<SavedObjectAttributes>>;
+bulkCreate: (objects?: SavedObjectsBulkCreateObject<unknown>[], options?: SavedObjectsBulkCreateOptions) => Promise<SavedObjectsBatchResponse<unknown>>;
 ```
diff --git a/docs/development/core/public/kibana-plugin-public.savedobjectsclient.bulkget.md b/docs/development/core/public/kibana-plugin-public.savedobjectsclient.bulkget.md
index a54dfe72167a7..37b9f6d6c951d 100644
--- a/docs/development/core/public/kibana-plugin-public.savedobjectsclient.bulkget.md
+++ b/docs/development/core/public/kibana-plugin-public.savedobjectsclient.bulkget.md
@@ -12,7 +12,7 @@ Returns an array of objects by id
 bulkGet: (objects?: {
         id: string;
         type: string;
-    }[]) => Promise<SavedObjectsBatchResponse<SavedObjectAttributes>>;
+    }[]) => Promise<SavedObjectsBatchResponse<unknown>>;
 ```
 
 ## Example
diff --git a/docs/development/core/public/kibana-plugin-public.savedobjectsclient.bulkupdate.md b/docs/development/core/public/kibana-plugin-public.savedobjectsclient.bulkupdate.md
index 94ae9bb70ccda..2f8565def3d40 100644
--- a/docs/development/core/public/kibana-plugin-public.savedobjectsclient.bulkupdate.md
+++ b/docs/development/core/public/kibana-plugin-public.savedobjectsclient.bulkupdate.md
@@ -9,7 +9,7 @@ Update multiple documents at once
 <b>Signature:</b>
 
 ```typescript
-bulkUpdate<T extends SavedObjectAttributes>(objects?: SavedObjectsBulkUpdateObject[]): Promise<SavedObjectsBatchResponse<SavedObjectAttributes>>;
+bulkUpdate<T = unknown>(objects?: SavedObjectsBulkUpdateObject[]): Promise<SavedObjectsBatchResponse<unknown>>;
 ```
 
 ## Parameters
@@ -20,7 +20,7 @@ bulkUpdate<T extends SavedObjectAttributes>(objects?: SavedObjectsBulkUpdateObje
 
 <b>Returns:</b>
 
-`Promise<SavedObjectsBatchResponse<SavedObjectAttributes>>`
+`Promise<SavedObjectsBatchResponse<unknown>>`
 
 The result of the update operation containing both failed and updated saved objects.
 
diff --git a/docs/development/core/public/kibana-plugin-public.savedobjectsclient.create.md b/docs/development/core/public/kibana-plugin-public.savedobjectsclient.create.md
index 5a7666084ea0f..ea3fbe31ca8a5 100644
--- a/docs/development/core/public/kibana-plugin-public.savedobjectsclient.create.md
+++ b/docs/development/core/public/kibana-plugin-public.savedobjectsclient.create.md
@@ -9,5 +9,5 @@ Persists an object
 <b>Signature:</b>
 
 ```typescript
-create: <T extends SavedObjectAttributes>(type: string, attributes: T, options?: SavedObjectsCreateOptions) => Promise<SimpleSavedObject<T>>;
+create: <T = unknown>(type: string, attributes: T, options?: SavedObjectsCreateOptions) => Promise<SimpleSavedObject<T>>;
 ```
diff --git a/docs/development/core/public/kibana-plugin-public.savedobjectsclient.find.md b/docs/development/core/public/kibana-plugin-public.savedobjectsclient.find.md
index d3494045952ad..3d8005c9390b7 100644
--- a/docs/development/core/public/kibana-plugin-public.savedobjectsclient.find.md
+++ b/docs/development/core/public/kibana-plugin-public.savedobjectsclient.find.md
@@ -9,5 +9,5 @@ Search for objects
 <b>Signature:</b>
 
 ```typescript
-find: <T extends SavedObjectAttributes>(options: Pick<SavedObjectFindOptionsServer, "search" | "filter" | "type" | "page" | "perPage" | "sortField" | "fields" | "searchFields" | "hasReference" | "defaultSearchOperator">) => Promise<SavedObjectsFindResponsePublic<T>>;
+find: <T = unknown>(options: Pick<SavedObjectFindOptionsServer, "search" | "filter" | "type" | "page" | "perPage" | "sortField" | "fields" | "searchFields" | "hasReference" | "defaultSearchOperator">) => Promise<SavedObjectsFindResponsePublic<T>>;
 ```
diff --git a/docs/development/core/public/kibana-plugin-public.savedobjectsclient.get.md b/docs/development/core/public/kibana-plugin-public.savedobjectsclient.get.md
index bddbadd3e1361..37a91f7211da5 100644
--- a/docs/development/core/public/kibana-plugin-public.savedobjectsclient.get.md
+++ b/docs/development/core/public/kibana-plugin-public.savedobjectsclient.get.md
@@ -9,5 +9,5 @@ Fetches a single object
 <b>Signature:</b>
 
 ```typescript
-get: <T extends SavedObjectAttributes>(type: string, id: string) => Promise<SimpleSavedObject<T>>;
+get: <T = unknown>(type: string, id: string) => Promise<SimpleSavedObject<T>>;
 ```
diff --git a/docs/development/core/public/kibana-plugin-public.savedobjectsclient.md b/docs/development/core/public/kibana-plugin-public.savedobjectsclient.md
index 7aa17eae2da87..5c22a2a0bdd91 100644
--- a/docs/development/core/public/kibana-plugin-public.savedobjectsclient.md
+++ b/docs/development/core/public/kibana-plugin-public.savedobjectsclient.md
@@ -20,12 +20,12 @@ The constructor for this class is marked as internal. Third-party code should no
 
 |  Property | Modifiers | Type | Description |
 |  --- | --- | --- | --- |
-|  [bulkCreate](./kibana-plugin-public.savedobjectsclient.bulkcreate.md) |  | <code>(objects?: SavedObjectsBulkCreateObject&lt;SavedObjectAttributes&gt;[], options?: SavedObjectsBulkCreateOptions) =&gt; Promise&lt;SavedObjectsBatchResponse&lt;SavedObjectAttributes&gt;&gt;</code> | Creates multiple documents at once |
-|  [bulkGet](./kibana-plugin-public.savedobjectsclient.bulkget.md) |  | <code>(objects?: {</code><br/><code>        id: string;</code><br/><code>        type: string;</code><br/><code>    }[]) =&gt; Promise&lt;SavedObjectsBatchResponse&lt;SavedObjectAttributes&gt;&gt;</code> | Returns an array of objects by id |
-|  [create](./kibana-plugin-public.savedobjectsclient.create.md) |  | <code>&lt;T extends SavedObjectAttributes&gt;(type: string, attributes: T, options?: SavedObjectsCreateOptions) =&gt; Promise&lt;SimpleSavedObject&lt;T&gt;&gt;</code> | Persists an object |
+|  [bulkCreate](./kibana-plugin-public.savedobjectsclient.bulkcreate.md) |  | <code>(objects?: SavedObjectsBulkCreateObject&lt;unknown&gt;[], options?: SavedObjectsBulkCreateOptions) =&gt; Promise&lt;SavedObjectsBatchResponse&lt;unknown&gt;&gt;</code> | Creates multiple documents at once |
+|  [bulkGet](./kibana-plugin-public.savedobjectsclient.bulkget.md) |  | <code>(objects?: {</code><br/><code>        id: string;</code><br/><code>        type: string;</code><br/><code>    }[]) =&gt; Promise&lt;SavedObjectsBatchResponse&lt;unknown&gt;&gt;</code> | Returns an array of objects by id |
+|  [create](./kibana-plugin-public.savedobjectsclient.create.md) |  | <code>&lt;T = unknown&gt;(type: string, attributes: T, options?: SavedObjectsCreateOptions) =&gt; Promise&lt;SimpleSavedObject&lt;T&gt;&gt;</code> | Persists an object |
 |  [delete](./kibana-plugin-public.savedobjectsclient.delete.md) |  | <code>(type: string, id: string) =&gt; Promise&lt;{}&gt;</code> | Deletes an object |
-|  [find](./kibana-plugin-public.savedobjectsclient.find.md) |  | <code>&lt;T extends SavedObjectAttributes&gt;(options: Pick&lt;SavedObjectFindOptionsServer, &quot;search&quot; &#124; &quot;filter&quot; &#124; &quot;type&quot; &#124; &quot;page&quot; &#124; &quot;perPage&quot; &#124; &quot;sortField&quot; &#124; &quot;fields&quot; &#124; &quot;searchFields&quot; &#124; &quot;hasReference&quot; &#124; &quot;defaultSearchOperator&quot;&gt;) =&gt; Promise&lt;SavedObjectsFindResponsePublic&lt;T&gt;&gt;</code> | Search for objects |
-|  [get](./kibana-plugin-public.savedobjectsclient.get.md) |  | <code>&lt;T extends SavedObjectAttributes&gt;(type: string, id: string) =&gt; Promise&lt;SimpleSavedObject&lt;T&gt;&gt;</code> | Fetches a single object |
+|  [find](./kibana-plugin-public.savedobjectsclient.find.md) |  | <code>&lt;T = unknown&gt;(options: Pick&lt;SavedObjectFindOptionsServer, &quot;search&quot; &#124; &quot;filter&quot; &#124; &quot;type&quot; &#124; &quot;page&quot; &#124; &quot;perPage&quot; &#124; &quot;sortField&quot; &#124; &quot;fields&quot; &#124; &quot;searchFields&quot; &#124; &quot;hasReference&quot; &#124; &quot;defaultSearchOperator&quot;&gt;) =&gt; Promise&lt;SavedObjectsFindResponsePublic&lt;T&gt;&gt;</code> | Search for objects |
+|  [get](./kibana-plugin-public.savedobjectsclient.get.md) |  | <code>&lt;T = unknown&gt;(type: string, id: string) =&gt; Promise&lt;SimpleSavedObject&lt;T&gt;&gt;</code> | Fetches a single object |
 
 ## Methods
 
diff --git a/docs/development/core/public/kibana-plugin-public.savedobjectsclient.update.md b/docs/development/core/public/kibana-plugin-public.savedobjectsclient.update.md
index 9f7e46943bbd5..d1049a75edf1f 100644
--- a/docs/development/core/public/kibana-plugin-public.savedobjectsclient.update.md
+++ b/docs/development/core/public/kibana-plugin-public.savedobjectsclient.update.md
@@ -9,7 +9,7 @@ Updates an object
 <b>Signature:</b>
 
 ```typescript
-update<T extends SavedObjectAttributes>(type: string, id: string, attributes: T, { version, migrationVersion, references }?: SavedObjectsUpdateOptions): Promise<SimpleSavedObject<T>>;
+update<T = unknown>(type: string, id: string, attributes: T, { version, migrationVersion, references }?: SavedObjectsUpdateOptions): Promise<SimpleSavedObject<T>>;
 ```
 
 ## Parameters
diff --git a/docs/development/core/public/kibana-plugin-public.savedobjectsfindresponsepublic.md b/docs/development/core/public/kibana-plugin-public.savedobjectsfindresponsepublic.md
index f60e7305fba34..31ab73464dfd9 100644
--- a/docs/development/core/public/kibana-plugin-public.savedobjectsfindresponsepublic.md
+++ b/docs/development/core/public/kibana-plugin-public.savedobjectsfindresponsepublic.md
@@ -11,7 +11,7 @@ Return type of the Saved Objects `find()` method.
 <b>Signature:</b>
 
 ```typescript
-export interface SavedObjectsFindResponsePublic<T extends SavedObjectAttributes = SavedObjectAttributes> extends SavedObjectsBatchResponse<T> 
+export interface SavedObjectsFindResponsePublic<T = unknown> extends SavedObjectsBatchResponse<T> 
 ```
 
 ## Properties
diff --git a/docs/development/core/public/kibana-plugin-public.simplesavedobject.md b/docs/development/core/public/kibana-plugin-public.simplesavedobject.md
index 1f6de163ec17d..4906d5967f7df 100644
--- a/docs/development/core/public/kibana-plugin-public.simplesavedobject.md
+++ b/docs/development/core/public/kibana-plugin-public.simplesavedobject.md
@@ -11,7 +11,7 @@ It provides basic functionality for creating/saving/deleting saved objects, but
 <b>Signature:</b>
 
 ```typescript
-export declare class SimpleSavedObject<T extends SavedObjectAttributes> 
+export declare class SimpleSavedObject<T = unknown> 
 ```
 
 ## Constructors
diff --git a/docs/development/core/server/kibana-plugin-server.savedobject.md b/docs/development/core/server/kibana-plugin-server.savedobject.md
index b3184fd38ad93..5ab08f4eadf2e 100644
--- a/docs/development/core/server/kibana-plugin-server.savedobject.md
+++ b/docs/development/core/server/kibana-plugin-server.savedobject.md
@@ -8,7 +8,7 @@
 <b>Signature:</b>
 
 ```typescript
-export interface SavedObject<T extends SavedObjectAttributes = any> 
+export interface SavedObject<T = unknown> 
 ```
 
 ## Properties
diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsbulkcreateobject.md b/docs/development/core/server/kibana-plugin-server.savedobjectsbulkcreateobject.md
index 87386b986009d..7b765055de6c1 100644
--- a/docs/development/core/server/kibana-plugin-server.savedobjectsbulkcreateobject.md
+++ b/docs/development/core/server/kibana-plugin-server.savedobjectsbulkcreateobject.md
@@ -8,7 +8,7 @@
 <b>Signature:</b>
 
 ```typescript
-export interface SavedObjectsBulkCreateObject<T extends SavedObjectAttributes = any> 
+export interface SavedObjectsBulkCreateObject<T = unknown> 
 ```
 
 ## Properties
diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsbulkresponse.md b/docs/development/core/server/kibana-plugin-server.savedobjectsbulkresponse.md
index 20a1194c87eda..2ced4f4c8e1a0 100644
--- a/docs/development/core/server/kibana-plugin-server.savedobjectsbulkresponse.md
+++ b/docs/development/core/server/kibana-plugin-server.savedobjectsbulkresponse.md
@@ -8,7 +8,7 @@
 <b>Signature:</b>
 
 ```typescript
-export interface SavedObjectsBulkResponse<T extends SavedObjectAttributes = any> 
+export interface SavedObjectsBulkResponse<T = unknown> 
 ```
 
 ## Properties
diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsbulkupdateobject.md b/docs/development/core/server/kibana-plugin-server.savedobjectsbulkupdateobject.md
index 8e4e3d761148e..67013290a629d 100644
--- a/docs/development/core/server/kibana-plugin-server.savedobjectsbulkupdateobject.md
+++ b/docs/development/core/server/kibana-plugin-server.savedobjectsbulkupdateobject.md
@@ -8,7 +8,7 @@
 <b>Signature:</b>
 
 ```typescript
-export interface SavedObjectsBulkUpdateObject<T extends SavedObjectAttributes = any> extends Pick<SavedObjectsUpdateOptions, 'version' | 'references'> 
+export interface SavedObjectsBulkUpdateObject<T = unknown> extends Pick<SavedObjectsUpdateOptions, 'version' | 'references'> 
 ```
 
 ## Properties
diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsbulkupdateresponse.md b/docs/development/core/server/kibana-plugin-server.savedobjectsbulkupdateresponse.md
index 065b9df0823cd..3468991611608 100644
--- a/docs/development/core/server/kibana-plugin-server.savedobjectsbulkupdateresponse.md
+++ b/docs/development/core/server/kibana-plugin-server.savedobjectsbulkupdateresponse.md
@@ -8,7 +8,7 @@
 <b>Signature:</b>
 
 ```typescript
-export interface SavedObjectsBulkUpdateResponse<T extends SavedObjectAttributes = any> 
+export interface SavedObjectsBulkUpdateResponse<T = unknown> 
 ```
 
 ## Properties
diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsclient.bulkcreate.md b/docs/development/core/server/kibana-plugin-server.savedobjectsclient.bulkcreate.md
index 40f947188de54..47da795631a3a 100644
--- a/docs/development/core/server/kibana-plugin-server.savedobjectsclient.bulkcreate.md
+++ b/docs/development/core/server/kibana-plugin-server.savedobjectsclient.bulkcreate.md
@@ -9,7 +9,7 @@ Persists multiple documents batched together as a single request
 <b>Signature:</b>
 
 ```typescript
-bulkCreate<T extends SavedObjectAttributes = any>(objects: Array<SavedObjectsBulkCreateObject<T>>, options?: SavedObjectsCreateOptions): Promise<SavedObjectsBulkResponse<T>>;
+bulkCreate<T = unknown>(objects: Array<SavedObjectsBulkCreateObject<T>>, options?: SavedObjectsCreateOptions): Promise<SavedObjectsBulkResponse<T>>;
 ```
 
 ## Parameters
diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsclient.bulkget.md b/docs/development/core/server/kibana-plugin-server.savedobjectsclient.bulkget.md
index c86c30d14db3b..71006e2afa3ee 100644
--- a/docs/development/core/server/kibana-plugin-server.savedobjectsclient.bulkget.md
+++ b/docs/development/core/server/kibana-plugin-server.savedobjectsclient.bulkget.md
@@ -9,7 +9,7 @@ Returns an array of objects by id
 <b>Signature:</b>
 
 ```typescript
-bulkGet<T extends SavedObjectAttributes = any>(objects?: SavedObjectsBulkGetObject[], options?: SavedObjectsBaseOptions): Promise<SavedObjectsBulkResponse<T>>;
+bulkGet<T = unknown>(objects?: SavedObjectsBulkGetObject[], options?: SavedObjectsBaseOptions): Promise<SavedObjectsBulkResponse<T>>;
 ```
 
 ## Parameters
diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsclient.bulkupdate.md b/docs/development/core/server/kibana-plugin-server.savedobjectsclient.bulkupdate.md
index 33958837ebca3..cceeb9c1ca320 100644
--- a/docs/development/core/server/kibana-plugin-server.savedobjectsclient.bulkupdate.md
+++ b/docs/development/core/server/kibana-plugin-server.savedobjectsclient.bulkupdate.md
@@ -9,7 +9,7 @@ Bulk Updates multiple SavedObject at once
 <b>Signature:</b>
 
 ```typescript
-bulkUpdate<T extends SavedObjectAttributes = any>(objects: Array<SavedObjectsBulkUpdateObject<T>>, options?: SavedObjectsBulkUpdateOptions): Promise<SavedObjectsBulkUpdateResponse<T>>;
+bulkUpdate<T = unknown>(objects: Array<SavedObjectsBulkUpdateObject<T>>, options?: SavedObjectsBulkUpdateOptions): Promise<SavedObjectsBulkUpdateResponse<T>>;
 ```
 
 ## Parameters
diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsclient.create.md b/docs/development/core/server/kibana-plugin-server.savedobjectsclient.create.md
index ddb78a57e71bc..7f6fc117937cb 100644
--- a/docs/development/core/server/kibana-plugin-server.savedobjectsclient.create.md
+++ b/docs/development/core/server/kibana-plugin-server.savedobjectsclient.create.md
@@ -9,7 +9,7 @@ Persists a SavedObject
 <b>Signature:</b>
 
 ```typescript
-create<T extends SavedObjectAttributes = any>(type: string, attributes: T, options?: SavedObjectsCreateOptions): Promise<SavedObject<T>>;
+create<T = unknown>(type: string, attributes: T, options?: SavedObjectsCreateOptions): Promise<SavedObject<T>>;
 ```
 
 ## Parameters
diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsclient.find.md b/docs/development/core/server/kibana-plugin-server.savedobjectsclient.find.md
index f72691d3ce0c8..c8804e2a97851 100644
--- a/docs/development/core/server/kibana-plugin-server.savedobjectsclient.find.md
+++ b/docs/development/core/server/kibana-plugin-server.savedobjectsclient.find.md
@@ -9,7 +9,7 @@ Find all SavedObjects matching the search query
 <b>Signature:</b>
 
 ```typescript
-find<T extends SavedObjectAttributes = any>(options: SavedObjectsFindOptions): Promise<SavedObjectsFindResponse<T>>;
+find<T = unknown>(options: SavedObjectsFindOptions): Promise<SavedObjectsFindResponse<T>>;
 ```
 
 ## Parameters
diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsclient.get.md b/docs/development/core/server/kibana-plugin-server.savedobjectsclient.get.md
index 3906462184d4f..b48cef25ca3d1 100644
--- a/docs/development/core/server/kibana-plugin-server.savedobjectsclient.get.md
+++ b/docs/development/core/server/kibana-plugin-server.savedobjectsclient.get.md
@@ -9,7 +9,7 @@ Retrieves a single object
 <b>Signature:</b>
 
 ```typescript
-get<T extends SavedObjectAttributes = any>(type: string, id: string, options?: SavedObjectsBaseOptions): Promise<SavedObject<T>>;
+get<T = unknown>(type: string, id: string, options?: SavedObjectsBaseOptions): Promise<SavedObject<T>>;
 ```
 
 ## Parameters
diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsclient.update.md b/docs/development/core/server/kibana-plugin-server.savedobjectsclient.update.md
index 2c71e518b7b05..ed6243c409fa4 100644
--- a/docs/development/core/server/kibana-plugin-server.savedobjectsclient.update.md
+++ b/docs/development/core/server/kibana-plugin-server.savedobjectsclient.update.md
@@ -9,7 +9,7 @@ Updates an SavedObject
 <b>Signature:</b>
 
 ```typescript
-update<T extends SavedObjectAttributes = any>(type: string, id: string, attributes: Partial<T>, options?: SavedObjectsUpdateOptions): Promise<SavedObjectsUpdateResponse<T>>;
+update<T = unknown>(type: string, id: string, attributes: Partial<T>, options?: SavedObjectsUpdateOptions): Promise<SavedObjectsUpdateResponse<T>>;
 ```
 
 ## Parameters
diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsfindresponse.md b/docs/development/core/server/kibana-plugin-server.savedobjectsfindresponse.md
index efdc07cea88fd..a79a23db967cc 100644
--- a/docs/development/core/server/kibana-plugin-server.savedobjectsfindresponse.md
+++ b/docs/development/core/server/kibana-plugin-server.savedobjectsfindresponse.md
@@ -11,7 +11,7 @@ Return type of the Saved Objects `find()` method.
 <b>Signature:</b>
 
 ```typescript
-export interface SavedObjectsFindResponse<T extends SavedObjectAttributes = any> 
+export interface SavedObjectsFindResponse<T = unknown> 
 ```
 
 ## Properties
diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.bulkcreate.md b/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.bulkcreate.md
index dfe9e51e62483..f0d8d9edfbe79 100644
--- a/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.bulkcreate.md
+++ b/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.bulkcreate.md
@@ -9,7 +9,7 @@ Creates multiple documents at once
 <b>Signature:</b>
 
 ```typescript
-bulkCreate<T extends SavedObjectAttributes = any>(objects: Array<SavedObjectsBulkCreateObject<T>>, options?: SavedObjectsCreateOptions): Promise<SavedObjectsBulkResponse<T>>;
+bulkCreate<T = unknown>(objects: Array<SavedObjectsBulkCreateObject<T>>, options?: SavedObjectsCreateOptions): Promise<SavedObjectsBulkResponse<T>>;
 ```
 
 ## Parameters
diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.bulkget.md b/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.bulkget.md
index 34b113bce5410..e27c5fc3bec9a 100644
--- a/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.bulkget.md
+++ b/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.bulkget.md
@@ -9,7 +9,7 @@ Returns an array of objects by id
 <b>Signature:</b>
 
 ```typescript
-bulkGet<T extends SavedObjectAttributes = any>(objects?: SavedObjectsBulkGetObject[], options?: SavedObjectsBaseOptions): Promise<SavedObjectsBulkResponse<T>>;
+bulkGet<T = unknown>(objects?: SavedObjectsBulkGetObject[], options?: SavedObjectsBaseOptions): Promise<SavedObjectsBulkResponse<T>>;
 ```
 
 ## Parameters
diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.bulkupdate.md b/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.bulkupdate.md
index 23c7a92624957..5ad09d59f4061 100644
--- a/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.bulkupdate.md
+++ b/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.bulkupdate.md
@@ -9,7 +9,7 @@ Updates multiple objects in bulk
 <b>Signature:</b>
 
 ```typescript
-bulkUpdate<T extends SavedObjectAttributes = any>(objects: Array<SavedObjectsBulkUpdateObject<T>>, options?: SavedObjectsBulkUpdateOptions): Promise<SavedObjectsBulkUpdateResponse<T>>;
+bulkUpdate<T = unknown>(objects: Array<SavedObjectsBulkUpdateObject<T>>, options?: SavedObjectsBulkUpdateOptions): Promise<SavedObjectsBulkUpdateResponse<T>>;
 ```
 
 ## Parameters
diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.create.md b/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.create.md
index 29e3c3ab24654..fd6495bd2d3c4 100644
--- a/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.create.md
+++ b/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.create.md
@@ -9,7 +9,7 @@ Persists an object
 <b>Signature:</b>
 
 ```typescript
-create<T extends SavedObjectAttributes>(type: string, attributes: T, options?: SavedObjectsCreateOptions): Promise<SavedObject<T>>;
+create<T = unknown>(type: string, attributes: T, options?: SavedObjectsCreateOptions): Promise<SavedObject<T>>;
 ```
 
 ## Parameters
diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.find.md b/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.find.md
index dbf6d59e78d85..ccb9feca1669e 100644
--- a/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.find.md
+++ b/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.find.md
@@ -7,7 +7,7 @@
 <b>Signature:</b>
 
 ```typescript
-find<T extends SavedObjectAttributes = any>({ search, defaultSearchOperator, searchFields, hasReference, page, perPage, sortField, sortOrder, fields, namespace, type, filter, }: SavedObjectsFindOptions): Promise<SavedObjectsFindResponse<T>>;
+find<T = unknown>({ search, defaultSearchOperator, searchFields, hasReference, page, perPage, sortField, sortOrder, fields, namespace, type, filter, }: SavedObjectsFindOptions): Promise<SavedObjectsFindResponse<T>>;
 ```
 
 ## Parameters
diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.get.md b/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.get.md
index 930a4647ca175..b3ccbc7277b4e 100644
--- a/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.get.md
+++ b/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.get.md
@@ -9,7 +9,7 @@ Gets a single object
 <b>Signature:</b>
 
 ```typescript
-get<T extends SavedObjectAttributes = any>(type: string, id: string, options?: SavedObjectsBaseOptions): Promise<SavedObject<T>>;
+get<T = unknown>(type: string, id: string, options?: SavedObjectsBaseOptions): Promise<SavedObject<T>>;
 ```
 
 ## Parameters
diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.update.md b/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.update.md
index 5e9f69ecc567b..bb215cdb97af5 100644
--- a/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.update.md
+++ b/docs/development/core/server/kibana-plugin-server.savedobjectsrepository.update.md
@@ -9,7 +9,7 @@ Updates an object
 <b>Signature:</b>
 
 ```typescript
-update<T extends SavedObjectAttributes = any>(type: string, id: string, attributes: Partial<T>, options?: SavedObjectsUpdateOptions): Promise<SavedObjectsUpdateResponse<T>>;
+update<T = unknown>(type: string, id: string, attributes: Partial<T>, options?: SavedObjectsUpdateOptions): Promise<SavedObjectsUpdateResponse<T>>;
 ```
 
 ## Parameters
diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsupdateresponse.md b/docs/development/core/server/kibana-plugin-server.savedobjectsupdateresponse.md
index 64c9037735358..130b0b4faaa07 100644
--- a/docs/development/core/server/kibana-plugin-server.savedobjectsupdateresponse.md
+++ b/docs/development/core/server/kibana-plugin-server.savedobjectsupdateresponse.md
@@ -8,7 +8,7 @@
 <b>Signature:</b>
 
 ```typescript
-export interface SavedObjectsUpdateResponse<T extends SavedObjectAttributes = any> extends Omit<SavedObject<T>, 'attributes' | 'references'> 
+export interface SavedObjectsUpdateResponse<T = unknown> extends Omit<SavedObject<T>, 'attributes' | 'references'> 
 ```
 
 ## Properties
diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md
index ca2f6789bebee..ba1988b857385 100644
--- a/src/core/public/public.api.md
+++ b/src/core/public/public.api.md
@@ -932,7 +932,7 @@ export type RecursiveReadonly<T> = T extends (...args: any[]) => any ? T : T ext
 }> : T;
 
 // @public (undocumented)
-export interface SavedObject<T extends SavedObjectAttributes = any> {
+export interface SavedObject<T = unknown> {
     attributes: T;
     // (undocumented)
     error?: {
@@ -975,13 +975,13 @@ export interface SavedObjectsBaseOptions {
 }
 
 // @public (undocumented)
-export interface SavedObjectsBatchResponse<T extends SavedObjectAttributes = SavedObjectAttributes> {
+export interface SavedObjectsBatchResponse<T = unknown> {
     // (undocumented)
     savedObjects: Array<SimpleSavedObject<T>>;
 }
 
 // @public (undocumented)
-export interface SavedObjectsBulkCreateObject<T extends SavedObjectAttributes = SavedObjectAttributes> extends SavedObjectsCreateOptions {
+export interface SavedObjectsBulkCreateObject<T = unknown> extends SavedObjectsCreateOptions {
     // (undocumented)
     attributes: T;
     // (undocumented)
@@ -994,7 +994,7 @@ export interface SavedObjectsBulkCreateOptions {
 }
 
 // @public (undocumented)
-export interface SavedObjectsBulkUpdateObject<T extends SavedObjectAttributes = SavedObjectAttributes> {
+export interface SavedObjectsBulkUpdateObject<T = unknown> {
     // (undocumented)
     attributes: T;
     // (undocumented)
@@ -1017,17 +1017,17 @@ export interface SavedObjectsBulkUpdateOptions {
 export class SavedObjectsClient {
     // @internal
     constructor(http: HttpSetup);
-    bulkCreate: (objects?: SavedObjectsBulkCreateObject<SavedObjectAttributes>[], options?: SavedObjectsBulkCreateOptions) => Promise<SavedObjectsBatchResponse<SavedObjectAttributes>>;
+    bulkCreate: (objects?: SavedObjectsBulkCreateObject<unknown>[], options?: SavedObjectsBulkCreateOptions) => Promise<SavedObjectsBatchResponse<unknown>>;
     bulkGet: (objects?: {
         id: string;
         type: string;
-    }[]) => Promise<SavedObjectsBatchResponse<SavedObjectAttributes>>;
-    bulkUpdate<T extends SavedObjectAttributes>(objects?: SavedObjectsBulkUpdateObject[]): Promise<SavedObjectsBatchResponse<SavedObjectAttributes>>;
-    create: <T extends SavedObjectAttributes>(type: string, attributes: T, options?: SavedObjectsCreateOptions) => Promise<SimpleSavedObject<T>>;
+    }[]) => Promise<SavedObjectsBatchResponse<unknown>>;
+    bulkUpdate<T = unknown>(objects?: SavedObjectsBulkUpdateObject[]): Promise<SavedObjectsBatchResponse<unknown>>;
+    create: <T = unknown>(type: string, attributes: T, options?: SavedObjectsCreateOptions) => Promise<SimpleSavedObject<T>>;
     delete: (type: string, id: string) => Promise<{}>;
-    find: <T extends SavedObjectAttributes>(options: Pick<SavedObjectsFindOptions, "search" | "filter" | "type" | "page" | "perPage" | "sortField" | "fields" | "searchFields" | "hasReference" | "defaultSearchOperator">) => Promise<SavedObjectsFindResponsePublic<T>>;
-    get: <T extends SavedObjectAttributes>(type: string, id: string) => Promise<SimpleSavedObject<T>>;
-    update<T extends SavedObjectAttributes>(type: string, id: string, attributes: T, { version, migrationVersion, references }?: SavedObjectsUpdateOptions): Promise<SimpleSavedObject<T>>;
+    find: <T = unknown>(options: Pick<SavedObjectsFindOptions, "search" | "filter" | "type" | "page" | "perPage" | "sortField" | "fields" | "searchFields" | "hasReference" | "defaultSearchOperator">) => Promise<SavedObjectsFindResponsePublic<T>>;
+    get: <T = unknown>(type: string, id: string) => Promise<SimpleSavedObject<T>>;
+    update<T = unknown>(type: string, id: string, attributes: T, { version, migrationVersion, references }?: SavedObjectsUpdateOptions): Promise<SimpleSavedObject<T>>;
 }
 
 // @public
@@ -1069,7 +1069,7 @@ export interface SavedObjectsFindOptions extends SavedObjectsBaseOptions {
 }
 
 // @public
-export interface SavedObjectsFindResponsePublic<T extends SavedObjectAttributes = SavedObjectAttributes> extends SavedObjectsBatchResponse<T> {
+export interface SavedObjectsFindResponsePublic<T = unknown> extends SavedObjectsBatchResponse<T> {
     // (undocumented)
     page: number;
     // (undocumented)
@@ -1176,7 +1176,7 @@ export interface SavedObjectsUpdateOptions {
 }
 
 // @public
-export class SimpleSavedObject<T extends SavedObjectAttributes> {
+export class SimpleSavedObject<T = unknown> {
     constructor(client: SavedObjectsClientContract, { id, type, version, attributes, error, references, migrationVersion }: SavedObject<T>);
     // (undocumented)
     attributes: T;
diff --git a/src/core/public/saved_objects/saved_objects_client.ts b/src/core/public/saved_objects/saved_objects_client.ts
index ccb23793a8534..afc77806afb91 100644
--- a/src/core/public/saved_objects/saved_objects_client.ts
+++ b/src/core/public/saved_objects/saved_objects_client.ts
@@ -22,7 +22,6 @@ import { resolve as resolveUrl } from 'url';
 
 import {
   SavedObject,
-  SavedObjectAttributes,
   SavedObjectReference,
   SavedObjectsClientContract as SavedObjectsApi,
   SavedObjectsFindOptions as SavedObjectFindOptionsServer,
@@ -61,9 +60,7 @@ export interface SavedObjectsCreateOptions {
  *
  * @public
  */
-export interface SavedObjectsBulkCreateObject<
-  T extends SavedObjectAttributes = SavedObjectAttributes
-> extends SavedObjectsCreateOptions {
+export interface SavedObjectsBulkCreateObject<T = unknown> extends SavedObjectsCreateOptions {
   type: string;
   attributes: T;
 }
@@ -75,9 +72,7 @@ export interface SavedObjectsBulkCreateOptions {
 }
 
 /** @public */
-export interface SavedObjectsBulkUpdateObject<
-  T extends SavedObjectAttributes = SavedObjectAttributes
-> {
+export interface SavedObjectsBulkUpdateObject<T = unknown> {
   type: string;
   id: string;
   attributes: T;
@@ -99,9 +94,7 @@ export interface SavedObjectsUpdateOptions {
 }
 
 /** @public */
-export interface SavedObjectsBatchResponse<
-  T extends SavedObjectAttributes = SavedObjectAttributes
-> {
+export interface SavedObjectsBatchResponse<T = unknown> {
   savedObjects: Array<SimpleSavedObject<T>>;
 }
 
@@ -113,9 +106,7 @@ export interface SavedObjectsBatchResponse<
  *
  * @public
  */
-export interface SavedObjectsFindResponsePublic<
-  T extends SavedObjectAttributes = SavedObjectAttributes
-> extends SavedObjectsBatchResponse<T> {
+export interface SavedObjectsFindResponsePublic<T = unknown> extends SavedObjectsBatchResponse<T> {
   total: number;
   perPage: number;
   page: number;
@@ -124,7 +115,7 @@ export interface SavedObjectsFindResponsePublic<
 interface BatchQueueEntry {
   type: string;
   id: string;
-  resolve: <T extends SavedObjectAttributes>(value: SimpleSavedObject<T> | SavedObject<T>) => void;
+  resolve: <T = unknown>(value: SimpleSavedObject<T> | SavedObject<T>) => void;
   reject: (reason?: any) => void;
 }
 
@@ -207,7 +198,7 @@ export class SavedObjectsClient {
    * @param options
    * @returns
    */
-  public create = <T extends SavedObjectAttributes>(
+  public create = <T = unknown>(
     type: string,
     attributes: T,
     options: SavedObjectsCreateOptions = {}
@@ -300,7 +291,7 @@ export class SavedObjectsClient {
    * @property {object} [options.hasReference] - { type, id }
    * @returns A find result with objects matching the specified search.
    */
-  public find = <T extends SavedObjectAttributes>(
+  public find = <T = unknown>(
     options: SavedObjectsFindOptions
   ): Promise<SavedObjectsFindResponsePublic<T>> => {
     const path = this.getPath(['_find']);
@@ -348,10 +339,7 @@ export class SavedObjectsClient {
    * @param {string} id
    * @returns The saved object for the given type and id.
    */
-  public get = <T extends SavedObjectAttributes>(
-    type: string,
-    id: string
-  ): Promise<SimpleSavedObject<T>> => {
+  public get = <T = unknown>(type: string, id: string): Promise<SimpleSavedObject<T>> => {
     if (!type || !id) {
       return Promise.reject(new Error('requires type and id'));
     }
@@ -402,7 +390,7 @@ export class SavedObjectsClient {
    * @prop {object} options.migrationVersion - The optional migrationVersion of this document
    * @returns
    */
-  public update<T extends SavedObjectAttributes>(
+  public update<T = unknown>(
     type: string,
     id: string,
     attributes: T,
@@ -434,7 +422,7 @@ export class SavedObjectsClient {
    * @param {array} objects - [{ type, id, attributes, options: { version, references } }]
    * @returns The result of the update operation containing both failed and updated saved objects.
    */
-  public bulkUpdate<T extends SavedObjectAttributes>(objects: SavedObjectsBulkUpdateObject[] = []) {
+  public bulkUpdate<T = unknown>(objects: SavedObjectsBulkUpdateObject[] = []) {
     const path = this.getPath(['_bulk_update']);
 
     return this.savedObjectsFetch(path, {
@@ -449,9 +437,7 @@ export class SavedObjectsClient {
     });
   }
 
-  private createSavedObject<T extends SavedObjectAttributes>(
-    options: SavedObject<T>
-  ): SimpleSavedObject<T> {
+  private createSavedObject<T = unknown>(options: SavedObject<T>): SimpleSavedObject<T> {
     return new SimpleSavedObject(this, options);
   }
 
diff --git a/src/core/public/saved_objects/simple_saved_object.ts b/src/core/public/saved_objects/simple_saved_object.ts
index 8e464680bcf17..d3ba506b865a4 100644
--- a/src/core/public/saved_objects/simple_saved_object.ts
+++ b/src/core/public/saved_objects/simple_saved_object.ts
@@ -18,7 +18,7 @@
  */
 
 import { get, has, set } from 'lodash';
-import { SavedObject as SavedObjectType, SavedObjectAttributes } from '../../server';
+import { SavedObject as SavedObjectType } from '../../server';
 import { SavedObjectsClientContract } from './saved_objects_client';
 
 /**
@@ -30,7 +30,7 @@ import { SavedObjectsClientContract } from './saved_objects_client';
  *
  * @public
  */
-export class SimpleSavedObject<T extends SavedObjectAttributes> {
+export class SimpleSavedObject<T = unknown> {
   public attributes: T;
   // We want to use the same interface this class had in JS
   public _version?: SavedObjectType<T>['version'];
@@ -46,7 +46,7 @@ export class SimpleSavedObject<T extends SavedObjectAttributes> {
   ) {
     this.id = id;
     this.type = type;
-    this.attributes = attributes || {};
+    this.attributes = attributes || ({} as T);
     this.references = references || [];
     this._version = version;
     this.migrationVersion = migrationVersion;
diff --git a/src/core/server/saved_objects/import/collect_saved_objects.ts b/src/core/server/saved_objects/import/collect_saved_objects.ts
index 65ffd4d9a1d57..1a8ede41d0b2c 100644
--- a/src/core/server/saved_objects/import/collect_saved_objects.ts
+++ b/src/core/server/saved_objects/import/collect_saved_objects.ts
@@ -42,10 +42,10 @@ export async function collectSavedObjects({
   supportedTypes,
 }: CollectSavedObjectsOptions) {
   const errors: SavedObjectsImportError[] = [];
-  const collectedObjects: SavedObject[] = await createPromiseFromStreams([
+  const collectedObjects: Array<SavedObject<{ title: string }>> = await createPromiseFromStreams([
     readStream,
     createLimitStream(objectLimit),
-    createFilterStream<SavedObject>(obj => {
+    createFilterStream<SavedObject<{ title: string }>>(obj => {
       if (supportedTypes.includes(obj.type)) {
         return true;
       }
diff --git a/src/core/server/saved_objects/import/extract_errors.ts b/src/core/server/saved_objects/import/extract_errors.ts
index 725e935f6e21d..5728ce8b7b59f 100644
--- a/src/core/server/saved_objects/import/extract_errors.ts
+++ b/src/core/server/saved_objects/import/extract_errors.ts
@@ -20,11 +20,12 @@ import { SavedObject } from '../types';
 import { SavedObjectsImportError } from './types';
 
 export function extractErrors(
-  savedObjectResults: SavedObject[],
-  savedObjectsToImport: SavedObject[]
+  // TODO: define saved object type
+  savedObjectResults: Array<SavedObject<any>>,
+  savedObjectsToImport: Array<SavedObject<any>>
 ) {
   const errors: SavedObjectsImportError[] = [];
-  const originalSavedObjectsMap = new Map<string, SavedObject>();
+  const originalSavedObjectsMap = new Map<string, SavedObject<{ title: string }>>();
   for (const savedObject of savedObjectsToImport) {
     originalSavedObjectsMap.set(`${savedObject.type}:${savedObject.id}`, savedObject);
   }
diff --git a/src/core/server/saved_objects/import/validate_references.ts b/src/core/server/saved_objects/import/validate_references.ts
index 4d9ee59f9df15..f0c033c1d00b4 100644
--- a/src/core/server/saved_objects/import/validate_references.ts
+++ b/src/core/server/saved_objects/import/validate_references.ts
@@ -77,7 +77,7 @@ export async function getNonExistingReferenceAsKeys(
 }
 
 export async function validateReferences(
-  savedObjects: SavedObject[],
+  savedObjects: Array<SavedObject<{ title?: string }>>,
   savedObjectsClient: SavedObjectsClientContract,
   namespace?: string
 ) {
diff --git a/src/core/server/saved_objects/management/management.ts b/src/core/server/saved_objects/management/management.ts
index 7b5274da91fc8..b7dce2c087c5f 100644
--- a/src/core/server/saved_objects/management/management.ts
+++ b/src/core/server/saved_objects/management/management.ts
@@ -23,9 +23,9 @@ interface SavedObjectsManagementTypeDefinition {
   isImportableAndExportable?: boolean;
   defaultSearchField?: string;
   icon?: string;
-  getTitle?: (savedObject: SavedObject) => string;
-  getEditUrl?: (savedObject: SavedObject) => string;
-  getInAppUrl?: (savedObject: SavedObject) => { path: string; uiCapabilitiesPath: string };
+  getTitle?: (savedObject: SavedObject<any>) => string;
+  getEditUrl?: (savedObject: SavedObject<any>) => string;
+  getInAppUrl?: (savedObject: SavedObject<any>) => { path: string; uiCapabilitiesPath: string };
 }
 
 export interface SavedObjectsManagementDefinition {
diff --git a/src/core/server/saved_objects/serialization/types.ts b/src/core/server/saved_objects/serialization/types.ts
index aaf6f45c244ec..524c2c8ffae7a 100644
--- a/src/core/server/saved_objects/serialization/types.ts
+++ b/src/core/server/saved_objects/serialization/types.ts
@@ -50,7 +50,7 @@ export interface SavedObjectsRawDocSource {
  * scenario out of the box.
  */
 interface SavedObjectDoc {
-  attributes: object;
+  attributes: unknown;
   id?: string; // NOTE: SavedObjectDoc is used for uncreated objects where `id` is optional
   type: string;
   namespace?: string;
diff --git a/src/core/server/saved_objects/service/lib/repository.ts b/src/core/server/saved_objects/service/lib/repository.ts
index b485b8dfe398c..72a7867854b60 100644
--- a/src/core/server/saved_objects/service/lib/repository.ts
+++ b/src/core/server/saved_objects/service/lib/repository.ts
@@ -48,7 +48,6 @@ import {
 } from '../saved_objects_client';
 import {
   SavedObject,
-  SavedObjectAttributes,
   SavedObjectsBaseOptions,
   SavedObjectsFindOptions,
   SavedObjectsMigrationVersion,
@@ -213,7 +212,7 @@ export class SavedObjectsRepository {
    * @property {array} [options.references=[]] - [{ name, type, id }]
    * @returns {promise} - { id, type, version, attributes }
    */
-  public async create<T extends SavedObjectAttributes>(
+  public async create<T = unknown>(
     type: string,
     attributes: T,
     options: SavedObjectsCreateOptions = {}
@@ -254,7 +253,7 @@ export class SavedObjectsRepository {
         body: raw._source,
       });
 
-      return this._rawToSavedObject({
+      return this._rawToSavedObject<T>({
         ...raw,
         ...response,
       });
@@ -277,7 +276,7 @@ export class SavedObjectsRepository {
    * @property {string} [options.namespace]
    * @returns {promise} -  {saved_objects: [[{ id, type, version, references, attributes, error: { message } }]}
    */
-  async bulkCreate<T extends SavedObjectAttributes = any>(
+  async bulkCreate<T = unknown>(
     objects: Array<SavedObjectsBulkCreateObject<T>>,
     options: SavedObjectsCreateOptions = {}
   ): Promise<SavedObjectsBulkResponse<T>> {
@@ -464,7 +463,7 @@ export class SavedObjectsRepository {
    * @property {object} [options.hasReference] - { type, id }
    * @returns {promise} - { saved_objects: [{ id, type, version, attributes }], total, per_page, page }
    */
-  async find<T extends SavedObjectAttributes = any>({
+  async find<T = unknown>({
     search,
     defaultSearchOperator = 'OR',
     searchFields,
@@ -577,7 +576,7 @@ export class SavedObjectsRepository {
    *   { id: 'foo', type: 'index-pattern' }
    * ])
    */
-  async bulkGet<T extends SavedObjectAttributes = any>(
+  async bulkGet<T = unknown>(
     objects: SavedObjectsBulkGetObject[] = [],
     options: SavedObjectsBaseOptions = {}
   ): Promise<SavedObjectsBulkResponse<T>> {
@@ -648,7 +647,7 @@ export class SavedObjectsRepository {
    * @property {string} [options.namespace]
    * @returns {promise} - { id, type, version, attributes }
    */
-  async get<T extends SavedObjectAttributes = any>(
+  async get<T = unknown>(
     type: string,
     id: string,
     options: SavedObjectsBaseOptions = {}
@@ -696,7 +695,7 @@ export class SavedObjectsRepository {
    * @property {array} [options.references] - [{ name, type, id }]
    * @returns {promise}
    */
-  async update<T extends SavedObjectAttributes = any>(
+  async update<T = unknown>(
     type: string,
     id: string,
     attributes: Partial<T>,
@@ -753,7 +752,7 @@ export class SavedObjectsRepository {
    * @property {string} [options.namespace]
    * @returns {promise} -  {saved_objects: [[{ id, type, version, references, attributes, error: { message } }]}
    */
-  async bulkUpdate<T extends SavedObjectAttributes = any>(
+  async bulkUpdate<T = unknown>(
     objects: Array<SavedObjectsBulkUpdateObject<T>>,
     options: SavedObjectsBulkUpdateOptions = {}
   ): Promise<SavedObjectsBulkUpdateResponse<T>> {
@@ -972,7 +971,7 @@ export class SavedObjectsRepository {
   // includes the namespace, and we use this for migrating documents. However, we don't
   // want the namespace to be returned from the repository, as the repository scopes each
   // method transparently to the specified namespace.
-  private _rawToSavedObject(raw: SavedObjectsRawDoc): SavedObject {
+  private _rawToSavedObject<T = unknown>(raw: SavedObjectsRawDoc): SavedObject<T> {
     const savedObject = this._serializer.rawToSavedObject(raw);
     return omit(savedObject, 'namespace');
   }
diff --git a/src/core/server/saved_objects/service/saved_objects_client.ts b/src/core/server/saved_objects/service/saved_objects_client.ts
index b0b2633646e10..70d69374ba8fe 100644
--- a/src/core/server/saved_objects/service/saved_objects_client.ts
+++ b/src/core/server/saved_objects/service/saved_objects_client.ts
@@ -20,7 +20,6 @@
 import { ISavedObjectsRepository } from './lib';
 import {
   SavedObject,
-  SavedObjectAttributes,
   SavedObjectReference,
   SavedObjectsMigrationVersion,
   SavedObjectsBaseOptions,
@@ -49,7 +48,7 @@ export interface SavedObjectsCreateOptions extends SavedObjectsBaseOptions {
  *
  * @public
  */
-export interface SavedObjectsBulkCreateObject<T extends SavedObjectAttributes = any> {
+export interface SavedObjectsBulkCreateObject<T = unknown> {
   id?: string;
   type: string;
   attributes: T;
@@ -62,7 +61,7 @@ export interface SavedObjectsBulkCreateObject<T extends SavedObjectAttributes =
  *
  * @public
  */
-export interface SavedObjectsBulkUpdateObject<T extends SavedObjectAttributes = any>
+export interface SavedObjectsBulkUpdateObject<T = unknown>
   extends Pick<SavedObjectsUpdateOptions, 'version' | 'references'> {
   /** The ID of this Saved Object, guaranteed to be unique for all objects of the same `type` */
   id: string;
@@ -76,7 +75,7 @@ export interface SavedObjectsBulkUpdateObject<T extends SavedObjectAttributes =
  *
  * @public
  */
-export interface SavedObjectsBulkResponse<T extends SavedObjectAttributes = any> {
+export interface SavedObjectsBulkResponse<T = unknown> {
   saved_objects: Array<SavedObject<T>>;
 }
 
@@ -88,7 +87,7 @@ export interface SavedObjectsBulkResponse<T extends SavedObjectAttributes = any>
  *
  * @public
  */
-export interface SavedObjectsFindResponse<T extends SavedObjectAttributes = any> {
+export interface SavedObjectsFindResponse<T = unknown> {
   saved_objects: Array<SavedObject<T>>;
   total: number;
   per_page: number;
@@ -141,7 +140,7 @@ export interface SavedObjectsBulkGetObject {
  *
  * @public
  */
-export interface SavedObjectsBulkResponse<T extends SavedObjectAttributes = any> {
+export interface SavedObjectsBulkResponse<T = unknown> {
   saved_objects: Array<SavedObject<T>>;
 }
 
@@ -149,7 +148,7 @@ export interface SavedObjectsBulkResponse<T extends SavedObjectAttributes = any>
  *
  * @public
  */
-export interface SavedObjectsBulkUpdateResponse<T extends SavedObjectAttributes = any> {
+export interface SavedObjectsBulkUpdateResponse<T = unknown> {
   saved_objects: Array<SavedObjectsUpdateResponse<T>>;
 }
 
@@ -157,7 +156,7 @@ export interface SavedObjectsBulkUpdateResponse<T extends SavedObjectAttributes
  *
  * @public
  */
-export interface SavedObjectsUpdateResponse<T extends SavedObjectAttributes = any>
+export interface SavedObjectsUpdateResponse<T = unknown>
   extends Omit<SavedObject<T>, 'attributes' | 'references'> {
   attributes: Partial<T>;
   references: SavedObjectReference[] | undefined;
@@ -185,11 +184,7 @@ export class SavedObjectsClient {
    * @param attributes
    * @param options
    */
-  async create<T extends SavedObjectAttributes = any>(
-    type: string,
-    attributes: T,
-    options?: SavedObjectsCreateOptions
-  ) {
+  async create<T = unknown>(type: string, attributes: T, options?: SavedObjectsCreateOptions) {
     return await this._repository.create(type, attributes, options);
   }
 
@@ -199,7 +194,7 @@ export class SavedObjectsClient {
    * @param objects
    * @param options
    */
-  async bulkCreate<T extends SavedObjectAttributes = any>(
+  async bulkCreate<T = unknown>(
     objects: Array<SavedObjectsBulkCreateObject<T>>,
     options?: SavedObjectsCreateOptions
   ) {
@@ -222,9 +217,7 @@ export class SavedObjectsClient {
    *
    * @param options
    */
-  async find<T extends SavedObjectAttributes = any>(
-    options: SavedObjectsFindOptions
-  ): Promise<SavedObjectsFindResponse<T>> {
+  async find<T = unknown>(options: SavedObjectsFindOptions): Promise<SavedObjectsFindResponse<T>> {
     return await this._repository.find(options);
   }
 
@@ -239,7 +232,7 @@ export class SavedObjectsClient {
    *   { id: 'foo', type: 'index-pattern' }
    * ])
    */
-  async bulkGet<T extends SavedObjectAttributes = any>(
+  async bulkGet<T = unknown>(
     objects: SavedObjectsBulkGetObject[] = [],
     options: SavedObjectsBaseOptions = {}
   ): Promise<SavedObjectsBulkResponse<T>> {
@@ -253,7 +246,7 @@ export class SavedObjectsClient {
    * @param id - The ID of the SavedObject to retrieve
    * @param options
    */
-  async get<T extends SavedObjectAttributes = any>(
+  async get<T = unknown>(
     type: string,
     id: string,
     options: SavedObjectsBaseOptions = {}
@@ -268,7 +261,7 @@ export class SavedObjectsClient {
    * @param id
    * @param options
    */
-  async update<T extends SavedObjectAttributes = any>(
+  async update<T = unknown>(
     type: string,
     id: string,
     attributes: Partial<T>,
@@ -282,7 +275,7 @@ export class SavedObjectsClient {
    *
    * @param objects
    */
-  async bulkUpdate<T extends SavedObjectAttributes = any>(
+  async bulkUpdate<T = unknown>(
     objects: Array<SavedObjectsBulkUpdateObject<T>>,
     options?: SavedObjectsBulkUpdateOptions
   ): Promise<SavedObjectsBulkUpdateResponse<T>> {
diff --git a/src/core/server/saved_objects/types.ts b/src/core/server/saved_objects/types.ts
index a4fde1765b7d3..9c204784b0aeb 100644
--- a/src/core/server/saved_objects/types.ts
+++ b/src/core/server/saved_objects/types.ts
@@ -33,7 +33,6 @@ export {
   SavedObjectsImportRetry,
 } from './import/types';
 
-import { SavedObjectAttributes } from '../../types';
 import { LegacyConfig } from '../legacy';
 export {
   SavedObjectAttributes,
@@ -64,7 +63,7 @@ export interface SavedObjectsMigrationVersion {
  *
  * @public
  */
-export interface SavedObject<T extends SavedObjectAttributes = any> {
+export interface SavedObject<T = unknown> {
   /** The ID of this Saved Object, guaranteed to be unique for all objects of the same `type` */
   id: string;
   /**  The type of Saved Object. Each plugin can define it's own custom Saved Object types. */
diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md
index 053a60028fc5f..f717f30fdb0cf 100644
--- a/src/core/server/server.api.md
+++ b/src/core/server/server.api.md
@@ -1462,7 +1462,7 @@ export interface RouteValidatorOptions {
 }
 
 // @public (undocumented)
-export interface SavedObject<T extends SavedObjectAttributes = any> {
+export interface SavedObject<T = unknown> {
     attributes: T;
     // (undocumented)
     error?: {
@@ -1524,7 +1524,7 @@ export interface SavedObjectsBaseOptions {
 }
 
 // @public (undocumented)
-export interface SavedObjectsBulkCreateObject<T extends SavedObjectAttributes = any> {
+export interface SavedObjectsBulkCreateObject<T = unknown> {
     // (undocumented)
     attributes: T;
     // (undocumented)
@@ -1546,19 +1546,19 @@ export interface SavedObjectsBulkGetObject {
 }
 
 // @public (undocumented)
-export interface SavedObjectsBulkResponse<T extends SavedObjectAttributes = any> {
+export interface SavedObjectsBulkResponse<T = unknown> {
     // (undocumented)
     saved_objects: Array<SavedObject<T>>;
 }
 
 // @public (undocumented)
-export interface SavedObjectsBulkResponse<T extends SavedObjectAttributes = any> {
+export interface SavedObjectsBulkResponse<T = unknown> {
     // (undocumented)
     saved_objects: Array<SavedObject<T>>;
 }
 
 // @public (undocumented)
-export interface SavedObjectsBulkUpdateObject<T extends SavedObjectAttributes = any> extends Pick<SavedObjectsUpdateOptions, 'version' | 'references'> {
+export interface SavedObjectsBulkUpdateObject<T = unknown> extends Pick<SavedObjectsUpdateOptions, 'version' | 'references'> {
     attributes: Partial<T>;
     id: string;
     type: string;
@@ -1570,7 +1570,7 @@ export interface SavedObjectsBulkUpdateOptions extends SavedObjectsBaseOptions {
 }
 
 // @public (undocumented)
-export interface SavedObjectsBulkUpdateResponse<T extends SavedObjectAttributes = any> {
+export interface SavedObjectsBulkUpdateResponse<T = unknown> {
     // (undocumented)
     saved_objects: Array<SavedObjectsUpdateResponse<T>>;
 }
@@ -1579,18 +1579,18 @@ export interface SavedObjectsBulkUpdateResponse<T extends SavedObjectAttributes
 export class SavedObjectsClient {
     // @internal
     constructor(repository: ISavedObjectsRepository);
-    bulkCreate<T extends SavedObjectAttributes = any>(objects: Array<SavedObjectsBulkCreateObject<T>>, options?: SavedObjectsCreateOptions): Promise<SavedObjectsBulkResponse<T>>;
-    bulkGet<T extends SavedObjectAttributes = any>(objects?: SavedObjectsBulkGetObject[], options?: SavedObjectsBaseOptions): Promise<SavedObjectsBulkResponse<T>>;
-    bulkUpdate<T extends SavedObjectAttributes = any>(objects: Array<SavedObjectsBulkUpdateObject<T>>, options?: SavedObjectsBulkUpdateOptions): Promise<SavedObjectsBulkUpdateResponse<T>>;
-    create<T extends SavedObjectAttributes = any>(type: string, attributes: T, options?: SavedObjectsCreateOptions): Promise<SavedObject<T>>;
+    bulkCreate<T = unknown>(objects: Array<SavedObjectsBulkCreateObject<T>>, options?: SavedObjectsCreateOptions): Promise<SavedObjectsBulkResponse<T>>;
+    bulkGet<T = unknown>(objects?: SavedObjectsBulkGetObject[], options?: SavedObjectsBaseOptions): Promise<SavedObjectsBulkResponse<T>>;
+    bulkUpdate<T = unknown>(objects: Array<SavedObjectsBulkUpdateObject<T>>, options?: SavedObjectsBulkUpdateOptions): Promise<SavedObjectsBulkUpdateResponse<T>>;
+    create<T = unknown>(type: string, attributes: T, options?: SavedObjectsCreateOptions): Promise<SavedObject<T>>;
     delete(type: string, id: string, options?: SavedObjectsDeleteOptions): Promise<{}>;
     // (undocumented)
     static errors: typeof SavedObjectsErrorHelpers;
     // (undocumented)
     errors: typeof SavedObjectsErrorHelpers;
-    find<T extends SavedObjectAttributes = any>(options: SavedObjectsFindOptions): Promise<SavedObjectsFindResponse<T>>;
-    get<T extends SavedObjectAttributes = any>(type: string, id: string, options?: SavedObjectsBaseOptions): Promise<SavedObject<T>>;
-    update<T extends SavedObjectAttributes = any>(type: string, id: string, attributes: Partial<T>, options?: SavedObjectsUpdateOptions): Promise<SavedObjectsUpdateResponse<T>>;
+    find<T = unknown>(options: SavedObjectsFindOptions): Promise<SavedObjectsFindResponse<T>>;
+    get<T = unknown>(type: string, id: string, options?: SavedObjectsBaseOptions): Promise<SavedObject<T>>;
+    update<T = unknown>(type: string, id: string, attributes: Partial<T>, options?: SavedObjectsUpdateOptions): Promise<SavedObjectsUpdateResponse<T>>;
 }
 
 // @public
@@ -1772,7 +1772,7 @@ export interface SavedObjectsFindOptions extends SavedObjectsBaseOptions {
 }
 
 // @public
-export interface SavedObjectsFindResponse<T extends SavedObjectAttributes = any> {
+export interface SavedObjectsFindResponse<T = unknown> {
     // (undocumented)
     page: number;
     // (undocumented)
@@ -1951,10 +1951,10 @@ export interface SavedObjectsRawDoc {
 
 // @public (undocumented)
 export class SavedObjectsRepository {
-    bulkCreate<T extends SavedObjectAttributes = any>(objects: Array<SavedObjectsBulkCreateObject<T>>, options?: SavedObjectsCreateOptions): Promise<SavedObjectsBulkResponse<T>>;
-    bulkGet<T extends SavedObjectAttributes = any>(objects?: SavedObjectsBulkGetObject[], options?: SavedObjectsBaseOptions): Promise<SavedObjectsBulkResponse<T>>;
-    bulkUpdate<T extends SavedObjectAttributes = any>(objects: Array<SavedObjectsBulkUpdateObject<T>>, options?: SavedObjectsBulkUpdateOptions): Promise<SavedObjectsBulkUpdateResponse<T>>;
-    create<T extends SavedObjectAttributes>(type: string, attributes: T, options?: SavedObjectsCreateOptions): Promise<SavedObject<T>>;
+    bulkCreate<T = unknown>(objects: Array<SavedObjectsBulkCreateObject<T>>, options?: SavedObjectsCreateOptions): Promise<SavedObjectsBulkResponse<T>>;
+    bulkGet<T = unknown>(objects?: SavedObjectsBulkGetObject[], options?: SavedObjectsBaseOptions): Promise<SavedObjectsBulkResponse<T>>;
+    bulkUpdate<T = unknown>(objects: Array<SavedObjectsBulkUpdateObject<T>>, options?: SavedObjectsBulkUpdateOptions): Promise<SavedObjectsBulkUpdateResponse<T>>;
+    create<T = unknown>(type: string, attributes: T, options?: SavedObjectsCreateOptions): Promise<SavedObject<T>>;
     // Warning: (ae-forgotten-export) The symbol "KibanaMigrator" needs to be exported by the entry point index.d.ts
     //
     // @internal
@@ -1962,8 +1962,8 @@ export class SavedObjectsRepository {
     delete(type: string, id: string, options?: SavedObjectsDeleteOptions): Promise<{}>;
     deleteByNamespace(namespace: string, options?: SavedObjectsDeleteByNamespaceOptions): Promise<any>;
     // (undocumented)
-    find<T extends SavedObjectAttributes = any>({ search, defaultSearchOperator, searchFields, hasReference, page, perPage, sortField, sortOrder, fields, namespace, type, filter, }: SavedObjectsFindOptions): Promise<SavedObjectsFindResponse<T>>;
-    get<T extends SavedObjectAttributes = any>(type: string, id: string, options?: SavedObjectsBaseOptions): Promise<SavedObject<T>>;
+    find<T = unknown>({ search, defaultSearchOperator, searchFields, hasReference, page, perPage, sortField, sortOrder, fields, namespace, type, filter, }: SavedObjectsFindOptions): Promise<SavedObjectsFindResponse<T>>;
+    get<T = unknown>(type: string, id: string, options?: SavedObjectsBaseOptions): Promise<SavedObject<T>>;
     incrementCounter(type: string, id: string, counterFieldName: string, options?: SavedObjectsIncrementCounterOptions): Promise<{
         id: string;
         type: string;
@@ -1972,7 +1972,7 @@ export class SavedObjectsRepository {
         version: string;
         attributes: any;
     }>;
-    update<T extends SavedObjectAttributes = any>(type: string, id: string, attributes: Partial<T>, options?: SavedObjectsUpdateOptions): Promise<SavedObjectsUpdateResponse<T>>;
+    update<T = unknown>(type: string, id: string, attributes: Partial<T>, options?: SavedObjectsUpdateOptions): Promise<SavedObjectsUpdateResponse<T>>;
     }
 
 // @public
@@ -2062,7 +2062,7 @@ export interface SavedObjectsUpdateOptions extends SavedObjectsBaseOptions {
 }
 
 // @public (undocumented)
-export interface SavedObjectsUpdateResponse<T extends SavedObjectAttributes = any> extends Omit<SavedObject<T>, 'attributes' | 'references'> {
+export interface SavedObjectsUpdateResponse<T = unknown> extends Omit<SavedObject<T>, 'attributes' | 'references'> {
     // (undocumented)
     attributes: Partial<T>;
     // (undocumented)
diff --git a/src/core/server/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.ts b/src/core/server/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.ts
index 0544a1806e09a..55e32b1e3bb37 100644
--- a/src/core/server/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.ts
+++ b/src/core/server/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.ts
@@ -45,7 +45,10 @@ export async function createOrUpgradeSavedConfig(
   });
 
   // default to the attributes of the upgradeableConfig if available
-  const attributes = defaults({ buildNum }, upgradeableConfig ? upgradeableConfig.attributes : {});
+  const attributes = defaults(
+    { buildNum },
+    upgradeableConfig ? (upgradeableConfig.attributes as any) : {}
+  );
 
   try {
     // create the new SavedConfig
diff --git a/src/core/server/ui_settings/ui_settings_client.ts b/src/core/server/ui_settings/ui_settings_client.ts
index 3c9c232bff280..a7e55d2b2da65 100644
--- a/src/core/server/ui_settings/ui_settings_client.ts
+++ b/src/core/server/ui_settings/ui_settings_client.ts
@@ -185,7 +185,7 @@ export class UiSettingsClient implements IUiSettingsClient {
     autoCreateOrUpgradeIfMissing = true,
   }: ReadOptions = {}): Promise<Record<string, any>> {
     try {
-      const resp = await this.savedObjectsClient.get(this.type, this.id);
+      const resp = await this.savedObjectsClient.get<Record<string, any>>(this.type, this.id);
       return resp.attributes;
     } catch (error) {
       if (SavedObjectsErrorHelpers.isNotFoundError(error) && autoCreateOrUpgradeIfMissing) {
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_index_pattern.test.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_index_pattern.test.tsx
index 96b8cc383888e..b6fd5ee60b8e2 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_index_pattern.test.tsx
+++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_index_pattern.test.tsx
@@ -31,14 +31,14 @@ const indexPattern1 = {
   attributes: {
     title: 'test1 title',
   },
-} as SavedObject;
+} as SavedObject<any>;
 
 const indexPattern2 = {
   id: 'test2',
   attributes: {
     title: 'test2 title',
   },
-} as SavedObject;
+} as SavedObject<any>;
 
 const defaultProps = {
   indexPatternList: [indexPattern1, indexPattern2],
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_index_pattern.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_index_pattern.tsx
index a4e8ee2ca3d8a..cca523ee2c1bd 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_index_pattern.tsx
+++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/discover_index_pattern.tsx
@@ -18,6 +18,7 @@
  */
 import React, { useState } from 'react';
 import { SavedObject } from 'kibana/server';
+import { IndexPatternAttributes } from 'src/plugins/data/public';
 import { I18nProvider } from '@kbn/i18n/react';
 
 import { IndexPatternRef } from './types';
@@ -26,11 +27,11 @@ export interface DiscoverIndexPatternProps {
   /**
    * list of available index patterns, if length > 1, component offers a "change" link
    */
-  indexPatternList: SavedObject[];
+  indexPatternList: Array<SavedObject<IndexPatternAttributes>>;
   /**
    * currently selected index pattern, due to angular issues it's undefined at first rendering
    */
-  selectedIndexPattern: SavedObject;
+  selectedIndexPattern: SavedObject<IndexPatternAttributes>;
   /**
    * triggered when user selects a new index pattern
    */
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx
index 7cfc7c4dbc81c..bbb6bf26e5b31 100644
--- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx
+++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx
@@ -24,6 +24,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
 import {
   indexPatterns,
   DataPublicPluginStart,
+  IndexPatternAttributes,
 } from '../../../../../../../../../../plugins/data/public';
 import { SavedObjectsClient, IUiSettingsClient } from '../../../../../../../../../../core/public';
 import { MAX_SEARCH_SIZE } from '../../constants';
@@ -96,7 +97,7 @@ export class StepIndexPattern extends Component<StepIndexPatternProps, StepIndex
   }
 
   fetchExistingIndexPatterns = async () => {
-    const { savedObjects } = await this.props.savedObjectsClient.find({
+    const { savedObjects } = await this.props.savedObjectsClient.find<IndexPatternAttributes>({
       type: 'index-pattern',
       fields: ['title'],
       perPage: 10000,
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/helpers/arg_value_suggestions.ts b/src/legacy/core_plugins/vis_type_timelion/public/helpers/arg_value_suggestions.ts
index 95e01f9c8db5b..ea9532964d6fe 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/helpers/arg_value_suggestions.ts
+++ b/src/legacy/core_plugins/vis_type_timelion/public/helpers/arg_value_suggestions.ts
@@ -20,7 +20,10 @@
 import { get } from 'lodash';
 import { getIndexPatterns, getSavedObjectsClient } from './plugin_services';
 import { TimelionFunctionArgs } from '../../../../../plugins/timelion/common/types';
-import { indexPatterns as indexPatternsUtils } from '../../../../../plugins/data/public';
+import {
+  indexPatterns as indexPatternsUtils,
+  IndexPatternAttributes,
+} from '../../../../../plugins/data/public';
 
 export interface Location {
   min: number;
@@ -53,7 +56,7 @@ export function getArgValueSuggestions() {
     }
     const indexPatternTitle = get(indexPatternArg, 'value.text');
 
-    const { savedObjects } = await savedObjectsClient.find({
+    const { savedObjects } = await savedObjectsClient.find<IndexPatternAttributes>({
       type: 'index-pattern',
       fields: ['title'],
       search: `"${indexPatternTitle}"`,
@@ -84,7 +87,7 @@ export function getArgValueSuggestions() {
     es: {
       async index(partial: string) {
         const search = partial ? `${partial}*` : '*';
-        const resp = await savedObjectsClient.find({
+        const resp = await savedObjectsClient.find<IndexPatternAttributes>({
           type: 'index-pattern',
           fields: ['title', 'type'],
           search: `${search}`,
diff --git a/src/legacy/core_plugins/visualizations/public/embeddable/get_index_pattern.ts b/src/legacy/core_plugins/visualizations/public/embeddable/get_index_pattern.ts
index 25aa77ec73579..cfb2960cfbb7c 100644
--- a/src/legacy/core_plugins/visualizations/public/embeddable/get_index_pattern.ts
+++ b/src/legacy/core_plugins/visualizations/public/embeddable/get_index_pattern.ts
@@ -18,7 +18,11 @@
  */
 
 import { VisSavedObject } from './visualize_embeddable';
-import { indexPatterns, IIndexPattern } from '../../../../../plugins/data/public';
+import {
+  indexPatterns,
+  IIndexPattern,
+  IndexPatternAttributes,
+} from '../../../../../plugins/data/public';
 import { getUISettings, getSavedObjects } from '../np_ready/public/services';
 
 export async function getIndexPattern(
@@ -32,7 +36,7 @@ export async function getIndexPattern(
   const defaultIndex = getUISettings().get('defaultIndex');
 
   if (savedVis.vis.params.index_pattern) {
-    const indexPatternObjects = await savedObjectsClient.find({
+    const indexPatternObjects = await savedObjectsClient.find<IndexPatternAttributes>({
       type: 'index-pattern',
       fields: ['title', 'fields'],
       search: `"${savedVis.vis.params.index_pattern}"`,
@@ -42,6 +46,9 @@ export async function getIndexPattern(
     return indexPattern;
   }
 
-  const savedObject = await savedObjectsClient.get('index-pattern', defaultIndex);
+  const savedObject = await savedObjectsClient.get<IndexPatternAttributes>(
+    'index-pattern',
+    defaultIndex
+  );
   return indexPatterns.getFromSavedObject(savedObject);
 }
diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts
index 98cdd20ea4b84..698edbf9cd6a8 100644
--- a/src/plugins/data/common/index_patterns/types.ts
+++ b/src/plugins/data/common/index_patterns/types.ts
@@ -34,3 +34,15 @@ export interface IIndexPattern {
     }
   >;
 }
+
+/**
+ * Use data plugin interface instead
+ * @deprecated
+ */
+export interface IndexPatternAttributes {
+  type: string;
+  fields: string;
+  title: string;
+  typeMeta: string;
+  timeFieldName?: string;
+}
diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts
index cbd4bfd348797..978f140eb1d26 100644
--- a/src/plugins/data/public/index.ts
+++ b/src/plugins/data/public/index.ts
@@ -263,6 +263,7 @@ export {
   IFieldSubType,
   ES_FIELD_TYPES,
   KBN_FIELD_TYPES,
+  IndexPatternAttributes,
 } from '../common';
 
 /*
diff --git a/src/plugins/data/public/index_patterns/index_patterns/index_patterns.ts b/src/plugins/data/public/index_patterns/index_patterns/index_patterns.ts
index 5f95b101302ef..acce5ed57683c 100644
--- a/src/plugins/data/public/index_patterns/index_patterns/index_patterns.ts
+++ b/src/plugins/data/public/index_patterns/index_patterns/index_patterns.ts
@@ -50,7 +50,7 @@ export class IndexPatternsService {
 
   private async refreshSavedObjectsCache() {
     this.savedObjectsCache = (
-      await this.savedObjectsClient.find({
+      await this.savedObjectsClient.find<Record<string, any>>({
         type: 'index-pattern',
         fields: ['title'],
         perPage: 10000,
diff --git a/src/plugins/data/public/index_patterns/lib/get_from_saved_object.ts b/src/plugins/data/public/index_patterns/lib/get_from_saved_object.ts
index 60b2023f25609..1630a4547b7a1 100644
--- a/src/plugins/data/public/index_patterns/lib/get_from_saved_object.ts
+++ b/src/plugins/data/public/index_patterns/lib/get_from_saved_object.ts
@@ -17,17 +17,20 @@
  * under the License.
  */
 
+import { SavedObject } from 'src/core/public';
 import { get } from 'lodash';
-import { IIndexPattern } from '../..';
+import { IIndexPattern, IndexPatternAttributes } from '../..';
 
-export function getFromSavedObject(savedObject: any): IIndexPattern | undefined {
+export function getFromSavedObject(
+  savedObject: SavedObject<IndexPatternAttributes>
+): IIndexPattern | undefined {
   if (get(savedObject, 'attributes.fields') === undefined) {
     return;
   }
 
   return {
     id: savedObject.id,
-    fields: JSON.parse(savedObject.attributes.fields),
+    fields: JSON.parse(savedObject.attributes.fields!),
     title: savedObject.attributes.title,
   };
 }
diff --git a/src/plugins/data/public/ui/query_string_input/fetch_index_patterns.ts b/src/plugins/data/public/ui/query_string_input/fetch_index_patterns.ts
index 6bef11e4fc46c..1e01d2452ce04 100644
--- a/src/plugins/data/public/ui/query_string_input/fetch_index_patterns.ts
+++ b/src/plugins/data/public/ui/query_string_input/fetch_index_patterns.ts
@@ -18,7 +18,7 @@
  */
 import { isEmpty } from 'lodash';
 import { IUiSettingsClient, SavedObjectsClientContract } from 'src/core/public';
-import { indexPatterns } from '../..';
+import { indexPatterns, IndexPatternAttributes } from '../..';
 
 export async function fetchIndexPatterns(
   savedObjectsClient: SavedObjectsClientContract,
@@ -30,7 +30,7 @@ export async function fetchIndexPatterns(
   }
 
   const searchString = indexPatternStrings.map(string => `"${string}"`).join(' | ');
-  const indexPatternsFromSavedObjects = await savedObjectsClient.find({
+  const indexPatternsFromSavedObjects = await savedObjectsClient.find<IndexPatternAttributes>({
     type: 'index-pattern',
     fields: ['title', 'fields'],
     search: searchString,
@@ -38,7 +38,7 @@ export async function fetchIndexPatterns(
   });
 
   const exactMatches = indexPatternsFromSavedObjects.savedObjects.filter(savedObject => {
-    return indexPatternStrings.includes(savedObject.attributes.title as string);
+    return indexPatternStrings.includes(savedObject.attributes.title);
   });
 
   const defaultIndex = uiSettings.get('defaultIndex');
@@ -46,7 +46,10 @@ export async function fetchIndexPatterns(
   const allMatches =
     exactMatches.length === indexPatternStrings.length
       ? exactMatches
-      : [...exactMatches, await savedObjectsClient.get('index-pattern', defaultIndex)];
+      : [
+          ...exactMatches,
+          await savedObjectsClient.get<IndexPatternAttributes>('index-pattern', defaultIndex),
+        ];
 
   return allMatches.map(indexPatterns.getFromSavedObject);
 }
diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts
index 40d367138b60d..020c3c4c1192d 100644
--- a/src/plugins/data/server/index.ts
+++ b/src/plugins/data/server/index.ts
@@ -142,6 +142,7 @@ export {
   IFieldSubType,
   ES_FIELD_TYPES,
   KBN_FIELD_TYPES,
+  IndexPatternAttributes,
 } from '../common';
 
 /**
diff --git a/src/plugins/home/server/services/sample_data/lib/sample_dataset_registry_types.ts b/src/plugins/home/server/services/sample_data/lib/sample_dataset_registry_types.ts
index 29cf2289fc5b3..bfe99730039d2 100644
--- a/src/plugins/home/server/services/sample_data/lib/sample_dataset_registry_types.ts
+++ b/src/plugins/home/server/services/sample_data/lib/sample_dataset_registry_types.ts
@@ -67,7 +67,7 @@ export interface AppLinkSchema {
   label: string;
 }
 
-export interface SampleDatasetSchema {
+export interface SampleDatasetSchema<T = unknown> {
   id: string;
   name: string;
   description: string;
@@ -83,7 +83,7 @@ export interface SampleDatasetSchema {
 
   // Kibana saved objects (index patter, visualizations, dashboard, ...)
   // Should provide a nice demo of Kibana's functionality with the sample data set
-  savedObjects: SavedObject[];
+  savedObjects: Array<SavedObject<T>>;
   dataIndices: DataIndexSchema[];
   status?: string | undefined;
   statusMsg?: unknown;
diff --git a/src/plugins/home/server/services/sample_data/sample_data_registry.ts b/src/plugins/home/server/services/sample_data/sample_data_registry.ts
index aac680211e52e..b6d04c5c0b6a3 100644
--- a/src/plugins/home/server/services/sample_data/sample_data_registry.ts
+++ b/src/plugins/home/server/services/sample_data/sample_data_registry.ts
@@ -137,9 +137,9 @@ export class SampleDataRegistry {
           throw new Error(`Unable to find sample dataset with id: ${sampleDataId}`);
         }
 
-        const dashboard = sampleDataset.savedObjects.find((savedObject: SavedObject) => {
+        const dashboard = sampleDataset.savedObjects.find(savedObject => {
           return savedObject.id === dashboardId && savedObject.type === 'dashboard';
-        });
+        }) as SavedObject<{ panelsJSON: string }>;
         if (!dashboard) {
           throw new Error(`Unable to find dashboard with id: ${dashboardId}`);
         }
diff --git a/src/plugins/saved_objects/public/finder/saved_object_finder.tsx b/src/plugins/saved_objects/public/finder/saved_object_finder.tsx
index b503392c9827f..81600e9f68634 100644
--- a/src/plugins/saved_objects/public/finder/saved_object_finder.tsx
+++ b/src/plugins/saved_objects/public/finder/saved_object_finder.tsx
@@ -43,14 +43,13 @@ import { Direction } from '@elastic/eui/src/services/sort/sort_direction';
 import { i18n } from '@kbn/i18n';
 
 import {
-  SavedObjectAttributes,
   SimpleSavedObject,
   CoreStart,
   IUiSettingsClient,
   SavedObjectsStart,
-} from '../../../../core/public';
+} from 'src/core/public';
 
-export interface SavedObjectMetaData<T extends SavedObjectAttributes> {
+export interface SavedObjectMetaData<T = unknown> {
   type: string;
   name: string;
   getIconForSavedObject(savedObject: SimpleSavedObject<T>): IconType;
@@ -59,12 +58,17 @@ export interface SavedObjectMetaData<T extends SavedObjectAttributes> {
   includeFields?: string[];
 }
 
+interface FinderAttributes {
+  title?: string;
+  type: string;
+}
+
 interface SavedObjectFinderState {
   items: Array<{
     title: string | null;
-    id: SimpleSavedObject<SavedObjectAttributes>['id'];
-    type: SimpleSavedObject<SavedObjectAttributes>['type'];
-    savedObject: SimpleSavedObject<SavedObjectAttributes>;
+    id: SimpleSavedObject['id'];
+    type: SimpleSavedObject['type'];
+    savedObject: SimpleSavedObject<FinderAttributes>;
   }>;
   query: string;
   isFetchingItems: boolean;
@@ -78,13 +82,13 @@ interface SavedObjectFinderState {
 
 interface BaseSavedObjectFinder {
   onChoose?: (
-    id: SimpleSavedObject<SavedObjectAttributes>['id'],
-    type: SimpleSavedObject<SavedObjectAttributes>['type'],
+    id: SimpleSavedObject['id'],
+    type: SimpleSavedObject['type'],
     name: string,
-    savedObject: SimpleSavedObject<SavedObjectAttributes>
+    savedObject: SimpleSavedObject
   ) => void;
   noItemsMessage?: React.ReactNode;
-  savedObjectMetaData: Array<SavedObjectMetaData<SavedObjectAttributes>>;
+  savedObjectMetaData: Array<SavedObjectMetaData<FinderAttributes>>;
   showFilter?: boolean;
 }
 
@@ -128,7 +132,7 @@ class SavedObjectFinderUi extends React.Component<
       .reduce((allFields, currentFields) => allFields.concat(currentFields), ['title']);
 
     const perPage = this.props.uiSettings.get('savedObjects:listingLimit');
-    const resp = await this.props.savedObjects.client.find({
+    const resp = await this.props.savedObjects.client.find<FinderAttributes>({
       type: Object.keys(metaDataMap),
       fields: [...new Set(fields)],
       search: query ? `${query}*` : undefined,
@@ -163,6 +167,7 @@ class SavedObjectFinderUi extends React.Component<
             id,
             type,
           } = savedObject;
+
           return {
             title: typeof title === 'string' ? title : '',
             id,
@@ -208,7 +213,7 @@ class SavedObjectFinderUi extends React.Component<
     );
   }
 
-  private getSavedObjectMetaDataMap(): Record<string, SavedObjectMetaData<SavedObjectAttributes>> {
+  private getSavedObjectMetaDataMap(): Record<string, SavedObjectMetaData> {
     return this.props.savedObjectMetaData.reduce(
       (map, metaData) => ({ ...map, [metaData.type]: metaData }),
       {}
@@ -470,7 +475,7 @@ class SavedObjectFinderUi extends React.Component<
                 currentSavedObjectMetaData ||
                 ({
                   getIconForSavedObject: () => 'document',
-                } as Pick<SavedObjectMetaData<SavedObjectAttributes>, 'getIconForSavedObject'>)
+                } as Pick<SavedObjectMetaData<{ title: string }>, 'getIconForSavedObject'>)
               ).getIconForSavedObject(item.savedObject);
               return (
                 <EuiListGroupItem
diff --git a/src/plugins/saved_objects/public/saved_object/saved_object_loader.ts b/src/plugins/saved_objects/public/saved_object/saved_object_loader.ts
index 36a169d20e51c..178edadaff6c4 100644
--- a/src/plugins/saved_objects/public/saved_object/saved_object_loader.ts
+++ b/src/plugins/saved_objects/public/saved_object/saved_object_loader.ts
@@ -124,7 +124,7 @@ export class SavedObjectLoader {
    */
   findAll(search: string = '', size: number = 100, fields?: string[]) {
     return this.savedObjectsClient
-      .find({
+      .find<Record<string, unknown>>({
         type: this.lowercaseType,
         search: search ? `${search}*` : undefined,
         perPage: size,
diff --git a/src/plugins/share/server/routes/lib/short_url_lookup.test.ts b/src/plugins/share/server/routes/lib/short_url_lookup.test.ts
index 87e2b7b726e59..2b33489eb27c9 100644
--- a/src/plugins/share/server/routes/lib/short_url_lookup.test.ts
+++ b/src/plugins/share/server/routes/lib/short_url_lookup.test.ts
@@ -17,9 +17,10 @@
  * under the License.
  */
 
-import { shortUrlLookupProvider, ShortUrlLookupService } from './short_url_lookup';
-import { SavedObjectsClientContract, Logger } from 'kibana/server';
-import { SavedObjectsClient } from '../../../../../core/server';
+import { shortUrlLookupProvider, ShortUrlLookupService, UrlAttributes } from './short_url_lookup';
+import { SavedObjectsClientContract, SavedObject } from 'kibana/server';
+
+import { savedObjectsClientMock, loggingServiceMock } from '../../../../../core/server/mocks';
 
 describe('shortUrlLookupProvider', () => {
   const ID = 'bf00ad16941fc51420f91a93428b27a0';
@@ -31,15 +32,10 @@ describe('shortUrlLookupProvider', () => {
   let shortUrl: ShortUrlLookupService;
 
   beforeEach(() => {
-    savedObjects = ({
-      get: jest.fn(),
-      create: jest.fn(() => Promise.resolve({ id: ID })),
-      update: jest.fn(),
-      errors: SavedObjectsClient.errors,
-    } as unknown) as jest.Mocked<SavedObjectsClientContract>;
-
+    savedObjects = savedObjectsClientMock.create();
+    savedObjects.create.mockResolvedValue({ id: ID } as SavedObject<UrlAttributes>);
     deps = { savedObjects };
-    shortUrl = shortUrlLookupProvider({ logger: ({ warn: () => {} } as unknown) as Logger });
+    shortUrl = shortUrlLookupProvider({ logger: loggingServiceMock.create().get() });
   });
 
   describe('generateUrlId', () => {
@@ -55,13 +51,13 @@ describe('shortUrlLookupProvider', () => {
       const [type, attributes, options] = savedObjects.create.mock.calls[0];
 
       expect(type).toEqual(TYPE);
-      expect(Object.keys(attributes).sort()).toEqual([
+      expect(Object.keys(attributes as UrlAttributes).sort()).toEqual([
         'accessCount',
         'accessDate',
         'createDate',
         'url',
       ]);
-      expect(attributes.url).toEqual(URL);
+      expect((attributes as UrlAttributes).url).toEqual(URL);
       expect(options!.id).toEqual(ID);
     });
 
@@ -72,13 +68,13 @@ describe('shortUrlLookupProvider', () => {
       const [type, attributes] = savedObjects.create.mock.calls[0];
 
       expect(type).toEqual(TYPE);
-      expect(Object.keys(attributes).sort()).toEqual([
+      expect(Object.keys(attributes as UrlAttributes).sort()).toEqual([
         'accessCount',
         'accessDate',
         'createDate',
         'url',
       ]);
-      expect(attributes.url).toEqual(URL);
+      expect((attributes as UrlAttributes).url).toEqual(URL);
     });
 
     it('gracefully handles version conflict', async () => {
@@ -119,7 +115,7 @@ describe('shortUrlLookupProvider', () => {
       expect(type).toEqual(TYPE);
       expect(id).toEqual(ID);
       expect(Object.keys(attributes).sort()).toEqual(['accessCount', 'accessDate']);
-      expect(attributes.accessCount).toEqual(3);
+      expect((attributes as UrlAttributes).accessCount).toEqual(3);
     });
   });
 });
diff --git a/src/plugins/share/server/routes/lib/short_url_lookup.ts b/src/plugins/share/server/routes/lib/short_url_lookup.ts
index 0d8a9c86621de..65fa33a940fac 100644
--- a/src/plugins/share/server/routes/lib/short_url_lookup.ts
+++ b/src/plugins/share/server/routes/lib/short_url_lookup.ts
@@ -27,13 +27,20 @@ export interface ShortUrlLookupService {
   getUrl(url: string, deps: { savedObjects: SavedObjectsClientContract }): Promise<string>;
 }
 
+export interface UrlAttributes {
+  url: string;
+  accessCount: number;
+  createDate: number;
+  accessDate: number;
+}
+
 export function shortUrlLookupProvider({ logger }: { logger: Logger }): ShortUrlLookupService {
   async function updateMetadata(
-    doc: SavedObject,
+    doc: SavedObject<UrlAttributes>,
     { savedObjects }: { savedObjects: SavedObjectsClientContract }
   ) {
     try {
-      await savedObjects.update('url', doc.id, {
+      await savedObjects.update<UrlAttributes>('url', doc.id, {
         accessDate: new Date().valueOf(),
         accessCount: get(doc, 'attributes.accessCount', 0) + 1,
       });
@@ -53,7 +60,7 @@ export function shortUrlLookupProvider({ logger }: { logger: Logger }): ShortUrl
       const { isConflictError } = savedObjects.errors;
 
       try {
-        const doc = await savedObjects.create(
+        const doc = await savedObjects.create<UrlAttributes>(
           'url',
           {
             url,
@@ -75,7 +82,7 @@ export function shortUrlLookupProvider({ logger }: { logger: Logger }): ShortUrl
     },
 
     async getUrl(id, { savedObjects }) {
-      const doc = await savedObjects.get('url', id);
+      const doc = await savedObjects.get<UrlAttributes>('url', id);
       updateMetadata(doc, { savedObjects });
 
       return doc.attributes.url;
diff --git a/x-pack/legacy/plugins/canvas/public/lib/es_service.ts b/x-pack/legacy/plugins/canvas/public/lib/es_service.ts
index 2fa1bf94ad669..32f4fe041423c 100644
--- a/x-pack/legacy/plugins/canvas/public/lib/es_service.ts
+++ b/x-pack/legacy/plugins/canvas/public/lib/es_service.ts
@@ -4,6 +4,8 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+import { IndexPatternAttributes } from 'src/plugins/data/public';
+
 import { API_ROUTE } from '../../common/lib/constants';
 // @ts-ignore untyped local
 import { fetch } from '../../common/lib/fetch';
@@ -44,7 +46,7 @@ export const getFields = (index = '_all') => {
 
 export const getIndices = () =>
   getSavedObjectsClient()
-    .find({
+    .find<IndexPatternAttributes>({
       type: 'index-pattern',
       fields: ['title'],
       searchFields: ['title'],
@@ -62,7 +64,7 @@ export const getDefaultIndex = () => {
 
   return defaultIndexId
     ? getSavedObjectsClient()
-        .get('index-pattern', defaultIndexId)
+        .get<IndexPatternAttributes>('index-pattern', defaultIndexId)
         .then(defaultIndex => defaultIndex.attributes.title)
         .catch(err => notify.error(err, { title: strings.getDefaultIndexFetchErrorMessage() }))
     : Promise.resolve('');
diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace_loader.ts b/x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace_loader.ts
index 184fbc01eb96f..d9bb119006e78 100644
--- a/x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace_loader.ts
+++ b/x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace_loader.ts
@@ -52,7 +52,7 @@ export function createSavedWorkspacesLoader(
     },
     find: (searchString: string, size: number = 100) => {
       return savedObjectsClient
-        .find({
+        .find<Record<string, unknown>>({
           type: SavedWorkspace.type,
           search: searchString ? `${searchString}*` : undefined,
           perPage: size,
diff --git a/x-pack/legacy/plugins/ml/common/types/kibana.ts b/x-pack/legacy/plugins/ml/common/types/kibana.ts
index d647bd882162b..4a2edfebd1bac 100644
--- a/x-pack/legacy/plugins/ml/common/types/kibana.ts
+++ b/x-pack/legacy/plugins/ml/common/types/kibana.ts
@@ -6,7 +6,8 @@
 
 // custom edits or fixes for default kibana types which are incomplete
 
-import { SavedObjectAttributes, SimpleSavedObject } from 'kibana/public';
+import { SimpleSavedObject } from 'kibana/public';
+import { IndexPatternAttributes } from 'src/plugins/data/common';
 
 export type IndexPatternTitle = string;
 
@@ -17,8 +18,9 @@ export interface Route {
   k7Breadcrumbs: () => any;
 }
 
-export type IndexPatternSavedObject = SimpleSavedObject<SavedObjectAttributes>;
-export type SavedSearchSavedObject = SimpleSavedObject<SavedObjectAttributes>;
+export type IndexPatternSavedObject = SimpleSavedObject<IndexPatternAttributes>;
+// TODO define saved object type
+export type SavedSearchSavedObject = SimpleSavedObject<any>;
 
 export function isSavedSearchSavedObject(
   ss: SavedSearchSavedObject | null
diff --git a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/recognize/resolvers.ts b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/recognize/resolvers.ts
index fa0ed34dca622..9c60b84b16bdf 100644
--- a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/recognize/resolvers.ts
+++ b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/recognize/resolvers.ts
@@ -61,7 +61,7 @@ export const checkForSavedObjects = async (objects: KibanaObjects): Promise<Kiba
   try {
     return await Object.keys(objects).reduce(async (prevPromise, type) => {
       const acc = await prevPromise;
-      const { savedObjects } = await savedObjectsClient.find({
+      const { savedObjects } = await savedObjectsClient.find<any>({
         type,
         perPage: 1000,
       });
diff --git a/x-pack/legacy/plugins/ml/public/application/util/index_utils.ts b/x-pack/legacy/plugins/ml/public/application/util/index_utils.ts
index 88b56b2329ae6..220d707ddd665 100644
--- a/x-pack/legacy/plugins/ml/public/application/util/index_utils.ts
+++ b/x-pack/legacy/plugins/ml/public/application/util/index_utils.ts
@@ -5,11 +5,12 @@
  */
 
 import { i18n } from '@kbn/i18n';
-import { Query } from 'src/plugins/data/public';
 import {
   IndexPattern,
   IIndexPattern,
   IndexPatternsContract,
+  Query,
+  IndexPatternAttributes,
 } from '../../../../../../../src/plugins/data/public';
 import { getToastNotifications, getSavedObjectsClient } from './dependency_cache';
 import { IndexPatternSavedObject, SavedSearchSavedObject } from '../../../common/types/kibana';
@@ -22,7 +23,7 @@ export function loadIndexPatterns(indexPatterns: IndexPatternsContract) {
   indexPatternsContract = indexPatterns;
   const savedObjectsClient = getSavedObjectsClient();
   return savedObjectsClient
-    .find({
+    .find<IndexPatternAttributes>({
       type: 'index-pattern',
       fields: ['id', 'title', 'type', 'fields'],
       perPage: 10000,
diff --git a/x-pack/legacy/plugins/ml/server/models/data_recognizer/data_recognizer.ts b/x-pack/legacy/plugins/ml/server/models/data_recognizer/data_recognizer.ts
index b62e44c299a2d..553de75e38e05 100644
--- a/x-pack/legacy/plugins/ml/server/models/data_recognizer/data_recognizer.ts
+++ b/x-pack/legacy/plugins/ml/server/models/data_recognizer/data_recognizer.ts
@@ -8,6 +8,7 @@ import fs from 'fs';
 import Boom from 'boom';
 import numeral from '@elastic/numeral';
 import { CallAPIOptions, RequestHandlerContext, SavedObjectsClientContract } from 'kibana/server';
+import { IndexPatternAttributes } from 'src/plugins/data/server';
 import { merge } from 'lodash';
 import { MlJob } from '../../../common/types/jobs';
 import {
@@ -532,7 +533,10 @@ export class DataRecognizer {
   }
 
   async loadIndexPatterns() {
-    return await this.savedObjectsClient.find({ type: 'index-pattern', perPage: 1000 });
+    return await this.savedObjectsClient.find<IndexPatternAttributes>({
+      type: 'index-pattern',
+      perPage: 1000,
+    });
   }
 
   // returns a id based on an index pattern name
@@ -620,7 +624,8 @@ export class DataRecognizer {
 
   // find all existing savedObjects for a given type
   loadExistingSavedObjects(type: string) {
-    return this.savedObjectsClient.find({ type, perPage: 1000 });
+    // TODO: define saved object type
+    return this.savedObjectsClient.find<any>({ type, perPage: 1000 });
   }
 
   // save the savedObjects if they do not exist already
diff --git a/x-pack/legacy/plugins/ml/server/models/job_service/new_job_caps/rollup.ts b/x-pack/legacy/plugins/ml/server/models/job_service/new_job_caps/rollup.ts
index 11b0802192e1f..1e9ce3d8d5022 100644
--- a/x-pack/legacy/plugins/ml/server/models/job_service/new_job_caps/rollup.ts
+++ b/x-pack/legacy/plugins/ml/server/models/job_service/new_job_caps/rollup.ts
@@ -5,6 +5,7 @@
  */
 
 import { SavedObject } from 'src/core/server';
+import { IndexPatternAttributes } from 'src/plugins/data/server';
 import { SavedObjectsClientContract } from 'kibana/server';
 import { FieldId } from '../../../../common/types/fields';
 import { ES_AGGREGATION } from '../../../../common/constants/aggregation_types';
@@ -58,8 +59,8 @@ export async function rollupServiceProvider(
 async function loadRollupIndexPattern(
   indexPattern: string,
   savedObjectsClient: SavedObjectsClientContract
-): Promise<SavedObject | null> {
-  const resp = await savedObjectsClient.find({
+): Promise<SavedObject<IndexPatternAttributes> | null> {
+  const resp = await savedObjectsClient.find<IndexPatternAttributes>({
     type: 'index-pattern',
     fields: ['title', 'type', 'typeMeta'],
     perPage: 1000,
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.ts
index bcf091544e52a..d1f41efdddd14 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_filter.ts
@@ -51,6 +51,15 @@ interface GetFilterArgs {
   index: string[] | undefined | null;
 }
 
+interface QueryAttributes {
+  // NOTE: doesn't match Query interface
+  query: {
+    query: string;
+    language: string;
+  };
+  filters: PartialFilter[];
+}
+
 export const getFilter = async ({
   filters,
   index,
@@ -72,7 +81,10 @@ export const getFilter = async ({
       if (savedId != null && index != null) {
         try {
           // try to get the saved object first
-          const savedObject = await services.savedObjectsClient.get('query', savedId);
+          const savedObject = await services.savedObjectsClient.get<QueryAttributes>(
+            'query',
+            savedId
+          );
           return getQueryFilter(
             savedObject.attributes.query.query,
             savedObject.attributes.query.language,
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.ts
index 29d4d2182bd53..c93990e25b52b 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_input_output_index.ts
@@ -16,7 +16,9 @@ export const getInputIndex = async (
   if (inputIndex != null) {
     return inputIndex;
   } else {
-    const configuration = await services.savedObjectsClient.get('config', version);
+    const configuration = await services.savedObjectsClient.get<{
+      'siem:defaultIndex': string[];
+    }>('config', version);
     if (configuration.attributes != null && configuration.attributes[DEFAULT_INDEX_KEY] != null) {
       return configuration.attributes[DEFAULT_INDEX_KEY];
     } else {
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts
index 79337aa91b1fe..b9ac852701268 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts
@@ -22,7 +22,17 @@ import { SignalRuleAlertTypeDefinition } from './types';
 import { getGapBetweenRuns } from './utils';
 import { ruleStatusSavedObjectType } from '../rules/saved_object_mappings';
 import { IRuleSavedAttributesSavedObjectAttributes } from '../rules/types';
-
+interface AlertAttributes {
+  enabled: boolean;
+  name: string;
+  tags: string[];
+  createdBy: string;
+  createdAt: string;
+  updatedBy: string;
+  schedule: {
+    interval: string;
+  };
+}
 export const signalRulesAlertType = ({
   logger,
   version,
@@ -82,7 +92,7 @@ export const signalRulesAlertType = ({
         type,
       } = params;
       // TODO: Remove this hard extraction of name once this is fixed: https://github.com/elastic/kibana/issues/50522
-      const savedObject = await services.savedObjectsClient.get('alert', alertId);
+      const savedObject = await services.savedObjectsClient.get<AlertAttributes>('alert', alertId);
       const ruleStatusSavedObjects = await services.savedObjectsClient.find<
         IRuleSavedAttributesSavedObjectAttributes
       >({
@@ -123,15 +133,15 @@ export const signalRulesAlertType = ({
         );
       }
 
-      const name: string = savedObject.attributes.name;
-      const tags: string[] = savedObject.attributes.tags;
+      const name = savedObject.attributes.name;
+      const tags = savedObject.attributes.tags;
 
-      const createdBy: string = savedObject.attributes.createdBy;
-      const createdAt: string = savedObject.attributes.createdAt;
-      const updatedBy: string = savedObject.attributes.updatedBy;
-      const updatedAt: string = savedObject.updated_at ?? '';
-      const interval: string = savedObject.attributes.schedule.interval;
-      const enabled: boolean = savedObject.attributes.enabled;
+      const createdBy = savedObject.attributes.createdBy;
+      const createdAt = savedObject.attributes.createdAt;
+      const updatedBy = savedObject.attributes.updatedBy;
+      const updatedAt = savedObject.updated_at ?? '';
+      const interval = savedObject.attributes.schedule.interval;
+      const enabled = savedObject.attributes.enabled;
       const gap = getGapBetweenRuns({
         previousStartedAt: previousStartedAt != null ? moment(previousStartedAt) : null, // TODO: Remove this once previousStartedAt is no longer a string
         interval,
diff --git a/x-pack/legacy/plugins/task_manager/server/migrations.ts b/x-pack/legacy/plugins/task_manager/server/migrations.ts
index 7dce763ddf309..97c4f97f59c58 100644
--- a/x-pack/legacy/plugins/task_manager/server/migrations.ts
+++ b/x-pack/legacy/plugins/task_manager/server/migrations.ts
@@ -7,7 +7,7 @@ import { SavedObject } from '../../../../../src/core/server';
 
 export const migrations = {
   task: {
-    '7.4.0': (doc: SavedObject) => ({
+    '7.4.0': (doc: SavedObject<Record<any, any>>) => ({
       ...doc,
       updated_at: new Date().toISOString(),
     }),
@@ -18,7 +18,7 @@ export const migrations = {
 function moveIntervalIntoSchedule({
   attributes: { interval, ...attributes },
   ...doc
-}: SavedObject) {
+}: SavedObject<Record<any, any>>) {
   return {
     ...doc,
     attributes: {
diff --git a/x-pack/legacy/plugins/transform/public/app/lib/kibana/common.ts b/x-pack/legacy/plugins/transform/public/app/lib/kibana/common.ts
index aba61766b5d2b..aa4cd21281e22 100644
--- a/x-pack/legacy/plugins/transform/public/app/lib/kibana/common.ts
+++ b/x-pack/legacy/plugins/transform/public/app/lib/kibana/common.ts
@@ -9,6 +9,7 @@ import {
   IndexPattern,
   esQuery,
   IndexPatternsContract,
+  IndexPatternAttributes,
 } from '../../../../../../../../src/plugins/data/public';
 
 import { matchAllQuery } from '../../common';
@@ -29,7 +30,7 @@ export function loadIndexPatterns(
 ) {
   fullIndexPatterns = indexPatterns;
   return savedObjectsClient
-    .find({
+    .find<IndexPatternAttributes>({
       type: 'index-pattern',
       fields: ['id', 'title', 'type', 'fields'],
       perPage: 10000,
diff --git a/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/telemetry/usage_collector.ts b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/telemetry/usage_collector.ts
index 99c0441063ce6..1d24d190fa9f2 100644
--- a/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/telemetry/usage_collector.ts
+++ b/x-pack/legacy/plugins/upgrade_assistant/server/np_ready/lib/telemetry/usage_collector.ts
@@ -24,7 +24,12 @@ async function getSavedObjectAttributesFromRepo(
   docID: string
 ) {
   try {
-    return (await savedObjectsRepository.get(docType, docID)).attributes;
+    return (
+      await savedObjectsRepository.get<UpgradeAssistantTelemetrySavedObjectAttributes>(
+        docType,
+        docID
+      )
+    ).attributes;
   } catch (e) {
     return null;
   }
diff --git a/x-pack/plugins/actions/server/actions_client.ts b/x-pack/plugins/actions/server/actions_client.ts
index 7ac8ea24b5218..a06048953b62c 100644
--- a/x-pack/plugins/actions/server/actions_client.ts
+++ b/x-pack/plugins/actions/server/actions_client.ts
@@ -10,7 +10,7 @@ import {
   SavedObjectsClientContract,
   SavedObjectAttributes,
   SavedObject,
-} from '../../../../src/core/server';
+} from 'src/core/server';
 
 import { ActionTypeRegistry } from './action_type_registry';
 import { validateConfig, validateSecrets } from './lib';
@@ -118,7 +118,7 @@ export class ActionsClient {
    * Update action
    */
   public async update({ id, action }: UpdateOptions): Promise<ActionResult> {
-    const existingObject = await this.savedObjectsClient.get('action', id);
+    const existingObject = await this.savedObjectsClient.get<RawAction>('action', id);
     const { actionTypeId } = existingObject.attributes;
     const { name, config, secrets } = action;
     const actionType = this.actionTypeRegistry.get(actionTypeId);
@@ -144,13 +144,13 @@ export class ActionsClient {
    * Get an action
    */
   public async get({ id }: { id: string }): Promise<ActionResult> {
-    const result = await this.savedObjectsClient.get('action', id);
+    const result = await this.savedObjectsClient.get<RawAction>('action', id);
 
     return {
       id,
-      actionTypeId: result.attributes.actionTypeId as string,
-      name: result.attributes.name as string,
-      config: result.attributes.config as Record<string, any>,
+      actionTypeId: result.attributes.actionTypeId,
+      name: result.attributes.name,
+      config: result.attributes.config,
     };
   }
 
diff --git a/x-pack/plugins/actions/server/create_execute_function.ts b/x-pack/plugins/actions/server/create_execute_function.ts
index c2e8795b5f133..5316e833f33d9 100644
--- a/x-pack/plugins/actions/server/create_execute_function.ts
+++ b/x-pack/plugins/actions/server/create_execute_function.ts
@@ -6,7 +6,7 @@
 
 import { SavedObjectsClientContract } from '../../../../src/core/server';
 import { TaskManagerStartContract } from '../../task_manager/server';
-import { GetBasePathFunction } from './types';
+import { GetBasePathFunction, RawAction } from './types';
 
 interface CreateExecuteFunctionOptions {
   taskManager: TaskManagerStartContract;
@@ -59,7 +59,7 @@ export function createExecuteFunction({
     };
 
     const savedObjectsClient = getScopedSavedObjectsClient(fakeRequest);
-    const actionSavedObject = await savedObjectsClient.get('action', id);
+    const actionSavedObject = await savedObjectsClient.get<RawAction>('action', id);
     const actionTaskParamsRecord = await savedObjectsClient.create('action_task_params', {
       actionId: id,
       params,
diff --git a/x-pack/plugins/alerting/server/alerts_client.ts b/x-pack/plugins/alerting/server/alerts_client.ts
index ad308f819da34..9a56781aa1d7d 100644
--- a/x-pack/plugins/alerting/server/alerts_client.ts
+++ b/x-pack/plugins/alerting/server/alerts_client.ts
@@ -203,7 +203,7 @@ export class AlertsClient {
   }
 
   public async get({ id }: { id: string }): Promise<SanitizedAlert> {
-    const result = await this.savedObjectsClient.get('alert', id);
+    const result = await this.savedObjectsClient.get<RawAlert>('alert', id);
     return this.getAlertFromRaw(result.id, result.attributes, result.updated_at, result.references);
   }
 
diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts
index 34278ee62dbc4..c2a3fbcf38069 100644
--- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts
+++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts
@@ -5,8 +5,7 @@
  */
 
 import { pick, mapValues, omit } from 'lodash';
-import { Logger } from '../../../../../src/core/server';
-import { SavedObject } from '../../../../../src/core/server';
+import { Logger, SavedObject } from '../../../../../src/core/server';
 import { TaskRunnerContext } from './task_runner_factory';
 import { ConcreteTaskInstance } from '../../../../plugins/task_manager/server';
 import { createExecutionHandler } from './create_execution_handler';
diff --git a/x-pack/plugins/canvas/server/routes/workpad/update.ts b/x-pack/plugins/canvas/server/routes/workpad/update.ts
index 0d4809a9ee168..83b8fef48e9be 100644
--- a/x-pack/plugins/canvas/server/routes/workpad/update.ts
+++ b/x-pack/plugins/canvas/server/routes/workpad/update.ts
@@ -6,8 +6,7 @@
 
 import { schema, TypeOf } from '@kbn/config-schema';
 import { omit } from 'lodash';
-import { KibanaResponseFactory } from 'src/core/server';
-import { SavedObjectsClientContract } from 'src/core/server';
+import { KibanaResponseFactory, SavedObjectsClientContract } from 'src/core/server';
 import { RouteInitializerDeps } from '../';
 import {
   CANVAS_TYPE,
diff --git a/x-pack/plugins/case/server/services/tags/read_tags.ts b/x-pack/plugins/case/server/services/tags/read_tags.ts
index 58ab99b164cfb..da5905fe4ea35 100644
--- a/x-pack/plugins/case/server/services/tags/read_tags.ts
+++ b/x-pack/plugins/case/server/services/tags/read_tags.ts
@@ -52,7 +52,7 @@ export const readRawTags = async ({
     page: 1,
     perPage,
   });
-  const tags = await client.find({
+  const tags = await client.find<CaseAttributes>({
     type: CASE_SAVED_OBJECT,
     fields: ['tags'],
     page: 1,
diff --git a/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.ts b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.ts
index 1e439b68f8c30..849bfda2bc86f 100644
--- a/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.ts
+++ b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.ts
@@ -7,7 +7,6 @@
 import uuid from 'uuid';
 import {
   SavedObject,
-  SavedObjectAttributes,
   SavedObjectsBaseOptions,
   SavedObjectsBulkCreateObject,
   SavedObjectsBulkGetObject,
@@ -42,7 +41,7 @@ export class EncryptedSavedObjectsClientWrapper implements SavedObjectsClientCon
     public readonly errors = options.baseClient.errors
   ) {}
 
-  public async create<T extends SavedObjectAttributes>(
+  public async create<T = unknown>(
     type: string,
     attributes: T = {} as T,
     options: SavedObjectsCreateOptions = {}
@@ -66,15 +65,15 @@ export class EncryptedSavedObjectsClientWrapper implements SavedObjectsClientCon
         type,
         await this.options.service.encryptAttributes(
           { type, id, namespace: options.namespace },
-          attributes
+          attributes as Record<string, unknown>
         ),
         { ...options, id }
       )
-    );
+    ) as SavedObject<T>;
   }
 
-  public async bulkCreate(
-    objects: SavedObjectsBulkCreateObject[],
+  public async bulkCreate<T = unknown>(
+    objects: Array<SavedObjectsBulkCreateObject<T>>,
     options?: SavedObjectsBaseOptions
   ) {
     // We encrypt attributes for every object in parallel and that can potentially exhaust libuv or
@@ -101,14 +100,14 @@ export class EncryptedSavedObjectsClientWrapper implements SavedObjectsClientCon
           id,
           attributes: await this.options.service.encryptAttributes(
             { type: object.type, id, namespace: options && options.namespace },
-            object.attributes
+            object.attributes as Record<string, unknown>
           ),
-        };
+        } as SavedObjectsBulkCreateObject<T>;
       })
     );
 
     return this.stripEncryptedAttributesFromBulkResponse(
-      await this.options.baseClient.bulkCreate(encryptedObjects, options)
+      await this.options.baseClient.bulkCreate<T>(encryptedObjects, options)
     );
   }
 
@@ -144,28 +143,28 @@ export class EncryptedSavedObjectsClientWrapper implements SavedObjectsClientCon
     return await this.options.baseClient.delete(type, id, options);
   }
 
-  public async find(options: SavedObjectsFindOptions) {
+  public async find<T = unknown>(options: SavedObjectsFindOptions) {
     return this.stripEncryptedAttributesFromBulkResponse(
-      await this.options.baseClient.find(options)
+      await this.options.baseClient.find<T>(options)
     );
   }
 
-  public async bulkGet(
+  public async bulkGet<T = unknown>(
     objects: SavedObjectsBulkGetObject[] = [],
     options?: SavedObjectsBaseOptions
   ) {
     return this.stripEncryptedAttributesFromBulkResponse(
-      await this.options.baseClient.bulkGet(objects, options)
+      await this.options.baseClient.bulkGet<T>(objects, options)
     );
   }
 
-  public async get(type: string, id: string, options?: SavedObjectsBaseOptions) {
+  public async get<T = unknown>(type: string, id: string, options?: SavedObjectsBaseOptions) {
     return this.stripEncryptedAttributesFromResponse(
-      await this.options.baseClient.get(type, id, options)
+      await this.options.baseClient.get<T>(type, id, options)
     );
   }
 
-  public async update<T extends SavedObjectAttributes>(
+  public async update<T = unknown>(
     type: string,
     id: string,
     attributes: Partial<T>,
@@ -199,7 +198,7 @@ export class EncryptedSavedObjectsClientWrapper implements SavedObjectsClientCon
     if (this.options.service.isRegistered(response.type)) {
       response.attributes = this.options.service.stripEncryptedAttributes(
         response.type,
-        response.attributes
+        response.attributes as Record<string, unknown>
       );
     }
 
@@ -218,7 +217,7 @@ export class EncryptedSavedObjectsClientWrapper implements SavedObjectsClientCon
       if (this.options.service.isRegistered(savedObject.type)) {
         savedObject.attributes = this.options.service.stripEncryptedAttributes(
           savedObject.type,
-          savedObject.attributes
+          savedObject.attributes as Record<string, unknown>
         );
       }
     }
diff --git a/x-pack/plugins/encrypted_saved_objects/server/saved_objects/index.ts b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/index.ts
index 80bd2ab7b5171..0dbdc2f3ac7e3 100644
--- a/x-pack/plugins/encrypted_saved_objects/server/saved_objects/index.ts
+++ b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/index.ts
@@ -4,12 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import {
-  CoreSetup,
-  SavedObject,
-  SavedObjectAttributes,
-  SavedObjectsBaseOptions,
-} from 'src/core/server';
+import { CoreSetup, SavedObject, SavedObjectsBaseOptions } from 'src/core/server';
 import { EncryptedSavedObjectsService } from '../crypto';
 import { EncryptedSavedObjectsClientWrapper } from './encrypted_saved_objects_client_wrapper';
 
@@ -20,7 +15,7 @@ interface SetupSavedObjectsParams {
 }
 
 export interface SavedObjectsSetup {
-  getDecryptedAsInternalUser: <T extends SavedObjectAttributes = any>(
+  getDecryptedAsInternalUser: <T = unknown>(
     type: string,
     id: string,
     options?: SavedObjectsBaseOptions
@@ -47,7 +42,7 @@ export function setupSavedObjects({
     core.savedObjects.createInternalRepository()
   );
   return {
-    getDecryptedAsInternalUser: async <T extends SavedObjectAttributes = any>(
+    getDecryptedAsInternalUser: async <T = unknown>(
       type: string,
       id: string,
       options?: SavedObjectsBaseOptions
@@ -56,10 +51,10 @@ export function setupSavedObjects({
       const savedObject = await internalRepository.get(type, id, options);
       return {
         ...savedObject,
-        attributes: await service.decryptAttributes(
+        attributes: (await service.decryptAttributes(
           { type, id, namespace: options && options.namespace },
-          savedObject.attributes
-        ),
+          savedObject.attributes as Record<string, unknown>
+        )) as T,
       };
     },
   };
diff --git a/x-pack/plugins/infra/public/hooks/use_bulk_get_saved_object.tsx b/x-pack/plugins/infra/public/hooks/use_bulk_get_saved_object.tsx
index 80d1b67e59798..2c553b57dcd48 100644
--- a/x-pack/plugins/infra/public/hooks/use_bulk_get_saved_object.tsx
+++ b/x-pack/plugins/infra/public/hooks/use_bulk_get_saved_object.tsx
@@ -5,12 +5,13 @@
  */
 
 import { useState, useCallback } from 'react';
-import { SavedObjectAttributes, SavedObjectsBatchResponse } from 'src/core/public';
+import { SavedObjectsBatchResponse } from 'src/core/public';
 import { useKibana } from '../../../../../src/plugins/kibana_react/public';
 
 export const useBulkGetSavedObject = (type: string) => {
   const kibana = useKibana();
-  const [data, setData] = useState<SavedObjectsBatchResponse<SavedObjectAttributes> | null>(null);
+  // TODO: define saved object type
+  const [data, setData] = useState<SavedObjectsBatchResponse<any> | null>(null);
   const [error, setError] = useState<string | null>(null);
   const [loading, setLoading] = useState<boolean>(false);
 
diff --git a/x-pack/plugins/lens/server/routes/existing_fields.test.ts b/x-pack/plugins/lens/server/routes/existing_fields.test.ts
index 1f19671826807..9bd11b6863d93 100644
--- a/x-pack/plugins/lens/server/routes/existing_fields.test.ts
+++ b/x-pack/plugins/lens/server/routes/existing_fields.test.ts
@@ -91,6 +91,8 @@ describe('buildFieldList', () => {
     type: 'indexpattern',
     attributes: {
       title: 'testpattern',
+      type: 'type',
+      typeMeta: 'typemeta',
       fields: JSON.stringify([
         { name: 'foo', scripted: true, lang: 'painless', script: '2+2' },
         { name: 'bar' },
diff --git a/x-pack/plugins/lens/server/routes/existing_fields.ts b/x-pack/plugins/lens/server/routes/existing_fields.ts
index a059e32585909..40b2766a647cf 100644
--- a/x-pack/plugins/lens/server/routes/existing_fields.ts
+++ b/x-pack/plugins/lens/server/routes/existing_fields.ts
@@ -9,7 +9,10 @@ import { schema } from '@kbn/config-schema';
 import { IScopedClusterClient, SavedObject, RequestHandlerContext } from 'src/core/server';
 import { CoreSetup } from 'src/core/server';
 import { BASE_API_URL } from '../../common';
-import { IndexPatternsFetcher } from '../../../../../src/plugins/data/server';
+import {
+  IndexPatternsFetcher,
+  IndexPatternAttributes,
+} from '../../../../../src/plugins/data/server';
 
 /**
  * The number of docs to sample to determine field empty status.
@@ -125,7 +128,10 @@ async function fetchFieldExistence({
 async function fetchIndexPatternDefinition(indexPatternId: string, context: RequestHandlerContext) {
   const savedObjectsClient = context.core.savedObjects.client;
   const requestClient = context.core.elasticsearch.dataClient;
-  const indexPattern = await savedObjectsClient.get('index-pattern', indexPatternId);
+  const indexPattern = await savedObjectsClient.get<IndexPatternAttributes>(
+    'index-pattern',
+    indexPatternId
+  );
   const indexPatternTitle = indexPattern.attributes.title;
   // TODO: maybe don't use IndexPatternsFetcher at all, since we're only using it
   // to look up field values in the resulting documents. We can accomplish the same
@@ -155,7 +161,7 @@ async function fetchIndexPatternDefinition(indexPatternId: string, context: Requ
  * Exported only for unit tests.
  */
 export function buildFieldList(
-  indexPattern: SavedObject,
+  indexPattern: SavedObject<IndexPatternAttributes>,
   mappings: MappingResult,
   fieldDescriptors: FieldDescriptor[]
 ): Field[] {
diff --git a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts
index 03b1d770fa770..2209e7fb66fcb 100644
--- a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts
+++ b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts
@@ -5,7 +5,6 @@
  */
 
 import {
-  SavedObjectAttributes,
   SavedObjectsBaseOptions,
   SavedObjectsBulkCreateObject,
   SavedObjectsBulkGetObject,
@@ -46,7 +45,7 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra
     this.checkSavedObjectsPrivilegesAsCurrentUser = checkSavedObjectsPrivilegesAsCurrentUser;
   }
 
-  public async create<T extends SavedObjectAttributes>(
+  public async create<T = unknown>(
     type: string,
     attributes: T = {} as T,
     options: SavedObjectsCreateOptions = {}
@@ -56,8 +55,8 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra
     return await this.baseClient.create(type, attributes, options);
   }
 
-  public async bulkCreate(
-    objects: SavedObjectsBulkCreateObject[],
+  public async bulkCreate<T = unknown>(
+    objects: Array<SavedObjectsBulkCreateObject<T>>,
     options: SavedObjectsBaseOptions = {}
   ) {
     await this.ensureAuthorized(
@@ -76,13 +75,13 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra
     return await this.baseClient.delete(type, id, options);
   }
 
-  public async find(options: SavedObjectsFindOptions) {
+  public async find<T = unknown>(options: SavedObjectsFindOptions) {
     await this.ensureAuthorized(options.type, 'find', options.namespace, { options });
 
-    return this.baseClient.find(options);
+    return this.baseClient.find<T>(options);
   }
 
-  public async bulkGet(
+  public async bulkGet<T = unknown>(
     objects: SavedObjectsBulkGetObject[] = [],
     options: SavedObjectsBaseOptions = {}
   ) {
@@ -91,16 +90,16 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra
       options,
     });
 
-    return await this.baseClient.bulkGet(objects, options);
+    return await this.baseClient.bulkGet<T>(objects, options);
   }
 
-  public async get(type: string, id: string, options: SavedObjectsBaseOptions = {}) {
+  public async get<T = unknown>(type: string, id: string, options: SavedObjectsBaseOptions = {}) {
     await this.ensureAuthorized(type, 'get', options.namespace, { type, id, options });
 
-    return await this.baseClient.get(type, id, options);
+    return await this.baseClient.get<T>(type, id, options);
   }
 
-  public async update<T extends SavedObjectAttributes>(
+  public async update<T = unknown>(
     type: string,
     id: string,
     attributes: Partial<T>,
@@ -116,8 +115,8 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra
     return await this.baseClient.update(type, id, attributes, options);
   }
 
-  public async bulkUpdate(
-    objects: SavedObjectsBulkUpdateObject[] = [],
+  public async bulkUpdate<T = unknown>(
+    objects: Array<SavedObjectsBulkUpdateObject<T>> = [],
     options: SavedObjectsBaseOptions = {}
   ) {
     await this.ensureAuthorized(
@@ -127,7 +126,7 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra
       { objects, options }
     );
 
-    return await this.baseClient.bulkUpdate(objects, options);
+    return await this.baseClient.bulkUpdate<T>(objects, options);
   }
 
   private async checkPrivileges(actions: string | string[], namespace?: string) {
diff --git a/x-pack/plugins/spaces/server/lib/saved_objects_client/spaces_saved_objects_client.ts b/x-pack/plugins/spaces/server/lib/saved_objects_client/spaces_saved_objects_client.ts
index 62a78679175b3..534d797123940 100644
--- a/x-pack/plugins/spaces/server/lib/saved_objects_client/spaces_saved_objects_client.ts
+++ b/x-pack/plugins/spaces/server/lib/saved_objects_client/spaces_saved_objects_client.ts
@@ -5,7 +5,6 @@
  */
 
 import {
-  SavedObjectAttributes,
   SavedObjectsBaseOptions,
   SavedObjectsBulkCreateObject,
   SavedObjectsBulkGetObject,
@@ -65,14 +64,14 @@ export class SpacesSavedObjectsClient implements SavedObjectsClientContract {
    * @property {string} [options.namespace]
    * @returns {promise} - { id, type, version, attributes }
    */
-  public async create<T extends SavedObjectAttributes>(
+  public async create<T = unknown>(
     type: string,
     attributes: T = {} as T,
     options: SavedObjectsCreateOptions = {}
   ) {
     throwErrorIfNamespaceSpecified(options);
 
-    return await this.client.create(type, attributes, {
+    return await this.client.create<T>(type, attributes, {
       ...options,
       namespace: spaceIdToNamespace(this.spaceId),
     });
@@ -87,8 +86,8 @@ export class SpacesSavedObjectsClient implements SavedObjectsClientContract {
    * @property {string} [options.namespace]
    * @returns {promise} - { saved_objects: [{ id, type, version, attributes, error: { message } }]}
    */
-  public async bulkCreate(
-    objects: SavedObjectsBulkCreateObject[],
+  public async bulkCreate<T = unknown>(
+    objects: Array<SavedObjectsBulkCreateObject<T>>,
     options: SavedObjectsBaseOptions = {}
   ) {
     throwErrorIfNamespaceSpecified(options);
@@ -133,10 +132,10 @@ export class SpacesSavedObjectsClient implements SavedObjectsClientContract {
    * @property {object} [options.hasReference] - { type, id }
    * @returns {promise} - { saved_objects: [{ id, type, version, attributes }], total, per_page, page }
    */
-  public async find(options: SavedObjectsFindOptions) {
+  public async find<T = unknown>(options: SavedObjectsFindOptions) {
     throwErrorIfNamespaceSpecified(options);
 
-    return await this.client.find({
+    return await this.client.find<T>({
       ...options,
       type: (options.type ? coerceToArray(options.type) : this.types).filter(
         type => type !== 'space'
@@ -159,13 +158,13 @@ export class SpacesSavedObjectsClient implements SavedObjectsClientContract {
    *   { id: 'foo', type: 'index-pattern' }
    * ])
    */
-  public async bulkGet(
+  public async bulkGet<T = unknown>(
     objects: SavedObjectsBulkGetObject[] = [],
     options: SavedObjectsBaseOptions = {}
   ) {
     throwErrorIfNamespaceSpecified(options);
 
-    return await this.client.bulkGet(objects, {
+    return await this.client.bulkGet<T>(objects, {
       ...options,
       namespace: spaceIdToNamespace(this.spaceId),
     });
@@ -180,10 +179,10 @@ export class SpacesSavedObjectsClient implements SavedObjectsClientContract {
    * @property {string} [options.namespace]
    * @returns {promise} - { id, type, version, attributes }
    */
-  public async get(type: string, id: string, options: SavedObjectsBaseOptions = {}) {
+  public async get<T = unknown>(type: string, id: string, options: SavedObjectsBaseOptions = {}) {
     throwErrorIfNamespaceSpecified(options);
 
-    return await this.client.get(type, id, {
+    return await this.client.get<T>(type, id, {
       ...options,
       namespace: spaceIdToNamespace(this.spaceId),
     });
@@ -199,7 +198,7 @@ export class SpacesSavedObjectsClient implements SavedObjectsClientContract {
    * @property {string} [options.namespace]
    * @returns {promise}
    */
-  public async update<T extends SavedObjectAttributes>(
+  public async update<T = unknown>(
     type: string,
     id: string,
     attributes: Partial<T>,
@@ -225,8 +224,8 @@ export class SpacesSavedObjectsClient implements SavedObjectsClientContract {
    *   { id: 'foo', type: 'index-pattern', attributes: {} }
    * ])
    */
-  public async bulkUpdate(
-    objects: SavedObjectsBulkUpdateObject[] = [],
+  public async bulkUpdate<T = unknown>(
+    objects: Array<SavedObjectsBulkUpdateObject<T>> = [],
     options: SavedObjectsBaseOptions = {}
   ) {
     throwErrorIfNamespaceSpecified(options);
diff --git a/x-pack/plugins/task_manager/server/task_store.test.ts b/x-pack/plugins/task_manager/server/task_store.test.ts
index 6ed6ae16fa0f9..97794311fb3d2 100644
--- a/x-pack/plugins/task_manager/server/task_store.test.ts
+++ b/x-pack/plugins/task_manager/server/task_store.test.ts
@@ -18,11 +18,7 @@ import {
 } from './task';
 import { StoreOpts, OwnershipClaimingOpts, TaskStore, SearchOpts } from './task_store';
 import { savedObjectsRepositoryMock } from '../../../../src/core/server/mocks';
-import {
-  SavedObjectsSerializer,
-  SavedObjectTypeRegistry,
-  SavedObjectAttributes,
-} from '../../../../src/core/server';
+import { SavedObjectsSerializer, SavedObjectTypeRegistry } from '../../../../src/core/server';
 import { SavedObjectsErrorHelpers } from '../../../../src/core/server/saved_objects/service/lib/errors';
 import { asTaskClaimEvent, TaskEvent } from './task_events';
 import { asOk, asErr } from './lib/result_type';
@@ -64,15 +60,13 @@ describe('TaskStore', () => {
   describe('schedule', () => {
     async function testSchedule(task: TaskInstance) {
       const callCluster = jest.fn();
-      savedObjectsClient.create.mockImplementation(
-        async (type: string, attributes: SavedObjectAttributes) => ({
-          id: 'testid',
-          type,
-          attributes,
-          references: [],
-          version: '123',
-        })
-      );
+      savedObjectsClient.create.mockImplementation(async (type: string, attributes: any) => ({
+        id: 'testid',
+        type,
+        attributes,
+        references: [],
+        version: '123',
+      }));
       const store = new TaskStore({
         index: 'tasky',
         taskManagerId: '',
@@ -155,14 +149,14 @@ describe('TaskStore', () => {
     test('sets runAt to now if not specified', async () => {
       await testSchedule({ taskType: 'dernstraight', params: {}, state: {} });
       expect(savedObjectsClient.create).toHaveBeenCalledTimes(1);
-      const attributes = savedObjectsClient.create.mock.calls[0][1];
+      const attributes: any = savedObjectsClient.create.mock.calls[0][1];
       expect(new Date(attributes.runAt as string).getTime()).toEqual(mockedDate.getTime());
     });
 
     test('ensures params and state are not null', async () => {
       await testSchedule({ taskType: 'yawn' } as any);
       expect(savedObjectsClient.create).toHaveBeenCalledTimes(1);
-      const attributes = savedObjectsClient.create.mock.calls[0][1];
+      const attributes: any = savedObjectsClient.create.mock.calls[0][1];
       expect(attributes.params).toEqual('{}');
       expect(attributes.state).toEqual('{}');
     });
@@ -751,7 +745,7 @@ if (doc['task.runAt'].size()!=0) {
       };
 
       savedObjectsClient.update.mockImplementation(
-        async (type: string, id: string, attributes: SavedObjectAttributes) => {
+        async (type: string, id: string, attributes: any) => {
           return {
             id,
             type,
diff --git a/x-pack/plugins/task_manager/server/task_store.ts b/x-pack/plugins/task_manager/server/task_store.ts
index 3915eeffc5519..0e487386eb04d 100644
--- a/x-pack/plugins/task_manager/server/task_store.ts
+++ b/x-pack/plugins/task_manager/server/task_store.ts
@@ -428,7 +428,8 @@ function taskInstanceToAttributes(doc: TaskInstance): SavedObjectAttributes {
 }
 
 export function savedObjectToConcreteTaskInstance(
-  savedObject: Omit<SavedObject, 'references'>
+  // TODO: define saved object type
+  savedObject: Omit<SavedObject<any>, 'references'>
 ): ConcreteTaskInstance {
   return {
     ...savedObject.attributes,
diff --git a/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.ts b/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.ts
index 071067ffa85cb..3529d8f3ae9c9 100644
--- a/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.ts
+++ b/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.ts
@@ -59,7 +59,9 @@ export function resolveCopyToSpaceConflictsSuite(
       .then((response: any) => response.body);
   };
 
-  const getObjectsAtSpace = async (spaceId: string): Promise<[SavedObject, SavedObject]> => {
+  const getObjectsAtSpace = async (
+    spaceId: string
+  ): Promise<[SavedObject<any>, SavedObject<any>]> => {
     const dashboard = await getDashboardAtSpace(spaceId);
     const visualization = await getVisualizationAtSpace(spaceId);
     return [dashboard, visualization];

From 1eb01760365030f1793f086dea2f32a28a15a125 Mon Sep 17 00:00:00 2001
From: Yuliia Naumenko <jo.naumenko@gmail.com>
Date: Fri, 21 Feb 2020 13:58:54 -0800
Subject: [PATCH 135/174] Added UI support for the default action group for
 Alert Type Model (#57603)

* Added UI support for the default action group for Alert Type Model

* Fixed set default on  alert type select

* Fixed type check

* Moved setting of default alert type to the server api

* Added default value for actionGroups if it is empty in register alert type functions

* Fixed type check

* Fixed due to comments	aed89377b9	Yuliia Naumenko <yuliia.naumenko@elastic.com>	Feb 20, 2020 at 12:40 PM

* Renamed defaultActionGroup to defaultActionGroupId

* Fixed failing tests
---
 .../server/alerts/license_expiration.ts       |  1 +
 .../signals/signal_rule_alert_type.ts         |  1 +
 x-pack/plugins/alerting/common/index.ts       |  5 ++
 .../server/alert_type_registry.test.ts        | 50 ++++++++++++++++---
 .../alerting/server/alert_type_registry.ts    |  1 +
 .../alerting/server/alerts_client.test.ts     | 12 ++++-
 x-pack/plugins/alerting/server/index.ts       |  1 +
 .../lib/validate_alert_type_params.test.ts    | 24 +++++++--
 .../server/routes/list_alert_types.test.ts    | 24 +++++++--
 .../create_execution_handler.test.ts          |  1 +
 .../server/task_runner/task_runner.test.ts    |  1 +
 .../task_runner/task_runner_factory.test.ts   |  1 +
 x-pack/plugins/alerting/server/types.ts       |  8 +--
 .../public/application/lib/alert_api.test.ts  |  1 +
 .../sections/alert_add/alert_form.test.tsx    |  1 +
 .../sections/alert_add/alert_form.tsx         | 40 ++++++++-------
 .../components/alert_details.test.tsx         | 16 ++++++
 .../triggers_actions_ui/public/types.ts       |  8 +--
 .../common/fixtures/plugins/alerts/index.ts   | 35 +++++++++++--
 .../tests/alerting/list_alert_types.ts        |  1 +
 .../tests/alerting/list_alert_types.ts        |  1 +
 .../fixtures/plugins/alerts/index.ts          |  1 +
 22 files changed, 190 insertions(+), 44 deletions(-)

diff --git a/x-pack/legacy/plugins/monitoring/server/alerts/license_expiration.ts b/x-pack/legacy/plugins/monitoring/server/alerts/license_expiration.ts
index f968e90f70b2d..9ef19e58bada7 100644
--- a/x-pack/legacy/plugins/monitoring/server/alerts/license_expiration.ts
+++ b/x-pack/legacy/plugins/monitoring/server/alerts/license_expiration.ts
@@ -54,6 +54,7 @@ export const getLicenseExpiration = (
         }),
       },
     ],
+    defaultActionGroupId: 'default',
     async executor({
       services,
       params,
diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts
index b9ac852701268..f7fabfb980195 100644
--- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts
+++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts
@@ -51,6 +51,7 @@ export const signalRulesAlertType = ({
         }),
       },
     ],
+    defaultActionGroupId: 'default',
     validate: {
       params: schema.object({
         description: schema.string(),
diff --git a/x-pack/plugins/alerting/common/index.ts b/x-pack/plugins/alerting/common/index.ts
index 03b3487f10f1d..8c6969cded85a 100644
--- a/x-pack/plugins/alerting/common/index.ts
+++ b/x-pack/plugins/alerting/common/index.ts
@@ -7,3 +7,8 @@
 export * from './alert';
 export * from './alert_instance';
 export * from './alert_task_instance';
+
+export interface ActionGroup {
+  id: string;
+  name: string;
+}
diff --git a/x-pack/plugins/alerting/server/alert_type_registry.test.ts b/x-pack/plugins/alerting/server/alert_type_registry.test.ts
index 1a820d55af8a4..f4749772c7004 100644
--- a/x-pack/plugins/alerting/server/alert_type_registry.test.ts
+++ b/x-pack/plugins/alerting/server/alert_type_registry.test.ts
@@ -27,7 +27,13 @@ describe('has()', () => {
     registry.register({
       id: 'foo',
       name: 'Foo',
-      actionGroups: [],
+      actionGroups: [
+        {
+          id: 'default',
+          name: 'Default',
+        },
+      ],
+      defaultActionGroupId: 'default',
       executor: jest.fn(),
     });
     expect(registry.has('foo')).toEqual(true);
@@ -39,7 +45,13 @@ describe('register()', () => {
     const alertType = {
       id: 'test',
       name: 'Test',
-      actionGroups: [],
+      actionGroups: [
+        {
+          id: 'default',
+          name: 'Default',
+        },
+      ],
+      defaultActionGroupId: 'default',
       executor: jest.fn(),
     };
     // eslint-disable-next-line @typescript-eslint/no-var-requires
@@ -64,14 +76,26 @@ describe('register()', () => {
     registry.register({
       id: 'test',
       name: 'Test',
-      actionGroups: [],
+      actionGroups: [
+        {
+          id: 'default',
+          name: 'Default',
+        },
+      ],
+      defaultActionGroupId: 'default',
       executor: jest.fn(),
     });
     expect(() =>
       registry.register({
         id: 'test',
         name: 'Test',
-        actionGroups: [],
+        actionGroups: [
+          {
+            id: 'default',
+            name: 'Default',
+          },
+        ],
+        defaultActionGroupId: 'default',
         executor: jest.fn(),
       })
     ).toThrowErrorMatchingInlineSnapshot(`"Alert type \\"test\\" is already registered."`);
@@ -84,13 +108,25 @@ describe('get()', () => {
     registry.register({
       id: 'test',
       name: 'Test',
-      actionGroups: [],
+      actionGroups: [
+        {
+          id: 'default',
+          name: 'Default',
+        },
+      ],
+      defaultActionGroupId: 'default',
       executor: jest.fn(),
     });
     const alertType = registry.get('test');
     expect(alertType).toMatchInlineSnapshot(`
       Object {
-        "actionGroups": Array [],
+        "actionGroups": Array [
+          Object {
+            "id": "default",
+            "name": "Default",
+          },
+        ],
+        "defaultActionGroupId": "default",
         "executor": [MockFunction],
         "id": "test",
         "name": "Test",
@@ -124,6 +160,7 @@ describe('list()', () => {
           name: 'Test Action Group',
         },
       ],
+      defaultActionGroupId: 'testActionGroup',
       executor: jest.fn(),
     });
     const result = registry.list();
@@ -136,6 +173,7 @@ describe('list()', () => {
               "name": "Test Action Group",
             },
           ],
+          "defaultActionGroupId": "testActionGroup",
           "id": "test",
           "name": "Test",
         },
diff --git a/x-pack/plugins/alerting/server/alert_type_registry.ts b/x-pack/plugins/alerting/server/alert_type_registry.ts
index b0976d67c70a0..d9045fb986745 100644
--- a/x-pack/plugins/alerting/server/alert_type_registry.ts
+++ b/x-pack/plugins/alerting/server/alert_type_registry.ts
@@ -70,6 +70,7 @@ export class AlertTypeRegistry {
       id: alertTypeId,
       name: alertType.name,
       actionGroups: alertType.actionGroups,
+      defaultActionGroupId: alertType.defaultActionGroupId,
     }));
   }
 }
diff --git a/x-pack/plugins/alerting/server/alerts_client.test.ts b/x-pack/plugins/alerting/server/alerts_client.test.ts
index 629bd05a8c76a..ed6e7562e3acd 100644
--- a/x-pack/plugins/alerting/server/alerts_client.test.ts
+++ b/x-pack/plugins/alerting/server/alerts_client.test.ts
@@ -87,6 +87,7 @@ describe('create()', () => {
       id: '123',
       name: 'Test',
       actionGroups: [{ id: 'default', name: 'Default' }],
+      defaultActionGroupId: 'default',
       async executor() {},
     });
   });
@@ -522,7 +523,13 @@ describe('create()', () => {
     alertTypeRegistry.get.mockReturnValue({
       id: '123',
       name: 'Test',
-      actionGroups: [],
+      actionGroups: [
+        {
+          id: 'default',
+          name: 'Default',
+        },
+      ],
+      defaultActionGroupId: 'default',
       validate: {
         params: schema.object({
           param1: schema.string(),
@@ -1885,6 +1892,7 @@ describe('update()', () => {
       id: '123',
       name: 'Test',
       actionGroups: [{ id: 'default', name: 'Default' }],
+      defaultActionGroupId: 'default',
       async executor() {},
     });
   });
@@ -2415,6 +2423,7 @@ describe('update()', () => {
       id: '123',
       name: 'Test',
       actionGroups: [{ id: 'default', name: 'Default' }],
+      defaultActionGroupId: 'default',
       validate: {
         params: schema.object({
           param1: schema.string(),
@@ -2647,6 +2656,7 @@ describe('update()', () => {
         id: '123',
         name: 'Test',
         actionGroups: [{ id: 'default', name: 'Default' }],
+        defaultActionGroupId: 'default',
         async executor() {},
       });
       savedObjectsClient.bulkGet.mockResolvedValueOnce({
diff --git a/x-pack/plugins/alerting/server/index.ts b/x-pack/plugins/alerting/server/index.ts
index 58b77f0f510f7..727e38d9ba56b 100644
--- a/x-pack/plugins/alerting/server/index.ts
+++ b/x-pack/plugins/alerting/server/index.ts
@@ -12,6 +12,7 @@ export type AlertsClient = PublicMethodsOf<AlertsClientClass>;
 
 export {
   AlertType,
+  ActionGroup,
   AlertingPlugin,
   AlertExecutorOptions,
   AlertActionParams,
diff --git a/x-pack/plugins/alerting/server/lib/validate_alert_type_params.test.ts b/x-pack/plugins/alerting/server/lib/validate_alert_type_params.test.ts
index e9a61354001f1..67820e44f567e 100644
--- a/x-pack/plugins/alerting/server/lib/validate_alert_type_params.test.ts
+++ b/x-pack/plugins/alerting/server/lib/validate_alert_type_params.test.ts
@@ -12,7 +12,13 @@ test('should return passed in params when validation not defined', () => {
     {
       id: 'my-alert-type',
       name: 'My description',
-      actionGroups: [],
+      actionGroups: [
+        {
+          id: 'default',
+          name: 'Default',
+        },
+      ],
+      defaultActionGroupId: 'default',
       async executor() {},
     },
     {
@@ -27,7 +33,13 @@ test('should validate and apply defaults when params is valid', () => {
     {
       id: 'my-alert-type',
       name: 'My description',
-      actionGroups: [],
+      actionGroups: [
+        {
+          id: 'default',
+          name: 'Default',
+        },
+      ],
+      defaultActionGroupId: 'default',
       validate: {
         params: schema.object({
           param1: schema.string(),
@@ -50,7 +62,13 @@ test('should validate and throw error when params is invalid', () => {
       {
         id: 'my-alert-type',
         name: 'My description',
-        actionGroups: [],
+        actionGroups: [
+          {
+            id: 'default',
+            name: 'Default',
+          },
+        ],
+        defaultActionGroupId: 'default',
         validate: {
           params: schema.object({
             param1: schema.string(),
diff --git a/x-pack/plugins/alerting/server/routes/list_alert_types.test.ts b/x-pack/plugins/alerting/server/routes/list_alert_types.test.ts
index 3e9f57d55122d..96ee8c5717453 100644
--- a/x-pack/plugins/alerting/server/routes/list_alert_types.test.ts
+++ b/x-pack/plugins/alerting/server/routes/list_alert_types.test.ts
@@ -40,7 +40,13 @@ describe('listAlertTypesRoute', () => {
       {
         id: '1',
         name: 'name',
-        actionGroups: [],
+        actionGroups: [
+          {
+            id: 'default',
+            name: 'Default',
+          },
+        ],
+        defaultActionGroupId: 'default',
       },
     ];
 
@@ -50,7 +56,13 @@ describe('listAlertTypesRoute', () => {
       Object {
         "body": Array [
           Object {
-            "actionGroups": Array [],
+            "actionGroups": Array [
+              Object {
+                "id": "default",
+                "name": "Default",
+              },
+            ],
+            "defaultActionGroupId": "default",
             "id": "1",
             "name": "name",
           },
@@ -128,7 +140,13 @@ describe('listAlertTypesRoute', () => {
       {
         id: '1',
         name: 'name',
-        actionGroups: [],
+        actionGroups: [
+          {
+            id: 'default',
+            name: 'Default',
+          },
+        ],
+        defaultActionGroupId: 'default',
       },
     ];
 
diff --git a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts
index 1b3a9ed8d7b06..32299680a1f8c 100644
--- a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts
+++ b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts
@@ -15,6 +15,7 @@ const alertType: AlertType = {
     { id: 'default', name: 'Default' },
     { id: 'other-group', name: 'Other Group' },
   ],
+  defaultActionGroupId: 'default',
   executor: jest.fn(),
 };
 
diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts
index af0e7b658240a..d1bc0de3ae0e2 100644
--- a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts
+++ b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts
@@ -17,6 +17,7 @@ const alertType = {
   id: 'test',
   name: 'My test alert',
   actionGroups: [{ id: 'default', name: 'Default' }],
+  defaultActionGroupId: 'default',
   executor: jest.fn(),
 };
 let fakeTimer: sinon.SinonFakeTimers;
diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts
index 4dad46ef32f21..f885b0bdbd046 100644
--- a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts
+++ b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts
@@ -14,6 +14,7 @@ const alertType = {
   id: 'test',
   name: 'My test alert',
   actionGroups: [{ id: 'default', name: 'Default' }],
+  defaultActionGroupId: 'default',
   executor: jest.fn(),
 };
 let fakeTimer: sinon.SinonFakeTimers;
diff --git a/x-pack/plugins/alerting/server/types.ts b/x-pack/plugins/alerting/server/types.ts
index 202a73f076859..90bc7996729a6 100644
--- a/x-pack/plugins/alerting/server/types.ts
+++ b/x-pack/plugins/alerting/server/types.ts
@@ -8,7 +8,7 @@ import { AlertInstance } from './alert_instance';
 import { AlertTypeRegistry as OrigAlertTypeRegistry } from './alert_type_registry';
 import { PluginSetupContract, PluginStartContract } from './plugin';
 import { SavedObjectAttributes, SavedObjectsClientContract } from '../../../../src/core/server';
-import { Alert, AlertActionParams } from '../common';
+import { Alert, AlertActionParams, ActionGroup } from '../common';
 import { AlertsClient } from './alerts_client';
 export * from '../common';
 
@@ -52,11 +52,6 @@ export interface AlertExecutorOptions {
   updatedBy: string | null;
 }
 
-export interface ActionGroup {
-  id: string;
-  name: string;
-}
-
 export interface AlertType {
   id: string;
   name: string;
@@ -64,6 +59,7 @@ export interface AlertType {
     params?: { validate: (object: any) => any };
   };
   actionGroups: ActionGroup[];
+  defaultActionGroupId: ActionGroup['id'];
   executor: ({ services, params, state }: AlertExecutorOptions) => Promise<State | void>;
 }
 
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts
index 93a46862f4cd2..1e53e7d983848 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts
@@ -40,6 +40,7 @@ describe('loadAlertTypes', () => {
         name: 'Test',
         actionVariables: ['var1'],
         actionGroups: [{ id: 'default', name: 'Default' }],
+        defaultActionGroupId: 'default',
       },
     ];
     http.get.mockResolvedValueOnce(resolvedValue);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_add/alert_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_add/alert_form.test.tsx
index ce524ed8178ee..aa71621f1a914 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_add/alert_form.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_add/alert_form.test.tsx
@@ -158,6 +158,7 @@ describe('alert_form', () => {
       alertTypeRegistry.has.mockReturnValue(true);
       actionTypeRegistry.list.mockReturnValue([actionType]);
       actionTypeRegistry.has.mockReturnValue(true);
+      actionTypeRegistry.get.mockReturnValue(actionType);
 
       const initialAlert = ({
         name: 'test',
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_add/alert_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_add/alert_form.tsx
index 95c049f32436a..18dc88f54e907 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_add/alert_form.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_add/alert_form.tsx
@@ -40,7 +40,6 @@ import {
   ActionTypeIndex,
   ActionConnector,
   AlertTypeIndex,
-  ActionGroup,
 } from '../../../types';
 import { SectionLoading } from '../../components/section_loading';
 import { ConnectorAddModal } from '../action_connector_form/connector_add_modal';
@@ -119,7 +118,7 @@ export const AlertForm = ({
   const [alertThrottleUnit, setAlertThrottleUnit] = useState<string>('m');
   const [isAddActionPanelOpen, setIsAddActionPanelOpen] = useState<boolean>(true);
   const [connectors, setConnectors] = useState<ActionConnector[]>([]);
-  const [defaultActionGroup, setDefaultActionGroup] = useState<ActionGroup | undefined>(undefined);
+  const [defaultActionGroupId, setDefaultActionGroupId] = useState<string | undefined>(undefined);
   const [activeActionItem, setActiveActionItem] = useState<ActiveActionConnectorState | undefined>(
     undefined
   );
@@ -166,13 +165,14 @@ export const AlertForm = ({
           ],
           name: 'threshold',
           actionVariables: ['ctx.metadata.name', 'ctx.metadata.test'],
+          defaultActionGroupId: 'alert',
         });
         const index: AlertTypeIndex = {};
         for (const alertTypeItem of alertTypes) {
           index[alertTypeItem.id] = alertTypeItem;
         }
-        if (alert.alertTypeId) {
-          setDefaultActionGroup(index[alert.alertTypeId].actionGroups[0]);
+        if (alert.alertTypeId && index[alert.alertTypeId]) {
+          setDefaultActionGroupId(index[alert.alertTypeId].defaultActionGroupId);
         }
         setAlertTypesIndex(index);
       } catch (e) {
@@ -251,6 +251,14 @@ export const AlertForm = ({
     : null;
 
   function addActionType(actionTypeModel: ActionTypeModel) {
+    if (!defaultActionGroupId) {
+      toastNotifications!.addDanger({
+        title: i18n.translate('xpack.triggersActionsUI.sections.alertForm.unableToAddAction', {
+          defaultMessage: 'Unable to add action, because default action group is not defined',
+        }),
+      });
+      return;
+    }
     setIsAddActionPanelOpen(false);
     const actionTypeConnectors = connectors.filter(
       field => field.actionTypeId === actionTypeModel.id
@@ -266,7 +274,7 @@ export const AlertForm = ({
         alert.actions.push({
           id: '',
           actionTypeId: actionTypeModel.id,
-          group: defaultActionGroup?.id ?? 'Alert',
+          group: defaultActionGroupId,
           params: {},
         });
         setActionProperty('id', freeConnectors[0].id, alert.actions.length - 1);
@@ -278,7 +286,7 @@ export const AlertForm = ({
       alert.actions.push({
         id: '',
         actionTypeId: actionTypeModel.id,
-        group: defaultActionGroup?.id ?? 'Alert',
+        group: defaultActionGroupId,
         params: {},
       });
       setActionProperty('id', alert.actions.length, alert.actions.length - 1);
@@ -294,12 +302,8 @@ export const AlertForm = ({
         onClick={() => {
           setAlertProperty('alertTypeId', item.id);
           setAlertTypeModel(item);
-          if (
-            alertTypesIndex &&
-            alertTypesIndex[item.id] &&
-            alertTypesIndex[item.id].actionGroups.length > 0
-          ) {
-            setDefaultActionGroup(alertTypesIndex[item.id].actionGroups[0]);
+          if (alertTypesIndex && alertTypesIndex[item.id]) {
+            setDefaultActionGroupId(alertTypesIndex[item.id].defaultActionGroupId);
           }
         }}
       >
@@ -356,7 +360,7 @@ export const AlertForm = ({
         id,
       }));
     const actionTypeRegisterd = actionTypeRegistry.get(actionConnector.actionTypeId);
-    if (actionTypeRegisterd === null || actionItem.group !== defaultActionGroup?.id) return null;
+    if (!actionTypeRegisterd || actionItem.group !== defaultActionGroupId) return null;
     const ParamsFieldsComponent = actionTypeRegisterd.actionParamsFields;
     const actionParamsErrors: { errors: IErrorObject } =
       Object.keys(actionsErrors).length > 0 ? actionsErrors[actionItem.id] : { errors: {} };
@@ -368,7 +372,7 @@ export const AlertForm = ({
         id={index.toString()}
         className="euiAccordionForm"
         buttonContentClassName="euiAccordionForm__button"
-        data-test-subj="alertActionAccordion"
+        data-test-subj={`alertActionAccordion-${defaultActionGroupId}`}
         buttonContent={
           <EuiFlexGroup gutterSize="s" alignItems="center">
             <EuiFlexItem grow={false}>
@@ -465,7 +469,9 @@ export const AlertForm = ({
             errors={actionParamsErrors.errors}
             editAction={setActionParamsProperty}
             messageVariables={
-              alertTypesIndex ? alertTypesIndex[alert.alertTypeId].actionVariables : undefined
+              alertTypesIndex && alertTypesIndex[alert.alertTypeId]
+                ? alertTypesIndex[alert.alertTypeId].actionVariables
+                : undefined
             }
             defaultMessage={alertTypeModel?.defaultActionMessage ?? undefined}
           />
@@ -479,7 +485,7 @@ export const AlertForm = ({
       ? actionTypesIndex[actionItem.actionTypeId].name
       : actionItem.actionTypeId;
     const actionTypeRegisterd = actionTypeRegistry.get(actionItem.actionTypeId);
-    if (actionTypeRegisterd === null || actionItem.group !== defaultActionGroup?.id) return null;
+    if (!actionTypeRegisterd || actionItem.group !== defaultActionGroupId) return null;
     return (
       <EuiAccordion
         initialIsOpen={true}
@@ -487,7 +493,7 @@ export const AlertForm = ({
         id={index.toString()}
         className="euiAccordionForm"
         buttonContentClassName="euiAccordionForm__button"
-        data-test-subj="alertActionAccordion"
+        data-test-subj={`alertActionAccordion-${defaultActionGroupId}`}
         buttonContent={
           <EuiFlexGroup gutterSize="s" alignItems="center">
             <EuiFlexItem grow={false}>
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx
index c67954bdc44fd..d2cf2decc4a16 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx
@@ -45,6 +45,7 @@ describe('alert_details', () => {
       id: '.noop',
       name: 'No Op',
       actionGroups: [{ id: 'default', name: 'Default' }],
+      defaultActionGroupId: 'default',
       actionVariables: [],
     };
 
@@ -65,6 +66,7 @@ describe('alert_details', () => {
       id: '.noop',
       name: 'No Op',
       actionGroups: [{ id: 'default', name: 'Default' }],
+      defaultActionGroupId: 'default',
       actionVariables: [],
     };
 
@@ -92,6 +94,7 @@ describe('alert_details', () => {
         id: '.noop',
         name: 'No Op',
         actionGroups: [{ id: 'default', name: 'Default' }],
+        defaultActionGroupId: 'default',
         actionVariables: [],
       };
 
@@ -141,6 +144,7 @@ describe('alert_details', () => {
         id: '.noop',
         name: 'No Op',
         actionGroups: [{ id: 'default', name: 'Default' }],
+        defaultActionGroupId: 'default',
         actionVariables: [],
       };
       const actionTypes: ActionType[] = [
@@ -191,6 +195,7 @@ describe('alert_details', () => {
         id: '.noop',
         name: 'No Op',
         actionGroups: [{ id: 'default', name: 'Default' }],
+        defaultActionGroupId: 'default',
         actionVariables: [],
       };
 
@@ -215,6 +220,7 @@ describe('alert_details', () => {
         id: '.noop',
         name: 'No Op',
         actionGroups: [{ id: 'default', name: 'Default' }],
+        defaultActionGroupId: 'default',
         actionVariables: [],
       };
 
@@ -239,6 +245,7 @@ describe('alert_details', () => {
         id: '.noop',
         name: 'No Op',
         actionGroups: [{ id: 'default', name: 'Default' }],
+        defaultActionGroupId: 'default',
         actionVariables: [],
       };
 
@@ -268,6 +275,7 @@ describe('enable button', () => {
       id: '.noop',
       name: 'No Op',
       actionGroups: [{ id: 'default', name: 'Default' }],
+      defaultActionGroupId: 'default',
       actionVariables: [],
     };
 
@@ -293,6 +301,7 @@ describe('enable button', () => {
       id: '.noop',
       name: 'No Op',
       actionGroups: [{ id: 'default', name: 'Default' }],
+      defaultActionGroupId: 'default',
       actionVariables: [],
     };
 
@@ -318,6 +327,7 @@ describe('enable button', () => {
       id: '.noop',
       name: 'No Op',
       actionGroups: [{ id: 'default', name: 'Default' }],
+      defaultActionGroupId: 'default',
       actionVariables: [],
     };
 
@@ -352,6 +362,7 @@ describe('enable button', () => {
       id: '.noop',
       name: 'No Op',
       actionGroups: [{ id: 'default', name: 'Default' }],
+      defaultActionGroupId: 'default',
       actionVariables: [],
     };
 
@@ -389,6 +400,7 @@ describe('mute button', () => {
       id: '.noop',
       name: 'No Op',
       actionGroups: [{ id: 'default', name: 'Default' }],
+      defaultActionGroupId: 'default',
       actionVariables: [],
     };
 
@@ -415,6 +427,7 @@ describe('mute button', () => {
       id: '.noop',
       name: 'No Op',
       actionGroups: [{ id: 'default', name: 'Default' }],
+      defaultActionGroupId: 'default',
       actionVariables: [],
     };
 
@@ -441,6 +454,7 @@ describe('mute button', () => {
       id: '.noop',
       name: 'No Op',
       actionGroups: [{ id: 'default', name: 'Default' }],
+      defaultActionGroupId: 'default',
       actionVariables: [],
     };
 
@@ -476,6 +490,7 @@ describe('mute button', () => {
       id: '.noop',
       name: 'No Op',
       actionGroups: [{ id: 'default', name: 'Default' }],
+      defaultActionGroupId: 'default',
       actionVariables: [],
     };
 
@@ -511,6 +526,7 @@ describe('mute button', () => {
       id: '.noop',
       name: 'No Op',
       actionGroups: [{ id: 'default', name: 'Default' }],
+      defaultActionGroupId: 'default',
       actionVariables: [],
     };
 
diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts
index aa1d3d068ed77..2119c08bedc31 100644
--- a/x-pack/plugins/triggers_actions_ui/public/types.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/types.ts
@@ -3,6 +3,7 @@
  * or more contributor license agreements. Licensed under the Elastic License;
  * you may not use this file except in compliance with the Elastic License.
  */
+import { ActionGroup } from '../../alerting/common';
 import { ActionType } from '../../actions/common';
 import { TypeRegistry } from './application/type_registry';
 import {
@@ -70,17 +71,16 @@ export interface ActionConnectorTableItem extends ActionConnector {
   actionType: ActionType['name'];
 }
 
-export interface ActionGroup {
-  id: string;
-  name: string;
-}
 export interface AlertType {
   id: string;
   name: string;
   actionGroups: ActionGroup[];
   actionVariables: string[];
+  defaultActionGroupId: ActionGroup['id'];
 }
 
+export type SanitizedAlertType = Omit<AlertType, 'apiKey'>;
+
 export type AlertWithoutId = Omit<Alert, 'id'>;
 
 export interface AlertTableItem extends Alert {
diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts
index b06e0c9e0f8cd..2e7674f2b3eb7 100644
--- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts
+++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts
@@ -206,6 +206,7 @@ export default function(kibana: any) {
           { id: 'default', name: 'Default' },
           { id: 'other', name: 'Other' },
         ],
+        defaultActionGroupId: 'default',
         async executor(alertExecutorOptions: AlertExecutorOptions) {
           const {
             services,
@@ -260,6 +261,7 @@ export default function(kibana: any) {
           { id: 'default', name: 'Default' },
           { id: 'other', name: 'Other' },
         ],
+        defaultActionGroupId: 'default',
         async executor(alertExecutorOptions: AlertExecutorOptions) {
           const { services, state } = alertExecutorOptions;
           const group = 'default';
@@ -281,7 +283,13 @@ export default function(kibana: any) {
       const neverFiringAlertType: AlertType = {
         id: 'test.never-firing',
         name: 'Test: Never firing',
-        actionGroups: [],
+        actionGroups: [
+          {
+            id: 'default',
+            name: 'Default',
+          },
+        ],
+        defaultActionGroupId: 'default',
         async executor({ services, params, state }: AlertExecutorOptions) {
           await services.callCluster('index', {
             index: params.index,
@@ -301,7 +309,13 @@ export default function(kibana: any) {
       const failingAlertType: AlertType = {
         id: 'test.failing',
         name: 'Test: Failing',
-        actionGroups: [],
+        actionGroups: [
+          {
+            id: 'default',
+            name: 'Default',
+          },
+        ],
+        defaultActionGroupId: 'default',
         async executor({ services, params, state }: AlertExecutorOptions) {
           await services.callCluster('index', {
             index: params.index,
@@ -319,7 +333,13 @@ export default function(kibana: any) {
       const authorizationAlertType: AlertType = {
         id: 'test.authorization',
         name: 'Test: Authorization',
-        actionGroups: [],
+        actionGroups: [
+          {
+            id: 'default',
+            name: 'Default',
+          },
+        ],
+        defaultActionGroupId: 'default',
         validate: {
           params: schema.object({
             callClusterAuthorizationIndex: schema.string(),
@@ -378,7 +398,13 @@ export default function(kibana: any) {
       const validationAlertType: AlertType = {
         id: 'test.validation',
         name: 'Test: Validation',
-        actionGroups: [],
+        actionGroups: [
+          {
+            id: 'default',
+            name: 'Default',
+          },
+        ],
+        defaultActionGroupId: 'default',
         validate: {
           params: schema.object({
             param1: schema.string(),
@@ -390,6 +416,7 @@ export default function(kibana: any) {
         id: 'test.noop',
         name: 'Test: Noop',
         actionGroups: [{ id: 'default', name: 'Default' }],
+        defaultActionGroupId: 'default',
         async executor({ services, params, state }: AlertExecutorOptions) {},
       };
       server.newPlatform.setup.plugins.alerting.registerType(alwaysFiringAlertType);
diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/list_alert_types.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/list_alert_types.ts
index 517a60f77849e..30c1548b7db2a 100644
--- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/list_alert_types.ts
+++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/list_alert_types.ts
@@ -41,6 +41,7 @@ export default function listAlertTypes({ getService }: FtrProviderContext) {
               );
               expect(fixtureAlertType).to.eql({
                 actionGroups: [{ id: 'default', name: 'Default' }],
+                defaultActionGroupId: 'default',
                 id: 'test.noop',
                 name: 'Test: Noop',
               });
diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/list_alert_types.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/list_alert_types.ts
index 55570744f6af9..590de1ea7ce0b 100644
--- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/list_alert_types.ts
+++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/list_alert_types.ts
@@ -21,6 +21,7 @@ export default function listAlertTypes({ getService }: FtrProviderContext) {
       const fixtureAlertType = response.body.find((alertType: any) => alertType.id === 'test.noop');
       expect(fixtureAlertType).to.eql({
         actionGroups: [{ id: 'default', name: 'Default' }],
+        defaultActionGroupId: 'default',
         id: 'test.noop',
         name: 'Test: Noop',
       });
diff --git a/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/index.ts b/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/index.ts
index 678707af40bae..9069044b83ed9 100644
--- a/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/index.ts
+++ b/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/index.ts
@@ -23,6 +23,7 @@ function createNoopAlertType(setupContract: any) {
     id: 'test.noop',
     name: 'Test: Noop',
     actionGroups: [{ id: 'default', name: 'Default' }],
+    defaultActionGroupId: 'default',
     async executor() {},
   };
   setupContract.registerType(noopAlertType);

From 5fefb76a261f41e04ad263bdd44fde72ab2f2496 Mon Sep 17 00:00:00 2001
From: Caroline Horn <549577+cchaos@users.noreply.github.com>
Date: Fri, 21 Feb 2020 18:28:53 -0500
Subject: [PATCH 136/174] Use EuiTokens for ES field types (#57911)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* [FieldIcon] Refactor to extend EuiToken and props

* [Add to FieldIcon] Export props

* [Graph] Updated instances of FieldIcon

* [Maps] Update FieldIcon instance

* [Lens] Update FieldIcon usage


* [Discover] Update FieldIcon usage


* Remove comment

* [Lens] Delete unused files

* [Graph] Fix alignments

* More cleanup

* Fixing snaps

* [Lens] Removing method `getColorForDataType`

The method no longer returns the correct color and EuiProgress doesn’t currently support all the colors from the token map. @cchaos will follow up with a PR to address the coloring of the field data charts. Right now they will fallback to pink for progress and default for charts.

* [Maps] Fixing implementations of FieldIcon

* [Graph] Using css utility class instead of custom class

* Snap

* Fix css to use var
---
 .../public/discover/np_ready/_discover.scss   |   2 +-
 .../__snapshots__/field_name.test.tsx.snap    | 150 ++++++++++-------
 .../directives/field_name/field_name.tsx      |  32 ++--
 .../field_chooser/_field_chooser.scss         |   1 -
 .../np_ready/components/table/table_row.tsx   |   7 +-
 .../__snapshots__/field_icon.test.tsx.snap    | 156 ++++++++++++++++--
 .../public/field_icon/field_icon.test.tsx     |  36 +++-
 .../public/field_icon/field_icon.tsx          |  63 ++++---
 .../components/field_manager/field_editor.tsx |  16 +-
 .../components/field_manager/field_icon.tsx   |  37 -----
 .../components/field_manager/field_picker.tsx |   2 +-
 .../lens_field_icon.test.tsx.snap             |  10 +-
 .../indexpattern_datasource/_datapanel.scss   |  13 ++
 .../indexpattern_datasource/_field_item.scss  |   9 +-
 .../indexpattern_datasource/datapanel.tsx     |   5 +-
 .../dimension_panel/field_select.tsx          |   1 +
 .../field_icon.test.tsx                       |  55 ------
 .../indexpattern_datasource/field_icon.tsx    |  43 -----
 .../indexpattern_datasource/field_item.tsx    |  15 +-
 .../lens_field_icon.test.tsx                  |  13 +-
 .../lens_field_icon.tsx                       |  18 +-
 .../add_tooltip_field_popover.test.js.snap    |  30 ++--
 .../components/add_tooltip_field_popover.js   |  13 +-
 .../public/components/single_field_select.js  |  15 +-
 .../styles/vector/components/field_select.js  |  15 +-
 25 files changed, 422 insertions(+), 335 deletions(-)
 delete mode 100644 x-pack/legacy/plugins/graph/public/components/field_manager/field_icon.tsx
 delete mode 100644 x-pack/legacy/plugins/lens/public/indexpattern_datasource/field_icon.test.tsx
 delete mode 100644 x-pack/legacy/plugins/lens/public/indexpattern_datasource/field_icon.tsx

diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/_discover.scss b/src/legacy/core_plugins/kibana/public/discover/np_ready/_discover.scss
index 0da28e41579ae..62e7a96ed80cf 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/_discover.scss
+++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/_discover.scss
@@ -206,7 +206,7 @@ discover-app {
   background-color: transparent;
 }
 
-.dscField--noResults {
+.dscFieldName--noResults {
   color: $euiColorDarkShade;
 }
 
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name/__snapshots__/field_name.test.tsx.snap b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name/__snapshots__/field_name.test.tsx.snap
index bdb003771619c..23288fc5feb59 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name/__snapshots__/field_name.test.tsx.snap
+++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name/__snapshots__/field_name.test.tsx.snap
@@ -1,73 +1,109 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`FieldName renders a geo field, useShortDots is set to true 1`] = `
-<span
-  class="dscField--noResults"
-  title="Geo point field"
+<div
+  class="euiFlexGroup euiFlexGroup--gutterSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow dscFieldName dscFieldName--noResults"
 >
-  <svg
-    aria-hidden="true"
-    aria-label="Geo point field"
-    class="euiIcon euiIcon--small euiIcon-isLoading"
-    focusable="false"
-    height="16"
-    role="img"
-    viewBox="0 0 16 16"
-    width="16"
-    xmlns="http://www.w3.org/2000/svg"
-  />
-  <span
-    class="dscFieldName"
+  <div
+    class="euiFlexItem euiFlexItem--flexGrowZero"
   >
-    t.t.test
-  </span>
-</span>
+    <span
+      aria-label="Geo point field"
+      class="euiToken euiToken--euiColorVis5 euiToken--square euiToken--light euiToken--small kbnFieldIcon"
+      title="Geo point field"
+    >
+      <svg
+        aria-hidden="true"
+        class="euiIcon euiIcon--medium euiIcon-isLoading"
+        focusable="false"
+        height="16"
+        role="img"
+        viewBox="0 0 16 16"
+        width="16"
+        xmlns="http://www.w3.org/2000/svg"
+      />
+    </span>
+  </div>
+  <div
+    class="euiFlexItem eui-textTruncate"
+  >
+    <span
+      class="dscFieldName__displayName eui-textTruncate"
+    >
+      t.t.test
+    </span>
+  </div>
+</div>
 `;
 
 exports[`FieldName renders a number field by providing a field record, useShortDots is set to false 1`] = `
-<span
-  class=""
-  title="Number field"
+<div
+  class="euiFlexGroup euiFlexGroup--gutterSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow dscFieldName"
 >
-  <svg
-    aria-hidden="true"
-    aria-label="Number field"
-    class="euiIcon euiIcon--small euiIcon-isLoading"
-    focusable="false"
-    height="16"
-    role="img"
-    viewBox="0 0 16 16"
-    width="16"
-    xmlns="http://www.w3.org/2000/svg"
-  />
-  <span
-    class="dscFieldName"
+  <div
+    class="euiFlexItem euiFlexItem--flexGrowZero"
+  >
+    <span
+      aria-label="Number field"
+      class="euiToken euiToken--euiColorVis0 euiToken--square euiToken--light euiToken--small kbnFieldIcon"
+      title="Number field"
+    >
+      <svg
+        aria-hidden="true"
+        class="euiIcon euiIcon--medium euiIcon-isLoading"
+        focusable="false"
+        height="16"
+        role="img"
+        viewBox="0 0 16 16"
+        width="16"
+        xmlns="http://www.w3.org/2000/svg"
+      />
+    </span>
+  </div>
+  <div
+    class="euiFlexItem eui-textTruncate"
   >
-    test.test.test
-  </span>
-</span>
+    <span
+      class="dscFieldName__displayName eui-textTruncate"
+    >
+      test.test.test
+    </span>
+  </div>
+</div>
 `;
 
 exports[`FieldName renders a string field by providing fieldType and fieldName 1`] = `
-<span
-  class=""
-  title="String field"
+<div
+  class="euiFlexGroup euiFlexGroup--gutterSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow dscFieldName"
 >
-  <svg
-    aria-hidden="true"
-    aria-label="String field"
-    class="euiIcon euiIcon--small euiIcon-isLoading"
-    focusable="false"
-    height="16"
-    role="img"
-    viewBox="0 0 16 16"
-    width="16"
-    xmlns="http://www.w3.org/2000/svg"
-  />
-  <span
-    class="dscFieldName"
+  <div
+    class="euiFlexItem euiFlexItem--flexGrowZero"
+  >
+    <span
+      aria-label="String field"
+      class="euiToken euiToken--euiColorVis1 euiToken--square euiToken--light euiToken--small kbnFieldIcon"
+      title="String field"
+    >
+      <svg
+        aria-hidden="true"
+        class="euiIcon euiIcon--medium euiIcon-isLoading"
+        focusable="false"
+        height="16"
+        role="img"
+        viewBox="0 0 16 16"
+        width="16"
+        xmlns="http://www.w3.org/2000/svg"
+      />
+    </span>
+  </div>
+  <div
+    class="euiFlexItem eui-textTruncate"
   >
-    test
-  </span>
-</span>
+    <span
+      class="dscFieldName__displayName eui-textTruncate"
+    >
+      test
+    </span>
+  </div>
+</div>
 `;
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name/field_name.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name/field_name.tsx
index 95720bee38df8..54e1c1706a856 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name/field_name.tsx
+++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name/field_name.tsx
@@ -18,7 +18,9 @@
  */
 import React from 'react';
 import classNames from 'classnames';
-import { FieldIcon } from '../../../../../../../../../plugins/kibana_react/public';
+import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
+
+import { FieldIcon, FieldIconProps } from '../../../../../../../../../plugins/kibana_react/public';
 import { shortenDottedString } from '../../../../../../../../../plugins/data/common/utils';
 import { getFieldTypeName } from './field_type_name';
 
@@ -35,25 +37,35 @@ interface Props {
   fieldName?: string;
   fieldType?: string;
   useShortDots?: boolean;
+  fieldIconProps?: Omit<FieldIconProps, 'type'>;
 }
 
-export function FieldName({ field, fieldName, fieldType, useShortDots }: Props) {
+export function FieldName({ field, fieldName, fieldType, useShortDots, fieldIconProps }: Props) {
   const type = field ? String(field.type) : String(fieldType);
   const typeName = getFieldTypeName(type);
 
   const name = field ? String(field.name) : String(fieldName);
   const displayName = useShortDots ? shortenDottedString(name) : name;
 
-  const className = classNames({
-    'dscField--noResults': field ? !field.rowCount && !field.scripted : false,
-    // this is currently not styled, should display an icon
-    scripted: field ? field.scripted : false,
+  const noResults = field ? !field.rowCount && !field.scripted : false;
+
+  const className = classNames('dscFieldName', {
+    'dscFieldName--noResults': noResults,
   });
 
   return (
-    <span className={className} title={typeName}>
-      <FieldIcon type={type} label={typeName} />
-      <span className="dscFieldName">{displayName}</span>
-    </span>
+    <EuiFlexGroup className={className} alignItems="center" gutterSize="s" responsive={false}>
+      <EuiFlexItem grow={false}>
+        <FieldIcon
+          type={type}
+          label={typeName}
+          scripted={field ? field.scripted : false}
+          {...fieldIconProps}
+        />
+      </EuiFlexItem>
+      <EuiFlexItem className="eui-textTruncate">
+        <span className="dscFieldName__displayName eui-textTruncate">{displayName}</span>
+      </EuiFlexItem>
+    </EuiFlexGroup>
   );
 }
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/_field_chooser.scss b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/_field_chooser.scss
index fe13ac2fafa01..b05775c4ee95c 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/_field_chooser.scss
+++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/field_chooser/_field_chooser.scss
@@ -10,7 +10,6 @@
 
 .dscFieldName {
   color: $euiColorDarkShade;
-  padding-left: $euiSizeS;
 }
 
 
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/table/table_row.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/table/table_row.tsx
index 7a78e89416361..5b13f6b3655c3 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/table/table_row.tsx
+++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/table/table_row.tsx
@@ -87,7 +87,12 @@ export function DocViewTableRow({
         </td>
       )}
       <td className="kbnDocViewer__field">
-        <FieldName field={fieldMapping} fieldName={field} fieldType={fieldType} />
+        <FieldName
+          field={fieldMapping}
+          fieldName={field}
+          fieldType={fieldType}
+          fieldIconProps={{ fill: 'none', color: 'gray' }}
+        />
       </td>
       <td>
         {isCollapsible && (
diff --git a/src/plugins/kibana_react/public/field_icon/__snapshots__/field_icon.test.tsx.snap b/src/plugins/kibana_react/public/field_icon/__snapshots__/field_icon.test.tsx.snap
index 870dbdc533267..cde6a625ac8e8 100644
--- a/src/plugins/kibana_react/public/field_icon/__snapshots__/field_icon.test.tsx.snap
+++ b/src/plugins/kibana_react/public/field_icon/__snapshots__/field_icon.test.tsx.snap
@@ -1,37 +1,159 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`FieldIcon renders a blackwhite icon for a string 1`] = `
-<EuiIcon
-  aria-label="string"
+exports[`FieldIcon changes fill when scripted is true 1`] = `
+<EuiToken
+  aria-label="test"
+  className="kbnFieldIcon"
+  fill="dark"
+  iconType="tokenNumber"
   size="s"
-  type="string"
+  title="test"
 />
 `;
 
-exports[`FieldIcon renders a colored icon for a number 1`] = `
-<EuiIcon
+exports[`FieldIcon renders an icon for an unknown type 1`] = `
+<EuiToken
   aria-label="test"
-  color="#54B399"
+  className="kbnFieldIcon"
+  color="gray"
+  iconType="questionInCircle"
   size="s"
-  type="number"
+  title="test"
 />
 `;
 
-exports[`FieldIcon renders an icon for an unknown type 1`] = `
-<EuiIcon
-  aria-label="test"
-  color="#54B399"
+exports[`FieldIcon renders known field types _source is rendered 1`] = `
+<EuiToken
+  aria-label="_source"
+  className="kbnFieldIcon"
+  color="gray"
+  iconType="editorCodeBlock"
+  size="s"
+  title="_source"
+/>
+`;
+
+exports[`FieldIcon renders known field types boolean is rendered 1`] = `
+<EuiToken
+  aria-label="boolean"
+  className="kbnFieldIcon"
+  iconType="tokenBoolean"
+  size="s"
+  title="boolean"
+/>
+`;
+
+exports[`FieldIcon renders known field types conflict is rendered 1`] = `
+<EuiToken
+  aria-label="conflict"
+  className="kbnFieldIcon"
+  color="euiVisColor9"
+  iconType="alert"
+  size="s"
+  title="conflict"
+/>
+`;
+
+exports[`FieldIcon renders known field types date is rendered 1`] = `
+<EuiToken
+  aria-label="date"
+  className="kbnFieldIcon"
+  iconType="tokenDate"
+  size="s"
+  title="date"
+/>
+`;
+
+exports[`FieldIcon renders known field types geo_point is rendered 1`] = `
+<EuiToken
+  aria-label="geo_point"
+  className="kbnFieldIcon"
+  iconType="tokenGeo"
+  size="s"
+  title="geo_point"
+/>
+`;
+
+exports[`FieldIcon renders known field types geo_shape is rendered 1`] = `
+<EuiToken
+  aria-label="geo_shape"
+  className="kbnFieldIcon"
+  iconType="tokenGeo"
   size="s"
-  type="questionInCircle"
+  title="geo_shape"
+/>
+`;
+
+exports[`FieldIcon renders known field types ip is rendered 1`] = `
+<EuiToken
+  aria-label="ip"
+  className="kbnFieldIcon"
+  iconType="tokenIP"
+  size="s"
+  title="ip"
+/>
+`;
+
+exports[`FieldIcon renders known field types murmur3 is rendered 1`] = `
+<EuiToken
+  aria-label="murmur3"
+  className="kbnFieldIcon"
+  iconType="tokenFile"
+  size="s"
+  title="murmur3"
+/>
+`;
+
+exports[`FieldIcon renders known field types nested is rendered 1`] = `
+<EuiToken
+  aria-label="nested"
+  className="kbnFieldIcon"
+  iconType="tokenNested"
+  size="s"
+  title="nested"
+/>
+`;
+
+exports[`FieldIcon renders known field types number is rendered 1`] = `
+<EuiToken
+  aria-label="number"
+  className="kbnFieldIcon"
+  iconType="tokenNumber"
+  size="s"
+  title="number"
+/>
+`;
+
+exports[`FieldIcon renders known field types string is rendered 1`] = `
+<EuiToken
+  aria-label="string"
+  className="kbnFieldIcon"
+  iconType="tokenString"
+  size="s"
+  title="string"
 />
 `;
 
 exports[`FieldIcon renders with className if provided 1`] = `
-<EuiIcon
+<EuiToken
   aria-label="test"
-  className="myClass"
-  color="#54B399"
+  className="kbnFieldIcon myClass"
+  color="gray"
+  iconType="questionInCircle"
   size="s"
-  type="questionInCircle"
+  title="test"
+/>
+`;
+
+exports[`FieldIcon supports same props as EuiToken 1`] = `
+<EuiToken
+  aria-label="test"
+  className="kbnFieldIcon"
+  color="euiColorVis0"
+  fill="none"
+  iconType="tokenNumber"
+  shape="circle"
+  size="l"
+  title="test"
 />
 `;
diff --git a/src/plugins/kibana_react/public/field_icon/field_icon.test.tsx b/src/plugins/kibana_react/public/field_icon/field_icon.test.tsx
index 90a858e31b4f3..51ff5f603ea37 100644
--- a/src/plugins/kibana_react/public/field_icon/field_icon.test.tsx
+++ b/src/plugins/kibana_react/public/field_icon/field_icon.test.tsx
@@ -18,24 +18,44 @@
  */
 import React from 'react';
 import { shallow } from 'enzyme';
-import { FieldIcon } from './field_icon';
+import { FieldIcon, typeToEuiIconMap } from './field_icon';
 
-test('FieldIcon renders a blackwhite icon for a string', () => {
-  const component = shallow(<FieldIcon type="string" />);
+const availableTypes = Object.keys(typeToEuiIconMap);
+
+describe('FieldIcon renders known field types', () => {
+  availableTypes.forEach(type => {
+    test(`${type} is rendered`, () => {
+      const component = shallow(<FieldIcon type={type} />);
+      expect(component).toMatchSnapshot();
+    });
+  });
+});
+
+test('FieldIcon renders an icon for an unknown type', () => {
+  const component = shallow(<FieldIcon type="sdfsdf" label="test" />);
   expect(component).toMatchSnapshot();
 });
 
-test('FieldIcon renders a colored icon for a number', () => {
-  const component = shallow(<FieldIcon type="number" label="test" useColor />);
+test('FieldIcon supports same props as EuiToken', () => {
+  const component = shallow(
+    <FieldIcon
+      type="number"
+      label="test"
+      color="euiColorVis0"
+      size="l"
+      shape="circle"
+      fill="none"
+    />
+  );
   expect(component).toMatchSnapshot();
 });
 
-test('FieldIcon renders an icon for an unknown type', () => {
-  const component = shallow(<FieldIcon type="sdfsdf" label="test" useColor />);
+test('FieldIcon changes fill when scripted is true', () => {
+  const component = shallow(<FieldIcon type="number" label="test" scripted={true} />);
   expect(component).toMatchSnapshot();
 });
 
 test('FieldIcon renders with className if provided', () => {
-  const component = shallow(<FieldIcon type="sdfsdf" label="test" className="myClass" useColor />);
+  const component = shallow(<FieldIcon type="sdfsdf" label="test" className="myClass" />);
   expect(component).toMatchSnapshot();
 });
diff --git a/src/plugins/kibana_react/public/field_icon/field_icon.tsx b/src/plugins/kibana_react/public/field_icon/field_icon.tsx
index 2e199a7471a64..2da1eba31e254 100644
--- a/src/plugins/kibana_react/public/field_icon/field_icon.tsx
+++ b/src/plugins/kibana_react/public/field_icon/field_icon.tsx
@@ -17,14 +17,10 @@
  * under the License.
  */
 import React from 'react';
-import { euiPaletteColorBlind, EuiIcon } from '@elastic/eui';
-import { IconSize } from '@elastic/eui/src/components/icon/icon';
+import classNames from 'classnames';
+import { EuiToken, EuiTokenProps } from '@elastic/eui';
 
-interface IconMapEntry {
-  icon: string;
-  color: string;
-}
-interface FieldIconProps {
+export interface FieldIconProps extends Omit<EuiTokenProps, 'iconType'> {
   type:
     | 'boolean'
     | 'conflict'
@@ -39,51 +35,50 @@ interface FieldIconProps {
     | string
     | 'nested';
   label?: string;
-  size?: IconSize;
-  useColor?: boolean;
-  className?: string;
+  scripted?: boolean;
 }
 
-const colors = euiPaletteColorBlind();
-
 // defaultIcon => a unknown datatype
-const defaultIcon = { icon: 'questionInCircle', color: colors[0] };
+const defaultIcon = { iconType: 'questionInCircle', color: 'gray' };
 
-export const typeToEuiIconMap: Partial<Record<string, IconMapEntry>> = {
-  boolean: { icon: 'invert', color: colors[5] },
+export const typeToEuiIconMap: Partial<Record<string, EuiTokenProps>> = {
+  boolean: { iconType: 'tokenBoolean' },
   // icon for an index pattern mapping conflict in discover
-  conflict: { icon: 'alert', color: colors[8] },
-  date: { icon: 'calendar', color: colors[7] },
-  geo_point: { icon: 'globe', color: colors[2] },
-  geo_shape: { icon: 'globe', color: colors[2] },
-  ip: { icon: 'storage', color: colors[8] },
+  conflict: { iconType: 'alert', color: 'euiVisColor9' },
+  date: { iconType: 'tokenDate' },
+  geo_point: { iconType: 'tokenGeo' },
+  geo_shape: { iconType: 'tokenGeo' },
+  ip: { iconType: 'tokenIP' },
   // is a plugin's data type https://www.elastic.co/guide/en/elasticsearch/plugins/current/mapper-murmur3-usage.html
-  murmur3: { icon: 'document', color: colors[1] },
-  number: { icon: 'number', color: colors[0] },
-  _source: { icon: 'editorCodeBlock', color: colors[3] },
-  string: { icon: 'string', color: colors[4] },
-  nested: { icon: 'nested', color: colors[2] },
+  murmur3: { iconType: 'tokenFile' },
+  number: { iconType: 'tokenNumber' },
+  _source: { iconType: 'editorCodeBlock', color: 'gray' },
+  string: { iconType: 'tokenString' },
+  nested: { iconType: 'tokenNested' },
 };
 
 /**
- * Field icon used across the app
+ * Field token icon used across the app
  */
 export function FieldIcon({
   type,
   label,
   size = 's',
-  useColor = false,
-  className = undefined,
+  scripted,
+  className,
+  ...rest
 }: FieldIconProps) {
-  const euiIcon = typeToEuiIconMap[type] || defaultIcon;
+  const token = typeToEuiIconMap[type] || defaultIcon;
 
   return (
-    <EuiIcon
-      type={euiIcon.icon}
+    <EuiToken
+      {...token}
+      className={classNames('kbnFieldIcon', className)}
       aria-label={label || type}
-      size={size as IconSize}
-      color={useColor || type === 'conflict' ? euiIcon.color : undefined}
-      className={className}
+      title={label || type}
+      size={size as EuiTokenProps['size']}
+      fill={scripted ? 'dark' : undefined}
+      {...rest}
     />
   );
 }
diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/field_editor.tsx b/x-pack/legacy/plugins/graph/public/components/field_manager/field_editor.tsx
index 6152f33350917..f2a4c28afcdae 100644
--- a/x-pack/legacy/plugins/graph/public/components/field_manager/field_editor.tsx
+++ b/x-pack/legacy/plugins/graph/public/components/field_manager/field_editor.tsx
@@ -237,10 +237,18 @@ export function FieldEditor({
                     renderOption={(option, searchValue, contentClassName) => {
                       const { type, label } = option;
                       return (
-                        <span className={contentClassName}>
-                          <FieldIcon type={type!} size="m" useColor />{' '}
-                          <EuiHighlight search={searchValue}>{label}</EuiHighlight>
-                        </span>
+                        <EuiFlexGroup
+                          className={contentClassName}
+                          gutterSize="s"
+                          alignItems="center"
+                        >
+                          <EuiFlexItem grow={null}>
+                            <FieldIcon type={type!} fill="none" />
+                          </EuiFlexItem>
+                          <EuiFlexItem>
+                            <EuiHighlight search={searchValue}>{label}</EuiHighlight>
+                          </EuiFlexItem>
+                        </EuiFlexGroup>
                       );
                     }}
                     compressed
diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/field_icon.tsx b/x-pack/legacy/plugins/graph/public/components/field_manager/field_icon.tsx
deleted file mode 100644
index 0c099135f631d..0000000000000
--- a/x-pack/legacy/plugins/graph/public/components/field_manager/field_icon.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React from 'react';
-import { ICON_TYPES, euiPaletteColorBlind, EuiIcon } from '@elastic/eui';
-
-function stringToNum(s: string) {
-  return Array.from(s).reduce((acc, ch) => acc + ch.charCodeAt(0), 1);
-}
-
-function getIconForDataType(dataType: string) {
-  const icons: Partial<Record<string, UnwrapArray<typeof ICON_TYPES>>> = {
-    boolean: 'invert',
-    date: 'calendar',
-    geo_point: 'globe',
-    ip: 'storage',
-  };
-  return icons[dataType] || ICON_TYPES.find(t => t === dataType) || 'document';
-}
-
-export function getColorForDataType(type: string) {
-  const iconType = getIconForDataType(type);
-  const colors = euiPaletteColorBlind();
-  const colorIndex = stringToNum(iconType) % colors.length;
-  return colors[colorIndex];
-}
-
-export type UnwrapArray<T> = T extends Array<infer P> ? P : T;
-
-export function FieldIcon({ type }: { type: string }) {
-  const iconType = getIconForDataType(type);
-
-  return <EuiIcon type={iconType} color={getColorForDataType(type)} />;
-}
diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/field_picker.tsx b/x-pack/legacy/plugins/graph/public/components/field_manager/field_picker.tsx
index b38e3f8430980..30f1fcffd4f67 100644
--- a/x-pack/legacy/plugins/graph/public/components/field_manager/field_picker.tsx
+++ b/x-pack/legacy/plugins/graph/public/components/field_manager/field_picker.tsx
@@ -122,7 +122,7 @@ function toOptions(
       .filter(field => isExplorable(field) || field.selected)
       .map(field => ({
         label: field.name,
-        prepend: <FieldIcon type={field.type} size="m" useColor />,
+        prepend: <FieldIcon className="eui-alignMiddle" type={field.type} fill="none" />,
         checked: field.selected ? 'on' : undefined,
       }))
   );
diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/__snapshots__/lens_field_icon.test.tsx.snap b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/__snapshots__/lens_field_icon.test.tsx.snap
index 5593a1af00d70..8bbe49b2e0d7f 100644
--- a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/__snapshots__/lens_field_icon.test.tsx.snap
+++ b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/__snapshots__/lens_field_icon.test.tsx.snap
@@ -1,10 +1,16 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`LensFieldIcon accepts FieldIcon props 1`] = `
+<FieldIcon
+  className="lnsFieldListPanel__fieldIcon"
+  fill="none"
+  type="date"
+/>
+`;
+
 exports[`LensFieldIcon renders properly 1`] = `
 <FieldIcon
   className="lnsFieldListPanel__fieldIcon"
-  size="m"
   type="date"
-  useColor={true}
 />
 `;
diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/_datapanel.scss b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/_datapanel.scss
index ed39beeb7d088..77d4b41a0413c 100644
--- a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/_datapanel.scss
+++ b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/_datapanel.scss
@@ -52,3 +52,16 @@
   @include euiFormControlLayoutPadding(1, 'right');
   @include euiFormControlLayoutPadding(1, 'left');
 }
+
+.lnsInnerIndexPatternDataPanel__filterType {
+  padding: $euiSizeS;
+}
+
+.lnsInnerIndexPatternDataPanel__filterTypeInner {
+  display: flex;
+  align-items: center;
+
+  .lnsFieldListPanel__fieldIcon {
+    margin-right: $euiSizeS;
+  }
+}
diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/_field_item.scss b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/_field_item.scss
index 54f9a3787466d..89f6bbf908419 100644
--- a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/_field_item.scss
+++ b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/_field_item.scss
@@ -14,7 +14,7 @@
 }
 
 .lnsFieldItem--missing {
-  background: lightOrDarkTheme(transparentize($euiColorMediumShade, .9), $euiColorEmptyShade);
+  background: lightOrDarkTheme(transparentize($euiColorMediumShade, 0.9), $euiColorEmptyShade);
   color: $euiColorDarkShade;
 }
 
@@ -24,10 +24,10 @@
   display: flex;
   align-items: flex-start;
   transition: box-shadow $euiAnimSpeedFast $euiAnimSlightResistance,
-              background-color $euiAnimSpeedFast $euiAnimSlightResistance; // sass-lint:disable-line indentation
+    background-color $euiAnimSpeedFast $euiAnimSlightResistance; // sass-lint:disable-line indentation
 
   .lnsFieldItem__name {
-    margin-left: $euiSizeXS;
+    margin-left: $euiSizeS;
     flex-grow: 1;
   }
 
@@ -37,7 +37,8 @@
   }
 
   .lnsFieldListPanel__fieldIcon {
-    margin-top: 2px;
+    margin-top: $euiSizeXS / 2;
+    margin-right: $euiSizeXS / 2;
   }
 
   .lnsFieldItem__infoIcon {
diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/datapanel.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/datapanel.tsx
index 3231ab7d7ff12..69982aed78b40 100644
--- a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/datapanel.tsx
+++ b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/datapanel.tsx
@@ -384,6 +384,7 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({
                 data-test-subj="lnsIndexPatternTypeFilterOptions"
                 items={(availableFieldTypes as DataType[]).map(type => (
                   <EuiContextMenuItem
+                    className="lnsInnerIndexPatternDataPanel__filterType"
                     key={type}
                     icon={localState.typeFilter.includes(type) ? 'check' : 'empty'}
                     data-test-subj={`typeFilter-${type}`}
@@ -397,7 +398,9 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({
                       }));
                     }}
                   >
-                    <LensFieldIcon type={type} /> {fieldTypeNames[type]}
+                    <span className="lnsInnerIndexPatternDataPanel__filterTypeInner">
+                      <LensFieldIcon type={type} /> {fieldTypeNames[type]}
+                    </span>
                   </EuiContextMenuItem>
                 ))}
               />
diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx
index 46d7233ba9587..77435fcdf3eed 100644
--- a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx
+++ b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx
@@ -169,6 +169,7 @@ export function FieldSelect({
             <EuiFlexItem grow={null}>
               <LensFieldIcon
                 type={((option.value as unknown) as { dataType: DataType }).dataType}
+                fill="none"
               />
             </EuiFlexItem>
             <EuiFlexItem>
diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/field_icon.test.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/field_icon.test.tsx
deleted file mode 100644
index 6b12bb5feef1b..0000000000000
--- a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/field_icon.test.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-/*
- * 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 { shallow } from 'enzyme';
-import React from 'react';
-import { FieldIcon } from './field_icon';
-
-describe('FieldIcon', () => {
-  it('should render icons', () => {
-    expect(shallow(<FieldIcon type="boolean" />)).toMatchInlineSnapshot(`
-      <EuiIcon
-        className="lnsFieldListPanel__fieldIcon lnsFieldListPanel__fieldIcon--boolean"
-        color="#D6BF57"
-        type="invert"
-      />
-    `);
-    expect(shallow(<FieldIcon type="date" />)).toMatchInlineSnapshot(`
-      <EuiIcon
-        className="lnsFieldListPanel__fieldIcon lnsFieldListPanel__fieldIcon--date"
-        color="#DA8B45"
-        type="calendar"
-      />
-    `);
-    expect(shallow(<FieldIcon type="number" />)).toMatchInlineSnapshot(`
-      <EuiIcon
-        className="lnsFieldListPanel__fieldIcon lnsFieldListPanel__fieldIcon--number"
-        color="#54B399"
-        type="number"
-      />
-    `);
-    expect(shallow(<FieldIcon type="string" />)).toMatchInlineSnapshot(`
-      <EuiIcon
-        className="lnsFieldListPanel__fieldIcon lnsFieldListPanel__fieldIcon--string"
-        color="#CA8EAE"
-        type="string"
-      />
-    `);
-    expect(shallow(<FieldIcon type="ip" />)).toMatchInlineSnapshot(`
-      <EuiIcon
-        className="lnsFieldListPanel__fieldIcon lnsFieldListPanel__fieldIcon--ip"
-        color="#AA6556"
-        type="ip"
-      />
-    `);
-  });
-});
diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/field_icon.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/field_icon.tsx
deleted file mode 100644
index 796f200bffd97..0000000000000
--- a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/field_icon.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React from 'react';
-import { ICON_TYPES, euiPaletteColorBlind, EuiIcon } from '@elastic/eui';
-import classNames from 'classnames';
-import { DataType } from '../types';
-
-function stringToNum(s: string) {
-  return Array.from(s).reduce((acc, ch) => acc + ch.charCodeAt(0), 1);
-}
-
-function getIconForDataType(dataType: string) {
-  const icons: Partial<Record<string, UnwrapArray<typeof ICON_TYPES>>> = {
-    boolean: 'invert',
-    date: 'calendar',
-    ip: 'ip',
-  };
-  return icons[dataType] || ICON_TYPES.find(t => t === dataType) || 'empty';
-}
-
-export function getColorForDataType(type: string) {
-  const iconType = getIconForDataType(type);
-  const colors = euiPaletteColorBlind();
-  const colorIndex = stringToNum(iconType) % colors.length;
-  return colors[colorIndex];
-}
-
-export type UnwrapArray<T> = T extends Array<infer P> ? P : T;
-
-export function FieldIcon({ type }: { type: DataType }) {
-  const iconType = getIconForDataType(type);
-
-  const classes = classNames(
-    'lnsFieldListPanel__fieldIcon',
-    `lnsFieldListPanel__fieldIcon--${type}`
-  );
-
-  return <EuiIcon type={iconType} color={getColorForDataType(type)} className={classes} />;
-}
diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/field_item.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/field_item.tsx
index 0271d2ca021c5..94d644e6590e1 100644
--- a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/field_item.tsx
+++ b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/field_item.tsx
@@ -46,7 +46,7 @@ import { DragDrop } from '../drag_drop';
 import { DatasourceDataPanelProps, DataType } from '../types';
 import { BucketedAggregation, FieldStatsResponse } from '../../../../../plugins/lens/common';
 import { IndexPattern, IndexPatternField } from './types';
-import { getColorForDataType, LensFieldIcon } from './lens_field_icon';
+import { LensFieldIcon } from './lens_field_icon';
 import { trackUiEvent } from '../lens_ui_telemetry';
 
 export interface FieldItemProps {
@@ -294,11 +294,6 @@ function FieldItemPopoverContents(props: State & FieldItemProps) {
     );
   }
 
-  const euiButtonColor =
-    field.type === 'string' ? 'accent' : field.type === 'number' ? 'secondary' : 'primary';
-  const euiTextColor =
-    field.type === 'string' ? 'accent' : field.type === 'number' ? 'secondary' : 'default';
-
   const fromDate = DateMath.parse(dateRange.fromDate);
   const toDate = DateMath.parse(dateRange.toDate);
 
@@ -391,8 +386,6 @@ function FieldItemPopoverContents(props: State & FieldItemProps) {
     const specId = i18n.translate('xpack.lens.indexPattern.fieldStatsCountLabel', {
       defaultMessage: 'Count',
     });
-    const expectedColor = getColorForDataType(field.type);
-    const seriesColors = expectedColor ? [expectedColor] : undefined;
 
     if (field.type === 'date') {
       return wrapInPopover(
@@ -429,7 +422,6 @@ function FieldItemPopoverContents(props: State & FieldItemProps) {
             yAccessors={['count']}
             xScaleType={ScaleType.Time}
             yScaleType={ScaleType.Linear}
-            customSeriesColors={seriesColors}
             timeZone="local"
           />
         </Chart>
@@ -453,7 +445,6 @@ function FieldItemPopoverContents(props: State & FieldItemProps) {
             yAccessors={['count']}
             xScaleType={ScaleType.Linear}
             yScaleType={ScaleType.Linear}
-            customSeriesColors={seriesColors}
           />
         </Chart>
       );
@@ -486,7 +477,7 @@ function FieldItemPopoverContents(props: State & FieldItemProps) {
                   )}
                 </EuiFlexItem>
                 <EuiFlexItem grow={false}>
-                  <EuiText size="xs" textAlign="left" color={euiTextColor}>
+                  <EuiText size="xs" textAlign="left" color="accent">
                     {Math.round((topValue.count / props.sampledValues!) * 100)}%
                   </EuiText>
                 </EuiFlexItem>
@@ -497,7 +488,7 @@ function FieldItemPopoverContents(props: State & FieldItemProps) {
                 value={topValue.count / props.sampledValues!}
                 max={1}
                 size="s"
-                color={euiButtonColor}
+                color="accent"
               />
             </div>
           );
diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/lens_field_icon.test.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/lens_field_icon.test.tsx
index 961e22380bdca..317ce8f032f94 100644
--- a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/lens_field_icon.test.tsx
+++ b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/lens_field_icon.test.tsx
@@ -11,19 +11,14 @@
  */
 import React from 'react';
 import { shallow } from 'enzyme';
-import { LensFieldIcon, getColorForDataType } from './lens_field_icon';
+import { LensFieldIcon } from './lens_field_icon';
 
 test('LensFieldIcon renders properly', () => {
   const component = shallow(<LensFieldIcon type={'date'} />);
   expect(component).toMatchSnapshot();
 });
 
-test('LensFieldIcon getColorForDataType for a valid type', () => {
-  const color = getColorForDataType('date');
-  expect(color).toEqual('#DA8B45');
-});
-
-test('LensFieldIcon getColorForDataType for an invalid type', () => {
-  const color = getColorForDataType('invalid');
-  expect(color).toEqual('#54B399');
+test('LensFieldIcon accepts FieldIcon props', () => {
+  const component = shallow(<LensFieldIcon type={'date'} fill={'none'} />);
+  expect(component).toMatchSnapshot();
 });
diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/lens_field_icon.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/lens_field_icon.tsx
index 2e6a5fcd8115f..06eda73748cef 100644
--- a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/lens_field_icon.tsx
+++ b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/lens_field_icon.tsx
@@ -5,26 +5,16 @@
  */
 
 import React from 'react';
-import { euiPaletteColorBlind } from '@elastic/eui';
-import { FieldIcon, typeToEuiIconMap } from '../../../../../../src/plugins/kibana_react/public';
+import { FieldIcon, FieldIconProps } from '../../../../../../src/plugins/kibana_react/public';
 import { DataType } from '../types';
 import { normalizeOperationDataType } from './utils';
 
-export function getColorForDataType(type: string) {
-  const iconMap = typeToEuiIconMap[normalizeOperationDataType(type as DataType)];
-  if (iconMap) {
-    return iconMap.color;
-  }
-  return euiPaletteColorBlind()[0];
-}
-
-export function LensFieldIcon({ type }: { type: DataType }) {
+export function LensFieldIcon({ type, ...rest }: FieldIconProps & { type: DataType }) {
   return (
     <FieldIcon
-      type={normalizeOperationDataType(type)}
       className="lnsFieldListPanel__fieldIcon"
-      size="m"
-      useColor
+      type={normalizeOperationDataType(type)}
+      {...rest}
     />
   );
 }
diff --git a/x-pack/legacy/plugins/maps/public/components/__snapshots__/add_tooltip_field_popover.test.js.snap b/x-pack/legacy/plugins/maps/public/components/__snapshots__/add_tooltip_field_popover.test.js.snap
index f37dfdd879c5b..d0cdbe7243abe 100644
--- a/x-pack/legacy/plugins/maps/public/components/__snapshots__/add_tooltip_field_popover.test.js.snap
+++ b/x-pack/legacy/plugins/maps/public/components/__snapshots__/add_tooltip_field_popover.test.js.snap
@@ -23,7 +23,7 @@ exports[`Should remove selected fields from selectable 1`] = `
   id="addTooltipFieldPopover"
   isOpen={false}
   ownFocus={true}
-  panelPaddingSize="m"
+  panelPaddingSize="none"
 >
   <EuiSelectable
     onChange={[Function]}
@@ -32,14 +32,19 @@ exports[`Should remove selected fields from selectable 1`] = `
         Object {
           "label": "@timestamp",
           "prepend": <FieldIcon
-            size="m"
+            className="eui-alignMiddle"
+            fill="none"
             type="date"
-            useColor={true}
           />,
           "value": "@timestamp",
         },
       ]
     }
+    searchProps={
+      Object {
+        "compressed": true,
+      }
+    }
     searchable={true}
     singleSelection={false}
   >
@@ -88,7 +93,7 @@ exports[`Should render 1`] = `
   id="addTooltipFieldPopover"
   isOpen={false}
   ownFocus={true}
-  panelPaddingSize="m"
+  panelPaddingSize="none"
 >
   <EuiSelectable
     onChange={[Function]}
@@ -97,32 +102,37 @@ exports[`Should render 1`] = `
         Object {
           "label": "@timestamp",
           "prepend": <FieldIcon
-            size="m"
+            className="eui-alignMiddle"
+            fill="none"
             type="date"
-            useColor={true}
           />,
           "value": "@timestamp",
         },
         Object {
           "label": "custom label for prop1",
           "prepend": <FieldIcon
-            size="m"
+            className="eui-alignMiddle"
+            fill="none"
             type="string"
-            useColor={true}
           />,
           "value": "prop1",
         },
         Object {
           "label": "prop2",
           "prepend": <FieldIcon
-            size="m"
+            className="eui-alignMiddle"
+            fill="none"
             type="string"
-            useColor={true}
           />,
           "value": "prop2",
         },
       ]
     }
+    searchProps={
+      Object {
+        "compressed": true,
+      }
+    }
     searchable={true}
     singleSelection={false}
   >
diff --git a/x-pack/legacy/plugins/maps/public/components/add_tooltip_field_popover.js b/x-pack/legacy/plugins/maps/public/components/add_tooltip_field_popover.js
index bddb74596f4ef..07bc54663c1d8 100644
--- a/x-pack/legacy/plugins/maps/public/components/add_tooltip_field_popover.js
+++ b/x-pack/legacy/plugins/maps/public/components/add_tooltip_field_popover.js
@@ -39,7 +39,10 @@ function getOptions(fields, selectedFields) {
     .map(field => {
       return {
         value: field.name,
-        prepend: 'type' in field ? <FieldIcon type={field.type} size="m" useColor /> : null,
+        prepend:
+          'type' in field ? (
+            <FieldIcon className="eui-alignMiddle" type={field.type} fill="none" />
+          ) : null,
         label: 'label' in field ? field.label : field.name,
       };
     })
@@ -127,7 +130,12 @@ export class AddTooltipFieldPopover extends Component {
 
     return (
       <Fragment>
-        <EuiSelectable searchable options={this.state.options} onChange={this._onSelect}>
+        <EuiSelectable
+          searchable
+          searchProps={{ compressed: true }}
+          options={this.state.options}
+          onChange={this._onSelect}
+        >
           {(list, search) => (
             <div style={{ width: '300px' }}>
               <EuiPopoverTitle>{search}</EuiPopoverTitle>
@@ -161,6 +169,7 @@ export class AddTooltipFieldPopover extends Component {
         button={this._renderAddButton()}
         isOpen={this.state.isPopoverOpen}
         closePopover={this._closePopover}
+        panelPaddingSize="none"
         ownFocus
       >
         {this._renderContent()}
diff --git a/x-pack/legacy/plugins/maps/public/components/single_field_select.js b/x-pack/legacy/plugins/maps/public/components/single_field_select.js
index 7351ce7691a82..98e33454b041b 100644
--- a/x-pack/legacy/plugins/maps/public/components/single_field_select.js
+++ b/x-pack/legacy/plugins/maps/public/components/single_field_select.js
@@ -8,7 +8,7 @@ import _ from 'lodash';
 import PropTypes from 'prop-types';
 import React from 'react';
 
-import { EuiComboBox, EuiHighlight } from '@elastic/eui';
+import { EuiComboBox, EuiHighlight, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
 import { FieldIcon } from '../../../../../../src/plugins/kibana_react/public';
 
 function fieldsToOptions(fields) {
@@ -30,11 +30,14 @@ function fieldsToOptions(fields) {
 
 function renderOption(option, searchValue, contentClassName) {
   return (
-    <span className={contentClassName}>
-      <FieldIcon type={option.value.type} size="m" useColor />
-      &nbsp;
-      <EuiHighlight search={searchValue}>{option.label}</EuiHighlight>
-    </span>
+    <EuiFlexGroup className={contentClassName} gutterSize="s" alignItems="center">
+      <EuiFlexItem grow={null}>
+        <FieldIcon type={option.value.type} fill="none" />
+      </EuiFlexItem>
+      <EuiFlexItem>
+        <EuiHighlight search={searchValue}>{option.label}</EuiHighlight>
+      </EuiFlexItem>
+    </EuiFlexGroup>
   );
 }
 
diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/field_select.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/field_select.js
index a32c2ce04d735..cf0ec5589d6bc 100644
--- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/field_select.js
+++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/field_select.js
@@ -7,18 +7,21 @@
 import PropTypes from 'prop-types';
 import React from 'react';
 
-import { EuiComboBox, EuiHighlight } from '@elastic/eui';
+import { EuiComboBox, EuiHighlight, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
 import { FIELD_ORIGIN } from '../../../../../common/constants';
 import { i18n } from '@kbn/i18n';
 import { FieldIcon } from '../../../../../../../../../src/plugins/kibana_react/public';
 
 function renderOption(option, searchValue, contentClassName) {
   return (
-    <span className={contentClassName}>
-      <FieldIcon type={option.value.type} size="m" useColor />
-      &nbsp;
-      <EuiHighlight search={searchValue}>{option.label}</EuiHighlight>
-    </span>
+    <EuiFlexGroup className={contentClassName} gutterSize="s" alignItems="center">
+      <EuiFlexItem grow={null}>
+        <FieldIcon type={option.value.type} fill="none" />
+      </EuiFlexItem>
+      <EuiFlexItem>
+        <EuiHighlight search={searchValue}>{option.label}</EuiHighlight>
+      </EuiFlexItem>
+    </EuiFlexGroup>
   );
 }
 

From 9c8c47befb177ba9fd5e4a102ae363140e2668a7 Mon Sep 17 00:00:00 2001
From: Aaron Caldwell <aaron.caldwell@elastic.co>
Date: Fri, 21 Feb 2020 16:37:39 -0700
Subject: [PATCH 137/174] [File upload] Update remaining File Upload
 dependencies for NP migration (#58128)

* Remove old route ref, no longer used

* Use core elasticsearch service

* Remove getSavedObjectsRepository, use NP internalRepository

* Update tests and clean up

* Remove unused test vars
---
 x-pack/legacy/plugins/file_upload/index.js    | 22 ++++-------
 .../call_with_internal_user_factory.d.ts      |  7 ----
 .../client/call_with_internal_user_factory.js | 18 ---------
 .../call_with_internal_user_factory.test.ts   | 22 -----------
 .../client/call_with_request_factory.js       | 15 ++++---
 .../server/kibana_server_services.js          | 18 +++++++++
 .../plugins/file_upload/server/plugin.js      | 22 ++++++-----
 .../file_upload/server/routes/file_upload.js  | 10 ++---
 .../telemetry/file_upload_usage_collector.ts  | 12 +-----
 .../server/telemetry/telemetry.test.ts        | 10 +----
 .../file_upload/server/telemetry/telemetry.ts | 39 ++++---------------
 11 files changed, 63 insertions(+), 132 deletions(-)
 delete mode 100644 x-pack/legacy/plugins/file_upload/server/client/call_with_internal_user_factory.d.ts
 delete mode 100644 x-pack/legacy/plugins/file_upload/server/client/call_with_internal_user_factory.js
 delete mode 100644 x-pack/legacy/plugins/file_upload/server/client/call_with_internal_user_factory.test.ts
 create mode 100644 x-pack/legacy/plugins/file_upload/server/kibana_server_services.js

diff --git a/x-pack/legacy/plugins/file_upload/index.js b/x-pack/legacy/plugins/file_upload/index.js
index d29226c802b06..23e1e1d98aa7f 100644
--- a/x-pack/legacy/plugins/file_upload/index.js
+++ b/x-pack/legacy/plugins/file_upload/index.js
@@ -8,9 +8,10 @@ import { mappings } from './mappings';
 
 export const fileUpload = kibana => {
   return new kibana.Plugin({
-    require: ['elasticsearch', 'xpack_main'],
+    require: ['elasticsearch'],
     name: 'file_upload',
     id: 'file_upload',
+    // TODO: uiExports and savedObjectSchemas to be removed on migration
     uiExports: {
       mappings,
     },
@@ -22,23 +23,14 @@ export const fileUpload = kibana => {
 
     init(server) {
       const coreSetup = server.newPlatform.setup.core;
+      const coreStart = server.newPlatform.start.core;
       const { usageCollection } = server.newPlatform.setup.plugins;
-      const pluginsSetup = {
+      const pluginsStart = {
         usageCollection,
       };
-
-      // legacy dependencies
-      const __LEGACY = {
-        route: server.route.bind(server),
-        plugins: {
-          elasticsearch: server.plugins.elasticsearch,
-        },
-        savedObjects: {
-          getSavedObjectsRepository: server.savedObjects.getSavedObjectsRepository,
-        },
-      };
-
-      new FileUploadPlugin().setup(coreSetup, pluginsSetup, __LEGACY);
+      const fileUploadPlugin = new FileUploadPlugin();
+      fileUploadPlugin.setup(coreSetup);
+      fileUploadPlugin.start(coreStart, pluginsStart);
     },
   });
 };
diff --git a/x-pack/legacy/plugins/file_upload/server/client/call_with_internal_user_factory.d.ts b/x-pack/legacy/plugins/file_upload/server/client/call_with_internal_user_factory.d.ts
deleted file mode 100644
index 9c1000db8cb56..0000000000000
--- a/x-pack/legacy/plugins/file_upload/server/client/call_with_internal_user_factory.d.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-export function callWithInternalUserFactory(elasticsearchPlugin: any): any;
diff --git a/x-pack/legacy/plugins/file_upload/server/client/call_with_internal_user_factory.js b/x-pack/legacy/plugins/file_upload/server/client/call_with_internal_user_factory.js
deleted file mode 100644
index 2e5431bdd6ce2..0000000000000
--- a/x-pack/legacy/plugins/file_upload/server/client/call_with_internal_user_factory.js
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { once } from 'lodash';
-
-const _callWithInternalUser = once(elasticsearchPlugin => {
-  const { callWithInternalUser } = elasticsearchPlugin.getCluster('admin');
-  return callWithInternalUser;
-});
-
-export const callWithInternalUserFactory = elasticsearchPlugin => {
-  return (...args) => {
-    return _callWithInternalUser(elasticsearchPlugin)(...args);
-  };
-};
diff --git a/x-pack/legacy/plugins/file_upload/server/client/call_with_internal_user_factory.test.ts b/x-pack/legacy/plugins/file_upload/server/client/call_with_internal_user_factory.test.ts
deleted file mode 100644
index 04c5013ed8e67..0000000000000
--- a/x-pack/legacy/plugins/file_upload/server/client/call_with_internal_user_factory.test.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { callWithInternalUserFactory } from './call_with_internal_user_factory';
-
-describe('call_with_internal_user_factory', () => {
-  describe('callWithInternalUserFactory', () => {
-    it('should use internal user "admin"', () => {
-      const callWithInternalUser: any = jest.fn();
-      const elasticsearchPlugin: any = {
-        getCluster: jest.fn(() => ({ callWithInternalUser })),
-      };
-      const callWithInternalUserInstance = callWithInternalUserFactory(elasticsearchPlugin);
-      callWithInternalUserInstance();
-
-      expect(elasticsearchPlugin.getCluster).toHaveBeenCalledWith('admin');
-    });
-  });
-});
diff --git a/x-pack/legacy/plugins/file_upload/server/client/call_with_request_factory.js b/x-pack/legacy/plugins/file_upload/server/client/call_with_request_factory.js
index a0b0d2d1c7ce3..bef6c369fd9ac 100644
--- a/x-pack/legacy/plugins/file_upload/server/client/call_with_request_factory.js
+++ b/x-pack/legacy/plugins/file_upload/server/client/call_with_request_factory.js
@@ -5,14 +5,17 @@
  */
 
 import { once } from 'lodash';
+import { getDataClient } from '../kibana_server_services';
 
-const callWithRequest = once(elasticsearchPlugin => {
-  const cluster = elasticsearchPlugin.getCluster('data');
-  return cluster.callWithRequest;
-});
+const callWithRequest = once(() => getDataClient());
 
-export const callWithRequestFactory = (elasticsearchPlugin, request) => {
+export const callWithRequestFactory = request => {
   return (...args) => {
-    return callWithRequest(elasticsearchPlugin)(request, ...args);
+    return (
+      callWithRequest()
+        .asScoped(request)
+        // @ts-ignore
+        .callAsCurrentUser(...args)
+    );
   };
 };
diff --git a/x-pack/legacy/plugins/file_upload/server/kibana_server_services.js b/x-pack/legacy/plugins/file_upload/server/kibana_server_services.js
new file mode 100644
index 0000000000000..104e49015ba80
--- /dev/null
+++ b/x-pack/legacy/plugins/file_upload/server/kibana_server_services.js
@@ -0,0 +1,18 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+let dataClient;
+
+export const setElasticsearchClientServices = elasticsearch => {
+  ({ dataClient } = elasticsearch);
+};
+export const getDataClient = () => dataClient;
+
+let internalRepository;
+export const setInternalRepository = createInternalRepository => {
+  internalRepository = createInternalRepository();
+};
+export const getInternalRepository = () => internalRepository;
diff --git a/x-pack/legacy/plugins/file_upload/server/plugin.js b/x-pack/legacy/plugins/file_upload/server/plugin.js
index 23fb8bda897f0..c448676f813ea 100644
--- a/x-pack/legacy/plugins/file_upload/server/plugin.js
+++ b/x-pack/legacy/plugins/file_upload/server/plugin.js
@@ -5,19 +5,23 @@
  */
 
 import { initRoutes } from './routes/file_upload';
+import { setElasticsearchClientServices, setInternalRepository } from './kibana_server_services';
 import { registerFileUploadUsageCollector } from './telemetry';
 
 export class FileUploadPlugin {
-  setup(core, plugins, __LEGACY) {
-    const elasticsearchPlugin = __LEGACY.plugins.elasticsearch;
-    const getSavedObjectsRepository = __LEGACY.savedObjects.getSavedObjectsRepository;
-    const router = core.http.createRouter();
+  constructor() {
+    this.router = null;
+  }
+
+  setup(core) {
+    setElasticsearchClientServices(core.elasticsearch);
+    this.router = core.http.createRouter();
+  }
 
-    initRoutes(router, elasticsearchPlugin, getSavedObjectsRepository);
+  start(core, plugins) {
+    initRoutes(this.router, core.savedObjects.getSavedObjectsRepository);
+    setInternalRepository(core.savedObjects.createInternalRepository);
 
-    registerFileUploadUsageCollector(plugins.usageCollection, {
-      elasticsearchPlugin,
-      getSavedObjectsRepository,
-    });
+    registerFileUploadUsageCollector(plugins.usageCollection);
   }
 }
diff --git a/x-pack/legacy/plugins/file_upload/server/routes/file_upload.js b/x-pack/legacy/plugins/file_upload/server/routes/file_upload.js
index 1c27c2d7d68e9..acbc907729d95 100644
--- a/x-pack/legacy/plugins/file_upload/server/routes/file_upload.js
+++ b/x-pack/legacy/plugins/file_upload/server/routes/file_upload.js
@@ -75,7 +75,7 @@ export const idConditionalValidation = (body, boolHasId) =>
     )
     .validate(body);
 
-const finishValidationAndProcessReq = (elasticsearchPlugin, getSavedObjectsRepository) => {
+const finishValidationAndProcessReq = () => {
   return async (con, req, { ok, badRequest }) => {
     const {
       query: { id },
@@ -86,7 +86,7 @@ const finishValidationAndProcessReq = (elasticsearchPlugin, getSavedObjectsRepos
     let resp;
     try {
       const validIdReqData = idConditionalValidation(body, boolHasId);
-      const callWithRequest = callWithRequestFactory(elasticsearchPlugin, req);
+      const callWithRequest = callWithRequestFactory(req);
       const { importData: importDataFunc } = importDataProvider(callWithRequest);
 
       const { index, settings, mappings, ingestPipeline, data } = validIdReqData;
@@ -103,7 +103,7 @@ const finishValidationAndProcessReq = (elasticsearchPlugin, getSavedObjectsRepos
         resp = ok({ body: processedReq });
         // If no id's been established then this is a new index, update telemetry
         if (!boolHasId) {
-          await updateTelemetry({ elasticsearchPlugin, getSavedObjectsRepository });
+          await updateTelemetry();
         }
       } else {
         resp = badRequest(`Error processing request 1: ${processedReq.error.message}`, ['body']);
@@ -115,7 +115,7 @@ const finishValidationAndProcessReq = (elasticsearchPlugin, getSavedObjectsRepos
   };
 };
 
-export const initRoutes = (router, esPlugin, getSavedObjectsRepository) => {
+export const initRoutes = router => {
   router.post(
     {
       path: `${IMPORT_ROUTE}{id?}`,
@@ -125,6 +125,6 @@ export const initRoutes = (router, esPlugin, getSavedObjectsRepository) => {
       },
       options,
     },
-    finishValidationAndProcessReq(esPlugin, getSavedObjectsRepository)
+    finishValidationAndProcessReq()
   );
 };
diff --git a/x-pack/legacy/plugins/file_upload/server/telemetry/file_upload_usage_collector.ts b/x-pack/legacy/plugins/file_upload/server/telemetry/file_upload_usage_collector.ts
index a2b359ae11638..2c2b1183fd5bf 100644
--- a/x-pack/legacy/plugins/file_upload/server/telemetry/file_upload_usage_collector.ts
+++ b/x-pack/legacy/plugins/file_upload/server/telemetry/file_upload_usage_collector.ts
@@ -9,19 +9,11 @@ import { getTelemetry, initTelemetry } from './telemetry';
 
 const TELEMETRY_TYPE = 'fileUploadTelemetry';
 
-export function registerFileUploadUsageCollector(
-  usageCollection: UsageCollectionSetup,
-  deps: {
-    elasticsearchPlugin: any;
-    getSavedObjectsRepository: any;
-  }
-): void {
-  const { elasticsearchPlugin, getSavedObjectsRepository } = deps;
+export function registerFileUploadUsageCollector(usageCollection: UsageCollectionSetup): void {
   const fileUploadUsageCollector = usageCollection.makeUsageCollector({
     type: TELEMETRY_TYPE,
     isReady: () => true,
-    fetch: async () =>
-      (await getTelemetry(elasticsearchPlugin, getSavedObjectsRepository)) || initTelemetry(),
+    fetch: async () => (await getTelemetry()) || initTelemetry(),
   });
 
   usageCollection.registerCollector(fileUploadUsageCollector);
diff --git a/x-pack/legacy/plugins/file_upload/server/telemetry/telemetry.test.ts b/x-pack/legacy/plugins/file_upload/server/telemetry/telemetry.test.ts
index 1c785d8e7b61c..fadad307c0710 100644
--- a/x-pack/legacy/plugins/file_upload/server/telemetry/telemetry.test.ts
+++ b/x-pack/legacy/plugins/file_upload/server/telemetry/telemetry.test.ts
@@ -6,8 +6,6 @@
 
 import { getTelemetry, updateTelemetry } from './telemetry';
 
-const elasticsearchPlugin: any = null;
-const getSavedObjectsRepository: any = null;
 const internalRepository = () => ({
   get: jest.fn(() => null),
   create: jest.fn(() => ({ attributes: 'test' })),
@@ -25,7 +23,7 @@ describe('file upload plugin telemetry', () => {
   describe('getTelemetry', () => {
     it('should get existing telemetry', async () => {
       const internalRepo = mockInit();
-      await getTelemetry(elasticsearchPlugin, getSavedObjectsRepository, internalRepo);
+      await getTelemetry(internalRepo);
       expect(internalRepo.update.mock.calls.length).toBe(0);
       expect(internalRepo.get.mock.calls.length).toBe(1);
       expect(internalRepo.create.mock.calls.length).toBe(0);
@@ -40,11 +38,7 @@ describe('file upload plugin telemetry', () => {
         },
       });
 
-      await updateTelemetry({
-        elasticsearchPlugin,
-        getSavedObjectsRepository,
-        internalRepo,
-      });
+      await updateTelemetry(internalRepo);
       expect(internalRepo.update.mock.calls.length).toBe(1);
       expect(internalRepo.get.mock.calls.length).toBe(1);
       expect(internalRepo.create.mock.calls.length).toBe(0);
diff --git a/x-pack/legacy/plugins/file_upload/server/telemetry/telemetry.ts b/x-pack/legacy/plugins/file_upload/server/telemetry/telemetry.ts
index 5ffa735f4c83a..2978dec7aa68d 100644
--- a/x-pack/legacy/plugins/file_upload/server/telemetry/telemetry.ts
+++ b/x-pack/legacy/plugins/file_upload/server/telemetry/telemetry.ts
@@ -5,7 +5,8 @@
  */
 
 import _ from 'lodash';
-import { callWithInternalUserFactory } from '../client/call_with_internal_user_factory';
+// @ts-ignore
+import { getInternalRepository } from '../kibana_server_services';
 
 export const TELEMETRY_DOC_ID = 'file-upload-telemetry';
 
@@ -17,27 +18,14 @@ export interface TelemetrySavedObject {
   attributes: Telemetry;
 }
 
-export function getInternalRepository(
-  elasticsearchPlugin: any,
-  getSavedObjectsRepository: any
-): any {
-  const callWithInternalUser = callWithInternalUserFactory(elasticsearchPlugin);
-  return getSavedObjectsRepository(callWithInternalUser);
-}
-
 export function initTelemetry(): Telemetry {
   return {
     filesUploadedTotalCount: 0,
   };
 }
 
-export async function getTelemetry(
-  elasticsearchPlugin: any,
-  getSavedObjectsRepository: any,
-  internalRepo?: object
-): Promise<Telemetry> {
-  const internalRepository =
-    internalRepo || getInternalRepository(elasticsearchPlugin, getSavedObjectsRepository);
+export async function getTelemetry(internalRepo?: object): Promise<Telemetry> {
+  const internalRepository = internalRepo || getInternalRepository();
   let telemetrySavedObject;
 
   try {
@@ -49,22 +37,9 @@ export async function getTelemetry(
   return telemetrySavedObject ? telemetrySavedObject.attributes : null;
 }
 
-export async function updateTelemetry({
-  elasticsearchPlugin,
-  getSavedObjectsRepository,
-  internalRepo,
-}: {
-  elasticsearchPlugin: any;
-  getSavedObjectsRepository: any;
-  internalRepo?: any;
-}) {
-  const internalRepository =
-    internalRepo || getInternalRepository(elasticsearchPlugin, getSavedObjectsRepository);
-  let telemetry = await getTelemetry(
-    elasticsearchPlugin,
-    getSavedObjectsRepository,
-    internalRepository
-  );
+export async function updateTelemetry(internalRepo?: any) {
+  const internalRepository = internalRepo || getInternalRepository();
+  let telemetry = await getTelemetry(internalRepository);
   // Create if doesn't exist
   if (!telemetry || _.isEmpty(telemetry)) {
     const newTelemetrySavedObject = await internalRepository.create(

From 98aa1d2d4f974f72a9a5397b1b91f11509f6fb7a Mon Sep 17 00:00:00 2001
From: Steph Milovic <stephanie.milovic@elastic.co>
Date: Sat, 22 Feb 2020 07:29:44 -0700
Subject: [PATCH 138/174] [SIEM] [Case] Enable case by default. Snake to camel
 on UI (#57936)

---
 .../siem/public/components/links/index.tsx    |  5 +-
 .../components/navigation/index.test.tsx      |  4 +-
 .../siem/public/containers/case/api.ts        | 21 ++--
 .../siem/public/containers/case/types.ts      | 40 ++++++--
 .../public/containers/case/use_get_case.tsx   |  8 +-
 .../public/containers/case/use_get_cases.tsx  | 20 ++--
 .../containers/case/use_update_case.tsx       |  6 +-
 .../siem/public/containers/case/utils.ts      | 33 +++++++
 .../components/all_cases/__mock__/index.tsx   | 77 +++++++++++++++
 .../case/components/all_cases/columns.tsx     | 42 +++++---
 .../case/components/all_cases/index.test.tsx  | 97 +++++++++++++++++++
 .../pages/case/components/all_cases/index.tsx | 34 +++----
 .../components/case_view/__mock__/index.tsx   | 34 +++++++
 .../case/components/case_view/index.test.tsx  | 82 ++++++++++++++++
 .../pages/case/components/case_view/index.tsx | 35 +++++--
 .../pages/case/components/create/index.tsx    |  4 +-
 .../pages/case/components/user_list/index.tsx |  4 +-
 .../public/pages/home/home_navigations.tsx    |  2 +-
 x-pack/plugins/case/server/config.ts          |  4 +-
 x-pack/plugins/case/server/plugin.ts          |  1 +
 .../routes/api/__tests__/update_case.test.ts  | 39 +++++++-
 .../case/server/routes/api/get_all_cases.ts   |  7 +-
 .../plugins/case/server/routes/api/schema.ts  |  5 +
 .../plugins/case/server/routes/api/types.ts   | 13 +++
 .../case/server/routes/api/update_case.ts     | 71 ++++++++++++--
 .../plugins/case/server/routes/api/utils.ts   | 18 ++++
 26 files changed, 605 insertions(+), 101 deletions(-)
 create mode 100644 x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/__mock__/index.tsx
 create mode 100644 x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.test.tsx
 create mode 100644 x-pack/legacy/plugins/siem/public/pages/case/components/case_view/__mock__/index.tsx
 create mode 100644 x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.test.tsx

diff --git a/x-pack/legacy/plugins/siem/public/components/links/index.tsx b/x-pack/legacy/plugins/siem/public/components/links/index.tsx
index 4f74f9ff2f5d6..b6548e3e950ba 100644
--- a/x-pack/legacy/plugins/siem/public/components/links/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/links/index.tsx
@@ -44,7 +44,10 @@ const CaseDetailsLinkComponent: React.FC<{ children?: React.ReactNode; detailNam
   children,
   detailName,
 }) => (
-  <EuiLink href={getCaseDetailsUrl(encodeURIComponent(detailName))}>
+  <EuiLink
+    href={getCaseDetailsUrl(encodeURIComponent(detailName))}
+    data-test-subj="case-details-link"
+  >
     {children ? children : detailName}
   </EuiLink>
 );
diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx
index 8eb08bd3d62f0..e1b3951a2317d 100644
--- a/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx
@@ -67,7 +67,7 @@ describe('SIEM Navigation', () => {
         detailName: undefined,
         navTabs: {
           case: {
-            disabled: true,
+            disabled: false,
             href: '#/link-to/case',
             id: 'case',
             name: 'Case',
@@ -160,7 +160,7 @@ describe('SIEM Navigation', () => {
         filters: [],
         navTabs: {
           case: {
-            disabled: true,
+            disabled: false,
             href: '#/link-to/case',
             id: 'case',
             name: 'Case',
diff --git a/x-pack/legacy/plugins/siem/public/containers/case/api.ts b/x-pack/legacy/plugins/siem/public/containers/case/api.ts
index 830e00c70975e..bff3bfd62a85c 100644
--- a/x-pack/legacy/plugins/siem/public/containers/case/api.ts
+++ b/x-pack/legacy/plugins/siem/public/containers/case/api.ts
@@ -5,12 +5,12 @@
  */
 
 import { KibanaServices } from '../../lib/kibana';
-import { AllCases, FetchCasesProps, Case, NewCase, SortFieldCase } from './types';
-import { Direction } from '../../graphql/types';
+import { FetchCasesProps, Case, NewCase, SortFieldCase, AllCases, CaseSnake } from './types';
 import { throwIfNotOk } from '../../hooks/api/api';
 import { CASES_URL } from './constants';
+import { convertToCamelCase, convertAllCasesToCamel } from './utils';
 
-export const getCase = async (caseId: string, includeComments: boolean) => {
+export const getCase = async (caseId: string, includeComments: boolean): Promise<Case> => {
   const response = await KibanaServices.get().http.fetch(`${CASES_URL}/${caseId}`, {
     method: 'GET',
     asResponse: true,
@@ -19,7 +19,7 @@ export const getCase = async (caseId: string, includeComments: boolean) => {
     },
   });
   await throwIfNotOk(response.response);
-  return response.body!;
+  return convertToCamelCase<CaseSnake, Case>(response.body!);
 };
 
 export const getCases = async ({
@@ -31,7 +31,7 @@ export const getCases = async ({
     page: 1,
     perPage: 20,
     sortField: SortFieldCase.createdAt,
-    sortOrder: Direction.desc,
+    sortOrder: 'desc',
   },
 }: FetchCasesProps): Promise<AllCases> => {
   const tags = [...(filterOptions.tags?.map(t => `case-workflow.attributes.tags: ${t}`) ?? [])];
@@ -46,7 +46,7 @@ export const getCases = async ({
     asResponse: true,
   });
   await throwIfNotOk(response.response);
-  return response.body!;
+  return convertAllCasesToCamel(response.body!);
 };
 
 export const createCase = async (newCase: NewCase): Promise<Case> => {
@@ -56,18 +56,19 @@ export const createCase = async (newCase: NewCase): Promise<Case> => {
     body: JSON.stringify(newCase),
   });
   await throwIfNotOk(response.response);
-  return response.body!;
+  return convertToCamelCase<CaseSnake, Case>(response.body!);
 };
 
 export const updateCaseProperty = async (
   caseId: string,
-  updatedCase: Partial<Case>
+  updatedCase: Partial<Case>,
+  version: string
 ): Promise<Partial<Case>> => {
   const response = await KibanaServices.get().http.fetch(`${CASES_URL}/${caseId}`, {
     method: 'PATCH',
     asResponse: true,
-    body: JSON.stringify(updatedCase),
+    body: JSON.stringify({ case: updatedCase, version }),
   });
   await throwIfNotOk(response.response);
-  return response.body!;
+  return convertToCamelCase<Partial<CaseSnake>, Partial<Case>>(response.body!);
 };
diff --git a/x-pack/legacy/plugins/siem/public/containers/case/types.ts b/x-pack/legacy/plugins/siem/public/containers/case/types.ts
index 0f80b2327a30c..1aea0b0f50a89 100644
--- a/x-pack/legacy/plugins/siem/public/containers/case/types.ts
+++ b/x-pack/legacy/plugins/siem/public/containers/case/types.ts
@@ -4,7 +4,6 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { Direction } from '../../graphql/types';
 interface FormData {
   isNew?: boolean;
 }
@@ -15,22 +14,35 @@ export interface NewCase extends FormData {
   title: string;
 }
 
-export interface Case {
+export interface CaseSnake {
   case_id: string;
   created_at: string;
-  created_by: ElasticUser;
+  created_by: ElasticUserSnake;
   description: string;
   state: string;
   tags: string[];
   title: string;
   updated_at: string;
+  version?: string;
+}
+
+export interface Case {
+  caseId: string;
+  createdAt: string;
+  createdBy: ElasticUser;
+  description: string;
+  state: string;
+  tags: string[];
+  title: string;
+  updatedAt: string;
+  version?: string;
 }
 
 export interface QueryParams {
   page: number;
   perPage: number;
   sortField: SortFieldCase;
-  sortOrder: Direction;
+  sortOrder: 'asc' | 'desc';
 }
 
 export interface FilterOptions {
@@ -38,21 +50,33 @@ export interface FilterOptions {
   tags: string[];
 }
 
+export interface AllCasesSnake {
+  cases: CaseSnake[];
+  page: number;
+  per_page: number;
+  total: number;
+}
+
 export interface AllCases {
   cases: Case[];
   page: number;
-  per_page: number;
+  perPage: number;
   total: number;
 }
 export enum SortFieldCase {
-  createdAt = 'created_at',
+  createdAt = 'createdAt',
   state = 'state',
-  updatedAt = 'updated_at',
+  updatedAt = 'updatedAt',
+}
+
+export interface ElasticUserSnake {
+  readonly username: string;
+  readonly full_name?: string | null;
 }
 
 export interface ElasticUser {
   readonly username: string;
-  readonly full_name?: string;
+  readonly fullName?: string | null;
 }
 
 export interface FetchCasesProps {
diff --git a/x-pack/legacy/plugins/siem/public/containers/case/use_get_case.tsx b/x-pack/legacy/plugins/siem/public/containers/case/use_get_case.tsx
index 8cc961c68fdf0..bf76b69ef22d6 100644
--- a/x-pack/legacy/plugins/siem/public/containers/case/use_get_case.tsx
+++ b/x-pack/legacy/plugins/siem/public/containers/case/use_get_case.tsx
@@ -50,16 +50,16 @@ const dataFetchReducer = (state: CaseState, action: Action): CaseState => {
   }
 };
 const initialData: Case = {
-  case_id: '',
-  created_at: '',
-  created_by: {
+  caseId: '',
+  createdAt: '',
+  createdBy: {
     username: '',
   },
   description: '',
   state: '',
   tags: [],
   title: '',
-  updated_at: '',
+  updatedAt: '',
 };
 
 export const useGetCase = (caseId: string): [CaseState] => {
diff --git a/x-pack/legacy/plugins/siem/public/containers/case/use_get_cases.tsx b/x-pack/legacy/plugins/siem/public/containers/case/use_get_cases.tsx
index db9c07747ba04..4037823ccfc94 100644
--- a/x-pack/legacy/plugins/siem/public/containers/case/use_get_cases.tsx
+++ b/x-pack/legacy/plugins/siem/public/containers/case/use_get_cases.tsx
@@ -17,7 +17,6 @@ import {
 } from './constants';
 import { AllCases, SortFieldCase, FilterOptions, QueryParams } from './types';
 import { getTypedPayload } from './utils';
-import { Direction } from '../../graphql/types';
 import { errorToToaster } from '../../components/ml/api/error_to_toaster';
 import { useStateToaster } from '../../components/toasters';
 import * as i18n from './translations';
@@ -31,16 +30,9 @@ export interface UseGetCasesState {
   filterOptions: FilterOptions;
 }
 
-export interface QueryArgs {
-  page?: number;
-  perPage?: number;
-  sortField?: SortFieldCase;
-  sortOrder?: Direction;
-}
-
 export interface Action {
   type: string;
-  payload?: AllCases | QueryArgs | FilterOptions;
+  payload?: AllCases | Partial<QueryParams> | FilterOptions;
 }
 const dataFetchReducer = (state: UseGetCasesState, action: Action): UseGetCasesState => {
   switch (action.type) {
@@ -83,13 +75,13 @@ const dataFetchReducer = (state: UseGetCasesState, action: Action): UseGetCasesS
 
 const initialData: AllCases = {
   page: 0,
-  per_page: 0,
+  perPage: 0,
   total: 0,
   cases: [],
 };
 export const useGetCases = (): [
   UseGetCasesState,
-  Dispatch<SetStateAction<QueryArgs>>,
+  Dispatch<SetStateAction<Partial<QueryParams>>>,
   Dispatch<SetStateAction<FilterOptions>>
 ] => {
   const [state, dispatch] = useReducer(dataFetchReducer, {
@@ -104,11 +96,11 @@ export const useGetCases = (): [
       page: DEFAULT_TABLE_ACTIVE_PAGE,
       perPage: DEFAULT_TABLE_LIMIT,
       sortField: SortFieldCase.createdAt,
-      sortOrder: Direction.desc,
+      sortOrder: 'desc',
     },
   });
-  const [queryParams, setQueryParams] = useState(state.queryParams as QueryArgs);
-  const [filterQuery, setFilters] = useState(state.filterOptions as FilterOptions);
+  const [queryParams, setQueryParams] = useState<Partial<QueryParams>>(state.queryParams);
+  const [filterQuery, setFilters] = useState<FilterOptions>(state.filterOptions);
   const [, dispatchToaster] = useStateToaster();
 
   useEffect(() => {
diff --git a/x-pack/legacy/plugins/siem/public/containers/case/use_update_case.tsx b/x-pack/legacy/plugins/siem/public/containers/case/use_update_case.tsx
index 68592c17e58dc..62e3d87b528c0 100644
--- a/x-pack/legacy/plugins/siem/public/containers/case/use_update_case.tsx
+++ b/x-pack/legacy/plugins/siem/public/containers/case/use_update_case.tsx
@@ -96,7 +96,11 @@ export const useUpdateCase = (
     const updateData = async (updateKey: keyof Case) => {
       dispatch({ type: FETCH_INIT });
       try {
-        const response = await updateCaseProperty(caseId, { [updateKey]: state.data[updateKey] });
+        const response = await updateCaseProperty(
+          caseId,
+          { [updateKey]: state.data[updateKey] },
+          state.data.version ?? '' // saved object versions are typed as string | undefined, hope that's not true
+        );
         dispatch({ type: FETCH_SUCCESS, payload: response });
       } catch (error) {
         errorToToaster({ title: i18n.ERROR_TITLE, error, dispatchToaster });
diff --git a/x-pack/legacy/plugins/siem/public/containers/case/utils.ts b/x-pack/legacy/plugins/siem/public/containers/case/utils.ts
index 8e6eaca1a8f0c..14a3819bdfdad 100644
--- a/x-pack/legacy/plugins/siem/public/containers/case/utils.ts
+++ b/x-pack/legacy/plugins/siem/public/containers/case/utils.ts
@@ -4,4 +4,37 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+import { camelCase, isArray, isObject, set } from 'lodash';
+import { AllCases, AllCasesSnake, Case, CaseSnake } from './types';
+
 export const getTypedPayload = <T>(a: unknown): T => a as T;
+
+export const convertArrayToCamelCase = (arrayOfSnakes: unknown[]): unknown[] =>
+  arrayOfSnakes.reduce((acc: unknown[], value) => {
+    if (isArray(value)) {
+      return [...acc, convertArrayToCamelCase(value)];
+    } else if (isObject(value)) {
+      return [...acc, convertToCamelCase(value)];
+    } else {
+      return [...acc, value];
+    }
+  }, []);
+
+export const convertToCamelCase = <T, U extends {}>(snakeCase: T): U =>
+  Object.entries(snakeCase).reduce((acc, [key, value]) => {
+    if (isArray(value)) {
+      set(acc, camelCase(key), convertArrayToCamelCase(value));
+    } else if (isObject(value)) {
+      set(acc, camelCase(key), convertToCamelCase(value));
+    } else {
+      set(acc, camelCase(key), value);
+    }
+    return acc;
+  }, {} as U);
+
+export const convertAllCasesToCamel = (snakeCases: AllCasesSnake): AllCases => ({
+  cases: snakeCases.cases.map(snakeCase => convertToCamelCase<CaseSnake, Case>(snakeCase)),
+  page: snakeCases.page,
+  perPage: snakeCases.per_page,
+  total: snakeCases.total,
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/__mock__/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/__mock__/index.tsx
new file mode 100644
index 0000000000000..98a67304fcf1f
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/__mock__/index.tsx
@@ -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 { SortFieldCase } from '../../../../../containers/case/types';
+import { UseGetCasesState } from '../../../../../containers/case/use_get_cases';
+
+export const useGetCasesMockState: UseGetCasesState = {
+  data: {
+    cases: [
+      {
+        caseId: '3c4ddcc0-4e99-11ea-9290-35d05cb55c15',
+        createdAt: '2020-02-13T19:44:23.627Z',
+        createdBy: { username: 'elastic' },
+        description: 'Security banana Issue',
+        state: 'open',
+        tags: ['defacement'],
+        title: 'Another horrible breach',
+        updatedAt: '2020-02-13T19:44:23.627Z',
+      },
+      {
+        caseId: '362a5c10-4e99-11ea-9290-35d05cb55c15',
+        createdAt: '2020-02-13T19:44:13.328Z',
+        createdBy: { username: 'elastic' },
+        description: 'Security banana Issue',
+        state: 'open',
+        tags: ['phishing'],
+        title: 'Bad email',
+        updatedAt: '2020-02-13T19:44:13.328Z',
+      },
+      {
+        caseId: '34f8b9e0-4e99-11ea-9290-35d05cb55c15',
+        createdAt: '2020-02-13T19:44:11.328Z',
+        createdBy: { username: 'elastic' },
+        description: 'Security banana Issue',
+        state: 'open',
+        tags: ['phishing'],
+        title: 'Bad email',
+        updatedAt: '2020-02-13T19:44:11.328Z',
+      },
+      {
+        caseId: '31890e90-4e99-11ea-9290-35d05cb55c15',
+        createdAt: '2020-02-13T19:44:05.563Z',
+        createdBy: { username: 'elastic' },
+        description: 'Security banana Issue',
+        state: 'closed',
+        tags: ['phishing'],
+        title: 'Uh oh',
+        updatedAt: '2020-02-18T21:32:24.056Z',
+      },
+      {
+        caseId: '2f5b3210-4e99-11ea-9290-35d05cb55c15',
+        createdAt: '2020-02-13T19:44:01.901Z',
+        createdBy: { username: 'elastic' },
+        description: 'Security banana Issue',
+        state: 'open',
+        tags: ['phishing'],
+        title: 'Uh oh',
+        updatedAt: '2020-02-13T19:44:01.901Z',
+      },
+    ],
+    page: 1,
+    perPage: 5,
+    total: 10,
+  },
+  isLoading: false,
+  isError: false,
+  queryParams: {
+    page: 1,
+    perPage: 5,
+    sortField: SortFieldCase.createdAt,
+    sortOrder: 'desc',
+  },
+  filterOptions: { search: '', tags: [] },
+};
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/columns.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/columns.tsx
index 92cd16fd2000e..4c47bf605051d 100644
--- a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/columns.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/columns.tsx
@@ -14,14 +14,15 @@ import * as i18n from './translations';
 
 export type CasesColumns = EuiTableFieldDataColumnType<Case> | EuiTableComputedColumnType<Case>;
 
-const renderStringField = (field: string) => (field != null ? field : getEmptyTagValue());
+const renderStringField = (field: string, dataTestSubj: string) =>
+  field != null ? <span data-test-subj={dataTestSubj}>{field}</span> : getEmptyTagValue();
 
 export const getCasesColumns = (): CasesColumns[] => [
   {
     name: i18n.CASE_TITLE,
     render: (theCase: Case) => {
-      if (theCase.case_id != null && theCase.title != null) {
-        return <CaseDetailsLink detailName={theCase.case_id}>{theCase.title}</CaseDetailsLink>;
+      if (theCase.caseId != null && theCase.title != null) {
+        return <CaseDetailsLink detailName={theCase.caseId}>{theCase.title}</CaseDetailsLink>;
       }
       return getEmptyTagValue();
     },
@@ -34,7 +35,11 @@ export const getCasesColumns = (): CasesColumns[] => [
         return (
           <TruncatableText>
             {tags.map((tag: string, i: number) => (
-              <EuiBadge color="hollow" key={`${tag}-${i}`}>
+              <EuiBadge
+                color="hollow"
+                key={`${tag}-${i}`}
+                data-test-subj={`case-table-column-tags-${i}`}
+              >
                 {tag}
               </EuiBadge>
             ))}
@@ -46,28 +51,39 @@ export const getCasesColumns = (): CasesColumns[] => [
     truncateText: true,
   },
   {
-    field: 'created_at',
+    field: 'createdAt',
     name: i18n.CREATED_AT,
     sortable: true,
-    render: (createdAt: Case['created_at']) => {
+    render: (createdAt: Case['createdAt']) => {
       if (createdAt != null) {
-        return <FormattedRelativePreferenceDate value={createdAt} />;
+        return (
+          <FormattedRelativePreferenceDate
+            value={createdAt}
+            data-test-subj={`case-table-column-createdAt`}
+          />
+        );
       }
       return getEmptyTagValue();
     },
   },
   {
-    field: 'created_by.username',
+    field: 'createdBy.username',
     name: i18n.REPORTER,
-    render: (createdBy: Case['created_by']['username']) => renderStringField(createdBy),
+    render: (createdBy: Case['createdBy']['username']) =>
+      renderStringField(createdBy, `case-table-column-username`),
   },
   {
-    field: 'updated_at',
+    field: 'updatedAt',
     name: i18n.LAST_UPDATED,
     sortable: true,
-    render: (updatedAt: Case['updated_at']) => {
+    render: (updatedAt: Case['updatedAt']) => {
       if (updatedAt != null) {
-        return <FormattedRelativePreferenceDate value={updatedAt} />;
+        return (
+          <FormattedRelativePreferenceDate
+            value={updatedAt}
+            data-test-subj={`case-table-column-updatedAt`}
+          />
+        );
       }
       return getEmptyTagValue();
     },
@@ -76,6 +92,6 @@ export const getCasesColumns = (): CasesColumns[] => [
     field: 'state',
     name: i18n.STATE,
     sortable: true,
-    render: (state: Case['state']) => renderStringField(state),
+    render: (state: Case['state']) => renderStringField(state, `case-table-column-state`),
   },
 ];
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.test.tsx
new file mode 100644
index 0000000000000..5a87cf53142f7
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.test.tsx
@@ -0,0 +1,97 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { mount } from 'enzyme';
+import moment from 'moment-timezone';
+import { AllCases } from './';
+import { TestProviders } from '../../../../mock';
+import { useGetCasesMockState } from './__mock__';
+import * as apiHook from '../../../../containers/case/use_get_cases';
+
+describe('AllCases', () => {
+  const setQueryParams = jest.fn();
+  const setFilters = jest.fn();
+  beforeEach(() => {
+    jest.resetAllMocks();
+    jest
+      .spyOn(apiHook, 'useGetCases')
+      .mockReturnValue([useGetCasesMockState, setQueryParams, setFilters]);
+    moment.tz.setDefault('UTC');
+  });
+  it('should render AllCases', () => {
+    const wrapper = mount(
+      <TestProviders>
+        <AllCases />
+      </TestProviders>
+    );
+    expect(
+      wrapper
+        .find(`a[data-test-subj="case-details-link"]`)
+        .first()
+        .prop('href')
+    ).toEqual(`#/link-to/case/${useGetCasesMockState.data.cases[0].caseId}`);
+    expect(
+      wrapper
+        .find(`a[data-test-subj="case-details-link"]`)
+        .first()
+        .text()
+    ).toEqual(useGetCasesMockState.data.cases[0].title);
+    expect(
+      wrapper
+        .find(`[data-test-subj="case-table-column-state"]`)
+        .first()
+        .text()
+    ).toEqual(useGetCasesMockState.data.cases[0].state);
+    expect(
+      wrapper
+        .find(`span[data-test-subj="case-table-column-tags-0"]`)
+        .first()
+        .prop('title')
+    ).toEqual(useGetCasesMockState.data.cases[0].tags[0]);
+    expect(
+      wrapper
+        .find(`[data-test-subj="case-table-column-username"]`)
+        .first()
+        .text()
+    ).toEqual(useGetCasesMockState.data.cases[0].createdBy.username);
+    expect(
+      wrapper
+        .find(`[data-test-subj="case-table-column-createdAt"]`)
+        .first()
+        .prop('value')
+    ).toEqual(useGetCasesMockState.data.cases[0].createdAt);
+    expect(
+      wrapper
+        .find(`[data-test-subj="case-table-column-updatedAt"]`)
+        .first()
+        .prop('value')
+    ).toEqual(useGetCasesMockState.data.cases[0].updatedAt);
+
+    expect(
+      wrapper
+        .find(`[data-test-subj="case-table-case-count"]`)
+        .first()
+        .text()
+    ).toEqual('Showing 10 cases');
+  });
+  it('should tableHeaderSortButton AllCases', () => {
+    const wrapper = mount(
+      <TestProviders>
+        <AllCases />
+      </TestProviders>
+    );
+    wrapper
+      .find('[data-test-subj="tableHeaderCell_state_5"] [data-test-subj="tableHeaderSortButton"]')
+      .simulate('click');
+    expect(setQueryParams).toBeCalledWith({
+      page: 1,
+      perPage: 5,
+      sortField: 'state',
+      sortOrder: 'asc',
+    });
+  });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.tsx
index b1dd39c95e191..3253a036c2990 100644
--- a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.tsx
@@ -18,7 +18,6 @@ import * as i18n from './translations';
 import { getCasesColumns } from './columns';
 import { SortFieldCase, Case, FilterOptions } from '../../../../containers/case/types';
 
-import { Direction } from '../../../../graphql/types';
 import { useGetCases } from '../../../../containers/case/use_get_cases';
 import { EuiBasicTableOnChange } from '../../../detection_engine/rules/types';
 import { Panel } from '../../../../components/panel';
@@ -32,7 +31,16 @@ import {
   UtilityBarText,
 } from '../../../../components/detection_engine/utility_bar';
 import { getCreateCaseUrl } from '../../../../components/link_to';
-
+const getSortField = (field: string): SortFieldCase => {
+  if (field === SortFieldCase.createdAt) {
+    return SortFieldCase.createdAt;
+  } else if (field === SortFieldCase.state) {
+    return SortFieldCase.state;
+  } else if (field === SortFieldCase.updatedAt) {
+    return SortFieldCase.updatedAt;
+  }
+  return SortFieldCase.createdAt;
+};
 export const AllCases = React.memo(() => {
   const [
     { data, isLoading, queryParams, filterOptions },
@@ -44,24 +52,10 @@ export const AllCases = React.memo(() => {
     ({ page, sort }: EuiBasicTableOnChange) => {
       let newQueryParams = queryParams;
       if (sort) {
-        let newSort;
-        switch (sort.field) {
-          case 'state':
-            newSort = SortFieldCase.state;
-            break;
-          case 'created_at':
-            newSort = SortFieldCase.createdAt;
-            break;
-          case 'updated_at':
-            newSort = SortFieldCase.updatedAt;
-            break;
-          default:
-            newSort = SortFieldCase.createdAt;
-        }
         newQueryParams = {
           ...newQueryParams,
-          sortField: newSort,
-          sortOrder: sort.direction as Direction,
+          sortField: getSortField(sort.field),
+          sortOrder: sort.direction,
         };
       }
       if (page) {
@@ -114,7 +108,9 @@ export const AllCases = React.memo(() => {
           <UtilityBar border>
             <UtilityBarSection>
               <UtilityBarGroup>
-                <UtilityBarText>{i18n.SHOWING_CASES(data.total ?? 0)}</UtilityBarText>
+                <UtilityBarText data-test-subj="case-table-case-count">
+                  {i18n.SHOWING_CASES(data.total ?? 0)}
+                </UtilityBarText>
               </UtilityBarGroup>
             </UtilityBarSection>
           </UtilityBar>
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/__mock__/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/__mock__/index.tsx
new file mode 100644
index 0000000000000..7480c4fc4bb2a
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/__mock__/index.tsx
@@ -0,0 +1,34 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { CaseProps } from '../index';
+import { Case } from '../../../../../containers/case/types';
+
+export const caseProps: CaseProps = {
+  caseId: '3c4ddcc0-4e99-11ea-9290-35d05cb55c15',
+  initialData: {
+    caseId: '3c4ddcc0-4e99-11ea-9290-35d05cb55c15',
+    createdAt: '2020-02-13T19:44:23.627Z',
+    createdBy: { fullName: null, username: 'elastic' },
+    description: 'Security banana Issue',
+    state: 'open',
+    tags: ['defacement'],
+    title: 'Another horrible breach!!',
+    updatedAt: '2020-02-19T15:02:57.995Z',
+  },
+  isLoading: false,
+};
+
+export const data: Case = {
+  caseId: '3c4ddcc0-4e99-11ea-9290-35d05cb55c15',
+  createdAt: '2020-02-13T19:44:23.627Z',
+  createdBy: { username: 'elastic', fullName: null },
+  description: 'Security banana Issue',
+  state: 'open',
+  tags: ['defacement'],
+  title: 'Another horrible breach!!',
+  updatedAt: '2020-02-19T15:02:57.995Z',
+};
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.test.tsx
new file mode 100644
index 0000000000000..a9e694bad705d
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.test.tsx
@@ -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 React from 'react';
+import { mount } from 'enzyme';
+import { CaseComponent } from './';
+import * as apiHook from '../../../../containers/case/use_update_case';
+import { caseProps, data } from './__mock__';
+import { TestProviders } from '../../../../mock';
+
+describe('CaseView ', () => {
+  const dispatchUpdateCaseProperty = jest.fn();
+
+  beforeEach(() => {
+    jest.resetAllMocks();
+    jest.spyOn(apiHook, 'useUpdateCase').mockReturnValue([{ data }, dispatchUpdateCaseProperty]);
+  });
+
+  it('should render CaseComponent', () => {
+    const wrapper = mount(
+      <TestProviders>
+        <CaseComponent {...caseProps} />
+      </TestProviders>
+    );
+    expect(
+      wrapper
+        .find(`[data-test-subj="case-view-title"]`)
+        .first()
+        .prop('title')
+    ).toEqual(data.title);
+    expect(
+      wrapper
+        .find(`[data-test-subj="case-view-state"]`)
+        .first()
+        .text()
+    ).toEqual(data.state);
+    expect(
+      wrapper
+        .find(`[data-test-subj="case-view-tag-list"] .euiBadge__text`)
+        .first()
+        .text()
+    ).toEqual(data.tags[0]);
+    expect(
+      wrapper
+        .find(`[data-test-subj="case-view-username"]`)
+        .first()
+        .text()
+    ).toEqual(data.createdBy.username);
+    expect(
+      wrapper
+        .find(`[data-test-subj="case-view-createdAt"]`)
+        .first()
+        .prop('value')
+    ).toEqual(data.createdAt);
+    expect(
+      wrapper
+        .find(`[data-test-subj="case-view-description"]`)
+        .first()
+        .prop('raw')
+    ).toEqual(data.description);
+  });
+
+  it('should dispatch update state when button is toggled', () => {
+    const wrapper = mount(
+      <TestProviders>
+        <CaseComponent {...caseProps} />
+      </TestProviders>
+    );
+
+    wrapper
+      .find('input[data-test-subj="toggle-case-state"]')
+      .simulate('change', { target: { value: false } });
+
+    expect(dispatchUpdateCaseProperty).toBeCalledWith({
+      updateKey: 'state',
+      updateValue: 'closed',
+    });
+  });
+});
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.tsx
index a92cf99097fce..5cd71c5855d34 100644
--- a/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.tsx
@@ -60,13 +60,13 @@ const BackgroundWrapper = styled.div`
   `}
 `;
 
-interface CasesProps {
+export interface CaseProps {
   caseId: string;
   initialData: Case;
   isLoading: boolean;
 }
 
-export const Cases = React.memo<CasesProps>(({ caseId, initialData, isLoading }) => {
+export const CaseComponent = React.memo<CaseProps>(({ caseId, initialData, isLoading }) => {
   const [{ data }, dispatchUpdateCaseProperty] = useUpdateCase(caseId, initialData);
   const [isEditDescription, setIsEditDescription] = useState(false);
   const [isEditTags, setIsEditTags] = useState(false);
@@ -162,14 +162,14 @@ export const Cases = React.memo<CasesProps>(({ caseId, initialData, isLoading })
   ];
   const userActions = [
     {
-      avatarName: data.created_by.username,
+      avatarName: data.createdBy.username,
       title: (
         <EuiFlexGroup alignItems="baseline" gutterSize="none" justifyContent="spaceBetween">
           <EuiFlexItem grow={false}>
             <p>
-              <strong>{`${data.created_by.username}`}</strong>
+              <strong>{`${data.createdBy.username}`}</strong>
               {` ${i18n.ADDED_DESCRIPTION} `}{' '}
-              <FormattedRelativePreferenceDate value={data.created_at} />
+              <FormattedRelativePreferenceDate value={data.createdAt} />
               {/* STEPH FIX come back and add label `on` */}
             </p>
           </EuiFlexItem>
@@ -206,7 +206,7 @@ export const Cases = React.memo<CasesProps>(({ caseId, initialData, isLoading })
           </EuiFlexGroup>
         </>
       ) : (
-        <Markdown raw={data.description} />
+        <Markdown raw={data.description} data-test-subj="case-view-description" />
       ),
     },
   ];
@@ -229,6 +229,7 @@ export const Cases = React.memo<CasesProps>(({ caseId, initialData, isLoading })
             href: getCaseUrl(),
             text: i18n.BACK_TO_ALL,
           }}
+          data-test-subj="case-view-title"
           titleNode={titleNode}
           title={title}
         >
@@ -239,13 +240,21 @@ export const Cases = React.memo<CasesProps>(({ caseId, initialData, isLoading })
                   <EuiFlexItem grow={false}>
                     <EuiDescriptionListTitle>{i18n.STATUS}</EuiDescriptionListTitle>
                     <EuiDescriptionListDescription>
-                      <EuiBadge color={isCaseOpen ? 'secondary' : 'danger'}>{data.state}</EuiBadge>
+                      <EuiBadge
+                        color={isCaseOpen ? 'secondary' : 'danger'}
+                        data-test-subj="case-view-state"
+                      >
+                        {data.state}
+                      </EuiBadge>
                     </EuiDescriptionListDescription>
                   </EuiFlexItem>
                   <EuiFlexItem>
                     <EuiDescriptionListTitle>{i18n.CASE_OPENED}</EuiDescriptionListTitle>
                     <EuiDescriptionListDescription>
-                      <FormattedRelativePreferenceDate value={data.created_at} />
+                      <FormattedRelativePreferenceDate
+                        data-test-subj="case-view-createdAt"
+                        value={data.createdAt}
+                      />
                     </EuiDescriptionListDescription>
                   </EuiFlexItem>
                 </EuiFlexGroup>
@@ -255,6 +264,7 @@ export const Cases = React.memo<CasesProps>(({ caseId, initialData, isLoading })
               <EuiFlexGroup gutterSize="l" alignItems="center">
                 <EuiFlexItem>
                   <EuiButtonToggle
+                    data-test-subj="toggle-case-state"
                     label={isCaseOpen ? 'Close case' : 'Reopen case'}
                     iconType={isCaseOpen ? 'checkInCircleFilled' : 'magnet'}
                     onChange={onSetIsCaseOpen}
@@ -276,8 +286,13 @@ export const Cases = React.memo<CasesProps>(({ caseId, initialData, isLoading })
               <UserActionTree userActions={userActions} />
             </EuiFlexItem>
             <EuiFlexItem grow={2}>
-              <UserList headline={i18n.REPORTER} users={[data.created_by]} />
+              <UserList
+                data-test-subj="case-view-user-list"
+                headline={i18n.REPORTER}
+                users={[data.createdBy]}
+              />
               <TagList
+                data-test-subj="case-view-tag-list"
                 tags={tags}
                 iconAction={{
                   'aria-label': title,
@@ -310,7 +325,7 @@ export const CaseView = React.memo(({ caseId }: Props) => {
     );
   }
 
-  return <Cases caseId={caseId} initialData={data} isLoading={isLoading} />;
+  return <CaseComponent caseId={caseId} initialData={data} isLoading={isLoading} />;
 });
 
 CaseView.displayName = 'CaseView';
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/create/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/create/index.tsx
index 9fd1525003b0b..7d79e287b22e7 100644
--- a/x-pack/legacy/plugins/siem/public/pages/case/components/create/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/create/index.tsx
@@ -48,8 +48,8 @@ export const Create = React.memo(() => {
     }
   }, [form]);
 
-  if (newCase && newCase.case_id) {
-    return <Redirect to={`/${SiemPageName.case}/${newCase.case_id}`} />;
+  if (newCase && newCase.caseId) {
+    return <Redirect to={`/${SiemPageName.case}/${newCase.caseId}`} />;
   }
   return (
     <EuiPanel>
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/user_list/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/user_list/index.tsx
index b80ee58f8abbf..33e0a9541c5b4 100644
--- a/x-pack/legacy/plugins/siem/public/pages/case/components/user_list/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/case/components/user_list/index.tsx
@@ -42,7 +42,7 @@ const renderUsers = (users: ElasticUser[]) => {
           <EuiFlexItem>
             <p>
               <strong>
-                <small>{username}</small>
+                <small data-test-subj="case-view-username">{username}</small>
               </strong>
             </p>
           </EuiFlexItem>
@@ -50,7 +50,7 @@ const renderUsers = (users: ElasticUser[]) => {
       </EuiFlexItem>
       <EuiFlexItem grow={false}>
         <EuiButtonIcon
-          onClick={() => window.alert('Email clicked')}
+          onClick={() => {}} // TO DO
           iconType="email"
           aria-label="email"
         />
diff --git a/x-pack/legacy/plugins/siem/public/pages/home/home_navigations.tsx b/x-pack/legacy/plugins/siem/public/pages/home/home_navigations.tsx
index 42d333f4f893e..a087dca38de00 100644
--- a/x-pack/legacy/plugins/siem/public/pages/home/home_navigations.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/home/home_navigations.tsx
@@ -55,7 +55,7 @@ export const navTabs: SiemNavTab = {
     id: SiemPageName.case,
     name: i18n.CASE,
     href: getCaseUrl(),
-    disabled: true,
+    disabled: false,
     urlKey: 'case',
   },
 };
diff --git a/x-pack/plugins/case/server/config.ts b/x-pack/plugins/case/server/config.ts
index a7cb117198f9b..8ff9a793b17dc 100644
--- a/x-pack/plugins/case/server/config.ts
+++ b/x-pack/plugins/case/server/config.ts
@@ -7,9 +7,7 @@
 import { schema, TypeOf } from '@kbn/config-schema';
 
 export const ConfigSchema = schema.object({
-  enabled: schema.boolean({ defaultValue: false }),
-  indexPattern: schema.string({ defaultValue: '.case-test-2' }),
-  secret: schema.string({ defaultValue: 'Cool secret huh?' }),
+  enabled: schema.boolean({ defaultValue: true }),
 });
 
 export type ConfigType = TypeOf<typeof ConfigSchema>;
diff --git a/x-pack/plugins/case/server/plugin.ts b/x-pack/plugins/case/server/plugin.ts
index 37d087433a2ed..5ca640f0b25c3 100644
--- a/x-pack/plugins/case/server/plugin.ts
+++ b/x-pack/plugins/case/server/plugin.ts
@@ -34,6 +34,7 @@ export class CasePlugin {
     if (!config.enabled) {
       return;
     }
+
     const service = new CaseService(this.log);
 
     this.log.debug(
diff --git a/x-pack/plugins/case/server/routes/api/__tests__/update_case.test.ts b/x-pack/plugins/case/server/routes/api/__tests__/update_case.test.ts
index 23283d7f8a5be..25d5cafb4bb06 100644
--- a/x-pack/plugins/case/server/routes/api/__tests__/update_case.test.ts
+++ b/x-pack/plugins/case/server/routes/api/__tests__/update_case.test.ts
@@ -27,7 +27,8 @@ describe('UPDATE case', () => {
         id: 'mock-id-1',
       },
       body: {
-        state: 'closed',
+        case: { state: 'closed' },
+        version: 'WzAsMV0=',
       },
     });
 
@@ -38,6 +39,42 @@ describe('UPDATE case', () => {
     expect(typeof response.payload.updated_at).toBe('string');
     expect(response.payload.state).toEqual('closed');
   });
+  it(`Fails with 409 if version does not match`, async () => {
+    const request = httpServerMock.createKibanaRequest({
+      path: '/api/cases/{id}',
+      method: 'patch',
+      params: {
+        id: 'mock-id-1',
+      },
+      body: {
+        case: { state: 'closed' },
+        version: 'badv=',
+      },
+    });
+
+    const theContext = createRouteContext(createMockSavedObjectsRepository(mockCases));
+
+    const response = await routeHandler(theContext, request, kibanaResponseFactory);
+    expect(response.status).toEqual(409);
+  });
+  it(`Fails with 406 if updated field is unchanged`, async () => {
+    const request = httpServerMock.createKibanaRequest({
+      path: '/api/cases/{id}',
+      method: 'patch',
+      params: {
+        id: 'mock-id-1',
+      },
+      body: {
+        case: { state: 'open' },
+        version: 'WzAsMV0=',
+      },
+    });
+
+    const theContext = createRouteContext(createMockSavedObjectsRepository(mockCases));
+
+    const response = await routeHandler(theContext, request, kibanaResponseFactory);
+    expect(response.status).toEqual(406);
+  });
   it(`Returns an error if updateCase throws`, async () => {
     const request = httpServerMock.createKibanaRequest({
       path: '/api/cases/{id}',
diff --git a/x-pack/plugins/case/server/routes/api/get_all_cases.ts b/x-pack/plugins/case/server/routes/api/get_all_cases.ts
index 09075a32ac377..ba26a07dc2394 100644
--- a/x-pack/plugins/case/server/routes/api/get_all_cases.ts
+++ b/x-pack/plugins/case/server/routes/api/get_all_cases.ts
@@ -6,7 +6,7 @@
 
 import { schema } from '@kbn/config-schema';
 import { RouteDeps } from '.';
-import { formatAllCases, wrapError } from './utils';
+import { formatAllCases, sortToSnake, wrapError } from './utils';
 import { SavedObjectsFindOptionsSchema } from './schema';
 import { AllCases } from './types';
 
@@ -23,7 +23,10 @@ export function initGetAllCasesApi({ caseService, router }: RouteDeps) {
         const args = request.query
           ? {
               client: context.core.savedObjects.client,
-              options: request.query,
+              options: {
+                ...request.query,
+                sortField: sortToSnake(request.query.sortField ?? ''),
+              },
             }
           : {
               client: context.core.savedObjects.client,
diff --git a/x-pack/plugins/case/server/routes/api/schema.ts b/x-pack/plugins/case/server/routes/api/schema.ts
index 962dc474254f0..468abc8e7226f 100644
--- a/x-pack/plugins/case/server/routes/api/schema.ts
+++ b/x-pack/plugins/case/server/routes/api/schema.ts
@@ -41,6 +41,11 @@ export const UpdatedCaseSchema = schema.object({
   title: schema.maybe(schema.string()),
 });
 
+export const UpdateCaseArguments = schema.object({
+  case: UpdatedCaseSchema,
+  version: schema.string(),
+});
+
 export const SavedObjectsFindOptionsSchema = schema.object({
   defaultSearchOperator: schema.maybe(schema.oneOf([schema.literal('AND'), schema.literal('OR')])),
   fields: schema.maybe(schema.arrayOf(schema.string())),
diff --git a/x-pack/plugins/case/server/routes/api/types.ts b/x-pack/plugins/case/server/routes/api/types.ts
index 2d1a88bcf1429..5f1c207bf9829 100644
--- a/x-pack/plugins/case/server/routes/api/types.ts
+++ b/x-pack/plugins/case/server/routes/api/types.ts
@@ -32,12 +32,14 @@ export interface CaseAttributes extends NewCaseType, SavedObjectAttributes {
 
 export type FlattenedCaseSavedObject = CaseAttributes & {
   case_id: string;
+  version: string;
   comments: FlattenedCommentSavedObject[];
 };
 
 export type FlattenedCasesSavedObject = Array<
   CaseAttributes & {
     case_id: string;
+    version: string;
     // TO DO it is partial because we need to add it the commentCount
     commentCount?: number;
   }
@@ -52,6 +54,7 @@ export interface AllCases {
 
 export type FlattenedCommentSavedObject = CommentAttributes & {
   comment_id: string;
+  version: string;
   // TO DO We might want to add the case_id where this comment is related too
 };
 
@@ -69,3 +72,13 @@ export interface UpdatedCaseType {
   title?: UpdatedCaseTyped['title'];
   updated_at: string;
 }
+
+export enum SortFieldCase {
+  createdAt = 'created_at',
+  state = 'state',
+  updatedAt = 'updated_at',
+}
+
+export type Writable<T> = {
+  -readonly [K in keyof T]: T[K];
+};
diff --git a/x-pack/plugins/case/server/routes/api/update_case.ts b/x-pack/plugins/case/server/routes/api/update_case.ts
index 2a814c7259e4a..1c1a56dfe9b3a 100644
--- a/x-pack/plugins/case/server/routes/api/update_case.ts
+++ b/x-pack/plugins/case/server/routes/api/update_case.ts
@@ -5,9 +5,17 @@
  */
 
 import { schema } from '@kbn/config-schema';
+import { SavedObject } from 'kibana/server';
+import Boom from 'boom';
+import { difference } from 'lodash';
 import { wrapError } from './utils';
 import { RouteDeps } from '.';
-import { UpdatedCaseSchema } from './schema';
+import { UpdateCaseArguments } from './schema';
+import { CaseAttributes, UpdatedCaseTyped, Writable } from './types';
+
+interface UpdateCase extends Writable<UpdatedCaseTyped> {
+  [key: string]: any;
+}
 
 export function initUpdateCaseApi({ caseService, router }: RouteDeps) {
   router.patch(
@@ -17,23 +25,70 @@ export function initUpdateCaseApi({ caseService, router }: RouteDeps) {
         params: schema.object({
           id: schema.string(),
         }),
-        body: UpdatedCaseSchema,
+        body: UpdateCaseArguments,
       },
     },
     async (context, request, response) => {
+      let theCase: SavedObject<CaseAttributes>;
       try {
-        const updatedCase = await caseService.updateCase({
+        theCase = await caseService.getCase({
           client: context.core.savedObjects.client,
           caseId: request.params.id,
-          updatedAttributes: {
-            ...request.body,
-            updated_at: new Date().toISOString(),
-          },
         });
-        return response.ok({ body: updatedCase.attributes });
       } catch (error) {
         return response.customError(wrapError(error));
       }
+
+      if (request.body.version !== theCase.version) {
+        return response.customError(
+          wrapError(
+            Boom.conflict(
+              'This case has been updated. Please refresh before saving additional updates.'
+            )
+          )
+        );
+      }
+      const currentCase = theCase.attributes;
+      const updateCase: Partial<UpdateCase> = Object.entries(request.body.case).reduce(
+        (acc, [key, value]) => {
+          const currentValue = currentCase[key];
+          if (
+            Array.isArray(value) &&
+            Array.isArray(currentValue) &&
+            difference(value, currentValue).length !== 0
+          ) {
+            return {
+              ...acc,
+              [key]: value,
+            };
+          } else if (value !== currentCase[key]) {
+            return {
+              ...acc,
+              [key]: value,
+            };
+          }
+          return acc;
+        },
+        {}
+      );
+      if (Object.keys(updateCase).length > 0) {
+        try {
+          const updatedCase = await caseService.updateCase({
+            client: context.core.savedObjects.client,
+            caseId: request.params.id,
+            updatedAttributes: {
+              ...updateCase,
+              updated_at: new Date().toISOString(),
+            },
+          });
+          return response.ok({ body: { ...updatedCase.attributes, version: updatedCase.version } });
+        } catch (error) {
+          return response.customError(wrapError(error));
+        }
+      }
+      return response.customError(
+        wrapError(Boom.notAcceptable('All update fields are identical to current version.'))
+      );
     }
   );
 }
diff --git a/x-pack/plugins/case/server/routes/api/utils.ts b/x-pack/plugins/case/server/routes/api/utils.ts
index 51944b04836ab..32de41e1c01c5 100644
--- a/x-pack/plugins/case/server/routes/api/utils.ts
+++ b/x-pack/plugins/case/server/routes/api/utils.ts
@@ -20,6 +20,7 @@ import {
   AllCases,
   NewCaseType,
   NewCommentType,
+  SortFieldCase,
   UserType,
 } from './types';
 
@@ -80,6 +81,7 @@ export const flattenCaseSavedObject = (
   comments: Array<SavedObject<CommentAttributes>>
 ): FlattenedCaseSavedObject => ({
   case_id: savedObject.id,
+  version: savedObject.version ? savedObject.version : '0',
   comments: flattenCommentSavedObjects(comments),
   ...savedObject.attributes,
 });
@@ -107,5 +109,21 @@ export const flattenCommentSavedObject = (
   savedObject: SavedObject<CommentAttributes>
 ): FlattenedCommentSavedObject => ({
   comment_id: savedObject.id,
+  version: savedObject.version ? savedObject.version : '0',
   ...savedObject.attributes,
 });
+
+export const sortToSnake = (sortField: string): SortFieldCase => {
+  switch (sortField) {
+    case 'state':
+      return SortFieldCase.state;
+    case 'createdAt':
+    case 'created_at':
+      return SortFieldCase.createdAt;
+    case 'updatedAt':
+    case 'updated_at':
+      return SortFieldCase.updatedAt;
+    default:
+      return SortFieldCase.createdAt;
+  }
+};

From db0a9cc61ef78b7865e74862895a5b63fe50154e Mon Sep 17 00:00:00 2001
From: Maryia Lapata <mary.lopato@gmail.com>
Date: Mon, 24 Feb 2020 14:03:16 +0300
Subject: [PATCH 139/174] [Bug fix] Update nav link when it belongs to the same
 plugin (#58008)

* Update nav link when it belongs to the same plugin

* Move the plugin name check to history listener

* Add isUrlBelongsToApp function

* Code review comments

* Update unit tests

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../dashboard/np_ready/dashboard_constants.ts |  2 +
 .../kibana/public/dashboard/plugin.ts         | 15 +++-
 .../url/kbn_url_tracker.test.ts               | 79 ++++++++++++++-----
 .../state_management/url/kbn_url_tracker.ts   | 19 ++++-
 4 files changed, 89 insertions(+), 26 deletions(-)

diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_constants.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_constants.ts
index fe42e07912799..0820ebd371004 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_constants.ts
+++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_constants.ts
@@ -23,6 +23,8 @@ export const DashboardConstants = {
   CREATE_NEW_DASHBOARD_URL: '/dashboard',
   ADD_EMBEDDABLE_ID: 'addEmbeddableId',
   ADD_EMBEDDABLE_TYPE: 'addEmbeddableType',
+  DASHBOARDS_ID: 'dashboards',
+  DASHBOARD_ID: 'dashboard',
 };
 
 export function createDashboardEditUrl(id: string) {
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/plugin.ts b/src/legacy/core_plugins/kibana/public/dashboard/plugin.ts
index 7d330676e79ed..7d64ee969212f 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/plugin.ts
+++ b/src/legacy/core_plugins/kibana/public/dashboard/plugin.ts
@@ -83,7 +83,14 @@ export class DashboardPlugin implements Plugin {
     );
     const { appMounted, appUnMounted, stop: stopUrlTracker } = createKbnUrlTracker({
       baseUrl: core.http.basePath.prepend('/app/kibana'),
-      defaultSubUrl: '#/dashboards',
+      defaultSubUrl: `#${DashboardConstants.LANDING_PAGE_PATH}`,
+      shouldTrackUrlUpdate: pathname => {
+        const targetAppName = pathname.split('/')[1];
+        return (
+          targetAppName === DashboardConstants.DASHBOARDS_ID ||
+          targetAppName === DashboardConstants.DASHBOARD_ID
+        );
+      },
       storageKey: 'lastUrl:dashboard',
       navLinkUpdater$: this.appStateUpdater,
       toastNotifications: core.notifications.toasts,
@@ -150,15 +157,15 @@ export class DashboardPlugin implements Plugin {
     };
     kibanaLegacy.registerLegacyApp({
       ...app,
-      id: 'dashboard',
+      id: DashboardConstants.DASHBOARD_ID,
       // only register the updater in once app, otherwise all updates would happen twice
       updater$: this.appStateUpdater.asObservable(),
       navLinkId: 'kibana:dashboard',
     });
-    kibanaLegacy.registerLegacyApp({ ...app, id: 'dashboards' });
+    kibanaLegacy.registerLegacyApp({ ...app, id: DashboardConstants.DASHBOARDS_ID });
 
     home.featureCatalogue.register({
-      id: 'dashboard',
+      id: DashboardConstants.DASHBOARD_ID,
       title: i18n.translate('kbn.dashboard.featureCatalogue.dashboardTitle', {
         defaultMessage: 'Dashboard',
       }),
diff --git a/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.test.ts b/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.test.ts
index 4cf74d991ceb9..701154c06a2ff 100644
--- a/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.test.ts
+++ b/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.test.ts
@@ -38,7 +38,7 @@ describe('kbnUrlTracker', () => {
   let navLinkUpdaterSubject: BehaviorSubject<(app: AppBase) => { activeUrl?: string } | undefined>;
   let toastService: jest.Mocked<ToastsSetup>;
 
-  function createTracker() {
+  function createTracker(shouldTrackUrlUpdate?: (pathname: string) => boolean) {
     urlTracker = createKbnUrlTracker({
       baseUrl: '/app/test',
       defaultSubUrl: '#/start',
@@ -57,6 +57,7 @@ describe('kbnUrlTracker', () => {
       ],
       navLinkUpdater$: navLinkUpdaterSubject,
       toastNotifications: toastService,
+      shouldTrackUrlUpdate,
     });
   }
 
@@ -82,44 +83,44 @@ describe('kbnUrlTracker', () => {
   });
 
   test('set nav link to session storage value if defined', () => {
-    storage.setItem('storageKey', '#/deep/path');
+    storage.setItem('storageKey', '#/start/deep/path');
     createTracker();
-    expect(getActiveNavLinkUrl()).toEqual('/app/test#/deep/path');
+    expect(getActiveNavLinkUrl()).toEqual('/app/test#/start/deep/path');
   });
 
   test('set nav link to default if app gets mounted', () => {
-    storage.setItem('storageKey', '#/deep/path');
+    storage.setItem('storageKey', '#/start/deep/path');
     createTracker();
     urlTracker.appMounted();
     expect(getActiveNavLinkUrl()).toEqual('/app/test#/start');
   });
 
   test('keep nav link to default if path gets changed while app mounted', () => {
-    storage.setItem('storageKey', '#/deep/path');
+    storage.setItem('storageKey', '#/start/deep/path');
     createTracker();
     urlTracker.appMounted();
-    history.push('/deep/path/2');
+    history.push('/start/deep/path/2');
     expect(getActiveNavLinkUrl()).toEqual('/app/test#/start');
   });
 
   test('change nav link to last visited url within app after unmount', () => {
     createTracker();
     urlTracker.appMounted();
-    history.push('/deep/path/2');
-    history.push('/deep/path/3');
+    history.push('/start/deep/path/2');
+    history.push('/start/deep/path/3');
     urlTracker.appUnMounted();
-    expect(getActiveNavLinkUrl()).toEqual('/app/test#/deep/path/3');
+    expect(getActiveNavLinkUrl()).toEqual('/app/test#/start/deep/path/3');
   });
 
   test('unhash all urls that are recorded while app is mounted', () => {
     (unhashUrl as jest.Mock).mockImplementation(x => x + '?unhashed');
     createTracker();
     urlTracker.appMounted();
-    history.push('/deep/path/2');
-    history.push('/deep/path/3');
+    history.push('/start/deep/path/2');
+    history.push('/start/deep/path/3');
     urlTracker.appUnMounted();
     expect(unhashUrl).toHaveBeenCalledTimes(2);
-    expect(getActiveNavLinkUrl()).toEqual('/app/test#/deep/path/3?unhashed');
+    expect(getActiveNavLinkUrl()).toEqual('/app/test#/start/deep/path/3?unhashed');
   });
 
   test('show warning and use hashed url if unhashing does not work', () => {
@@ -128,17 +129,17 @@ describe('kbnUrlTracker', () => {
     });
     createTracker();
     urlTracker.appMounted();
-    history.push('/deep/path/2');
+    history.push('/start/deep/path/2');
     urlTracker.appUnMounted();
-    expect(getActiveNavLinkUrl()).toEqual('/app/test#/deep/path/2');
+    expect(getActiveNavLinkUrl()).toEqual('/app/test#/start/deep/path/2');
     expect(toastService.addDanger).toHaveBeenCalledWith('unhash broke');
   });
 
   test('change nav link back to default if app gets mounted again', () => {
     createTracker();
     urlTracker.appMounted();
-    history.push('/deep/path/2');
-    history.push('/deep/path/3');
+    history.push('/start/deep/path/2');
+    history.push('/start/deep/path/3');
     urlTracker.appUnMounted();
     urlTracker.appMounted();
     expect(getActiveNavLinkUrl()).toEqual('/app/test#/start');
@@ -151,11 +152,11 @@ describe('kbnUrlTracker', () => {
   });
 
   test('update state param without overwriting rest of the url when app is not mounted', () => {
-    storage.setItem('storageKey', '#/deep/path?extrastate=1');
+    storage.setItem('storageKey', '#/start/deep/path?extrastate=1');
     createTracker();
     state1Subject.next({ key1: 'abc' });
     expect(getActiveNavLinkUrl()).toMatchInlineSnapshot(
-      `"/app/test#/deep/path?extrastate=1&state1=(key1:abc)"`
+      `"/app/test#/start/deep/path?extrastate=1&state1=(key1:abc)"`
     );
   });
 
@@ -184,7 +185,45 @@ describe('kbnUrlTracker', () => {
 
   test('set url to storage when setActiveUrl was called', () => {
     createTracker();
-    urlTracker.setActiveUrl('/deep/path/4');
-    expect(storage.getItem('storageKey')).toEqual('#/deep/path/4');
+    urlTracker.setActiveUrl('/start/deep/path/4');
+    expect(storage.getItem('storageKey')).toEqual('#/start/deep/path/4');
+  });
+
+  describe('shouldTrackUrlUpdate', () => {
+    test('change nav link when shouldTrackUrlUpdate is not overridden', () => {
+      storage.setItem('storageKey', '#/start/deep/path');
+      createTracker();
+      urlTracker.appMounted();
+      history.push('/start/path');
+      urlTracker.appUnMounted();
+      expect(getActiveNavLinkUrl()).toEqual('/app/test#/start/path');
+    });
+
+    test('not change nav link when shouldTrackUrlUpdate is not overridden', () => {
+      storage.setItem('storageKey', '#/start/deep/path');
+      createTracker();
+      urlTracker.appMounted();
+      history.push('/setup/path/2');
+      urlTracker.appUnMounted();
+      expect(getActiveNavLinkUrl()).toEqual('/app/test#/start/deep/path');
+    });
+
+    test('change nav link when shouldTrackUrlUpdate is overridden', () => {
+      storage.setItem('storageKey', '#/start/deep/path');
+      createTracker(() => true);
+      urlTracker.appMounted();
+      history.push('/setup/path/2');
+      urlTracker.appUnMounted();
+      expect(getActiveNavLinkUrl()).toEqual('/app/test#/setup/path/2');
+    });
+
+    test('not change nav link when shouldTrackUrlUpdate is overridden', () => {
+      storage.setItem('storageKey', '#/start/deep/path');
+      createTracker(() => false);
+      urlTracker.appMounted();
+      history.push('/setup/path/2');
+      urlTracker.appUnMounted();
+      expect(getActiveNavLinkUrl()).toEqual('/app/test#/start/deep/path');
+    });
   });
 });
diff --git a/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.ts b/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.ts
index 2edd135c184ec..b778535a2d428 100644
--- a/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.ts
+++ b/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.ts
@@ -58,6 +58,12 @@ export function createKbnUrlTracker({
   toastNotifications,
   history,
   storage,
+  shouldTrackUrlUpdate = pathname => {
+    const currentAppName = defaultSubUrl.slice(2); // cut hash and slash symbols
+    const targetAppName = pathname.split('/')[1];
+
+    return currentAppName === targetAppName;
+  },
 }: {
   /**
    * Base url of the current app. This will be used as a prefix for the
@@ -82,7 +88,7 @@ export function createKbnUrlTracker({
     stateUpdate$: Observable<unknown>;
   }>;
   /**
-   * Key used to store the current sub url in session storage. This key should only be used for one active url tracker at any given ntime.
+   * Key used to store the current sub url in session storage. This key should only be used for one active url tracker at any given time.
    */
   storageKey: string;
   /**
@@ -101,6 +107,13 @@ export function createKbnUrlTracker({
    * Storage object to use to persist currently active url. If this isn't provided, the browser wide session storage instance will be used.
    */
   storage?: Storage;
+  /**
+   * Checks if pathname belongs to current app. It's used in history listener to define whether it's necessary to set pathname as active url or not.
+   * The default implementation compares the app name to the first part of pathname. Consumers can override this function for more complex cases.
+   *
+   * @param {string} pathname A location's pathname which comes to history listener
+   */
+  shouldTrackUrlUpdate?: (pathname: string) => boolean;
 }): KbnUrlTracker {
   const historyInstance = history || createHashHistory();
   const storageInstance = storage || sessionStorage;
@@ -148,7 +161,9 @@ export function createKbnUrlTracker({
     unsubscribe();
     // track current hash when within app
     unsubscribeURLHistory = historyInstance.listen(location => {
-      setActiveUrl(location.pathname + location.search);
+      if (shouldTrackUrlUpdate(location.pathname)) {
+        setActiveUrl(location.pathname + location.search);
+      }
     });
   }
 

From e64eff0a3d5fde205256ab74b731a63766822f9a Mon Sep 17 00:00:00 2001
From: Tyler Smalley <tyler.smalley@elastic.co>
Date: Mon, 24 Feb 2020 06:02:27 -0800
Subject: [PATCH 140/174] Temporarily removes kbn-optimizer cache key tests
 (#58318)

While we investigate why they are interfering with other tests.

Signed-off-by: Tyler Smalley <tyler.smalley@elastic.co>

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../src/optimizer/cache_keys.test.ts          | 206 ------------------
 1 file changed, 206 deletions(-)
 delete mode 100644 packages/kbn-optimizer/src/optimizer/cache_keys.test.ts

diff --git a/packages/kbn-optimizer/src/optimizer/cache_keys.test.ts b/packages/kbn-optimizer/src/optimizer/cache_keys.test.ts
deleted file mode 100644
index 2337017f54ed8..0000000000000
--- a/packages/kbn-optimizer/src/optimizer/cache_keys.test.ts
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import Path from 'path';
-
-import jestDiff from 'jest-diff';
-import { REPO_ROOT, createAbsolutePathSerializer } from '@kbn/dev-utils';
-
-import { reformatJestDiff, getOptimizerCacheKey, diffCacheKey } from './cache_keys';
-import { OptimizerConfig } from './optimizer_config';
-
-jest.mock('./get_changes.ts', () => ({
-  getChanges: async () =>
-    new Map([
-      ['/foo/bar/a', 'modified'],
-      ['/foo/bar/b', 'modified'],
-      ['/foo/bar/c', 'deleted'],
-    ]),
-}));
-
-jest.mock('./get_mtimes.ts', () => ({
-  getMtimes: async (paths: string[]) => new Map(paths.map(path => [path, 12345])),
-}));
-
-jest.mock('execa');
-
-jest.mock('fs', () => {
-  const realFs = jest.requireActual('fs');
-  jest.spyOn(realFs, 'readFile');
-  return realFs;
-});
-
-expect.addSnapshotSerializer(createAbsolutePathSerializer());
-
-jest.requireMock('execa').mockImplementation(async (cmd: string, args: string[], opts: object) => {
-  expect(cmd).toBe('git');
-  expect(args).toEqual([
-    'log',
-    '-n',
-    '1',
-    '--pretty=format:%H',
-    '--',
-    expect.stringContaining('kbn-optimizer'),
-  ]);
-  expect(opts).toEqual({
-    cwd: REPO_ROOT,
-  });
-
-  return {
-    stdout: '<last commit sha>',
-  };
-});
-
-describe('getOptimizerCacheKey()', () => {
-  it('uses latest commit, bootstrap cache, and changed files to create unique value', async () => {
-    jest
-      .requireMock('fs')
-      .readFile.mockImplementation(
-        (path: string, enc: string, cb: (err: null, file: string) => void) => {
-          expect(path).toBe(
-            Path.resolve(REPO_ROOT, 'packages/kbn-optimizer/target/.bootstrap-cache')
-          );
-          expect(enc).toBe('utf8');
-          cb(null, '<bootstrap cache>');
-        }
-      );
-
-    const config = OptimizerConfig.create({
-      repoRoot: REPO_ROOT,
-    });
-
-    await expect(getOptimizerCacheKey(config)).resolves.toMatchInlineSnapshot(`
-            Object {
-              "bootstrap": "<bootstrap cache>",
-              "deletedPaths": Array [
-                "/foo/bar/c",
-              ],
-              "lastCommit": "<last commit sha>",
-              "modifiedTimes": Object {
-                "/foo/bar/a": 12345,
-                "/foo/bar/b": 12345,
-              },
-              "workerConfig": Object {
-                "browserslistEnv": "dev",
-                "cache": true,
-                "dist": false,
-                "optimizerCacheKey": "♻",
-                "profileWebpack": false,
-                "repoRoot": <absolute path>,
-                "watch": false,
-              },
-            }
-          `);
-  });
-});
-
-describe('diffCacheKey()', () => {
-  it('returns undefined if values are equal', () => {
-    expect(diffCacheKey('1', '1')).toBe(undefined);
-    expect(diffCacheKey(1, 1)).toBe(undefined);
-    expect(diffCacheKey(['1', '2', { a: 'b' }], ['1', '2', { a: 'b' }])).toBe(undefined);
-    expect(
-      diffCacheKey(
-        {
-          a: '1',
-          b: '2',
-        },
-        {
-          b: '2',
-          a: '1',
-        }
-      )
-    ).toBe(undefined);
-  });
-
-  it('returns a diff if the values are different', () => {
-    expect(diffCacheKey(['1', '2', { a: 'b' }], ['1', '2', { b: 'a' }])).toMatchInlineSnapshot(`
-      "- Expected
-      + Received
-
-        Array [
-          \\"1\\",
-          \\"2\\",
-          Object {
-      -     \\"a\\": \\"b\\",
-      +     \\"b\\": \\"a\\",
-          },
-        ]"
-    `);
-    expect(
-      diffCacheKey(
-        {
-          a: '1',
-          b: '1',
-        },
-        {
-          b: '2',
-          a: '2',
-        }
-      )
-    ).toMatchInlineSnapshot(`
-      "- Expected
-      + Received
-
-        Object {
-      -   \\"a\\": \\"1\\",
-      -   \\"b\\": \\"1\\",
-      +   \\"a\\": \\"2\\",
-      +   \\"b\\": \\"2\\",
-        }"
-    `);
-  });
-});
-
-describe('reformatJestDiff()', () => {
-  it('reformats large jestDiff output to focus on the changed lines', () => {
-    const diff = jestDiff(
-      {
-        a: ['1', '1', '1', '1', '1', '1', '1', '2', '1', '1', '1', '1', '1', '1', '1', '1', '1'],
-      },
-      {
-        b: ['1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '2', '1', '1', '1', '1'],
-      }
-    );
-
-    expect(reformatJestDiff(diff)).toMatchInlineSnapshot(`
-      "- Expected
-      + Received
-
-        Object {
-      -   \\"a\\": Array [
-      +   \\"b\\": Array [
-            \\"1\\",
-            \\"1\\",
-            ...
-            \\"1\\",
-            \\"1\\",
-      -     \\"2\\",
-            \\"1\\",
-            \\"1\\",
-            ...
-            \\"1\\",
-            \\"1\\",
-      +     \\"2\\",
-            \\"1\\",
-            \\"1\\",
-            ..."
-    `);
-  });
-});

From 581f1bd6e9329b66db5bc3cb3348475595db5aa2 Mon Sep 17 00:00:00 2001
From: Vadim Dalecky <streamich@users.noreply.github.com>
Date: Mon, 24 Feb 2020 16:26:34 +0100
Subject: [PATCH 141/174] Expressions debug mode (#57841)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat: 🎸 add ability to execute expression in debug mode

* feat: 🎸 store resolved arguments in debug information

* feat: 🎸 track function execution time and set "success" flag

* feat: 🎸 store debug information for functions that throw

* feat: 🎸 use performance.now, safe "fn" reference, fix typo
---
 src/plugins/expressions/common/ast/types.ts   |  53 ++++
 .../common/execution/execution.test.ts        | 253 +++++++++++++++++-
 .../expressions/common/execution/execution.ts |  67 ++++-
 .../expressions/common/executor/executor.ts   |  26 +-
 4 files changed, 379 insertions(+), 20 deletions(-)

diff --git a/src/plugins/expressions/common/ast/types.ts b/src/plugins/expressions/common/ast/types.ts
index 82a7578dd4b89..0b505f117a580 100644
--- a/src/plugins/expressions/common/ast/types.ts
+++ b/src/plugins/expressions/common/ast/types.ts
@@ -17,6 +17,9 @@
  * under the License.
  */
 
+import { ExpressionValue, ExpressionValueError } from '../expression_types';
+import { ExpressionFunction } from '../../public';
+
 export type ExpressionAstNode =
   | ExpressionAstExpression
   | ExpressionAstFunction
@@ -31,6 +34,56 @@ export interface ExpressionAstFunction {
   type: 'function';
   function: string;
   arguments: Record<string, ExpressionAstArgument[]>;
+
+  /**
+   * Debug information added to each function when expression is executed in *debug mode*.
+   */
+  debug?: ExpressionAstFunctionDebug;
+}
+
+export interface ExpressionAstFunctionDebug {
+  /**
+   * True if function successfully returned output, false if function threw.
+   */
+  success: boolean;
+
+  /**
+   * Reference to the expression function this AST node represents.
+   */
+  fn: ExpressionFunction;
+
+  /**
+   * Input that expression function received as its first argument.
+   */
+  input: ExpressionValue;
+
+  /**
+   * Map of resolved arguments expression function received as its second argument.
+   */
+  args: Record<string, ExpressionValue>;
+
+  /**
+   * Result returned by the expression function. Including an error result
+   * if it was returned by the function (not thrown).
+   */
+  output?: ExpressionValue;
+
+  /**
+   * Error that function threw normalized to `ExpressionValueError`.
+   */
+  error?: ExpressionValueError;
+
+  /**
+   * Raw error that was thrown by the function, if any.
+   */
+  rawError?: any | Error;
+
+  /**
+   * Time in milliseconds it took to execute the function. Duration can be
+   * `undefined` if error happened during argument resolution, because function
+   * timing starts after the arguments have been resolved.
+   */
+  duration: number | undefined;
 }
 
 export type ExpressionAstArgument = string | boolean | number | ExpressionAstExpression;
diff --git a/src/plugins/expressions/common/execution/execution.test.ts b/src/plugins/expressions/common/execution/execution.test.ts
index b6c1721e33eef..f6ff9efca848b 100644
--- a/src/plugins/expressions/common/execution/execution.test.ts
+++ b/src/plugins/expressions/common/execution/execution.test.ts
@@ -18,20 +18,28 @@
  */
 
 import { Execution } from './execution';
-import { parseExpression } from '../ast';
+import { parseExpression, ExpressionAstExpression } from '../ast';
 import { createUnitTestExecutor } from '../test_helpers';
 import { ExpressionFunctionDefinition } from '../../public';
 import { ExecutionContract } from './execution_contract';
 
+beforeAll(() => {
+  if (typeof performance === 'undefined') {
+    (global as any).performance = { now: Date.now };
+  }
+});
+
 const createExecution = (
   expression: string = 'foo bar=123',
-  context: Record<string, unknown> = {}
+  context: Record<string, unknown> = {},
+  debug: boolean = false
 ) => {
   const executor = createUnitTestExecutor();
   const execution = new Execution({
     executor,
     ast: parseExpression(expression),
     context,
+    debug,
   });
   return execution;
 };
@@ -406,4 +414,245 @@ describe('Execution', () => {
       });
     });
   });
+
+  describe('debug mode', () => {
+    test('can execute expression in debug mode', async () => {
+      const execution = createExecution('add val=1 | add val=2 | add val=3', {}, true);
+      execution.start(-1);
+      const result = await execution.result;
+
+      expect(result).toEqual({
+        type: 'num',
+        value: 5,
+      });
+    });
+
+    test('can execute expression with sub-expression in debug mode', async () => {
+      const execution = createExecution(
+        'add val={var_set name=foo value=5 | var name=foo} | add val=10',
+        {},
+        true
+      );
+      execution.start(0);
+      const result = await execution.result;
+
+      expect(result).toEqual({
+        type: 'num',
+        value: 15,
+      });
+    });
+
+    describe('when functions succeed', () => {
+      test('sets "success" flag on all functions to true', async () => {
+        const execution = createExecution('add val=1 | add val=2 | add val=3', {}, true);
+        execution.start(-1);
+        await execution.result;
+
+        for (const node of execution.state.get().ast.chain) {
+          expect(node.debug?.success).toBe(true);
+        }
+      });
+
+      test('stores "fn" reference to the function', async () => {
+        const execution = createExecution('add val=1 | add val=2 | add val=3', {}, true);
+        execution.start(-1);
+        await execution.result;
+
+        for (const node of execution.state.get().ast.chain) {
+          expect(node.debug?.fn.name).toBe('add');
+        }
+      });
+
+      test('saves duration it took to execute each function', async () => {
+        const execution = createExecution('add val=1 | add val=2 | add val=3', {}, true);
+        execution.start(-1);
+        await execution.result;
+
+        for (const node of execution.state.get().ast.chain) {
+          expect(typeof node.debug?.duration).toBe('number');
+          expect(node.debug?.duration).toBeLessThan(100);
+          expect(node.debug?.duration).toBeGreaterThanOrEqual(0);
+        }
+      });
+
+      test('sets duration to 10 milliseconds when function executes 10 milliseconds', async () => {
+        const execution = createExecution('sleep 10', {}, true);
+        execution.start(-1);
+        await execution.result;
+
+        const node = execution.state.get().ast.chain[0];
+        expect(typeof node.debug?.duration).toBe('number');
+        expect(node.debug?.duration).toBeLessThan(50);
+        expect(node.debug?.duration).toBeGreaterThanOrEqual(5);
+      });
+
+      test('adds .debug field in expression AST on each executed function', async () => {
+        const execution = createExecution('add val=1 | add val=2 | add val=3', {}, true);
+        execution.start(-1);
+        await execution.result;
+
+        for (const node of execution.state.get().ast.chain) {
+          expect(typeof node.debug).toBe('object');
+          expect(!!node.debug).toBe(true);
+        }
+      });
+
+      test('stores input of each function', async () => {
+        const execution = createExecution('add val=1 | add val=2 | add val=3', {}, true);
+        execution.start(-1);
+        await execution.result;
+
+        const { chain } = execution.state.get().ast;
+
+        expect(chain[0].debug!.input).toBe(-1);
+        expect(chain[1].debug!.input).toEqual({
+          type: 'num',
+          value: 0,
+        });
+        expect(chain[2].debug!.input).toEqual({
+          type: 'num',
+          value: 2,
+        });
+      });
+
+      test('stores output of each function', async () => {
+        const execution = createExecution('add val=1 | add val=2 | add val=3', {}, true);
+        execution.start(-1);
+        await execution.result;
+
+        const { chain } = execution.state.get().ast;
+
+        expect(chain[0].debug!.output).toEqual({
+          type: 'num',
+          value: 0,
+        });
+        expect(chain[1].debug!.output).toEqual({
+          type: 'num',
+          value: 2,
+        });
+        expect(chain[2].debug!.output).toEqual({
+          type: 'num',
+          value: 5,
+        });
+      });
+
+      test('stores resolved arguments of a function', async () => {
+        const execution = createExecution(
+          'add val={var_set name=foo value=5 | var name=foo} | add val=10',
+          {},
+          true
+        );
+        execution.start(-1);
+        await execution.result;
+
+        const { chain } = execution.state.get().ast;
+
+        expect(chain[0].debug!.args).toEqual({
+          val: 5,
+        });
+
+        expect((chain[0].arguments.val[0] as ExpressionAstExpression).chain[0].debug!.args).toEqual(
+          {
+            name: 'foo',
+            value: 5,
+          }
+        );
+      });
+
+      test('store debug information about sub-expressions', async () => {
+        const execution = createExecution(
+          'add val={var_set name=foo value=5 | var name=foo} | add val=10',
+          {},
+          true
+        );
+        execution.start(0);
+        await execution.result;
+
+        const { chain } = execution.state.get().ast.chain[0].arguments
+          .val[0] as ExpressionAstExpression;
+
+        expect(typeof chain[0].debug).toBe('object');
+        expect(typeof chain[1].debug).toBe('object');
+        expect(!!chain[0].debug).toBe(true);
+        expect(!!chain[1].debug).toBe(true);
+
+        expect(chain[0].debug!.input).toBe(0);
+        expect(chain[0].debug!.output).toBe(0);
+        expect(chain[1].debug!.input).toBe(0);
+        expect(chain[1].debug!.output).toBe(5);
+      });
+    });
+
+    describe('when expression throws', () => {
+      const executor = createUnitTestExecutor();
+      executor.registerFunction({
+        name: 'throws',
+        args: {},
+        help: '',
+        fn: () => {
+          throw new Error('foo');
+        },
+      });
+
+      test('stores debug information up until the function that throws', async () => {
+        const execution = new Execution({
+          executor,
+          ast: parseExpression('add val=1 | throws | add val=3'),
+          debug: true,
+        });
+        execution.start(0);
+        await execution.result;
+
+        const node1 = execution.state.get().ast.chain[0];
+        const node2 = execution.state.get().ast.chain[1];
+        const node3 = execution.state.get().ast.chain[2];
+
+        expect(typeof node1.debug).toBe('object');
+        expect(typeof node2.debug).toBe('object');
+        expect(typeof node3.debug).toBe('undefined');
+      });
+
+      test('stores error thrown in debug information', async () => {
+        const execution = new Execution({
+          executor,
+          ast: parseExpression('add val=1 | throws | add val=3'),
+          debug: true,
+        });
+        execution.start(0);
+        await execution.result;
+
+        const node2 = execution.state.get().ast.chain[1];
+
+        expect(node2.debug?.error).toMatchObject({
+          type: 'error',
+          error: {
+            message: '[throws] > foo',
+          },
+        });
+        expect(node2.debug?.rawError).toBeInstanceOf(Error);
+      });
+
+      test('sets .debug object to expected shape', async () => {
+        const execution = new Execution({
+          executor,
+          ast: parseExpression('add val=1 | throws | add val=3'),
+          debug: true,
+        });
+        execution.start(0);
+        await execution.result;
+
+        const node2 = execution.state.get().ast.chain[1];
+
+        expect(node2.debug).toMatchObject({
+          success: false,
+          fn: expect.any(Object),
+          input: expect.any(Object),
+          args: expect.any(Object),
+          error: expect.any(Object),
+          rawError: expect.any(Error),
+          duration: expect.any(Number),
+        });
+      });
+    });
+  });
 });
diff --git a/src/plugins/expressions/common/execution/execution.ts b/src/plugins/expressions/common/execution/execution.ts
index 2a272e187cffc..7e7df822724ae 100644
--- a/src/plugins/expressions/common/execution/execution.ts
+++ b/src/plugins/expressions/common/execution/execution.ts
@@ -23,7 +23,7 @@ import { createExecutionContainer, ExecutionContainer } from './container';
 import { createError } from '../util';
 import { Defer } from '../../../kibana_utils/common';
 import { RequestAdapter, DataAdapter } from '../../../inspector/common';
-import { isExpressionValueError } from '../expression_types/specs/error';
+import { isExpressionValueError, ExpressionValueError } from '../expression_types/specs/error';
 import {
   ExpressionAstExpression,
   ExpressionAstFunction,
@@ -32,7 +32,7 @@ import {
   parseExpression,
 } from '../ast';
 import { ExecutionContext, DefaultInspectorAdapters } from './types';
-import { getType } from '../expression_types';
+import { getType, ExpressionValue } from '../expression_types';
 import { ArgumentType, ExpressionFunction } from '../expression_functions';
 import { getByAlias } from '../util/get_by_alias';
 import { ExecutionContract } from './execution_contract';
@@ -44,6 +44,13 @@ export interface ExecutionParams<
   ast?: ExpressionAstExpression;
   expression?: string;
   context?: ExtraContext;
+
+  /**
+   * Whether to execute expression in *debug mode*. In *debug mode* inputs and
+   * outputs as well as all resolved arguments and time it took to execute each
+   * function are saved and are available for introspection.
+   */
+  debug?: boolean;
 }
 
 const createDefaultInspectorAdapters = (): DefaultInspectorAdapters => ({
@@ -190,23 +197,55 @@ export class Execution<
       }
 
       const { function: fnName, arguments: fnArgs } = link;
-      const fnDef = getByAlias(this.state.get().functions, fnName);
+      const fn = getByAlias(this.state.get().functions, fnName);
 
-      if (!fnDef) {
+      if (!fn) {
         return createError({ message: `Function ${fnName} could not be found.` });
       }
 
+      let args: Record<string, ExpressionValue> = {};
+      let timeStart: number | undefined;
+
       try {
-        // Resolve arguments before passing to function
-        // resolveArgs returns an object because the arguments themselves might
-        // actually have a 'then' function which would be treated as a promise
-        const { resolvedArgs } = await this.resolveArgs(fnDef, input, fnArgs);
-        const output = await this.invokeFunction(fnDef, input, resolvedArgs);
+        // `resolveArgs` returns an object because the arguments themselves might
+        // actually have a `then` function which would be treated as a `Promise`.
+        const { resolvedArgs } = await this.resolveArgs(fn, input, fnArgs);
+        args = resolvedArgs;
+        timeStart = this.params.debug ? performance.now() : 0;
+        const output = await this.invokeFunction(fn, input, resolvedArgs);
+
+        if (this.params.debug) {
+          const timeEnd: number = performance.now();
+          (link as ExpressionAstFunction).debug = {
+            success: true,
+            fn,
+            input,
+            args: resolvedArgs,
+            output,
+            duration: timeEnd - timeStart,
+          };
+        }
+
         if (getType(output) === 'error') return output;
         input = output;
-      } catch (e) {
-        e.message = `[${fnName}] > ${e.message}`;
-        return createError(e);
+      } catch (rawError) {
+        const timeEnd: number = this.params.debug ? performance.now() : 0;
+        rawError.message = `[${fnName}] > ${rawError.message}`;
+        const error = createError(rawError) as ExpressionValueError;
+
+        if (this.params.debug) {
+          (link as ExpressionAstFunction).debug = {
+            success: false,
+            fn,
+            input,
+            args,
+            error,
+            rawError,
+            duration: timeStart ? timeEnd - timeStart : undefined,
+          };
+        }
+
+        return error;
       }
     }
 
@@ -327,7 +366,9 @@ export class Execution<
     const resolveArgFns = mapValues(argAstsWithDefaults, (asts, argName) => {
       return asts.map((item: ExpressionAstExpression) => {
         return async (subInput = input) => {
-          const output = await this.params.executor.interpret(item, subInput);
+          const output = await this.params.executor.interpret(item, subInput, {
+            debug: this.params.debug,
+          });
           if (isExpressionValueError(output)) throw output.error;
           const casted = this.cast(output, argDefs[argName as any].types);
           return casted;
diff --git a/src/plugins/expressions/common/executor/executor.ts b/src/plugins/expressions/common/executor/executor.ts
index af3662d13de4e..2ecbc5f75a9e8 100644
--- a/src/plugins/expressions/common/executor/executor.ts
+++ b/src/plugins/expressions/common/executor/executor.ts
@@ -31,6 +31,15 @@ import { ExpressionAstExpression, ExpressionAstNode } from '../ast';
 import { typeSpecs } from '../expression_types/specs';
 import { functionSpecs } from '../expression_functions/specs';
 
+export interface ExpressionExecOptions {
+  /**
+   * Whether to execute expression in *debug mode*. In *debug mode* inputs and
+   * outputs as well as all resolved arguments and time it took to execute each
+   * function are saved and are available for introspection.
+   */
+  debug?: boolean;
+}
+
 export class TypesRegistry implements IRegistry<ExpressionType> {
   constructor(private readonly executor: Executor<any>) {}
 
@@ -145,10 +154,14 @@ export class Executor<Context extends Record<string, unknown> = Record<string, u
     return this.state.selectors.getContext();
   }
 
-  public async interpret<T>(ast: ExpressionAstNode, input: T): Promise<unknown> {
+  public async interpret<T>(
+    ast: ExpressionAstNode,
+    input: T,
+    options?: ExpressionExecOptions
+  ): Promise<unknown> {
     switch (getType(ast)) {
       case 'expression':
-        return await this.interpretExpression(ast as ExpressionAstExpression, input);
+        return await this.interpretExpression(ast as ExpressionAstExpression, input, options);
       case 'string':
       case 'number':
       case 'null':
@@ -161,9 +174,10 @@ export class Executor<Context extends Record<string, unknown> = Record<string, u
 
   public async interpretExpression<T>(
     ast: string | ExpressionAstExpression,
-    input: T
+    input: T,
+    options?: ExpressionExecOptions
   ): Promise<unknown> {
-    const execution = this.createExecution(ast);
+    const execution = this.createExecution(ast, undefined, options);
     execution.start(input);
     return await execution.result;
   }
@@ -192,7 +206,8 @@ export class Executor<Context extends Record<string, unknown> = Record<string, u
     Output = unknown
   >(
     ast: string | ExpressionAstExpression,
-    context: ExtraContext = {} as ExtraContext
+    context: ExtraContext = {} as ExtraContext,
+    { debug }: ExpressionExecOptions = {} as ExpressionExecOptions
   ): Execution<Context & ExtraContext, Input, Output> {
     const params: ExecutionParams<Context & ExtraContext> = {
       executor: this,
@@ -200,6 +215,7 @@ export class Executor<Context extends Record<string, unknown> = Record<string, u
         ...this.context,
         ...context,
       } as Context & ExtraContext,
+      debug,
     };
 
     if (typeof ast === 'string') params.expression = ast;

From 25dc7c03cb1e3a9b167f31dd8fc804c97e29ec21 Mon Sep 17 00:00:00 2001
From: Mikhail Shustov <restrry@gmail.com>
Date: Mon, 24 Feb 2020 16:40:20 +0100
Subject: [PATCH 142/174] no sparse array by default. (#58212)

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../src/types/array_type.test.ts              | 37 ++++++++++++++++---
 .../kbn-config-schema/src/types/array_type.ts |  4 +-
 2 files changed, 34 insertions(+), 7 deletions(-)

diff --git a/packages/kbn-config-schema/src/types/array_type.test.ts b/packages/kbn-config-schema/src/types/array_type.test.ts
index 73661ef849cf4..66b72096a593d 100644
--- a/packages/kbn-config-schema/src/types/array_type.test.ts
+++ b/packages/kbn-config-schema/src/types/array_type.test.ts
@@ -85,14 +85,29 @@ test('fails if mixed types of content in array', () => {
   );
 });
 
-test('returns empty array if input is empty but type has default value', () => {
-  const type = schema.arrayOf(schema.string({ defaultValue: 'test' }));
+test('fails if sparse content in array', () => {
+  const type = schema.arrayOf(schema.string());
   expect(type.validate([])).toEqual([]);
+  expect(() => type.validate([undefined])).toThrowErrorMatchingInlineSnapshot(
+    `"[0]: sparse array are not allowed"`
+  );
 });
 
-test('returns empty array if input is empty even if type is required', () => {
-  const type = schema.arrayOf(schema.string());
+test('fails if sparse content in array if optional', () => {
+  const type = schema.arrayOf(schema.maybe(schema.string()));
+  expect(type.validate([])).toEqual([]);
+  expect(() => type.validate([undefined])).toThrowErrorMatchingInlineSnapshot(
+    `"[0]: sparse array are not allowed"`
+  );
+});
+
+test('fails if sparse content in array if nullable', () => {
+  const type = schema.arrayOf(schema.nullable(schema.string()));
   expect(type.validate([])).toEqual([]);
+  expect(type.validate([null])).toEqual([null]);
+  expect(() => type.validate([undefined])).toThrowErrorMatchingInlineSnapshot(
+    `"[0]: sparse array are not allowed"`
+  );
 });
 
 test('fails for null values if optional', () => {
@@ -102,9 +117,19 @@ test('fails for null values if optional', () => {
   );
 });
 
+test('returns empty array if input is empty but type has default value', () => {
+  const type = schema.arrayOf(schema.string({ defaultValue: 'test' }));
+  expect(type.validate([])).toEqual([]);
+});
+
+test('returns empty array if input is empty even if type is required', () => {
+  const type = schema.arrayOf(schema.string());
+  expect(type.validate([])).toEqual([]);
+});
+
 test('handles default values for undefined values', () => {
-  const type = schema.arrayOf(schema.string({ defaultValue: 'foo' }));
-  expect(type.validate([undefined])).toEqual(['foo']);
+  const type = schema.arrayOf(schema.string(), { defaultValue: ['foo'] });
+  expect(type.validate(undefined)).toEqual(['foo']);
 });
 
 test('array within array', () => {
diff --git a/packages/kbn-config-schema/src/types/array_type.ts b/packages/kbn-config-schema/src/types/array_type.ts
index ad74f375588ad..a0353e8348ddd 100644
--- a/packages/kbn-config-schema/src/types/array_type.ts
+++ b/packages/kbn-config-schema/src/types/array_type.ts
@@ -31,7 +31,7 @@ export class ArrayType<T> extends Type<T[]> {
     let schema = internals
       .array()
       .items(type.getSchema().optional())
-      .sparse();
+      .sparse(false);
 
     if (options.minSize !== undefined) {
       schema = schema.min(options.minSize);
@@ -49,6 +49,8 @@ export class ArrayType<T> extends Type<T[]> {
       case 'any.required':
       case 'array.base':
         return `expected value of type [array] but got [${typeDetect(value)}]`;
+      case 'array.sparse':
+        return `sparse array are not allowed`;
       case 'array.parse':
         return `could not parse array value from [${value}]`;
       case 'array.min':

From 6b735c9ca016a65cc0c2e29fb144a1046020f1d3 Mon Sep 17 00:00:00 2001
From: Melissa Alvarez <melissa.alvarez@elastic.co>
Date: Mon, 24 Feb 2020 11:17:29 -0500
Subject: [PATCH 143/174] [ML] New Platform server shim: update usage collector
 to use core savedObjects (#58058)

*  use NP savedObjects and createInternalRepository for usage collection

* fix route doc typo

* update MlTelemetrySavedObject type

* remove fileDataVisualizer routes dependency on legacy es plugin

* update mlTelemetry tests

* remove deprecated use of getSavedObjectsClient
---
 x-pack/legacy/plugins/ml/index.ts             |   3 +-
 .../ml/server/lib/ml_telemetry/index.ts       |   1 -
 .../ml_telemetry/make_ml_usage_collector.ts   |  15 ++-
 .../lib/ml_telemetry/ml_telemetry.test.ts     | 102 +++++-------------
 .../server/lib/ml_telemetry/ml_telemetry.ts   |  39 +++----
 .../plugins/ml/server/new_platform/plugin.ts  |  21 ++--
 .../ml/server/routes/file_data_visualizer.ts  |   2 +-
 .../ml/server/routes/job_audit_messages.ts    |   2 +-
 8 files changed, 55 insertions(+), 130 deletions(-)

diff --git a/x-pack/legacy/plugins/ml/index.ts b/x-pack/legacy/plugins/ml/index.ts
index 0ef5e14e44f71..09f1b9ccedce4 100755
--- a/x-pack/legacy/plugins/ml/index.ts
+++ b/x-pack/legacy/plugins/ml/index.ts
@@ -81,7 +81,8 @@ export const ml = (kibana: any) => {
         injectUiAppVars: server.injectUiAppVars,
         http: mlHttpService,
         savedObjects: server.savedObjects,
-        elasticsearch: kbnServer.newPlatform.setup.core.elasticsearch, // NP
+        coreSavedObjects: kbnServer.newPlatform.start.core.savedObjects,
+        elasticsearch: kbnServer.newPlatform.setup.core.elasticsearch,
       };
       const { usageCollection, cloud, home } = kbnServer.newPlatform.setup.plugins;
       const plugins = {
diff --git a/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/index.ts b/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/index.ts
index 5da4f6b62bcec..dffd95f50e0d9 100644
--- a/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/index.ts
+++ b/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/index.ts
@@ -6,7 +6,6 @@
 
 export {
   createMlTelemetry,
-  getSavedObjectsClient,
   incrementFileDataVisualizerIndexCreationCount,
   storeMlTelemetry,
   MlTelemetry,
diff --git a/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/make_ml_usage_collector.ts b/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/make_ml_usage_collector.ts
index 293480b2aa5dc..a120450bbb2b0 100644
--- a/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/make_ml_usage_collector.ts
+++ b/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/make_ml_usage_collector.ts
@@ -5,19 +5,17 @@
  */
 
 import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
+import { SavedObjectsServiceStart } from 'src/core/server';
 import {
   createMlTelemetry,
-  getSavedObjectsClient,
   ML_TELEMETRY_DOC_ID,
   MlTelemetry,
   MlTelemetrySavedObject,
 } from './ml_telemetry';
 
-import { UsageInitialization } from '../../new_platform/plugin';
-
 export function makeMlUsageCollector(
   usageCollection: UsageCollectionSetup | undefined,
-  { elasticsearchPlugin, savedObjects }: UsageInitialization
+  savedObjects: SavedObjectsServiceStart
 ): void {
   if (!usageCollection) {
     return;
@@ -28,11 +26,10 @@ export function makeMlUsageCollector(
     isReady: () => true,
     fetch: async (): Promise<MlTelemetry> => {
       try {
-        const savedObjectsClient = getSavedObjectsClient(elasticsearchPlugin, savedObjects);
-        const mlTelemetrySavedObject = (await savedObjectsClient.get(
-          'ml-telemetry',
-          ML_TELEMETRY_DOC_ID
-        )) as MlTelemetrySavedObject;
+        const mlTelemetrySavedObject: MlTelemetrySavedObject = await savedObjects
+          .createInternalRepository()
+          .get('ml-telemetry', ML_TELEMETRY_DOC_ID);
+
         return mlTelemetrySavedObject.attributes;
       } catch (err) {
         return createMlTelemetry();
diff --git a/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/ml_telemetry.test.ts b/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/ml_telemetry.test.ts
index fcf3763626b6f..9d14ffb31be63 100644
--- a/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/ml_telemetry.test.ts
+++ b/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/ml_telemetry.test.ts
@@ -6,7 +6,6 @@
 
 import {
   createMlTelemetry,
-  getSavedObjectsClient,
   incrementFileDataVisualizerIndexCreationCount,
   ML_TELEMETRY_DOC_ID,
   MlTelemetry,
@@ -26,22 +25,11 @@ describe('ml_telemetry', () => {
   });
 
   describe('storeMlTelemetry', () => {
-    let elasticsearchPlugin: any;
-    let savedObjects: any;
     let mlTelemetry: MlTelemetry;
-    let savedObjectsClientInstance: any;
+    let internalRepository: any;
 
     beforeEach(() => {
-      savedObjectsClientInstance = { create: jest.fn() };
-      const callWithInternalUser = jest.fn();
-      const internalRepository = jest.fn();
-      elasticsearchPlugin = {
-        getCluster: jest.fn(() => ({ callWithInternalUser })),
-      };
-      savedObjects = {
-        SavedObjectsClient: jest.fn(() => savedObjectsClientInstance),
-        getSavedObjectsRepository: jest.fn(() => internalRepository),
-      };
+      internalRepository = { create: jest.fn(), get: jest.fn() };
       mlTelemetry = {
         file_data_visualizer: {
           index_creation_count: 1,
@@ -49,59 +37,28 @@ describe('ml_telemetry', () => {
       };
     });
 
-    it('should call savedObjectsClient create with the given MlTelemetry object', () => {
-      storeMlTelemetry(elasticsearchPlugin, savedObjects, mlTelemetry);
-      expect(savedObjectsClientInstance.create.mock.calls[0][1]).toBe(mlTelemetry);
+    it('should call internalRepository create with the given MlTelemetry object', () => {
+      storeMlTelemetry(internalRepository, mlTelemetry);
+      expect(internalRepository.create.mock.calls[0][1]).toBe(mlTelemetry);
     });
 
-    it('should call savedObjectsClient create with the ml-telemetry document type and ID', () => {
-      storeMlTelemetry(elasticsearchPlugin, savedObjects, mlTelemetry);
-      expect(savedObjectsClientInstance.create.mock.calls[0][0]).toBe('ml-telemetry');
-      expect(savedObjectsClientInstance.create.mock.calls[0][2].id).toBe(ML_TELEMETRY_DOC_ID);
+    it('should call internalRepository create with the ml-telemetry document type and ID', () => {
+      storeMlTelemetry(internalRepository, mlTelemetry);
+      expect(internalRepository.create.mock.calls[0][0]).toBe('ml-telemetry');
+      expect(internalRepository.create.mock.calls[0][2].id).toBe(ML_TELEMETRY_DOC_ID);
     });
 
-    it('should call savedObjectsClient create with overwrite: true', () => {
-      storeMlTelemetry(elasticsearchPlugin, savedObjects, mlTelemetry);
-      expect(savedObjectsClientInstance.create.mock.calls[0][2].overwrite).toBe(true);
-    });
-  });
-
-  describe('getSavedObjectsClient', () => {
-    let elasticsearchPlugin: any;
-    let savedObjects: any;
-    let savedObjectsClientInstance: any;
-    let callWithInternalUser: any;
-    let internalRepository: any;
-
-    beforeEach(() => {
-      savedObjectsClientInstance = { create: jest.fn() };
-      callWithInternalUser = jest.fn();
-      internalRepository = jest.fn();
-      elasticsearchPlugin = {
-        getCluster: jest.fn(() => ({ callWithInternalUser })),
-      };
-      savedObjects = {
-        SavedObjectsClient: jest.fn(() => savedObjectsClientInstance),
-        getSavedObjectsRepository: jest.fn(() => internalRepository),
-      };
-    });
-
-    it('should return a SavedObjectsClient initialized with the saved objects internal repository', () => {
-      const result = getSavedObjectsClient(elasticsearchPlugin, savedObjects);
-
-      expect(result).toBe(savedObjectsClientInstance);
-      expect(savedObjects.SavedObjectsClient).toHaveBeenCalledWith(internalRepository);
+    it('should call internalRepository create with overwrite: true', () => {
+      storeMlTelemetry(internalRepository, mlTelemetry);
+      expect(internalRepository.create.mock.calls[0][2].overwrite).toBe(true);
     });
   });
 
   describe('incrementFileDataVisualizerIndexCreationCount', () => {
-    let elasticsearchPlugin: any;
     let savedObjects: any;
-    let savedObjectsClientInstance: any;
-    let callWithInternalUser: any;
     let internalRepository: any;
 
-    function createSavedObjectsClientInstance(
+    function createInternalRepositoryInstance(
       telemetryEnabled?: boolean,
       indexCreationCount?: number
     ) {
@@ -136,51 +93,42 @@ describe('ml_telemetry', () => {
     }
 
     function mockInit(telemetryEnabled?: boolean, indexCreationCount?: number): void {
-      savedObjectsClientInstance = createSavedObjectsClientInstance(
-        telemetryEnabled,
-        indexCreationCount
-      );
-      callWithInternalUser = jest.fn();
-      internalRepository = jest.fn();
+      internalRepository = createInternalRepositoryInstance(telemetryEnabled, indexCreationCount);
       savedObjects = {
-        SavedObjectsClient: jest.fn(() => savedObjectsClientInstance),
-        getSavedObjectsRepository: jest.fn(() => internalRepository),
-      };
-      elasticsearchPlugin = {
-        getCluster: jest.fn(() => ({ callWithInternalUser })),
+        createInternalRepository: jest.fn(() => internalRepository),
       };
     }
 
     it('should not increment if telemetry status cannot be determined', async () => {
       mockInit();
-      await incrementFileDataVisualizerIndexCreationCount(elasticsearchPlugin, savedObjects);
+      await incrementFileDataVisualizerIndexCreationCount(savedObjects);
 
-      expect(savedObjectsClientInstance.create.mock.calls).toHaveLength(0);
+      expect(internalRepository.create.mock.calls).toHaveLength(0);
     });
 
     it('should not increment if telemetry status is disabled', async () => {
       mockInit(false);
-      await incrementFileDataVisualizerIndexCreationCount(elasticsearchPlugin, savedObjects);
+      await incrementFileDataVisualizerIndexCreationCount(savedObjects);
 
-      expect(savedObjectsClientInstance.create.mock.calls).toHaveLength(0);
+      expect(internalRepository.create.mock.calls).toHaveLength(0);
     });
 
     it('should initialize index_creation_count with 1', async () => {
       mockInit(true);
-      await incrementFileDataVisualizerIndexCreationCount(elasticsearchPlugin, savedObjects);
+      await incrementFileDataVisualizerIndexCreationCount(savedObjects);
 
-      expect(savedObjectsClientInstance.create.mock.calls[0][0]).toBe('ml-telemetry');
-      expect(savedObjectsClientInstance.create.mock.calls[0][1]).toEqual({
+      expect(internalRepository.create.mock.calls[0][0]).toBe('ml-telemetry');
+      expect(internalRepository.create.mock.calls[0][1]).toEqual({
         file_data_visualizer: { index_creation_count: 1 },
       });
     });
 
     it('should increment index_creation_count to 2', async () => {
       mockInit(true, 1);
-      await incrementFileDataVisualizerIndexCreationCount(elasticsearchPlugin, savedObjects);
+      await incrementFileDataVisualizerIndexCreationCount(savedObjects);
 
-      expect(savedObjectsClientInstance.create.mock.calls[0][0]).toBe('ml-telemetry');
-      expect(savedObjectsClientInstance.create.mock.calls[0][1]).toEqual({
+      expect(internalRepository.create.mock.calls[0][0]).toBe('ml-telemetry');
+      expect(internalRepository.create.mock.calls[0][1]).toEqual({
         file_data_visualizer: { index_creation_count: 2 },
       });
     });
diff --git a/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/ml_telemetry.ts b/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/ml_telemetry.ts
index 1bac3f1780644..d76b1ee94e21e 100644
--- a/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/ml_telemetry.ts
+++ b/x-pack/legacy/plugins/ml/server/lib/ml_telemetry/ml_telemetry.ts
@@ -4,11 +4,13 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { ElasticsearchPlugin } from 'src/legacy/core_plugins/elasticsearch';
-import { SavedObjectsLegacyService } from 'src/legacy/server/kbn_server';
-import { callWithInternalUserFactory } from '../../client/call_with_internal_user_factory';
+import {
+  SavedObjectAttributes,
+  SavedObjectsServiceStart,
+  ISavedObjectsRepository,
+} from 'src/core/server';
 
-export interface MlTelemetry {
+export interface MlTelemetry extends SavedObjectAttributes {
   file_data_visualizer: {
     index_creation_count: number;
   };
@@ -29,35 +31,22 @@ export function createMlTelemetry(count: number = 0): MlTelemetry {
 }
 // savedObjects
 export function storeMlTelemetry(
-  elasticsearchPlugin: ElasticsearchPlugin,
-  savedObjects: SavedObjectsLegacyService,
+  internalRepository: ISavedObjectsRepository,
   mlTelemetry: MlTelemetry
 ): void {
-  const savedObjectsClient = getSavedObjectsClient(elasticsearchPlugin, savedObjects);
-  savedObjectsClient.create('ml-telemetry', mlTelemetry, {
+  internalRepository.create('ml-telemetry', mlTelemetry, {
     id: ML_TELEMETRY_DOC_ID,
     overwrite: true,
   });
 }
-// needs savedObjects and elasticsearchPlugin
-export function getSavedObjectsClient(
-  elasticsearchPlugin: ElasticsearchPlugin,
-  savedObjects: SavedObjectsLegacyService
-): any {
-  const { SavedObjectsClient, getSavedObjectsRepository } = savedObjects;
-  const callWithInternalUser = callWithInternalUserFactory(elasticsearchPlugin);
-  const internalRepository = getSavedObjectsRepository(callWithInternalUser);
-  return new SavedObjectsClient(internalRepository);
-}
 
 export async function incrementFileDataVisualizerIndexCreationCount(
-  elasticsearchPlugin: ElasticsearchPlugin,
-  savedObjects: SavedObjectsLegacyService
+  savedObjects: SavedObjectsServiceStart
 ): Promise<void> {
-  const savedObjectsClient = getSavedObjectsClient(elasticsearchPlugin, savedObjects);
-
+  const internalRepository = await savedObjects.createInternalRepository();
   try {
-    const { attributes } = await savedObjectsClient.get('telemetry', 'telemetry');
+    const { attributes } = await internalRepository.get('telemetry', 'telemetry');
+
     if (attributes.enabled === false) {
       return;
     }
@@ -70,7 +59,7 @@ export async function incrementFileDataVisualizerIndexCreationCount(
   let indicesCount = 1;
 
   try {
-    const { attributes } = (await savedObjectsClient.get(
+    const { attributes } = (await internalRepository.get(
       'ml-telemetry',
       ML_TELEMETRY_DOC_ID
     )) as MlTelemetrySavedObject;
@@ -80,5 +69,5 @@ export async function incrementFileDataVisualizerIndexCreationCount(
   }
 
   const mlTelemetry = createMlTelemetry(indicesCount);
-  storeMlTelemetry(elasticsearchPlugin, savedObjects, mlTelemetry);
+  storeMlTelemetry(internalRepository, mlTelemetry);
 }
diff --git a/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts b/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts
index 10961182be841..43c276ac63a13 100644
--- a/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts
+++ b/x-pack/legacy/plugins/ml/server/new_platform/plugin.ts
@@ -14,6 +14,7 @@ import {
   CoreSetup,
   IRouter,
   IScopedClusterClient,
+  SavedObjectsServiceStart,
 } from 'src/core/server';
 import { ElasticsearchPlugin } from 'src/legacy/core_plugins/elasticsearch';
 import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
@@ -28,12 +29,10 @@ import { LICENSE_TYPE } from '../../common/constants/license';
 import { annotationRoutes } from '../routes/annotations';
 import { jobRoutes } from '../routes/anomaly_detectors';
 import { dataFeedRoutes } from '../routes/datafeeds';
-// @ts-ignore: could not find declaration file for module
 import { indicesRoutes } from '../routes/indices';
 import { jobValidationRoutes } from '../routes/job_validation';
 import { makeMlUsageCollector } from '../lib/ml_telemetry';
 import { notificationRoutes } from '../routes/notification_settings';
-// @ts-ignore: could not find declaration file for module
 import { systemRoutes } from '../routes/system';
 import { dataFrameAnalyticsRoutes } from '../routes/data_frame_analytics';
 import { dataRecognizer } from '../routes/modules';
@@ -45,7 +44,6 @@ import { filtersRoutes } from '../routes/filters';
 import { resultsServiceRoutes } from '../routes/results_service';
 import { jobServiceRoutes } from '../routes/job_service';
 import { jobAuditMessagesRoutes } from '../routes/job_audit_messages';
-// @ts-ignore: could not find declaration file for module
 import { fileDataVisualizerRoutes } from '../routes/file_data_visualizer';
 import { initMlServerLog, LogInitialization } from '../client/log';
 import { HomeServerPluginSetup } from '../../../../../../src/plugins/home/server';
@@ -67,6 +65,7 @@ export interface MlCoreSetup {
   injectUiAppVars: (id: string, callback: () => {}) => any;
   http: MlHttpServiceSetup;
   savedObjects: SavedObjectsLegacyService;
+  coreSavedObjects: SavedObjectsServiceStart;
   elasticsearch: ElasticsearchServiceSetup;
 }
 export interface MlInitializerContext extends PluginInitializerContext {
@@ -93,15 +92,11 @@ export interface RouteInitialization {
   route(route: ServerRoute | ServerRoute[]): void;
   router: IRouter;
   xpackMainPlugin: MlXpackMainPlugin;
-  savedObjects?: SavedObjectsLegacyService;
+  savedObjects?: SavedObjectsServiceStart;
   spacesPlugin: any;
   securityPlugin: any;
   cloud?: CloudSetup;
 }
-export interface UsageInitialization {
-  elasticsearchPlugin: ElasticsearchPlugin;
-  savedObjects: SavedObjectsLegacyService;
-}
 
 declare module 'kibana/server' {
   interface RequestHandlerContext {
@@ -123,7 +118,7 @@ export class Plugin {
 
   public setup(core: MlCoreSetup, plugins: PluginsSetup) {
     const xpackMainPlugin: MlXpackMainPlugin = plugins.xpackMain;
-    const { http } = core;
+    const { http, coreSavedObjects } = core;
     const pluginId = this.pluginId;
 
     mirrorPluginStatus(xpackMainPlugin, plugins.ml);
@@ -208,14 +203,10 @@ export class Plugin {
     const extendedRouteInitializationDeps: RouteInitialization = {
       ...routeInitializationDeps,
       config: this.config,
-      savedObjects: core.savedObjects,
+      savedObjects: coreSavedObjects,
       spacesPlugin: plugins.spaces,
       cloud: plugins.cloud,
     };
-    const usageInitializationDeps: UsageInitialization = {
-      elasticsearchPlugin: plugins.elasticsearch,
-      savedObjects: core.savedObjects,
-    };
 
     const logInitializationDeps: LogInitialization = {
       log: this.log,
@@ -240,7 +231,7 @@ export class Plugin {
     fileDataVisualizerRoutes(extendedRouteInitializationDeps);
 
     initMlServerLog(logInitializationDeps);
-    makeMlUsageCollector(plugins.usageCollection, usageInitializationDeps);
+    makeMlUsageCollector(plugins.usageCollection, coreSavedObjects);
   }
 
   public stop() {}
diff --git a/x-pack/legacy/plugins/ml/server/routes/file_data_visualizer.ts b/x-pack/legacy/plugins/ml/server/routes/file_data_visualizer.ts
index 95f2a9fe7298f..d5a992c933293 100644
--- a/x-pack/legacy/plugins/ml/server/routes/file_data_visualizer.ts
+++ b/x-pack/legacy/plugins/ml/server/routes/file_data_visualizer.ts
@@ -138,7 +138,7 @@ export function fileDataVisualizerRoutes({
         // follow-up import calls to just add additional data will include the `id` of the created
         // index, we'll ignore those and don't increment the counter.
         if (id === undefined) {
-          await incrementFileDataVisualizerIndexCreationCount(elasticsearchPlugin, savedObjects!);
+          await incrementFileDataVisualizerIndexCreationCount(savedObjects!);
         }
 
         const result = await importData(
diff --git a/x-pack/legacy/plugins/ml/server/routes/job_audit_messages.ts b/x-pack/legacy/plugins/ml/server/routes/job_audit_messages.ts
index 7298312990005..76986b935b993 100644
--- a/x-pack/legacy/plugins/ml/server/routes/job_audit_messages.ts
+++ b/x-pack/legacy/plugins/ml/server/routes/job_audit_messages.ts
@@ -50,7 +50,7 @@ export function jobAuditMessagesRoutes({ xpackMainPlugin, router }: RouteInitial
   /**
    * @apiGroup JobAuditMessages
    *
-   * @api {get} /api/ml/results/anomalies_table_data Get all audit messages
+   * @api {get} /api/ml/job_audit_messages/messages Get all audit messages
    * @apiName GetAllJobAuditMessages
    * @apiDescription Returns all audit messages
    */

From c6f5fdd061d93ad0d67335a658449be92e24640c Mon Sep 17 00:00:00 2001
From: Marta Bondyra <marta.bondyra@elastic.co>
Date: Mon, 24 Feb 2020 17:42:34 +0100
Subject: [PATCH 144/174] Advanced settings UI change to centralize save state
 (#53693)

---
 .../advanced_settings/public/_index.scss      |    2 +-
 .../public/management_app/_index.scss         |    3 +
 .../management_app/advanced_settings.scss     |   40 +-
 .../management_app/advanced_settings.tsx      |   16 +-
 .../management_app/components/_index.scss     |    1 +
 .../field/__snapshots__/field.test.tsx.snap   | 7069 ++++++++---------
 .../components/field/field.test.tsx           |  267 +-
 .../management_app/components/field/field.tsx |  545 +-
 .../management_app/components/field/index.ts  |    2 +-
 .../form/__snapshots__/form.test.tsx.snap     | 1228 ++-
 .../management_app/components/form/_form.scss |   13 +
 .../components/form/_index.scss               |    1 +
 .../components/form/form.test.tsx             |  114 +-
 .../management_app/components/form/form.tsx   |  280 +-
 .../public/management_app/types.ts            |   13 +
 .../telemetry_management_section.tsx          |   54 +-
 test/functional/page_objects/settings_page.ts |    6 +-
 .../translations/translations/ja-JP.json      |   11 -
 .../translations/translations/zh-CN.json      |   10 -
 19 files changed, 5086 insertions(+), 4589 deletions(-)
 create mode 100644 src/plugins/advanced_settings/public/management_app/_index.scss
 create mode 100644 src/plugins/advanced_settings/public/management_app/components/_index.scss
 create mode 100644 src/plugins/advanced_settings/public/management_app/components/form/_form.scss
 create mode 100644 src/plugins/advanced_settings/public/management_app/components/form/_index.scss

diff --git a/src/plugins/advanced_settings/public/_index.scss b/src/plugins/advanced_settings/public/_index.scss
index f3fe78bf6a9c0..d13c37bff32d0 100644
--- a/src/plugins/advanced_settings/public/_index.scss
+++ b/src/plugins/advanced_settings/public/_index.scss
@@ -17,4 +17,4 @@
  * under the License.
  */
 
- @import './management_app/advanced_settings';
+@import './management_app/index';
diff --git a/src/plugins/advanced_settings/public/management_app/_index.scss b/src/plugins/advanced_settings/public/management_app/_index.scss
new file mode 100644
index 0000000000000..aa1980692f7b7
--- /dev/null
+++ b/src/plugins/advanced_settings/public/management_app/_index.scss
@@ -0,0 +1,3 @@
+@import './advanced_settings';
+
+@import './components/index';
diff --git a/src/plugins/advanced_settings/public/management_app/advanced_settings.scss b/src/plugins/advanced_settings/public/management_app/advanced_settings.scss
index 79b6feccb6b7d..016edb2817da8 100644
--- a/src/plugins/advanced_settings/public/management_app/advanced_settings.scss
+++ b/src/plugins/advanced_settings/public/management_app/advanced_settings.scss
@@ -17,21 +17,27 @@
  * under the License.
  */
 
-.mgtAdvancedSettings__field {
+ .mgtAdvancedSettings__field {
   + * {
     margin-top: $euiSize;
   }
 
-  &Wrapper {
-    width: 640px;
 
-    @include internetExplorerOnly() {
-      min-height: 1px;
-    }
+  padding-left: $euiSizeS;
+  margin-left: -$euiSizeS;
+  &--unsaved {
+    // Simulates a left side border without shifting content
+    box-shadow: -$euiSizeXS 0px $euiColorSecondary;
   }
-
-  &Actions {
-    padding-top: $euiSizeM;
+  &--invalid {
+    // Simulates a left side border without shifting content
+    box-shadow: -$euiSizeXS 0px $euiColorDanger;
+  }
+  @include internetExplorerOnly() {
+    min-height: 1px;
+  }
+  &Row {
+    padding-left: $euiSizeS;
   }
 
   @include internetExplorerOnly {
@@ -40,3 +46,19 @@
     }
   }
 }
+
+.mgtAdvancedSettingsForm__unsavedCount {
+  @include euiBreakpoint('xs', 's') {
+    display: none;
+  }
+}
+
+.mgtAdvancedSettingsForm__unsavedCountMessage{
+  // Simulates a left side border without shifting content
+  box-shadow: -$euiSizeXS 0px $euiColorSecondary;
+  padding-left: $euiSizeS;
+}
+
+.mgtAdvancedSettingsForm__button {
+  width: 100%;
+}
diff --git a/src/plugins/advanced_settings/public/management_app/advanced_settings.tsx b/src/plugins/advanced_settings/public/management_app/advanced_settings.tsx
index 5057d072e3e41..39312c9340ff9 100644
--- a/src/plugins/advanced_settings/public/management_app/advanced_settings.tsx
+++ b/src/plugins/advanced_settings/public/management_app/advanced_settings.tsx
@@ -38,7 +38,7 @@ import { ComponentRegistry } from '../';
 
 import { getAriaName, toEditableConfig, DEFAULT_CATEGORY } from './lib';
 
-import { FieldSetting, IQuery } from './types';
+import { FieldSetting, IQuery, SettingsChanges } from './types';
 
 interface AdvancedSettingsProps {
   enableSaving: boolean;
@@ -177,6 +177,13 @@ export class AdvancedSettingsComponent extends Component<
     });
   };
 
+  saveConfig = async (changes: SettingsChanges) => {
+    const arr = Object.entries(changes).map(([key, value]) =>
+      this.props.uiSettings.set(key, value)
+    );
+    return Promise.all(arr);
+  };
+
   render() {
     const { filteredSettings, query, footerQueryMatched } = this.state;
     const componentRegistry = this.props.componentRegistry;
@@ -205,18 +212,19 @@ export class AdvancedSettingsComponent extends Component<
         <AdvancedSettingsVoiceAnnouncement queryText={query.text} settings={filteredSettings} />
 
         <Form
-          settings={filteredSettings}
+          settings={this.groupedSettings}
+          visibleSettings={filteredSettings}
           categories={this.categories}
           categoryCounts={this.categoryCounts}
           clearQuery={this.clearQuery}
-          save={this.props.uiSettings.set.bind(this.props.uiSettings)}
-          clear={this.props.uiSettings.remove.bind(this.props.uiSettings)}
+          save={this.saveConfig}
           showNoResultsMessage={!footerQueryMatched}
           enableSaving={this.props.enableSaving}
           dockLinks={this.props.dockLinks}
           toasts={this.props.toasts}
         />
         <PageFooter
+          toasts={this.props.toasts}
           query={query}
           onQueryMatchChange={this.onFooterQueryMatchChange}
           enableSaving={this.props.enableSaving}
diff --git a/src/plugins/advanced_settings/public/management_app/components/_index.scss b/src/plugins/advanced_settings/public/management_app/components/_index.scss
new file mode 100644
index 0000000000000..d2d2e38947f76
--- /dev/null
+++ b/src/plugins/advanced_settings/public/management_app/components/_index.scss
@@ -0,0 +1 @@
+@import './form/index';
diff --git a/src/plugins/advanced_settings/public/management_app/components/field/__snapshots__/field.test.tsx.snap b/src/plugins/advanced_settings/public/management_app/components/field/__snapshots__/field.test.tsx.snap
index 5121785b05cad..2f4d806e60244 100644
--- a/src/plugins/advanced_settings/public/management_app/components/field/__snapshots__/field.test.tsx.snap
+++ b/src/plugins/advanced_settings/public/management_app/components/field/__snapshots__/field.test.tsx.snap
@@ -1,3912 +1,3821 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`Field for array setting should render as read only if saving is disabled 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Array test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Array test setting
+      
+    </h3>
+  }
 >
-  <EuiFlexItem
-    grow={false}
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="array:test:setting"
+    labelType="label"
   >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Array test setting",
-              }
-            }
-          />
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Array test setting
-          
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "array:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={null}
-        isInvalid={false}
-        label="array:test:setting"
-        labelType="label"
-      >
-        <EuiFieldText
-          aria-label="array test setting"
-          data-test-subj="advancedSetting-editField-array:test:setting"
-          disabled={true}
-          isLoading={false}
-          onChange={[Function]}
-          onKeyDown={[Function]}
-          value="default_value"
-        />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+    <EuiFieldText
+      aria-label="array test setting"
+      data-test-subj="advancedSetting-editField-array:test:setting"
+      disabled={true}
+      fullWidth={true}
+      onChange={[Function]}
+      value="default_value"
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for array setting should render as read only with help text if overridden 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
->
-  <EuiFlexItem
-    grow={false}
-  >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Array test setting",
-              }
-            }
-          />
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Array test setting",
+          }
+        }
+      />
+      <React.Fragment>
+        <EuiSpacer
+          size="s"
+        />
+        <EuiText
+          size="xs"
+        >
           <React.Fragment>
-            <EuiSpacer
-              size="s"
-            />
-            <EuiText
-              size="xs"
-            >
-              <React.Fragment>
-                <FormattedMessage
-                  defaultMessage="Default: {value}"
-                  id="advancedSettings.field.defaultValueText"
-                  values={
-                    Object {
-                      "value": <EuiCode>
-                        default_value
-                      </EuiCode>,
-                    }
-                  }
-                />
-              </React.Fragment>
-            </EuiText>
-          </React.Fragment>
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Array test setting
-          
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "array:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={
-          <EuiText
-            size="xs"
-          >
             <FormattedMessage
-              defaultMessage="This setting is overridden by the Kibana server and can not be changed."
-              id="advancedSettings.field.helpText"
-              values={Object {}}
+              defaultMessage="Default: {value}"
+              id="advancedSettings.field.defaultValueText"
+              values={
+                Object {
+                  "value": <EuiCode>
+                    default_value
+                  </EuiCode>,
+                }
+              }
             />
-          </EuiText>
-        }
-        isInvalid={false}
-        label="array:test:setting"
-        labelType="label"
+          </React.Fragment>
+        </EuiText>
+      </React.Fragment>
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Array test setting
+      
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={
+      <EuiText
+        size="xs"
       >
-        <EuiFieldText
-          aria-label="array test setting"
-          data-test-subj="advancedSetting-editField-array:test:setting"
-          disabled={true}
-          isLoading={false}
-          onChange={[Function]}
-          onKeyDown={[Function]}
-          value="user, value"
+        <FormattedMessage
+          defaultMessage="This setting is overridden by the Kibana server and can not be changed."
+          id="advancedSettings.field.helpText"
+          values={Object {}}
         />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+      </EuiText>
+    }
+    label="array:test:setting"
+    labelType="label"
+  >
+    <EuiFieldText
+      aria-label="array test setting"
+      data-test-subj="advancedSetting-editField-array:test:setting"
+      disabled={true}
+      fullWidth={true}
+      onChange={[Function]}
+      value="user, value"
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for array setting should render custom setting icon if it is custom 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Array test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Array test setting
+      <EuiIconTip
+        aria-label="Custom setting"
+        color="primary"
+        content={
+          <FormattedMessage
+            defaultMessage="Custom setting"
+            id="advancedSettings.field.customSettingTooltip"
+            values={Object {}}
+          />
+        }
+        type="asterisk"
+      />
+    </h3>
+  }
 >
-  <EuiFlexItem
-    grow={false}
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="array:test:setting"
+    labelType="label"
   >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Array test setting",
-              }
-            }
-          />
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Array test setting
-          <EuiIconTip
-            aria-label="Custom setting"
-            color="primary"
-            content={
-              <FormattedMessage
-                defaultMessage="Custom setting"
-                id="advancedSettings.field.customSettingTooltip"
-                values={Object {}}
-              />
-            }
-            type="asterisk"
-          />
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "array:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={null}
-        isInvalid={false}
-        label="array:test:setting"
-        labelType="label"
-      >
-        <EuiFieldText
-          aria-label="array test setting"
-          data-test-subj="advancedSetting-editField-array:test:setting"
-          disabled={false}
-          isLoading={false}
-          onChange={[Function]}
-          onKeyDown={[Function]}
-          value="default_value"
-        />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+    <EuiFieldText
+      aria-label="array test setting"
+      data-test-subj="advancedSetting-editField-array:test:setting"
+      disabled={false}
+      fullWidth={true}
+      onChange={[Function]}
+      value="default_value"
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for array setting should render default value if there is no user value set 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Array test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Array test setting
+      
+    </h3>
+  }
 >
-  <EuiFlexItem
-    grow={false}
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="array:test:setting"
+    labelType="label"
   >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Array test setting",
-              }
-            }
-          />
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Array test setting
-          
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "array:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={null}
-        isInvalid={false}
-        label="array:test:setting"
-        labelType="label"
+    <EuiFieldText
+      aria-label="array test setting"
+      data-test-subj="advancedSetting-editField-array:test:setting"
+      disabled={false}
+      fullWidth={true}
+      onChange={[Function]}
+      value="default_value"
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
+`;
+
+exports[`Field for array setting should render unsaved value if there are unsaved changes 1`] = `
+<EuiDescribedFormGroup
+  className="mgtAdvancedSettings__field mgtAdvancedSettings__field--unsaved"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Array test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Array test setting
+      <EuiIconTip
+        aria-label="Custom setting"
+        color="primary"
+        content={
+          <FormattedMessage
+            defaultMessage="Custom setting"
+            id="advancedSettings.field.customSettingTooltip"
+            values={Object {}}
+          />
+        }
+        type="asterisk"
+      />
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="array:test:setting"
+    labelType="label"
+  >
+    <EuiFieldText
+      aria-describedby="array:test:setting"
+      aria-label="array test setting"
+      data-test-subj="advancedSetting-editField-array:test:setting"
+      disabled={false}
+      fullWidth={true}
+      onChange={[Function]}
+      value={
+        Array [
+          "example_value",
+        ]
+      }
+    />
+    <EuiScreenReaderOnly>
+      <p
+        id="array:test:setting"
       >
-        <EuiFieldText
-          aria-label="array test setting"
-          data-test-subj="advancedSetting-editField-array:test:setting"
-          disabled={false}
-          isLoading={false}
-          onChange={[Function]}
-          onKeyDown={[Function]}
-          value="default_value"
-        />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+        Setting is currently not saved.
+      </p>
+    </EuiScreenReaderOnly>
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for array setting should render user value if there is user value is set 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
->
-  <EuiFlexItem
-    grow={false}
-  >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Array test setting",
-              }
-            }
-          />
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Array test setting",
+          }
+        }
+      />
+      <React.Fragment>
+        <EuiSpacer
+          size="s"
+        />
+        <EuiText
+          size="xs"
+        >
           <React.Fragment>
-            <EuiSpacer
-              size="s"
+            <FormattedMessage
+              defaultMessage="Default: {value}"
+              id="advancedSettings.field.defaultValueText"
+              values={
+                Object {
+                  "value": <EuiCode>
+                    default_value
+                  </EuiCode>,
+                }
+              }
             />
-            <EuiText
-              size="xs"
-            >
-              <React.Fragment>
-                <FormattedMessage
-                  defaultMessage="Default: {value}"
-                  id="advancedSettings.field.defaultValueText"
-                  values={
-                    Object {
-                      "value": <EuiCode>
-                        default_value
-                      </EuiCode>,
-                    }
-                  }
-                />
-              </React.Fragment>
-            </EuiText>
           </React.Fragment>
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Array test setting
-          
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "array:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={
-          <span>
-            <span>
-              <ForwardRef
-                aria-label="Reset array test setting to default"
-                data-test-subj="advancedSetting-resetField-array:test:setting"
-                onClick={[Function]}
-              >
-                <FormattedMessage
-                  defaultMessage="Reset to default"
-                  id="advancedSettings.field.resetToDefaultLinkText"
-                  values={Object {}}
-                />
-              </ForwardRef>
-                 
-            </span>
-          </span>
-        }
-        isInvalid={false}
-        label="array:test:setting"
-        labelType="label"
-      >
-        <EuiFieldText
-          aria-label="array test setting"
-          data-test-subj="advancedSetting-editField-array:test:setting"
-          disabled={false}
-          isLoading={false}
-          onChange={[Function]}
-          onKeyDown={[Function]}
-          value="user, value"
-        />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+        </EuiText>
+      </React.Fragment>
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Array test setting
+      
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={
+      <span>
+        <span>
+          <ForwardRef
+            aria-label="Reset array test setting to default"
+            data-test-subj="advancedSetting-resetField-array:test:setting"
+            onClick={[Function]}
+          >
+            <FormattedMessage
+              defaultMessage="Reset to default"
+              id="advancedSettings.field.resetToDefaultLinkText"
+              values={Object {}}
+            />
+          </ForwardRef>
+             
+        </span>
+      </span>
+    }
+    label="array:test:setting"
+    labelType="label"
+  >
+    <EuiFieldText
+      aria-label="array test setting"
+      data-test-subj="advancedSetting-editField-array:test:setting"
+      disabled={false}
+      fullWidth={true}
+      onChange={[Function]}
+      value="user, value"
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for boolean setting should render as read only if saving is disabled 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Boolean test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Boolean test setting
+      
+    </h3>
+  }
 >
-  <EuiFlexItem
-    grow={false}
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={false}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="boolean:test:setting"
+    labelType="label"
   >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Boolean test setting",
-              }
-            }
-          />
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Boolean test setting
-          
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "boolean:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={false}
-        hasEmptyLabelSpace={false}
-        helpText={null}
-        isInvalid={false}
-        label="boolean:test:setting"
-        labelType="label"
-      >
-        <EuiSwitch
-          aria-label="boolean test setting"
-          checked={true}
-          data-test-subj="advancedSetting-editField-boolean:test:setting"
-          disabled={true}
-          label={
-            <FormattedMessage
-              defaultMessage="On"
-              id="advancedSettings.field.onLabel"
-              values={Object {}}
-            />
-          }
-          onChange={[Function]}
-          onKeyDown={[Function]}
+    <EuiSwitch
+      aria-label="boolean test setting"
+      checked={true}
+      data-test-subj="advancedSetting-editField-boolean:test:setting"
+      disabled={true}
+      label={
+        <FormattedMessage
+          defaultMessage="On"
+          id="advancedSettings.field.onLabel"
+          values={Object {}}
         />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+      }
+      onChange={[Function]}
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for boolean setting should render as read only with help text if overridden 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
->
-  <EuiFlexItem
-    grow={false}
-  >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Boolean test setting",
-              }
-            }
-          />
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Boolean test setting",
+          }
+        }
+      />
+      <React.Fragment>
+        <EuiSpacer
+          size="s"
+        />
+        <EuiText
+          size="xs"
+        >
           <React.Fragment>
-            <EuiSpacer
-              size="s"
-            />
-            <EuiText
-              size="xs"
-            >
-              <React.Fragment>
-                <FormattedMessage
-                  defaultMessage="Default: {value}"
-                  id="advancedSettings.field.defaultValueText"
-                  values={
-                    Object {
-                      "value": <EuiCode>
-                        true
-                      </EuiCode>,
-                    }
-                  }
-                />
-              </React.Fragment>
-            </EuiText>
-          </React.Fragment>
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Boolean test setting
-          
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "boolean:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={false}
-        hasEmptyLabelSpace={false}
-        helpText={
-          <EuiText
-            size="xs"
-          >
             <FormattedMessage
-              defaultMessage="This setting is overridden by the Kibana server and can not be changed."
-              id="advancedSettings.field.helpText"
-              values={Object {}}
+              defaultMessage="Default: {value}"
+              id="advancedSettings.field.defaultValueText"
+              values={
+                Object {
+                  "value": <EuiCode>
+                    true
+                  </EuiCode>,
+                }
+              }
             />
-          </EuiText>
-        }
-        isInvalid={false}
-        label="boolean:test:setting"
-        labelType="label"
+          </React.Fragment>
+        </EuiText>
+      </React.Fragment>
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Boolean test setting
+      
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={false}
+    hasEmptyLabelSpace={false}
+    helpText={
+      <EuiText
+        size="xs"
       >
-        <EuiSwitch
-          aria-label="boolean test setting"
-          checked={false}
-          data-test-subj="advancedSetting-editField-boolean:test:setting"
-          disabled={true}
-          label={
-            <FormattedMessage
-              defaultMessage="Off"
-              id="advancedSettings.field.offLabel"
-              values={Object {}}
-            />
-          }
-          onChange={[Function]}
-          onKeyDown={[Function]}
+        <FormattedMessage
+          defaultMessage="This setting is overridden by the Kibana server and can not be changed."
+          id="advancedSettings.field.helpText"
+          values={Object {}}
+        />
+      </EuiText>
+    }
+    label="boolean:test:setting"
+    labelType="label"
+  >
+    <EuiSwitch
+      aria-label="boolean test setting"
+      checked={false}
+      data-test-subj="advancedSetting-editField-boolean:test:setting"
+      disabled={true}
+      label={
+        <FormattedMessage
+          defaultMessage="Off"
+          id="advancedSettings.field.offLabel"
+          values={Object {}}
         />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+      }
+      onChange={[Function]}
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for boolean setting should render custom setting icon if it is custom 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Boolean test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Boolean test setting
+      <EuiIconTip
+        aria-label="Custom setting"
+        color="primary"
+        content={
+          <FormattedMessage
+            defaultMessage="Custom setting"
+            id="advancedSettings.field.customSettingTooltip"
+            values={Object {}}
+          />
+        }
+        type="asterisk"
+      />
+    </h3>
+  }
 >
-  <EuiFlexItem
-    grow={false}
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={false}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="boolean:test:setting"
+    labelType="label"
   >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Boolean test setting",
-              }
-            }
-          />
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Boolean test setting
-          <EuiIconTip
-            aria-label="Custom setting"
-            color="primary"
-            content={
-              <FormattedMessage
-                defaultMessage="Custom setting"
-                id="advancedSettings.field.customSettingTooltip"
-                values={Object {}}
-              />
-            }
-            type="asterisk"
-          />
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "boolean:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={false}
-        hasEmptyLabelSpace={false}
-        helpText={null}
-        isInvalid={false}
-        label="boolean:test:setting"
-        labelType="label"
-      >
-        <EuiSwitch
-          aria-label="boolean test setting"
-          checked={true}
-          data-test-subj="advancedSetting-editField-boolean:test:setting"
-          disabled={false}
-          label={
-            <FormattedMessage
-              defaultMessage="On"
-              id="advancedSettings.field.onLabel"
-              values={Object {}}
-            />
-          }
-          onChange={[Function]}
-          onKeyDown={[Function]}
+    <EuiSwitch
+      aria-label="boolean test setting"
+      checked={true}
+      data-test-subj="advancedSetting-editField-boolean:test:setting"
+      disabled={false}
+      label={
+        <FormattedMessage
+          defaultMessage="On"
+          id="advancedSettings.field.onLabel"
+          values={Object {}}
         />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+      }
+      onChange={[Function]}
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for boolean setting should render default value if there is no user value set 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Boolean test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Boolean test setting
+      
+    </h3>
+  }
 >
-  <EuiFlexItem
-    grow={false}
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={false}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="boolean:test:setting"
+    labelType="label"
   >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Boolean test setting",
-              }
-            }
-          />
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Boolean test setting
-          
-        </h3>
+    <EuiSwitch
+      aria-label="boolean test setting"
+      checked={true}
+      data-test-subj="advancedSetting-editField-boolean:test:setting"
+      disabled={false}
+      label={
+        <FormattedMessage
+          defaultMessage="On"
+          id="advancedSettings.field.onLabel"
+          values={Object {}}
+        />
       }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "boolean:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={false}
-        hasEmptyLabelSpace={false}
-        helpText={null}
-        isInvalid={false}
-        label="boolean:test:setting"
-        labelType="label"
-      >
-        <EuiSwitch
-          aria-label="boolean test setting"
-          checked={true}
-          data-test-subj="advancedSetting-editField-boolean:test:setting"
-          disabled={false}
-          label={
-            <FormattedMessage
-              defaultMessage="On"
-              id="advancedSettings.field.onLabel"
-              values={Object {}}
-            />
+      onChange={[Function]}
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
+`;
+
+exports[`Field for boolean setting should render unsaved value if there are unsaved changes 1`] = `
+<EuiDescribedFormGroup
+  className="mgtAdvancedSettings__field mgtAdvancedSettings__field--unsaved"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Boolean test setting",
           }
-          onChange={[Function]}
-          onKeyDown={[Function]}
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Boolean test setting
+      <EuiIconTip
+        aria-label="Custom setting"
+        color="primary"
+        content={
+          <FormattedMessage
+            defaultMessage="Custom setting"
+            id="advancedSettings.field.customSettingTooltip"
+            values={Object {}}
+          />
+        }
+        type="asterisk"
+      />
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={false}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="boolean:test:setting"
+    labelType="label"
+  >
+    <EuiSwitch
+      aria-describedby="boolean:test:setting"
+      aria-label="boolean test setting"
+      checked={false}
+      data-test-subj="advancedSetting-editField-boolean:test:setting"
+      disabled={false}
+      label={
+        <FormattedMessage
+          defaultMessage="Off"
+          id="advancedSettings.field.offLabel"
+          values={Object {}}
         />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+      }
+      onChange={[Function]}
+    />
+    <EuiScreenReaderOnly>
+      <p
+        id="boolean:test:setting"
+      >
+        Setting is currently not saved.
+      </p>
+    </EuiScreenReaderOnly>
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for boolean setting should render user value if there is user value is set 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
->
-  <EuiFlexItem
-    grow={false}
-  >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Boolean test setting",
-              }
-            }
-          />
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Boolean test setting",
+          }
+        }
+      />
+      <React.Fragment>
+        <EuiSpacer
+          size="s"
+        />
+        <EuiText
+          size="xs"
+        >
           <React.Fragment>
-            <EuiSpacer
-              size="s"
+            <FormattedMessage
+              defaultMessage="Default: {value}"
+              id="advancedSettings.field.defaultValueText"
+              values={
+                Object {
+                  "value": <EuiCode>
+                    true
+                  </EuiCode>,
+                }
+              }
             />
-            <EuiText
-              size="xs"
-            >
-              <React.Fragment>
-                <FormattedMessage
-                  defaultMessage="Default: {value}"
-                  id="advancedSettings.field.defaultValueText"
-                  values={
-                    Object {
-                      "value": <EuiCode>
-                        true
-                      </EuiCode>,
-                    }
-                  }
-                />
-              </React.Fragment>
-            </EuiText>
           </React.Fragment>
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Boolean test setting
-          
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "boolean:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={false}
-        hasEmptyLabelSpace={false}
-        helpText={
-          <span>
-            <span>
-              <ForwardRef
-                aria-label="Reset boolean test setting to default"
-                data-test-subj="advancedSetting-resetField-boolean:test:setting"
-                onClick={[Function]}
-              >
-                <FormattedMessage
-                  defaultMessage="Reset to default"
-                  id="advancedSettings.field.resetToDefaultLinkText"
-                  values={Object {}}
-                />
-              </ForwardRef>
-                 
-            </span>
-          </span>
-        }
-        isInvalid={false}
-        label="boolean:test:setting"
-        labelType="label"
-      >
-        <EuiSwitch
-          aria-label="boolean test setting"
-          checked={false}
-          data-test-subj="advancedSetting-editField-boolean:test:setting"
-          disabled={false}
-          label={
+        </EuiText>
+      </React.Fragment>
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Boolean test setting
+      
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={false}
+    hasEmptyLabelSpace={false}
+    helpText={
+      <span>
+        <span>
+          <ForwardRef
+            aria-label="Reset boolean test setting to default"
+            data-test-subj="advancedSetting-resetField-boolean:test:setting"
+            onClick={[Function]}
+          >
             <FormattedMessage
-              defaultMessage="Off"
-              id="advancedSettings.field.offLabel"
+              defaultMessage="Reset to default"
+              id="advancedSettings.field.resetToDefaultLinkText"
               values={Object {}}
             />
-          }
-          onChange={[Function]}
-          onKeyDown={[Function]}
+          </ForwardRef>
+             
+        </span>
+      </span>
+    }
+    label="boolean:test:setting"
+    labelType="label"
+  >
+    <EuiSwitch
+      aria-label="boolean test setting"
+      checked={false}
+      data-test-subj="advancedSetting-editField-boolean:test:setting"
+      disabled={false}
+      label={
+        <FormattedMessage
+          defaultMessage="Off"
+          id="advancedSettings.field.offLabel"
+          values={Object {}}
         />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+      }
+      onChange={[Function]}
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for image setting should render as read only if saving is disabled 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Image test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Image test setting
+      
+    </h3>
+  }
 >
-  <EuiFlexItem
-    grow={false}
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="image:test:setting"
+    labelType="label"
   >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Image test setting",
-              }
-            }
-          />
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Image test setting
-          
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "image:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={null}
-        isInvalid={false}
-        label="image:test:setting"
-        labelType="label"
-      >
-        <EuiFilePicker
-          accept=".jpg,.jpeg,.png"
-          compressed={false}
-          data-test-subj="advancedSetting-editField-image:test:setting"
-          disabled={true}
-          display="large"
-          initialPromptText="Select or drag and drop a file"
-          onChange={[Function]}
-          onKeyDown={[Function]}
-        />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+    <EuiFilePicker
+      accept=".jpg,.jpeg,.png"
+      compressed={false}
+      data-test-subj="advancedSetting-editField-image:test:setting"
+      disabled={true}
+      display="large"
+      fullWidth={true}
+      initialPromptText="Select or drag and drop a file"
+      onChange={[Function]}
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for image setting should render as read only with help text if overridden 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
->
-  <EuiFlexItem
-    grow={false}
-  >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Image test setting",
-              }
-            }
-          />
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Image test setting",
+          }
+        }
+      />
+      <React.Fragment>
+        <EuiSpacer
+          size="s"
+        />
+        <EuiText
+          size="xs"
+        >
           <React.Fragment>
-            <EuiSpacer
-              size="s"
-            />
-            <EuiText
-              size="xs"
-            >
-              <React.Fragment>
-                <FormattedMessage
-                  defaultMessage="Default: {value}"
-                  id="advancedSettings.field.defaultValueText"
-                  values={
-                    Object {
-                      "value": <EuiCode>
-                        null
-                      </EuiCode>,
-                    }
-                  }
-                />
-              </React.Fragment>
-            </EuiText>
-          </React.Fragment>
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Image test setting
-          
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "image:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={
-          <EuiText
-            size="xs"
-          >
             <FormattedMessage
-              defaultMessage="This setting is overridden by the Kibana server and can not be changed."
-              id="advancedSettings.field.helpText"
-              values={Object {}}
+              defaultMessage="Default: {value}"
+              id="advancedSettings.field.defaultValueText"
+              values={
+                Object {
+                  "value": <EuiCode>
+                    null
+                  </EuiCode>,
+                }
+              }
             />
-          </EuiText>
-        }
-        isInvalid={false}
-        label="image:test:setting"
-        labelType="label"
+          </React.Fragment>
+        </EuiText>
+      </React.Fragment>
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Image test setting
+      
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={
+      <EuiText
+        size="xs"
       >
-        <EuiImage
-          allowFullScreen={true}
-          alt="image:test:setting"
-          aria-label="image test setting"
-          url="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="
+        <FormattedMessage
+          defaultMessage="This setting is overridden by the Kibana server and can not be changed."
+          id="advancedSettings.field.helpText"
+          values={Object {}}
         />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+      </EuiText>
+    }
+    label="image:test:setting"
+    labelType="label"
+  >
+    <EuiImage
+      allowFullScreen={true}
+      alt="image:test:setting"
+      aria-label="image test setting"
+      url="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for image setting should render custom setting icon if it is custom 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Image test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Image test setting
+      <EuiIconTip
+        aria-label="Custom setting"
+        color="primary"
+        content={
+          <FormattedMessage
+            defaultMessage="Custom setting"
+            id="advancedSettings.field.customSettingTooltip"
+            values={Object {}}
+          />
+        }
+        type="asterisk"
+      />
+    </h3>
+  }
 >
-  <EuiFlexItem
-    grow={false}
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="image:test:setting"
+    labelType="label"
   >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Image test setting",
-              }
-            }
-          />
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Image test setting
-          <EuiIconTip
-            aria-label="Custom setting"
-            color="primary"
-            content={
-              <FormattedMessage
-                defaultMessage="Custom setting"
-                id="advancedSettings.field.customSettingTooltip"
-                values={Object {}}
-              />
-            }
-            type="asterisk"
-          />
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "image:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={null}
-        isInvalid={false}
-        label="image:test:setting"
-        labelType="label"
-      >
-        <EuiFilePicker
-          accept=".jpg,.jpeg,.png"
-          compressed={false}
-          data-test-subj="advancedSetting-editField-image:test:setting"
-          disabled={false}
-          display="large"
-          initialPromptText="Select or drag and drop a file"
-          onChange={[Function]}
-          onKeyDown={[Function]}
-        />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+    <EuiFilePicker
+      accept=".jpg,.jpeg,.png"
+      compressed={false}
+      data-test-subj="advancedSetting-editField-image:test:setting"
+      disabled={false}
+      display="large"
+      fullWidth={true}
+      initialPromptText="Select or drag and drop a file"
+      onChange={[Function]}
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for image setting should render default value if there is no user value set 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Image test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Image test setting
+      
+    </h3>
+  }
 >
-  <EuiFlexItem
-    grow={false}
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="image:test:setting"
+    labelType="label"
   >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Image test setting",
-              }
-            }
-          />
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Image test setting
-          
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "image:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={null}
-        isInvalid={false}
-        label="image:test:setting"
-        labelType="label"
+    <EuiFilePicker
+      accept=".jpg,.jpeg,.png"
+      compressed={false}
+      data-test-subj="advancedSetting-editField-image:test:setting"
+      disabled={false}
+      display="large"
+      fullWidth={true}
+      initialPromptText="Select or drag and drop a file"
+      onChange={[Function]}
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
+`;
+
+exports[`Field for image setting should render unsaved value if there are unsaved changes 1`] = `
+<EuiDescribedFormGroup
+  className="mgtAdvancedSettings__field mgtAdvancedSettings__field--unsaved"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Image test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Image test setting
+      <EuiIconTip
+        aria-label="Custom setting"
+        color="primary"
+        content={
+          <FormattedMessage
+            defaultMessage="Custom setting"
+            id="advancedSettings.field.customSettingTooltip"
+            values={Object {}}
+          />
+        }
+        type="asterisk"
+      />
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="image:test:setting"
+    labelType="label"
+  >
+    <EuiFilePicker
+      accept=".jpg,.jpeg,.png"
+      compressed={false}
+      data-test-subj="advancedSetting-editField-image:test:setting"
+      disabled={false}
+      display="large"
+      fullWidth={true}
+      initialPromptText="Select or drag and drop a file"
+      onChange={[Function]}
+    />
+    <EuiScreenReaderOnly>
+      <p
+        id="image:test:setting"
       >
-        <EuiFilePicker
-          accept=".jpg,.jpeg,.png"
-          compressed={false}
-          data-test-subj="advancedSetting-editField-image:test:setting"
-          disabled={false}
-          display="large"
-          initialPromptText="Select or drag and drop a file"
-          onChange={[Function]}
-          onKeyDown={[Function]}
-        />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+        Setting is currently not saved.
+      </p>
+    </EuiScreenReaderOnly>
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for image setting should render user value if there is user value is set 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
->
-  <EuiFlexItem
-    grow={false}
-  >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Image test setting",
-              }
-            }
-          />
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Image test setting",
+          }
+        }
+      />
+      <React.Fragment>
+        <EuiSpacer
+          size="s"
+        />
+        <EuiText
+          size="xs"
+        >
           <React.Fragment>
-            <EuiSpacer
-              size="s"
+            <FormattedMessage
+              defaultMessage="Default: {value}"
+              id="advancedSettings.field.defaultValueText"
+              values={
+                Object {
+                  "value": <EuiCode>
+                    null
+                  </EuiCode>,
+                }
+              }
             />
-            <EuiText
-              size="xs"
-            >
-              <React.Fragment>
-                <FormattedMessage
-                  defaultMessage="Default: {value}"
-                  id="advancedSettings.field.defaultValueText"
-                  values={
-                    Object {
-                      "value": <EuiCode>
-                        null
-                      </EuiCode>,
-                    }
-                  }
-                />
-              </React.Fragment>
-            </EuiText>
           </React.Fragment>
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Image test setting
-          
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "image:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={
-          <span>
-            <span>
-              <ForwardRef
-                aria-label="Reset image test setting to default"
-                data-test-subj="advancedSetting-resetField-image:test:setting"
-                onClick={[Function]}
-              >
-                <FormattedMessage
-                  defaultMessage="Reset to default"
-                  id="advancedSettings.field.resetToDefaultLinkText"
-                  values={Object {}}
-                />
-              </ForwardRef>
-                 
-            </span>
-            <span>
-              <ForwardRef
-                aria-label="Change image test setting"
-                data-test-subj="advancedSetting-changeImage-image:test:setting"
-                onClick={[Function]}
-              >
-                <FormattedMessage
-                  defaultMessage="Change image"
-                  id="advancedSettings.field.changeImageLinkText"
-                  values={Object {}}
-                />
-              </ForwardRef>
-            </span>
-          </span>
-        }
-        isInvalid={false}
-        label="image:test:setting"
-        labelType="label"
-      >
-        <EuiImage
-          allowFullScreen={true}
-          alt="image:test:setting"
-          aria-label="image test setting"
-          url="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="
-        />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+        </EuiText>
+      </React.Fragment>
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Image test setting
+      
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={
+      <span>
+        <span>
+          <ForwardRef
+            aria-label="Reset image test setting to default"
+            data-test-subj="advancedSetting-resetField-image:test:setting"
+            onClick={[Function]}
+          >
+            <FormattedMessage
+              defaultMessage="Reset to default"
+              id="advancedSettings.field.resetToDefaultLinkText"
+              values={Object {}}
+            />
+          </ForwardRef>
+             
+        </span>
+        <span>
+          <ForwardRef
+            aria-label="Change image test setting"
+            data-test-subj="advancedSetting-changeImage-image:test:setting"
+            onClick={[Function]}
+          >
+            <FormattedMessage
+              defaultMessage="Change image"
+              id="advancedSettings.field.changeImageLinkText"
+              values={Object {}}
+            />
+          </ForwardRef>
+        </span>
+      </span>
+    }
+    label="image:test:setting"
+    labelType="label"
+  >
+    <EuiImage
+      allowFullScreen={true}
+      alt="image:test:setting"
+      aria-label="image test setting"
+      url="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for json setting should render as read only if saving is disabled 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
->
-  <EuiFlexItem
-    grow={false}
-  >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Json test setting",
-              }
-            }
-          />
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Json test setting",
+          }
+        }
+      />
+      <React.Fragment>
+        <EuiSpacer
+          size="s"
+        />
+        <EuiText
+          size="xs"
+        >
           <React.Fragment>
-            <EuiSpacer
-              size="s"
+            <FormattedMessage
+              defaultMessage="Default: {value}"
+              id="advancedSettings.field.defaultValueTypeJsonText"
+              values={
+                Object {
+                  "value": <EuiCodeBlock
+                    language="json"
+                    paddingSize="s"
+                  >
+                    {}
+                  </EuiCodeBlock>,
+                }
+              }
             />
-            <EuiText
-              size="xs"
-            >
-              <React.Fragment>
-                <FormattedMessage
-                  defaultMessage="Default: {value}"
-                  id="advancedSettings.field.defaultValueTypeJsonText"
-                  values={
-                    Object {
-                      "value": <EuiCodeBlock
-                        language="json"
-                        paddingSize="s"
-                      >
-                        {}
-                      </EuiCodeBlock>,
-                    }
-                  }
-                />
-              </React.Fragment>
-            </EuiText>
           </React.Fragment>
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Json test setting
-          
-        </h3>
-      }
+        </EuiText>
+      </React.Fragment>
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Json test setting
+      
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="json:test:setting"
+    labelType="label"
+  >
+    <div
+      data-test-subj="advancedSetting-editField-json:test:setting"
     >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "json:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={null}
-        isInvalid={false}
-        label="json:test:setting"
-        labelType="label"
-      >
-        <div
-          data-test-subj="advancedSetting-editField-json:test:setting"
-        >
-          <EuiCodeEditor
-            aria-label="json test setting"
-            editorProps={
-              Object {
-                "$blockScrolling": Infinity,
-              }
-            }
-            height="auto"
-            isReadOnly={true}
-            maxLines={30}
-            minLines={6}
-            mode="json"
-            onChange={[Function]}
-            setOptions={
-              Object {
-                "showLineNumbers": false,
-                "tabSize": 2,
-              }
-            }
-            showGutter={false}
-            theme="textmate"
-            value="{\\"foo\\": \\"bar\\"}"
-            width="100%"
-          />
-        </div>
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+      <EuiCodeEditor
+        aria-label="json test setting"
+        editorProps={
+          Object {
+            "$blockScrolling": Infinity,
+          }
+        }
+        fullWidth={true}
+        height="auto"
+        isReadOnly={true}
+        maxLines={30}
+        minLines={6}
+        mode="json"
+        onChange={[Function]}
+        setOptions={
+          Object {
+            "showLineNumbers": false,
+            "tabSize": 2,
+          }
+        }
+        showGutter={false}
+        theme="textmate"
+        value="{\\"foo\\": \\"bar\\"}"
+        width="100%"
+      />
+    </div>
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for json setting should render as read only with help text if overridden 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
->
-  <EuiFlexItem
-    grow={false}
-  >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Json test setting",
-              }
-            }
-          />
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Json test setting",
+          }
+        }
+      />
+      <React.Fragment>
+        <EuiSpacer
+          size="s"
+        />
+        <EuiText
+          size="xs"
+        >
           <React.Fragment>
-            <EuiSpacer
-              size="s"
+            <FormattedMessage
+              defaultMessage="Default: {value}"
+              id="advancedSettings.field.defaultValueTypeJsonText"
+              values={
+                Object {
+                  "value": <EuiCodeBlock
+                    language="json"
+                    paddingSize="s"
+                  >
+                    {}
+                  </EuiCodeBlock>,
+                }
+              }
             />
-            <EuiText
-              size="xs"
-            >
-              <React.Fragment>
-                <FormattedMessage
-                  defaultMessage="Default: {value}"
-                  id="advancedSettings.field.defaultValueTypeJsonText"
-                  values={
-                    Object {
-                      "value": <EuiCodeBlock
-                        language="json"
-                        paddingSize="s"
-                      >
-                        {}
-                      </EuiCodeBlock>,
-                    }
-                  }
-                />
-              </React.Fragment>
-            </EuiText>
           </React.Fragment>
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Json test setting
-          
-        </h3>
-      }
+        </EuiText>
+      </React.Fragment>
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Json test setting
+      
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={
+      <EuiText
+        size="xs"
+      >
+        <FormattedMessage
+          defaultMessage="This setting is overridden by the Kibana server and can not be changed."
+          id="advancedSettings.field.helpText"
+          values={Object {}}
+        />
+      </EuiText>
+    }
+    label="json:test:setting"
+    labelType="label"
+  >
+    <div
+      data-test-subj="advancedSetting-editField-json:test:setting"
     >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "json:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={
-          <EuiText
-            size="xs"
-          >
-            <FormattedMessage
-              defaultMessage="This setting is overridden by the Kibana server and can not be changed."
-              id="advancedSettings.field.helpText"
-              values={Object {}}
-            />
-          </EuiText>
+      <EuiCodeEditor
+        aria-label="json test setting"
+        editorProps={
+          Object {
+            "$blockScrolling": Infinity,
+          }
         }
-        isInvalid={false}
-        label="json:test:setting"
-        labelType="label"
-      >
-        <div
-          data-test-subj="advancedSetting-editField-json:test:setting"
-        >
-          <EuiCodeEditor
-            aria-label="json test setting"
-            editorProps={
-              Object {
-                "$blockScrolling": Infinity,
-              }
-            }
-            height="auto"
-            isReadOnly={true}
-            maxLines={30}
-            minLines={6}
-            mode="json"
-            onChange={[Function]}
-            setOptions={
-              Object {
-                "showLineNumbers": false,
-                "tabSize": 2,
-              }
-            }
-            showGutter={false}
-            theme="textmate"
-            value="{\\"hello\\": \\"world\\"}"
-            width="100%"
-          />
-        </div>
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+        fullWidth={true}
+        height="auto"
+        isReadOnly={true}
+        maxLines={30}
+        minLines={6}
+        mode="json"
+        onChange={[Function]}
+        setOptions={
+          Object {
+            "showLineNumbers": false,
+            "tabSize": 2,
+          }
+        }
+        showGutter={false}
+        theme="textmate"
+        value="{\\"hello\\": \\"world\\"}"
+        width="100%"
+      />
+    </div>
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for json setting should render custom setting icon if it is custom 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Json test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Json test setting
+      <EuiIconTip
+        aria-label="Custom setting"
+        color="primary"
+        content={
+          <FormattedMessage
+            defaultMessage="Custom setting"
+            id="advancedSettings.field.customSettingTooltip"
+            values={Object {}}
+          />
+        }
+        type="asterisk"
+      />
+    </h3>
+  }
 >
-  <EuiFlexItem
-    grow={false}
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="json:test:setting"
+    labelType="label"
   >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Json test setting",
-              }
-            }
-          />
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Json test setting
-          <EuiIconTip
-            aria-label="Custom setting"
-            color="primary"
-            content={
-              <FormattedMessage
-                defaultMessage="Custom setting"
-                id="advancedSettings.field.customSettingTooltip"
-                values={Object {}}
-              />
-            }
-            type="asterisk"
-          />
-        </h3>
-      }
+    <div
+      data-test-subj="advancedSetting-editField-json:test:setting"
     >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "json:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={null}
-        isInvalid={false}
-        label="json:test:setting"
-        labelType="label"
-      >
-        <div
-          data-test-subj="advancedSetting-editField-json:test:setting"
-        >
-          <EuiCodeEditor
-            aria-label="json test setting"
-            editorProps={
-              Object {
-                "$blockScrolling": Infinity,
-              }
-            }
-            height="auto"
-            isReadOnly={false}
-            maxLines={30}
-            minLines={6}
-            mode="json"
-            onChange={[Function]}
-            setOptions={
-              Object {
-                "showLineNumbers": false,
-                "tabSize": 2,
-              }
-            }
-            showGutter={false}
-            theme="textmate"
-            value="{\\"foo\\": \\"bar\\"}"
-            width="100%"
-          />
-        </div>
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+      <EuiCodeEditor
+        aria-label="json test setting"
+        editorProps={
+          Object {
+            "$blockScrolling": Infinity,
+          }
+        }
+        fullWidth={true}
+        height="auto"
+        isReadOnly={false}
+        maxLines={30}
+        minLines={6}
+        mode="json"
+        onChange={[Function]}
+        setOptions={
+          Object {
+            "showLineNumbers": false,
+            "tabSize": 2,
+          }
+        }
+        showGutter={false}
+        theme="textmate"
+        value="{\\"foo\\": \\"bar\\"}"
+        width="100%"
+      />
+    </div>
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for json setting should render default value if there is no user value set 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
->
-  <EuiFlexItem
-    grow={false}
-  >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Json test setting",
-              }
-            }
-          />
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Json test setting",
+          }
+        }
+      />
+      <React.Fragment>
+        <EuiSpacer
+          size="s"
+        />
+        <EuiText
+          size="xs"
+        >
           <React.Fragment>
-            <EuiSpacer
-              size="s"
+            <FormattedMessage
+              defaultMessage="Default: {value}"
+              id="advancedSettings.field.defaultValueTypeJsonText"
+              values={
+                Object {
+                  "value": <EuiCodeBlock
+                    language="json"
+                    paddingSize="s"
+                  >
+                    {}
+                  </EuiCodeBlock>,
+                }
+              }
             />
-            <EuiText
-              size="xs"
-            >
-              <React.Fragment>
-                <FormattedMessage
-                  defaultMessage="Default: {value}"
-                  id="advancedSettings.field.defaultValueTypeJsonText"
-                  values={
-                    Object {
-                      "value": <EuiCodeBlock
-                        language="json"
-                        paddingSize="s"
-                      >
-                        {}
-                      </EuiCodeBlock>,
-                    }
-                  }
-                />
-              </React.Fragment>
-            </EuiText>
           </React.Fragment>
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Json test setting
-          
-        </h3>
-      }
+        </EuiText>
+      </React.Fragment>
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Json test setting
+      
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={
+      <span>
+        <span>
+          <ForwardRef
+            aria-label="Reset json test setting to default"
+            data-test-subj="advancedSetting-resetField-json:test:setting"
+            onClick={[Function]}
+          >
+            <FormattedMessage
+              defaultMessage="Reset to default"
+              id="advancedSettings.field.resetToDefaultLinkText"
+              values={Object {}}
+            />
+          </ForwardRef>
+             
+        </span>
+      </span>
+    }
+    label="json:test:setting"
+    labelType="label"
+  >
+    <div
+      data-test-subj="advancedSetting-editField-json:test:setting"
     >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "json:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={
-          <span>
-            <span>
-              <ForwardRef
-                aria-label="Reset json test setting to default"
-                data-test-subj="advancedSetting-resetField-json:test:setting"
-                onClick={[Function]}
-              >
-                <FormattedMessage
-                  defaultMessage="Reset to default"
-                  id="advancedSettings.field.resetToDefaultLinkText"
-                  values={Object {}}
-                />
-              </ForwardRef>
-                 
-            </span>
-          </span>
-        }
-        isInvalid={false}
-        label="json:test:setting"
-        labelType="label"
+      <EuiCodeEditor
+        aria-label="json test setting"
+        editorProps={
+          Object {
+            "$blockScrolling": Infinity,
+          }
+        }
+        fullWidth={true}
+        height="auto"
+        isReadOnly={false}
+        maxLines={30}
+        minLines={6}
+        mode="json"
+        onChange={[Function]}
+        setOptions={
+          Object {
+            "showLineNumbers": false,
+            "tabSize": 2,
+          }
+        }
+        showGutter={false}
+        theme="textmate"
+        value="{\\"foo\\": \\"bar\\"}"
+        width="100%"
+      />
+    </div>
+  </EuiFormRow>
+</EuiDescribedFormGroup>
+`;
+
+exports[`Field for json setting should render unsaved value if there are unsaved changes 1`] = `
+<EuiDescribedFormGroup
+  className="mgtAdvancedSettings__field mgtAdvancedSettings__field--unsaved"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Json test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Json test setting
+      <EuiIconTip
+        aria-label="Custom setting"
+        color="primary"
+        content={
+          <FormattedMessage
+            defaultMessage="Custom setting"
+            id="advancedSettings.field.customSettingTooltip"
+            values={Object {}}
+          />
+        }
+        type="asterisk"
+      />
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="json:test:setting"
+    labelType="label"
+  >
+    <div
+      data-test-subj="advancedSetting-editField-json:test:setting"
+    >
+      <EuiCodeEditor
+        aria-describedby="json:test:setting"
+        aria-label="json test setting"
+        editorProps={
+          Object {
+            "$blockScrolling": Infinity,
+          }
+        }
+        fullWidth={true}
+        height="auto"
+        isReadOnly={false}
+        maxLines={30}
+        minLines={6}
+        mode="json"
+        onChange={[Function]}
+        setOptions={
+          Object {
+            "showLineNumbers": false,
+            "tabSize": 2,
+          }
+        }
+        showGutter={false}
+        theme="textmate"
+        value={
+          Object {
+            "foo": "bar2",
+          }
+        }
+        width="100%"
+      />
+    </div>
+    <EuiScreenReaderOnly>
+      <p
+        id="json:test:setting"
       >
-        <div
-          data-test-subj="advancedSetting-editField-json:test:setting"
-        >
-          <EuiCodeEditor
-            aria-label="json test setting"
-            editorProps={
-              Object {
-                "$blockScrolling": Infinity,
-              }
-            }
-            height="auto"
-            isReadOnly={false}
-            maxLines={30}
-            minLines={6}
-            mode="json"
-            onChange={[Function]}
-            setOptions={
-              Object {
-                "showLineNumbers": false,
-                "tabSize": 2,
-              }
-            }
-            showGutter={false}
-            theme="textmate"
-            value="{\\"foo\\": \\"bar\\"}"
-            width="100%"
-          />
-        </div>
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+        Setting is currently not saved.
+      </p>
+    </EuiScreenReaderOnly>
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for json setting should render user value if there is user value is set 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
->
-  <EuiFlexItem
-    grow={false}
-  >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Json test setting",
-              }
-            }
-          />
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Json test setting",
+          }
+        }
+      />
+      <React.Fragment>
+        <EuiSpacer
+          size="s"
+        />
+        <EuiText
+          size="xs"
+        >
           <React.Fragment>
-            <EuiSpacer
-              size="s"
+            <FormattedMessage
+              defaultMessage="Default: {value}"
+              id="advancedSettings.field.defaultValueTypeJsonText"
+              values={
+                Object {
+                  "value": <EuiCodeBlock
+                    language="json"
+                    paddingSize="s"
+                  >
+                    {}
+                  </EuiCodeBlock>,
+                }
+              }
             />
-            <EuiText
-              size="xs"
-            >
-              <React.Fragment>
-                <FormattedMessage
-                  defaultMessage="Default: {value}"
-                  id="advancedSettings.field.defaultValueTypeJsonText"
-                  values={
-                    Object {
-                      "value": <EuiCodeBlock
-                        language="json"
-                        paddingSize="s"
-                      >
-                        {}
-                      </EuiCodeBlock>,
-                    }
-                  }
-                />
-              </React.Fragment>
-            </EuiText>
           </React.Fragment>
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Json test setting
-          
-        </h3>
-      }
+        </EuiText>
+      </React.Fragment>
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Json test setting
+      
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={
+      <span>
+        <span>
+          <ForwardRef
+            aria-label="Reset json test setting to default"
+            data-test-subj="advancedSetting-resetField-json:test:setting"
+            onClick={[Function]}
+          >
+            <FormattedMessage
+              defaultMessage="Reset to default"
+              id="advancedSettings.field.resetToDefaultLinkText"
+              values={Object {}}
+            />
+          </ForwardRef>
+             
+        </span>
+      </span>
+    }
+    label="json:test:setting"
+    labelType="label"
+  >
+    <div
+      data-test-subj="advancedSetting-editField-json:test:setting"
     >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "json:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={
-          <span>
-            <span>
-              <ForwardRef
-                aria-label="Reset json test setting to default"
-                data-test-subj="advancedSetting-resetField-json:test:setting"
-                onClick={[Function]}
-              >
-                <FormattedMessage
-                  defaultMessage="Reset to default"
-                  id="advancedSettings.field.resetToDefaultLinkText"
-                  values={Object {}}
-                />
-              </ForwardRef>
-                 
-            </span>
-          </span>
-        }
-        isInvalid={false}
-        label="json:test:setting"
-        labelType="label"
-      >
-        <div
-          data-test-subj="advancedSetting-editField-json:test:setting"
-        >
-          <EuiCodeEditor
-            aria-label="json test setting"
-            editorProps={
-              Object {
-                "$blockScrolling": Infinity,
-              }
-            }
-            height="auto"
-            isReadOnly={false}
-            maxLines={30}
-            minLines={6}
-            mode="json"
-            onChange={[Function]}
-            setOptions={
-              Object {
-                "showLineNumbers": false,
-                "tabSize": 2,
-              }
-            }
-            showGutter={false}
-            theme="textmate"
-            value="{\\"hello\\": \\"world\\"}"
-            width="100%"
-          />
-        </div>
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+      <EuiCodeEditor
+        aria-label="json test setting"
+        editorProps={
+          Object {
+            "$blockScrolling": Infinity,
+          }
+        }
+        fullWidth={true}
+        height="auto"
+        isReadOnly={false}
+        maxLines={30}
+        minLines={6}
+        mode="json"
+        onChange={[Function]}
+        setOptions={
+          Object {
+            "showLineNumbers": false,
+            "tabSize": 2,
+          }
+        }
+        showGutter={false}
+        theme="textmate"
+        value="{\\"hello\\": \\"world\\"}"
+        width="100%"
+      />
+    </div>
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for markdown setting should render as read only if saving is disabled 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Markdown test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Markdown test setting
+      
+    </h3>
+  }
 >
-  <EuiFlexItem
-    grow={false}
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="markdown:test:setting"
+    labelType="label"
   >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Markdown test setting",
-              }
-            }
-          />
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Markdown test setting
-          
-        </h3>
-      }
+    <div
+      data-test-subj="advancedSetting-editField-markdown:test:setting"
     >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "markdown:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={null}
-        isInvalid={false}
-        label="markdown:test:setting"
-        labelType="label"
-      >
-        <div
-          data-test-subj="advancedSetting-editField-markdown:test:setting"
-        >
-          <EuiCodeEditor
-            aria-label="markdown test setting"
-            editorProps={
-              Object {
-                "$blockScrolling": Infinity,
-              }
-            }
-            height="auto"
-            isReadOnly={true}
-            maxLines={30}
-            minLines={6}
-            mode="markdown"
-            onChange={[Function]}
-            setOptions={
-              Object {
-                "showLineNumbers": false,
-                "tabSize": 2,
-              }
-            }
-            showGutter={false}
-            theme="textmate"
-            value=""
-            width="100%"
-          />
-        </div>
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+      <EuiCodeEditor
+        aria-label="markdown test setting"
+        editorProps={
+          Object {
+            "$blockScrolling": Infinity,
+          }
+        }
+        fullWidth={true}
+        height="auto"
+        isReadOnly={true}
+        maxLines={30}
+        minLines={6}
+        mode="markdown"
+        onChange={[Function]}
+        setOptions={
+          Object {
+            "showLineNumbers": false,
+            "tabSize": 2,
+          }
+        }
+        showGutter={false}
+        theme="textmate"
+        value=""
+        width="100%"
+      />
+    </div>
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for markdown setting should render as read only with help text if overridden 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
->
-  <EuiFlexItem
-    grow={false}
-  >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Markdown test setting",
-              }
-            }
-          />
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Markdown test setting",
+          }
+        }
+      />
+      <React.Fragment>
+        <EuiSpacer
+          size="s"
+        />
+        <EuiText
+          size="xs"
+        >
           <React.Fragment>
-            <EuiSpacer
-              size="s"
+            <FormattedMessage
+              defaultMessage="Default: {value}"
+              id="advancedSettings.field.defaultValueText"
+              values={
+                Object {
+                  "value": <EuiCode>
+                    null
+                  </EuiCode>,
+                }
+              }
             />
-            <EuiText
-              size="xs"
-            >
-              <React.Fragment>
-                <FormattedMessage
-                  defaultMessage="Default: {value}"
-                  id="advancedSettings.field.defaultValueText"
-                  values={
-                    Object {
-                      "value": <EuiCode>
-                        null
-                      </EuiCode>,
-                    }
-                  }
-                />
-              </React.Fragment>
-            </EuiText>
           </React.Fragment>
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Markdown test setting
-          
-        </h3>
-      }
+        </EuiText>
+      </React.Fragment>
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Markdown test setting
+      
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={
+      <EuiText
+        size="xs"
+      >
+        <FormattedMessage
+          defaultMessage="This setting is overridden by the Kibana server and can not be changed."
+          id="advancedSettings.field.helpText"
+          values={Object {}}
+        />
+      </EuiText>
+    }
+    label="markdown:test:setting"
+    labelType="label"
+  >
+    <div
+      data-test-subj="advancedSetting-editField-markdown:test:setting"
     >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "markdown:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={
-          <EuiText
-            size="xs"
-          >
-            <FormattedMessage
-              defaultMessage="This setting is overridden by the Kibana server and can not be changed."
-              id="advancedSettings.field.helpText"
-              values={Object {}}
-            />
-          </EuiText>
+      <EuiCodeEditor
+        aria-label="markdown test setting"
+        editorProps={
+          Object {
+            "$blockScrolling": Infinity,
+          }
         }
-        isInvalid={false}
-        label="markdown:test:setting"
-        labelType="label"
-      >
-        <div
-          data-test-subj="advancedSetting-editField-markdown:test:setting"
-        >
-          <EuiCodeEditor
-            aria-label="markdown test setting"
-            editorProps={
-              Object {
-                "$blockScrolling": Infinity,
-              }
-            }
-            height="auto"
-            isReadOnly={true}
-            maxLines={30}
-            minLines={6}
-            mode="markdown"
-            onChange={[Function]}
-            setOptions={
-              Object {
-                "showLineNumbers": false,
-                "tabSize": 2,
-              }
-            }
-            showGutter={false}
-            theme="textmate"
-            value="**bold**"
-            width="100%"
-          />
-        </div>
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+        fullWidth={true}
+        height="auto"
+        isReadOnly={true}
+        maxLines={30}
+        minLines={6}
+        mode="markdown"
+        onChange={[Function]}
+        setOptions={
+          Object {
+            "showLineNumbers": false,
+            "tabSize": 2,
+          }
+        }
+        showGutter={false}
+        theme="textmate"
+        value="**bold**"
+        width="100%"
+      />
+    </div>
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for markdown setting should render custom setting icon if it is custom 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Markdown test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Markdown test setting
+      <EuiIconTip
+        aria-label="Custom setting"
+        color="primary"
+        content={
+          <FormattedMessage
+            defaultMessage="Custom setting"
+            id="advancedSettings.field.customSettingTooltip"
+            values={Object {}}
+          />
+        }
+        type="asterisk"
+      />
+    </h3>
+  }
 >
-  <EuiFlexItem
-    grow={false}
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="markdown:test:setting"
+    labelType="label"
   >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Markdown test setting",
-              }
-            }
-          />
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Markdown test setting
-          <EuiIconTip
-            aria-label="Custom setting"
-            color="primary"
-            content={
-              <FormattedMessage
-                defaultMessage="Custom setting"
-                id="advancedSettings.field.customSettingTooltip"
-                values={Object {}}
-              />
-            }
-            type="asterisk"
-          />
-        </h3>
-      }
+    <div
+      data-test-subj="advancedSetting-editField-markdown:test:setting"
     >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "markdown:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={null}
-        isInvalid={false}
-        label="markdown:test:setting"
-        labelType="label"
-      >
-        <div
-          data-test-subj="advancedSetting-editField-markdown:test:setting"
-        >
-          <EuiCodeEditor
-            aria-label="markdown test setting"
-            editorProps={
-              Object {
-                "$blockScrolling": Infinity,
-              }
-            }
-            height="auto"
-            isReadOnly={false}
-            maxLines={30}
-            minLines={6}
-            mode="markdown"
-            onChange={[Function]}
-            setOptions={
-              Object {
-                "showLineNumbers": false,
-                "tabSize": 2,
-              }
-            }
-            showGutter={false}
-            theme="textmate"
-            value=""
-            width="100%"
-          />
-        </div>
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+      <EuiCodeEditor
+        aria-label="markdown test setting"
+        editorProps={
+          Object {
+            "$blockScrolling": Infinity,
+          }
+        }
+        fullWidth={true}
+        height="auto"
+        isReadOnly={false}
+        maxLines={30}
+        minLines={6}
+        mode="markdown"
+        onChange={[Function]}
+        setOptions={
+          Object {
+            "showLineNumbers": false,
+            "tabSize": 2,
+          }
+        }
+        showGutter={false}
+        theme="textmate"
+        value=""
+        width="100%"
+      />
+    </div>
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for markdown setting should render default value if there is no user value set 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Markdown test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Markdown test setting
+      
+    </h3>
+  }
 >
-  <EuiFlexItem
-    grow={false}
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="markdown:test:setting"
+    labelType="label"
   >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Markdown test setting",
-              }
-            }
-          />
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Markdown test setting
-          
-        </h3>
-      }
+    <div
+      data-test-subj="advancedSetting-editField-markdown:test:setting"
+    >
+      <EuiCodeEditor
+        aria-label="markdown test setting"
+        editorProps={
+          Object {
+            "$blockScrolling": Infinity,
+          }
+        }
+        fullWidth={true}
+        height="auto"
+        isReadOnly={false}
+        maxLines={30}
+        minLines={6}
+        mode="markdown"
+        onChange={[Function]}
+        setOptions={
+          Object {
+            "showLineNumbers": false,
+            "tabSize": 2,
+          }
+        }
+        showGutter={false}
+        theme="textmate"
+        value=""
+        width="100%"
+      />
+    </div>
+  </EuiFormRow>
+</EuiDescribedFormGroup>
+`;
+
+exports[`Field for markdown setting should render unsaved value if there are unsaved changes 1`] = `
+<EuiDescribedFormGroup
+  className="mgtAdvancedSettings__field mgtAdvancedSettings__field--unsaved"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Markdown test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Markdown test setting
+      <EuiIconTip
+        aria-label="Custom setting"
+        color="primary"
+        content={
+          <FormattedMessage
+            defaultMessage="Custom setting"
+            id="advancedSettings.field.customSettingTooltip"
+            values={Object {}}
+          />
+        }
+        type="asterisk"
+      />
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="markdown:test:setting"
+    labelType="label"
+  >
+    <div
+      data-test-subj="advancedSetting-editField-markdown:test:setting"
     >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "markdown:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={null}
-        isInvalid={false}
-        label="markdown:test:setting"
-        labelType="label"
+      <EuiCodeEditor
+        aria-describedby="markdown:test:setting"
+        aria-label="markdown test setting"
+        editorProps={
+          Object {
+            "$blockScrolling": Infinity,
+          }
+        }
+        fullWidth={true}
+        height="auto"
+        isReadOnly={false}
+        maxLines={30}
+        minLines={6}
+        mode="markdown"
+        onChange={[Function]}
+        setOptions={
+          Object {
+            "showLineNumbers": false,
+            "tabSize": 2,
+          }
+        }
+        showGutter={false}
+        theme="textmate"
+        value="Hello World"
+        width="100%"
+      />
+    </div>
+    <EuiScreenReaderOnly>
+      <p
+        id="markdown:test:setting"
       >
-        <div
-          data-test-subj="advancedSetting-editField-markdown:test:setting"
-        >
-          <EuiCodeEditor
-            aria-label="markdown test setting"
-            editorProps={
-              Object {
-                "$blockScrolling": Infinity,
-              }
-            }
-            height="auto"
-            isReadOnly={false}
-            maxLines={30}
-            minLines={6}
-            mode="markdown"
-            onChange={[Function]}
-            setOptions={
-              Object {
-                "showLineNumbers": false,
-                "tabSize": 2,
-              }
-            }
-            showGutter={false}
-            theme="textmate"
-            value=""
-            width="100%"
-          />
-        </div>
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+        Setting is currently not saved.
+      </p>
+    </EuiScreenReaderOnly>
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for markdown setting should render user value if there is user value is set 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
->
-  <EuiFlexItem
-    grow={false}
-  >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Markdown test setting",
-              }
-            }
-          />
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Markdown test setting",
+          }
+        }
+      />
+      <React.Fragment>
+        <EuiSpacer
+          size="s"
+        />
+        <EuiText
+          size="xs"
+        >
           <React.Fragment>
-            <EuiSpacer
-              size="s"
+            <FormattedMessage
+              defaultMessage="Default: {value}"
+              id="advancedSettings.field.defaultValueText"
+              values={
+                Object {
+                  "value": <EuiCode>
+                    null
+                  </EuiCode>,
+                }
+              }
             />
-            <EuiText
-              size="xs"
-            >
-              <React.Fragment>
-                <FormattedMessage
-                  defaultMessage="Default: {value}"
-                  id="advancedSettings.field.defaultValueText"
-                  values={
-                    Object {
-                      "value": <EuiCode>
-                        null
-                      </EuiCode>,
-                    }
-                  }
-                />
-              </React.Fragment>
-            </EuiText>
           </React.Fragment>
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Markdown test setting
-          
-        </h3>
-      }
+        </EuiText>
+      </React.Fragment>
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Markdown test setting
+      
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={
+      <span>
+        <span>
+          <ForwardRef
+            aria-label="Reset markdown test setting to default"
+            data-test-subj="advancedSetting-resetField-markdown:test:setting"
+            onClick={[Function]}
+          >
+            <FormattedMessage
+              defaultMessage="Reset to default"
+              id="advancedSettings.field.resetToDefaultLinkText"
+              values={Object {}}
+            />
+          </ForwardRef>
+             
+        </span>
+      </span>
+    }
+    label="markdown:test:setting"
+    labelType="label"
+  >
+    <div
+      data-test-subj="advancedSetting-editField-markdown:test:setting"
     >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "markdown:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={
-          <span>
-            <span>
-              <ForwardRef
-                aria-label="Reset markdown test setting to default"
-                data-test-subj="advancedSetting-resetField-markdown:test:setting"
-                onClick={[Function]}
-              >
-                <FormattedMessage
-                  defaultMessage="Reset to default"
-                  id="advancedSettings.field.resetToDefaultLinkText"
-                  values={Object {}}
-                />
-              </ForwardRef>
-                 
-            </span>
-          </span>
-        }
-        isInvalid={false}
-        label="markdown:test:setting"
-        labelType="label"
-      >
-        <div
-          data-test-subj="advancedSetting-editField-markdown:test:setting"
-        >
-          <EuiCodeEditor
-            aria-label="markdown test setting"
-            editorProps={
-              Object {
-                "$blockScrolling": Infinity,
-              }
-            }
-            height="auto"
-            isReadOnly={false}
-            maxLines={30}
-            minLines={6}
-            mode="markdown"
-            onChange={[Function]}
-            setOptions={
-              Object {
-                "showLineNumbers": false,
-                "tabSize": 2,
-              }
-            }
-            showGutter={false}
-            theme="textmate"
-            value="**bold**"
-            width="100%"
-          />
-        </div>
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+      <EuiCodeEditor
+        aria-label="markdown test setting"
+        editorProps={
+          Object {
+            "$blockScrolling": Infinity,
+          }
+        }
+        fullWidth={true}
+        height="auto"
+        isReadOnly={false}
+        maxLines={30}
+        minLines={6}
+        mode="markdown"
+        onChange={[Function]}
+        setOptions={
+          Object {
+            "showLineNumbers": false,
+            "tabSize": 2,
+          }
+        }
+        showGutter={false}
+        theme="textmate"
+        value="**bold**"
+        width="100%"
+      />
+    </div>
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for number setting should render as read only if saving is disabled 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Number test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Number test setting
+      
+    </h3>
+  }
 >
-  <EuiFlexItem
-    grow={false}
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="number:test:setting"
+    labelType="label"
   >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Number test setting",
-              }
-            }
-          />
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Number test setting
-          
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "number:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={null}
-        isInvalid={false}
-        label="number:test:setting"
-        labelType="label"
-      >
-        <EuiFieldNumber
-          aria-label="number test setting"
-          data-test-subj="advancedSetting-editField-number:test:setting"
-          disabled={true}
-          isLoading={false}
-          onChange={[Function]}
-          onKeyDown={[Function]}
-          value={5}
-        />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+    <EuiFieldNumber
+      aria-label="number test setting"
+      data-test-subj="advancedSetting-editField-number:test:setting"
+      disabled={true}
+      fullWidth={true}
+      onChange={[Function]}
+      value={5}
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for number setting should render as read only with help text if overridden 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
->
-  <EuiFlexItem
-    grow={false}
-  >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Number test setting",
-              }
-            }
-          />
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Number test setting",
+          }
+        }
+      />
+      <React.Fragment>
+        <EuiSpacer
+          size="s"
+        />
+        <EuiText
+          size="xs"
+        >
           <React.Fragment>
-            <EuiSpacer
-              size="s"
-            />
-            <EuiText
-              size="xs"
-            >
-              <React.Fragment>
-                <FormattedMessage
-                  defaultMessage="Default: {value}"
-                  id="advancedSettings.field.defaultValueText"
-                  values={
-                    Object {
-                      "value": <EuiCode>
-                        5
-                      </EuiCode>,
-                    }
-                  }
-                />
-              </React.Fragment>
-            </EuiText>
-          </React.Fragment>
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Number test setting
-          
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "number:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={
-          <EuiText
-            size="xs"
-          >
             <FormattedMessage
-              defaultMessage="This setting is overridden by the Kibana server and can not be changed."
-              id="advancedSettings.field.helpText"
-              values={Object {}}
+              defaultMessage="Default: {value}"
+              id="advancedSettings.field.defaultValueText"
+              values={
+                Object {
+                  "value": <EuiCode>
+                    5
+                  </EuiCode>,
+                }
+              }
             />
-          </EuiText>
-        }
-        isInvalid={false}
-        label="number:test:setting"
-        labelType="label"
+          </React.Fragment>
+        </EuiText>
+      </React.Fragment>
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Number test setting
+      
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={
+      <EuiText
+        size="xs"
       >
-        <EuiFieldNumber
-          aria-label="number test setting"
-          data-test-subj="advancedSetting-editField-number:test:setting"
-          disabled={true}
-          isLoading={false}
-          onChange={[Function]}
-          onKeyDown={[Function]}
-          value={10}
+        <FormattedMessage
+          defaultMessage="This setting is overridden by the Kibana server and can not be changed."
+          id="advancedSettings.field.helpText"
+          values={Object {}}
         />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+      </EuiText>
+    }
+    label="number:test:setting"
+    labelType="label"
+  >
+    <EuiFieldNumber
+      aria-label="number test setting"
+      data-test-subj="advancedSetting-editField-number:test:setting"
+      disabled={true}
+      fullWidth={true}
+      onChange={[Function]}
+      value={10}
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for number setting should render custom setting icon if it is custom 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Number test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Number test setting
+      <EuiIconTip
+        aria-label="Custom setting"
+        color="primary"
+        content={
+          <FormattedMessage
+            defaultMessage="Custom setting"
+            id="advancedSettings.field.customSettingTooltip"
+            values={Object {}}
+          />
+        }
+        type="asterisk"
+      />
+    </h3>
+  }
 >
-  <EuiFlexItem
-    grow={false}
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="number:test:setting"
+    labelType="label"
   >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Number test setting",
-              }
-            }
-          />
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Number test setting
-          <EuiIconTip
-            aria-label="Custom setting"
-            color="primary"
-            content={
-              <FormattedMessage
-                defaultMessage="Custom setting"
-                id="advancedSettings.field.customSettingTooltip"
-                values={Object {}}
-              />
-            }
-            type="asterisk"
-          />
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "number:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={null}
-        isInvalid={false}
-        label="number:test:setting"
-        labelType="label"
-      >
-        <EuiFieldNumber
-          aria-label="number test setting"
-          data-test-subj="advancedSetting-editField-number:test:setting"
-          disabled={false}
-          isLoading={false}
-          onChange={[Function]}
-          onKeyDown={[Function]}
-          value={5}
-        />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+    <EuiFieldNumber
+      aria-label="number test setting"
+      data-test-subj="advancedSetting-editField-number:test:setting"
+      disabled={false}
+      fullWidth={true}
+      onChange={[Function]}
+      value={5}
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
+`;
+
+exports[`Field for number setting should render default value if there is no user value set 1`] = `
+<EuiDescribedFormGroup
+  className="mgtAdvancedSettings__field"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Number test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Number test setting
+      
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="number:test:setting"
+    labelType="label"
+  >
+    <EuiFieldNumber
+      aria-label="number test setting"
+      data-test-subj="advancedSetting-editField-number:test:setting"
+      disabled={false}
+      fullWidth={true}
+      onChange={[Function]}
+      value={5}
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
-exports[`Field for number setting should render default value if there is no user value set 1`] = `
-<EuiFlexGroup
-  className="mgtAdvancedSettings__field"
+exports[`Field for number setting should render unsaved value if there are unsaved changes 1`] = `
+<EuiDescribedFormGroup
+  className="mgtAdvancedSettings__field mgtAdvancedSettings__field--unsaved"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Number test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Number test setting
+      <EuiIconTip
+        aria-label="Custom setting"
+        color="primary"
+        content={
+          <FormattedMessage
+            defaultMessage="Custom setting"
+            id="advancedSettings.field.customSettingTooltip"
+            values={Object {}}
+          />
+        }
+        type="asterisk"
+      />
+    </h3>
+  }
 >
-  <EuiFlexItem
-    grow={false}
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="number:test:setting"
+    labelType="label"
   >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Number test setting",
-              }
-            }
-          />
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Number test setting
-          
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "number:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={null}
-        isInvalid={false}
-        label="number:test:setting"
-        labelType="label"
+    <EuiFieldNumber
+      aria-describedby="number:test:setting"
+      aria-label="number test setting"
+      data-test-subj="advancedSetting-editField-number:test:setting"
+      disabled={false}
+      fullWidth={true}
+      onChange={[Function]}
+      value={1}
+    />
+    <EuiScreenReaderOnly>
+      <p
+        id="number:test:setting"
       >
-        <EuiFieldNumber
-          aria-label="number test setting"
-          data-test-subj="advancedSetting-editField-number:test:setting"
-          disabled={false}
-          isLoading={false}
-          onChange={[Function]}
-          onKeyDown={[Function]}
-          value={5}
-        />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+        Setting is currently not saved.
+      </p>
+    </EuiScreenReaderOnly>
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for number setting should render user value if there is user value is set 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
->
-  <EuiFlexItem
-    grow={false}
-  >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Number test setting",
-              }
-            }
-          />
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Number test setting",
+          }
+        }
+      />
+      <React.Fragment>
+        <EuiSpacer
+          size="s"
+        />
+        <EuiText
+          size="xs"
+        >
           <React.Fragment>
-            <EuiSpacer
-              size="s"
+            <FormattedMessage
+              defaultMessage="Default: {value}"
+              id="advancedSettings.field.defaultValueText"
+              values={
+                Object {
+                  "value": <EuiCode>
+                    5
+                  </EuiCode>,
+                }
+              }
             />
-            <EuiText
-              size="xs"
-            >
-              <React.Fragment>
-                <FormattedMessage
-                  defaultMessage="Default: {value}"
-                  id="advancedSettings.field.defaultValueText"
-                  values={
-                    Object {
-                      "value": <EuiCode>
-                        5
-                      </EuiCode>,
-                    }
-                  }
-                />
-              </React.Fragment>
-            </EuiText>
           </React.Fragment>
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Number test setting
-          
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "number:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={
-          <span>
-            <span>
-              <ForwardRef
-                aria-label="Reset number test setting to default"
-                data-test-subj="advancedSetting-resetField-number:test:setting"
-                onClick={[Function]}
-              >
-                <FormattedMessage
-                  defaultMessage="Reset to default"
-                  id="advancedSettings.field.resetToDefaultLinkText"
-                  values={Object {}}
-                />
-              </ForwardRef>
-                 
-            </span>
-          </span>
-        }
-        isInvalid={false}
-        label="number:test:setting"
-        labelType="label"
-      >
-        <EuiFieldNumber
-          aria-label="number test setting"
-          data-test-subj="advancedSetting-editField-number:test:setting"
-          disabled={false}
-          isLoading={false}
-          onChange={[Function]}
-          onKeyDown={[Function]}
-          value={10}
-        />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+        </EuiText>
+      </React.Fragment>
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Number test setting
+      
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={
+      <span>
+        <span>
+          <ForwardRef
+            aria-label="Reset number test setting to default"
+            data-test-subj="advancedSetting-resetField-number:test:setting"
+            onClick={[Function]}
+          >
+            <FormattedMessage
+              defaultMessage="Reset to default"
+              id="advancedSettings.field.resetToDefaultLinkText"
+              values={Object {}}
+            />
+          </ForwardRef>
+             
+        </span>
+      </span>
+    }
+    label="number:test:setting"
+    labelType="label"
+  >
+    <EuiFieldNumber
+      aria-label="number test setting"
+      data-test-subj="advancedSetting-editField-number:test:setting"
+      disabled={false}
+      fullWidth={true}
+      onChange={[Function]}
+      value={10}
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for select setting should render as read only if saving is disabled 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Select test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Select test setting
+      
+    </h3>
+  }
 >
-  <EuiFlexItem
-    grow={false}
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="select:test:setting"
+    labelType="label"
   >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Select test setting",
-              }
-            }
-          />
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Select test setting
-          
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "select:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={null}
-        isInvalid={false}
-        label="select:test:setting"
-        labelType="label"
-      >
-        <EuiSelect
-          aria-label="select test setting"
-          data-test-subj="advancedSetting-editField-select:test:setting"
-          disabled={true}
-          isLoading={false}
-          onChange={[Function]}
-          onKeyDown={[Function]}
-          options={
-            Array [
-              Object {
-                "text": "Apple",
-                "value": "apple",
-              },
-              Object {
-                "text": "Orange",
-                "value": "orange",
-              },
-              Object {
-                "text": "banana",
-                "value": "banana",
-              },
-            ]
-          }
-          value="orange"
-        />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+    <EuiSelect
+      aria-label="select test setting"
+      data-test-subj="advancedSetting-editField-select:test:setting"
+      disabled={true}
+      fullWidth={true}
+      onChange={[Function]}
+      options={
+        Array [
+          Object {
+            "text": "Apple",
+            "value": "apple",
+          },
+          Object {
+            "text": "Orange",
+            "value": "orange",
+          },
+          Object {
+            "text": "banana",
+            "value": "banana",
+          },
+        ]
+      }
+      value="orange"
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for select setting should render as read only with help text if overridden 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
->
-  <EuiFlexItem
-    grow={false}
-  >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Select test setting",
-              }
-            }
-          />
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Select test setting",
+          }
+        }
+      />
+      <React.Fragment>
+        <EuiSpacer
+          size="s"
+        />
+        <EuiText
+          size="xs"
+        >
           <React.Fragment>
-            <EuiSpacer
-              size="s"
-            />
-            <EuiText
-              size="xs"
-            >
-              <React.Fragment>
-                <FormattedMessage
-                  defaultMessage="Default: {value}"
-                  id="advancedSettings.field.defaultValueText"
-                  values={
-                    Object {
-                      "value": <EuiCode>
-                        Orange
-                      </EuiCode>,
-                    }
-                  }
-                />
-              </React.Fragment>
-            </EuiText>
-          </React.Fragment>
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Select test setting
-          
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "select:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={
-          <EuiText
-            size="xs"
-          >
             <FormattedMessage
-              defaultMessage="This setting is overridden by the Kibana server and can not be changed."
-              id="advancedSettings.field.helpText"
-              values={Object {}}
+              defaultMessage="Default: {value}"
+              id="advancedSettings.field.defaultValueText"
+              values={
+                Object {
+                  "value": <EuiCode>
+                    Orange
+                  </EuiCode>,
+                }
+              }
             />
-          </EuiText>
-        }
-        isInvalid={false}
-        label="select:test:setting"
-        labelType="label"
+          </React.Fragment>
+        </EuiText>
+      </React.Fragment>
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Select test setting
+      
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={
+      <EuiText
+        size="xs"
       >
-        <EuiSelect
-          aria-label="select test setting"
-          data-test-subj="advancedSetting-editField-select:test:setting"
-          disabled={true}
-          isLoading={false}
-          onChange={[Function]}
-          onKeyDown={[Function]}
-          options={
-            Array [
-              Object {
-                "text": "Apple",
-                "value": "apple",
-              },
-              Object {
-                "text": "Orange",
-                "value": "orange",
-              },
-              Object {
-                "text": "banana",
-                "value": "banana",
-              },
-            ]
-          }
-          value="banana"
+        <FormattedMessage
+          defaultMessage="This setting is overridden by the Kibana server and can not be changed."
+          id="advancedSettings.field.helpText"
+          values={Object {}}
         />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+      </EuiText>
+    }
+    label="select:test:setting"
+    labelType="label"
+  >
+    <EuiSelect
+      aria-label="select test setting"
+      data-test-subj="advancedSetting-editField-select:test:setting"
+      disabled={true}
+      fullWidth={true}
+      onChange={[Function]}
+      options={
+        Array [
+          Object {
+            "text": "Apple",
+            "value": "apple",
+          },
+          Object {
+            "text": "Orange",
+            "value": "orange",
+          },
+          Object {
+            "text": "banana",
+            "value": "banana",
+          },
+        ]
+      }
+      value="banana"
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for select setting should render custom setting icon if it is custom 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Select test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Select test setting
+      <EuiIconTip
+        aria-label="Custom setting"
+        color="primary"
+        content={
+          <FormattedMessage
+            defaultMessage="Custom setting"
+            id="advancedSettings.field.customSettingTooltip"
+            values={Object {}}
+          />
+        }
+        type="asterisk"
+      />
+    </h3>
+  }
 >
-  <EuiFlexItem
-    grow={false}
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="select:test:setting"
+    labelType="label"
   >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Select test setting",
-              }
-            }
-          />
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Select test setting
-          <EuiIconTip
-            aria-label="Custom setting"
-            color="primary"
-            content={
-              <FormattedMessage
-                defaultMessage="Custom setting"
-                id="advancedSettings.field.customSettingTooltip"
-                values={Object {}}
-              />
-            }
-            type="asterisk"
-          />
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "select:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={null}
-        isInvalid={false}
-        label="select:test:setting"
-        labelType="label"
-      >
-        <EuiSelect
-          aria-label="select test setting"
-          data-test-subj="advancedSetting-editField-select:test:setting"
-          disabled={false}
-          isLoading={false}
-          onChange={[Function]}
-          onKeyDown={[Function]}
-          options={
-            Array [
-              Object {
-                "text": "Apple",
-                "value": "apple",
-              },
-              Object {
-                "text": "Orange",
-                "value": "orange",
-              },
-              Object {
-                "text": "banana",
-                "value": "banana",
-              },
-            ]
-          }
-          value="orange"
-        />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+    <EuiSelect
+      aria-label="select test setting"
+      data-test-subj="advancedSetting-editField-select:test:setting"
+      disabled={false}
+      fullWidth={true}
+      onChange={[Function]}
+      options={
+        Array [
+          Object {
+            "text": "Apple",
+            "value": "apple",
+          },
+          Object {
+            "text": "Orange",
+            "value": "orange",
+          },
+          Object {
+            "text": "banana",
+            "value": "banana",
+          },
+        ]
+      }
+      value="orange"
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for select setting should render default value if there is no user value set 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Select test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Select test setting
+      
+    </h3>
+  }
 >
-  <EuiFlexItem
-    grow={false}
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="select:test:setting"
+    labelType="label"
   >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Select test setting",
-              }
-            }
-          />
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Select test setting
-          
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "select:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={null}
-        isInvalid={false}
-        label="select:test:setting"
-        labelType="label"
+    <EuiSelect
+      aria-label="select test setting"
+      data-test-subj="advancedSetting-editField-select:test:setting"
+      disabled={false}
+      fullWidth={true}
+      onChange={[Function]}
+      options={
+        Array [
+          Object {
+            "text": "Apple",
+            "value": "apple",
+          },
+          Object {
+            "text": "Orange",
+            "value": "orange",
+          },
+          Object {
+            "text": "banana",
+            "value": "banana",
+          },
+        ]
+      }
+      value="orange"
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
+`;
+
+exports[`Field for select setting should render unsaved value if there are unsaved changes 1`] = `
+<EuiDescribedFormGroup
+  className="mgtAdvancedSettings__field mgtAdvancedSettings__field--unsaved"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Select test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Select test setting
+      <EuiIconTip
+        aria-label="Custom setting"
+        color="primary"
+        content={
+          <FormattedMessage
+            defaultMessage="Custom setting"
+            id="advancedSettings.field.customSettingTooltip"
+            values={Object {}}
+          />
+        }
+        type="asterisk"
+      />
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="select:test:setting"
+    labelType="label"
+  >
+    <EuiSelect
+      aria-describedby="select:test:setting"
+      aria-label="select test setting"
+      data-test-subj="advancedSetting-editField-select:test:setting"
+      disabled={false}
+      fullWidth={true}
+      onChange={[Function]}
+      options={
+        Array [
+          Object {
+            "text": "Apple",
+            "value": "apple",
+          },
+          Object {
+            "text": "Orange",
+            "value": "orange",
+          },
+          Object {
+            "text": "banana",
+            "value": "banana",
+          },
+        ]
+      }
+      value="banana"
+    />
+    <EuiScreenReaderOnly>
+      <p
+        id="select:test:setting"
       >
-        <EuiSelect
-          aria-label="select test setting"
-          data-test-subj="advancedSetting-editField-select:test:setting"
-          disabled={false}
-          isLoading={false}
-          onChange={[Function]}
-          onKeyDown={[Function]}
-          options={
-            Array [
-              Object {
-                "text": "Apple",
-                "value": "apple",
-              },
-              Object {
-                "text": "Orange",
-                "value": "orange",
-              },
-              Object {
-                "text": "banana",
-                "value": "banana",
-              },
-            ]
-          }
-          value="orange"
-        />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+        Setting is currently not saved.
+      </p>
+    </EuiScreenReaderOnly>
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for select setting should render user value if there is user value is set 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
->
-  <EuiFlexItem
-    grow={false}
-  >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for Select test setting",
-              }
-            }
-          />
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for Select test setting",
+          }
+        }
+      />
+      <React.Fragment>
+        <EuiSpacer
+          size="s"
+        />
+        <EuiText
+          size="xs"
+        >
           <React.Fragment>
-            <EuiSpacer
-              size="s"
+            <FormattedMessage
+              defaultMessage="Default: {value}"
+              id="advancedSettings.field.defaultValueText"
+              values={
+                Object {
+                  "value": <EuiCode>
+                    Orange
+                  </EuiCode>,
+                }
+              }
             />
-            <EuiText
-              size="xs"
-            >
-              <React.Fragment>
-                <FormattedMessage
-                  defaultMessage="Default: {value}"
-                  id="advancedSettings.field.defaultValueText"
-                  values={
-                    Object {
-                      "value": <EuiCode>
-                        Orange
-                      </EuiCode>,
-                    }
-                  }
-                />
-              </React.Fragment>
-            </EuiText>
           </React.Fragment>
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          Select test setting
-          
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "select:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={
-          <span>
-            <span>
-              <ForwardRef
-                aria-label="Reset select test setting to default"
-                data-test-subj="advancedSetting-resetField-select:test:setting"
-                onClick={[Function]}
-              >
-                <FormattedMessage
-                  defaultMessage="Reset to default"
-                  id="advancedSettings.field.resetToDefaultLinkText"
-                  values={Object {}}
-                />
-              </ForwardRef>
-                 
-            </span>
-          </span>
-        }
-        isInvalid={false}
-        label="select:test:setting"
-        labelType="label"
-      >
-        <EuiSelect
-          aria-label="select test setting"
-          data-test-subj="advancedSetting-editField-select:test:setting"
-          disabled={false}
-          isLoading={false}
-          onChange={[Function]}
-          onKeyDown={[Function]}
-          options={
-            Array [
-              Object {
-                "text": "Apple",
-                "value": "apple",
-              },
-              Object {
-                "text": "Orange",
-                "value": "orange",
-              },
-              Object {
-                "text": "banana",
-                "value": "banana",
-              },
-            ]
-          }
-          value="banana"
-        />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+        </EuiText>
+      </React.Fragment>
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      Select test setting
+      
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={
+      <span>
+        <span>
+          <ForwardRef
+            aria-label="Reset select test setting to default"
+            data-test-subj="advancedSetting-resetField-select:test:setting"
+            onClick={[Function]}
+          >
+            <FormattedMessage
+              defaultMessage="Reset to default"
+              id="advancedSettings.field.resetToDefaultLinkText"
+              values={Object {}}
+            />
+          </ForwardRef>
+             
+        </span>
+      </span>
+    }
+    label="select:test:setting"
+    labelType="label"
+  >
+    <EuiSelect
+      aria-label="select test setting"
+      data-test-subj="advancedSetting-editField-select:test:setting"
+      disabled={false}
+      fullWidth={true}
+      onChange={[Function]}
+      options={
+        Array [
+          Object {
+            "text": "Apple",
+            "value": "apple",
+          },
+          Object {
+            "text": "Orange",
+            "value": "orange",
+          },
+          Object {
+            "text": "banana",
+            "value": "banana",
+          },
+        ]
+      }
+      value="banana"
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for string setting should render as read only if saving is disabled 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for String test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      String test setting
+      
+    </h3>
+  }
 >
-  <EuiFlexItem
-    grow={false}
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="string:test:setting"
+    labelType="label"
   >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for String test setting",
-              }
-            }
-          />
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          String test setting
-          
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "string:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={null}
-        isInvalid={false}
-        label="string:test:setting"
-        labelType="label"
-      >
-        <EuiFieldText
-          aria-label="string test setting"
-          data-test-subj="advancedSetting-editField-string:test:setting"
-          disabled={true}
-          isLoading={false}
-          onChange={[Function]}
-          onKeyDown={[Function]}
-          value=""
-        />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+    <EuiFieldText
+      aria-label="string test setting"
+      data-test-subj="advancedSetting-editField-string:test:setting"
+      disabled={true}
+      fullWidth={true}
+      onChange={[Function]}
+      value=""
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for string setting should render as read only with help text if overridden 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
->
-  <EuiFlexItem
-    grow={false}
-  >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for String test setting",
-              }
-            }
-          />
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for String test setting",
+          }
+        }
+      />
+      <React.Fragment>
+        <EuiSpacer
+          size="s"
+        />
+        <EuiText
+          size="xs"
+        >
           <React.Fragment>
-            <EuiSpacer
-              size="s"
-            />
-            <EuiText
-              size="xs"
-            >
-              <React.Fragment>
-                <FormattedMessage
-                  defaultMessage="Default: {value}"
-                  id="advancedSettings.field.defaultValueText"
-                  values={
-                    Object {
-                      "value": <EuiCode>
-                        null
-                      </EuiCode>,
-                    }
-                  }
-                />
-              </React.Fragment>
-            </EuiText>
-          </React.Fragment>
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          String test setting
-          
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "string:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={
-          <EuiText
-            size="xs"
-          >
             <FormattedMessage
-              defaultMessage="This setting is overridden by the Kibana server and can not be changed."
-              id="advancedSettings.field.helpText"
-              values={Object {}}
+              defaultMessage="Default: {value}"
+              id="advancedSettings.field.defaultValueText"
+              values={
+                Object {
+                  "value": <EuiCode>
+                    null
+                  </EuiCode>,
+                }
+              }
             />
-          </EuiText>
-        }
-        isInvalid={false}
-        label="string:test:setting"
-        labelType="label"
+          </React.Fragment>
+        </EuiText>
+      </React.Fragment>
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      String test setting
+      
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={
+      <EuiText
+        size="xs"
       >
-        <EuiFieldText
-          aria-label="string test setting"
-          data-test-subj="advancedSetting-editField-string:test:setting"
-          disabled={true}
-          isLoading={false}
-          onChange={[Function]}
-          onKeyDown={[Function]}
-          value="foo"
+        <FormattedMessage
+          defaultMessage="This setting is overridden by the Kibana server and can not be changed."
+          id="advancedSettings.field.helpText"
+          values={Object {}}
         />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+      </EuiText>
+    }
+    label="string:test:setting"
+    labelType="label"
+  >
+    <EuiFieldText
+      aria-label="string test setting"
+      data-test-subj="advancedSetting-editField-string:test:setting"
+      disabled={true}
+      fullWidth={true}
+      onChange={[Function]}
+      value="foo"
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for string setting should render custom setting icon if it is custom 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for String test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      String test setting
+      <EuiIconTip
+        aria-label="Custom setting"
+        color="primary"
+        content={
+          <FormattedMessage
+            defaultMessage="Custom setting"
+            id="advancedSettings.field.customSettingTooltip"
+            values={Object {}}
+          />
+        }
+        type="asterisk"
+      />
+    </h3>
+  }
 >
-  <EuiFlexItem
-    grow={false}
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="string:test:setting"
+    labelType="label"
   >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for String test setting",
-              }
-            }
-          />
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          String test setting
-          <EuiIconTip
-            aria-label="Custom setting"
-            color="primary"
-            content={
-              <FormattedMessage
-                defaultMessage="Custom setting"
-                id="advancedSettings.field.customSettingTooltip"
-                values={Object {}}
-              />
-            }
-            type="asterisk"
-          />
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "string:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={null}
-        isInvalid={false}
-        label="string:test:setting"
-        labelType="label"
-      >
-        <EuiFieldText
-          aria-label="string test setting"
-          data-test-subj="advancedSetting-editField-string:test:setting"
-          disabled={false}
-          isLoading={false}
-          onChange={[Function]}
-          onKeyDown={[Function]}
-          value=""
-        />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+    <EuiFieldText
+      aria-label="string test setting"
+      data-test-subj="advancedSetting-editField-string:test:setting"
+      disabled={false}
+      fullWidth={true}
+      onChange={[Function]}
+      value=""
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for string setting should render default value if there is no user value set 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for String test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      String test setting
+      
+    </h3>
+  }
 >
-  <EuiFlexItem
-    grow={false}
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="string:test:setting"
+    labelType="label"
   >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for String test setting",
-              }
-            }
-          />
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          String test setting
-          
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "string:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={null}
-        isInvalid={false}
-        label="string:test:setting"
-        labelType="label"
+    <EuiFieldText
+      aria-label="string test setting"
+      data-test-subj="advancedSetting-editField-string:test:setting"
+      disabled={false}
+      fullWidth={true}
+      onChange={[Function]}
+      value=""
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
+`;
+
+exports[`Field for string setting should render unsaved value if there are unsaved changes 1`] = `
+<EuiDescribedFormGroup
+  className="mgtAdvancedSettings__field mgtAdvancedSettings__field--unsaved"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for String test setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      String test setting
+      <EuiIconTip
+        aria-label="Custom setting"
+        color="primary"
+        content={
+          <FormattedMessage
+            defaultMessage="Custom setting"
+            id="advancedSettings.field.customSettingTooltip"
+            values={Object {}}
+          />
+        }
+        type="asterisk"
+      />
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="string:test:setting"
+    labelType="label"
+  >
+    <EuiFieldText
+      aria-describedby="string:test:setting"
+      aria-label="string test setting"
+      data-test-subj="advancedSetting-editField-string:test:setting"
+      disabled={false}
+      fullWidth={true}
+      onChange={[Function]}
+      value="hello world"
+    />
+    <EuiScreenReaderOnly>
+      <p
+        id="string:test:setting"
       >
-        <EuiFieldText
-          aria-label="string test setting"
-          data-test-subj="advancedSetting-editField-string:test:setting"
-          disabled={false}
-          isLoading={false}
-          onChange={[Function]}
-          onKeyDown={[Function]}
-          value=""
-        />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+        Setting is currently not saved.
+      </p>
+    </EuiScreenReaderOnly>
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for string setting should render user value if there is user value is set 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
->
-  <EuiFlexItem
-    grow={false}
-  >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for String test setting",
-              }
-            }
-          />
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for String test setting",
+          }
+        }
+      />
+      <React.Fragment>
+        <EuiSpacer
+          size="s"
+        />
+        <EuiText
+          size="xs"
+        >
           <React.Fragment>
-            <EuiSpacer
-              size="s"
+            <FormattedMessage
+              defaultMessage="Default: {value}"
+              id="advancedSettings.field.defaultValueText"
+              values={
+                Object {
+                  "value": <EuiCode>
+                    null
+                  </EuiCode>,
+                }
+              }
             />
-            <EuiText
-              size="xs"
-            >
-              <React.Fragment>
-                <FormattedMessage
-                  defaultMessage="Default: {value}"
-                  id="advancedSettings.field.defaultValueText"
-                  values={
-                    Object {
-                      "value": <EuiCode>
-                        null
-                      </EuiCode>,
-                    }
-                  }
-                />
-              </React.Fragment>
-            </EuiText>
           </React.Fragment>
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          String test setting
-          
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "string:test:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={
-          <span>
-            <span>
-              <ForwardRef
-                aria-label="Reset string test setting to default"
-                data-test-subj="advancedSetting-resetField-string:test:setting"
-                onClick={[Function]}
-              >
-                <FormattedMessage
-                  defaultMessage="Reset to default"
-                  id="advancedSettings.field.resetToDefaultLinkText"
-                  values={Object {}}
-                />
-              </ForwardRef>
-                 
-            </span>
-          </span>
-        }
-        isInvalid={false}
-        label="string:test:setting"
-        labelType="label"
-      >
-        <EuiFieldText
-          aria-label="string test setting"
-          data-test-subj="advancedSetting-editField-string:test:setting"
-          disabled={false}
-          isLoading={false}
-          onChange={[Function]}
-          onKeyDown={[Function]}
-          value="foo"
-        />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+        </EuiText>
+      </React.Fragment>
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      String test setting
+      
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={
+      <span>
+        <span>
+          <ForwardRef
+            aria-label="Reset string test setting to default"
+            data-test-subj="advancedSetting-resetField-string:test:setting"
+            onClick={[Function]}
+          >
+            <FormattedMessage
+              defaultMessage="Reset to default"
+              id="advancedSettings.field.resetToDefaultLinkText"
+              values={Object {}}
+            />
+          </ForwardRef>
+             
+        </span>
+      </span>
+    }
+    label="string:test:setting"
+    labelType="label"
+  >
+    <EuiFieldText
+      aria-label="string test setting"
+      data-test-subj="advancedSetting-editField-string:test:setting"
+      disabled={false}
+      fullWidth={true}
+      onChange={[Function]}
+      value="foo"
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for stringWithValidation setting should render as read only if saving is disabled 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for String test validation setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      String test validation setting
+      
+    </h3>
+  }
 >
-  <EuiFlexItem
-    grow={false}
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="string:test-validation:setting"
+    labelType="label"
   >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for String test validation setting",
-              }
-            }
-          />
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          String test validation setting
-          
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "string:test-validation:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={null}
-        isInvalid={false}
-        label="string:test-validation:setting"
-        labelType="label"
-      >
-        <EuiFieldText
-          aria-label="string test validation setting"
-          data-test-subj="advancedSetting-editField-string:test-validation:setting"
-          disabled={true}
-          isLoading={false}
-          onChange={[Function]}
-          onKeyDown={[Function]}
-          value="foo-default"
-        />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+    <EuiFieldText
+      aria-label="string test validation setting"
+      data-test-subj="advancedSetting-editField-string:test-validation:setting"
+      disabled={true}
+      fullWidth={true}
+      onChange={[Function]}
+      value="foo-default"
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for stringWithValidation setting should render as read only with help text if overridden 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
->
-  <EuiFlexItem
-    grow={false}
-  >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for String test validation setting",
-              }
-            }
-          />
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for String test validation setting",
+          }
+        }
+      />
+      <React.Fragment>
+        <EuiSpacer
+          size="s"
+        />
+        <EuiText
+          size="xs"
+        >
           <React.Fragment>
-            <EuiSpacer
-              size="s"
-            />
-            <EuiText
-              size="xs"
-            >
-              <React.Fragment>
-                <FormattedMessage
-                  defaultMessage="Default: {value}"
-                  id="advancedSettings.field.defaultValueText"
-                  values={
-                    Object {
-                      "value": <EuiCode>
-                        foo-default
-                      </EuiCode>,
-                    }
-                  }
-                />
-              </React.Fragment>
-            </EuiText>
-          </React.Fragment>
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          String test validation setting
-          
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "string:test-validation:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={
-          <EuiText
-            size="xs"
-          >
             <FormattedMessage
-              defaultMessage="This setting is overridden by the Kibana server and can not be changed."
-              id="advancedSettings.field.helpText"
-              values={Object {}}
+              defaultMessage="Default: {value}"
+              id="advancedSettings.field.defaultValueText"
+              values={
+                Object {
+                  "value": <EuiCode>
+                    foo-default
+                  </EuiCode>,
+                }
+              }
             />
-          </EuiText>
-        }
-        isInvalid={false}
-        label="string:test-validation:setting"
-        labelType="label"
+          </React.Fragment>
+        </EuiText>
+      </React.Fragment>
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      String test validation setting
+      
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={
+      <EuiText
+        size="xs"
       >
-        <EuiFieldText
-          aria-label="string test validation setting"
-          data-test-subj="advancedSetting-editField-string:test-validation:setting"
-          disabled={true}
-          isLoading={false}
-          onChange={[Function]}
-          onKeyDown={[Function]}
-          value="fooUserValue"
+        <FormattedMessage
+          defaultMessage="This setting is overridden by the Kibana server and can not be changed."
+          id="advancedSettings.field.helpText"
+          values={Object {}}
         />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+      </EuiText>
+    }
+    label="string:test-validation:setting"
+    labelType="label"
+  >
+    <EuiFieldText
+      aria-label="string test validation setting"
+      data-test-subj="advancedSetting-editField-string:test-validation:setting"
+      disabled={true}
+      fullWidth={true}
+      onChange={[Function]}
+      value="fooUserValue"
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for stringWithValidation setting should render custom setting icon if it is custom 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for String test validation setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      String test validation setting
+      <EuiIconTip
+        aria-label="Custom setting"
+        color="primary"
+        content={
+          <FormattedMessage
+            defaultMessage="Custom setting"
+            id="advancedSettings.field.customSettingTooltip"
+            values={Object {}}
+          />
+        }
+        type="asterisk"
+      />
+    </h3>
+  }
 >
-  <EuiFlexItem
-    grow={false}
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="string:test-validation:setting"
+    labelType="label"
   >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for String test validation setting",
-              }
-            }
-          />
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          String test validation setting
-          <EuiIconTip
-            aria-label="Custom setting"
-            color="primary"
-            content={
-              <FormattedMessage
-                defaultMessage="Custom setting"
-                id="advancedSettings.field.customSettingTooltip"
-                values={Object {}}
-              />
-            }
-            type="asterisk"
-          />
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "string:test-validation:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={null}
-        isInvalid={false}
-        label="string:test-validation:setting"
-        labelType="label"
-      >
-        <EuiFieldText
-          aria-label="string test validation setting"
-          data-test-subj="advancedSetting-editField-string:test-validation:setting"
-          disabled={false}
-          isLoading={false}
-          onChange={[Function]}
-          onKeyDown={[Function]}
-          value="foo-default"
-        />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+    <EuiFieldText
+      aria-label="string test validation setting"
+      data-test-subj="advancedSetting-editField-string:test-validation:setting"
+      disabled={false}
+      fullWidth={true}
+      onChange={[Function]}
+      value="foo-default"
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for stringWithValidation setting should render default value if there is no user value set 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for String test validation setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      String test validation setting
+      
+    </h3>
+  }
 >
-  <EuiFlexItem
-    grow={false}
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="string:test-validation:setting"
+    labelType="label"
   >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for String test validation setting",
-              }
-            }
-          />
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          String test validation setting
-          
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "string:test-validation:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={null}
-        isInvalid={false}
-        label="string:test-validation:setting"
-        labelType="label"
+    <EuiFieldText
+      aria-label="string test validation setting"
+      data-test-subj="advancedSetting-editField-string:test-validation:setting"
+      disabled={false}
+      fullWidth={true}
+      onChange={[Function]}
+      value="foo-default"
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
+`;
+
+exports[`Field for stringWithValidation setting should render unsaved value if there are unsaved changes 1`] = `
+<EuiDescribedFormGroup
+  className="mgtAdvancedSettings__field mgtAdvancedSettings__field--unsaved"
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for String test validation setting",
+          }
+        }
+      />
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      String test validation setting
+      <EuiIconTip
+        aria-label="Custom setting"
+        color="primary"
+        content={
+          <FormattedMessage
+            defaultMessage="Custom setting"
+            id="advancedSettings.field.customSettingTooltip"
+            values={Object {}}
+          />
+        }
+        type="asterisk"
+      />
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={null}
+    label="string:test-validation:setting"
+    labelType="label"
+  >
+    <EuiFieldText
+      aria-describedby="string:test-validation:setting"
+      aria-label="string test validation setting"
+      data-test-subj="advancedSetting-editField-string:test-validation:setting"
+      disabled={false}
+      fullWidth={true}
+      onChange={[Function]}
+      value="hello world"
+    />
+    <EuiScreenReaderOnly>
+      <p
+        id="string:test-validation:setting"
       >
-        <EuiFieldText
-          aria-label="string test validation setting"
-          data-test-subj="advancedSetting-editField-string:test-validation:setting"
-          disabled={false}
-          isLoading={false}
-          onChange={[Function]}
-          onKeyDown={[Function]}
-          value="foo-default"
-        />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+        Setting is currently not saved.
+      </p>
+    </EuiScreenReaderOnly>
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
 
 exports[`Field for stringWithValidation setting should render user value if there is user value is set 1`] = `
-<EuiFlexGroup
+<EuiDescribedFormGroup
   className="mgtAdvancedSettings__field"
->
-  <EuiFlexItem
-    grow={false}
-  >
-    <EuiDescribedFormGroup
-      className="mgtAdvancedSettings__fieldWrapper"
-      description={
-        <React.Fragment>
-          <div
-            dangerouslySetInnerHTML={
-              Object {
-                "__html": "Description for String test validation setting",
-              }
-            }
-          />
+  description={
+    <React.Fragment>
+      <div
+        dangerouslySetInnerHTML={
+          Object {
+            "__html": "Description for String test validation setting",
+          }
+        }
+      />
+      <React.Fragment>
+        <EuiSpacer
+          size="s"
+        />
+        <EuiText
+          size="xs"
+        >
           <React.Fragment>
-            <EuiSpacer
-              size="s"
+            <FormattedMessage
+              defaultMessage="Default: {value}"
+              id="advancedSettings.field.defaultValueText"
+              values={
+                Object {
+                  "value": <EuiCode>
+                    foo-default
+                  </EuiCode>,
+                }
+              }
             />
-            <EuiText
-              size="xs"
-            >
-              <React.Fragment>
-                <FormattedMessage
-                  defaultMessage="Default: {value}"
-                  id="advancedSettings.field.defaultValueText"
-                  values={
-                    Object {
-                      "value": <EuiCode>
-                        foo-default
-                      </EuiCode>,
-                    }
-                  }
-                />
-              </React.Fragment>
-            </EuiText>
           </React.Fragment>
-        </React.Fragment>
-      }
-      title={
-        <h3>
-          String test validation setting
-          
-        </h3>
-      }
-    >
-      <EuiFormRow
-        className="mgtAdvancedSettings__fieldRow"
-        describedByIds={
-          Array [
-            "string:test-validation:setting-aria",
-          ]
-        }
-        display="row"
-        error={null}
-        fullWidth={false}
-        hasChildLabel={true}
-        hasEmptyLabelSpace={false}
-        helpText={
-          <span>
-            <span>
-              <ForwardRef
-                aria-label="Reset string test validation setting to default"
-                data-test-subj="advancedSetting-resetField-string:test-validation:setting"
-                onClick={[Function]}
-              >
-                <FormattedMessage
-                  defaultMessage="Reset to default"
-                  id="advancedSettings.field.resetToDefaultLinkText"
-                  values={Object {}}
-                />
-              </ForwardRef>
-                 
-            </span>
-          </span>
-        }
-        isInvalid={false}
-        label="string:test-validation:setting"
-        labelType="label"
-      >
-        <EuiFieldText
-          aria-label="string test validation setting"
-          data-test-subj="advancedSetting-editField-string:test-validation:setting"
-          disabled={false}
-          isLoading={false}
-          onChange={[Function]}
-          onKeyDown={[Function]}
-          value="fooUserValue"
-        />
-      </EuiFormRow>
-    </EuiDescribedFormGroup>
-  </EuiFlexItem>
-  <EuiFlexItem
-    grow={false}
-  />
-</EuiFlexGroup>
+        </EuiText>
+      </React.Fragment>
+    </React.Fragment>
+  }
+  fullWidth={true}
+  title={
+    <h3>
+      String test validation setting
+      
+    </h3>
+  }
+>
+  <EuiFormRow
+    className="mgtAdvancedSettings__fieldRow"
+    describedByIds={Array []}
+    display="row"
+    fullWidth={true}
+    hasChildLabel={true}
+    hasEmptyLabelSpace={false}
+    helpText={
+      <span>
+        <span>
+          <ForwardRef
+            aria-label="Reset string test validation setting to default"
+            data-test-subj="advancedSetting-resetField-string:test-validation:setting"
+            onClick={[Function]}
+          >
+            <FormattedMessage
+              defaultMessage="Reset to default"
+              id="advancedSettings.field.resetToDefaultLinkText"
+              values={Object {}}
+            />
+          </ForwardRef>
+             
+        </span>
+      </span>
+    }
+    label="string:test-validation:setting"
+    labelType="label"
+  >
+    <EuiFieldText
+      aria-label="string test validation setting"
+      data-test-subj="advancedSetting-editField-string:test-validation:setting"
+      disabled={false}
+      fullWidth={true}
+      onChange={[Function]}
+      value="fooUserValue"
+    />
+  </EuiFormRow>
+</EuiDescribedFormGroup>
 `;
diff --git a/src/plugins/advanced_settings/public/management_app/components/field/field.test.tsx b/src/plugins/advanced_settings/public/management_app/components/field/field.test.tsx
index 81df22ccf6e43..8e41fed685898 100644
--- a/src/plugins/advanced_settings/public/management_app/components/field/field.test.tsx
+++ b/src/plugins/advanced_settings/public/management_app/components/field/field.test.tsx
@@ -20,21 +20,14 @@
 import React from 'react';
 import { I18nProvider } from '@kbn/i18n/react';
 import { shallowWithI18nProvider, mountWithI18nProvider } from 'test_utils/enzyme_helpers';
-import { mount } from 'enzyme';
+import { mount, ReactWrapper } from 'enzyme';
 import { FieldSetting } from '../../types';
 import { UiSettingsType, StringValidation } from '../../../../../../core/public';
 import { notificationServiceMock, docLinksServiceMock } from '../../../../../../core/public/mocks';
 
 // @ts-ignore
 import { findTestSubject } from '@elastic/eui/lib/test';
-import { Field } from './field';
-
-jest.mock('ui/notify', () => ({
-  toastNotifications: {
-    addDanger: () => {},
-    add: jest.fn(),
-  },
-}));
+import { Field, getEditableValue } from './field';
 
 jest.mock('brace/theme/textmate', () => 'brace/theme/textmate');
 jest.mock('brace/mode/markdown', () => 'brace/mode/markdown');
@@ -45,6 +38,18 @@ const defaults = {
   category: ['category'],
 };
 
+const exampleValues = {
+  array: ['example_value'],
+  boolean: false,
+  image: 'data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=',
+  json: { foo: 'bar2' },
+  markdown: 'Hello World',
+  number: 1,
+  select: 'banana',
+  string: 'hello world',
+  stringWithValidation: 'foo',
+};
+
 const settings: Record<string, FieldSetting> = {
   array: {
     name: 'array:test:setting',
@@ -161,7 +166,7 @@ const settings: Record<string, FieldSetting> = {
     description: 'Description for String test validation setting',
     type: 'string',
     validation: {
-      regex: new RegExp('/^foo'),
+      regex: new RegExp('^foo'),
       message: 'must start with "foo"',
     },
     value: undefined,
@@ -182,11 +187,22 @@ const userValues = {
   string: 'foo',
   stringWithValidation: 'fooUserValue',
 };
+
 const invalidUserValues = {
   stringWithValidation: 'invalidUserValue',
 };
-const save = jest.fn(() => Promise.resolve(true));
-const clear = jest.fn(() => Promise.resolve(true));
+
+const handleChange = jest.fn();
+const clearChange = jest.fn();
+
+const getFieldSettingValue = (wrapper: ReactWrapper, name: string, type: string) => {
+  const field = findTestSubject(wrapper, `advancedSetting-editField-${name}`);
+  if (type === 'boolean') {
+    return field.props()['aria-checked'];
+  } else {
+    return field.props().value;
+  }
+};
 
 describe('Field', () => {
   Object.keys(settings).forEach(type => {
@@ -197,8 +213,7 @@ describe('Field', () => {
         const component = shallowWithI18nProvider(
           <Field
             setting={setting}
-            save={save}
-            clear={clear}
+            handleChange={handleChange}
             enableSaving={true}
             toasts={notificationServiceMock.createStartContract().toasts}
             dockLinks={docLinksServiceMock.createStartContract().links}
@@ -217,8 +232,7 @@ describe('Field', () => {
               value: userValues[type],
               isOverridden: true,
             }}
-            save={save}
-            clear={clear}
+            handleChange={handleChange}
             enableSaving={true}
             toasts={notificationServiceMock.createStartContract().toasts}
             dockLinks={docLinksServiceMock.createStartContract().links}
@@ -232,14 +246,12 @@ describe('Field', () => {
         const component = shallowWithI18nProvider(
           <Field
             setting={setting}
-            save={save}
-            clear={clear}
+            handleChange={handleChange}
             enableSaving={false}
             toasts={notificationServiceMock.createStartContract().toasts}
             dockLinks={docLinksServiceMock.createStartContract().links}
           />
         );
-
         expect(component).toMatchSnapshot();
       });
 
@@ -251,8 +263,7 @@ describe('Field', () => {
               // @ts-ignore
               value: userValues[type],
             }}
-            save={save}
-            clear={clear}
+            handleChange={handleChange}
             enableSaving={true}
             toasts={notificationServiceMock.createStartContract().toasts}
             dockLinks={docLinksServiceMock.createStartContract().links}
@@ -269,48 +280,44 @@ describe('Field', () => {
               ...setting,
               isCustom: true,
             }}
-            save={save}
-            clear={clear}
+            handleChange={handleChange}
             enableSaving={true}
             toasts={notificationServiceMock.createStartContract().toasts}
             dockLinks={docLinksServiceMock.createStartContract().links}
           />
         );
-
         expect(component).toMatchSnapshot();
       });
-    });
-
-    if (type === 'select') {
-      it('should use options for rendering values', () => {
-        const component = mountWithI18nProvider(
+      it('should render unsaved value if there are unsaved changes', async () => {
+        const component = shallowWithI18nProvider(
           <Field
             setting={{
               ...setting,
               isCustom: true,
             }}
-            save={save}
-            clear={clear}
+            handleChange={handleChange}
             enableSaving={true}
             toasts={notificationServiceMock.createStartContract().toasts}
             dockLinks={docLinksServiceMock.createStartContract().links}
+            unsavedChanges={{
+              // @ts-ignore
+              value: exampleValues[setting.type],
+            }}
           />
         );
-        const select = findTestSubject(component, `advancedSetting-editField-${setting.name}`);
-        // @ts-ignore
-        const labels = select.find('option').map(option => option.prop('value'));
-        expect(labels).toEqual(['apple', 'orange', 'banana']);
+        expect(component).toMatchSnapshot();
       });
+    });
 
-      it('should use optionLabels for rendering labels', () => {
+    if (type === 'select') {
+      it('should use options for rendering values and optionsLabels for rendering labels', () => {
         const component = mountWithI18nProvider(
           <Field
             setting={{
               ...setting,
               isCustom: true,
             }}
-            save={save}
-            clear={clear}
+            handleChange={handleChange}
             enableSaving={true}
             toasts={notificationServiceMock.createStartContract().toasts}
             dockLinks={docLinksServiceMock.createStartContract().links}
@@ -318,6 +325,9 @@ describe('Field', () => {
         );
         const select = findTestSubject(component, `advancedSetting-editField-${setting.name}`);
         // @ts-ignore
+        const values = select.find('option').map(option => option.prop('value'));
+        expect(values).toEqual(['apple', 'orange', 'banana']);
+        // @ts-ignore
         const labels = select.find('option').map(option => option.text());
         expect(labels).toEqual(['Apple', 'Orange', 'banana']);
       });
@@ -328,8 +338,8 @@ describe('Field', () => {
         <I18nProvider>
           <Field
             setting={setting}
-            save={save}
-            clear={clear}
+            clearChange={clearChange}
+            handleChange={handleChange}
             enableSaving={true}
             toasts={notificationServiceMock.createStartContract().toasts}
             dockLinks={docLinksServiceMock.createStartContract().links}
@@ -352,90 +362,52 @@ describe('Field', () => {
         const userValue = userValues[type];
         (component.instance() as Field).getImageAsBase64 = ({}: Blob) => Promise.resolve('');
 
-        it('should be able to change value from no value and cancel', async () => {
-          await (component.instance() as Field).onImageChange([userValue]);
-          const updated = wrapper.update();
-          findTestSubject(updated, `advancedSetting-cancelEditField-${setting.name}`).simulate(
-            'click'
-          );
-          expect(
-            (component.instance() as Field).state.unsavedValue ===
-              (component.instance() as Field).state.savedValue
-          ).toBe(true);
-        });
-
-        it('should be able to change value and save', async () => {
-          await (component.instance() as Field).onImageChange([userValue]);
-          const updated = wrapper.update();
-          findTestSubject(updated, `advancedSetting-saveEditField-${setting.name}`).simulate(
-            'click'
-          );
-          expect(save).toBeCalled();
-          component.setState({ savedValue: userValue });
+        it('should be able to change value and cancel', async () => {
+          (component.instance() as Field).onImageChange([userValue]);
+          expect(handleChange).toBeCalled();
           await wrapper.setProps({
+            unsavedChanges: {
+              value: userValue,
+              changeImage: true,
+            },
             setting: {
               ...(component.instance() as Field).props.setting,
               value: userValue,
             },
           });
-
           await (component.instance() as Field).cancelChangeImage();
+          expect(clearChange).toBeCalledWith(setting.name);
           wrapper.update();
         });
 
-        it('should be able to change value from existing value and save', async () => {
+        it('should be able to change value from existing value', async () => {
+          await wrapper.setProps({
+            unsavedChanges: {},
+          });
           const updated = wrapper.update();
           findTestSubject(updated, `advancedSetting-changeImage-${setting.name}`).simulate('click');
-
           const newUserValue = `${userValue}=`;
           await (component.instance() as Field).onImageChange([newUserValue]);
-          const updated2 = wrapper.update();
-          findTestSubject(updated2, `advancedSetting-saveEditField-${setting.name}`).simulate(
-            'click'
-          );
-          expect(save).toBeCalled();
-          component.setState({ savedValue: newUserValue });
-          await wrapper.setProps({
-            setting: {
-              ...(component.instance() as Field).props.setting,
-              value: newUserValue,
-            },
-          });
-          wrapper.update();
+          expect(handleChange).toBeCalled();
         });
 
         it('should be able to reset to default value', async () => {
           const updated = wrapper.update();
           findTestSubject(updated, `advancedSetting-resetField-${setting.name}`).simulate('click');
-          expect(clear).toBeCalled();
+          expect(handleChange).toBeCalledWith(setting.name, {
+            value: getEditableValue(setting.type, setting.defVal),
+            changeImage: true,
+          });
         });
       });
     } else if (type === 'markdown' || type === 'json') {
       describe(`for changing ${type} setting`, () => {
         const { wrapper, component } = setup();
         const userValue = userValues[type];
-        const fieldUserValue = userValue;
-
-        it('should be able to change value and cancel', async () => {
-          (component.instance() as Field).onCodeEditorChange(fieldUserValue as UiSettingsType);
-          const updated = wrapper.update();
-          findTestSubject(updated, `advancedSetting-cancelEditField-${setting.name}`).simulate(
-            'click'
-          );
-          expect(
-            (component.instance() as Field).state.unsavedValue ===
-              (component.instance() as Field).state.savedValue
-          ).toBe(true);
-        });
 
-        it('should be able to change value and save', async () => {
-          (component.instance() as Field).onCodeEditorChange(fieldUserValue as UiSettingsType);
-          const updated = wrapper.update();
-          findTestSubject(updated, `advancedSetting-saveEditField-${setting.name}`).simulate(
-            'click'
-          );
-          expect(save).toBeCalled();
-          component.setState({ savedValue: fieldUserValue });
+        it('should be able to change value', async () => {
+          (component.instance() as Field).onCodeEditorChange(userValue as UiSettingsType);
+          expect(handleChange).toBeCalledWith(setting.name, { value: userValue });
           await wrapper.setProps({
             setting: {
               ...(component.instance() as Field).props.setting,
@@ -445,19 +417,21 @@ describe('Field', () => {
           wrapper.update();
         });
 
+        it('should be able to reset to default value', async () => {
+          const updated = wrapper.update();
+          findTestSubject(updated, `advancedSetting-resetField-${setting.name}`).simulate('click');
+          expect(handleChange).toBeCalledWith(setting.name, {
+            value: getEditableValue(setting.type, setting.defVal),
+          });
+        });
+
         if (type === 'json') {
           it('should be able to clear value and have empty object populate', async () => {
-            (component.instance() as Field).onCodeEditorChange('' as UiSettingsType);
+            await (component.instance() as Field).onCodeEditorChange('' as UiSettingsType);
             wrapper.update();
-            expect((component.instance() as Field).state.unsavedValue).toEqual('{}');
+            expect(handleChange).toBeCalledWith(setting.name, { value: setting.defVal });
           });
         }
-
-        it('should be able to reset to default value', async () => {
-          const updated = wrapper.update();
-          findTestSubject(updated, `advancedSetting-resetField-${setting.name}`).simulate('click');
-          expect(clear).toBeCalled();
-        });
       });
     } else {
       describe(`for changing ${type} setting`, () => {
@@ -470,76 +444,45 @@ describe('Field', () => {
           // @ts-ignore
           const invalidUserValue = invalidUserValues[type];
           it('should display an error when validation fails', async () => {
-            (component.instance() as Field).onFieldChange(invalidUserValue);
+            await (component.instance() as Field).onFieldChange(invalidUserValue);
+            const expectedUnsavedChanges = {
+              value: invalidUserValue,
+              error: (setting.validation as StringValidation).message,
+              isInvalid: true,
+            };
+            expect(handleChange).toBeCalledWith(setting.name, expectedUnsavedChanges);
+            wrapper.setProps({ unsavedChanges: expectedUnsavedChanges });
             const updated = wrapper.update();
             const errorMessage = updated.find('.euiFormErrorText').text();
-            expect(errorMessage).toEqual((setting.validation as StringValidation).message);
+            expect(errorMessage).toEqual(expectedUnsavedChanges.error);
           });
         }
 
-        it('should be able to change value and cancel', async () => {
-          (component.instance() as Field).onFieldChange(fieldUserValue);
+        it('should be able to change value', async () => {
+          await (component.instance() as Field).onFieldChange(fieldUserValue);
           const updated = wrapper.update();
-          findTestSubject(updated, `advancedSetting-cancelEditField-${setting.name}`).simulate(
-            'click'
-          );
-          expect(
-            (component.instance() as Field).state.unsavedValue ===
-              (component.instance() as Field).state.savedValue
-          ).toBe(true);
+          expect(handleChange).toBeCalledWith(setting.name, { value: fieldUserValue });
+          updated.setProps({ unsavedChanges: { value: fieldUserValue } });
+          const currentValue = getFieldSettingValue(updated, setting.name, type);
+          expect(currentValue).toEqual(fieldUserValue);
         });
 
-        it('should be able to change value and save', async () => {
-          (component.instance() as Field).onFieldChange(fieldUserValue);
-          const updated = wrapper.update();
-          findTestSubject(updated, `advancedSetting-saveEditField-${setting.name}`).simulate(
-            'click'
-          );
-          expect(save).toBeCalled();
-          component.setState({ savedValue: fieldUserValue });
+        it('should be able to reset to default value', async () => {
           await wrapper.setProps({
-            setting: {
-              ...(component.instance() as Field).props.setting,
-              value: userValue,
-            },
+            unsavedChanges: {},
+            setting: { ...setting, value: fieldUserValue },
           });
-          wrapper.update();
-        });
-
-        it('should be able to reset to default value', async () => {
           const updated = wrapper.update();
           findTestSubject(updated, `advancedSetting-resetField-${setting.name}`).simulate('click');
-          expect(clear).toBeCalled();
+          const expectedEditableValue = getEditableValue(setting.type, setting.defVal);
+          expect(handleChange).toBeCalledWith(setting.name, {
+            value: expectedEditableValue,
+          });
+          updated.setProps({ unsavedChanges: { value: expectedEditableValue } });
+          const currentValue = getFieldSettingValue(updated, setting.name, type);
+          expect(currentValue).toEqual(expectedEditableValue);
         });
       });
     }
   });
-
-  it('should show a reload toast when saving setting requiring a page reload', async () => {
-    const setting = {
-      ...settings.string,
-      requiresPageReload: true,
-    };
-    const toasts = notificationServiceMock.createStartContract().toasts;
-    const wrapper = mountWithI18nProvider(
-      <Field
-        setting={setting}
-        save={save}
-        clear={clear}
-        enableSaving={true}
-        toasts={toasts}
-        dockLinks={docLinksServiceMock.createStartContract().links}
-      />
-    );
-    (wrapper.instance() as Field).onFieldChange({ target: { value: 'a new value' } });
-    const updated = wrapper.update();
-    findTestSubject(updated, `advancedSetting-saveEditField-${setting.name}`).simulate('click');
-    expect(save).toHaveBeenCalled();
-    await save();
-    expect(toasts.add).toHaveBeenCalledWith(
-      expect.objectContaining({
-        title: expect.stringContaining('Please reload the page'),
-      })
-    );
-  });
 });
diff --git a/src/plugins/advanced_settings/public/management_app/components/field/field.tsx b/src/plugins/advanced_settings/public/management_app/components/field/field.tsx
index 7158e3d5e7b3e..d9c3752d1c0a5 100644
--- a/src/plugins/advanced_settings/public/management_app/components/field/field.tsx
+++ b/src/plugins/advanced_settings/public/management_app/components/field/field.tsx
@@ -18,17 +18,16 @@
  */
 
 import React, { PureComponent, Fragment } from 'react';
-import ReactDOM from 'react-dom';
+import classNames from 'classnames';
 
 import 'brace/theme/textmate';
 import 'brace/mode/markdown';
 
 import {
   EuiBadge,
-  EuiButton,
-  EuiButtonEmpty,
   EuiCode,
   EuiCodeBlock,
+  EuiScreenReaderOnly,
   // @ts-ignore
   EuiCodeEditor,
   EuiDescribedFormGroup,
@@ -36,23 +35,20 @@ import {
   EuiFieldText,
   // @ts-ignore
   EuiFilePicker,
-  EuiFlexGroup,
-  EuiFlexItem,
   EuiFormRow,
   EuiIconTip,
   EuiImage,
   EuiLink,
   EuiSpacer,
-  EuiToolTip,
   EuiText,
   EuiSelect,
   EuiSwitch,
   EuiSwitchEvent,
-  keyCodes,
+  EuiToolTip,
 } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
 import { FormattedMessage } from '@kbn/i18n/react';
-import { FieldSetting } from '../../types';
+import { FieldSetting, FieldState } from '../../types';
 import { isDefaultValue } from '../../lib';
 import {
   UiSettingsType,
@@ -64,71 +60,37 @@ import {
 
 interface FieldProps {
   setting: FieldSetting;
-  save: (name: string, value: string) => Promise<boolean>;
-  clear: (name: string) => Promise<boolean>;
+  handleChange: (name: string, value: FieldState) => void;
   enableSaving: boolean;
   dockLinks: DocLinksStart['links'];
   toasts: ToastsStart;
+  clearChange?: (name: string) => void;
+  unsavedChanges?: FieldState;
+  loading?: boolean;
 }
 
-interface FieldState {
-  unsavedValue: any;
-  savedValue: any;
-  loading: boolean;
-  isInvalid: boolean;
-  error: string | null;
-  changeImage: boolean;
-  isJsonArray: boolean;
-}
-
-export class Field extends PureComponent<FieldProps, FieldState> {
-  private changeImageForm: EuiFilePicker | undefined;
-  constructor(props: FieldProps) {
-    super(props);
-    const { type, value, defVal } = this.props.setting;
-    const editableValue = this.getEditableValue(type, value, defVal);
-
-    this.state = {
-      isInvalid: false,
-      error: null,
-      loading: false,
-      changeImage: false,
-      savedValue: editableValue,
-      unsavedValue: editableValue,
-      isJsonArray: type === 'json' ? Array.isArray(JSON.parse(String(defVal) || '{}')) : false,
-    };
-  }
-
-  UNSAFE_componentWillReceiveProps(nextProps: FieldProps) {
-    const { unsavedValue } = this.state;
-    const { type, value, defVal } = nextProps.setting;
-    const editableValue = this.getEditableValue(type, value, defVal);
-
-    this.setState({
-      savedValue: editableValue,
-      unsavedValue: value === null || value === undefined ? editableValue : unsavedValue,
-    });
+export const getEditableValue = (
+  type: UiSettingsType,
+  value: FieldSetting['value'],
+  defVal?: FieldSetting['defVal']
+) => {
+  const val = value === null || value === undefined ? defVal : value;
+  switch (type) {
+    case 'array':
+      return (val as string[]).join(', ');
+    case 'boolean':
+      return !!val;
+    case 'number':
+      return Number(val);
+    case 'image':
+      return val;
+    default:
+      return val || '';
   }
+};
 
-  getEditableValue(
-    type: UiSettingsType,
-    value: FieldSetting['value'],
-    defVal: FieldSetting['defVal']
-  ) {
-    const val = value === null || value === undefined ? defVal : value;
-    switch (type) {
-      case 'array':
-        return (val as string[]).join(', ');
-      case 'boolean':
-        return !!val;
-      case 'number':
-        return Number(val);
-      case 'image':
-        return val;
-      default:
-        return val || '';
-    }
-  }
+export class Field extends PureComponent<FieldProps> {
+  private changeImageForm: EuiFilePicker | undefined = React.createRef();
 
   getDisplayedDefaultValue(
     type: UiSettingsType,
@@ -150,47 +112,60 @@ export class Field extends PureComponent<FieldProps, FieldState> {
     }
   }
 
-  setLoading(loading: boolean) {
-    this.setState({
-      loading,
-    });
-  }
+  handleChange = (unsavedChanges: FieldState) => {
+    this.props.handleChange(this.props.setting.name, unsavedChanges);
+  };
 
-  clearError() {
-    this.setState({
-      isInvalid: false,
-      error: null,
-    });
+  resetField = () => {
+    const { type, defVal } = this.props.setting;
+    if (type === 'image') {
+      this.cancelChangeImage();
+      return this.handleChange({
+        value: getEditableValue(type, defVal),
+        changeImage: true,
+      });
+    }
+    return this.handleChange({ value: getEditableValue(type, defVal) });
+  };
+
+  componentDidUpdate(prevProps: FieldProps) {
+    if (
+      prevProps.setting.type === 'image' &&
+      prevProps.unsavedChanges?.value &&
+      !this.props.unsavedChanges?.value
+    ) {
+      this.cancelChangeImage();
+    }
   }
 
   onCodeEditorChange = (value: UiSettingsType) => {
-    const { type } = this.props.setting;
-    const { isJsonArray } = this.state;
+    const { defVal, type } = this.props.setting;
 
     let newUnsavedValue;
-    let isInvalid = false;
-    let error = null;
+    let errorParams = {};
 
     switch (type) {
       case 'json':
+        const isJsonArray = Array.isArray(JSON.parse((defVal as string) || '{}'));
         newUnsavedValue = value.trim() || (isJsonArray ? '[]' : '{}');
         try {
           JSON.parse(newUnsavedValue);
         } catch (e) {
-          isInvalid = true;
-          error = i18n.translate('advancedSettings.field.codeEditorSyntaxErrorMessage', {
-            defaultMessage: 'Invalid JSON syntax',
-          });
+          errorParams = {
+            error: i18n.translate('advancedSettings.field.codeEditorSyntaxErrorMessage', {
+              defaultMessage: 'Invalid JSON syntax',
+            }),
+            isInvalid: true,
+          };
         }
         break;
       default:
         newUnsavedValue = value;
     }
 
-    this.setState({
-      error,
-      isInvalid,
-      unsavedValue: newUnsavedValue,
+    this.handleChange({
+      value: newUnsavedValue,
+      ...errorParams,
     });
   };
 
@@ -201,58 +176,44 @@ export class Field extends PureComponent<FieldProps, FieldState> {
   onFieldChangeEvent = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) =>
     this.onFieldChange(e.target.value);
 
-  onFieldChange = (value: any) => {
-    const { type, validation } = this.props.setting;
-    const { unsavedValue } = this.state;
-
+  onFieldChange = (targetValue: any) => {
+    const { type, validation, value, defVal } = this.props.setting;
     let newUnsavedValue;
 
     switch (type) {
       case 'boolean':
-        newUnsavedValue = !unsavedValue;
+        const { unsavedChanges } = this.props;
+        const currentValue = unsavedChanges
+          ? unsavedChanges.value
+          : getEditableValue(type, value, defVal);
+        newUnsavedValue = !currentValue;
         break;
       case 'number':
-        newUnsavedValue = Number(value);
+        newUnsavedValue = Number(targetValue);
         break;
       default:
-        newUnsavedValue = value;
+        newUnsavedValue = targetValue;
     }
 
-    let isInvalid = false;
-    let error = null;
+    let errorParams = {};
 
-    if (validation && (validation as StringValidationRegex).regex) {
+    if ((validation as StringValidationRegex)?.regex) {
       if (!(validation as StringValidationRegex).regex!.test(newUnsavedValue.toString())) {
-        error = (validation as StringValidationRegex).message;
-        isInvalid = true;
+        errorParams = {
+          error: (validation as StringValidationRegex).message,
+          isInvalid: true,
+        };
       }
     }
 
-    this.setState({
-      unsavedValue: newUnsavedValue,
-      isInvalid,
-      error,
+    this.handleChange({
+      value: newUnsavedValue,
+      ...errorParams,
     });
   };
 
-  onFieldKeyDown = ({ keyCode }: { keyCode: number }) => {
-    if (keyCode === keyCodes.ENTER) {
-      this.saveEdit();
-    }
-    if (keyCode === keyCodes.ESCAPE) {
-      this.cancelEdit();
-    }
-  };
-
-  onFieldEscape = ({ keyCode }: { keyCode: number }) => {
-    if (keyCode === keyCodes.ESCAPE) {
-      this.cancelEdit();
-    }
-  };
-
   onImageChange = async (files: any[]) => {
     if (!files.length) {
-      this.clearError();
       this.setState({
         unsavedValue: null,
       });
@@ -266,19 +227,24 @@ export class Field extends PureComponent<FieldProps, FieldState> {
       if (file instanceof File) {
         base64Image = (await this.getImageAsBase64(file)) as string;
       }
-      const isInvalid = !!(maxSize && maxSize.length && base64Image.length > maxSize.length);
-      this.setState({
-        isInvalid,
-        error: isInvalid
-          ? i18n.translate('advancedSettings.field.imageTooLargeErrorMessage', {
-              defaultMessage: 'Image is too large, maximum size is {maxSizeDescription}',
-              values: {
-                maxSizeDescription: maxSize.description,
-              },
-            })
-          : null,
+
+      let errorParams = {};
+      const isInvalid = !!(maxSize?.length && base64Image.length > maxSize.length);
+      if (isInvalid) {
+        errorParams = {
+          isInvalid,
+          error: i18n.translate('advancedSettings.field.imageTooLargeErrorMessage', {
+            defaultMessage: 'Image is too large, maximum size is {maxSizeDescription}',
+            values: {
+              maxSizeDescription: maxSize.description,
+            },
+          }),
+        };
+      }
+      this.handleChange({
         changeImage: true,
-        unsavedValue: base64Image,
+        value: base64Image,
+        ...errorParams,
       });
     } catch (err) {
       this.props.toasts.addDanger(
@@ -305,152 +271,62 @@ export class Field extends PureComponent<FieldProps, FieldState> {
   }
 
   changeImage = () => {
-    this.setState({
+    this.handleChange({
+      value: null,
       changeImage: true,
     });
   };
 
   cancelChangeImage = () => {
-    const { savedValue } = this.state;
-
-    if (this.changeImageForm) {
-      this.changeImageForm.fileInput.value = null;
-      this.changeImageForm.handleChange();
-    }
-
-    this.setState({
-      changeImage: false,
-      unsavedValue: savedValue,
-    });
-  };
-
-  cancelEdit = () => {
-    const { savedValue } = this.state;
-    this.clearError();
-    this.setState({
-      unsavedValue: savedValue,
-    });
-  };
-
-  showPageReloadToast = () => {
-    if (this.props.setting.requiresPageReload) {
-      this.props.toasts.add({
-        title: i18n.translate('advancedSettings.field.requiresPageReloadToastDescription', {
-          defaultMessage: 'Please reload the page for the "{settingName}" setting to take effect.',
-          values: {
-            settingName: this.props.setting.displayName || this.props.setting.name,
-          },
-        }),
-        text: element => {
-          const content = (
-            <>
-              <EuiFlexGroup justifyContent="flexEnd" gutterSize="s">
-                <EuiFlexItem grow={false}>
-                  <EuiButton size="s" onClick={() => window.location.reload()}>
-                    {i18n.translate('advancedSettings.field.requiresPageReloadToastButtonLabel', {
-                      defaultMessage: 'Reload page',
-                    })}
-                  </EuiButton>
-                </EuiFlexItem>
-              </EuiFlexGroup>
-            </>
-          );
-          ReactDOM.render(content, element);
-          return () => ReactDOM.unmountComponentAtNode(element);
-        },
-        color: 'success',
-      });
-    }
-  };
-
-  saveEdit = async () => {
-    const { name, defVal, type } = this.props.setting;
-    const { changeImage, savedValue, unsavedValue, isJsonArray } = this.state;
-
-    if (savedValue === unsavedValue) {
-      return;
-    }
-
-    let valueToSave = unsavedValue;
-    let isSameValue = false;
-
-    switch (type) {
-      case 'array':
-        valueToSave = valueToSave.split(',').map((val: string) => val.trim());
-        isSameValue = valueToSave.join(',') === (defVal as string[]).join(',');
-        break;
-      case 'json':
-        valueToSave = valueToSave.trim();
-        valueToSave = valueToSave || (isJsonArray ? '[]' : '{}');
-      default:
-        isSameValue = valueToSave === defVal;
-    }
-
-    this.setLoading(true);
-    try {
-      if (isSameValue) {
-        await this.props.clear(name);
-      } else {
-        await this.props.save(name, valueToSave);
-      }
-
-      this.showPageReloadToast();
-
-      if (changeImage) {
-        this.cancelChangeImage();
-      }
-    } catch (e) {
-      this.props.toasts.addDanger(
-        i18n.translate('advancedSettings.field.saveFieldErrorMessage', {
-          defaultMessage: 'Unable to save {name}',
-          values: { name },
-        })
-      );
+    if (this.changeImageForm.current) {
+      this.changeImageForm.current.fileInput.value = null;
+      this.changeImageForm.current.handleChange({});
     }
-    this.setLoading(false);
-  };
-
-  resetField = async () => {
-    const { name } = this.props.setting;
-    this.setLoading(true);
-    try {
-      await this.props.clear(name);
-      this.showPageReloadToast();
-      this.cancelChangeImage();
-      this.clearError();
-    } catch (e) {
-      this.props.toasts.addDanger(
-        i18n.translate('advancedSettings.field.resetFieldErrorMessage', {
-          defaultMessage: 'Unable to reset {name}',
-          values: { name },
-        })
-      );
+    if (this.props.clearChange) {
+      this.props.clearChange(this.props.setting.name);
     }
-    this.setLoading(false);
   };
 
-  renderField(setting: FieldSetting) {
-    const { enableSaving } = this.props;
-    const { loading, changeImage, unsavedValue } = this.state;
-    const { name, value, type, options, optionLabels = {}, isOverridden, ariaName } = setting;
+  renderField(id: string, setting: FieldSetting) {
+    const { enableSaving, unsavedChanges, loading } = this.props;
+    const {
+      name,
+      value,
+      type,
+      options,
+      optionLabels = {},
+      isOverridden,
+      defVal,
+      ariaName,
+    } = setting;
+    const a11yProps: { [key: string]: string } = unsavedChanges
+      ? {
+          'aria-label': ariaName,
+          'aria-describedby': id,
+        }
+      : {
+          'aria-label': ariaName,
+        };
+    const currentValue = unsavedChanges
+      ? unsavedChanges.value
+      : getEditableValue(type, value, defVal);
 
     switch (type) {
       case 'boolean':
         return (
           <EuiSwitch
             label={
-              !!unsavedValue ? (
+              !!currentValue ? (
                 <FormattedMessage id="advancedSettings.field.onLabel" defaultMessage="On" />
               ) : (
                 <FormattedMessage id="advancedSettings.field.offLabel" defaultMessage="Off" />
               )
             }
-            checked={!!unsavedValue}
+            checked={!!currentValue}
             onChange={this.onFieldChangeSwitch}
             disabled={loading || isOverridden || !enableSaving}
-            onKeyDown={this.onFieldKeyDown}
             data-test-subj={`advancedSetting-editField-${name}`}
-            aria-label={ariaName}
+            {...a11yProps}
           />
         );
       case 'markdown':
@@ -458,10 +334,10 @@ export class Field extends PureComponent<FieldProps, FieldState> {
         return (
           <div data-test-subj={`advancedSetting-editField-${name}`}>
             <EuiCodeEditor
-              aria-label={ariaName}
+              {...a11yProps}
               mode={type}
               theme="textmate"
-              value={unsavedValue}
+              value={currentValue}
               onChange={this.onCodeEditorChange}
               width="100%"
               height="auto"
@@ -476,24 +352,22 @@ export class Field extends PureComponent<FieldProps, FieldState> {
                 $blockScrolling: Infinity,
               }}
               showGutter={false}
+              fullWidth
             />
           </div>
         );
       case 'image':
+        const changeImage = unsavedChanges?.changeImage;
         if (!isDefaultValue(setting) && !changeImage) {
-          return (
-            <EuiImage aria-label={ariaName} allowFullScreen url={value as string} alt={name} />
-          );
+          return <EuiImage {...a11yProps} allowFullScreen url={value as string} alt={name} />;
         } else {
           return (
             <EuiFilePicker
               disabled={loading || isOverridden || !enableSaving}
               onChange={this.onImageChange}
               accept=".jpg,.jpeg,.png"
-              ref={(input: HTMLInputElement) => {
-                this.changeImageForm = input;
-              }}
-              onKeyDown={this.onFieldEscape}
+              ref={this.changeImageForm}
+              fullWidth
               data-test-subj={`advancedSetting-editField-${name}`}
             />
           );
@@ -501,8 +375,8 @@ export class Field extends PureComponent<FieldProps, FieldState> {
       case 'select':
         return (
           <EuiSelect
-            aria-label={ariaName}
-            value={unsavedValue}
+            {...a11yProps}
+            value={currentValue}
             options={(options as string[]).map(option => {
               return {
                 text: optionLabels.hasOwnProperty(option) ? optionLabels[option] : option,
@@ -512,31 +386,31 @@ export class Field extends PureComponent<FieldProps, FieldState> {
             onChange={this.onFieldChangeEvent}
             isLoading={loading}
             disabled={loading || isOverridden || !enableSaving}
-            onKeyDown={this.onFieldKeyDown}
+            fullWidth
             data-test-subj={`advancedSetting-editField-${name}`}
           />
         );
       case 'number':
         return (
           <EuiFieldNumber
-            aria-label={ariaName}
-            value={unsavedValue}
+            {...a11yProps}
+            value={currentValue}
             onChange={this.onFieldChangeEvent}
             isLoading={loading}
             disabled={loading || isOverridden || !enableSaving}
-            onKeyDown={this.onFieldKeyDown}
+            fullWidth
             data-test-subj={`advancedSetting-editField-${name}`}
           />
         );
       default:
         return (
           <EuiFieldText
-            aria-label={ariaName}
-            value={unsavedValue}
+            {...a11yProps}
+            value={currentValue}
             onChange={this.onFieldChangeEvent}
             isLoading={loading}
             disabled={loading || isOverridden || !enableSaving}
-            onKeyDown={this.onFieldKeyDown}
+            fullWidth
             data-test-subj={`advancedSetting-editField-${name}`}
           />
         );
@@ -699,8 +573,12 @@ export class Field extends PureComponent<FieldProps, FieldState> {
   }
 
   renderResetToDefaultLink(setting: FieldSetting) {
-    const { ariaName, name } = setting;
-    if (isDefaultValue(setting)) {
+    const { defVal, ariaName, name } = setting;
+    if (
+      defVal === this.props.unsavedChanges?.value ||
+      isDefaultValue(setting) ||
+      this.props.loading
+    ) {
       return;
     }
     return (
@@ -726,7 +604,7 @@ export class Field extends PureComponent<FieldProps, FieldState> {
   }
 
   renderChangeImageLink(setting: FieldSetting) {
-    const { changeImage } = this.state;
+    const changeImage = this.props.unsavedChanges?.changeImage;
     const { type, value, ariaName, name } = setting;
     if (type !== 'image' || !value || changeImage) {
       return;
@@ -752,84 +630,49 @@ export class Field extends PureComponent<FieldProps, FieldState> {
     );
   }
 
-  renderActions(setting: FieldSetting) {
-    const { ariaName, name } = setting;
-    const { loading, isInvalid, changeImage, savedValue, unsavedValue } = this.state;
-    const isDisabled = loading || setting.isOverridden;
-
-    if (savedValue === unsavedValue && !changeImage) {
-      return;
-    }
-
-    return (
-      <EuiFormRow className="mgtAdvancedSettings__fieldActions" hasEmptyLabelSpace>
-        <EuiFlexGroup>
-          <EuiFlexItem grow={false}>
-            <EuiButton
-              fill
-              aria-label={i18n.translate('advancedSettings.field.saveButtonAriaLabel', {
-                defaultMessage: 'Save {ariaName}',
-                values: {
-                  ariaName,
-                },
-              })}
-              onClick={this.saveEdit}
-              disabled={isDisabled || isInvalid}
-              data-test-subj={`advancedSetting-saveEditField-${name}`}
-            >
-              <FormattedMessage id="advancedSettings.field.saveButtonLabel" defaultMessage="Save" />
-            </EuiButton>
-          </EuiFlexItem>
-          <EuiFlexItem grow={false}>
-            <EuiButtonEmpty
-              aria-label={i18n.translate('advancedSettings.field.cancelEditingButtonAriaLabel', {
-                defaultMessage: 'Cancel editing {ariaName}',
-                values: {
-                  ariaName,
-                },
-              })}
-              onClick={() => (changeImage ? this.cancelChangeImage() : this.cancelEdit())}
-              disabled={isDisabled}
-              data-test-subj={`advancedSetting-cancelEditField-${name}`}
-            >
-              <FormattedMessage
-                id="advancedSettings.field.cancelEditingButtonLabel"
-                defaultMessage="Cancel"
-              />
-            </EuiButtonEmpty>
-          </EuiFlexItem>
-        </EuiFlexGroup>
-      </EuiFormRow>
-    );
-  }
-
   render() {
-    const { setting } = this.props;
-    const { error, isInvalid } = this.state;
+    const { setting, unsavedChanges } = this.props;
+    const error = unsavedChanges?.error;
+    const isInvalid = unsavedChanges?.isInvalid;
+
+    const className = classNames('mgtAdvancedSettings__field', {
+      'mgtAdvancedSettings__field--unsaved': unsavedChanges,
+      'mgtAdvancedSettings__field--invalid': isInvalid,
+    });
+    const id = setting.name;
 
     return (
-      <EuiFlexGroup className="mgtAdvancedSettings__field">
-        <EuiFlexItem grow={false}>
-          <EuiDescribedFormGroup
-            className="mgtAdvancedSettings__fieldWrapper"
-            title={this.renderTitle(setting)}
-            description={this.renderDescription(setting)}
-          >
-            <EuiFormRow
-              isInvalid={isInvalid}
-              error={error}
-              label={this.renderLabel(setting)}
-              helpText={this.renderHelpText(setting)}
-              describedByIds={[`${setting.name}-aria`]}
-              className="mgtAdvancedSettings__fieldRow"
-              hasChildLabel={setting.type !== 'boolean'}
-            >
-              {this.renderField(setting)}
-            </EuiFormRow>
-          </EuiDescribedFormGroup>
-        </EuiFlexItem>
-        <EuiFlexItem grow={false}>{this.renderActions(setting)}</EuiFlexItem>
-      </EuiFlexGroup>
+      <EuiDescribedFormGroup
+        className={className}
+        title={this.renderTitle(setting)}
+        description={this.renderDescription(setting)}
+        fullWidth
+      >
+        <EuiFormRow
+          isInvalid={isInvalid}
+          error={error}
+          label={this.renderLabel(setting)}
+          helpText={this.renderHelpText(setting)}
+          className="mgtAdvancedSettings__fieldRow"
+          hasChildLabel={setting.type !== 'boolean'}
+          fullWidth
+        >
+          <>
+            {this.renderField(id, setting)}
+            {unsavedChanges && (
+              <EuiScreenReaderOnly>
+                <p id={id}>
+                  {unsavedChanges.error
+                    ? unsavedChanges.error
+                    : i18n.translate('advancedSettings.field.settingIsUnsaved', {
+                        defaultMessage: 'Setting is currently not saved.',
+                      })}
+                </p>
+              </EuiScreenReaderOnly>
+            )}
+          </>
+        </EuiFormRow>
+      </EuiDescribedFormGroup>
     );
   }
 }
diff --git a/src/plugins/advanced_settings/public/management_app/components/field/index.ts b/src/plugins/advanced_settings/public/management_app/components/field/index.ts
index 5c86519116fe9..d1b9b34515532 100644
--- a/src/plugins/advanced_settings/public/management_app/components/field/index.ts
+++ b/src/plugins/advanced_settings/public/management_app/components/field/index.ts
@@ -17,4 +17,4 @@
  * under the License.
  */
 
-export { Field } from './field';
+export { Field, getEditableValue } from './field';
diff --git a/src/plugins/advanced_settings/public/management_app/components/form/__snapshots__/form.test.tsx.snap b/src/plugins/advanced_settings/public/management_app/components/form/__snapshots__/form.test.tsx.snap
index 8c471f5f5be9c..bce9cb67537db 100644
--- a/src/plugins/advanced_settings/public/management_app/components/form/__snapshots__/form.test.tsx.snap
+++ b/src/plugins/advanced_settings/public/management_app/components/form/__snapshots__/form.test.tsx.snap
@@ -1,449 +1,849 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`Form should not render no settings message when instructed not to 1`] = `<Fragment />`;
+exports[`Form should not render no settings message when instructed not to 1`] = `
+<Fragment>
+  <div>
+    <EuiPanel
+      paddingSize="l"
+    >
+      <EuiForm>
+        <EuiText>
+          <EuiFlexGroup
+            alignItems="baseline"
+          >
+            <EuiFlexItem
+              grow={false}
+            >
+              <h2>
+                General
+              </h2>
+            </EuiFlexItem>
+          </EuiFlexGroup>
+        </EuiText>
+        <EuiSpacer
+          size="m"
+        />
+        <Field
+          clearChange={[Function]}
+          dockLinks={Object {}}
+          enableSaving={true}
+          handleChange={[Function]}
+          key="general:test:date"
+          setting={
+            Object {
+              "ariaName": "general test date",
+              "category": Array [
+                "general",
+              ],
+              "defVal": "defVal",
+              "description": "bar",
+              "displayName": "Test date",
+              "isCustom": false,
+              "isOverridden": false,
+              "name": "general:test:date",
+              "readOnly": false,
+              "requiresPageReload": false,
+              "type": "string",
+              "value": "value",
+            }
+          }
+          toasts={Object {}}
+        />
+        <Field
+          clearChange={[Function]}
+          dockLinks={Object {}}
+          enableSaving={true}
+          handleChange={[Function]}
+          key="setting:test"
+          setting={
+            Object {
+              "ariaName": "setting test",
+              "category": Array [
+                "general",
+              ],
+              "defVal": "defVal",
+              "description": "foo",
+              "displayName": "Test setting",
+              "isCustom": false,
+              "isOverridden": false,
+              "name": "setting:test",
+              "readOnly": false,
+              "requiresPageReload": false,
+              "type": "string",
+              "value": "value",
+            }
+          }
+          toasts={Object {}}
+        />
+      </EuiForm>
+    </EuiPanel>
+    <EuiSpacer
+      size="l"
+    />
+    <EuiPanel
+      paddingSize="l"
+    >
+      <EuiForm>
+        <EuiText>
+          <EuiFlexGroup
+            alignItems="baseline"
+          >
+            <EuiFlexItem
+              grow={false}
+            >
+              <h2>
+                Dashboard
+              </h2>
+            </EuiFlexItem>
+          </EuiFlexGroup>
+        </EuiText>
+        <EuiSpacer
+          size="m"
+        />
+        <Field
+          clearChange={[Function]}
+          dockLinks={Object {}}
+          enableSaving={true}
+          handleChange={[Function]}
+          key="dashboard:test:setting"
+          setting={
+            Object {
+              "ariaName": "dashboard test setting",
+              "category": Array [
+                "dashboard",
+              ],
+              "defVal": "defVal",
+              "description": "description",
+              "displayName": "Dashboard test setting",
+              "isCustom": false,
+              "isOverridden": false,
+              "name": "dashboard:test:setting",
+              "readOnly": false,
+              "requiresPageReload": true,
+              "type": "string",
+              "value": "value",
+            }
+          }
+          toasts={Object {}}
+        />
+      </EuiForm>
+    </EuiPanel>
+    <EuiSpacer
+      size="l"
+    />
+    <EuiPanel
+      paddingSize="l"
+    >
+      <EuiForm>
+        <EuiText>
+          <EuiFlexGroup
+            alignItems="baseline"
+          >
+            <EuiFlexItem
+              grow={false}
+            >
+              <h2>
+                X-pack
+              </h2>
+            </EuiFlexItem>
+            <EuiFlexItem
+              grow={false}
+            >
+              <em>
+                <FormattedMessage
+                  defaultMessage="Search terms are hiding {settingsCount} settings {clearSearch}"
+                  id="advancedSettings.form.searchResultText"
+                  values={
+                    Object {
+                      "clearSearch": <ForwardRef
+                        onClick={[Function]}
+                      >
+                        <em>
+                          <FormattedMessage
+                            defaultMessage="(clear search)"
+                            id="advancedSettings.form.clearSearchResultText"
+                            values={Object {}}
+                          />
+                        </em>
+                      </ForwardRef>,
+                      "settingsCount": 9,
+                    }
+                  }
+                />
+              </em>
+            </EuiFlexItem>
+          </EuiFlexGroup>
+        </EuiText>
+        <EuiSpacer
+          size="m"
+        />
+        <Field
+          clearChange={[Function]}
+          dockLinks={Object {}}
+          enableSaving={true}
+          handleChange={[Function]}
+          key="xpack:test:setting"
+          setting={
+            Object {
+              "ariaName": "xpack test setting",
+              "category": Array [
+                "x-pack",
+              ],
+              "defVal": "defVal",
+              "description": "bar",
+              "displayName": "X-Pack test setting",
+              "isCustom": false,
+              "isOverridden": false,
+              "name": "xpack:test:setting",
+              "readOnly": false,
+              "requiresPageReload": false,
+              "type": "string",
+              "value": "value",
+            }
+          }
+          toasts={Object {}}
+        />
+      </EuiForm>
+    </EuiPanel>
+    <EuiSpacer
+      size="l"
+    />
+  </div>
+</Fragment>
+`;
 
 exports[`Form should render no settings message when there are no settings 1`] = `
 <Fragment>
-  <EuiPanel
-    paddingSize="l"
-  >
-    <FormattedMessage
-      defaultMessage="No settings found {clearSearch}"
-      id="advancedSettings.form.noSearchResultText"
-      values={
-        Object {
-          "clearSearch": <ForwardRef
-            onClick={[Function]}
+  <div>
+    <EuiPanel
+      paddingSize="l"
+    >
+      <EuiForm>
+        <EuiText>
+          <EuiFlexGroup
+            alignItems="baseline"
           >
-            <FormattedMessage
-              defaultMessage="(clear search)"
-              id="advancedSettings.form.clearNoSearchResultText"
-              values={Object {}}
-            />
-          </ForwardRef>,
-        }
-      }
+            <EuiFlexItem
+              grow={false}
+            >
+              <h2>
+                General
+              </h2>
+            </EuiFlexItem>
+          </EuiFlexGroup>
+        </EuiText>
+        <EuiSpacer
+          size="m"
+        />
+        <Field
+          clearChange={[Function]}
+          dockLinks={Object {}}
+          enableSaving={true}
+          handleChange={[Function]}
+          key="general:test:date"
+          setting={
+            Object {
+              "ariaName": "general test date",
+              "category": Array [
+                "general",
+              ],
+              "defVal": "defVal",
+              "description": "bar",
+              "displayName": "Test date",
+              "isCustom": false,
+              "isOverridden": false,
+              "name": "general:test:date",
+              "readOnly": false,
+              "requiresPageReload": false,
+              "type": "string",
+              "value": "value",
+            }
+          }
+          toasts={Object {}}
+        />
+        <Field
+          clearChange={[Function]}
+          dockLinks={Object {}}
+          enableSaving={true}
+          handleChange={[Function]}
+          key="setting:test"
+          setting={
+            Object {
+              "ariaName": "setting test",
+              "category": Array [
+                "general",
+              ],
+              "defVal": "defVal",
+              "description": "foo",
+              "displayName": "Test setting",
+              "isCustom": false,
+              "isOverridden": false,
+              "name": "setting:test",
+              "readOnly": false,
+              "requiresPageReload": false,
+              "type": "string",
+              "value": "value",
+            }
+          }
+          toasts={Object {}}
+        />
+      </EuiForm>
+    </EuiPanel>
+    <EuiSpacer
+      size="l"
     />
-  </EuiPanel>
+    <EuiPanel
+      paddingSize="l"
+    >
+      <EuiForm>
+        <EuiText>
+          <EuiFlexGroup
+            alignItems="baseline"
+          >
+            <EuiFlexItem
+              grow={false}
+            >
+              <h2>
+                Dashboard
+              </h2>
+            </EuiFlexItem>
+          </EuiFlexGroup>
+        </EuiText>
+        <EuiSpacer
+          size="m"
+        />
+        <Field
+          clearChange={[Function]}
+          dockLinks={Object {}}
+          enableSaving={true}
+          handleChange={[Function]}
+          key="dashboard:test:setting"
+          setting={
+            Object {
+              "ariaName": "dashboard test setting",
+              "category": Array [
+                "dashboard",
+              ],
+              "defVal": "defVal",
+              "description": "description",
+              "displayName": "Dashboard test setting",
+              "isCustom": false,
+              "isOverridden": false,
+              "name": "dashboard:test:setting",
+              "readOnly": false,
+              "requiresPageReload": true,
+              "type": "string",
+              "value": "value",
+            }
+          }
+          toasts={Object {}}
+        />
+      </EuiForm>
+    </EuiPanel>
+    <EuiSpacer
+      size="l"
+    />
+    <EuiPanel
+      paddingSize="l"
+    >
+      <EuiForm>
+        <EuiText>
+          <EuiFlexGroup
+            alignItems="baseline"
+          >
+            <EuiFlexItem
+              grow={false}
+            >
+              <h2>
+                X-pack
+              </h2>
+            </EuiFlexItem>
+            <EuiFlexItem
+              grow={false}
+            >
+              <em>
+                <FormattedMessage
+                  defaultMessage="Search terms are hiding {settingsCount} settings {clearSearch}"
+                  id="advancedSettings.form.searchResultText"
+                  values={
+                    Object {
+                      "clearSearch": <ForwardRef
+                        onClick={[Function]}
+                      >
+                        <em>
+                          <FormattedMessage
+                            defaultMessage="(clear search)"
+                            id="advancedSettings.form.clearSearchResultText"
+                            values={Object {}}
+                          />
+                        </em>
+                      </ForwardRef>,
+                      "settingsCount": 9,
+                    }
+                  }
+                />
+              </em>
+            </EuiFlexItem>
+          </EuiFlexGroup>
+        </EuiText>
+        <EuiSpacer
+          size="m"
+        />
+        <Field
+          clearChange={[Function]}
+          dockLinks={Object {}}
+          enableSaving={true}
+          handleChange={[Function]}
+          key="xpack:test:setting"
+          setting={
+            Object {
+              "ariaName": "xpack test setting",
+              "category": Array [
+                "x-pack",
+              ],
+              "defVal": "defVal",
+              "description": "bar",
+              "displayName": "X-Pack test setting",
+              "isCustom": false,
+              "isOverridden": false,
+              "name": "xpack:test:setting",
+              "readOnly": false,
+              "requiresPageReload": false,
+              "type": "string",
+              "value": "value",
+            }
+          }
+          toasts={Object {}}
+        />
+      </EuiForm>
+    </EuiPanel>
+    <EuiSpacer
+      size="l"
+    />
+  </div>
 </Fragment>
 `;
 
 exports[`Form should render normally 1`] = `
 <Fragment>
-  <EuiPanel
-    paddingSize="l"
-  >
-    <EuiForm>
-      <EuiText>
-        <EuiFlexGroup
-          alignItems="baseline"
-        >
-          <EuiFlexItem
-            grow={false}
+  <div>
+    <EuiPanel
+      paddingSize="l"
+    >
+      <EuiForm>
+        <EuiText>
+          <EuiFlexGroup
+            alignItems="baseline"
           >
-            <h2>
-              General
-            </h2>
-          </EuiFlexItem>
-        </EuiFlexGroup>
-      </EuiText>
-      <EuiSpacer
-        size="m"
-      />
-      <Field
-        clear={[Function]}
-        dockLinks={Object {}}
-        enableSaving={true}
-        key="general:test:date"
-        save={[Function]}
-        setting={
-          Object {
-            "ariaName": "general test date",
-            "category": Array [
-              "general",
-            ],
-            "defVal": "defVal",
-            "description": "description",
-            "displayName": "Test date",
-            "isCustom": false,
-            "isOverridden": false,
-            "name": "general:test:date",
-            "readOnly": false,
-            "requiresPageReload": false,
-            "type": "string",
-            "value": "value",
+            <EuiFlexItem
+              grow={false}
+            >
+              <h2>
+                General
+              </h2>
+            </EuiFlexItem>
+          </EuiFlexGroup>
+        </EuiText>
+        <EuiSpacer
+          size="m"
+        />
+        <Field
+          clearChange={[Function]}
+          dockLinks={Object {}}
+          enableSaving={true}
+          handleChange={[Function]}
+          key="general:test:date"
+          setting={
+            Object {
+              "ariaName": "general test date",
+              "category": Array [
+                "general",
+              ],
+              "defVal": "defVal",
+              "description": "bar",
+              "displayName": "Test date",
+              "isCustom": false,
+              "isOverridden": false,
+              "name": "general:test:date",
+              "readOnly": false,
+              "requiresPageReload": false,
+              "type": "string",
+              "value": "value",
+            }
           }
-        }
-        toasts={Object {}}
-      />
-      <Field
-        clear={[Function]}
-        dockLinks={Object {}}
-        enableSaving={true}
-        key="setting:test"
-        save={[Function]}
-        setting={
-          Object {
-            "ariaName": "setting test",
-            "category": Array [
-              "general",
-            ],
-            "defVal": "defVal",
-            "description": "description",
-            "displayName": "Test setting",
-            "isCustom": false,
-            "isOverridden": false,
-            "name": "setting:test",
-            "readOnly": false,
-            "requiresPageReload": false,
-            "type": "string",
-            "value": "value",
+          toasts={Object {}}
+        />
+        <Field
+          clearChange={[Function]}
+          dockLinks={Object {}}
+          enableSaving={true}
+          handleChange={[Function]}
+          key="setting:test"
+          setting={
+            Object {
+              "ariaName": "setting test",
+              "category": Array [
+                "general",
+              ],
+              "defVal": "defVal",
+              "description": "foo",
+              "displayName": "Test setting",
+              "isCustom": false,
+              "isOverridden": false,
+              "name": "setting:test",
+              "readOnly": false,
+              "requiresPageReload": false,
+              "type": "string",
+              "value": "value",
+            }
           }
-        }
-        toasts={Object {}}
-      />
-    </EuiForm>
-  </EuiPanel>
-  <EuiSpacer
-    size="l"
-  />
-  <EuiPanel
-    paddingSize="l"
-  >
-    <EuiForm>
-      <EuiText>
-        <EuiFlexGroup
-          alignItems="baseline"
-        >
-          <EuiFlexItem
-            grow={false}
+          toasts={Object {}}
+        />
+      </EuiForm>
+    </EuiPanel>
+    <EuiSpacer
+      size="l"
+    />
+    <EuiPanel
+      paddingSize="l"
+    >
+      <EuiForm>
+        <EuiText>
+          <EuiFlexGroup
+            alignItems="baseline"
           >
-            <h2>
-              Dashboard
-            </h2>
-          </EuiFlexItem>
-        </EuiFlexGroup>
-      </EuiText>
-      <EuiSpacer
-        size="m"
-      />
-      <Field
-        clear={[Function]}
-        dockLinks={Object {}}
-        enableSaving={true}
-        key="dashboard:test:setting"
-        save={[Function]}
-        setting={
-          Object {
-            "ariaName": "dashboard test setting",
-            "category": Array [
-              "dashboard",
-            ],
-            "defVal": "defVal",
-            "description": "description",
-            "displayName": "Dashboard test setting",
-            "isCustom": false,
-            "isOverridden": false,
-            "name": "dashboard:test:setting",
-            "readOnly": false,
-            "requiresPageReload": false,
-            "type": "string",
-            "value": "value",
+            <EuiFlexItem
+              grow={false}
+            >
+              <h2>
+                Dashboard
+              </h2>
+            </EuiFlexItem>
+          </EuiFlexGroup>
+        </EuiText>
+        <EuiSpacer
+          size="m"
+        />
+        <Field
+          clearChange={[Function]}
+          dockLinks={Object {}}
+          enableSaving={true}
+          handleChange={[Function]}
+          key="dashboard:test:setting"
+          setting={
+            Object {
+              "ariaName": "dashboard test setting",
+              "category": Array [
+                "dashboard",
+              ],
+              "defVal": "defVal",
+              "description": "description",
+              "displayName": "Dashboard test setting",
+              "isCustom": false,
+              "isOverridden": false,
+              "name": "dashboard:test:setting",
+              "readOnly": false,
+              "requiresPageReload": true,
+              "type": "string",
+              "value": "value",
+            }
           }
-        }
-        toasts={Object {}}
-      />
-    </EuiForm>
-  </EuiPanel>
-  <EuiSpacer
-    size="l"
-  />
-  <EuiPanel
-    paddingSize="l"
-  >
-    <EuiForm>
-      <EuiText>
-        <EuiFlexGroup
-          alignItems="baseline"
-        >
-          <EuiFlexItem
-            grow={false}
-          >
-            <h2>
-              X-pack
-            </h2>
-          </EuiFlexItem>
-          <EuiFlexItem
-            grow={false}
+          toasts={Object {}}
+        />
+      </EuiForm>
+    </EuiPanel>
+    <EuiSpacer
+      size="l"
+    />
+    <EuiPanel
+      paddingSize="l"
+    >
+      <EuiForm>
+        <EuiText>
+          <EuiFlexGroup
+            alignItems="baseline"
           >
-            <em>
-              <FormattedMessage
-                defaultMessage="Search terms are hiding {settingsCount} settings {clearSearch}"
-                id="advancedSettings.form.searchResultText"
-                values={
-                  Object {
-                    "clearSearch": <ForwardRef
-                      onClick={[Function]}
-                    >
-                      <em>
-                        <FormattedMessage
-                          defaultMessage="(clear search)"
-                          id="advancedSettings.form.clearSearchResultText"
-                          values={Object {}}
-                        />
-                      </em>
-                    </ForwardRef>,
-                    "settingsCount": 9,
+            <EuiFlexItem
+              grow={false}
+            >
+              <h2>
+                X-pack
+              </h2>
+            </EuiFlexItem>
+            <EuiFlexItem
+              grow={false}
+            >
+              <em>
+                <FormattedMessage
+                  defaultMessage="Search terms are hiding {settingsCount} settings {clearSearch}"
+                  id="advancedSettings.form.searchResultText"
+                  values={
+                    Object {
+                      "clearSearch": <ForwardRef
+                        onClick={[Function]}
+                      >
+                        <em>
+                          <FormattedMessage
+                            defaultMessage="(clear search)"
+                            id="advancedSettings.form.clearSearchResultText"
+                            values={Object {}}
+                          />
+                        </em>
+                      </ForwardRef>,
+                      "settingsCount": 9,
+                    }
                   }
-                }
-              />
-            </em>
-          </EuiFlexItem>
-        </EuiFlexGroup>
-      </EuiText>
-      <EuiSpacer
-        size="m"
-      />
-      <Field
-        clear={[Function]}
-        dockLinks={Object {}}
-        enableSaving={true}
-        key="xpack:test:setting"
-        save={[Function]}
-        setting={
-          Object {
-            "ariaName": "xpack test setting",
-            "category": Array [
-              "x-pack",
-            ],
-            "defVal": "defVal",
-            "description": "description",
-            "displayName": "X-Pack test setting",
-            "isCustom": false,
-            "isOverridden": false,
-            "name": "xpack:test:setting",
-            "readOnly": false,
-            "requiresPageReload": false,
-            "type": "string",
-            "value": "value",
+                />
+              </em>
+            </EuiFlexItem>
+          </EuiFlexGroup>
+        </EuiText>
+        <EuiSpacer
+          size="m"
+        />
+        <Field
+          clearChange={[Function]}
+          dockLinks={Object {}}
+          enableSaving={true}
+          handleChange={[Function]}
+          key="xpack:test:setting"
+          setting={
+            Object {
+              "ariaName": "xpack test setting",
+              "category": Array [
+                "x-pack",
+              ],
+              "defVal": "defVal",
+              "description": "bar",
+              "displayName": "X-Pack test setting",
+              "isCustom": false,
+              "isOverridden": false,
+              "name": "xpack:test:setting",
+              "readOnly": false,
+              "requiresPageReload": false,
+              "type": "string",
+              "value": "value",
+            }
           }
-        }
-        toasts={Object {}}
-      />
-    </EuiForm>
-  </EuiPanel>
-  <EuiSpacer
-    size="l"
-  />
+          toasts={Object {}}
+        />
+      </EuiForm>
+    </EuiPanel>
+    <EuiSpacer
+      size="l"
+    />
+  </div>
 </Fragment>
 `;
 
 exports[`Form should render read-only when saving is disabled 1`] = `
 <Fragment>
-  <EuiPanel
-    paddingSize="l"
-  >
-    <EuiForm>
-      <EuiText>
-        <EuiFlexGroup
-          alignItems="baseline"
-        >
-          <EuiFlexItem
-            grow={false}
+  <div>
+    <EuiPanel
+      paddingSize="l"
+    >
+      <EuiForm>
+        <EuiText>
+          <EuiFlexGroup
+            alignItems="baseline"
           >
-            <h2>
-              General
-            </h2>
-          </EuiFlexItem>
-        </EuiFlexGroup>
-      </EuiText>
-      <EuiSpacer
-        size="m"
-      />
-      <Field
-        clear={[Function]}
-        dockLinks={Object {}}
-        enableSaving={false}
-        key="general:test:date"
-        save={[Function]}
-        setting={
-          Object {
-            "ariaName": "general test date",
-            "category": Array [
-              "general",
-            ],
-            "defVal": "defVal",
-            "description": "description",
-            "displayName": "Test date",
-            "isCustom": false,
-            "isOverridden": false,
-            "name": "general:test:date",
-            "readOnly": false,
-            "requiresPageReload": false,
-            "type": "string",
-            "value": "value",
+            <EuiFlexItem
+              grow={false}
+            >
+              <h2>
+                General
+              </h2>
+            </EuiFlexItem>
+          </EuiFlexGroup>
+        </EuiText>
+        <EuiSpacer
+          size="m"
+        />
+        <Field
+          clearChange={[Function]}
+          dockLinks={Object {}}
+          enableSaving={false}
+          handleChange={[Function]}
+          key="general:test:date"
+          setting={
+            Object {
+              "ariaName": "general test date",
+              "category": Array [
+                "general",
+              ],
+              "defVal": "defVal",
+              "description": "bar",
+              "displayName": "Test date",
+              "isCustom": false,
+              "isOverridden": false,
+              "name": "general:test:date",
+              "readOnly": false,
+              "requiresPageReload": false,
+              "type": "string",
+              "value": "value",
+            }
           }
-        }
-        toasts={Object {}}
-      />
-      <Field
-        clear={[Function]}
-        dockLinks={Object {}}
-        enableSaving={false}
-        key="setting:test"
-        save={[Function]}
-        setting={
-          Object {
-            "ariaName": "setting test",
-            "category": Array [
-              "general",
-            ],
-            "defVal": "defVal",
-            "description": "description",
-            "displayName": "Test setting",
-            "isCustom": false,
-            "isOverridden": false,
-            "name": "setting:test",
-            "readOnly": false,
-            "requiresPageReload": false,
-            "type": "string",
-            "value": "value",
+          toasts={Object {}}
+        />
+        <Field
+          clearChange={[Function]}
+          dockLinks={Object {}}
+          enableSaving={false}
+          handleChange={[Function]}
+          key="setting:test"
+          setting={
+            Object {
+              "ariaName": "setting test",
+              "category": Array [
+                "general",
+              ],
+              "defVal": "defVal",
+              "description": "foo",
+              "displayName": "Test setting",
+              "isCustom": false,
+              "isOverridden": false,
+              "name": "setting:test",
+              "readOnly": false,
+              "requiresPageReload": false,
+              "type": "string",
+              "value": "value",
+            }
           }
-        }
-        toasts={Object {}}
-      />
-    </EuiForm>
-  </EuiPanel>
-  <EuiSpacer
-    size="l"
-  />
-  <EuiPanel
-    paddingSize="l"
-  >
-    <EuiForm>
-      <EuiText>
-        <EuiFlexGroup
-          alignItems="baseline"
-        >
-          <EuiFlexItem
-            grow={false}
+          toasts={Object {}}
+        />
+      </EuiForm>
+    </EuiPanel>
+    <EuiSpacer
+      size="l"
+    />
+    <EuiPanel
+      paddingSize="l"
+    >
+      <EuiForm>
+        <EuiText>
+          <EuiFlexGroup
+            alignItems="baseline"
           >
-            <h2>
-              Dashboard
-            </h2>
-          </EuiFlexItem>
-        </EuiFlexGroup>
-      </EuiText>
-      <EuiSpacer
-        size="m"
-      />
-      <Field
-        clear={[Function]}
-        dockLinks={Object {}}
-        enableSaving={false}
-        key="dashboard:test:setting"
-        save={[Function]}
-        setting={
-          Object {
-            "ariaName": "dashboard test setting",
-            "category": Array [
-              "dashboard",
-            ],
-            "defVal": "defVal",
-            "description": "description",
-            "displayName": "Dashboard test setting",
-            "isCustom": false,
-            "isOverridden": false,
-            "name": "dashboard:test:setting",
-            "readOnly": false,
-            "requiresPageReload": false,
-            "type": "string",
-            "value": "value",
+            <EuiFlexItem
+              grow={false}
+            >
+              <h2>
+                Dashboard
+              </h2>
+            </EuiFlexItem>
+          </EuiFlexGroup>
+        </EuiText>
+        <EuiSpacer
+          size="m"
+        />
+        <Field
+          clearChange={[Function]}
+          dockLinks={Object {}}
+          enableSaving={false}
+          handleChange={[Function]}
+          key="dashboard:test:setting"
+          setting={
+            Object {
+              "ariaName": "dashboard test setting",
+              "category": Array [
+                "dashboard",
+              ],
+              "defVal": "defVal",
+              "description": "description",
+              "displayName": "Dashboard test setting",
+              "isCustom": false,
+              "isOverridden": false,
+              "name": "dashboard:test:setting",
+              "readOnly": false,
+              "requiresPageReload": true,
+              "type": "string",
+              "value": "value",
+            }
           }
-        }
-        toasts={Object {}}
-      />
-    </EuiForm>
-  </EuiPanel>
-  <EuiSpacer
-    size="l"
-  />
-  <EuiPanel
-    paddingSize="l"
-  >
-    <EuiForm>
-      <EuiText>
-        <EuiFlexGroup
-          alignItems="baseline"
-        >
-          <EuiFlexItem
-            grow={false}
-          >
-            <h2>
-              X-pack
-            </h2>
-          </EuiFlexItem>
-          <EuiFlexItem
-            grow={false}
+          toasts={Object {}}
+        />
+      </EuiForm>
+    </EuiPanel>
+    <EuiSpacer
+      size="l"
+    />
+    <EuiPanel
+      paddingSize="l"
+    >
+      <EuiForm>
+        <EuiText>
+          <EuiFlexGroup
+            alignItems="baseline"
           >
-            <em>
-              <FormattedMessage
-                defaultMessage="Search terms are hiding {settingsCount} settings {clearSearch}"
-                id="advancedSettings.form.searchResultText"
-                values={
-                  Object {
-                    "clearSearch": <ForwardRef
-                      onClick={[Function]}
-                    >
-                      <em>
-                        <FormattedMessage
-                          defaultMessage="(clear search)"
-                          id="advancedSettings.form.clearSearchResultText"
-                          values={Object {}}
-                        />
-                      </em>
-                    </ForwardRef>,
-                    "settingsCount": 9,
+            <EuiFlexItem
+              grow={false}
+            >
+              <h2>
+                X-pack
+              </h2>
+            </EuiFlexItem>
+            <EuiFlexItem
+              grow={false}
+            >
+              <em>
+                <FormattedMessage
+                  defaultMessage="Search terms are hiding {settingsCount} settings {clearSearch}"
+                  id="advancedSettings.form.searchResultText"
+                  values={
+                    Object {
+                      "clearSearch": <ForwardRef
+                        onClick={[Function]}
+                      >
+                        <em>
+                          <FormattedMessage
+                            defaultMessage="(clear search)"
+                            id="advancedSettings.form.clearSearchResultText"
+                            values={Object {}}
+                          />
+                        </em>
+                      </ForwardRef>,
+                      "settingsCount": 9,
+                    }
                   }
-                }
-              />
-            </em>
-          </EuiFlexItem>
-        </EuiFlexGroup>
-      </EuiText>
-      <EuiSpacer
-        size="m"
-      />
-      <Field
-        clear={[Function]}
-        dockLinks={Object {}}
-        enableSaving={false}
-        key="xpack:test:setting"
-        save={[Function]}
-        setting={
-          Object {
-            "ariaName": "xpack test setting",
-            "category": Array [
-              "x-pack",
-            ],
-            "defVal": "defVal",
-            "description": "description",
-            "displayName": "X-Pack test setting",
-            "isCustom": false,
-            "isOverridden": false,
-            "name": "xpack:test:setting",
-            "readOnly": false,
-            "requiresPageReload": false,
-            "type": "string",
-            "value": "value",
+                />
+              </em>
+            </EuiFlexItem>
+          </EuiFlexGroup>
+        </EuiText>
+        <EuiSpacer
+          size="m"
+        />
+        <Field
+          clearChange={[Function]}
+          dockLinks={Object {}}
+          enableSaving={false}
+          handleChange={[Function]}
+          key="xpack:test:setting"
+          setting={
+            Object {
+              "ariaName": "xpack test setting",
+              "category": Array [
+                "x-pack",
+              ],
+              "defVal": "defVal",
+              "description": "bar",
+              "displayName": "X-Pack test setting",
+              "isCustom": false,
+              "isOverridden": false,
+              "name": "xpack:test:setting",
+              "readOnly": false,
+              "requiresPageReload": false,
+              "type": "string",
+              "value": "value",
+            }
           }
-        }
-        toasts={Object {}}
-      />
-    </EuiForm>
-  </EuiPanel>
-  <EuiSpacer
-    size="l"
-  />
+          toasts={Object {}}
+        />
+      </EuiForm>
+    </EuiPanel>
+    <EuiSpacer
+      size="l"
+    />
+  </div>
 </Fragment>
 `;
diff --git a/src/plugins/advanced_settings/public/management_app/components/form/_form.scss b/src/plugins/advanced_settings/public/management_app/components/form/_form.scss
new file mode 100644
index 0000000000000..02ebb90221d90
--- /dev/null
+++ b/src/plugins/advanced_settings/public/management_app/components/form/_form.scss
@@ -0,0 +1,13 @@
+@import '@elastic/eui/src/components/header/variables';
+@import '@elastic/eui/src/components/nav_drawer/variables';
+
+.mgtAdvancedSettingsForm__bottomBar {
+  margin-left: $euiNavDrawerWidthCollapsed;
+  z-index: 9; // Puts it inuder the nav drawer when expanded
+  &--pushForNav {
+    margin-left: $euiNavDrawerWidthExpanded;
+  }
+  @include euiBreakpoint('xs', 's') {
+    margin-left: 0;
+  }
+}
diff --git a/src/plugins/advanced_settings/public/management_app/components/form/_index.scss b/src/plugins/advanced_settings/public/management_app/components/form/_index.scss
new file mode 100644
index 0000000000000..2ef4ef1d20ce9
--- /dev/null
+++ b/src/plugins/advanced_settings/public/management_app/components/form/_index.scss
@@ -0,0 +1 @@
+@import './form';
diff --git a/src/plugins/advanced_settings/public/management_app/components/form/form.test.tsx b/src/plugins/advanced_settings/public/management_app/components/form/form.test.tsx
index 468cfbfc70820..0e942665b23a9 100644
--- a/src/plugins/advanced_settings/public/management_app/components/form/form.test.tsx
+++ b/src/plugins/advanced_settings/public/management_app/components/form/form.test.tsx
@@ -18,9 +18,14 @@
  */
 
 import React from 'react';
-import { shallowWithI18nProvider } from 'test_utils/enzyme_helpers';
+import { shallowWithI18nProvider, mountWithI18nProvider } from 'test_utils/enzyme_helpers';
 import { UiSettingsType } from '../../../../../../core/public';
 
+// @ts-ignore
+import { findTestSubject } from '@elastic/eui/lib/test';
+
+import { notificationServiceMock } from '../../../../../../core/public/mocks';
+import { SettingsChanges } from '../../types';
 import { Form } from './form';
 
 jest.mock('../field', () => ({
@@ -29,6 +34,25 @@ jest.mock('../field', () => ({
   },
 }));
 
+beforeAll(() => {
+  const localStorage: Record<string, any> = {
+    'core.chrome.isLocked': true,
+  };
+
+  Object.defineProperty(window, 'localStorage', {
+    value: {
+      getItem: (key: string) => {
+        return localStorage[key] || null;
+      },
+    },
+    writable: true,
+  });
+});
+
+afterAll(() => {
+  delete (window as any).localStorage;
+});
+
 const defaults = {
   requiresPageReload: false,
   readOnly: false,
@@ -43,50 +67,52 @@ const defaults = {
 const settings = {
   dashboard: [
     {
+      ...defaults,
       name: 'dashboard:test:setting',
       ariaName: 'dashboard test setting',
       displayName: 'Dashboard test setting',
       category: ['dashboard'],
-      ...defaults,
+      requiresPageReload: true,
     },
   ],
   general: [
     {
+      ...defaults,
       name: 'general:test:date',
       ariaName: 'general test date',
       displayName: 'Test date',
       description: 'bar',
       category: ['general'],
-      ...defaults,
     },
     {
+      ...defaults,
       name: 'setting:test',
       ariaName: 'setting test',
       displayName: 'Test setting',
       description: 'foo',
       category: ['general'],
-      ...defaults,
     },
   ],
   'x-pack': [
     {
+      ...defaults,
       name: 'xpack:test:setting',
       ariaName: 'xpack test setting',
       displayName: 'X-Pack test setting',
       category: ['x-pack'],
       description: 'bar',
-      ...defaults,
     },
   ],
 };
+
 const categories = ['general', 'dashboard', 'hiddenCategory', 'x-pack'];
 const categoryCounts = {
   general: 2,
   dashboard: 1,
   'x-pack': 10,
 };
-const save = (key: string, value: any) => Promise.resolve(true);
-const clear = (key: string) => Promise.resolve(true);
+const save = jest.fn((changes: SettingsChanges) => Promise.resolve([true]));
+
 const clearQuery = () => {};
 
 describe('Form', () => {
@@ -94,10 +120,10 @@ describe('Form', () => {
     const component = shallowWithI18nProvider(
       <Form
         settings={settings}
+        visibleSettings={settings}
         categories={categories}
         categoryCounts={categoryCounts}
         save={save}
-        clear={clear}
         clearQuery={clearQuery}
         showNoResultsMessage={true}
         enableSaving={true}
@@ -113,10 +139,10 @@ describe('Form', () => {
     const component = shallowWithI18nProvider(
       <Form
         settings={settings}
+        visibleSettings={settings}
         categories={categories}
         categoryCounts={categoryCounts}
         save={save}
-        clear={clear}
         clearQuery={clearQuery}
         showNoResultsMessage={true}
         enableSaving={false}
@@ -132,10 +158,10 @@ describe('Form', () => {
     const component = shallowWithI18nProvider(
       <Form
         settings={{}}
+        visibleSettings={settings}
         categories={categories}
         categoryCounts={categoryCounts}
         save={save}
-        clear={clear}
         clearQuery={clearQuery}
         showNoResultsMessage={true}
         enableSaving={true}
@@ -151,10 +177,10 @@ describe('Form', () => {
     const component = shallowWithI18nProvider(
       <Form
         settings={{}}
+        visibleSettings={settings}
         categories={categories}
         categoryCounts={categoryCounts}
         save={save}
-        clear={clear}
         clearQuery={clearQuery}
         showNoResultsMessage={false}
         enableSaving={true}
@@ -165,4 +191,70 @@ describe('Form', () => {
 
     expect(component).toMatchSnapshot();
   });
+
+  it('should hide bottom bar when clicking on the cancel changes button', async () => {
+    const wrapper = mountWithI18nProvider(
+      <Form
+        settings={settings}
+        visibleSettings={settings}
+        categories={categories}
+        categoryCounts={categoryCounts}
+        save={save}
+        clearQuery={clearQuery}
+        showNoResultsMessage={true}
+        enableSaving={false}
+        toasts={{} as any}
+        dockLinks={{} as any}
+      />
+    );
+    (wrapper.instance() as Form).setState({
+      unsavedChanges: {
+        'dashboard:test:setting': {
+          value: 'changedValue',
+        },
+      },
+    });
+    const updated = wrapper.update();
+    expect(updated.exists('[data-test-subj="advancedSetting-bottomBar"]')).toEqual(true);
+    await findTestSubject(updated, `advancedSetting-cancelButton`).simulate('click');
+    updated.update();
+    expect(updated.exists('[data-test-subj="advancedSetting-bottomBar"]')).toEqual(false);
+  });
+
+  it('should show a reload toast when saving setting requiring a page reload', async () => {
+    const toasts = notificationServiceMock.createStartContract().toasts;
+    const wrapper = mountWithI18nProvider(
+      <Form
+        settings={settings}
+        visibleSettings={settings}
+        categories={categories}
+        categoryCounts={categoryCounts}
+        save={save}
+        clearQuery={clearQuery}
+        showNoResultsMessage={true}
+        enableSaving={false}
+        toasts={toasts}
+        dockLinks={{} as any}
+      />
+    );
+    (wrapper.instance() as Form).setState({
+      unsavedChanges: {
+        'dashboard:test:setting': {
+          value: 'changedValue',
+        },
+      },
+    });
+    const updated = wrapper.update();
+
+    findTestSubject(updated, `advancedSetting-saveButton`).simulate('click');
+    expect(save).toHaveBeenCalled();
+    await save({ 'dashboard:test:setting': 'changedValue' });
+    expect(toasts.add).toHaveBeenCalledWith(
+      expect.objectContaining({
+        title: expect.stringContaining(
+          'One or more settings require you to reload the page to take effect.'
+        ),
+      })
+    );
+  });
 });
diff --git a/src/plugins/advanced_settings/public/management_app/components/form/form.tsx b/src/plugins/advanced_settings/public/management_app/components/form/form.tsx
index 91d587866836e..ef433dd990d33 100644
--- a/src/plugins/advanced_settings/public/management_app/components/form/form.tsx
+++ b/src/plugins/advanced_settings/public/management_app/components/form/form.tsx
@@ -18,7 +18,7 @@
  */
 
 import React, { PureComponent, Fragment } from 'react';
-
+import classNames from 'classnames';
 import {
   EuiFlexGroup,
   EuiFlexItem,
@@ -27,30 +27,188 @@ import {
   EuiPanel,
   EuiSpacer,
   EuiText,
+  EuiTextColor,
+  EuiBottomBar,
+  EuiButton,
+  EuiToolTip,
+  EuiButtonEmpty,
 } from '@elastic/eui';
 import { FormattedMessage } from '@kbn/i18n/react';
+import { isEmpty } from 'lodash';
+import { i18n } from '@kbn/i18n';
+import { toMountPoint } from '../../../../../kibana_react/public';
 import { DocLinksStart, ToastsStart } from '../../../../../../core/public';
 
 import { getCategoryName } from '../../lib';
-import { Field } from '../field';
-import { FieldSetting } from '../../types';
+import { Field, getEditableValue } from '../field';
+import { FieldSetting, SettingsChanges, FieldState } from '../../types';
 
 type Category = string;
+const NAV_IS_LOCKED_KEY = 'core.chrome.isLocked';
 
 interface FormProps {
   settings: Record<string, FieldSetting[]>;
+  visibleSettings: Record<string, FieldSetting[]>;
   categories: Category[];
   categoryCounts: Record<string, number>;
   clearQuery: () => void;
-  save: (key: string, value: any) => Promise<boolean>;
-  clear: (key: string) => Promise<boolean>;
+  save: (changes: SettingsChanges) => Promise<boolean[]>;
   showNoResultsMessage: boolean;
   enableSaving: boolean;
   dockLinks: DocLinksStart['links'];
   toasts: ToastsStart;
 }
 
+interface FormState {
+  unsavedChanges: {
+    [key: string]: FieldState;
+  };
+  loading: boolean;
+}
+
 export class Form extends PureComponent<FormProps> {
+  state: FormState = {
+    unsavedChanges: {},
+    loading: false,
+  };
+
+  setLoading(loading: boolean) {
+    this.setState({
+      loading,
+    });
+  }
+
+  getSettingByKey = (key: string): FieldSetting | undefined => {
+    return Object.values(this.props.settings)
+      .flat()
+      .find(el => el.name === key);
+  };
+
+  getCountOfUnsavedChanges = (): number => {
+    return Object.keys(this.state.unsavedChanges).length;
+  };
+
+  getCountOfHiddenUnsavedChanges = (): number => {
+    const shownSettings = Object.values(this.props.visibleSettings)
+      .flat()
+      .map(setting => setting.name);
+    return Object.keys(this.state.unsavedChanges).filter(key => !shownSettings.includes(key))
+      .length;
+  };
+
+  areChangesInvalid = (): boolean => {
+    const { unsavedChanges } = this.state;
+    return Object.values(unsavedChanges).some(({ isInvalid }) => isInvalid);
+  };
+
+  handleChange = (key: string, change: FieldState) => {
+    const setting = this.getSettingByKey(key);
+    if (!setting) {
+      return;
+    }
+    const { type, defVal, value } = setting;
+    const savedValue = getEditableValue(type, value, defVal);
+    if (change.value === savedValue) {
+      return this.clearChange(key);
+    }
+    this.setState({
+      unsavedChanges: {
+        ...this.state.unsavedChanges,
+        [key]: change,
+      },
+    });
+  };
+
+  clearChange = (key: string) => {
+    if (!this.state.unsavedChanges[key]) {
+      return;
+    }
+    const unsavedChanges = { ...this.state.unsavedChanges };
+    delete unsavedChanges[key];
+
+    this.setState({
+      unsavedChanges,
+    });
+  };
+
+  clearAllUnsaved = () => {
+    this.setState({ unsavedChanges: {} });
+  };
+
+  saveAll = async () => {
+    this.setLoading(true);
+    const { unsavedChanges } = this.state;
+
+    if (isEmpty(unsavedChanges)) {
+      return;
+    }
+    const configToSave: SettingsChanges = {};
+    let requiresReload = false;
+
+    Object.entries(unsavedChanges).forEach(([name, { value }]) => {
+      const setting = this.getSettingByKey(name);
+      if (!setting) {
+        return;
+      }
+      const { defVal, type, requiresPageReload } = setting;
+      let valueToSave = value;
+      let equalsToDefault = false;
+      switch (type) {
+        case 'array':
+          valueToSave = valueToSave.split(',').map((val: string) => val.trim());
+          equalsToDefault = valueToSave.join(',') === (defVal as string[]).join(',');
+          break;
+        case 'json':
+          const isArray = Array.isArray(JSON.parse((defVal as string) || '{}'));
+          valueToSave = valueToSave.trim();
+          valueToSave = valueToSave || (isArray ? '[]' : '{}');
+        default:
+          equalsToDefault = valueToSave === defVal;
+      }
+      if (requiresPageReload) {
+        requiresReload = true;
+      }
+      configToSave[name] = equalsToDefault ? null : valueToSave;
+    });
+
+    try {
+      await this.props.save(configToSave);
+      this.clearAllUnsaved();
+      if (requiresReload) {
+        this.renderPageReloadToast();
+      }
+    } catch (e) {
+      this.props.toasts.addDanger(
+        i18n.translate('advancedSettings.form.saveErrorMessage', {
+          defaultMessage: 'Unable to save',
+        })
+      );
+    }
+    this.setLoading(false);
+  };
+
+  renderPageReloadToast = () => {
+    this.props.toasts.add({
+      title: i18n.translate('advancedSettings.form.requiresPageReloadToastDescription', {
+        defaultMessage: 'One or more settings require you to reload the page to take effect.',
+      }),
+      text: toMountPoint(
+        <>
+          <EuiFlexGroup justifyContent="flexEnd" gutterSize="s">
+            <EuiFlexItem grow={false}>
+              <EuiButton size="s" onClick={() => window.location.reload()}>
+                {i18n.translate('advancedSettings.form.requiresPageReloadToastButtonLabel', {
+                  defaultMessage: 'Reload page',
+                })}
+              </EuiButton>
+            </EuiFlexItem>
+          </EuiFlexGroup>
+        </>
+      ),
+      color: 'success',
+    });
+  };
+
   renderClearQueryLink(totalSettings: number, currentSettings: number) {
     const { clearQuery } = this.props;
 
@@ -102,8 +260,9 @@ export class Form extends PureComponent<FormProps> {
                 <Field
                   key={setting.name}
                   setting={setting}
-                  save={this.props.save}
-                  clear={this.props.clear}
+                  handleChange={this.handleChange}
+                  unsavedChanges={this.state.unsavedChanges[setting.name]}
+                  clearChange={this.clearChange}
                   enableSaving={this.props.enableSaving}
                   dockLinks={this.props.dockLinks}
                   toasts={this.props.toasts}
@@ -141,23 +300,116 @@ export class Form extends PureComponent<FormProps> {
     return null;
   }
 
+  renderCountOfUnsaved = () => {
+    const unsavedCount = this.getCountOfUnsavedChanges();
+    const hiddenUnsavedCount = this.getCountOfHiddenUnsavedChanges();
+    return (
+      <EuiTextColor className="mgtAdvancedSettingsForm__unsavedCountMessage" color="ghost">
+        <FormattedMessage
+          id="advancedSettings.form.countOfSettingsChanged"
+          defaultMessage="{unsavedCount} unsaved {unsavedCount, plural,
+              one {setting}
+              other {settings}
+            }{hiddenCount, plural,
+              =0 {}
+              other {, # hidden}
+            }"
+          values={{
+            unsavedCount,
+            hiddenCount: hiddenUnsavedCount,
+          }}
+        />
+      </EuiTextColor>
+    );
+  };
+
+  renderBottomBar = () => {
+    const areChangesInvalid = this.areChangesInvalid();
+    const bottomBarClasses = classNames('mgtAdvancedSettingsForm__bottomBar', {
+      'mgtAdvancedSettingsForm__bottomBar--pushForNav':
+        localStorage.getItem(NAV_IS_LOCKED_KEY) === 'true',
+    });
+    return (
+      <EuiBottomBar className={bottomBarClasses} data-test-subj="advancedSetting-bottomBar">
+        <EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
+          <EuiFlexItem grow={false} className="mgtAdvancedSettingsForm__unsavedCount">
+            <p id="aria-describedby.countOfUnsavedSettings">{this.renderCountOfUnsaved()}</p>
+          </EuiFlexItem>
+          <EuiFlexItem grow={false}>
+            <EuiFlexGroup gutterSize="s">
+              <EuiFlexItem grow={false}>
+                <EuiButtonEmpty
+                  color="ghost"
+                  size="s"
+                  iconType="cross"
+                  onClick={this.clearAllUnsaved}
+                  aria-describedby="aria-describedby.countOfUnsavedSettings"
+                  data-test-subj="advancedSetting-cancelButton"
+                >
+                  {i18n.translate('advancedSettings.form.cancelButtonLabel', {
+                    defaultMessage: 'Cancel changes',
+                  })}
+                </EuiButtonEmpty>
+              </EuiFlexItem>
+              <EuiFlexItem grow={false}>
+                <EuiToolTip
+                  content={
+                    areChangesInvalid &&
+                    i18n.translate('advancedSettings.form.saveButtonTooltipWithInvalidChanges', {
+                      defaultMessage: 'Fix invalid settings before saving.',
+                    })
+                  }
+                >
+                  <EuiButton
+                    className="mgtAdvancedSettingsForm__button"
+                    disabled={areChangesInvalid}
+                    color="secondary"
+                    fill
+                    size="s"
+                    iconType="check"
+                    onClick={this.saveAll}
+                    aria-describedby="aria-describedby.countOfUnsavedSettings"
+                    isLoading={this.state.loading}
+                    data-test-subj="advancedSetting-saveButton"
+                  >
+                    {i18n.translate('advancedSettings.form.saveButtonLabel', {
+                      defaultMessage: 'Save changes',
+                    })}
+                  </EuiButton>
+                </EuiToolTip>
+              </EuiFlexItem>
+            </EuiFlexGroup>
+          </EuiFlexItem>
+        </EuiFlexGroup>
+      </EuiBottomBar>
+    );
+  };
+
   render() {
-    const { settings, categories, categoryCounts, clearQuery } = this.props;
+    const { unsavedChanges } = this.state;
+    const { visibleSettings, categories, categoryCounts, clearQuery } = this.props;
     const currentCategories: Category[] = [];
 
     categories.forEach(category => {
-      if (settings[category] && settings[category].length) {
+      if (visibleSettings[category] && visibleSettings[category].length) {
         currentCategories.push(category);
       }
     });
 
     return (
       <Fragment>
-        {currentCategories.length
-          ? currentCategories.map(category => {
-              return this.renderCategory(category, settings[category], categoryCounts[category]);
-            })
-          : this.maybeRenderNoSettings(clearQuery)}
+        <div>
+          {currentCategories.length
+            ? currentCategories.map(category => {
+                return this.renderCategory(
+                  category,
+                  visibleSettings[category],
+                  categoryCounts[category]
+                );
+              })
+            : this.maybeRenderNoSettings(clearQuery)}
+        </div>
+        {!isEmpty(unsavedChanges) && this.renderBottomBar()}
       </Fragment>
     );
   }
diff --git a/src/plugins/advanced_settings/public/management_app/types.ts b/src/plugins/advanced_settings/public/management_app/types.ts
index 05bb5e754563d..d44a05ce36f5d 100644
--- a/src/plugins/advanced_settings/public/management_app/types.ts
+++ b/src/plugins/advanced_settings/public/management_app/types.ts
@@ -47,6 +47,19 @@ export interface FieldSetting {
 }
 
 // until eui searchbar and query are typed
+
+export interface SettingsChanges {
+  [key: string]: any;
+}
+
+export interface FieldState {
+  value?: any;
+  changeImage?: boolean;
+  loading?: boolean;
+  isInvalid?: boolean;
+  error?: string | null;
+}
+
 export interface IQuery {
   ast: any; // incomplete
   text: string;
diff --git a/src/plugins/telemetry/public/components/telemetry_management_section.tsx b/src/plugins/telemetry/public/components/telemetry_management_section.tsx
index 20c8873b13272..bf14c33a48048 100644
--- a/src/plugins/telemetry/public/components/telemetry_management_section.tsx
+++ b/src/plugins/telemetry/public/components/telemetry_management_section.tsx
@@ -33,8 +33,8 @@ import { FormattedMessage } from '@kbn/i18n/react';
 import { i18n } from '@kbn/i18n';
 import { PRIVACY_STATEMENT_URL } from '../../common/constants';
 import { OptInExampleFlyout } from './opt_in_example_flyout';
-// @ts-ignore
 import { Field } from '../../../advanced_settings/public';
+import { ToastsStart } from '../../../../core/public/';
 import { TelemetryService } from '../services/telemetry_service';
 const SEARCH_TERMS = ['telemetry', 'usage', 'data', 'usage data'];
 
@@ -44,12 +44,14 @@ interface Props {
   showAppliesSettingMessage: boolean;
   enableSaving: boolean;
   query?: any;
+  toasts: ToastsStart;
 }
 
 interface State {
   processing: boolean;
   showExample: boolean;
   queryMatches: boolean | null;
+  enabled: boolean;
 }
 
 export class TelemetryManagementSection extends Component<Props, State> {
@@ -57,6 +59,7 @@ export class TelemetryManagementSection extends Component<Props, State> {
     processing: false,
     showExample: false,
     queryMatches: null,
+    enabled: this.props.telemetryService.getIsOptedIn() || false,
   };
 
   UNSAFE_componentWillReceiveProps(nextProps: Props) {
@@ -79,7 +82,7 @@ export class TelemetryManagementSection extends Component<Props, State> {
 
   render() {
     const { telemetryService } = this.props;
-    const { showExample, queryMatches } = this.state;
+    const { showExample, queryMatches, enabled, processing } = this.state;
 
     if (!telemetryService.getCanChangeOptInStatus()) {
       return null;
@@ -119,7 +122,7 @@ export class TelemetryManagementSection extends Component<Props, State> {
                   displayName: i18n.translate('telemetry.provideUsageStatisticsTitle', {
                     defaultMessage: 'Provide usage statistics',
                   }),
-                  value: telemetryService.getIsOptedIn(),
+                  value: enabled,
                   description: this.renderDescription(),
                   defVal: true,
                   ariaName: i18n.translate('telemetry.provideUsageStatisticsAriaName', {
@@ -127,10 +130,10 @@ export class TelemetryManagementSection extends Component<Props, State> {
                   }),
                 } as any
               }
+              loading={processing}
               dockLinks={null as any}
               toasts={null as any}
-              save={this.toggleOptIn}
-              clear={this.toggleOptIn}
+              handleChange={this.toggleOptIn}
               enableSaving={this.props.enableSaving}
             />
           </EuiForm>
@@ -151,13 +154,13 @@ export class TelemetryManagementSection extends Component<Props, State> {
           <p>
             <FormattedMessage
               id="telemetry.callout.appliesSettingTitle"
-              defaultMessage="This setting applies to {allOfKibanaText}"
+              defaultMessage="Changes to this setting apply to {allOfKibanaText} and are saved automatically."
               values={{
                 allOfKibanaText: (
                   <strong>
                     <FormattedMessage
                       id="telemetry.callout.appliesSettingTitle.allOfKibanaText"
-                      defaultMessage="all of Kibana."
+                      defaultMessage="all of Kibana"
                     />
                   </strong>
                 ),
@@ -200,20 +203,35 @@ export class TelemetryManagementSection extends Component<Props, State> {
   );
 
   toggleOptIn = async (): Promise<boolean> => {
-    const { telemetryService } = this.props;
-    const newOptInValue = !telemetryService.getIsOptedIn();
+    const { telemetryService, toasts } = this.props;
+    const newOptInValue = !this.state.enabled;
 
     return new Promise((resolve, reject) => {
-      this.setState({ processing: true }, async () => {
-        try {
-          await telemetryService.setOptIn(newOptInValue);
-          this.setState({ processing: false });
-          resolve(true);
-        } catch (err) {
-          this.setState({ processing: false });
-          reject(err);
+      this.setState(
+        {
+          processing: true,
+          enabled: newOptInValue,
+        },
+        async () => {
+          try {
+            await telemetryService.setOptIn(newOptInValue);
+            this.setState({ processing: false });
+            toasts.addSuccess(
+              newOptInValue
+                ? i18n.translate('telemetry.optInSuccessOn', {
+                    defaultMessage: 'Usage data collection turned on.',
+                  })
+                : i18n.translate('telemetry.optInSuccessOff', {
+                    defaultMessage: 'Usage data collection turned off.',
+                  })
+            );
+            resolve(true);
+          } catch (err) {
+            this.setState({ processing: false });
+            reject(err);
+          }
         }
-      });
+      );
     });
   };
 
diff --git a/test/functional/page_objects/settings_page.ts b/test/functional/page_objects/settings_page.ts
index d7e5064cf7280..ff340c6b0abcd 100644
--- a/test/functional/page_objects/settings_page.ts
+++ b/test/functional/page_objects/settings_page.ts
@@ -94,7 +94,7 @@ export function SettingsPageProvider({ getService, getPageObjects }: FtrProvider
         `[data-test-subj="advancedSetting-editField-${propertyName}"] option[value="${propertyValue}"]`
       );
       await PageObjects.header.waitUntilLoadingHasFinished();
-      await testSubjects.click(`advancedSetting-saveEditField-${propertyName}`);
+      await testSubjects.click(`advancedSetting-saveButton`);
       await PageObjects.header.waitUntilLoadingHasFinished();
     }
 
@@ -102,14 +102,14 @@ export function SettingsPageProvider({ getService, getPageObjects }: FtrProvider
       const input = await testSubjects.find(`advancedSetting-editField-${propertyName}`);
       await input.clearValue();
       await input.type(propertyValue);
-      await testSubjects.click(`advancedSetting-saveEditField-${propertyName}`);
+      await testSubjects.click(`advancedSetting-saveButton`);
       await PageObjects.header.waitUntilLoadingHasFinished();
     }
 
     async toggleAdvancedSettingCheckbox(propertyName: string) {
       testSubjects.click(`advancedSetting-editField-${propertyName}`);
       await PageObjects.header.waitUntilLoadingHasFinished();
-      await testSubjects.click(`advancedSetting-saveEditField-${propertyName}`);
+      await testSubjects.click(`advancedSetting-saveButton`);
       await PageObjects.header.waitUntilLoadingHasFinished();
     }
 
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 4b06645cdfe04..78bb39dd22dea 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -1618,8 +1618,6 @@
     "advancedSettings.categoryNames.timelionLabel": "Timelion",
     "advancedSettings.categoryNames.visualizationsLabel": "ビジュアライゼーション",
     "advancedSettings.categorySearchLabel": "カテゴリー",
-    "advancedSettings.field.cancelEditingButtonAriaLabel": "{ariaName} の編集をキャンセル",
-    "advancedSettings.field.cancelEditingButtonLabel": "キャンセル",
     "advancedSettings.field.changeImageLinkAriaLabel": "{ariaName} を変更",
     "advancedSettings.field.changeImageLinkText": "画像を変更",
     "advancedSettings.field.codeEditorSyntaxErrorMessage": "無効な JSON 構文",
@@ -1632,17 +1630,10 @@
     "advancedSettings.field.imageTooLargeErrorMessage": "画像が大きすぎます。最大サイズは {maxSizeDescription} です",
     "advancedSettings.field.offLabel": "オフ",
     "advancedSettings.field.onLabel": "オン",
-    "advancedSettings.field.requiresPageReloadToastButtonLabel": "ページを再読み込み",
-    "advancedSettings.field.requiresPageReloadToastDescription": "「{settingName}」設定を有効にするには、ページを再読み込みしてください。",
-    "advancedSettings.field.resetFieldErrorMessage": "{name} をリセットできませんでした",
     "advancedSettings.field.resetToDefaultLinkAriaLabel": "{ariaName} をデフォルトにリセット",
     "advancedSettings.field.resetToDefaultLinkText": "デフォルトにリセット",
-    "advancedSettings.field.saveButtonAriaLabel": "{ariaName} を保存",
-    "advancedSettings.field.saveButtonLabel": "保存",
-    "advancedSettings.field.saveFieldErrorMessage": "{name} を保存できませんでした",
     "advancedSettings.form.clearNoSearchResultText": "(検索結果を消去)",
     "advancedSettings.form.clearSearchResultText": "(検索結果を消去)",
-    "advancedSettings.form.noSearchResultText": "設定が見つかりませんでした {clearSearch}",
     "advancedSettings.form.searchResultText": "検索用語により {settingsCount} 件の設定が非表示になっています {clearSearch}",
     "advancedSettings.pageTitle": "設定",
     "advancedSettings.searchBar.unableToParseQueryErrorMessage": "クエリをパースできません",
@@ -2474,8 +2465,6 @@
     "statusPage.statusApp.statusTitle": "プラグインステータス",
     "statusPage.statusTable.columns.idHeader": "ID",
     "statusPage.statusTable.columns.statusHeader": "ステータス",
-    "telemetry.callout.appliesSettingTitle": "この設定は {allOfKibanaText} に適用されます",
-    "telemetry.callout.appliesSettingTitle.allOfKibanaText": "Kibana のすべて",
     "telemetry.callout.clusterStatisticsDescription": "これは収集される基本的なクラスター統計の例です。インデックス、シャード、ノードの数が含まれます。監視がオンになっているかどうかなどのハイレベルの使用統計も含まれます。",
     "telemetry.callout.clusterStatisticsTitle": "クラスター統計",
     "telemetry.callout.errorLoadingClusterStatisticsDescription": "クラスター統計の取得中に予期せぬエラーが発生しました。Elasticsearch、Kibana、またはネットワークのエラーが原因の可能性があります。Kibana を確認し、ページを再読み込みして再試行してください。",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index ecf4dfbb33be6..fc9dacf0e50f7 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -1618,8 +1618,6 @@
     "advancedSettings.categoryNames.timelionLabel": "Timelion",
     "advancedSettings.categoryNames.visualizationsLabel": "可视化",
     "advancedSettings.categorySearchLabel": "类别",
-    "advancedSettings.field.cancelEditingButtonAriaLabel": "取消编辑 {ariaName}",
-    "advancedSettings.field.cancelEditingButtonLabel": "取消",
     "advancedSettings.field.changeImageLinkAriaLabel": "更改 {ariaName}",
     "advancedSettings.field.changeImageLinkText": "更改图片",
     "advancedSettings.field.codeEditorSyntaxErrorMessage": "JSON 语法无效",
@@ -1632,14 +1630,8 @@
     "advancedSettings.field.imageTooLargeErrorMessage": "图像过大,最大大小为 {maxSizeDescription}",
     "advancedSettings.field.offLabel": "关闭",
     "advancedSettings.field.onLabel": "开启",
-    "advancedSettings.field.requiresPageReloadToastButtonLabel": "重新加载页面",
-    "advancedSettings.field.requiresPageReloadToastDescription": "请重新加载页面,以使“{settingName}”设置生效。",
-    "advancedSettings.field.resetFieldErrorMessage": "无法重置 {name}",
     "advancedSettings.field.resetToDefaultLinkAriaLabel": "将 {ariaName} 重置为默认值",
     "advancedSettings.field.resetToDefaultLinkText": "重置为默认值",
-    "advancedSettings.field.saveButtonAriaLabel": "保存 {ariaName}",
-    "advancedSettings.field.saveButtonLabel": "保存",
-    "advancedSettings.field.saveFieldErrorMessage": "无法保存 {name}",
     "advancedSettings.form.clearNoSearchResultText": "(清除搜索)",
     "advancedSettings.form.clearSearchResultText": "(清除搜索)",
     "advancedSettings.form.noSearchResultText": "未找到设置{clearSearch}",
@@ -2474,8 +2466,6 @@
     "statusPage.statusApp.statusTitle": "插件状态",
     "statusPage.statusTable.columns.idHeader": "ID",
     "statusPage.statusTable.columns.statusHeader": "状态",
-    "telemetry.callout.appliesSettingTitle": "此设置适用于{allOfKibanaText}",
-    "telemetry.callout.appliesSettingTitle.allOfKibanaText": "所有 Kibana。",
     "telemetry.callout.clusterStatisticsDescription": "这是我们将收集的基本集群统计信息的示例。其包括索引、分片和节点的数目。还包括概括性的使用情况统计信息,例如监测是否打开。",
     "telemetry.callout.clusterStatisticsTitle": "集群统计信息",
     "telemetry.callout.errorLoadingClusterStatisticsDescription": "尝试提取集群统计信息时发生意外错误。发生此问题的原因可能是 Elasticsearch 出故障、Kibana 出故障或者有网络错误。检查 Kibana,然后重新加载页面并重试。",

From 256e4ab67c7fccae9aae38aac22f3788466f8b2f Mon Sep 17 00:00:00 2001
From: Brandon Kobel <brandon.kobel@elastic.co>
Date: Mon, 24 Feb 2020 09:40:37 -0800
Subject: [PATCH 145/174] Adding xpack.encryptedSavedObjects.encryptionKey to
 docker allow-list (#58291)

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../os_packages/docker_generator/resources/bin/kibana-docker     | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker
index 34ba25f92beb6..d4d2e86e1e96b 100755
--- a/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker
+++ b/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker
@@ -142,6 +142,7 @@ kibana_vars=(
     xpack.code.security.enableGitCertCheck
     xpack.code.security.gitHostWhitelist
     xpack.code.security.gitProtocolWhitelist
+    xpack.encryptedSavedObjects.encryptionKey
     xpack.graph.enabled
     xpack.graph.canEditDrillDownUrls
     xpack.graph.savePolicy

From b88b99140bc0d63036c0789d1ddc8dc9597e2b5e Mon Sep 17 00:00:00 2001
From: Walter Rafelsberger <walter@elastic.co>
Date: Mon, 24 Feb 2020 18:44:24 +0100
Subject: [PATCH 146/174] [ML] Fix transforms license check. (#58343)

Fixes an error where the transforms page would load blank with an expired license. Fixes the issue by adding a type guard. With an expired license, the page now renders again correctly the error message.
---
 .../lib/authorization/components/common.ts    | 24 +++++++++++++++----
 1 file changed, 20 insertions(+), 4 deletions(-)

diff --git a/x-pack/legacy/plugins/transform/public/app/lib/authorization/components/common.ts b/x-pack/legacy/plugins/transform/public/app/lib/authorization/components/common.ts
index 5aec2ac041db3..27556e0d673a8 100644
--- a/x-pack/legacy/plugins/transform/public/app/lib/authorization/components/common.ts
+++ b/x-pack/legacy/plugins/transform/public/app/lib/authorization/components/common.ts
@@ -21,19 +21,33 @@ export interface Privileges {
   missingPrivileges: MissingPrivileges;
 }
 
+function isPrivileges(arg: any): arg is Privileges {
+  return (
+    typeof arg === 'object' &&
+    arg !== null &&
+    arg.hasOwnProperty('hasAllPrivileges') &&
+    typeof arg.hasAllPrivileges === 'boolean' &&
+    arg.hasOwnProperty('missingPrivileges') &&
+    typeof arg.missingPrivileges === 'object' &&
+    arg.missingPrivileges !== null
+  );
+}
+
 export interface MissingPrivileges {
   [key: string]: string[] | undefined;
 }
 export const toArray = (value: string | string[]): string[] =>
   Array.isArray(value) ? value : [value];
 
-export const hasPrivilegeFactory = (privileges: Privileges) => (privilege: Privilege) => {
+export const hasPrivilegeFactory = (privileges: Privileges | undefined | null) => (
+  privilege: Privilege
+) => {
   const [section, requiredPrivilege] = privilege;
-  if (!privileges.missingPrivileges[section]) {
+  if (isPrivileges(privileges) && !privileges.missingPrivileges[section]) {
     // if the section does not exist in our missingPrivileges, everything is OK
     return true;
   }
-  if (privileges.missingPrivileges[section]!.length === 0) {
+  if (isPrivileges(privileges) && privileges.missingPrivileges[section]!.length === 0) {
     return true;
   }
   if (requiredPrivilege === '*') {
@@ -42,7 +56,9 @@ export const hasPrivilegeFactory = (privileges: Privileges) => (privilege: Privi
   }
   // If we require _some_ privilege, we make sure that the one
   // we require is *not* in the missingPrivilege array
-  return !privileges.missingPrivileges[section]!.includes(requiredPrivilege);
+  return (
+    isPrivileges(privileges) && !privileges.missingPrivileges[section]!.includes(requiredPrivilege)
+  );
 };
 
 // create the text for button's tooltips if the user

From 12f35d5788f5250803434b6d8f25d9df82ac0940 Mon Sep 17 00:00:00 2001
From: Jen Huang <its.jenetic@gmail.com>
Date: Mon, 24 Feb 2020 10:23:44 -0800
Subject: [PATCH 147/174] Add ingest manager header component (#58300)

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 x-pack/legacy/plugins/ingest_manager/index.ts |   2 +
 .../ingest_manager/components/header.tsx      |  62 ++++
 .../ingest_manager/components/index.ts        |   1 +
 .../ingest_manager/layouts/default.tsx        |  29 +-
 .../ingest_manager/layouts/index.tsx          |   1 +
 .../ingest_manager/layouts/with_header.tsx    |  29 ++
 .../sections/agent_config/list_page/index.tsx | 291 +++++++++---------
 ...illustration_kibana_getting_started@2x.png | Bin 0 -> 131132 bytes
 .../ingest_manager/sections/epm/index.tsx     |  69 ++++-
 .../ingest_manager/sections/fleet/index.tsx   |  46 ++-
 .../sections/overview/index.tsx               |  32 +-
 11 files changed, 397 insertions(+), 165 deletions(-)
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/header.tsx
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/with_header.tsx
 create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/assets/illustration_kibana_getting_started@2x.png

diff --git a/x-pack/legacy/plugins/ingest_manager/index.ts b/x-pack/legacy/plugins/ingest_manager/index.ts
index c20cc7225d780..7ed5599b234a3 100644
--- a/x-pack/legacy/plugins/ingest_manager/index.ts
+++ b/x-pack/legacy/plugins/ingest_manager/index.ts
@@ -3,6 +3,7 @@
  * or more contributor license agreements. Licensed under the Elastic License;
  * you may not use this file except in compliance with the Elastic License.
  */
+import { resolve } from 'path';
 import {
   savedObjectMappings,
   OUTPUT_SAVED_OBJECT_TYPE,
@@ -18,6 +19,7 @@ import {
 export function ingestManager(kibana: any) {
   return new kibana.Plugin({
     id: 'ingestManager',
+    publicDir: resolve(__dirname, '../../../plugins/ingest_manager/public'),
     uiExports: {
       savedObjectSchemas: {
         [AGENT_CONFIG_SAVED_OBJECT_TYPE]: {
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/header.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/header.tsx
new file mode 100644
index 0000000000000..0936b5dcfed10
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/header.tsx
@@ -0,0 +1,62 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import React from 'react';
+import styled from 'styled-components';
+import { EuiFlexGroup, EuiFlexItem, EuiTabs, EuiTab, EuiSpacer } from '@elastic/eui';
+import { Props as EuiTabProps } from '@elastic/eui/src/components/tabs/tab';
+
+const Container = styled.div`
+  border-bottom: ${props => props.theme.eui.euiBorderThin};
+  background-color: ${props => props.theme.eui.euiPageBackgroundColor};
+`;
+
+const Wrapper = styled.div`
+  max-width: 1200px;
+  margin-left: auto;
+  margin-right: auto;
+  padding-top: ${props => props.theme.eui.paddingSizes.xl};
+`;
+
+const Tabs = styled(EuiTabs)`
+  top: 1px;
+  &:before {
+    height: 0px;
+  }
+`;
+
+export interface HeaderProps {
+  leftColumn?: JSX.Element;
+  rightColumn?: JSX.Element;
+  tabs?: EuiTabProps[];
+}
+
+export const Header: React.FC<HeaderProps> = ({ leftColumn, rightColumn, tabs }) => (
+  <Container>
+    <Wrapper>
+      <EuiFlexGroup>
+        {leftColumn ? <EuiFlexItem>{leftColumn}</EuiFlexItem> : null}
+        {rightColumn ? <EuiFlexItem>{rightColumn}</EuiFlexItem> : null}
+      </EuiFlexGroup>
+      <EuiFlexGroup>
+        {tabs ? (
+          <EuiFlexItem>
+            <Tabs>
+              {tabs.map(props => (
+                <EuiTab {...props} key={props.id}>
+                  {props.name}
+                </EuiTab>
+              ))}
+            </Tabs>
+          </EuiFlexItem>
+        ) : (
+          <EuiFlexItem>
+            <EuiSpacer size="l" />
+          </EuiFlexItem>
+        )}
+      </EuiFlexGroup>
+    </Wrapper>
+  </Container>
+);
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/index.ts
index 5133d82588494..b6bb29462c569 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/index.ts
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/index.ts
@@ -4,3 +4,4 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 export { Loading } from './loading';
+export { Header, HeaderProps } from './header';
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx
index eaf49fed3d933..f99d1bfe50026 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx
@@ -4,17 +4,9 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 import React from 'react';
-import {
-  EuiPage,
-  EuiPageBody,
-  EuiTabs,
-  EuiTab,
-  EuiFlexGroup,
-  EuiFlexItem,
-  EuiIcon,
-} from '@elastic/eui';
+import styled from 'styled-components';
+import { EuiTabs, EuiTab, EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui';
 import { FormattedMessage } from '@kbn/i18n/react';
-import euiStyled from '../../../../../../legacy/common/eui_styled_components';
 import { Section } from '../sections';
 import { useLink, useConfig } from '../hooks';
 import { EPM_PATH, FLEET_PATH, AGENT_CONFIG_PATH } from '../constants';
@@ -24,7 +16,12 @@ interface Props {
   children?: React.ReactNode;
 }
 
-const Nav = euiStyled.nav`
+const Container = styled.div`
+  min-height: calc(100vh - ${props => props.theme.eui.euiHeaderChildSize});
+  background: ${props => props.theme.eui.euiColorEmptyShade};
+`;
+
+const Nav = styled.nav`
   background: ${props => props.theme.eui.euiColorEmptyShade};
   border-bottom: ${props => props.theme.eui.euiBorderThin};
   padding: ${props =>
@@ -32,13 +29,13 @@ const Nav = euiStyled.nav`
   .euiTabs {
     padding-left: 3px;
     margin-left: -3px;
-  };
+  }
 `;
 
 export const DefaultLayout: React.FunctionComponent<Props> = ({ section, children }) => {
   const { epm, fleet } = useConfig();
   return (
-    <div>
+    <Container>
       <Nav>
         <EuiFlexGroup gutterSize="l" alignItems="center">
           <EuiFlexItem grow={false}>
@@ -82,9 +79,7 @@ export const DefaultLayout: React.FunctionComponent<Props> = ({ section, childre
           </EuiFlexItem>
         </EuiFlexGroup>
       </Nav>
-      <EuiPage>
-        <EuiPageBody>{children}</EuiPageBody>
-      </EuiPage>
-    </div>
+      {children}
+    </Container>
   );
 };
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/index.tsx
index 858951bd0d38f..a9ef7f1656260 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/index.tsx
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/index.tsx
@@ -4,3 +4,4 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 export { DefaultLayout } from './default';
+export { WithHeaderLayout } from './with_header';
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/with_header.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/with_header.tsx
new file mode 100644
index 0000000000000..d59c99316c8b8
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/with_header.tsx
@@ -0,0 +1,29 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import React, { Fragment } from 'react';
+import styled from 'styled-components';
+import { EuiPage, EuiPageBody, EuiSpacer } from '@elastic/eui';
+import { Header, HeaderProps } from '../components';
+
+const Page = styled(EuiPage)`
+  background: ${props => props.theme.eui.euiColorEmptyShade};
+`;
+
+interface Props extends HeaderProps {
+  children?: React.ReactNode;
+}
+
+export const WithHeaderLayout: React.FC<Props> = ({ children, ...rest }) => (
+  <Fragment>
+    <Header {...rest} />
+    <Page restrictWidth={1200}>
+      <EuiPageBody>
+        <EuiSpacer size="m" />
+        {children}
+      </EuiPageBody>
+    </Page>
+  </Fragment>
+);
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/index.tsx
index ca9fb195166f6..ef5a38d486901 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/index.tsx
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/index.tsx
@@ -5,9 +5,6 @@
  */
 import React, { useState } from 'react';
 import {
-  EuiPageBody,
-  EuiPageContent,
-  EuiTitle,
   EuiSpacer,
   EuiText,
   EuiFlexGroup,
@@ -24,11 +21,43 @@ import { i18n } from '@kbn/i18n';
 import { FormattedMessage } from '@kbn/i18n/react';
 import { AgentConfig } from '../../../types';
 import { DEFAULT_AGENT_CONFIG_ID, AGENT_CONFIG_DETAILS_PATH } from '../../../constants';
+import { WithHeaderLayout } from '../../../layouts';
 // import { SearchBar } from '../../../components';
 import { useGetAgentConfigs, usePagination, useLink } from '../../../hooks';
 import { AgentConfigDeleteProvider } from '../components';
 import { CreateAgentConfigFlyout } from './components';
 
+const AgentConfigListPageLayout: React.FunctionComponent = ({ children }) => (
+  <WithHeaderLayout
+    leftColumn={
+      <EuiFlexGroup direction="column" gutterSize="m">
+        <EuiFlexItem>
+          <EuiText>
+            <h1>
+              <FormattedMessage
+                id="xpack.ingestManager.agentConfigList.pageTitle"
+                defaultMessage="Agent Configurations"
+              />
+            </h1>
+          </EuiText>
+        </EuiFlexItem>
+        <EuiFlexItem>
+          <EuiText color="subdued">
+            <p>
+              <FormattedMessage
+                id="xpack.ingestManager.agentConfigList.pageSubtitle"
+                defaultMessage="Use agent configurations to manage your agents and the data they collect."
+              />
+            </p>
+          </EuiText>
+        </EuiFlexItem>
+      </EuiFlexGroup>
+    }
+  >
+    {children}
+  </WithHeaderLayout>
+);
+
 export const AgentConfigListPage: React.FunctionComponent<{}> = () => {
   // Create agent config flyout state
   const [isCreateAgentConfigFlyoutOpen, setIsCreateAgentConfigFlyoutOpen] = useState<boolean>(
@@ -123,71 +152,46 @@ export const AgentConfigListPage: React.FunctionComponent<{}> = () => {
   );
 
   return (
-    <EuiPageBody>
-      <EuiPageContent>
-        {isCreateAgentConfigFlyoutOpen ? (
-          <CreateAgentConfigFlyout
-            onClose={() => {
-              setIsCreateAgentConfigFlyoutOpen(false);
-              sendRequest();
-            }}
-          />
-        ) : null}
-
-        <EuiTitle size="l">
-          <h1>
-            <FormattedMessage
-              id="xpack.ingestManager.agentConfigList.pageTitle"
-              defaultMessage="Agent configurations"
-            />
-          </h1>
-        </EuiTitle>
-        <EuiSpacer size="s" />
-        <EuiFlexGroup alignItems={'center'} justifyContent={'spaceBetween'}>
-          <EuiFlexItem grow={false}>
-            <EuiTitle size="s">
-              <EuiText color="subdued">
-                <FormattedMessage
-                  id="xpack.ingestManager.agentConfigList.pageDescription"
-                  defaultMessage="Lorem ipsum"
-                />
-              </EuiText>
-            </EuiTitle>
-          </EuiFlexItem>
-        </EuiFlexGroup>
-        <EuiSpacer size="m" />
-
-        <EuiFlexGroup alignItems={'center'} gutterSize="m">
-          {selectedAgentConfigs.length ? (
-            <EuiFlexItem>
-              <AgentConfigDeleteProvider>
-                {deleteAgentConfigsPrompt => (
-                  <EuiButton
-                    color="danger"
-                    onClick={() => {
-                      deleteAgentConfigsPrompt(
-                        selectedAgentConfigs.map(agentConfig => agentConfig.id),
-                        () => {
-                          sendRequest();
-                          setSelectedAgentConfigs([]);
-                        }
-                      );
+    <AgentConfigListPageLayout>
+      {isCreateAgentConfigFlyoutOpen ? (
+        <CreateAgentConfigFlyout
+          onClose={() => {
+            setIsCreateAgentConfigFlyoutOpen(false);
+            sendRequest();
+          }}
+        />
+      ) : null}
+      <EuiFlexGroup alignItems={'center'} gutterSize="m">
+        {selectedAgentConfigs.length ? (
+          <EuiFlexItem>
+            <AgentConfigDeleteProvider>
+              {deleteAgentConfigsPrompt => (
+                <EuiButton
+                  color="danger"
+                  onClick={() => {
+                    deleteAgentConfigsPrompt(
+                      selectedAgentConfigs.map(agentConfig => agentConfig.id),
+                      () => {
+                        sendRequest();
+                        setSelectedAgentConfigs([]);
+                      }
+                    );
+                  }}
+                >
+                  <FormattedMessage
+                    id="xpack.ingestManager.agentConfigList.deleteButton"
+                    defaultMessage="Delete {count, plural, one {# agent config} other {# agent configs}}"
+                    values={{
+                      count: selectedAgentConfigs.length,
                     }}
-                  >
-                    <FormattedMessage
-                      id="xpack.ingestManager.agentConfigList.deleteButton"
-                      defaultMessage="Delete {count, plural, one {# agent config} other {# agent configs}}"
-                      values={{
-                        count: selectedAgentConfigs.length,
-                      }}
-                    />
-                  </EuiButton>
-                )}
-              </AgentConfigDeleteProvider>
-            </EuiFlexItem>
-          ) : null}
-          <EuiFlexItem grow={4}>
-            {/* <SearchBar
+                  />
+                </EuiButton>
+              )}
+            </AgentConfigDeleteProvider>
+          </EuiFlexItem>
+        ) : null}
+        <EuiFlexItem grow={4}>
+          {/* <SearchBar
               value={search}
               onChange={newSearch => {
                 setPagination({
@@ -198,83 +202,82 @@ export const AgentConfigListPage: React.FunctionComponent<{}> = () => {
               }}
               fieldPrefix={AGENT_CONFIG_SAVED_OBJECT_TYPE}
             /> */}
-          </EuiFlexItem>
-          <EuiFlexItem grow={false}>
-            <EuiButton color="secondary" iconType="refresh" onClick={() => sendRequest()}>
-              <FormattedMessage
-                id="xpack.ingestManager.agentConfigList.reloadAgentConfigsButtonText"
-                defaultMessage="Reload"
-              />
-            </EuiButton>
-          </EuiFlexItem>
-          <EuiFlexItem grow={false}>
-            <EuiButton
-              fill
-              iconType="plusInCircle"
-              onClick={() => setIsCreateAgentConfigFlyoutOpen(true)}
-            >
-              <FormattedMessage
-                id="xpack.ingestManager.agentConfigList.addButton"
-                defaultMessage="Create new agent configuration"
-              />
-            </EuiButton>
-          </EuiFlexItem>
-        </EuiFlexGroup>
+        </EuiFlexItem>
+        <EuiFlexItem grow={false}>
+          <EuiButton color="secondary" iconType="refresh" onClick={() => sendRequest()}>
+            <FormattedMessage
+              id="xpack.ingestManager.agentConfigList.reloadAgentConfigsButtonText"
+              defaultMessage="Reload"
+            />
+          </EuiButton>
+        </EuiFlexItem>
+        <EuiFlexItem grow={false}>
+          <EuiButton
+            fill
+            iconType="plusInCircle"
+            onClick={() => setIsCreateAgentConfigFlyoutOpen(true)}
+          >
+            <FormattedMessage
+              id="xpack.ingestManager.agentConfigList.addButton"
+              defaultMessage="Create new agent configuration"
+            />
+          </EuiButton>
+        </EuiFlexItem>
+      </EuiFlexGroup>
 
-        <EuiSpacer size="m" />
-        <EuiBasicTable
-          loading={isLoading}
-          noItemsMessage={
-            isLoading ? (
-              <FormattedMessage
-                id="xpack.ingestManager.agentConfigList.loadingAgentConfigsMessage"
-                defaultMessage="Loading agent configurations…"
-              />
-            ) : !search.trim() && agentConfigData?.total === 0 ? (
-              emptyPrompt
-            ) : (
-              <FormattedMessage
-                id="xpack.ingestManager.agentConfigList.noFilteredAgentConfigsPrompt"
-                defaultMessage="No agent configurations found. {clearFiltersLink}"
-                values={{
-                  clearFiltersLink: (
-                    <EuiLink onClick={() => setSearch('')}>
-                      <FormattedMessage
-                        id="xpack.ingestManager.agentConfigList.clearFiltersLinkText"
-                        defaultMessage="Clear filters"
-                      />
-                    </EuiLink>
-                  ),
-                }}
-              />
-            )
-          }
-          items={agentConfigData ? agentConfigData.items : []}
-          itemId="id"
-          columns={columns}
-          isSelectable={true}
-          selection={{
-            selectable: (agentConfig: AgentConfig) => agentConfig.id !== DEFAULT_AGENT_CONFIG_ID,
-            onSelectionChange: (newSelectedAgentConfigs: AgentConfig[]) => {
-              setSelectedAgentConfigs(newSelectedAgentConfigs);
-            },
-          }}
-          pagination={{
-            pageIndex: pagination.currentPage - 1,
-            pageSize: pagination.pageSize,
-            totalItemCount: agentConfigData ? agentConfigData.total : 0,
-          }}
-          onChange={({ page }: { page: { index: number; size: number } }) => {
-            const newPagination = {
-              ...pagination,
-              currentPage: page.index + 1,
-              pageSize: page.size,
-            };
-            setPagination(newPagination);
-            sendRequest(); // todo: fix this to send pagination options
-          }}
-        />
-      </EuiPageContent>
-    </EuiPageBody>
+      <EuiSpacer size="m" />
+      <EuiBasicTable
+        loading={isLoading}
+        noItemsMessage={
+          isLoading ? (
+            <FormattedMessage
+              id="xpack.ingestManager.agentConfigList.loadingAgentConfigsMessage"
+              defaultMessage="Loading agent configurations…"
+            />
+          ) : !search.trim() && agentConfigData?.total === 0 ? (
+            emptyPrompt
+          ) : (
+            <FormattedMessage
+              id="xpack.ingestManager.agentConfigList.noFilteredAgentConfigsPrompt"
+              defaultMessage="No agent configurations found. {clearFiltersLink}"
+              values={{
+                clearFiltersLink: (
+                  <EuiLink onClick={() => setSearch('')}>
+                    <FormattedMessage
+                      id="xpack.ingestManager.agentConfigList.clearFiltersLinkText"
+                      defaultMessage="Clear filters"
+                    />
+                  </EuiLink>
+                ),
+              }}
+            />
+          )
+        }
+        items={agentConfigData ? agentConfigData.items : []}
+        itemId="id"
+        columns={columns}
+        isSelectable={true}
+        selection={{
+          selectable: (agentConfig: AgentConfig) => agentConfig.id !== DEFAULT_AGENT_CONFIG_ID,
+          onSelectionChange: (newSelectedAgentConfigs: AgentConfig[]) => {
+            setSelectedAgentConfigs(newSelectedAgentConfigs);
+          },
+        }}
+        pagination={{
+          pageIndex: pagination.currentPage - 1,
+          pageSize: pagination.pageSize,
+          totalItemCount: agentConfigData ? agentConfigData.total : 0,
+        }}
+        onChange={({ page }: { page: { index: number; size: number } }) => {
+          const newPagination = {
+            ...pagination,
+            currentPage: page.index + 1,
+            pageSize: page.size,
+          };
+          setPagination(newPagination);
+          sendRequest(); // todo: fix this to send pagination options
+        }}
+      />
+    </AgentConfigListPageLayout>
   );
 };
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/assets/illustration_kibana_getting_started@2x.png b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/assets/illustration_kibana_getting_started@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..cad64be0b6e36e79012970814c7018776bee5f73
GIT binary patch
literal 131132
zcmeEtg<F%~8}|_D6a|zN6;wtE0s_*C3KAk4ozgAR-Jl34I6_)V7-Qr{j7CtpMvYXu
zYs9GcLBH>Ny??}SUS8~iXJ_ZW&*%Q!dERTNJ)@yyp#*_IG%ub%(FB1mmw-T*Fyy4b
zZ_Y)OVt^kM&d+t<fj~^$7ypS~Xx`ZZfo_6cJdxM(OkJDw`J}(>wRk{=CMC2vipjf#
ze`R`6Ytdc&>?!ZQ-tf>ctp&GF!H1`k7FO%s(C&NgSaa|cd*RMAUYEG6-=i-enoL&U
zDMW#_Na~#)9v_kWNVxC&$L6W*zgTg%_@f=XZqi6dw^TnIwclg($Vq43v`JSYene(%
zV3gUA!Fw&mdS(U`?KpF?^|+U{0Bd^**e~#^;_XnskN|iI+;05$%fLZ$K(ve3%)?83
z|K2@{22uTcr}n?cE)?-Uh5UuX{}A$54S|EdPdxv=v*s^PmA-w;ibcTL$L2v@TMH?*
zQVtZptwDV8=b1N(@XlK<FP^BOJ?%Fle~L<9u_QWu8c1^S$3U2FPfyfO#w%V~A?<ha
zM|AaG8qdGIObGdBdV$X%+9OZr3=(78`4OYvSg4ky!>j(TN?X_EvXAMhl$7(#O~3Lv
z#a$od9ZC7uavQTOaimV@WH;=6hx?BextaQ;F6&%;8MVHs@aAcN%>%#qXQUDGmJ&XL
zP<4D9t!IN=B*`8r5fku?bL3dZRYRNgY9{Pm<oN86hgZ4@n?*u7;pE&UV`Drz_s1T0
z`lH6CD<(w1(-B(0BJzpnbYo)pfdMmua4+M>tE@A&kEBN0Yw;w|yO(KWxHf<X8@ayd
z_SpWw+Oc<?cyX1d);Z7}2EBbNYVPT#_@qaGKkqgH8R=UPavboZ*pYk&^!+a{(cWM+
zsvDTEvn-}-3#NfhQNA@cGSqu1JK~v#Fu`z$fwE!rkFN7xFjt^rE3K{UbE!dzE2dRa
zZA~!xgIQ?#mNEyw3q1<4|BVI%a((|-sgLg19~+N;`0U;Th}E^%^s}sisyBS}JZ~0e
zW%B?bGG|12^eB{~3sfQZ_l(#$v?Y_g7@t^~^)rOMV$5?{!G3}}t!zRBTIbWVD5;h>
z{wa=plz5TZ+gBYaJj8yx(7^)6SZLPK6GDYke$L3$$NiU8XPlSXgLao`X(x#8jtYr?
zXLw+bhnebAY=%|cgXqX5{w3kS2UgnbzC3evL(uQ~T>9UJEDB46%$l`~4Of2)gwedG
z@Cy>4|EvB#f?Yv)_A5tS17_a-z_H4oGk)%kATL{>tt#XavX(E$ZHzIJN)5;}CKNyS
z49}44yYY7$CLhOE;EnG0uT=MW_Bqo16bvOV6?hdE_R55rgIq7HJY@x}KFV7p1Tg{m
z{@c<v(@?Q1pq|rI_9u0{z)7QMO2?D4`6+MT>gq@~B-3HnG6f*Pe(UeKwS8e{@+<DH
z>kN~~L~{)%UJ_6W40}bt=iAW#YipmWxnd|Cg*5>w{P(oB2CN-cLbj?BwG1YLalb<u
zUUAm^x!<%ZJ$hir#Vmib@si}fv-rp*b%!t}cJPu6;vwlPZCy`C#ALuUL<z1VFR_<$
zOj-x@!VWwR0HW=+2_BMvAld4hVWne8LrXO%GwRA@z}y_#@~+~_rP+S~{!?HW=++zl
zYLZ-1$cl+6HD0$TovxI{N^qQvMoL_uOUUCA-_4vybk~3tg^bp=_T9-=(@?Yo!*Z-X
z!ji)9R;x=qBBs_{eQ~{aD<RT))TNTFrPGip&u_~z_P{uxx1P-z(D}_f|3JEZbx3B^
z0S`$!lF4_dq+<IFSicouTp|ITq`bX{m!6*zNKVs@lmw;nWYL+RRW;5_7$HW(cVf<@
z^nriTd;PXpptUx3+m>$O<TAwhLD%FJ_qKt_z@RgZx~}dAG^GNhz71CEAsnoclN4`X
z6_y0j|965e1*$$7d)BbZ%p)1LPf|C_Q?WO<M~KtuqI;tCBN@w@3r5yM(uJ@wx6hBi
z#l8g$TLQ3Cv-3KkHmvV;hJV1pR7hR#;pfluf!GWbQ$~q3bCu6x!G;?@s%1zeTj;Nu
zEN?z-*l<dkMh|9JdFRs<RIFF<+(M4+4>7Md5Y8i#=hqyL4KCAu{3py+)bA`qxRxL;
zCzw~|ku^Jm{@Wo3&v}VN-5w6}(FT>^OGWizZtN9TV!GJ)q+YKV(i8;iy%weR;|8|Y
zStQ7I?XT?#$pkB8r<3To#|%e{G0&Q~k=sX(!83NL-e|r!c-eSxZ0Nvbd3?KH$r}8_
zalGj~tq=0bIGcrvO#ZKhe~fh}i>R~b+^fJ%Rll;2Vj&)pO!Z<k&w-aHkUFD}F|4Nk
zo2hiZ>u<bpP>9<qqXM66)IR};#rSMJOvYj%!w>m>RaTQjbcoHh*{Jm#)Yp2~xUU@f
zHVXOUKTd9Zr}U-We|rWCI9~TxA4=`SI@Wj88-->g`_v*G|1)N;d*(}CfsAIgMM{r!
zObc@#KCdpRNUFCd{<+K53<GEH%N<@O;YO2IaGELzCn~0`qG(FAi%XTcB>y>+j}h`{
zI`46ZMBXT(QU_gmq7Y`IbyU}wu4N)|v4>+Q*fO~AD{1ApSrYDT*(ETbK?buJoqV+j
z&nY6Ge|xy?cpbn%ph?#<>idJMdCDtgvY4Gw<)Tbz^Jgm7waup*-$yo&w9`MQhFAmy
z7_SX37@MncNdBkB`b_66C!z$bqo~3-`7xh49?Q?FRmpHI(2%XEq$!MOqYWSq$H-4x
z6(O;2N4d`QCJ#uiNZ6^HF2E@uI!l-Toh4yB?8zx`7=22Q3``XQQQ(HK>pwZGXvj!N
zbR<~$XgZ!(UKuxe_;PiI{fb06prFFod}*&;?8aq-0|`r0F1~+A4IN|3aL0;=ByA50
zG_K&+*bYa>wARO;VG|WdMb~*N%r6rcWp24@0P?<Ko#V*k$H%0>QNxEcK2u?V^mZ}4
zs-gd>6=e#jm8_7TPE%L70ofd?Y>I0P7Vz+HwftHw+*lWnd-r>L-T76Z-z==%UvNo6
znUza{>n{f>>1*H>riqK_(TagsS$WxWZ5#=;KT3HO+W1518)aut+*hiqQ8{plLQj)R
zuGxc^cx<jvFMq9bBiup2CFTbQ{XeHH^vxdjq&8%a)wCsw<7OjE3B7Cp#f>M2(JHK2
zBE%C?b={`geBnj)111&ml&H+b+3%J4X5|0uzMs*xkNKL?-bX~4wdmzk37yj`T7GW%
zQC#!#j6UxGd%V}hM@d~m`nYjYgwi2YK0w<g4Rt!!{uQhI_U7NXp^?0dUvoV;OTI{E
z^!t31#Jdvj&f*F82PWC3R>tdPj6UnZq&Gf)sLAxBwuP-R=L7NvXb)ipz*0Dpm25F%
z8pZ#}J1>jQ$?4l<lLQgeDa`9}tsm`WLnk)glJI_+>skd%H!MG0-SglcQ5E9FANxzs
z)DJ2}qW%eS<qg~^n0CKL^5^553nnyb8j-G_j;!Q~5(crtTx3;r*?xUjucfE4P86?Q
zJ@l9le1{*GMqA&0qW@2TB?^`)zjmlMwN@jqT?H8)It|UB9#dL!8J$@Hd8nJP&I;){
zuG^Pq&TM90d{ofp|NE%z?hPe8EFar+#7351XUf^0nK_%upOZbI^k|1@_|v87%oVt{
zkI<pa(#3`oem{QLX#I=NO3ILOjASIdq65gG-rQ|vI5<dP8YI^DE1{5<^F#eO5TKTL
zbe;JE=$A=o7As(tWavRU`B`<xq~kgO)XBO%q=xpHM9`m4iD*weo^l=l@g6iqemhap
zaF#xTjEG6d+lZ~nuU=wK0tg5{lK6bOp=Bt#Mpqk5T1{6jX>V|uM8}lZcYJ`3Q>1fD
zifA`U0Do&}iTEyAl{mS#J=vtf^}W;qlat0?@TzwTNCihONMlL@`3TY@2O_(`{<jjD
zZuX;CZTWX4*XR7@#9FCYb|&E==b4So)fxGIzT=R4A8r#t``-ABPbJDzP?mV#cnrv2
z{2!4t_DE9}wz}uy!vVxyz4mZod3Gs0^Jdo%IpMC|&afVk)q+WwJ?EP4(=Uc5w?-Cj
zF|RAfHFEqtufRWRM3x#BSpJA{$KY}D{<_R|q!ptlmtLEi)b)p<UjESvBni}89^Ch?
z)nOyNQ1StArMDY`gnv%OdAc$XRhs(7LHx}P6B`}CqZ^gvn?mA9r2Xq?A2?n1ovy@^
z8yW}ol_<{XSB`rdP?Qk*6WI11`z6?_KKvJwuq6PWqUxV|H<rs7&~8JU0Bu<2?Mt37
zn#xB%nG^LPSXxTJ!G(tGT8wehnRI$VWh1>IuTnx7uX#_rvz~9wY)4@;chhgU9%rg2
zQ9fj5CU2zw7jV#)VP*90@7Om^)jeEmTIa7Qbh(i%p##{Vg_Pj(52mKiv_k>%oA5gO
z1`<dI^=)fso<>TE#>re*DX}6OVt;j4=CX9Y<KFG9a-6T|jM<`nAl>x~tv&W0qynk^
z<g>~s$I8gVyX$HylH4tveX>70+^&FV*PI*E>frmc6OVR4w5X8ga3KgkJH-QMTrO}R
z{(C16BtgsoaBg42haUNPCD2oG05sZ|Wi@?$@=oienM=p$dtGsO7Ncs+#L06~o&D{G
zF^gm9G%d;@#SAA`9;<lrESD18<zF>y>vMO;%+JR@VpPU@^ywwXu&VE8Mw1+JqAYCB
z0C)RnXf3F%d_Oago+^;4C*nKdDDKZJL}Om9PkvY9uo<4&fp*_Bk$MRlE65Bv5R#We
zD{(inyeHbA3Fg50Lrq9dCpM@}jC4jRHBZxgRjvAM+;Y;SpJlrJyg(i;zp>{$7qH=A
zcg9<i*H1-&nDUjbS^a#Im!-t&_=?*=$X3U$0_VoGkbYeoPh(kAp-S-nqAMpe#r3w8
z5TDVEb`^Gm`3gjgZxgBBDS6cFy}x=Gx`WxFHzT^vi-Qh%%6arc4bw^;#Iw!J$H~`&
zoCTY~y_Fmd36~)V{JPfz%6BCSw9a(4Bdp;<vS|O)Vrr3VlNO7IZpY<bLCQrf7s;LT
zQUwL_ai|U`yo009Pe~Dc<%ojSAX*s<Gp-GJXT@7$UpYiY#5@RE+yz94On)*LU!$O(
z(%FkI4Ca0!s_h7ZJuTKYjW#>ZWY(Fcs?zD@)IUzO9Vztf-N0oUP?EpEy>;WF+Pe-o
z)fo+nD@I=Ar66h;RL{m5+uk(cXQc>(k}aOvM^p08_4y)b`|00`e;PPORU{3EY-r;Q
zHqBxS6@yU^A{U=)1d6W73BL@S?*bi=wvdyheXrjcEm>1Y<YL;y(o+1@Prp16wTVX_
z(Sd{(44iIb*!sNl$c7U2Xv$G7dpH`6&8PDQ8lysysDVt}k|nmyi1MmQ^|(K4h4Cr5
zkD;g9x+(GIZVbx1nFzpgpkh|kmhzf>=RMRKC{QI805HHq>(6?_taEMA(N?sYVHQ>@
zB;D>SKBM$zv<cK+Os|px+pC!r0083GN@@CKl31bh`DevIq}q$yUoOGwWItr4C%f=N
z*KZJb>QlH%$;gockE-;pfIhM$f=dL**nUn^zPssU3?8p!;FxRqw0NmLz$&o(gMs61
z>OxTMVBrVH(Mx&`(sjP}A^w};9OPrk0ao^(up6b-SHF>5e-jKGst|*{v)f=#E+xC!
z6(gcX=P~D8h)Q1{6iKD;hi&4)Ri?_m3p`%dgeS&_Oeo5riW~Cic1qoB&mdryN8=KK
zEaqw0r`PN=-C9uV{!R79?}U?{Z2s_iNdzk14bz^=pJOp2A$=LD4Vw9w;$Cbbk{EtX
z;4;LxZs>(YJndvJpVeli%|Y#i=}jh-pYx}3mJo%6cP!MjTpHKs_vL%L0}uFE_KhU1
z?J~gp1!-jsQC-~9a~GNRBg6|&?)_C1Oy^?lYs`x#l~nOHX+fg`2312pI#BXKBX#oi
zmSM>uqB$9h_voAtLdP169-{;*b1t!8ggTGlPd(>tZd^zmsBe#b4xjmBz}(amG|Dx+
zUZ_#*uKRSTSi!tF3e;Q;`_1NXn;N4!VmwWJp-%D=rEh-p*5QEqW^w|zI?4rGZ(%Nh
ztg5xb7`6PcSdArmM&+zctH@8P;X|5?*PN?#g52!nj^Ap?&(5U!8M$)Lz54*7>bW59
zM|aIV=f`MD$&sK&n|0ccWY9{0{#P(LQ4RtF4uBJ%JjGZ$$$rcv+8vi!G9yU{=L<RW
zBCRuat^)!Ho=<DmF?u1HTXvcJ1#<Zpk%xdJr!Zz`$)e$fCkaT+PKwhuP-<!eRa0EQ
zLs8>z0y35T>BTc+rg+0zx)F*lX)`^8nNG(}0Ooj?Ye~Q-E%V?7^^1tVwbh=B(oVSw
z_rcX!MiBKUgsm;3FWYy~LdA97lwx*Qk~(^>CrOlFGgrVjTt?}yk<$n_Qi`10YVnrb
zF{d&Xhv=B5zb2z`3abArXhAO*)5SW^YWs3Gvmy}owo;h7XX02#seHiOClG}?G*A_O
zseUjA`yJd|KDZ90+$hb&RX@l)U4jYqHg%@)`I^9yM&wna@QX$Lqjt%OK?b>8P{6PG
z94OOyeBPo2g<Fk=OyD3{nDG{k=fvq?hw)jYCzr;??@kthHB_kTIig{Y@%>tZ!uFeX
zj|Mg0&8CKgjAOX;IJRfm&KgJ{1#eRO3YcSi<w+{_Vl?qEoBl37D<_f(q{J~B<?&%S
z#gF392_~V3pY;%J*>oR2b-B1-3$T$DG0Vj{e>}~pVg4tiF0d3@b!+=7z=ZWh!Iog?
z28sgaP4NHGQ1X1O3H3x9ZT`EP@x9Dw!srUBb=vz=>=$?e$HbR#c0GxAIH~SQ&-zWQ
zE*Fzpf6xqjukDI6=fk}dd|*@>yGx~ByQJsz?yt;)hg_VuX`JYU**LC~Uu5e&tFO2(
zbX+xSnKJW7lQVI<VTcD)GuXMs>#;q*OSQa=?#y3!Y0WrxU$j;SZ~@zpm9^rcq}oH5
z9+{qnhaP{9{4x86bmS!!(OU``vaQr#1Kga3n!#Rs^^3}KVJS71oPY8V0jOw^uA+y9
zNFjy`#ND0^{3~=-w&B*cJs^0sdb#I#r9Qj$J_JzwuGr1m84`=qwS*;`=L-N+yy76y
z8(Durj3Ab+*Sino%?p>HH2%fC{J!!;(1zwd`6`Cr(!COXzA0fkVwjp6W{^d}XtSRY
zE$^8WOj`RCB3p{1+!dO^Am^8s61Bq%jRYz{%jw18mSout!@qHc=$d(f`jb?eB7yL#
zDizm~qsnU5Ue0?n6#Yuykxf>FE6>N9=A1|j_)32CSP-qF{15DB+$MWo4=&C{aUng@
zXV<SYY5|^ENjc&lz2vz=bP|tCQUhw43WWHfwQGHNFcJUzViBw^N?2v{94oMB38Z@O
z_TSnbUmr1&m6<jZZL0h^2u2vSgoK|>vxbbEcXBOgzo)4-TK=YdD$xx7$iQ0?ctw$f
zNrU+tLll5qHb%1&*WM|qQD~tP>d<|OGHc>TqTrYumo>KbRc^}+vI~GjcDwWw*ry(5
z98JD0l2i7L{<*+t--<zG%C9jZyk(yD+%ls>N-ewdWFio`hv<-$B85B0R{aXMSfkp>
zBB+1}+>E>=@Y#6iU)HP&6l>N8RF`?Alx3b!E{+RTlRVE~2if6xu3KjAS5>_MmnlH<
zfSRKo|A9hn=bfm4oj>ZnTj<2KmqkKBkiez3w!KA5@w@;9_c+;sm2bBvWtJ#Py)DJ}
zLq9Zr2Z~xs)I>}r+VS)k0xHQx9=#y*Kc^QuE_A?Sxu}a-mVI*;gL%uwyT5%%oQ8$r
z&tF)g3F0bsY_i<&XZ}VHyLN8C19@h=CJO4ixrqt<9k;1L*DWNksyvZ#+Pnk+{bNif
zWrh*o`t*iav6xh3uc7ae0suMj^@!UMq8J9BY2(56TLAPEOg|YmZN!{otlbpViq4o_
z2z3Ey<SZ=d(^`G1&gr*Q|IKpYx?R!DwU~vk)jg!u8JkUx-kb5{NTE2u+bB^p>d3uw
zPwy}UP<Nf3YvJx`>wsAe{PLa5N(W;`<qMR%d-~QEmb9#KOWn3JKAc5{r{fNdRk5&e
zot6^OBi;KvGVYeM1;!Y0M_KUkBOnP))8LF%!KgRi13Fa~dIEv2GXvo|?dLO>l-fZ^
z?dwRc?F=*{2G3f3xMQKeEm{btq*d-gF>X*XHh66MR9}S~KmH95W+C`1r5i0f9gWbB
zKBXx3&|&usVI+G%3mo(n#fxH5+tZ&!SaARliOo|MK7j!uU4e%Hwt7e1%ikL6mKZyZ
z-t4@5t8yeZ1E~}{@ew*=NCj%R=H;EsjQHLIq=m>)$OD}8f}WdOHI7%QQS`D-W8~jr
z!Jk%+$cF3AiS-vX9~M`%VyO0Sgj!<l;SM*e(JMDBJHn;Lbx!tNa75wpLd%}*+%jyD
zZ4ymfjtwVZ4v^yovazcbRAv)E6ny)_nSwsXe}d=?jP8%zGxxLzE&(=)>uy)jX`j00
zwS(q;cD6$Hpq;b4l(samB^Yv%^MR<N(>kx}80IQA13qLGNJ(y-C$j{O@fJIghH9f@
z&QBUJ3BZ0Fn&BLe_VetS>bX|WrxonyGcpovo0h3p$HrIN+ifirW`w?7qPqUz!s-Lx
zzJZrGdI%*E66#KlKJCI?o}EXG_&@e{!i!tqSPPm9x9>gy7udIYpc$n!<MtM(KDTaT
zI(^Q<0E0Je)&$W4O95MJ)EMI}>SSMQ5kY<6)$$ZQpVx0t@We_T?ct?oVTDC^pMXm#
zH(yp`R*j46{7)1jJBgg>0i1CH_BL)ez3P7~%l!G^+1jGJ15hU)2|5MVr0wLhs_b4J
zGuoJ4L!S1_zVJKU=-qj@8ND;cT6Vf&iF05-T|KZMki9FOA~8(f^aWHdO14iTNA~-%
zj+}AYa!Xm4wskv@eZN^LbD(-W5>@-m6Z_`b23ffF%Is#U@VO+5nN7+Hj23(bdKAc{
z0DLD%tppJ|{@ZOa^H}5@EFO@!RfygxH>RqSrYS{DIiud}h5<_Kl`7!`{a5mCydh0q
z>bii9dcP@scyL&-ueM%_(==FK+v?w@k22l(4?aNHW*q63-Evy<b}g#m=trwZx$*D2
z<N9Hv=$i5L7Or6tYF9EPfV3_)v1lDx383uGtqgPs8duGID?{RJ_k73ORr(x$^3Z2(
zG=*K;_(+1y4DVga4f1QH_qg~}kDlJk)sD~+fCZ^gyed}A-BFRn&c6CjidSvhtD^*0
zkB}e7)rEznxwh3)<3`6Sp7e6W|8#XZ@To)uYfXFKgrUnckEVzV{Hq>(@#E`0`}~v0
zNz!BWLGq(lpIWzlk?wHjbOdmE!eE9=*+CZ=1_kb}@ph?doAm%>^b)>VZTq{NGqE++
z&)~PZ^proHtefRQXNNvr&LQh`+<^F0+&J{D$wAR4e)cM_e7(qy4p~|LyJs5*cl>jP
z%KcV}5IeOC1GGfeG5-$ZHrQj^0?}3@aH~g+>R@ojc0b@uO8AIQ;BtgEjhR(!@@lvL
zylmXuqRfL&rwISKRZaf=g_2i_MP$Z)3WZ@h>Zp<1i3a00K!|_I{dJ3)uM-j@2eRH~
z(7YbLB?HiZvj73h?|1?SkaV|%MY#$x{PZk~&vGa#2?dhdj_PP5y%)d6yW{;P7ikS$
zZLB~QN>p4IqJREUdss$0>pzV5i<Na#&hwvjN}4H1e2DpaHsAZ=42)FSwwqq=tWQLt
z@d@5F-RrVx@pD)8g-=7O(;d<JH7uq8U_aZ+ORufH!~m>8eNln3QZS&s9r&+Nc-so!
zem0vA1jd6E7S4o3f6U@6>=_ykG4z%?)$kt8aF=CYF)~u$v%ncv&2+@vtSFH!9|U;e
zG2N=?zb=cA3+1NM30%2;ZEpgMzzjIs&=VNU*nP^Kd^WmUTrM+8W-t1Z`Aqz>m=yvN
zMc{Rt8*m}u_-T)|Qk{;;_xB_hm2sqG_wn!44_tYm{H55Dx)Cp(Rb$zy5ghq@klmi^
zj!1U1<I8__rRz5?fEKhG^;xHN*)ZLAR>aSa)&57t)%vaEbO+>D?}m}us@?^0W&>;P
zoFDdku4T@|j5=?EfhJ_z6syzJ;%jCQNKyliHT(QLXTUSM8-ZvrxSAZ<ZU&Ao&{9vx
z2VaY#ns@Qa(u1oK^246p{@lG>Q1J54JZFmPnXPa3%wDX>cDB`>v!OqV)eeZp0cf)k
zwHxv&;rx*uc(!F)hPypr#c0|IPP3?8+IrhP(C03BNgGhv4lLbiOF6)kti<s%`iEx-
zfIw*lTD)bIb^OmPtqu$YVJ|zz2{kV{BpAF?q>eCJg^=Xrufpd@tZi9o_5H!uuNk6#
z<me%kE1~gb1$X9M#tbZNT?SJ3C(k%1;zL5U!|25Bh>3D-kC_8%yNxebKX5yikCQ!B
z^wWoxa01l$tH;C2V&_;~$ui@CRvM{h{?XfLGN9J?F-&|4b3Os+rh)&~bP?88kV%Cb
z@XR2+KKcSi8rat`ZC;L86>rm~6V|r<o|qyh$FFil8hl^vtW7Fk-}^bPBwdvBGS}SQ
zTaTf1bi-lk{PyQM{^0(!xUO@@1H~UIaGa*m<Y(LZiIMy;sPXa%+;jl9IZ_yUTIRkx
z_k3M2yztM5LV-fm%uN6ww^eb()1JF$hF83PfX|23op+BO%L3J{uYFr9O)U{+)pdTE
z5zo-sSRFzQ?nC%%mM5xr7#8k`-5d;5R`;KBH|wv9xJNfU+|@AHu*?1)x$aum@ILJR
z3mF-3sf@NUw|`gk%)PUM{;w)Vy6RQJee4^ndEB4GnjgqfaRq+H14o4X@RKU~I3qPl
z=q?CUl0mBc%iP6SV<-ly>1F!mS;KH4@9tN-misgn6^72Hjy=A{>IAnpKcMCNatOiz
zT_%IOevL!jr1hO!xzoMRPe$tRSH^bzk`G6>FCI)gB7exf?Ralx`1`fh9e^h*5oRN}
z$f1X8xV2^f%536EtFtu7d!B=6<5jd&NPSa_KzXX4+POI&e;zWz@1Ag{uBrvnRib>&
z1xVA*jr8?iBHf&QB)`rr0F9M>)AQtNph?*H)9X+7agAn|DyicoP+wZxO8!lwW8*hl
zuEJ?z=eD8=6m2{_?t-f}BaYvL5{<Oc@DY4rg)42Mf3L`C??$9OV{R($0Zgm-gRGnF
z444jeRVpQ62fgXxcWQn101?F6TFfHZ=eHZnJohtSo76o8`dyB=4(Md2qbIKt+|-^t
zY^|B%?b;#H?&UBcbEO?lbdv%&0}D(Jz?jgYPKxkF-(6Ryv7?&n!k73m8;Zv=a}#OZ
z2%Oi9Y>tEczp+LA`0B?B6oJ}VW2TB$eyNAt^`miMdeqG+QRc)0dxlKJjee>g(|w(r
zo3AAK)6mNF2wb-JSa5B??<J>n%`pKXQueq&T-##DNbOreRqG<bSl*4kbW>_!=*(Jl
zRQ^)Y^Mdlsa^`TYFgK4+;-u9pKI9AbLii%Vu9_9dV<2Eibnp}%$&OG(@0wXk*eUSV
zu^U_G=0~X(?|y`S1WHQC?QB*7VC%5}pL%%7ucZmWw8y>aqkp(-OI`xalnhel+ooi#
zrz>QcjGxE!NK2|2Opgk!hp+4HihdaolSO>pS#q%bo(nT#QFgF*NZwpJIx>4@`TOlJ
z=KW%x>y`Y`mH~b^Jx>qCy!52RQLW~q40H3EhTTd@F~3IJ5;D%K$%km-$$2h_X+MY9
zO|eI|m7{U8T{Tv**@Q}=cn<Z6i!?g|6AcISjxSk{SfT@_My}te)LRb#Cw4mENZtIE
zlD3qmpRX27^1cK@MPizaW{g-*PuCg+a!*u~nBP#egx%qI5JqvzXr%Wl7Yi|Ro^S9&
z><pB2%AOdaXOUaH5Z+pm8a3bDkeC*@9`_->4$5+vnj198)Ex5l(bU=EG@X9HINU6y
zkGzC*(e|gl<YfC+hsa2i!;*#IfmgsqKfCd{Cjiw%aWV1pQ>=lb`cjUQ|EY*@v#<jd
zO~%P3kaLnKihSi<v~M6m&Tn$UrnDJ}x@()8aE{kiEdOObP{H^iEp5KLTDkR49&hJ#
z!gu827#33gJCoG1ePOZNA*24N_=$K69T(C*(R^`AaqP?y<q!!uG*Lu}(B;Gl?xxbw
zXI%Hi<rP1#!ne&R6ahhJ9mi1+4-n#j#gbScg5zEWe_P)@KEOsTo&W3&eet@z77&8S
z=y4wu@bop!T7FDN)3T@PS<Br-1bSrO0sHX97a2{EtgfzePyi#!29hD;!b=`Gv!eQI
z7(SYUy&65YdAhoaag`P!Cjlr%D}GM4_Dyi!4p0F}dzi$Fa8&Vfu$orSDuaAkaetAc
z8Q-y=J)<I+(r~^+jMNbp=r0I;LRa!_*7OQam!(5`@@()(esU;UezMQhG9=^ecN0o|
z`EVH%QYO$Mq!4@V_~zZ9FZD>3ttPr<98cp`&Q<9X{!4&wYYyaW;w@Jk@cKj~@|%05
z7*lE>c+aa489nvR#mA~WHHKZ<w#Ui$6uY@>mv>+=yomPC%;|O8RJ@63+>vd^OLEY(
zD8D~-2=dm~E3gRfIiE^lm@ITsA%-8?`_*fmtw(59!d_!FAv`g%<Km|^UvQtTZ=rm=
z5M{g=ZMu<;@)z&?ef<Yg+YIMH#1kF?JewJbOI_$3JvRGw#o4s`_mOvjux*o*SIqE;
zR(hmUeyzpz0noGt@}oRkr;G09BUL!kedj1+>hz%)$L4CFyWgL+?vuVFHa3GxO9>pU
zP7Y4!qNSliMuc3yNv3WlEp;3%bfoc@kE6F~JQQN{ql{G9^03B_CWfGQzIV(b=>?|o
zc4gJup0LF$IB@S7c}R~Je@BE8Bp;vG29)~v-_e^6hnhkB90KsX@VeRYy@9D}^-?VZ
zGZDP$LTLdpttg$wsq-JX5f8}9VUB%+)L}oBWS8GrGubKTJsT19+-BxYM0qR~qFGY5
zO;*^dUxB>QO`FFzqfbd6L*e*T{}P_2BETZYeNRqv`sHfMTfh%b4uED%1(-);il#^0
zc>^pcg{WCxdLeyS%)PQ+|Dbgq+3IKzo^e(D#O+RBn-Q*ew$(xPv-Lna%`Mk>)eT>%
zZ5;8s;LDR8i}wKs@~6%3zu$@JjhxJqM$eMy%WFpME?irF*J(2EGgj}|@Os{f9dk+L
z2j^qypOaISyOU%7)2(>GD2jMT0wnL8_gtUkROscJ{UZbF@dgY+@?>T#6e%nZE<J&>
zRc0sR)!s@8iADPF!0&>V5_<Y^%g&$4X)qWjK9kWs(bpk4!N}i*+tmfg0u~vscW}&z
z?k1)ULh_@hbtf3SDXM#pAHUkohwz=ctov^ISI6A5*biEE461D&A^D^B%HOJe{d!=s
zqdWaKbR}C2xz)SYP?Rf$A-DsMulvov2A>`8+|!=&zFE2c?FqqPnGsq|#vj^7xSxFZ
z9P$Aif(Ym{-N=P~xf3I9ESE`EOuci)1_cT>qWWQOZq&S`G7UgVw9x3!rVO+o4BUU=
z&}~d1C41fko|^5~rl2J<>6y)km>v7Etl^WoV5+Zvnb`=x&(^MJJOp00=d}vaw7H%u
z53MEs2wg8@3HE|T67bC)P}NoA+cagDTJGwL=7mhS?L*9O36vH*@uT-GuwAfR9+~=B
zbd$yRDZ?zhOl7(Xd{uUzI7@I^Fjuw;H^_e%obl-!=E2r+7W_(&@sdg){vz+6l6h|Z
zLXq!^mq59F^ZYC7(u&DFhH_+h6p+;j)aE#u$@*$IarF&{v~iWKFKOte)y?wH(tu8f
zt0?vJhv_{1G8z4(rOE5+wH@ZS$I0--dF#IZ2X;=gmuOs1`A;m{%yZ4u^lWkyw^8{`
z2~Z>UG<dIP`k~_i!jnZzHO=tT+|_r&Z(Z6#wFmkfmyEF`X}>!X>>Das2s?#`SlMOo
zX`g~UpXuf0eb*$PozkZ1SQ*q6O+V4gfVLMaKG?LnAE!qORZS8B6$0n6r>76KWtXLe
zMFz1i3!x!zPG3fu@(pkA<D)(6H8SVv1GC8HCbd2gY~(Tn;vWW*c|X<Pr3`Bh(jLK9
zYZKTG(lf?struQG;kEqXEWf>}ke+)jck9*`_T2})ocgV`G_PJ2mJ)f>j9A~qfJ@g<
zT9osXTLtWhPlfi32SvQ!xuBRV+YVn@Cee2l&Bb!pQ__V(eoGaM4v01Hpr_*xU&lt&
z_{uH`c_A*ZR?dxE<viT33@U#9-67OZCwfao%a|q>H8ZAM1ixJ(Jj>2%^Z?xbSFiyf
zq#lv+x+=GvU%K9$TS7dZ(l7p-V5uK*4=uGdBt98EZLDiJSw7AX*n-&3tkahdnGRA@
z&DFF#)4GmG518U@WKpu2hY7@eV;q?Jbil43l$$t6mu}YoEaJx0rV{*y6V?{n6?j$Z
z>S`(M+uXGOy(1qwVsJ)>om5$x*UemmDkc${tmaT{{p2Ewwuf?q2|wnxw-{#%x75!|
zvO5p-&N47l88pa@xL<qQP16}K)!`)@PtuLD2(~}h*b1mnqP-Ata^!?UAW0*{Cm|2J
z^<B3yCxh3DQ|2Mu&&w%YDmR02HD$#etJm$Q4@M?Ih#UW~|KuE!*RKmyG7^19e=79M
zSe+G~^Aa-D6XV4{6lf=!D3bN%yt1EDbD2!NJ%(cvz4W2J#~Vy5wh?<E4@33yoX<(W
z@4mw=3HzkeDCEk%B2fB#5KHcs<a2gbvldF;%#y3ty`JoY{YA}J+A>Jhu#Jc*9t2As
z7jj~?(?=&8B(|W<%~I0(GYTzni{6zF*nr_k0;*Fs_F{6i8I0}&I2U4da-0j%B;<LS
zIy&}(bOb8~HkS*=Qe6H1Ah;6iZ5&gxa-H&8iU?TyedyiYD$qyS;JY6s9ccE_+by9m
zLsboCo<7rcLX+wc`um6a@D;l(Beg{>!tw!zF8l;Ga5kcba2%ddLBesvz`w6GXdW}(
z^k8Evx9>hH+0RmFeUbKaxn|&UN}R;!y9)|vqHr5S>TeL8?R>QtYip&%PGCMmqPL5=
zBLsh`Uvx5evDpm*xe;lx9!pBIY3X%~HEdqq^ZRn{ET(CTpA~d2><GINm0bU7Uk2e|
zP<QXjR={?pqO&taW=3<@1hK(FCHV@O49guan2uzIGQ4n4q8Qo2;<`NVIxkO?zUO@g
z4G^3eSY4jrWAB;zXrzwLLg(X##hMG~P+yHuaZaZ!Q+ycB8p2HRlZrpQyyp(9LI>{0
zeJ>s096UDPACh}Z=^LLER!`Q0yE@E|1{u<pROf9z*>c$~n6US$e9qS{rZJCrzyyR<
zA}4nyF979b)G1#s-gvfunSF0+4Wv13ke^Z^Twz8!z+qC%6xdRr8#qN1EJXc*RCWBr
z=3*VxwrURX8=eJ|X{ZxZ@RJ_y6irbtNbJAyxyI1R5&9=WJ-uxk<jwl*JOh1TT&(r^
zL?6!cgz_6#e9w=8eR;&U0SNgYs(4|yeVxusIIP3{m%Ym3TUeQXT!2-#Jv!R~gU{sP
z#Y?pAG=uBvg1ojKB)>RYYE&p{-U%8fH;#T>=ThzA6QURl>U?nFA4EAV8}?q$#4lG!
zRO-`cOq)mOVpT%GeuD@E1E}vklx#W848=kE6t{(+^Rv$8_dD~7I}>sPf-#JY<3m29
zZ;+lddSx6%ovyBbjp1bd`3WM8U4-07-7KQ|?%7()<ffgoMUNna`=~Wl_e$S+U7OIa
zP*o>{@@yT={Rw1mm>S>97dvxBHlk~xJzWD_J2m{vLKU`iVLGRjiLO7If+DojiNbYY
z_+o7<?AC{B?WIw`4tQd}>H~pO)k^AUR*8J)fVdsaka}S4jg?~gME|nXm*FX&i9qRX
zH{>QMucC<SG9g;y&5IDt`CYFu$L(<mDUsJs?5W9P@9Jp{M<igii7Dw~jQur+G;MpL
z2F9^&Sff+tpT^seGE-<b`jV+{{@K^!mJ#%{o+u+k??rAo98;~e;FC<2C)~kl=0!Z&
zE!K%eVKsBH4k8nE^A^1Iay(aHL(AF1bze3fk>e&)!*v?Au<3N}BSGsaiRV9@o58W+
zI{m%MzpVq{BeiUEXI{<V#RqrTvw-@W^H=|Y<C)o(gwDa$qZ-?|`k;L>L&AMzy{;p^
zz(tBcnnwH_l(u6@%pMwU(mh@l7$QvdwWxUGSmWop@1TTsjgGR2UUV+!)*;}|MRmZ%
zd&U74F!4`@#u+2SBL`4xAmQPB4D;ob1I$KB<X2sTV&%z{vH;@&jm`m?@|2v1VV%ag
z1EZLYC*;KBbs>)XYOv_ljEpY@xo_g0tE;TMKT4-|8yV9&LSh8;8@5<A&-Q)D#uJQ;
z(dDO|4bn-;+VaEdzvjl(f6f)0ou2X{DvB$dey2zJ#wBatZW;pt6Pq;HE5S{sj0&Kh
z0r9G-vONM1dMqf5S$Vl+OpCV?=luuqA;+l;A6@Rh0-v@Uu_OnP<p%h$=odDBc$$4&
zs2^z5Khm<-tbjwfv7{4-?*UoXI&x|ic_!njI9`{m4X;j3edPW!MvyNFiN{ZA86fS~
zdI|<mvWD+V#%A1T67Mj<Mu*N0NW4#hcni0IZdfTCw-l<K+Hc*SLOwD$+xNSF-mKld
z68gT^-+&=Q@^=5#@}QAHs60*a2jkQDS*Ss!W)iG*G9zL1S-pUil#{YWIodRV)`f1F
z`${!~mv>kPNBc#OjQ!w4i;vgqgSR#hx;tPoK(%p(&#xSgB_}LsM>L9MGtv>-MmUY$
zpYDyQYs*V686-zkr%E*1V-P|Wmx}ZXetsUC5&sTSO5~`pBZ=bp;=y1h3U?$T{Q*z6
z-5<~6yk=8%E&9oLeYhxU*9XkBa>SFGmM#YCH`6D@`=0s_9i*x+P{tM`v9BF2i8|b;
z{-!vpVkZ?RS?O^lNgAeWh`PKV9IgRFVjK(09yWgPn||;<O?4CJGLI<Isy}GFtdo6{
zu#s;edGzIr?2b>Iv2LPmsfViUxKfhuxB1ioeX%Mt$9{5PTnicbGb(0tS%THf@Mf!v
zVor)WIZ@_x{aO2TK{rYVQZmw&vZ_<G?y#KVLsRm2+_EXt0)((m`NB~oPFv=vbe=$O
z)C3uK|IXM=cr0y;qY@qJV5YRh%CWZ!piV0v%<RO7U=~rdb|bqdiCE{C===t7?CEl0
zYR&ed5BgQX3ya%=xe-!%db~%6_H~!wL9}jym<Chc@;H=>O7EcQn&i*A&&-b8`dVID
z&*E4$Ww5n3R}a-E?U+oi7(^#^xj^-$GT5`~hC>27RibS4G$TF&#bq@hVQ17T&4H;(
zUWQJyq4f)K>eKgp81=kA6~Gt>0jI82jHjWab^)Nkn=Btzo9aEBryjMdpQl^j%tSy!
zXO~0`e?BoDk)(Nw7k!lhCt&uht7=w`LC@@$v?`EwgvTRde&=p`AzVl|4J$3%`Ld1p
z`f%H)6%B>svK}rgu~Xma9MpfPI>P{R?<yF7hud^WGF^|}MSK9Nkz+;Y>5ROP7yLab
zCWX45xvqH-!38}oIaBCwC&MqvbKzQ?$BOrV`Q67%pN2pQLh~d#l7CieA`LE_Kq6<0
zb%{u>qj!{OG`;GkG0@TrqqnQN$VOjGcd4PRS%<_&Yd(DDxao*f_x}NXaislvK)fT`
zX$r4eiDrmHe~YV^7}*bZe|UY+i`~-1A-9?6ZlJ~q*&;#wg%zm{mJ$nH%6DYGq=OqB
zby(juTHLeq>mk4?7A>v1$)2-3S$^jw#qim;^xh!qt8PPnVn$Mu5a;vclFs^`JRY8|
z13X?I4R5$wBfaaRSvVYl%Kxo-Xz~V_iYs91U{HN>prv+XrTg@Cw{dpmtteYK*ZnT#
zz4hKmF@GM>F699I!mFIh)l~wpG`$&2(fdD;_uLGc$0p0uktetY6>CxN3l}ct%2v76
zvB`*2``MA8c;U@ou<%xX<I;pfD6;oyc47=E4XpYP>8|bWdymu3rft$?@2R>E^{iWm
zV=0JSJK;5#2ngIrVRiD2s{*3R-0?oBf?f-xt*+o^oxtj@zB<cu$hrdW8C?Qqmog=#
zHmF#9#_;K;^cTHKqvayg*RM+>=QgDu!ifoFMRvK6f*S6tcdb&O(Bu!r$Eyo2(Yxgs
z_vQ{u`Mi4BNf*4rUSzoLrvEr!M8o1F)J4(uS;O&?7s{`>$k7^O7Z7vn++&V8Ab!iX
z6=>TpI?O|ME!3WMmg~I$X5s2xM+1smfUz-?Icxn;sEOzfTE#jH7Our&euQJ$i^ij*
zBlkF;d&6&eDO%$t*YzlgK-a|yv(e|ocl;bCMkl_sC4V9470kwy>YvelIT1lV{60Vz
zwuc{0*<K*gB@n*$E_UPH5}@7w*4Oy8a}VYH`f9^pJ`Es&eyd-}>?s;>ZyMd3`lKc}
z?O5V7>#tUL6j!iji%BIl<48ke?c1OYOUj|2mvf$2@_eOrK3D=BtdQZkHflc1>hN|c
z&z<hdAFMTdP92q^#dv`GO;gd6k1lTXd}MQ&-Gz$nh?FDZUhGOc?SGWTFtD1~fK6`K
z6$ki)DVVAUVvE?XfC69Tixe`vZ>`7r;4Al=#`R9|y~Ved`bQK`Z@$n6;eDMx$4|6{
z+kp3seDK0Xqx0u|Kac5Oz|k0{mbvEU?De~5Jm!T7ym_KR4AIYjAAU@Z&W*c^>un#h
z1z*)#P6bN|s#uvV`|=NdTWZLtA68@sArqbQs>-;rnz{_T_-)dDb6>Qk3fYfDDB5CY
zuOD^H7_joCRram2nR;5>()%0bJ0|FtnfTc}t$M%dB2AsMVZQ<gQKTAK_(i8zygc=E
zfV@cZIun2B1yX@?l*{F(NT(~)-G$ZvnySdyTgk^n&qAc4XTJoTB{+ImxM4A`Ms{0J
zpyiZv$Hh+|P@j`gV9s5KrUNvEl3zKOn-Lc9wZ~uc&Iz*A10zpqvR?ie9!`*0wkttx
z`^>^fEzFp$ws|Y_?%q}9PSD0Ps;a-3Yl7l3l|#OnxvOYve{P+aOBYj!g}#Eg;N#8F
z=H3NHW9F!oai7Dh_)03C7&S2m&LN>9j9x~y?k_BJ@-|w?c68Vt1vU3pV375O{s#Q&
zd-b&CW%buBg3>=SynPQa7pdwFCx$!MX+o5Uc_s-ib|@kJTQ({2;iq>dva}c1&(sFQ
zn+cZM#My1?VXVs_cL(Nc#>Wczdt$Dv1Rg^xlOw0s)4r=-HR=V*PwzYlocD8C&o~8I
z_$n2)y2b~Z^*=}-6PjXr$_74|(_~Bf@$ZJ$`*0GL?277*R@sUoR#!FQP=Iocbc7&!
zdKBzZqIX{);s(4Ropub#k?Rztt^x@oX7^xUzm)0c!AjmPK)6;}MKlUGitR@~lB$<h
zf6+qeYYQ%)Jp`gz|Fd<wTx{#2O#f~lFJQmk%BO3CnuL&AO+wvfk`R@16L94XxOQ3h
zEu`<+A=$V2JZ-koSViDgPGCId2Gl4j*niUY9-^LTKfD~MqiuJu_o>5vK()Fnbg&w-
z7BNq6yX^fek`PsHt^5kM&|`SbMfPXKO~z&Cvbq2$uzas_rS#8o0(hI}cf7C&qe1uM
z<bnND??%Q_&P&U7sD^~))>7}N%=%*WJ1Y@4e`vnayq}g0`Q(zzht-1s`L|}rJSRd<
zr&#%9w1OhCFxU2bgm7|tLbDI6<d(kKuc{}+P>z>rwsp=QM~Sv2XAJE>F{=JZ>G?G<
z!+Rtmi^=D;)nP!br~oq!=Zl;#fHv<9f(#K*J+T93TA-%VBH!rRENFiJKZ%vyIbftp
z)!nMQ%SHYlR%wFqg9**a;Sl0F6nQME8H=M=#?93OJWZ`veA>_$r)hA(0?~Eg_U71{
z{ozQvWumlMZxjOK7<^a#&W*$!@21qegqUPy_-I$CmY5$_v;U`K*q4zrs|6jus{5an
zRSUz)zPyvd(-#8x&giKaKsBbJq1<Be4-vtyt(d!xcxBMym;q@0FJ_rPPn@wo*Ko2`
zH{Wv}SIsr-IOXgN6QkYfBR)fg>X?4}Pt$+Kv9RFZf=<}4R9ot^_L3MviK}GR$u2Gj
z&fGb-r$PAY=^Kk@L6hWHjKIzHyM7`Ct_$J*Y04<0QgUEuMa?a{WD#}i!RMb^3JmWG
zCU3l!{AdWTs9@==pv(JR+Z1QSbH3j)seC_nBu;1S{;#2f{FI*Rouv|Y2i1?GL2LNo
zW}9*cnkJT9|Ah$dqe0oZlkz7mmC0~fo5Iqa^F2ED(c6Cug(*Nc1%7MCK0ZQD5x(lZ
z!<z;;Lpw3^6*uhg#fX@Z)C?B5r16oaOs{*nrYqk4@YZBc6j7kR_n%osz6bwz7NGms
z=j_q)G_MoB>87Ml;oC5P2~0%=$Ydmvsda;Z5hDUOA>+hY^C<qn9nE2!L~Zc4PaT|B
zSPvnwyYc{hJx|74s}T3JywEghJ*;48fN1@-@kxpr)R3f~w+LZ;xbnSN&(m(BZI_vC
zl#Wdf=oG>qOjg1mXnQ%gIzt~LV5aaFZLsVTy(dj?aRqB@`bFnXewf?G=kHk4VKN;j
zFeP&Lh?ozN#|u-o?19n7VRfMdv`XUYFj4uLkg{U&T1$k(W*q7kM_LN5?xd^h?=?Kd
zgcB!!m7NCt4D6Rjl33TUCU+E&$x2U0t8tqTz;ikhyD7sZ$&IXZRCBD{!KD=nopvI7
zgS&1d3U#TnhXW`~f#4a<`!s1LGvq{0c_}L32>q-ycdKn8dXa*mEs*B@u%uzXAYk<L
zWhABjG*T^vgSf#{Oy?R{{GzI3!(xPRa@^SX22fOjLYi^z`O1Ol(9V+Fz74Chm*R9?
z(8iQ<*jb|r8lhr+6&OwuGN_DYPh9qa*B)n@#wR}$6|c@;SMQzf846(zc%Ey#NgI&p
zjfagUL&H2=Y8O{6Dl|K5)5Eo322-I1XTWMl3zA`FLzEWDCv#VYcL&s!&21~D#jVN3
zPdhW>5rY11LkdMW+BITt6PpK<*xmuu&8d}dxm(`(voJOj*?vx-kdT@OXA~XSZyXx>
zVsm#$L-`zOY^RYMln|g(oBu^Qc}r!j&!beuclEup76=rM6caU7xQ*}~_|!p_{b7t>
z<cZ;;hRshcU#Gg|dZ1Y-4%71X0}Ae@t9S{h&ryf~qL^1qOE-CSo}+uP$PsqO`wsWu
zId+=mXsaXpKvA)%oB1rP;O8Z!pX&mbieg&8RryC>t3fm5hTJ7L5XB#qZk5%QzfrTb
zvueL=<gGV7I(pUL1~<F5SHYH?{Mr1LX)xG9WoQK<6f#Q$k_)g2d+p`ce)-v1;ERZ|
z)js#!S!}w5c39cmCNhN=OoOPP(a@(;l`^U1)ek5wyBoO!A6<WlBLAAQH(g)36?>-m
z;<1-YQ921QHYJtQv&V9!;;np5LF-@#KSy;xaCxLrS5G%LE4~LlPz~|ddz!mwQ)|)@
zZopy<RNiM3Xl+OOl)+^fN-z!lAw6zlDgj6dkF-OI3YfTV00V)kg-Ac-D^X**FFYKw
zw*Y^Wqs9pXIIaLS5U8wwxze^1saNnqOIz@xsO;Q*#L%;AONHUyJ8($ht*MWE1;5s&
z1C)6D=Q7K)Cv(a%ak9HD*=4mA@8oMdIVNUbvC>u0UveI{6|ic~2dY9ZySkI*RP;&V
z?g_xuqDVc2LNs%yw*~kn94UT>#Z@#LH$<<Zed{s^r1%g=sH{3{%6syf;=L;O9m|kn
zTYm-Kl-P0NC^*iCx4@OV=~p>b*}KiGp${-W+|cUR*$vch6*vadw7or+(`pr)ve^#I
zT8%ZFkF*hzKc8%?KR;si$URv<rd?Cj*D`79%IyGdq<P&WmubJu2!Jk!c)YITK24Hq
zr5v)R5Tc*up^GRU=l~=723|j|7l%8Y1XP!utMUU{D_W_(yH9QBq%}4!)v1iToE;y_
zThUpKxu%WUuRQ;OBYCrlP%#tWe!o|7XJK_yu&Xc>bxTFDq#$F{b*RT$`E+8iXY=5~
zgm29(=M;We>`uzO1bFF(MPFrIT8qTK2fcQ8gA`VNwV~P@Cj;y?ymEEKs4w@Nhk@b+
zcRbIQt-b6Js#UL&S1K&{NLF!z2;>}69HVXXAV*sAhBy&qc@~u%#r@v;)ELYVrF_5E
zLapZ3Z0j%Gt8zAdxx_PWa+oDP#1pOG&onhBd585GUdPhDe|XZ-P;QCRpAz`hWgMUh
z43qiaOJ^TfezZLndofk@*2L*+&EkxVsprEllFU296n|%UKzD7H<`4qkc~K4;WR*Vr
zp~?|uc25(auK?Ql%G7KwY*U<p8x?~61>}N<IcmymCJY!}X(VdTdtx8RXq+^`KFXom
zmb+)-v*~;=kbOYRqVx3iTan8~YrG+ne|S-kjHeT9D5yD@O$ssg_wvdsm8`|I)L)()
z-Zh{t(RoVi&!Q~Nx^Trb@c8heB65cY>B=adW|(XGh1O-{+zvmqFr4tE120}t6PHP<
zGj(2RrlRi6o)(Y`Gl*u?;h-sX1^G@u-vU7$*Kud;d*5Tf<VO;(jb8u__WBjI0wcG1
zs^X7Nv^4W$lC}!gq-vq-m}Wg3d1bJQ>SM3peOks|6_hIKFf>P1siMyng+}!ZI<&&$
zTbDE5gkD5m|A(flaBK1nyNV)6NT)QTHUuP<ZpP>V8x4}uN;gVKH<JdD90Nvo2!b>S
z(kU%n()G>X_kGv3KVa|kp68Bp&V9cOrDbV{%HKIFDfBV=yP5(*q#A;%Zdu-?wTy6f
zmvzMFo;|@xLe=_uiypP+7=PcZ-#CmZYh=!QFqJLh(|*Mc0(jg|VLrxx@@T=+v`1qY
zVxHUfK?Nfd6PTkNrfjSVvQrN(?%twuVXn5=@D-&IhJG9Qf1bd8xQVBk##t^QL9<(Y
zbGXrOlGjH6nsOC6PRFB+qcWz3R&8g(SMPUIpEh(ob2{$?(*G`Z4Dq4Ah4UVS>WlJ%
zOgU430u?7HFh)nD|8c~^Ap}*9hv5y-wGx^mOIbDrj-L?e<kcCZ5lZ=ocb@<ngVVZ?
zi_Yg?RFD1cF9gJ@W#|$}h-!e+ivu+aJK$T(u`{qT&HFD7+fuk<CN?bqNq5C#rzii&
z`Y<|7hArCrGl+sHSld{8;56?LaP$7_z%Fgy7NCfShlbOc_b>exYiPD_xE(<{NquYk
zbQt^G?@tCk26jD#3X=25H@?qTT4yu#^@OL#m{2ve=#lRw<AD49rbeUX?`qni>qQ_u
zI|Z0kHD|=CK=ab*yFq)3c?t^T%xLN;D;Flx9&1X1+~V7^OBkJARfsh2W5y%`h7$ZA
z@&U9MydLnNW`U(KLq5$Pp1?6G8bdkypqyda;RhnX+0lQ&Pa_x@DE4)129ANZ1R^dc
zxnFTH1d4Ix)%@LZD1muMjg^e&*EBh*(;7`cC@{tg7^H6fC!?MM^r-+qGV!mj<+Y+c
z+uhhQ0a_em7j>~vV#vM1dw@9?5!>}-(>6W96SSM~WEc?xxKdA$`SbJxOfc2%Tg&d}
zuAI=L5ED>ARg5XcrOO=N{Y1gajxmSxjbwsD1MHn=f>$7uO!G6FHv=s(3n}h^siLZO
zNz@#8*t&N=lf!o=CJSnB_9KHJHn!d`A}V!S7+ffTiLWFb>)4Kz!W7A*TsAbN=xeb4
zBl_PmKmN^5m?~ItoR48w_|9A|N3UdO4T|@IXQ9P=A5Fq*t%G0CAC^D-{LEj9U)%Zl
z&l$Mr^8sE`wgH?6-zW?|BCdKfZBKrA!<(AEJkb!vu|M7ono)6N{cCM?X*+=^f|c)H
z2e6sWdYT9_AR>M#S=o52m1LZrGmJKVH%8L?_BY~{?qF(&56l>AJLblx)ufj|%OHZJ
zFDJA?D3q^<ZhMj390I1hW!3Up-(=v+aLbsto<T}MS@_~Q#zmSjNLTs`KQUZV5TO@(
zzE=`F+S_(}iUlHUy1CytK&!B&EO}n)ZEIoIA$QYr1cMi9I|uO5<8QJ8*Nepl+p#b+
z;NLDH=5MCpKbF;RTW5LzENvnsYzcOyz`4s?XT=FiX5PxNMFe&&Kgg8p&u{L8J;Iaw
z=^st1lsgY$jnL2Ll>mWX+KH<BIeFN>&F-qn7V3Yu9CgIrOtrhi3zOb+!FOIrYe-7k
z+%19I+ne;`$&?sEe}zI7+HUvLbxMRTJK_dKT1`Mrtx|2*-~7&%+IWYmA5aHr_^;&~
zU95VZ4VIOBK7;9`(ZJJie1X+9cBmG8!M&iNKj(RUskV&KY8v7Ohzf(%p51c$Fib<M
ziK!%_bEtZj8E#i&&c&KLVc`Ru{s)E*uXuaHe59r50N;FW_nZ7As;f3zG6L^D_Np2H
znnX%JpAxyPKS@ZlV0mdu-CFroZh!53>BWJB6u6PIN*8@`b6bDHLYB0Q#{a@0X#nY+
z+T%tU7nD3LYXO1;R{P5}^)SYxL=KxF?}@Bi$Dn2w60WOHO&nvt_;Up+L~$F5f-zci
zvm381<6|?v3rr*Mii5=Ym{LKmWcw~nI1wDt#c`IClTMMAxx@W0R(Pj7Qb{khW`X<r
zb>s#DxuH1`1&<a~5LXY*!MEY;){i*Len5{{l@q%1+FAR$U#EHb)!lh^6;&DE`%g_g
z<4_+?*L~{##W#OuiHRV%j%aZ`0f*_IY5xJ0|KC5Bt+(}*U(1x_N~}p8Hx?*7<so9H
zJHjwG+irxX$?Ew7g29;TsAJcqtEh#HUqCKG^s%#4@>>@6_w+R;WD%-e0Aa&>e+@}t
z&=D(_z_-?xyW*G%gHo`A`TapPe2C53>btTl|E9*f)U|Nbzt?hz%ZRBr9&7^46Iz$G
zTnZOirpsAMlF{{qpPyfcWE7L{r2Jw+B`lVI(yMufC3gy1@|+}aLeC_mPV6NnGde}-
z*g?6HcOK1HkRTDLW`qvV8d!YxNq3e<&#&Ue%cbog1NQ<D@%9(5tOz!1L}%6Ryzg`y
zAxW_OHPZ)UznTZpm}m@8T*u75|20E#Lb;@7JqgQi%0A3OruFD`=gV_Uxv=Z+)Kn)l
zvg)X?s8fx&*+l})w0LeV=bA;39XbF860WKIhB{Vc9D~qi57d{)Ua%|u)l1fb(-!1B
zgj+SOq-<=v&MT&u)&RjU{n{|1Se3V+pr*<;s7iAp=TALc-=FvcBZc0#hT|}JXpp?i
z>x9IEUtN%-*ZdEDhezmrHzemR1v*TG)rp`o)6=QaBzO`tpS6+E8e!%xhp&5V)HBj_
zFg$)KU_<-Vfr~O>?Z}qQNazzhyQ`8bO|;pJhwAi6yVSf>K#GcPA1^u$kNZ{dnal%U
z&5zj8xxH6S;96j9<7)vqc4*n;BhW3py}jB(wruuBTq@v!divC($+hIOi*Xe4W&&0Q
zWMvt>lD2tZv3j4CjySme#^udlU-?`otpsJ~IC29l0Fl^<eSs7fu}MnV-kEwiFFe+Z
zSp8LaTY>1ePH1P|ZxF&v$jyb_$~>r`!pu^|e$`5FnF-NJgeW_@KrN3)xl;`Ixkxvs
z7(c8+mCg=wVDM061s0!h!0EK|rBbMpZpsj_JQ8FGHSu>#!!Rq>#LaaCc5ijdk_#@D
ztq<j(WNBVEy>0IZ9x0tteDKEv@*kRx)`bju3Vw{NcBcq<?U#q}ph=ns7V?-i7Hiul
z=+2)5p{?8>a<8N|#;P)lM{0jXmXxW&>h8?Db&S?AvC^s;P=I52?NN#G=1nI%GnkY~
z?v#}2b+!19Jv^XJ_jMJ?w(%1C;E$OWdTCWsUNhETN3J+Ixvi`JfDhtUAWc|U2irM8
zr;jp0;Edyqdfk*Kj<k-F#C^_!_lsw8oiDz<y!XF)-`Ad$bR%YOZGwsFw%xjHDI(RV
zd}SLm2OnAU>b`}0rLQE)5-)j4>%*i+!@I260d}CKzg5~xIl>)!2z|ZpZtI7O?hpR$
zLJE6`BEh15>FI7&F@Jsf8xqV#SS9#wj1Q%CIeL5v?Vq}zh~&O)f;k5CSR7peLxuo0
z)Q8VrZA@*%4cOVqvJ8FF(ajjCEuk1VwJWi__16UGGhSq;=FGQd54Vol_r$3~j@P1f
z@Ym~HtA2WVZa9et#)Q#G3lMZX{Gn=Tuad7<3O6f&ZVacPIoMNuL(8+2)%w!L^IqGD
zOfejqeMQ&dy?{X``0QPr4!p^dPAP2(3s&lkdr|Xah^J)8+qpF5`bGav&^P7#)g;Cw
zQ=`k(pUs7?72*k)yF+KsU@&!THCk-AX`)+S+(uV55O%fr=xtP<zS;aAyWrY%j1{@+
z;pa4Oih|#yS-$&~3XLusIBPY$Ezm|6{c@Y76p-h=?EZ#aox?-=)Eqk!7KIF*<n8TX
zWLoQ{Z81fR1YXTEmUbRewQxY<=^Pdx`Tf9A&zh8^B!>+=UB`U8iO7a3|6<YS*O}B%
z1MAV%Qk-1?hB%lSoiP}tZeNwl2N5lb*PJs*W326FgQ*lxI5;jVSEM&qpEi2m{c>?C
z>;t}I;irllsE(6hP8o~qTOWMxvUh96@b2%p(uOvRK+kq{$qlr6*}0Hv83CM*7xqvM
z2{tq<zFRcC%)c3)3NS6171o=bW1)*tFc>a}463Df?5@|+M=&ZY8_nT@bh*kMAY~4Q
zPgHb~vyK}9Tb=-)u3?O$j?LQzX6KW^{xk~wve_0`-!(i8q4U>SSU2VwJ3RjiLqfc(
z7mw+3()gC9PE>O&%Gg8CMF#&_=_3kBtrw^E=gXfUhGcK+Y!a{Aib}d8$(Kx1)|58R
z_QJkruh!#BV^+?6Yfu&rWPKEZVFC6S@9eKj$}zy-L2!(u9T1!6Vpg7L%wgZUqSJNF
zgvKu6K4Zb1CN+=j-WDR@HI!I#=Vjx`HwQU>(7R!g#pz~HM6vBd2P7;h%a^F2$r#-R
z$&$4DR6n(7pXy0LLdJk-Z)U0eibh=i)gtrQiY^^jih~G}B;Cvr4b_`7N&weAr)DuF
zke_~}DBa>>+p{<gjQm%^E98Dv{AbbGh5^h)AyUt5FGiY{9e+G~2eF|SL|+%py=8-y
zfUW#(bh2wq)0>ZOjB(B$efW;K-``6$i$}UkU~E;D%(Y15LZkkxmU0Pi9v!cBPxmdH
zAtUbDr=za3btz~#2X#~bmrn+lYHB6&P(t0HCZ8|ucA(d__xxE9^%GZYy5ps6KiPoo
zuDQgE!_jCEVY1zePw1WvXyJCr-`}J5I;}Ak%ACez$o*+bIpeYlBZ|@fviV`Bi~J4*
zOd&14&!H69J<%dhb_yx#49nP9T3b~DNM;Bw>)w^=R%c0w=$52@?zTgkD@wc#)jK}Y
z*WN;!au^Km^3O@q7{$yA|8yOY@CUsFis74i6$j(|&`19L^94<VX)c9fELue6dS!<{
z#iU&2c?ubKF5divaAzT~{~8svoFC{-z)7?mp!z@cawm5A83>!*rhQQeyoay2sG|A;
zefG%;gZlHzJPZ(PNM-+183|osysEs1iRU&f6e$<;`SMA7QUh`7U8-LM-96qM%poCa
z$L9=_a%Hmmbr`u1>M=kNBNDtS1V)YLA1*dMov5Ms*_=HS{X{ozDE))&ze+lw>yzQ^
zY;i6ev>p?L;UrW!g%z_inveXhNrfMTWn*ICstTNSyrcwNe)&aP?hy)nOPGXggZyN0
zo!<I+=48Cha)E`VK>K;{{0=QQ7neC7=;(N=67sfS5|5{BeAb{W=RtJxQf5}l($&Yf
z{r##ZQU(a}EcH~@%yFbXk^;B^m{8hE3NQr~3G$UVP%$p7>&av2^S-q$Z!&({aSRh`
zTZfWK8ZNLa{Q?ZAunpoqvEd?FAk6j9wPXLCerPOG`wC7}>NlbF22>zO@GD;Zd(~Io
zQh~s+ee}R&j{n=BCK%#vtWQM8-h<Asp~Tcm6Lzf<n!8id;7vmUM<O{?#ow!Xkty$n
zQ87_ZNLXXa?oy5A&gXRF$_StJ$&e=}%(GuX7P@^8=x9?$0#w`X79%rL!*^jX)B^tg
zSu81fGEoMVXz5QWI2_I+x~y5y2nI;BGYBc<Cl6$JNDJij4;S#eqG_?d8dL)NI5xI}
zr<WG4X^S00E0J8<M+FpyN0f3?-NUs&^nPlEN?4}`UEp|I>Gw@Fd-J4skOOB-`ZC=i
z_FYil9)D<9gtIz~;evSMI%Bc^O=f+2g;5V0hKe!>-FXp!swQB<kXpp<?zkc0M~0sd
z52ZB_$ESG6mCxY8JL6v%o#E3q)DdUA1b7R#fekUB@8|6PG(UM`A_PanK#%z}XCW8$
z_x(g^m#|p_xL4r9O*5LbUn#68LyG2ksKn-uIPhfCOqi>3>&iYY&0Du*zi!7iykJ7x
zT3z=MVXT{!d6UZ<LGukgwam<!vT}ZbGD-uSLt4$WL&!G5(7<YaFUiZb)<6fQD}3yG
z$CyJKF||U6dWQ7}!+-M?+ErIWGcuB9-IA8S)4tpKXjsWvRc&EFh#bfIo!@Eg|KxM_
z-<=ypiIy?q0VyICP|e@#(4lwdvWA52)P637cf21G@=>G#M_pvrq2G?bw;)SO98x@R
z%AYU#Aw!kPxNVz_-S~NG!6(w33E9ZN+_EqOnf07nMEJy}&Wl~+U4S(Ve?Fn#CpVxK
zq8m9=RycQ#qusozo3R(g8y+QjWnklV2`YuWHvK>m1$L<OUq?zxiYnRn@{BBa-JMl<
zVtsp?i@_J%`tpJjfm4|}9!r-1uH>w}&uOhEO5AJN5eqC@YA*6Mxo!hb{xQ*qC&t9E
zkwEagWT3*+3N6ztA7(TS&8G3Vm0zT)IU?%q>bY(1-_(1Eu<Ry6rXSM)xnz4;o|8>}
z`^+Zm;9#M`|HJ3#w=Y_$3GKS+f7N>J;Gc7H)089W+Eipy=v!E{d5uh`Rcp_b+;m%-
z{A<(JevqeJ_HKmWH3DOON%^sQ!9*kK<XT08ACg?pg$*uMZM!BRBVn8e!W?9D3NneZ
zsCmUZzsNT6eY;qFFs`C&E94pYK$5f4%P6t&q0jeTO<SFvu~PbdiIfrJZ|&SI#*4l-
z#@RPl^@Af5B2z>C!C-NupKK)F3OH)9(k^#%?oEeI3y6MwDzkFM2Xyh>6!))Vw<}jt
zOHfPXW+HSB>Ar-U=U>p&Sl+r<$XhUg+Bgz8AFLrWpnfS|^jXExy{L7T$*1PsQZWF&
zt!e_C5*}>(n#!=61Wr6v@e`^?vDm~qhE8Y<`)zM#i<RSWgk&0VR^YxalQy@i_PbK1
z8bQyC{{)8ojyXQd#inL%{)YP{qbBG>(7PThU>={aUbQWjpN4yTJ==*k{|P;jUS;4b
zi`wHK6OWeaFTRD!NqG1&wqZbTSdi<0Fa3eQeyR7jgRyDC&`v#(`;Uux_lq3fdA5Vn
zJKxvmL-u>>oPJh6O%W<jSnyFqv*p|(A494<3M#c;T7>hoDw0;J;g()y(Ie45D`D^e
zMcV#;P<XYMD2c=KybrbsS3U~c_H0QtvhdINP#6#$zZ*0klsu3*v)~xoliH+1ur_Uq
zPTiY=xL5PDj+txK_iTstbVUi|T)mEcMk@R4F*YSL8QxXdpq?2wdw}FfQ5r$l2}ooO
zo^g=q=XZbad(l4}+0y=sBga^OwU0ed=DM`AdzYGVoG2D-D`aQf3afO3ja66sL2=W}
z>Y!Yg5$)%!$2aX)Q&6w!>MBk}{A#L=-N1uX1WrOGWZ1eu*ZGNZX(|U|-Q8BmU<K~M
zyZzv$j_C`UzI<6+TOI)Ns}ItACZ9%2b>=a7KE+j8#rk+-P>o&3cKB%`M9|b==u3Zx
zN)MY>Z_O`l>-CNu89M^m@@(bQox_W1C+?(VSb<ZQ5x(t9o4~I<q;Uv%#oL$n^q2?5
z6RW^YlLsQcER~Gnw&BvmhZXW4U)wwHU%R!!7L(xv8OPJAG6Bc?u89%U^-OC&^L?Vf
zOE<{sZNVEFvd~Jh>e%sm_lG@FfFa*a4|<-*<dLJJF`);D=NW}cKojtDneq8@g?><e
zb_oV={_mQz3Mg1RX)`9W3$`P+YLqp$yuPSZL#Y_CP(A+4b&1t!9^3d!s{&aswQxa|
z#zw^Y?is@+i_`q8!G-yY^BfjGNSo^x{>J>>KAmQLQh<@mFlhan5(Wr~fB4(kEku<m
z8-kMWOl~CO$7K|6W;y^3(~z9{q6Qx2R6jqBypbB-<F)*CTphK!?w1Z~PGBakN3()U
z+~V3O0ab+?Vy3My20)0aIC^MkK;wS;*Vrqn=vidxQ<aQKkD=boL%R@T=NC4GK~;Yn
zP7;udc$SlUcle4rOS60y!STk_0@3S<iJ}=q`7?w2<&sNTPy*%iHHTp^^6#ZAJckmT
zlg1x<oYt+++I;B0-662?zB4b5_`OV@e=*Abue6E8>l0*7v&%S<DsCD#=I*6Q5B3KG
z(LNN7NtM_uo5kxyYZKo?W9htXZPWr*C+jU+N3T=nAt<h#xfZT=k5$5xB_4r@kejsy
zoAq3-v(f~)AW2X!3Td%p&m5A;$5a`x(SAjO)UcUbfN~mm9(9Z4g)HwQg*jDns*xib
z`-t?)56pEW?oxo9_@ABAOqMC^?dcQ0F{^P@wtTGt`kDNu*1i#`fDQgclgt^mY^E}o
zbE+Y**`?iis$`<xETWd`2<aC8GxAk7$=BmZ(7&rh_#eW=M@sp@>J0JVKM7cW5`C=f
z{|*?1ts`6-n_k@aoKCG}&Dg%q>F@X7`l3Pz#h+qEQc%Kqw)9jb{qCY~wq`%IG&H>@
z&3VTEj7bf_yT3@%&IWyfJyjH|nGEyv;po6+ws-`X47@$RSMlDj&OxYFzX1?KHHnqY
zwbj5Zxgx-q83{jp3Vf$B+H~4l*(0{11qVj)lf5`iN7&Tv$zl9|&0h-qm;x<_ECR95
zZP`}~gYkp#`^tY9DA3LcDH}za=#%C}Ttu?8%)VK>`_UB*SwXeIJ`c9KO5ksYJzl3^
zwk*~(BXOCP7NP845Fk+*O0vKveaf%g5jet5#)^<?T|*3@>Xx=$T~4Iqj&5BATzC+P
z8cR*Ah{=Szi4;|SF_g?3u`zumt_tx_Q%_%WNt^qx-5#VYUHoP2)hTl#g##;?lK9k%
zn9?3st_F>^8x-^uGbI|He%`8<rJ{i0m6kfB4fuSl^1?1S+BoXOLN4;>g>imRS}AGN
zCTkz%H-^U>YoTytO+rtGod4eopj%!}lU5jds?~r3rB9H-RT4#i-FrT8m`m;&u{;Mg
zbE0vX<-*!SNnB`)MQ@)W$=4^Su@ct$LD<I}+sAMC!dqx5so8G&>a*xGOt|SpqBb=6
zz%rLIvw~FzB`wTjrBO`G;rBd*T3Cz)+^ivVw7()wJBWJKfm{(tb=jxpz1Uo+@dtTc
zChW{<*Be&bP%W1f%2}*pANyu>OVD8?6~7{`5GW>=JBm}M=g*t<*+Lz(V|tKB`$VFb
zFgRXe*#f|~uaNR1MRcq3U7`aqvlV0oewOv59pbHkOM|UyM#8`BwO$2o<PMmP$h1Jn
zLP{$ikYckSP2uF01D9^lfRi#qITb$of);3er!4Pek<&uLlxM%v)pWlD%bQqwg_Gwo
zpKgX@C>pgSj`<DwA9x-vSHUQi3xhF1c-p|7WQizp!H|$7y4L!*Sp<fDe2mwsACs;F
zA2^;&X=f)rkTS7gcob1iQczk~jwY}Yi?RFr>erh(ky3xwVC@CToA6gT%^eq2;eH28
zS98a6rdKWVVdJ_Xg0f9qFOy#0-^4^mdKVhvDZc;%u&X$;pGwNgP_tQAe*|n-SLM(c
zmkQ^5`2V_b@F_N8TD@I3*H{p8glwEcl;6<3%uYK11}fP~K<1eHHIHL0lyin~dM5u4
zNru3=qOtsqiJh*&rmg1d6!7PO5CZy<IIn5|CgrzaTr(LYGAGO<cWmV|yFk~l@#AV$
z5;~E1IG9alm!NaVKuyx${`6<(`l~hF)YU)TKIQ?63^`n>PCNy(c74t!%?c4hEak1&
z5s#cNrrN!<CGXOv^3a*D8GG3i7CN7<^j9<1ylY(Kd2^q|oQBOziOBENGY3+Y+JSM?
zURkHBT`n!Qa0hOpoWtkZV_8lclx@75ee&Z&?3~=rE!alZ*&-G}8D2rZkwzx;7j76k
zI_``1v1xy{#bJXE$x|{ou{}6J=1ZfQskv}WC<XB-8qtQT*zw3SJ6YJQD{2pLO*q~*
z<9>lQB2S274M^1|@wV)Wd!PKf_H4eOo5j?%=rJC*EIR>f#kTtq=I82hn&~4EucvAp
zLr7h4eR%m*zsy-my5VN-Imo5f==IOs=-_|w5sftA&fFo&&N|@2d#@fRKq1^0tz-6a
z4<W6%ZvAL0WVCa;4!o>-K(1@6mTy9lCOIGpl2zwoZ1z_R*g~6#<oQ_F@TS<Q0DIZ;
z5kKWZ^L6*smGeoC?vV`~jn^A<r1mzpY(61B$s+P_B>^3pSFv~8kD4kC+Podq#2>bk
zmKsr;SP^fP9IuP5Od0N7bFL4d3YUP`eV-W0e5&nhh$ZwcTn+>XCZ+!J%v|1GqYKCS
zHh-+?o`xfY@*X5^bsjM=yyx?`Poa#W)0e=07ZM+@3`irFvb_(&^~RR@9pi?esUqUB
z6gXbpj51dKBGci|_X+2ydWizBN7*FeSCmk+jy^fKf!Y0ZTJ>I?%=pC-(`GW=ORyH3
z92XX#LhxlMiHsWT4MH}W=@Xnl)iTIY(}cvu?s8qgb09|o79V&UgC0m;O5AqgAlZdK
zw?35tf7y5vWYDJX_>M}=bbE&)LZd*{4&?b}IKS4QV4uVkkV9*4mGJ8G1GmR;6d6mn
z6{+$14zUfjN~y^b-WhVs^C?GfEi7;1ZsM8MZoIB_HS8<02JhTlW|A-o#?I^+aP<<o
z*#3YB2YK@TZh%E;FgjcuKcbsV>Jk&o`8(d;BbX7}`yE)sh88f(+qK;wabt1bxHYl%
zVq;|Tdg!Ux@qEm>#X|iXViViLFJ{ldl>Q!8j$d9xXS6^SC!(;GFLJ9-t|1M}bTU5v
z?=9@#Udw#?z+S+2MYUhiPxVNvK>FP>KD{2I%q){ws`=dFsTi}o{2ezu{#^Vj^`?3!
zWgS^tRpWs40SQi0aiO_SYpZBRPe@;$ATkFcrLta{_o=WAhBH2fTQmdv(n`nocf)O3
zDPWW#baJnKU6^eEh&h4veI<)LmZ2F3-<Hw8`dMj-V-rG2yFqWG)}b;YR19LvX(pwd
zht#d*_EL*5_mQ~|r#Kty{W$wnyo@Ohjog<=I#YwS1}`kuR17fS7X+%p<a!~hv|uCK
ztA6^Sl8GP_ZbR~IN2ezedXLU{W74D2GR4nh!`35a7F=-Tz*6pa4Bi_n!N&<xCc^wX
zfFMC)7zG_Am{R8FuFxlu@?*&8<8eNpN~SN1@JzA8qm{PJF)mm2zSIxTK!8%F+=@*9
zYRXdaRIbP7AfU1?CiCEp5BR3klgX;HJwIx=>5@oU+jPagoEBM(zt}6QGo!`LDCXxc
z8Rf^`s&wnj&*q1{l2I+->ksSFc{>+x5ipP<+Eikx#D6|q%Lc0^1vIwksEUu^Tc$3U
zY#q}p=ftT~Z7#N%UDZD6P;+Y3K|4dLlV^mQifj0l^}V+x^#i8wh9}?l!t&m%%^2Bv
zwQmwvY4FV|e-v8glhWu7S=L4EJvtusUXOiNvUb?=g%R|cfpI&&&gZhZT?%T_Xw+-D
z?i%M8=eKsVqIIy6`%#{@um$Qy!263;S!DmXAA1vr1vOQ<i#^=UYt(T4vq!8nnU023
zP0Xa^7Dm`C`lc~%w`)7Z>)<=yr>7qGB(hg0BO4=q=Ms0RH#45bi5Cz0Sg|Ew7lH*S
zydS@w3dn}*=<J=<C8$v=bpbz7i$r&#g`4r#DUu2yKLEFN`m6;tKU*ucgpCb}4YX4t
z6H^HnSAovuIR5aX1%<y_(k5@(Z+>n^P#bsIMI+6-@X+xAM8g9a?T4+<TSFoUOR`Hj
z%%0DNSJV`JSfCV3>|vILrMIHq8$xpZxBUpaT~m;DMMDF}7Ik{c<d0B~k`glEw-8Q9
z1_ho-EqSJ0YV!sz%L6L;g^k^q#V&ZeaG33cT9W{1qqFAOm47+deR`An`Y6MBf!4cE
z9H!V}fK*?DgokmxzlWQ)vE8rQED<Q)#W!@~eI(qZQycPxQR)CQSkjg$$uJP1Vw^Te
zxnDm{BzG=V`4vm?qv)5SYT6>#oIb?>dkm+ugD%U!oYvR<?iP<YU8ylo4Hfa6eOhL>
zkEyct2xZT13GHL2$0@DlHEu;HowiC7Vb<tdc}ky}7Clu=t70snuQhHx=Cb}M#d2rS
zHVId(xq^uhfcEgnUwjJ|PD(Hmz79PXcN;l>c7p91j<|eQHM=OYe-gfD#?mtog}G0=
z-$eg6&_3D%0#G`*GDgT^C*hubvz*qy<^BVp)+N@!qaxOsqBjpKkC&SxCE)GP*$wu~
zjiDU6CVi5=l}v$r>E7|OHy_Vd9c`4@F&ir4-b8DvlSaAfCnySW+J=3DtOqf0x8$(-
z_CoqWvc1fg!JVZy9lmXbk{(k+mzU3{EbWTS6}RIP`Hz7qf}uOLhI7y!yin%&-~9f1
zC$Cu(japP?Z~FI-Byj-<I9Q&UJ~Z*%@;$`hv{3ndt{_81>AXL_qj$l1D7PFA<=yjh
zxb5dgzA0_<Q(7W|;P)MS2hAEhQUSxbAmTWjPW-2(jcGid0)N}Prq%O;V?!mR))rn1
zstkZ|5-f2Nyl}kf&X3f`fKlKp35h4yGO-W-<zm8<Wggof*rTqY0LnyoLkp^le#&B=
zSh=jzti6lic|KZmI)IpASOX_7Y%as%^6^(QdQxk0^8%__HuqX1dhZqc@!g52(39(q
z>J!$K;OV;&p6ryR#!CM25S?*IGIeej^;$8^O5E1(Ipc4p(i&+ROs<s0!?!K<(FL@B
z3&5Nk_!}gY`o+!VM8@&SB8Bqeyr2Zkk#ylcQ0cuWR0A^=V=HuPM(#}6y~lQBucoY8
zq-KZu#M4epY?&JQF%#(TT5ucIGi%V7-yq*}v;E>%U}(^(JoB&5+Orn5C);IbpR|Y%
zL<|)_$pj%Q_(9Y#+C)+KAJR7)M@|Q+ZZvgEsy$XGQq7#}9E|ZWVJ>_OH~(e~##I#b
zRfi`-o-!Y1r&_}GBTXf$+nvOFxrXjw?6|*JTWjZEGN%_;_?OP!)L4;AQroYTOw7JO
zGS&63FM!Axn29xUlufzfO;<T9n3yHqJeI-hhympkI8VH2JlH@dE@8082A_3{YzNyU
zFFs+JWGHwHJ;>N|b$;;Lxod%Hs$NmD?bUD%JT<J#g$(Xqr1ZpUkJHszI4Bv$z1T?9
zKc}T`F=m*Hz<Fth`w;eO)d58}FD(8UjQhIw%UY|7rT>Xx;w%AF_ed4GJsffLyse8r
zTc_MH{k>eJ?F(?>r-KX=WWUQFBK~X+6MpN*RK;zLi^@Y8<y2d}s54(P6<U;)z+G5^
zEP0($L8&IObD{;Y8MRc6MNk8JW^m6jf_FH*i?Z&cW7^)J_1e{{=fKcGn@VA1b>a{p
zropbxAh9k`uw-Iq<GyDd(j83<6bS7Uzpo`u!JMQv>rU^e^hbr>{N?1jjAz`HRS=|3
zm)tfmhl;WC;9{{ACvT+>Q(m8AVL8m3J;aD7Uwv@tdvm^nM}kQiM#XWjx8#C<ciL}h
z*TdHBUN>EwkN+(3WV9WknfW*<%YqMjW<JhmO2E^D^R1*cD*?{@5qp_}s5Mp-&l$4B
zRe{UA(7>|uXwLpvYTid>zwfHBxx}aJBusE`5UT1-`B+=hv5gN4H!WE|^QA4vOJ{iO
z7YM;!7#c(MjOlPjE8SB()}eZ@!GAuOUUhzjm#C|(&3&+sq6wZaP3=OEZ?2sYwaa-d
zdMg$V4@UTl2wm4#wmJeRA5*ST@XT=`@+r2VJa80F)C8Gq?}vW?vNhw@)Yb(S87YUd
zc+xkEDtZeMY$UCo%A5es)nmlK?DX~2<CTi}9VRogG`|_>`HnA^PrCj4MAQQOZ7l|(
zwX*93VdB<Se&TFZt)#t3Mb3wqr2Grd$4}`P$UqLt;Pq>WRLDG;FAb-f^QXZy)%irk
z`uEm_Z`<y+{IhSmuIQEhH+G<MB}j%*fo=DcDvJqewj?!Wl+g~vig{vUzN8M$_E*BR
zaGy6_hw8DeMcD}sIgyk-4i!{K_%!r=r=F<O52}azMGAW|`o8bPs)Nj#S!2#6n5Gtm
zcr&7|lsXClJEv!_%&KPuO}`lIwBU%a-sOb6QECgoWzGx1ii&^w9i?*>hrNx{{PK6j
zYM@xagxgbkPg(4~^mB?$PPF<{Vq34__J+#yeyDlW#?jR)&bT3RRlL2Lf=V8qwV<PK
ze|3sB@j4;h$=<y9%AEPtRRSG*MI)z+UKau8Vs*I8meS>z|5D57E#1<7Zk*`1r553T
zRB{n`_QKWyrn-#><?@&0HDR}HD%AY5_#rd0N0pHRGWT3;0m_ilT?Ca$95?}Z9+c|8
z{|RVhDs~9Mm(WFG_K)QZWdNv1VGw^5OXG^{l<+Ch=dBZL+aYVq9b+4-Rk6qw|8s>1
zimI_LYO_Yx9$NJgipbfK!I1&=M(!iM!DxargKo(r^`(uSFk*hlf6P=1RQbli<DHf+
zDgus5tL)5M6NXWXJzj^cPLlzNX{epWA`wseE*>zu^U%Awp87fxs5scs;;%-{;s|e9
z9?HCM9LZwO`)Q0A#=*kR`v^8_L~@Hbh7P0^vxG!bOQj_K@Ll3LUaXHgUuuDyqn8rb
z;7iHVC=oy%B_N;nIpVDU!-Mu)%l&L&37B!0h4naEzdYke%I)ohv&Vke-1YmV{8G1x
z&ha!Zq;qo2H2=6Ew6%^ot$XI|_9m3eR?<x!xa4i}xys#k_V^6hz<V*oo6(o*;&DPv
zY%{Z8X>as-1Y6;V*f3rXZ1G}nIQ^w;G=;2Fx5dkHX<1wvuK$W)VlF+BC#YxYgl8Qy
z-%S^YiO^HQ6a-2HlGCi+K{|bi?N}q#gZoDT1rr!1RVGM<4EWuI%Zc|HKcyl&4u!TB
zsl5&gu;PA)Pqi<pLp2qF)xFJW(L>EFief5fYAbTk;3L)7iy#i&{eyOa1LTV+oT#Ey
z#1YBJ(3xDi%Z<}u9D>C&;{&>W$x^?Q5}kSv-lagzvCvF%##hRqLX9VeQ}Ab}KBSVK
z*BfQ%Hqo&?Icp)t)*ioy&Q?k*Xv}fi*8{5E3^9wLzqiB*fjX!e*T}|n3LRRvG7hM3
zRfgAtj@@6C8TFHwT~B<vh_Tk(X4tuVWYzSFEe5(kUd!6Sh6$#$&ZhB`t*(kjm)*bb
z;bn^2&?%>{D4>_$wHPCeI5T6b{S_X70!Ej~MUL6hHycgy!?8-GUK^MacgC9=60eOT
z<&H^C7irO6oYOUdT+eYa8nY6>FV!p)Phq}Eb_p|zz<&Sx=FxRP$cW>jAgRhn`LT()
z_#d=F#N^;Ex+zGorTB*~(H!-D=Qej#R%EBu0q=0kZf!1xJ=P1Xsbb&RXCX{_au>s@
z9Ln2Uj10#vXDVAES)tE@dFXb^i*kiFM>adVK6nPA!(}eQ3C~k}3AW*?bVe)h4#$qS
zw^~1j?+(}Hi-1;cPw9^g+iL6xZTH6Wb)Caa!75+lpF$>I1XQynYCM09Z4l+EnCX4+
z3RLC*p*=ri)Yrh&n`Ua8CyL^4jEK2_y<C7ww*ncX*nyw!*l%hZYpw2{gNRsivAsjJ
zIwxWXtFt=C$$7-E1NE6h=s5nj&oX&)D$`r+Bcb=6SnJSQTl_DjCBF<rrDIPJDB1b)
zm78vB`>SS9f=iys=Oshq&y3lobY6zM2c>;fbubq6_iaZo((h_=yLU{_$Tq&=w@}PW
zD44rfAAl8y)iTU${Y!EB*=&#d%3+qIndgMGo3U=)H5D3OhjoP5Y}b|Ty)f${OVkoB
zn21ZirKKd+WSg_X*2ku)ZKN+%#eKT|dV&mLP?j}5J3VWMepRfnAFme=CpNy)O-*0H
zWoEX_|F7~eqx%{COqh1X(Ggoh4{YV=M6189i^|f)W#e?mOb!Hm?BJY_SHgYC(=Ziw
zmEtiuc+QyFYRF%C2V#{qx?R4iZL9yNNv%tZ--D&B2)+<HUs|d^4@-EHR40KM=vW`*
zT<t2T)a=cUxj28~y<Q)L4)}e`3(Qi$yieljTN$HT2XnoFY36dg%a&van=(78F^fuR
z&Da1RB)pB=!G=9Ygmj=@LT|2Mtkut$L{{<XKl$gA^EK|{RTX0B#TWud>uv^Q6>4h!
zNnExeM+*op9hjiM%=Xh-Ow0PL&!PKKt7ED^<4kz&k(8wTuTWL%w~V_9O)D5^(ly4b
z4PeU6oL#}s_=#|^r9v5dv3$&PQrTXvCoaI%Qk@ub3K&hacXp|>Luv?R<jTuu;bR^~
zZ84!A<M%xSl)3_05uu~9<c)Ig-sJNkRC$h0G2ABGf8d~LCKqpd$_IISWOGeUy;^r$
zzQj=-5t1Nrq1nnf`CKTeypt+X&y%gXYO*wKfT*8Oa@xjW^JhR@SbU!RFFhg+jTpC(
zGd*c4O2|yY4_SzSoda>`fnEWh0DY+@7?(Le%DM}$&fwcKn%cI;852j%(%)mcI0+Rl
zA8R_T8W#0yD;jqmVv<&Pg?}nBzo47ZVSDSl^IR(;C>(4^uV}(`)p5shAZc^<UP}5$
z?o#-1WG1;=2<1Jc2EX(CED3V|@iC;`POO9EjoUk4wg%M>U<j~|&s@(Ug^xL;!OV@=
zShwlxm+4B`Ur`is1D*4km)?#R-~E=`6Mk;j%DzcN@sY{KQn&38yQF`!3qHD|*M4#n
z>Xh>phi#8hRLmLnhz`z_8?fBDaEvtc9K>_-?ZBiY<qb|sLF(6>=IH4)z#6&Y6Xx3Z
zPCMbCL=QE?<)(R~cQj)L4H8LuA;e?sByrD~Ln*Meem=9+ErSgOTIjqBu>O&Z*=_Si
zsI%cjj8(M4Et)t2MR%o}RnFa89<#T1K;D?akx9(Y|1V2gYBohc&;3lqOKG){kMn|@
zUUTGAUgbJ)U^y{*H#0Ehk|%?6qKP7H#h&4Q40S4n$Y+}D;yE*lHcNXes0*h5xElNM
znI>=V)Q3@NpLYXGtAGm>&ZTB4t2&e!w`f1lkww&IxLr_!3IBHIdxMZNs|3rs>sBXU
zp$nRbavk+g%I?jA$z`>)-~AaXTi@(Gr^!^@J2X!lp`Rgoc`ZhWH^-dI^ks4n5*)A1
zX{l61K?+}XqgbN^P~}sKR>jZui;@nM>ixOCDTpBEhyHsaK@Sgk%sNu_M}E@w!oe7w
zzJ%bavJ;BeKG*;vdeZ*k@lve@3MX!ad@yxPIa!=or*%w^(@rUjia7}j_ORRj?!RKV
z(S;>;dk*CfO{6A@HptQ}{no$M%ws2%0bRiJ?{%J3s5=N>qiTW!=k*S5JPVl;85TPU
zLC!|>zhO!mtwuC+{;aplu1e3a^2rkQs5#t{bT8T(TU+fdiMN2I;>B5d;J3L{_+xm=
z+~M4~XoJ=Omph0o?(2mAqLJ4HTRx185em$D7Qj`>0(6OyG0xR=V38rC?ORkCu`oKk
z?bc7)f7SPvc0jGjY(m~|n!|GJn-{O5xdw!G=lN89lUTWt`J-rA&?$t^r1(~EnjIz(
z`mNJyc>=yLPUVy`xO;Gf%e)ryAR_eG<F7GSL^v=!3(5u7ivYiO8U}MmQ-AHHpOP_K
z-jruK63y|I?d8n<#$ws|t>Fs|zRp*u4kkuAVh4eCqVJG{vYRsUaF`<eHHd&Wf$!N{
z(RIbz0CPp=r>{A$eC~JqXQhqb^{9(dMPJ$;=!%oza^Z6N9dM+)%NX-1`c2P|s#)~U
zP~}8KR(2>J%BZby{bK>-AJJe(gaTlL?A7Fen)nu|H+Fnm|Bu!E{$R(I%IU{10#$Hn
zl>WVMKJjQ`bIS_NKUrU^SOWvRGKnJy<okX#!{>u*WJPVEV!83^G@DpHvY(!s>;$`Y
z&~s2xR>`DLJpOF6<VmW&U2Q`6m|`)>Cx*;ikoXvwy?rz9?r(l>MNJb;`@E|^jA6$Z
zmzg64*5xY!n7G;~+^Tgc&}oFeTn!-wzEj!;CaFI^LTo2Za)iWdebcbVFpr#;J#S5=
zXo5}UHOCm(Dd*f@!&ZEkIy3b<;)wb6|Lz2dIW791*MP07?@gGvut}Cw%pR7i7Bi`Z
zVtKK)t=t|^J?VRaTieygki{O7X+k?xNTOx(hbR?O=Nf$~N2T668-LX7!9F^fJgXzo
z^PHT_e<kHy-gCzEEq<_n)5UppAnN~WU_m~~a8psAGN$nMHp2L?oejTNtFf!erI;;U
zMtK@g>m|w-WpY7r0u%HJ2FPF9h}tpw)sO2j>R<7uScP<7{mROmjjM4<OW$nAlI@M)
z;70TDV^k||cN+=n%4{z3=o(Qtx5t98kcf>U-ohGYZ*xl`Q<kpy5l1*N|KvX?N65bz
zifL~^KsvmZ$CJ0(x3Qr#)KqI=daYrO>f_=0n)#GxFi%uX`ahi&{aM7SwWB{u`2p8J
zj<L8i-Xf+^ao<&Pvqu4|+{K8ss855XgTiIE`(JpT?}1fY>By_XOyefT%5|SUWnQas
zakWyY+NvQxzT`;@T|;CvMPfZIwMMZ`$ahB5eTwKd=degK#5_l6j{4IyLB1!c2S;y)
zB3}@p$XBFT0F$c%*xKXj;d<{+Jva70N0DSnAM~mZ5DhQB_*bk1>a|<vQ@)<b-;*W8
zWb{~x7<XUPB#ADk^O8Ox09HtxUl!j<s*$htbWJxI+?{HcxH=L-yb=UGa}#xh!Z14g
zjcU9mVNnhF3)eU?<$zwEy<yDDQV}1;;HFa;D$5=NQxUhRO%=YddJH0!HRxWInep|y
zyznvRzw_cP(};~#D%x0M%nW40khJ3bLL5@AX90chYU?6oe(C-BCdT9B@M*D!sWQH>
zGn@%PsrcxK;S;|n+$1*-Pr2hp_K<?dmLr6iB_e#Hkh$Ea59DU7w(g>+$0qmmMSkVs
zx;MjpA|`Ef{gWhZnLdV^Lbgw(q01@HZmhVop0!2obp-X|-6sZ_f?o-P9gerdHmPtV
zDK?X6SStheemocVC&L=k>uKD6%R|8|Z!eam`;@r`09gLsSz;ZvmC!kBkKHkpA=4Sv
ziNR)Wm$*ixt1GlbXLdCx7^@O7vOZh?>1QV-^iTVk`9+%S$G|>Jk44V@?|KgcgTMyN
zX%DnQ(G|&^VPpPxD;U}|(DThZ?*eIa2_e^5G&w(cp#TX#{egWtodWM?F^2HRW_psk
z^}*)nGG7{4LR4RII)pVuZlg)7<K54vU%}{F+&72I2Oh#0=4WSK)_?5kJkNqgpomFj
z74h;YLsWsnYU^_eGO|yw753t_aF$2sGyR-a*++z43*ujfA1UC1=yeso0!28W2KYa4
zS4rB(0M%J@O<&~jJNxN<*MY&9u0a)PBrJ>Pf}aJ8hdnnmWH+vDs$|Bmt~N%#F!ui0
zeSW)wVZa1>yeQGrXc@is8a=;D>T?9K%Y%PGO-EegW@~Ff0v@v4(XDzvj0zZJhg#np
zDz*;(QhnBax#jlLCNuyPEtWS0EQx#ZX-#J$fN0eIREGRpk9I5CdGZn0j|u#z%w)qJ
zNJ|)-d_6^5&2;O2emOsKT$(kk(yNq-g;9#j#=+Lx3Z22!*_^W0dAj2}zY+S-bF06g
zd*ut3Q>mVTNn^MvGf|@k&cYAY3yQxV;v%kx=?>e%))h!DITg0=zF=|!DRQC<l%kNX
zCZPUDEDQTdsbaXPeCi+z3DZY+>MCwmj_+S>?>lqSo>@}D*a+I&n9__!L#`VvFy9o0
zgRM_}vsNS}BBP%&TSBE*@cK}Y0+#Me@3f39zt|CL{<(K$mjCYsNK^%2%qCi_VVrJD
zT!^50)e!+?ncv0vP!f@PFPSf0x5?B%K>p$bs3R}aLUwD7p12OFldbpqYm!Uk_kDsb
zd=s$|BAJ^k;53FY0|X7D3XO4}Q~_&%%>4^$9b!Dr?<ZvVjjYLbNm;@+%T6Io=@{<g
z57SjC-xbchF3$JeQjaztHcjmr>F`r57yGyg5lqm@4_-s)(G-}u9d7hTmO47PomvTH
zEZ`H!!eqQ;jIz>lM5N9QI^yZL;++^x4NSa`zYy8|(n*FZeGe^IM-*v%|M(35AI#+s
zRh~r7G8Y|6WbKQBF=rU^IYDp6!Ny%yHa<9v?p0UV`RR{+69U<@TT$SIf<cV5aBes~
zYY^A9Nhpb~pXS(#d;5g40kS42q>2m0v1AfIa=RNW=l!fD&VT*e`Dx&#Gl3Oj_7#<L
zFaFXjZGW9*?`9amFvs0mllTe8)U$v7w!OITH`iJ6n%mEwhK|Zqg7Ci{Kpc7~e&C*p
z0xILj4N^1gqku*@mAXinnt4<-g~q;x+si={)&$E2aq}cS%m0uoh!LcL6erBXrC{n>
z0?B|rX@30MCl~4;73j*R29CoA=`WIvZ0;#&u+E3){!W}lBA~hn)-N~>d%}wPVNAsa
z!W3WFb_F(r4~9r0b@H1Jr?U~3Ty0^bvg+)>;;zC{^K1^aC6PqnR;hW6B}{;&wjHxb
z8mecx`|4fFBnG)xm}^CpmxCM`imev28#BY1l>7(N&cnL8ASaOD?_U7eV@V8)IJtI8
z5ifUa=dB&^?ri^&;~K<W30SuApdOGf6{;$m58G#WBri%Vi=r_YqmPiG2aS(@yTDb_
zUm*D*xF~nI97F8FFWtX=wq3$yox?-kbVd`w4Jg&zjpg_B1ZcwPx#2+LPCOKui`%<h
zul0(EYz$$$Nz@$kFmmC%scngREg!cgMh>v9{$HoWB#9~WD|byNqQ~t7gZMq^IaJxz
zpi%bZiG0+|H=UD_ej2$A2d*cLF&P%VlBVtMh76*<)&yrE1O%2kLT;-SEwWl-O_fZ|
zXWMnOPnPdq#6?jO%c?(%SED{3__WPNZp5fN8=DUs{9GHQX8EQxegQ7GjF7bOg+Ec%
zu66dbt$al(+f`odwT1#dV;Vj)sD1Zyg19m!jHn{2Uzfz2Hlkk6VRc*vb9Ek@cO^gH
zR2;*5f9PX;Bj5SgRL14<*Ds6#6L5d9ZOA_t*(YYRfg>rCbFrN>z_{qGvUnbg^J5$1
zj+_0galbAyc<#o+9A~PR+{42IVeh!djujaC%ozP}C71-NM?Il3OJp<wwHf(^op9{)
z&cCqYI-GAG{(Qx??E0g>?D-^Tl1T_LKi1dB6DEVA?E)+@nAXpB%qRd=3+fa4Xtc*{
zZnDpu{q^zK6~-)~)E)cbVHXXV1_$-snR+i^%r0WeuJ)HMv93uOdjODvygBb2=<UbY
z#9!GkkJ!bn8_+Y3jwS>H{jYdU5vDQS>*oQ#!`332_h($E?wX|nwX$!oR5se3ogDk(
zB(Ab6iRA;E-1P7MqIv5B%Y|t-HhE2q3wvo@fibY3uGsOMLpe{c$`3<j&-)9(cEEV$
z2+2NCK;fsEfhQjkVqQvg7#>B0SR-DOnv7s|?hX~e6cb9)1u2_s4SG|}rTltqiC5(8
z579PT>ZI>{Tw+B0%dqxZGX99J)edUr;)yPM;cg`=O3BA7O_v?oLf_X$k*_baJ852H
zCXk>*_Umc_b&6^3emb;-mwE7ZF4Ja@g<Uya+T(G?n{vOkj)Qnpzw^A~!y6~%!#?K#
zxq$T@-o61(r+VJzrY4^DyG!1_Fh>8lOwd}~y4U=;E_r#c&xNCBp9K}AmGA&X@{i9U
z{!-t=fVhC@r%l~kCh}8^V_Iy5$4{B%Kbkc)R+$F`h2Y_HtHGFaYo|(rz{YAq8KiNZ
zKZ-@_>IP;UHUjrpW1pBCDgMS~Ud6#s8cDdMY;`RQrQqqe;VEFZ{7I7Fu8E~A(_~Le
z-#G3SQ9pmDlMTIdPBkF0MEl9ulx3dLjC;sP>k;9JPzGdx6`wxhwDVNNH=ED_hjb@w
z@m8HRJT~#&b_pY~zG+jFL))V3x$*IB%6?uiE#?sQm`a$wnx9QkgqIZVeTh8X%I^Hb
ztcc_I@Wuzp!+UpX&GhZ7bjd)^%A?9J+V=7-7iDF+4qmPN^P!{_|J$pA?3aV;Z@+KG
z?&2)9aj*y>h+eD&Q{cLD6NB(SVZ&-+0%HSeHjT%4Q`cp%(=qcS8c8lpz4z}Lg;au!
zL$x|A`N3vy{D@J#bd|kVY1QXSh2>wp?5;8`TN-XKyb|j|&#%ERoi?qoJy6-#i(7QN
z9jQL!&9G<e4Z#7TX%-(aa)_O9dePl&p%Rzyp?(sS)%lnI2ezw>z8F-O*l^2_3CSn>
zC-!gX8im(o*$eOEoc2jWpEnuP!iz;Nw?4*1JxTW~F_nHErfo3V|La=MLYI8E7<xIQ
zvA?EDP?V&7$PAk2N0ARJwqyF8P|W4~k6?e-)IZ-rGJq<w9wy{Qla~fyM6Ot}*w3df
z3IX>!OMF4L$<lbXypzRaYS+rfd!dn~_$SrJx93Y7R2yMao*^%X(5>zZ(L5_g1isfv
zU9yWbj-%}ttRF+i+`@}7W02(dr)AQLjf`W1({H}AqJeWPUd|sM4CpDRRTS)Q-BLZr
z$Ue$4OEgImO^h!o!*!o5SRzvGCO%&>>?D}KKn!f4raUlZh2Ej@r+DRj1477r<qFds
z%O_pTN8<<rObqdTDwyh4-XE1rEF{{4F&W(VxciYK!OG6@3%pXTT!lUATrs@(NIaj)
zHrL|LjS3_xKzjFfa$7}?L_a<>-r?1O&+tp@*QI+JEZSUi?p9E}?B25yaDXH+wAO`h
z8y?Q-+~aSjZre;^^kVbLS^I*P%wO9A#S=*kZ?%fmMg9Zl`%_TL|M2t`4pDtiykC*-
zT0&}R=~AS-Q@XnZ>6DgEVTo0clJ4$?B^HoYdI9O~W&z*g@4fdA+<WewIiHy`^ZCq?
z2@i(|&~9q$JF+NzAdni;c<$$guOa9v`JwtT+!+9xWXx~U+H2nk3$Q0+8at^xE!XT|
zy0W@CpYDQu?YtKiD#BcOL>cR2QY+pi{QZ_!UjFfX#OPgofPi?G_*5OO@$aBi3E0Ez
zH?!oM&W#sZ--Glc)7$o=Vk^iq(iYW=Nlp27=k~w3Qxv_<5GdM~9ia#TO&^yR@f8tX
zJrQ~YH7f4N@a7(iK2UbWBO=NRh4&Hd)u@b0jx6=Rej)DX`Ep6JV}L<RAA#sTDdtb%
zi-QT(Q{IBK6|DAlfo(OX-`*)yc`l-J{`tt>G4pB>Loi3gIB%`WdV3z3gpdy(4<FyT
z_D=CD&SUIiVzRj3QHHyXm#Wxp)+%lK%Xo@-Uay%%96tBEvLxF_nXkf3Ejb=XT}?r|
z%d5S-zOt(s+PbPa2Z2UZc*w`mKalPs2>MiLciZ>ToQ-^xnrQogw<h+HOMiAsbnp?r
zFna12|6g7U731+EgN}HONjyznEoefJ);jfH-33wNC#Sg7sEfff>Y`yN2i38D8;g=m
z>Bsc3uxKQQb@6u)j<@3qCVPm1!Fn1i0WuPXoo)wB9p|%GqFY0XL<aI;-B+paoGXW1
z0{6=0ERTxW11S^w00z&&?xy32lYTvK5E&22)adU{PEd9^9TG!zS>d1VT1|7CdSTIs
z9NLi$Z^}WiavK8t2_BkTA9ZV}jA#8rnf{|wK_L;lw4it$1-)Tu*B`v5N;*{G4vHrH
zBoY)*jf2<<pK7gyE5=7B<drywDor=S!tvezeU-g6?>arTX?aX@JyAi$sYg8vQV2@=
zO!H9SC;D*k?Alj9@xw<nzj!%m({A4-1vCZlBkwUCmgd%K=Vc?kIvWns_1?QL{eHzF
zp8R<~ht^s&^&USDNqs*YDEukMi{rlv(;-OIw6?G&sXjoQb2Kx^mtzB7PGK{0_nlzi
z8-Hn22%^0ucUSbU2c`hKVG!?VW)r9Py%T;EH|{=}LQ0?)B2}hyC8$eMT%<$>;UFSp
zp<2VS-PKlL^>IaY25!Y(KsH?*zI(;9iR^zU5V5c`NT}qMF(HM36`~VA`E;9!sEn9@
z+6y_js_UTlKdnlqXq@`eCaK>U<gqnrPcADv8)g@G-sK}AKvGK1FbvdBC@Xmcny)@B
zD+k*P)lPl^473zNA%Do^SZ3u|>VN{Y6*YhFm$w|d@R#|#w1FmDBy}5h3hW(vl7xls
zbckX}<x)r{q{~4_Y3fqjpCb37IHsb8vTr1ujG!O>0Tx^9YTuF31nK%Cma2#NAXH@C
zYRemacH07-pEqN{2wS;HAvSq|MeqwAHhjxQNr3Jxe$+vkH-ns(ew!t25KFJk@LDIw
zLot}*T)VsLyBILgt5wLD<ul$6NNCNp+#-~iv@5~m0jrd^05N>*wJ_)8J8zGV%0Y)y
zHx2%^gFIednUuQLYhG?19zTa>%Rhc}xAO3>*ea4NBtRxw$p^;6ve~G-dP~exKl}_w
zz+k)Kmmj;Lq5$K_6OE1W7D}`EYhav_`KkZ{tJ1l-H3f`blljh+OUL0$C15`DU9jVE
z?tlG~L@Hk(u3k8##tiRw4|>tNA7-l~760m*ii0xVwQdhyhjrd>a&zb}{1-mgMVHg%
zZ9pBgbSM2~>!^6nA&qk9cCNVlX}^}Vkp?*$Z1(rz+h$*47c|~0!DD}4fc&yCVh)bO
zRslp)|BenTU63K}Cr*C9n}dnv7R^|Y4MSWluidF>b>$B1lMzC~5;dI1oi=gSRF=$(
z%~?P+R>%@?2&D#5o|hZXi4kEY0LemKyL}fl_-XBhUUrh`;91b#gjwoD;2(J#rnfDW
z{x(;La@s*-+1(GZlrc}R&B=g;e`VybMr?%}O(O+z^g9Lix_8GP56<G^G)w2yb=w^9
zp4LbFKuwzD0LE|s4W?f4lf4Tsz)U2N`kYQ6sm?&{hZoWU83~UvehbEzLZ=OnCwX2C
z4qHtC$48&P4!YG823t%u4*_BzWBB1)vIgn>E#c;^sK2*2|I$v>WJIq@J#gFSONV9y
zJB8V8%?ca15FU<qwJ})YA3xYwk`+%K=%o58-MWI06`dAb|96|`7|jBO7KMDPsM6Ks
zw*U|s>}|aEH~}ywgJby^5j?K~R2%k;)dN*hyAw-z4qaR;v$}R*DKr-=`WE(IW@VHO
zw+|5qE+bmOr|swJPuoO+Pz>?AF_$5(z_kHRUx1u=#YffyV_L52RYM;7P-a>;UdJRa
z!dL{hqu+TZnrQ$lj1{*Mu=7T#LRoG6D;Fb9#<78eC_D*iT;5IZ*gdcMHrTrm#V9HC
z^!NWt_9I(Bn}`W}kY=SWs0!larj%&Jai+xRLE+I({ixSxSc#{|u23OQGCmZ=33KV1
zD4KS+@z*kK^)SjtD3;u9K6?B)vS&Rg;Ek6$PSO3XHWXCy`^cX}Q&&h#-w<n4JD)B&
z{n*n=QqG=L*|D^JKB&A6u>R@f7q52!)QQJAx>njzT$soh6{C=atFWWC{iCuZkH~!k
zzcVuFrtLG)1+iAS|Niyd^p4pDSJlj7j{2lD+7pOp9VC^oR%tr|Rxi!pI$T{V1@8VL
z_pU>u!!l!#kex*&%7|$e1shnhM~HmYjMqz+={stHaQ}Nq(!p+NXmn@?Qllm?pBq6v
zD%^GW4AQXF(j}t^kA~Rb>*8B6vNZ!Ct`tKRi)uF`5ti^~VA-ku8iA}y>3c?_)XnJ(
z5_i$+CA=$59mBPtK56_oC}X<t10A(Bf-&S}Kf51roB-O_-TO_+MjNg|_uP<U)jw`A
z#zjPBN&IpGDbvy(NQ?RK6CD~sC>ULKv(jBrEI2N7ujFoe#)cl3hvaU4U?7_gtCSD+
z6{KkA#z~<-AFxG=!~*y#oi1gkBgyU21^0t>nI>6V7}`<?_}_SeG{*cx0jQmyfP=ug
zntk(@U8-UBjAO$)U71y~V<OIk#2*N$U9QMSw$%=y-+1fzz`n(IyaEUwfc3@w+Eeh)
z9p~VVs~J7@>0?4Ebc}zAa)_HmwI&bRmb9gl3z|I}M#0x=QN7omKJpGjwP7*<U4n=8
znJp7KER-WMa%&A?L5|VQ<ShCDhj1;AULS@6U_CG%2qtVXVOLO(_@&VZOnSvOswgC$
zd71LN3t&Q=2;~hls@(+;c~vC++ohD~{~&IN4Ux)Bcy=@hohyZ|upRaD=0~ztPbL+L
z8aOZT2$DeP*iux6;*`G$B7{SLIYj2&=QOX;-4?hCffL_Xg3=u33D^}1+vc*SU&)f{
za1lfV{z{S^daGwC*^p~)y7Knn@!7A?1sVSr*)lt42*q>oru8f)GeQ{<9j<T%I@+Mz
zNdmp4Fw)YLsXV$k(66C$!{Q0`^+hgl6Wb$W*a{mdLypn`-CjU(A7*}(f}9iLdOpx#
z#iVZ|X;lYrr(vNQ7wP}rg8yuP-m~}j|0OGN{gUztkhEqziejtEMpS)Y8glq_EEHuo
zbeMSRNXFyaB3U475C{DY{S7$+aZ>>l^z0QyHVp*^Vy#J%EkX9r%ckAzd0uyjgo^L$
z57h$n0{FVGJ?HS;Z$V&{*yehx`<jOZ1Ajp?f^g6VbBd=mF#|yE##{F#OT-L%Qh1r3
zBOM!cJbqpP!arcRr5p^khB!&H(a0p!@&#IdsAmETWiRBxJrG}r;9mVJy60~gQvAno
zdYK=Bs%IK=s78Am1Z-`fFE@Ofj0jAr88hD%`15KW&`bCYvu-$}UsuSLu;DUIhlos%
zvk<rM#L<B($8BzLbFG6C@Rj@iqpQ?8>86b?&rj78;1W<@NZ+mVl^vkbYW`;w+J8nd
z2S6OWZX|We*L|}UEz*&EJS_xKP82NfvYEk5dLwk<l;Rg{n6yeuwfDgL>Qh7pR|Lal
zH?~!pmL`h<$EeBSyhNNp<xgcpd6d8bUzA)zB5LJT69MCtt4|33rVT%Q`Onu}0!4)s
zBk(@8?VGn!wk`q*4c-|c>;>A`8BYKleyWC$VlL9wG@xOv))MBP+^pzDvrVMzZ-4E$
zA~icHsPijK0D4dAp<twrqin=l%Ie#zGQ6E(iN@1fK|hY$)Jp81nY{wQ1$21_4y!E`
z)Y5tSTB4hM=*l#XlI|h;jL8mUG>45jmhHH;IbY0?>uXzSjpkhBCqVgj*q2LOneXUF
zQD3(}TOAiK_0Io0$AgDWX~5<vESeVx-Y>5RUWWZ?oD4$n63}UhaG#IysJeiDG7CBa
zhDu<8<GGTy>st5Et!1=?CH{;6MSLmjPk3jE_TjFbj*K`AiI}v}Skc+qoC>6pa;=EI
zg!!{z2B1KxKv>WWQVBhslp*XVZ3+uwUFLU=f_Oz|)jafWn9EF|(4m^mGf_i_s^w0_
z3{9}ErDf9r92s*iJ_#@9_N=6ZEFK6#Io~zIt<8gL&n>%?By1d<Ym#Ig*~sH9`OCW;
zP2=RTa>p`yw;q!X<;>TNQ~g$L{R=%D1%ezsKpPlV<Nk&1lYfbSRm=!iBZM%o5gcGr
z+0FGA*H1rgC;)mgn|IG_)-DnZa-8SAMP_Rw*`D_!M=h?omO!CYm+f}RkDUa)N?WO;
zvhq*gSKj@ZwrBpFrdxZcxiddV6r`$P(O4lcoxmKOR|HT*w~{oL1OW*m;2VC%J4I>-
zGZJk-gt6!&6@_i1#n`Ccy-}ov^M$_`Xhc~+;VFvYG>Le>mByku5&K)Ix2rhIlaLzk
zW`G}L2)RzWjlIHGcanVb`E)^jm4tz?pR#F{Z49xe@gCbspe0*Za)U=_Y>Ww76Bd4-
z3%)A<8d0I7829<DP{MLqjmpA{K2)+XRj_1AU--S>ia?AOPDgUF(%#T9%}nCYdb^7#
zv8JTzhsUD^X);<vBqjiwL@WDcpE5GmciiQae8I6vMo*WX+(n^(v(xt|MUstLI4v7M
zh~)9R&$0<Ra*PsQ^Mz7olIRd7OpQoaj?M{fm9`AnX_@P(F<sQ`N*<2dkJ^;uovddo
zY(%%@6Pja`>&dDsh#kkHK-rn4RQ-w0kX15U2f;C_<-(WkL<S@)uOaKFZByHvKcfE>
zJS0kXQZQSCh2PNZun7MOBo}Ht&Af!559$KDuU{UA?fjrldga~gQK(t-Exymggu>hr
z586GqQL63u`+hY6^b4hrfDSKiNPf^<96ep(mPvXj+#(!+x#Xt*eQ-JzpiAv3AA0Ft
zP*)_FAc;;+b1ZH}BrN1d!pO+&7X`pro{~`2W3f0x=B3p@ISV-fakF~7!9i&)h~ZRV
z=rqNG88xL~D6%Oq#Ea0m>2RSC7JT~@>KW#VQ5-Vr5Z5@~Le;c4lIkhbh7SL4IK^z9
z;{X?_sdxtmPM6zH+;s9(`7(J}RsRZU2Y+JX#|G%gY8*weHOQQj@UOb^rGe_4s>>da
zTw$$WVTbW#jP0O7eZCRHXMA@}UqhMft$O*P)8|$`p<?ma?GH@WU}v=g0%Bx18MDkD
zkLJtU*7xXDeNMD>HA-s_0j}vxSeD+Y^8WP=+T@|^YaL(Ab)?bjO<6dP<{^QQ=H87p
ztT{$07uB{hx~58-%VVdSu{Kp2YdW#b-P(Rcbg3^h7;v1(`M&7yNC=I99(V~a@iiT!
z9SSbvF|m+(VZ&b}mhVja22{L>vAVgqyPgmmw=cU`!%9rtcnb&ZKfSlDLXbE-NJ18F
zN_NzHeW4e>PmGh#^5#5zkq>`6LI$XyVVR$SQW!#<QS&_K{Ui7VX@ojSS&iekkz1jP
z{|yK+9x;{b;K~7#W~X%?fbU?nEv6gR_$q~0$2n(dUzF^OH2&1~u`t!zzg|@ou~;or
zYbL(Na>q&xvrZ*&6_%6-$h;~b8%JX><l6hTDF4Dw#PbQ}CWf-KulK6DBw#7AgLjGg
z4<0)lksi=r_>*qmAWL!Ro~Ru?gW><kW%l~M7S>1%BweR*e)TLX*WUgEoWWwdoM%8g
zU^HknG;PQ>?6|^f=Vdw-*Bc~cq?Z^~1z%a`!k{XJxcourcfZU1m2GBB>+-cRH)EM5
zfo3&kZHY}URR+j)jT}$dIDZBDoNOG1MOh6xLFN`V-tD{vV^S|!Qoq~*?BGH^NLcWt
zLJpN=MMU@$D0qKiGJ9a-8+n73XrS>wGXwb&tCLwS1H6wYoWpkH7)i&u<n*#oUV3Yk
zqf8L>ukirFQd0Vwolo?6Hf1X)({N>QIe-16tpgqNu)H=Z!@Wb|DGbzO?F065^X@J<
z$ya`enrp^<ZPLhv3B%i?nK+KCibc<L2j#g%*A=AThXU<?3$MSnEd(oub9lxcEJLdn
z#QDQ-$j@<kv7sw2)>DOmWY<rQlovs5O~f$hGP8)?yl&^T;B~&#Uy#RpR08ktOx^&3
z4*PD)#QPYnhX-+SRJg>D%)j&W3tAM(7aE_z=(Jg`2GxbzS%f|wMh}iQ?<qJU5|vhd
zP*R^O%c2%2id^bBW&Ait^ta0_{PBUi+Yl@MLo|jVK0QZ?ekUid0&QY^kK?Cx0Ujv>
zggTK=lv4zi(6+wb)?cncLX9>8*&qxGIRfULkE0P~wwG*@QIkTme#&C8s)m=Ucy8(<
zQex(Ss>8d`&%ApAnP!Jp0t7QVecy^>-oi#zC;yyHkAG6F_ISm(4@u0e1{|sNwGP}J
zUkMShNx4zeB`jPvwpylU`muTix}1kiiABHG`<`a$19t(?Ave1bsS9mK$`B&Qz!$Yy
ztTaP((jJznj5vkVil-|MyhM9i47h+{+_8~xX{lCd)Rf|*PZb<jqiC4NN2ljgg@>q2
zmLdlrbE7f%b%~xZ6z2uW%mj+YEc&B(k4-g_w~2)-$V0Qy>g#qQpmMpK+o2dRK2`(0
z^hCz5X8VfhI}ks7{rlV=PVsm8n5cthC%c&>=5x~gXs{&dmHCI#H2?%6_?NR=B#V>1
z2*dvT+SOW)LJt@|pe(3ujKwqcF&-UB8VVRF0`oh@RD(6-Jk4n?rOBGWDyYe_@)ztu
z`i_5gRG9G1@F_IvpeC{eOLa)2KC(E+Xdzoe1Rg<e9s!AHEM=#6p9>Z-FoQ*5t5i8*
zpCL~Ogf5IrAl|==Q=*`4jo657=`Jdc2llxMuqijWSWVO?8EfpQCaM;aW@lVo{b=bn
zd*uj1q++HKL?1P*QaR_Hx$GkkU^~f~e(o$s?&rc(>Ydioz8`N|sKXVyL!I=nTivmw
z<n?}hTmT$^{CFQ#!4gFh>%T{`LOt}V{<ufy+UBR6PD7(#8EC~sF41cgD%O+)_61#1
zJoa_%j(k2oQdwlboZs%`k|LA7UF)#l=KwDSQn(RX3d~-MHHTh=2M%s6{*Bnw(>?**
zH^LI?{C5B9egA%-^>Iec4F@SfIfpMm7DG{LXsES8zk`lW?_Oklh)~6UsGi$@xPe=b
zlY=COVjq`Cs_kD>UUI*93cUbt6N6Amfh2-RyXu1mkjdd%nud_?dzyO)_5uv{Hg8sq
zDn`|Ef#)rrJ5pleQAtgxwZ^bDk-lwCtvYKtFc?_F&o6{QMHQp%YNS8_dq*QWZE>xB
zLlDChO$j4`7eKJ;oc!DUmn?FpJ;gp)U{}q3`0;gpBj8P*u&aoh97P{IjzCTI3+)=f
zZv~|EaTRX|0~+4{6&#kdBWXi&&9c1l3s^utesSpdSB$G*4iZx*J$=whU;GViLN!Ju
z2Pc`Al^--e)x{EB&=>DE?d_EI|F{6@&dh94K;DLE$|ZJ}#ePjj1?2Q(Z?>B;hX}1+
z9{})s<n)vny@wS6%K5EN^i56waUj&mE#NVsRIn%3M?(|S${$hNVi9dJRb-9VTEBkm
z#~qIn!<#2@##_^46J8%{73EZy?a*5n{ICcAu}c2<8#?Q;$`x3MA-o(F1dm^m+T7Yf
zT&(&EOB6fRGCcn6ON1RL(HN}bpZ~tp)SDE7H)==<@|a^}RLC(*4HdA@3LkuA*22&?
z(F-E4?PCCHtRAkx97sL|QNG5oxdh(G2zfo$;Uf1_0cz@B9C~^&5kZXcD0CbVRy|wZ
zIq|1cNgbV&8a<~a6r5Ph=?x%L|2|lq$I;7Q9;9-z#)(NS-sh~WI+0eGq*+U199kWT
z7ZJo6)GxE~7<Td)@b7CJT@0$vulNO`1WXV8KXk{#`Zlb2+qY(3jO=49#5HOzU+b)`
zf&z2gQ4-kRa{K9W;RUIk2~CW7CJR@2e%>8cL!%qHRBCMV{~@yDO}AVpwfb_bdn{Up
zwyJ93OeB6UJ<p37qv_kNE7r`}ExxIcX3GxGQf-%1e_1q+uvE+=c>!tpcIt@L>KWfA
zK|vbJV6}H5w_46YCfB!kFi4?eFE)r%fQEI^HCTa<_->;X=3zr<)<l7A&lO!-GxTwO
zhQ%M_p|d}w>dk`s@tOdLxZih{0nt6hmx@G1AxZ!A^{XEKW1ytWY4NOj>=55|H5U<1
zcw*8k5H-G*!b~(kz)&S2ft{@ehOF}K$LHUlMR>DVZ1_UAXQWvE15@GaklEMs_C?o7
z*09idh?{6ks{UusialrVfMF(-P*l#cu7BCoDOMnnV2}0<D|~+RxO*_;!WLe?2rVst
zh>`ITThLIV$2h8$peBET=2i@ytv(qBD9%%l7ar#Wth$tB9^NI0JN(vy-lGzzfV4X0
z9<<kG9iathZ@>e%Y`hH4E(o1+c}-^eccRim<np~3fC2t1GOlp*NllLmnR)C-VQ4o>
ze51XTl3N5a1IBUnUgn55b0DJ{8Zk0Tf%>TL81yKO5^>2QpypLHcc7yx%L}d7W_Xxe
z8P}qCH#0gY%Yl3*I;GCNVUPVz6#R@u&>wjyNTp^|LgAC!mE+g797Ck>JdlO~yjwOp
zYs*yRrH_|x)taA%ggiRPUOIYXo-Z?is|?9eoUL?g4{38w4f`%ul>amymZd#1d`tKe
zsprh_d_;eb@c-fvyUGqEaYj0IE{^Iyb#`1FBy-Hyw>r)ep=mP>q3&LMi$%*?AOZ!#
ztJj2cwS1`_2bhsZ2*$}Y^s9S@3?W10(MMh+97V2zAP6r;DX>$G#%L>EEV3%|!_5Rb
zKiGKDtgx;>0p*pWosJ$F3AH-Bh<m!ko^A<X)AZ#c<RZ_W8=am?`8Tw;%ic{mT!Zhv
z`p!1TNiqO_3zF#z+GP#&ul#HyoIBb=PiWx1x`d!@q1@m*wp6kuxMtU^=NujGYdXG;
zc4_p!a<^}*oO4LtZmnfQ_s->=5}kWp2|dEAhmZ<h{eE@rwZ-HElplqsgLhejTq)4$
zeZtxIuZnHfYEKX54GQts2f@V7by(jr0&5D-{Dc))O?VKIO#%TkOCvtlqOf7Gg7?UV
zYH}%zfUzZzjtEhWbRE?Z$7la!yi7ukR)<^4b0|Ex*}ykip*b&&edlci5^~PN+#z1-
zxmx2<J)}vPQSDw6k^X9b^DW8mEUdC;9?#Ke!lGqDx%Vs$K<htLXG$NrxHW?p{j+o0
z{{Hs4v_XzRtiSmNDgDZg;BHqYmfeR^Rtkd_opy*qfrW}EGw0;kdwNX5T=LH!KLyfw
z5-Gygr7-N!iqAchU{s#6$Ro=`^%Uv(1bXv$*nXoS74=W1+{K8~CM-mMRe{vRL>)>i
z%|gq^Y9FC~`eN242k&LU7q-+Is`k2EMa_kE507AgnZ~lY-$;^U*pEJM+MdfZV1hPi
zZQfJ6j?_C`(=keaPVm=nFm*X%-pyBEKgYHlkn;D~zOAa3ey{Pg0s_N%y&Ge>!g|ju
zNsi$t$A0g=bBT%ecG1iY(-<x(;z0E<%FnM^1gl-586NFI3beidvk!%5yisFq=_ZgU
z&pH?r$a?fa3fZ{UPoH_P%&KCBHGkad%aTAG<@ScM**>pRkvq$v$t1;%@TK&`dW9>t
z8<o9^$41J;0?W>$@$Jta9#$*rM!>O-oLmK~<~EZR!xq-P-B#$w^mFU~l1=>bux8N7
zrH{}qS-Yiuynp;Pc=qQBWO3{e``EM5g5<Cv*YJa)VR9J-zFrkHA15jQ76Soeyo9f7
zhV1|3H+?>n+SO;)w5GL~Ara>U=`ET>BuNOHejqfs{rtfuR8jmc2xtLh0BzQm=9f6d
zV&-w*=MtGIJOW?XXbB2^wc8|RNTX7OHRfeIOGE$M7krIJpB^s*>GQI%QbN5GZO)y9
zRDWq_x4Y-lyN!|o$F+o(4Q3`(KM3BO=?Ez@V@!)hsRgGM6^!1^FJOe{CP=Lt1460E
zF9OyA*6adB;LGT2Lp-Whg0c^C1$##oslP%4JSf`p%>7qus##C(D_@o5huycdsk6dh
zOM6PGYftPWY|~LPu&4GML-Ybn>)3=bSpw=KCEI+L@=*DoYj8#v?Q?B@VNJC40<*Ts
zg72DHP|Op(>T}O%787#BwU;+Reyzgy(MM@u!nd_x3<gv9t+XOgRejIRVpDSqj{)GE
znB&$i!zu*cRPwx;Z}?y${{qekb~UP}F+J^Q8Zbbw|8BuX28|Q4<J<8U9}MdK_`<%~
z6#m5{kN(HMS|9(d9UbqPu_1d`fPHQBmb-XJA~gZDobF)cnWBj(`_wbvXUbGK*nP(C
zef<hKIu-z+(BG=o#taYFUx={A(cKRF^!^T@qkHVwZ6fA;W<nnEIK9FmO#J~FS0*MQ
zH$ql_#30C2dBrEwnaok$29(fzwQnA~AEhY;89-;Oe+bMIK8o;6%1DDCbfj(xuDCwl
z{YEBl>qZrdUpGXXIJS+7eI*c5h_#^b%hX+Igu>|(CKPC4da~r#Q(te|Awv)Evh(+V
zh0{Tld`PK#=!_<{(}7*fo8LXe?z@=!ixwLADxtCQ;WB4E3jyz+z64v5MTI%;Ggs$f
z_Vn449YG;!nN^N09}6Yo2uPYHSOA2t+n+cXLe<!h@_6$DGU$w&{ZH!WiFF{}Muity
zkFz|oC`)ph7>_1JryVPblC3OUdWI4HX=Mp82+zlREO*-rTV}<`z$qwJakBAL0Yu5M
zR9MsT`+t3IRv9Kc6#(O^9cCkR2E)`2Uiz%9KZ5%@<B1nW2U;*Z4U%Jt126w0)^eGL
zq?ho}gAt8xv47>}X9YrWB%<VICxaYuoU`vXnnGI#3Djj}c6=odF91dIifUAyhMG&{
zA*83#6fjfajD}N_6fsT(rwHYaq?V~^cSJq1f*XQpa?*#72VQv}^W1r>OrqyCzU7;?
zE1;OxX%R&x<ZPzWw)~~Y<XhNjM$nL=I4;<)pBYOX7R$fW(MDyOebhqut`j9>M)|eW
z!yi})a-Y@u!@?5Wf(QAG*wp4d)=3sJxF{>WqG4wRUFnEWOVCcwKmJg>o_9Jh;Y(&;
zt0!{a-dwEGc58-vcHB^{MznZlaWs#vd)qLB4z7Ik#OZw6UV!1=S22A4<B1kLWc<Mk
z<I>-3s)h8Sgh{qYJUfrb&04-;&~W@IxKw~dCgK=!o~C)LX_o_xtIGut&>n*L;|RH1
zaLribAv>}+Z`n`$sO)|*@z4gejjxV7uf1GD-uqg6uGl*vfXg?oziN_P4S%UvJR!|G
zCp@)zEDveL88;dJdSNw6v_ldj5Y6+@4DXPdToc(7gm|^6-e$Jl?16iD2|W^@*beo?
zbBlI;zZio5wN3)p&}jvA=4a3})}I<UeDD$hBQZ1_*k3^8k|Aerq5&p>_;knv2j5%v
zUt!s!Z2cj-(JBqikf7YVuC1&|o5GU^DXLktDZVfux0PE4ao1tc9LBNusTaR&CD}hX
zcZw~+k;zmI_fjNKct<?;vH2@gpmyv$a=L*eK_1pOY1@Umi$8rvogPnqidm0elZ9`1
zHbU^_Sc^ceeM3xxqO@JEt*U0^ZrdaLA}(Y!4J>RTF8IdVnf6m6L6Pi`J80mfMPc1Y
z)k;aJ(DFO+GN^Knp5A*QDm&5kpMDlUcbBlx!_^5AdJ`J<KURGa3)YxGOaJt=zo-jj
zf?!Z75PH(khG=<>K+lBOoRG_jm9*<dnoEJgZA{*dc$xzMDo!COv^}KZ&4fRQvxCk9
zaWs!{btqZ$)Hur_+aQt`K(tnhS?SO2Je_kC6p^tdl{iFB8w)%+(s~BRYswdN0Yks5
zK@?DksId~YR<H}wp0Hn}VmK`+l$8ES1ERkjh}@BGj<Vv7xD?LX&;^D7T|s4#Cu=E9
zQ;pgvk3X^W6)osI(L2~XiaC6$j%z~ov3Z`y)m90&RY;Ww9R)H)6R0;|3@s*)Fo}%Z
z6PLV?^eX8&ZU{|g%q59q^1I`L<(UxFH7oHO(RGp0i?qqL81eM?=1E$^h<d;U0wlSj
z(KQXa=Qk)bRg!G<(k_75??sLiV2U5&%jXnG`y-WJvWcsV$XE3AT;b#SwWeS@RQI60
zh;WnDyDy;(5%IuUYaE3oti_D*50Pge^SYNo6|#NV+uLZ8O}e}-)+m7-s4P)O4h|#B
zG%yk*C$Gj%m!rVx%d~7FfZ*COlZaS?qRp2)n{5F-{Li`6uj|#@T=3Nt0`~-Rv5wJ(
zt!#1#lGW_}xN2>)vE*f1H?kH>XJ(WO*(BEAhI4WbRN==w-oS?$OmGORG1#E-sgSPq
z+I-!2{l0ztV`xHlOY@|}yTV(Ko5Ys=G6J;eAG+ndr%F&md!wBIKZb!vT1$JWz4?dB
zN3Nllu8dy|&JfK~at^<!-Os0?Zy;AKxHlioDQkbT$j^l*lB0|+aWZGPx`()GghV*C
z<%i98#R`k4RrB<lQ0Z_8s|n61hL!4%=<mJ6h=dNdPJ2w&&|yOD{d$wzg^PgAu8#Q9
zRUJqcZY6nmOIX!%(a?8$Z!!}DYPdi)0;J9u;tAsHTw}L(`Sirg=YbfETd-P9`j<q@
z5bTL4lB0tg$(}Ate~^a&={zZnl>S@iphh2bvP7;Lx3ta!B;|{!Tn5&z?+e<!_1oHb
zHtaedwe0LLf;5n6SP>tZ>9<l;OMUFdsrGfKj!q(l-<B8$saKVK0a;6!;AQRSr4|$z
ztEH!xFbs7GGSR}-kNhOez!*O|4gWB$Se@Y|r)I3Uq4bqnRCin8QmjqfPo)hHUwfP^
zyZv!!=E?I)iZYQy`DC$56}QozUwDT#6~r58ARtE7=sCd{p5VMHO4F25_SoW=K!5!!
zQ}3v6dWVCdZE<I%Xu2(d=;p{PI}3n479<z*g89o_q6w5-qihvrT=h54$A#ce+TcvF
zH{69W8EJu{=?TT4-;w{hVNEQtq09==2P*KbVV##Q>=XYe5BO~EDvP#B82&}^aUOcT
z-Ybx?R|WA`>#R?vDu^XuYFffv$KHs%br6`Ax1o>$U293!nTylfX?IB^q`_w^9$X2H
zi;TNwSOlYt-JtW9{1Mj4My8+=i1!AIf6%EI!AV6^NjZJ2>Z?*v7)<MQwRF<|MFL9V
zM?zh-5#_`erEDeu0(YDpjV+2QzGUt<i^X*87XCwTYIWNP7-}KO(2luzXy3IfC+HmV
zA!lCZD_be9=1`U9`^&$L<Ng#0T&%qS)Crwxx?qJyzebN^M21O;4o|PtQ+OdlOUH$n
zEhnNF<5hS1@R$&W(Bu5L8Hp{Eyk(d+@@8<WI5oW;#Y#EF7Nt}cuZ3&cUCN8ZK&{p-
z3e-?Yq;S97SHT&GFzkVhTg{_R8)q$VUX5KhB@Y+0;?P>RJiy_8k0$0WE3lGbnyw}$
zpD}BOtawG(g;1wHI<ihiou|K16RYZQk)?aEo06@E;zHS*&C07-_G++A{WQq$*S7#x
z&cyCO-B~4^(sxKN+-lQOv|)VB>0?Y=Sl{Lf7B07r?A+@F=Ez^RyBAwiAE?kW+F^yj
zdaVNd8)04soeRm<B1~M}t+XzcI-AQrq?-^awUw4e`uc@Gk6YKeKFRwgKBNVjF6q#g
zRs*m$BxO&f?qmCZ(0Z+pBVl&Mo7dht&d!S(7FgpyAMJ`Z8!47Fo!;1V!a(cVN+d;^
z?sm+dM!`ql8v@K$JSG}i13I<eE#ZiN4c#yrDz;_?yo38(sR`<LG=w-2$9J>Le6Q1R
zR-oG+4%;ajJqtdnRQ2dO{;v3QAR^QEfr28WrQJu2Ekb3dlLY#7KgOZhZ{;2&<aDjv
z+Nk4{!?i3Ads_NvbJ1b3B)}{@BcU?BmArj+)!NhA<88#z{gI;Yza=Qo`W3(9rI*ZL
z&cI#)`*<e%RP<^ff3wm{*m+OKZ57&akb8r?q(U%4vvq?iq8g2DmKhG-RmVcKECrtZ
zz%sumENK6gQIgm6U831jq?wN#W$}ftV*f^+39DlLLh8bvy&?mlX5~zQHm5RY2H2C8
z@goI=X1R|ty8;_JD~u~kO_@AUbKHM*d1riR)H9Kj1c4yoAdJrlz(7URWvVWB1&;@+
z5&3mcDpx|vi@*KS(J$!=5R-pE`rX!F9!wunqm4}w;+SW(4&B7oX|!Oz6-Z}6Wssq8
z+t&73-I$ELBrIH4+h`LG=#o}iyZ8LSplSm+*6SJZrFEm(NPG2<<3P*&ib8Hau>(o{
z!a!W`REvU^0P#|>k9y^w+<GpglipgtQoCnmFF;JvoOVECo0Bb2=)+*=xzdsB#zD|7
z))BwtInxZ!^t8pR`3nb_Zj!AP^Go@CuI)jjoy<2$gM{+dqKG!AJLuheOG#FQ_VXv?
z@c5U0BEB|Xgj72g8%gb=$*-^9^~z?de%9-UfRGTx1jGaJr3PO$k)+f@<s{L-lB7p-
zLFtV#<)$4>ba+Qr;x+pN@@oBM5muP@)!h7Ph=PBjl1eKJM?7U!Rdw^hO6`|WlL-9P
zsq_d<A5VvL>PiQly7U*{-v47zA(%awVW@y1G6Zk9jqE)Mj^?<)kkhF?AJGFg;sQ1t
z@z{%zyqm`qB_>rx`1W;EgJCZwLQrSJOU@#!$Qu2cp`5kx`)%b{Mdw0I7LU~cPG+sv
z@#GVH-Cq$>@Es$cV};1`*nb}ZA{s+PdMrdhpnyNb3lgxk<dD3sNgxx}o#omivYiBr
zypA6v&AhNPz;6*p$op*LU0*7)E_p<py#6TOUSqDQtDw2YPe$Iqg(+0S{VICNcU)j?
zWWOxQ%$KcUvVH)p|9lQcaKgy48)-u&eZQ(d4u>FpjDB{~`iWrn&C4FKikCHwot?_u
zYj)_Y@z|EB_@!*o72FCRXq_S;KJhqAL>RxqB9_!_>g5~<HqKI9U;X>}=(J(Ey&+L(
zEmzgR#(@hzcXN+3*W3;|YlXv9rK9yLZm^PUlaDuo1EpM;%~DpGMdgvT*RI5%LWfm&
z`h5g)gP@>3ivyn@#`|Lac#RoNU-gLy;lMOo9zk{H2sT~*ns4^bqsGvVO|db*eL45m
zq*5s7?$|WkeDwXHjb#KNoU{t+1u}dcJ7GP&@d@+)t_v>1I6HlgDKjhY2p;d#b?9^?
zU9c2C!w5(E*b)%R!cN%BhImV6<q9)MwbJ$%4CAkp+<zHk*`@3Z<(>mlsm;=cU^ak9
zI#U&}N;}A+0iTeqMK%8auT^sXOd<hY4MF{p-ki21pwge9ya}TG@j9p*yH0bByX8ao
zU_?i>h;=vKNHzC&KUWY0F)clQT8xdM1Y!=B1nd3xsE-&LO-t-A8oNZ>L_EYE0A>YL
zNg){Q|8|NMF3K#jC4EZ%o3Y|?OGLFV;P=R|<QCwo%j3|q_y!FRy{#OW>yDW1_!8eh
zICspVQPM<bvT!vmcm60}8ZlXN9`%1u3J0A%6UfEp-z<61o#zH-7VFiVqwJL7LHF<5
z$VoGE((Q}Ofi=K}nq=T05?^Vm3Mu9hR5DKqUiE%7feYq5qQsp7DbsBq#CP*gWIPU!
zr^;D1*hxW@yNpOu$PH9%Zjlk}dIChFPyx?zkA_g24=x~|Q<Gn?33}uB7cltWj;)Zo
zq=mysK!kyT5noKDiVz21M5s2UMg;Gtp>n@`T8?&0TEwI-qD1JOc9IpNw}*j9fK!FS
z;r@ZGo@=`H&vx&hIR@Nj$-{~Vu)NzHl0vWJl|OYD8}eM;5K<;uYsWizJFX#dAz?LD
zQiLBpPw*t{MHpXcF4&D`$F{GiN}o``5?BhEE`7mcW1}-#RQg@*g-EiYf}Mr+d&C6k
zEr1|yhmxYDO9%VeJ5$VXo$!+Vh-Np$v>Zq2kRFF+IH-ZzPv$$G*vQWR=%y-^{}<$q
z8%oQgZrn^w8_r`dIO$Q5Fo)TPm<uZ2&*E}hezFZ(sd!zIWuR|SqghVw8}VC*F^>pq
z9g9BvmJ>qf!)v&11`hyMQl7nG2R5z;-}zpERqVd>J9O>%7kaJHik1KK1#3D)>9@bG
zq2ru@z}Gv6KCIi*zV?=VVr^F<R7tW-DMk05Y8sP`C=cr=M?lo?k)`7b;e!KRO|L4x
zjWGVo2u~8){2s)p*-fnp#fR&R<kQ4mFI|pio3Fv%8m|%`o#^ZT=X8R@&xekQ7jcW#
zi@#@1^N9=M9E-F-m}kE$W){WsX|RykvQ(6Kr{G7}ij~$PoO_;&LuJNHzo=;{W*w4M
z+*3i=>0@ut{@#gy9c*6VUb-s6qT`F87yr3ZX`>jl{f^;`Mx5gd&8=Gk>AxM1orDl-
z%AdBrmzm6ox6j7%K6rg$-26*)!Xu=54FS4pZ<|UU1$VyK;)}7+(PV8VNuF4l^-`QU
zb}!v_KfIG$yg{Ljn7;Z_S$W+8B-GVmtKn~M9`lL+Z@ai?QIR8_w945rZQ7fPr$6|>
z>mVb8X*j}Zt*b><D2s9{H}iD|m?~y@;kfnZSk!pdwN?Z3ZllA~{$B>ua+bZjKRFsA
zSz@z+kZ6l@pJ3JX8?o0{H23K-?1BGcgCOhS<v3rUn7jky7!lpMIwBUo7rww2PQgQn
ztppnp!1@PgX9F$}^yYIg0w2!qQi8@Sen1fSHoyla&9G4yBH<fx*TO5Hv$+ViD1h;>
z^)x!W0y9|anCyqX?^f0O=p`w~HSd;cOm<oS8es2aVZ_n+#Arj@sydcwDO2s%+n7%8
za*m1)m+BZj>VW<+voykP0mjM!LHlLlxu>A-l7hm?YG+)3gXu2()#>2g*t5crIkUqh
z7lr%3PvLL6PQq-TX+sFZV<%*-_^!m3K7L*beohngDuyhB&tE)u8*q(&Kf%OSJrT~m
zFC!=3i4eTnvz2Fy^kf_Xclno=nnJOr7QR)^`}O|a>gGAUla|-v&*D#3ggz>59nUPn
zg4k<JSjiP5e!lf$)OB1oH36##ITesr%`uP?BF3T0F=gEzykmTtdB=Et&p}^}8|PyO
z4Y2S(y2w;jI-uGqxSKSv7i;@~JZDdfz=Y&(cyOa0I%T+SC+C)t4}-t=+6N-TnL382
z%~hqtrE2BJ?pymazSbAVGSB|(-i8>K&UIMNCy1@v7EG8Tk*W`mLgY=X2wP=0ojZuT
z2<5aOwBg$fS<DE(YW-kWn?s#c^C~s!Xik0MvCZMH<%{&0cf*l9-QmAJMriB&V$$u=
zMg(z$?r2`h2Ole@vMmKBWvSe3;-<@yDHGptU;mRZ*xuJ{uPiJS8r^+abeFvPRHkV2
zcEx|S|0jC|XWGNYD!*mP%!ltkA8h-EN@e)z)7JwwA3HutB}bH6QE>G;M3)BNB^EyA
z|9x+`Ly~AigNhW#{%2K;5S$yl^bZW=dqJDv&<T02_m)RAnC$KjH6A0~wTT;@4qAH5
zmTC)0x-@RKRnRY3*VVY)yMc#n6uP#eq;H*DnrdkW?#9vnem(I?c$-?I=}NAtZ^07r
zdj@h?vA`6&nsf|t%5QR;?)_x`ARefk9Q@Fxs6fT6;8z&rKQ>gcoqwaSfku7*cfp@%
zcj)+h6yE9eZLx5AI%9d?yLrhd-kvLb0NXS)E1V0Nf-qSktrnx-Kmj+K79%T#_LFU0
z8iScDgyBLGDNTiho3B^mGk(SZlR1(zmwCp#Zu?SYlm8UwjNid*=c?}tyt~k-khPRw
zk?{&H+*Pck<5MncGxKZWqQj_My4+D~Gv_HX6Rqn%S@5WJXsi|=yCL~7vaCwVx6jis
z5iqHT4pgUqlI8o}z^X*nOK93vbFj+wQc%5%EIZDW*%A}pL?OC<C=fjq=!kvv9jLqU
zV{vEf+^;ENJEDsi6QXMyVChix1utuY^{ng!;0D0!XdGjSeH^^lfBC?9GG2Zwh@kJQ
zpG6Tdp;{6mK$=?;pFlFT?cK{`R)MNNgP7q}J;M=8lRi4DUjT{nEY$V)_ZWsa=z4>J
z>M3rk-PLhRWm=P2I$e9b?xvmHE)%tkB8hY~PcGKtPs{s@-*dGBJX_=%Ftx_FhKILU
zx&j;iQsa%ET)7mFC|4hc@tq0oVCYopmELkz%+Ds?V}{oFeww)Myv#n@aVqoh&cx(k
z-<~_i$20cp6`ZTwdVy>~zxzYDm1?C0AoO((%gr3a4rSzgA@<YTY9{QJ<%x+n?_KVZ
zmo9n`9bqEkpN5}J{~s3s4({9!#ypwBr5a3ywm%(Jl8<b=!*ozyU|ABYV>hr1@rkOY
zhrICCmHZ9qhq%&^_*Ejg{=Fs$UN<xjd?824L#YlMbtGA1{jZAqz-pE0qJEVbPE`%d
zOkkg5ykNV*&kCs8w#7S>bSec=g#teFv9(}V-Oz7~2WGw|DIST?yPR9deW`x+2jic$
zDwxX}9<SeuY?`ow=^8$x>q8c#*wk2w6INjpvPZOQ``6S)`f!X(o8|MW>g~OzZ%n%Y
z>7Vcrp#E8Z?|>|8$#CdyQ|J%C10}vdGy7JoWIy+}iHnJsrP__aAimHlmIE4GmrvR?
zM6Tmg;`f-Xq)x@tTpbQtUe>L_=hUofl)0N#RFnYE)b|ngq0uPGgw;xXPf#_sJisjU
zI-16rVS7hhZDmOg-cF>9k1UCgZ1LFQ^`+bANXJA!Bd^IeU$EU4ie<;4l<F500Zq)i
z{t4IV2z4!_62d;%=h@O;EL+zBN2xD(E_ILo;xJ3gqsh$N`C$HEKdV43#1a5AEV!?N
z^rYL&f_qa~z2!4q`H<#xE`!B}e%caOL(F3>9y)Vu<Y#Rq;NSd*MDJB}KE&eKwLnK+
zDQE>ioA&8@!X=3tn7uiC(iSe_I*=nydhbm?DD*~Zu(~qq3jZQTK`dDkzlpoKs!!S|
z(aI5{KZG$;wWskiy$!l_BYU)%+jlF`ZWSkkvb0^R_-*B3K%{f+vvT)inWU1Davl*A
z0Yp_+2^1=TaF9Hm$&;v`1CC!&wOvSrYX1yLCHcSgZi#98Z^7$oDvDp$xQ1$UA~1)>
zXm74+&*&YAOxEtrUvPzo&nyf#w=5exvT%eVzFwB^qbk_M-!|Hb8m*u6gvHu<;q&fV
z#Q0l71j;$LW3R)CMzKx(=GqfrmLMG5;@wie+HDm<C~Ee4#Dfhzv_@9rtoMp6fpOW5
zgAVDd>+EOuj<cB31271z;as!?Z!9qp_Plim$8<H}soC}2!9FE*`E|&gwDGF`J<~Mf
z{!E7A1AytN6-vo26{T8Cp8f%jjZWyvRwxZEgdUgwAo^IJ`}l3*{pLKjz4Tdjjv(ex
zqh~4?ed$)M>Usk6H(14Ao3X5;Kn(UGlELF!x`mTZ0CkQ-HW(&(@C8&m`+FHd1*Tom
z5Qp$#ms9|LRhd$*0>RHeieaw0f_HRrI=AHJn6%<6Og1NvpS^8EN8w_MTNJ_08{?=|
z$`82Jn<~<+?N0DKug=c^^k2NK@AV@-MhXEyJF-25$ddDm#$9TO#}#i_Jwp+D#UR4n
z@G7uNe*k69VS1p5@uUA`hzr-GUIJimk_~NloggJYTr%CC+XSBuK&iXso=(@McJH9k
zEA}@uZs^C|YPZVDlszwRCqeb4lRZfpoixU@CeL*I^+u{`t%`F~za<IR!g2RduR-L8
z0*^jf>+Y?xs+&2sc$c7%*a?vV*&{h#HE{A+lK7?WTAe1VM!&+TjlFYr;`dj=Q3GrO
zRqRT7C>4L|R;Rk*08USz8`B0JJBRO^I5->_8u;a|qU_ohSxj1rjh0ExXQ-&Nd*T0c
zSD}~A)nnW*g7XPpDC6IwZ?(1gXH}GCkX%|lUUq6-uIS;`P_U)FCmYnrG8q2UVJl^k
zt6!l1W~6ydh>0i?-gy6I=*%GYY3eb^+O_2Z!~3ve@vG&U5{>HBriKGgiYL26F#CtG
z?P0;UtBuk>TmIVT1*DI-v?h1-56bo7Q@!ar^JP5{vX3s2quX4xAt_4UIcE5IRkn51
z@;hI7r@S;rv>NZeQs}=F4CRwH4=Z)4$0vuTG3Rm{wQ?pke#F#Jzs#KHP=qdZlNohQ
z>`SdDhs-XyOVl78Md0-Cql3DVsHIlSbK|}0I}(`wom5D5d$v6JR!`3n)&e`&F$0Cm
zIBF7nX_=~YE%tSe<L$am6xp|I=|Nw5nYh9PW8I^k(Ag?UQ=qLNQN6aH;6zoS8h6(f
zB@|P+`)UyfP<yE!7@eh~(I4M{4n+#1X5(ToO>);8Xyy{E;0~!-AeQ2EyD|2*2iFUF
zd!y7sbeTx2(7Ha|2)dhIMB<2(a-}oGHlO)B7&Q1U9zA28q(FusH$UsT4%QH{l;phm
z@9<uK*^YhemRjeB-{kklitTNIwJpRpap5q%=vTM}ns3t7>Mt;Pq+fD=5sSx1T?b!4
zM0~flklq)*XR^!nwVnu=5mWkmm%(y}CaySZRAuaORz@9du#K|B;&*jteN17)tjR3V
zgD|0<fLtqqOm<LwY+}$i#I`c{K=)Rb`v<q6tXc{#<)xLyGSG7V!-%=PcNc4<y<s>?
zO;8-!tJT(=2FnxS2_efAHC(_y*w&&;>JpQFA|wvSk&#868um)*z|9JCu4oQ+`+gia
z>tkZE4h&~S*WS}EW1>S}W5utS1V`i$9kmF}hDdbeWvY$`Bq^9ZX?$7AYTbgwi1Evm
z3cD2b=h74otypxaDnnn_3D?smqCnDXT^d-ey&#b>`pyv@69EnP^x;L2J;gfSFl(Fw
z-;nmv;kP6{qn@!pmwv_xlJxw6(J_7o6x+_X0w}1qAAig*zf!qyO2QrJdYi8ed~Cm|
zMs!O<K=%SG2=D`<LuTb#^S0;KB*lO(g|i^^T%aR90>sI_VeaUxj!tx+F)AbVbtMTL
zQJA`iikwR&Y@a`oo-Slc<Ix;-2ZL^_@y4g5na?lL=B!(KmSxP6irhT8tKfM(mYh&E
zb{TXx(O(7s`Q?|y;K;p+J)v;^#59$)XER>WUu`VCooF`g`ZxsGN%ptgFs+vulIoaz
z>K$$puG8<o&%x6?Yj)PJ_b?6`bz@bhEbkn9qi-HMj~uED@?Rr{{+CbwS8p*IdaJ2b
zl?$Z)0HiJvs&zH{MDw3B2jcb{pOzX{@qm}UwO;Z3Lbx|(_N|#_gV_sY1iH@wEWG~v
z6*mRUze#+!k`8J16`vT-%zl466gU3MN|!xN$s`HlA2btALEr5gguVCLIwq!qfq!&{
zp_+8ogAsRucr``T$*ZdfUv~at6yi6^s}>P$UW3e%X4l(uipzF;6{>|z@d{VefC#H6
zzibKMV(HTdUPqAO=DY6b_%=xln#VUNY((eV-se&JRS}+aB{Wct4YUkBX-uP9ZdT$8
z8{^?~#GBSe!D}<~wVbZ3WV6MjXXU$9uZ2xpId@*&or~8*mn4bX<0EMYz+bDA%gE~#
zREfYoOy@^lRrD5m1xD$3d;`qNH|v_^8tWe{L~P;Ux?6*kmk#2VGG3hFRmaKT{KLjw
z9G|$)Gvd*o8?TMqXJ~#*Qd7{Hb~>6xn)+)*uDG5_#98Wm#7=v4GO=nGyW2)U(%5yE
zr;0~8HvQMvIyoV8xbi6MW6wMF)=LUJ`^%Udva(h_>MwN^<*RgIFJ@_0^Z?b9E;`vG
zN!i?rakD|%(lB(H%J~e=;@t@oSz?pN4H5SN=Q0uQE^oXaIPtZ#vDl0y55E=N>nr0N
zUVHo8SP!+Htv8DHp8Ta)SPc$G;XgicFBMYcwj_f^*%WylcZbGdI<p<u#2N$`{poT*
z#e{Xn_ASj~S+uCe-Kw$o;}P(&@0>e8)&^VaE>8kK2L6PBKPFp+*Dmg8{oK*;mM8vl
zzjgc~aF&MY-)`96?wQ~1+#u4LAevfv>j%sc;O6(Mh`1W}Q$RqjzS*o+hfuJ8^2*)5
z$z{{G0(&3#M;JPOqiOTIk)K&}UnW!d0Qak_8TsA!gK=+CjJk32?d;etuervjm&JLE
zv))IKm-l#~km|Y`iJJ$U9u#!95ZAs#wIq&t)zQKj#ibad7m?3r?WC-^t-d02uVclL
z(-}0J&_z%q!2n=97!lQe>is`7U1dDo@B81x#2Jn_n9iw#sZAS>>F)0C?shmBX4=Hq
zw8`n_=uJ*d*EG}3|FiG!^?$Sn_QdV}T-Up<>y|B5;Ha;HC!7yT56ttxQewTz=bCJn
zZ>)(kJcb!Wh|7q-@#HK=p;wpkt_$JflOr983v_+LfcCGl{iF1#PQsb^h~BQctPFou
z;xZ^*4gqY0-O7FGeI&K#zan@XgU$}AWjObT{yUGbs>l_gg_&uS%d7QAT;EkaY2k~o
zDy8x?pYuOyNBPc64{BtOoFtQZ+RrP>e5MfcQJ%y#h0ESA?kgLN6voeP0`Y_W3KbfH
zOe2X8HYb<Cj9EFK%2<4sna`w^Kb#|DhvRX_vBNZvk;0n^3bh~5w>0;=9eHO9eR|)5
zZ}YOrAAEW~lMgrV8yxo8VeCobRT!4|i^^~IxA)K`C(3CtF+X#a=E?qwte~7KiRfe@
zV&?P0z0^eyBU0vo66qF)kw%_e>?30X7QbmQqqsUG2}G`R|F&sdSX9R!ewIY}qOmq}
zk?FtRbnF>M-#b;%nYI1jtVPztvI{UqDor`+9<9-$*8yqUQRX-fNx7Dp1&!PnlKld;
z*UGU>OeNnNFTPyUn%x%#XXba@B{CdPC}=Q&>E0ovX5M(MeR`e2xGsrDz*o^^_CAzZ
zv!?!Nfb9KbCv}HwZ+ltFnChR0UnMzW@lMqob5b9^urleBpM@0{oL4)m|3kdjO+J^8
z#_`9r%_JeSpNr7&5h~}r1o>bb<VKf?TwcceB%_+Uo$#Kx+nJT$6CPh4U*faU_2({C
znY%lTd8Q%R6zQE!Iy<QNYyJvqus$^Fm~ovDkRKTlW+rhx7v%r2zx~25fL7XFzZ35L
zD*NmExrf{hcgrX$V}r-^cAzuNVcwMXGZp^DSYQ|NyK<HmR$Wp!phW>rh0C7Z$nn`K
z>xy#j*==@y37XW5>VQPn-?Ss+ugZ5eaPD1@a|aBA{_#K_(8Y7k7Ugjn8Y{AzP<CWv
zrP%ECV{FK)eNuKMT2X%6#L%_ykP9YOy*P-`aj7sZF{o}S+A_6>929PtE{QCrE}q3z
z#`kDU8d@PWMZPLSKMWm!Z_^;lLgWUmo|fqvfK37$>&rvWY6c_jQTj2Jl|Qt6QM@!R
zdi#E8^N!9N6Jp0r3sP@X#2RZ=(kbp1%#k(<?9j>UJS_iAV>IjYOfkIT#jXSCQMH6^
zET#_2Z*u45xa)vC2H^L^>=tx0aX6bkj%L{nMW7$=I{6;_mVWRfjNpR-oZwF#LFFr1
zuxb}xhtq8#xUyYjX98B?RYMj5<*#`+vi#Ff(ta?}HzDXzi1DPg!2NBW|Lcwmjw&k`
z50^ZdamtcRzN~Ayl%mtti0H?gp`>T}mIc7po3x@KjZ`MIY0k#cpQ+QkJ0>({b%adc
zMQ>C_zeFTPTNm^5^_UUSg}`~#p)#RvujO;ri%D0#VsI@7Myzx{=YRgP|HDJGH3rw3
zxjis~QXAMsbYVEc9=yEzfE;mzemLjZ?$YDr<u9X%qSS=ybeY1}yq=q3HZiq%_?2`T
z&J2>1oj8{fRtYQcA?xXk{^QkSPr=oQ?YJxN2;A4fS%?boaj84N{4nh!yte;t+U?sZ
zzfjUUyLFzG?g)5QS7OxcAOHGM`()N6v%ctKvJj6Xh>2-tpQ8P&Rq~eV=R~E^PVC!4
zQwJRkVi?K6C^f^3myGiC!*GkhMvE_RAd7bTT^#wq)vAbyfY%1#`3K&T`huj0%NFKZ
zJIY_<{I#*62B?Mf<X30;MB=?~S2jjf;jO)D@{?HjjJ#sEfi=r+nxpYY54Y=3gYh2}
znnRX{?O|^V`px&_S+jCBaM_3&P}$nfS_Y0g6Q94sssY2PzP<gi7ARpH-Cv3?z7XY9
zPE(;HmiXbQpP~C@Vo%&#A(3h)Y52-l@nUQP?Vnn_<gFOwi!ar&|JOtbAoo2U5(wRf
z9)*@6DzVsesm5I@{3<k4oH0NbX;2KP@=T4jK{3##_FtbZbTNPnBwlXkn6vc@)A>bA
zw7g?Ul^s7zB-E%LW~*3c3BI;T<sjqQsq;5qh=Nq+y3{L?UWzEDkZcXc+CWWGl-NIV
zk<yDC2Jhg)pkV*WO^en)cRATQGzN2cV#-F<wOaz<wxQ9SLK)U~eX}a_Qu{ww%r4n3
zRd)2HOM7?NOcx$`Q=D7VTgONIJi2|BZ@2}TC_u9BxMlI@{}6b2m}#m!kH7qJf$ugf
ze`LL&oteGm*c#Krc#uv;iqL3O=6N1t^~}d`A>o{vNB~(t?1tXdwLVQpG`Esd??<=C
zY*KBb`6v{VQza4FYEe@yGxbdDwIECgT6S=tH2*r#*RH<I9Q0pvX;KaQwForZE&TCK
zJFM`N%YOju!q~h2rPRM}3tAWwQ-Lkd4~smmlH>i===wPzcUb24!{>dyaZT<Y<*fWW
z4|_wIgA*YDgf=~nVj2q9)K~Ww{`kXka?K>OoVrmGX4E9%l&4_S%@8w_lLVmb@!Y*c
zU}{WdCn>7rDk0(guzKYnhyCMyc+ywOY&!R48|4TnakY^jM+%j|(!|Yvw_iV7i)h8=
ztR&GmZ%yPgoewozBzN;72}4aN#@@+X75pN@Nf@ixC#>zv@GBbTpFdokwLE-^cPp^b
zSc&R-bm42>P;lAzW*KrA4^gW5e9&Ic#7KEk2)wa!+NAj@T|{GBbX9+#&UT}Hmm~Ud
zY0@F>e;P}~6F%9$>D;d{?GeduF(nelmu~+aslpzQjSe7*;vR;MTF2OT=R5}~``vMT
z-!CbQro+k)#*c*gm18gZ-p|d5o`9uTIY_?S7mmmWEWdhz+CGUEMogjWG?=$>Ya~r{
z@UTpl<xG*+YkYT`6RT~y@+a(r=FLcTj)$cbHx_qaq`pqlz}2+-SH<(sKYU!{rIJia
zKW~*>BDShi*M7h5$KvB~NjQN?Ahf|X-)RmDJN<e~Y~yhw(ODa7!>(uz48bkC)e`_s
zxxW=k;J)M&dslIB9<e9)>C?KIcbI*tY61ktxj6=i5dSGk#A}^)gqC=%o;Y5COIOHW
zl#b=W4QhUH(J=o?-UIL7g7nI9@C2wgVJiSm4}(s9J$77z<9|8Bm8sor@)o`~SxFJl
zhg-bN=RN=7dPuR$s63jy!$~zbk*AGYzIU2uDNiKw`sYa56HLv-4>Ft3)Z!YQ{Ig2K
z`9NF$!My#W;EXJ3CcF5i)XqfU6jZ&;U)*LYFWRF!7-twbu<Xu)NK!q|6FwZrjn+zH
z<Cb4-zuHv3ZgaR2V78YQ9#dV^qM*(C#KdGwQ(40hLl(`SQ3BI^-Xn;BJ&T7fO~2B;
zqucz!I`Tu+Z6@B1gm?rMADL1};)M2lfyg(U-rnD0{-vW;B%Bp*Qu0_J8a)?Xg^(Oe
zC-YBQ!y7jzI7?a+Y0k)8HNNuybbzq2r*tNDcxdI9m0dxX^-ar_Y@gN&C9p!a9P3Y=
z;78dU1!j>KGg3v-I!U>`vm$hD;tuRWQprnwfp4Ayjp_t+Yujl+J#PBVW+qS*j}HEY
z<WQFof6qDhhbzf6-|Y;S#979X>dp=i(Yz2cOwG41_ciIFh|WRBL=y3izclj$kG6*=
z^U)cd;=`GA3HK-KDD9A16^ofPD&u)f%NI$YT1IZpx!%N1rB1IuaG`;RrD)BDNKn~b
zD%+s^_hnn6zQL^${sZYJv=oe92PSzh8qnVYU=6}wLf0&=2dl)^0oRp>5=mnxZQZC-
zuOcOKAsoZ8x*Wc5BndHS_3{`*6`oFl^|znSPkg3!Fby@-URt4PscMCV-By!L$Z-<i
z3_W{OS*H2#YN4$<z|f$kFXSwmV-Zdtt&2QGFCF4DW3*|weqDJXwDdJv{p>~xn6{b|
zcEpjtk3XUUSLjrOqg*tjf->m-;l)iKyKGFQdRmM3=oeVL{6z*-%Da$-;Ek-Zv^|oc
zv0Cl%`lcd3{E)|*`O^PU!D@nGQD(ZaG7C<iJKxMH?2PWE3>h*QS}H*lBozAW(#oxC
z8(w}IO>|%HrOd5-{YKGiIw7~qaZ?aiYODw*C>8arxHa8eI^_$U-8*ihIE;7DaNs>=
zpM>-W`>1a3C)Oz`a)}vAZk5CrjuA*Uwfr<)Z~gDe<`H}NwB=7NFPX=sAE(AX994CX
z<}uUugHRi^TynO1x&;kW+x{!NG??TG9wu0P@0Ary{g%w>jOzNCI^Ia;`jp3ZAG0$X
zsp2gFTTo*8{8o*KYyGfpsvh=*_IOAc+yYV+7}tE_IrqgI!#=>5%DTs2)G*b4J)mH?
zFRhMGKs{!?ozJEBZtpbH8T$qS&x_~~>KfQLZrmrXPxUWgS;eZtPTPxG{CI!g5rI^n
z_Lk_zA8#t<6uP}u&dfVSzCAOR%Gcskld*IAj34U?du668EIT<!a_z;GwuMP+7S_mF
z?M@!6>0%CO(;6e!YX&aS=l`TpM86LfVR>!hitleQML$X9%Fxi3EL%whUqhEasDU<e
z`|QwN=D({TKFbflmS5pPoP(O*2aoQDPgUp;<&7Ptu3bmKZc>qyO}fMvH{83kK^Vlx
z8xnqV==1gOed=^BfNtG(zebACDas{1vd#K?*iW*3PJ%Z!WgbmDeMqt1UNlIm^dT|2
z)&$Zw_#66{-(8BV`Cy8Y8ns%SzY;98ZoZx9x!0~2{T3P1-dc;sg}tcnYHB+vK#aX8
zq6lOzpUe>Eh#};uF4?6T(^zl2W$C;JEwTEO?a4Os@z{(v215-taK{<))d5l$O5;OI
zg-QE-54b9lw_^i66g?^muhQj;-(=)RJ$$QudRP<YKIAGhY&8|0puyRu(D#^y^Js7i
zoBB2xMWMj7V<I>;9)15awrDiwGStOueCG&hJ737D{Bx*93a}(%=}}TuMm<m7fA4n=
za1E2&Zi_BmqQq2VJZBV2t#H*=W!Zal(1E3J0|L3<%rM7-tnqbk>>Gtv!Nk+;tNd^d
z&P=u&<3eg*fLvU2;}k)g1JXDT_kZeq_a6J4_MVh2XV3V2ED+L1Qth`Kecoc6{q@#)
zZoc-OZ==hVu7@O<cQZsH)SuDXohc<TI<Y#K{6%&%@QB~Te6HmdggGbOx=8aZ>zuvR
z`B|)v7{BZ^V~gN#Dvt7r{bS7;p7v{sIsb~?ZBipv$tDtI_iI0B#$QVH$Pq&ga3u|^
zI3kQ0-fJ{)qbjMN@<W=}ZFLAc1tIxf;tm|Bv*|E$)K7NSQc=}WrBa~(a#K0SDoU$L
ziL|k@YUJHIPK9Ji0QeSo#(j-Qb)Dc69J<|$FHzQF^u3PGob3CX_IS#Gu(7Ryqlkah
znSjm&>%3e!)h@P5#cijI6RcW#I85;pyBT8qSq_&oT4!}{MD5JRhepYJ+0DhUw~SCA
z?O{hIv|UhC+4%laH`7R`Nq<)z$4LlK9E|)2Ul}%tDT-B#X}Le-9#-G7J~xZ#**f~8
zRBm+q%OYCz9EozjFF*Zmf1FA{Jf(!<g^;}z)~!dX6cjw4ly}DIdDzZ+ca6HCv{fo5
z*?6+hcSzV+JLHaDQ}6lbVLN$f(6`h<-n~+@r4wGz`$Ln7U^gL$#!ucFC6!04ABEm5
zFtef-b(medSnVuW%<PNgT^8|(f*O*$i5ev4&-e+>qQH(kYAkz6^bB8|zWxmUTtvr|
zMvm5OU|e!?W8%uHLI8<Xry23-+~g+{xFY1W&>l%MljJCU&e2T{8w_)Gsm8IR%Fubm
zVBIG#K$rMGTrm3_5dwU!JCO|owG(8B9QNOyi)`Rxf)~q<1^RWk<$paJtH1)k*l+HF
zgU}5HTQ54|L6!;ae+?2J&h|JbdZb%sSQD}ak53XY?sAv!P7Kcq)1FsLTF^DrS7+O>
ziifv&kKIKt_1ZivJ1b7^amb9n^}s<x@!*+?mIWuG9LEHEiEtY&Wq`_0wy|m+JzJZp
z_unu__K%*2VNw|X8Mq;8no$qhWss^6?f9bRZSIghG+J?&=}k!QLG}3NWaz<u4INi(
z)N&j%o()rNy8<9-R|1yOsp_<gy!N0)oNPFYzgNzg&-?NpPizaj8r;uRni^*VH~se4
zu$^OXV*%g#BRqEDR=ib@^Of(Ie1ToI7Pw%=smF1<OClp^EWE%rsLLPq`?ar_8s<0~
zxEa{Qz3h|spIH#55}Vuz;-erXZ~KH0r5zpGrO2q;0SXs+WznaHUtQbSC{L%O@g}Gr
z_ANcmN9y9xXsNE|@kS=vg>pYP1l^V7AhhGI&b*Z-^hGa-IAfWL2EKWSg}N}R%}24M
z>QdG2CKG}zZ_0Y?C-d?|JY%4m(?*_BJNVqv1VQ6|FW*IfGWtEY(e+XC8ZRhis1|)$
zmoNLvM07zO%7{Uz947}<tO#efPom?O9i0Zxnc+9hGpU?ImD%^VBd_?&%0qc~RAZ%H
z@>i;?7-eRdyRs}u!uMP>Auo$qf_D|B0xd_`KWzC<<$GfqXC8L6_wER={`^rZ1S&W#
zsdl$hLOs@T*b-=q9lS`M{e98P<)U0-o1m#tnL?6qdsy<el}_(Zr&%@YeskEBCoxvY
zKQ=;xsgDB~-^2eDzr8CYQI<}29ecy5c_6W2me8x$H?wwZhJUS>Z-ifr&z#~hTjMo<
z`@Pba_8vf(<%ZUx6xzYp11R?+Tk#j)Iqmpf1v;*7Fj$WMQ1P2#1}WP2jxD)wD?A8@
zT5*PioX2Y8oG-v3JOS0k9xq4Z$&t4-G#NcuMl`}Zob!|8?Zd|Yss5FXKEn33wQ|br
zz4`Awl36Jmv!@AQSZ#q{D6kwb8te5~+-Gt{B}d{&8tyUA(pukVVRXPM)?HZq18y;6
zmEh{uv#aI>>J+ELZ9XwSh43e7!dq%m{i?=dA>=RCaLWi_<#O;vtta<dvrK~3RicLt
z%!ZzBv~tBPuFw0cx$gzkdltv9lYi8QyhTsTG`)&v!Bo3~O6mr+HNsEsB)Ui%o=n4e
zRbrC=KMPRYD|MI2X#+=xE?5ubivRCWKcQa_E7~C3%&-&zGCcADKTz@9sIlbx#R9ZP
zHdgeaukM!Ms$fh0jQY_jV9Kei4lVx$UC{N8FPWe-iMRGSS#mR8wztygP1TtFGyFXf
z?aPXFX>F&^auu)?sbwxT!8)0773$QzMhm0%miz29ilM-L+U^$41#1WrL$((kr}pvX
zOiF^1r^a?gLZLRK3QW6?2(P5``zzmS>A~OAyGKFIu-s+NX;o(S3dVw|y?RAGmb}-m
z!03xrh;1g)Sl1dOk3URDZ~IXldVQ}KZVOO7n`blHfbcF<f0KBA*ddWzWl7_PjzT=b
zmq^o{zn~qutqEzeIaLn$=M_Z+nFp9*Pii}9MI!w5xiBy#*bpI8qF0+O?Q!4Qr`P-v
z2*(c`_q9dQUK0oflkEJV0ck(g1L2Y=Fn<RLPXjBP-GDVE7fTS-nJFQ$;E(G8_YzGi
zUwOp%<xpGdtLe*TKI4HnIQiTCo(?VevzXZ8Ac-(_Q@V5jZ@=BaS^Bu^L{hrsFH$xp
z_YG1)GyStM2w%!T+5>+ke47T1&!2TvLhozRwpp6TZg&`}Js(h1HXTn09F=nCy}5Rx
z(VBoL=~H|hC?^MG-QF)E>+a(z1}+p+qwyDe?2k~5xO8~nXAnq43js1)TswpI6RwU)
zq!9;m6bG@1{C@Z@eC8%j8v83Py%K-Puc=ZC_0a|lLrX~-isi!}oU&hoBia9BfPS$A
z6;9gnCiMO1&(<T*^8#U=c1ft_U))Q#TN(S93iS*j)pe<tY!0t_dmoXM+MknqMjy_*
zvL80PZG+cLUZD5*is_M3pLMvTfIQYUZ@1I%E`o060YScb=g_)Ir;S>?yy{hh3_x`(
zw=_6qkAC;v9!e#H4RzYGWJm=LnJmsNlFKQnl&R1*cZ7FZ?jIXIX?woJp1ll8C?d&M
z2I8{shDxz@Q?eAD#`9!;te7Xptn^-9Cn{OD!*UE)gUVOx&kMOT7|qMuYR+X?hqzj%
zZt~~=N43dD+CPCgXXEg7V;PMBqnDGpDQ3QAfsWZ_@*~VnxIBbXI7i*ntV<Pr9Au~q
zE^(neeoIu-GIBDQ6_hruW&s`nK)#0WcyKOS10QO&B*JtL-m;bNDFp0!!@NGN-IqoQ
zk7r|JxRUKpq4D^c5QjUCTNX%r|J`vit!TPXQVksOvkRcqp6RYquyexbXMm{7K|+!`
z27+}GM7$mkI-Ngy$y2CFAAWekSzy5!J;Z3-iW0B*oqtD}QUP}o!&$InOZk>5mu0L)
zluwCAprz7St+853j7teP%pKdW;4o4OquSAq*`)i$(~W>5wDKK?^X9F_ygdjjMK-Mn
zWr4*fI$Y;qzeG9ZY>UJpcF63MX3j~k{5}tYx!ZO#7tvR6{IpLc%ehA&FT8B2tB$G@
zK2;Hd{@o+FRnshDKfqqQvwQNpKecB3T}UN~^_IX82~l-Y3?^ALske!^aM)W*r1}2H
zgt=<XR)9G9AzNc9jS)Iv9LW4?4p|xNZmyer#wThuo2Z$28hic4vrVUZ{2W5P*tW-D
zGNlZOcx$N}^c6f(_BU-`t~ZyLG$D3a4&3}?ZEDF!BS%6U{M#(RY*&l!#wW><3VbZL
z4LMOx2^-^RzV`A`M?dbeu^TjnfaOh}J)Tg`qc<Nkw*lgWGdwKVr@1rf@y4(f`)$<N
zFP?tgAG?gviQ+MZ{*&ZqW3bR1VRbvxpHrb92$0ujUp3?Qc2K#9<LGpN2C~Ng7?fFr
zBk>)(xvuomQeJ~4d5Y`H{zS~t;0LZM2D0uH^hCC2B%~AuJl6$S(R&e}*gtAJEb|pv
z>ml&}2zjg@bbR%jVxOb92fbnRwUeNs(A_1TSmz=xq|uRrPUe~w8>oUCXkdB!lzSR-
zj6+JTI&m+%;pq6~e~zWT2xqwPT~0L<##I`J`Y*bNJ+Kl3t0YRj5uGWPq#Jo#c2<SE
z6MB)3Z#tC!b5rYI<fb?Kq94cnE+52?fc%SD{C+FsK`Ir2VPB8f4l)3Xyl#3Srnaqx
z_|eA^a%ng@&-oQxxpZ?}^m=i)QHzc^KcWqVtJ_E=V3!w?Fq#Nx3CFC>{vXg8?fJXb
z60LTK=H}Yx%kC<rQ-fa7eJz46aT;j({n#*HO$XOB-j<G-Lgw3mIkY0;65&sk7v|rX
z${;mw@>7-o2U<wne^MwllDoLrWG#yl`t~Ujg;1zo)nOj9x8AudMxlw2spOS_;-*9V
z^76HM5`|O*DTI=&yt$;*EXA(^6+@f83Qt>#akaG@<>~W^Z*ia9Rv~*{8kJ5GG>;Ct
z8<yO|hFXn*CY`xZ#O=!1iSTkAL}GaBo;3eIOkwy%uu_Zc;T+4=7^@A?A;fJG{yc`Q
zB;OU&+*GOjGCCTf%byQgJINt#2tL`lD<rB!nU+9G<oQ<`=pgXDV(V4P`<}7sF4OiO
zS7jXt<qSdcyGBMDPR?xOv2T3ts7dPQA>7aM11PNXkR0DS<EAhbV90COZ>aha#Hl{8
z=o7K=y@Bf55=$cG*3~TnHZb)3W`xyn%ZQD?a^*9CnbZVYwxypO5J+RX=9ZxmAfK<+
zgss4p%3Pjv`5r3K&6M?lVtL#(^I|maz$2WP#x6FdV&GaAD3kU^Ji7`%kJQBi3dr)@
zggVaY=-Flduzzz$%GiGM3?~^QXWcK#;!U@&KSxf~Af<oP!|A+X{9E?HQMwy3a>8=|
zu8tq9vAhl6dw`Jrj9>?-Mi-44Ql()xYG_-UuqM3)6{wVUhu(H;_x)_&`-$6%<*C}D
z+rbx6OXru$PJWbRbkl%r{{8;ry$G9E6sYX`Rlv-xE_2h_=(_IBF{&Zap_o?~C-_Hf
zPVGibDT`)@U-0cMyTHZ>3A5SGMz5lmy7+TRv@y8OrXK~#`$L&Td!Z@YC}{d>Bkh~i
zLVIXLh9vnUWdy0|JgNQs45aC}XyCR5uR6qH^rw4QbK8oyezpG4d80oN2(`7!8EP_^
zszWc*^zcETBkdT}&n+Q_GYQ0Y_)EqsLp2k!?GZK*7~bl4?20|E*nNl8A&_njH!2b3
z#iJJTT7Y$EU=qBSLqZ`YOalochVe8G{;=@xE_uUWQ-KRb-hb}}&<=cyxLSOao-^^U
zc?5HB)G+pC$%)rDwLCk_*B<^57GLAOx}I>4pJQ^%BAKUR@f607g#U=tO?1jyXs!qV
zY!!^y^U?;4-NMG`eme)<+U1l!<-#`8tU`PHLxP%^<EIJ*rc$|1jMHu6y@<Opd$I`7
zJY6$c9OH~l6>zhBo${@3!$HHj;2Dv}JBRv~K#jY~Nk0wiV@xGxVm^(4=fK0c!3S$O
zdMifk>b>A<V@%Byhe1wD?QYpXhcR7fqKF~6SZ#elBZzJLkDm9?K}*D|Xcxt%n|^-C
z^J<Jk7K9?RGn3bA0b%sS-ZEWABU-(51(z%^2wB#hVLOy7{PU0%Q&lIo2xN7?B^FPb
z&uY@&?)o0#w;aht@Jx)!``|OO;sEM^KEl4>RG{AKEV}yF-6As%oxMkAx@ihKCC#Yq
z=@HH*MTCkb>y!f7*)xo}M2MGVbt42Rrda!@olH34dsEZ7KgB=rTGgwvsLgf{8fZJo
z1hxIkT-l@`9;P$-R%=R?=*q8@%F<aq@ZKiB5*s##IydFte>}8mJw0XG8OT^j_1lq)
z_0<y=n5w<UIV%Q%aLbLr(VsK~vp4y3d9?qwRE@ka0fyah&5RJaZLgHC=_jG@1!~b*
z+Ztt30y5UNa5QhffpMVOdVbHrtzkP6_Y%^$<-}u!&9{`}!P_SVEFq<!*x8+p4;f0=
z)txGLDU@21wYz?KWQ<5boi~WnJbKW$7z|A7Byf$goN#ae-#mK5Uk0XqP8XS3e;2$s
z`v%GDof5=^saL)FgWI$W!pa%wIX~}#8mp{VoTNB2C_mHYy*eaulrm#+6|%J9YSwMa
z-Bghv{qM7_CoMb2mbpwBbN>e+w8rTkLuVpn_!^U#`F54*%9EpO)Pq9((`oE<IL5DE
z#8oWKkxP%I<#3J{b-269w>toKp#O9fWcX`SZSkHvYm=cO{jW$ui};swn-NB=ok~nI
zmNP^{i;K9x_;Ne0H%S&lp%Q=(%tNMS^7}l2x7QGc;vV4HBwgYT)-r`}&tEb*X?@>0
zM1vm81Sfpyu;L!%U=pFSj3N^Jwi062#mqZC(qoVB(Gg{&j*tg`o@IoL-`V74T96{N
zTs)uhZ1&%T$-)st5>MEE{?zW}Zfru?BJSvOKh?CJ(o=2Pw~Gf?urXUn)3#&^4%}<%
zt<$1z_n1X#38j-yUjG*y(T{+jLs^_jrcwS?G2(wr2l`2rR7F(;swDGVhyQ@2QJQeS
z`zCJ2a4f0l#duf4{ZAhK8^%v!u9@#RXXnp@#)^RBU=~{p#1}HqNpm`a))e->w`(90
zEUD6PV|0^;(EaotCh7=$zaW`Qj|$&!e&;k!Td6@C_)2cXBNQFXVU33+w@I%K)j_Wo
zJ8X2j-!{2o3qms^h9!L{-u!VbfjICZQjVku_k6q3?RB1nb#GL#q{Idl`F+bld6|E(
zc(lPNrjTe&T_BeS6Qru$OLWrRAZ6aFbWz}R5s^{|{}IaDQngY_2U(BB#kty>dmU?l
zI7<0LJWw}$C%k5jP@4{kL3x&hm4tQJzQ+97@<SncJJ@QQPaX7Aqjb02F4CCJc#S2}
zbmhsq+f5h5z;~QqV5T+icB_UE2qr*>6K=a4ElV!TwqhHgPLSB3?^Q+%Zia_rX81o!
z19R;`poiB&G()l_E&uwe$J<jeVKmb?@KgH(-_O1D2#Li}BI1XT{nlk?%U^-3cybG0
z@+u$!)!jGI&zB!=Kz@XCH$72*Y^)$NKddlRB@jx{i6e6G9wg6B4yMj>=D?(XvHv9&
zg*G^g=3OTje3qJ7%sr9>A%%D}Z9B1uZ;csF6uWz0b7>Oj5IbMj%=|g<W&bVaS~yKT
zllpr5+5rz&j*<;7M>LI#Wd{8qnJIR^4ea<@SM*(Aht`|M+UuNyvNW59)~KxICOGT0
za7GC}H-T%;lkJiLG8j|Gdy%0LD1Vt8EeY*Q!ke%u;aKIn%=fr&h(6I6nGVswxcAvw
zmBw_@8;rgThBe|ykijb69lw37-;}^XvLP(kR$K2P&%KS&nYB~W2Ekni&#JM_lGm@`
z2gm%ja-H1;Bm|}3B|;m<Df&A^e&zPgRP)ACVgHYy2S#D)*#8Kv9^b1!LlxF{VGHu-
zx+$kI+sWI;LW3f-Q!`gma}Ar7W5<RsEdA7$g6Os;vU9K7gR|78y@Ot;K5m&!C%k^W
z-8c81Ms61Ll<oC)KV}bCrIL?L{@8K7k%Ho*yU1Y&9V4r$p9v0r-1{!lmhye|SWRn)
z6HoHkS0;82+lF`K-j~P=T-DWUUDnYJ-2m2{^er=Q7|u6J7O&t$R>yt<l^pgew&5}#
z_H*+LPp;l?N*j(xg|m<MAmWOZ$P<Zt3<E{{$u%Q3%%pS~($D?onngqlT6cbZ`BU>N
z6Je;VuerZ_=yQyYOJM4NYgZVM$>RJ)G%DhVn20D?v3p1-dYUG(hS?07`*M(@;#p!7
zBwM>{ql>!T)ZEBafmQ?kn&MItMvzbR62EVKL%a%<rNKYbjL8@@UB!`)M0lGS!thnk
zmfoTf0p$@b&l@mT3^}+Y@@8a!7}57Ir~Q>W9}9mZr=#={BtHy+3jlc9vc}GA?T2O#
zki3}ZwT*Jl+T(GZdb>+X_Dj~y`*mWH=oo=~H`8H+_SZs!i*5n*au0J7RY0Dd!a7_s
zB~G42FIZ3n7p?rj3Yksj503Z}39eE(j7OIsVc8-^t3u$Lu9moXL{h5rjp|mw3P?yA
z!Ao^)565A})PiZRGOAi@KJ+I&m*kS7pij3VeF^R!A#Rng_<FJ=Upk2iC?{Nu;L(Jn
z(Ih_p&T)z4c1fbJC-9PpP<QN0R1sqHH^eMYW8o<u_I~D?@dKj=^$CAPzi6-!vjp3H
z#J0OJVf=N8%ume&9wV|ceR<hklF99jX*yd^I8+%d<XgRThg%n$ig!OE?!tN{QB!0K
zbpR|4Can~|P!6*5*p=|;SY{{fHlL-xuavnQ^iDYb>2uw70f8yE+HAfg5%u{09V_vi
zt|3T5lVwZj^ILH&_U)FKi1xRu4|1tNCt-(7hIzNUkHUhA$)tL$u?Yo%mk=0VnWFg9
zHN9MrQJb4-FUF)smj1q<7_(c4UwJL(3Al_#FRuu@O|3Utpr*d4X@QbU9EL*c24r+L
zY+>(M^*kle8|WoS_hT28OOK+m4LLbJ=&OWD!2kz0Mk?{`lP_<x+zckj`HNQmy8Qud
zq!@CfMD<|jXXfbmiLDF2T75+@b?tR%vFlEfDm2|Pn7g#+Onb20E}nDKro6wP)Ra`D
z%W!54N}yB%e}2Ud-h+%N+~zZ0T4j9g&krg}*%PaDv7x;oIWtPk<lojZnDBfI9`y-S
zC8fs9=`um#Z&>@%W5H=6XqvG$|ISAY5bPTZ;Sn!#a?+Jb@7XEw@`;i4F1f@kT_BWn
z*Y!^Wi~g#kIX(4QEniVc7?ErNf2K(4*N=AvZBI|37S+S7t&UBtt!PF_wtouEcBFZk
zgFg@cBKv67nEb!=Pub_T%hee*-W_EsSO9p0sU?&g!`OkAIj@y+uXlnsD}KD7XK@c1
z{J4?79Ee_|ZHF{=Q3B`D6z@1HOom<k@ggbF$6M)UwQM<(in$mFo%+cjp)K_gRuar?
zTe;!rW#CFtwN@h|wLiw?`;i9F<_x<I5*uaxhRkSVi)QB@>@inMS&b4oP2kyDIhk(<
z_8^p=RE$O_RUIzNkhDQ}3$9)2GK1CAPEjx)6u?#Rb@wvC=GSDL)ou;yLOzo7GQoS5
zAE^>*Wj2Yhp=ja}HEM&Um=PtR*GWZ9pjdkUp{~eF_W~U{CS!|GObG%CEoQq068D7!
z_&jzIQwgugFrE8K4G^@_*=Cr^MgQ31r2*Q&SB}`j-dh)T2{KiEzPGBw&oRB8!%mty
zF&J4bk>zgTK_XB&_j&E&Nm<t(hAXSuWf3T5X|*t{3cVV8RDU3}&xyUUiA<4YpWEoa
z2&OexSud(HS#G+Z@)C^%=f8Vj{;uij#tsHf+x}$7@MLFqSZfS!spBz)hjY2HbH6>4
zV;IBwny_piCE?C^N*8jlgkNv8QMARJxvLanwiRnx6EcRQ>_KbdTc~tRomJArPQU|j
z7~^JIC3t9UG<|R=D&t0JX}>O1b0#!t_~&gxt%qGAIuku%(TPSp9aa)=qjKaLRADMy
z=W|ZXNBt^t!Ll&-2viXIU%f?(o*?Anq!0$N`*}hlew^CzmtYB~e4g=O$%pl33r;H*
zAfRdcQtlTHtI{an0y<h55MaBWOxiExo@9xq`h5x_s{Wz3r2!87V&myY6TyPe0uRwx
z(K;b)=+Rg^MRem}ED%2AF{&%uY$27qNAiBdeP5&$Mp^P+kTw3(2;e#T@}{|17L1IF
ztZ0PgAGe@mp+n`D_l{<9S*2~+g)IN-h&5=t0XR?zBFlmS<Tsc{R4Yq_5iKr|IKnCN
zp=4LOK_R&A{#E08!0eh(kb1B!&9Q6LDdBtDGn!==EIHMsDRQJJ<H|9gl=PZ#(fhRs
z-&|9ORN6kn1nm_xa3g<=vYQ}FQM^fr<qZ+ykcz?m)kr)We;rx0HUeFXSiV&Wh~wz?
zVCCQ<8uJtDbz05n$fO#IbkHP;z=sv@4p}I^BsM}6UL(VBInF#+eeUDmO!!yVyG?mo
zJPasnd<h60z+A<eIhec79L4ammHM_TIE58Dl!4w)WF)ra71TxYOyF4~gT(P`{J3oO
zpsX>H{IGir2_m7!5rvsE^VQc#99lPVaF0dL&47srF!Y$TKxt0?uv_y4LR2f+iXFk+
zSooJh@<W(B+x|eeXZa`WK&tgR+1I|Dzgn99H8*jaD1U9;%L`yiYV%7PdY<HewP3Zm
z{Tx!khLA|--u9ge-f_lUPyzlh-d>zpcayaOVjpdIqb2kBmk{g%9Ws=;!CpH*(=4|m
zV<UG;t9cDQD;}l1>s9i4I5)Qcf_!hzF0HO>2ruxYf&YaAByUZYWyxap_-;EM=cIri
zTJJPvj3B8M@L=z&^hr`73Flui?9JL0%)=d#e5e-*AE@{SerFU;fKI2Fjh`$iia!Sv
z9%jMwIVEs<=LFzJtc^J)#9a&$XSA+jkhM@dK~wNKy+}6-1eW+&8iYc6q%ZX}6vUKT
zQma1vJ6|2$|F9GL;&!q_LbZqCcF)*=AeJ}(Zjc8`>MHC)BW1;{3n?FSgpcB3%DSkV
zc6o+enLJqwE|~M>@WV6E4jEoIOfYbky(+~p(*maK7!%(QFFEEo6i1<PW$`GBQH=b3
zZCd@j;NYIdDAZ0?2(8$o`NExK5{=dgolCN@0!$dehU?m`1uuElUG<-+#2y}Brmv;r
zt0GY4lvbp%jSe`?-F7}X&azl$;Rph{LJY1lH8xn)dWl&Ncb6*=Og)qe9Tny~q9(uX
zOKZ5nq&bY;9(D@X@f;oPh>$Auqv2h2F$qb#G-^pYq&EI6Y3!G0Nei0qxNOdDb*#M&
zzhAy?cHg02&d8TE7VPTQYI*CktJTR(IGh`<l&-U_(COAt#6mD${E=O?VEb1@>RA`*
zrAZeYjuo<<Tf^iYgs0kF{tgATKUtl{)jIXl_krK33_OJ9+C<FR1`G`brs8Y{om5W}
zvCUo}IWR4d6*O_fa|b-Ncb1|qF5)oB_xOuC2*HykoW4dx5YN~!%H_Lc=OCsXYo(8u
z$exW<${@wo51$>8`5OY3tRtK%4i*Thf{A^)lv?JEHv<Sg4RKXJ;dkSC&(qp_XyLb9
z+2Bd+@NQ4yLE$wUvw$eEDIzB*raHJ41xS-LCNHYzKd<k7AQWntg2mDn+Ug;xpS%*%
zW_OG7(e&M!x<B20M)-_YgGGFZoQ&>Y(j>)aHP^AlYQiK=l9&s|U%!%WPx_4r5J7>j
zMQ{@sD3}2h_Z+7DHn#n>e{n}|N>XyeZ)b*JEveSiuqv{O^?7ZSRaU-I3RRp1O&3{L
zi&1*kG(WYKhrfxs`EH@b5&R~$zK_H&38%4i8Vud3MDF&|c!?^>p62bedmG6#v;}xf
zdD1GGE?TW=(pMQf5b}TjooB#Y-=-*{mrxj|_(O=jypG~%Xb~nG>t7hgOmhQ-W<C_G
zg{G4tW@$*;%6wiRzwDlgPbMUGvBP)Z2HQ@%n#b5-gs_)O)8|(@aUpNvDAyCRV()CM
z)+};WRTULlg}96%m8^2YJp#0)I_kugZyCnbw}x>mGY(@f{X_l7FY3hl9FOSl*TM~I
zjPTA5x;LJaH1vp|OQ)1!_lWeHJ{@}BKL<y51+76UBvry(HaAXNMPe13y9)k7<}Zqf
z1R;Pp=~_u%A-Pie?gc%*mj|sK>60kUl_9f+w7u$L2txmzRq-RT3~Z$1!%BQlN{f`h
z)CXUyOo+abHWF@knD`u76NqUPI}{pyUaPx=$C)#K*7zUg`HnPXFK3v<gy+!wOSAWd
z+|U`MR~{r`w==A&#kE?yvLvp`Mr=7Jc;EjL70^E}!i((krFlJzhESyrnv{FQoFGNW
z&*CO_ri6eYF#P$a^Ct6NEbJF11g2@Y6(NM6Y?3bN3bIr5FM0e@qq6EA+L>$qQFi(M
zn}Zbph5*XLWFT)?{6?N6<|S@5K}EaJ;el08h*9P>6ABE}z)2^gO0Ej8I&+4q+p(>+
z>z;_d-*f#jrV*(3Wb1R$7x`>GxpM46)@S(?f=yNIX_L;cQewT!hp~J*ZkD5iE)4hE
zv89h$rNmefGSo;`OzGyG{>AqZ01&SKDWNW~C5=fd2aisVJWA#9;BZjh|DA_Ml5qL0
zKObt8cZt1u;9|Z?@g7$GJ5g%r+f=emmQdx5R%;jyB=cSIT;8Z869cnkXZU<+d+w`r
zZp>;BSI@PtmqT^2=DraH$-njx+FNq1nG<Ob+<B+eBDrnie?e$;U{;aSncwxpFJ3YJ
z>Z)`u;{`L<K@!k~^IrSgo2#&!ut@XghZ-Z5#l~dRPq>CJ&`}X8mv?s$g}0J0uxnR}
zU<r*`wK{fU@AdB!AO4ceXzlH0iRbvBiZ>|p^=l`kMe|r;_irsn!I1c$Hw6cr9B#^*
ztJ}|8vfqLy=^252OONtG()SbzX%jmIYPW00x<evDQWsx?Kk0il;SEnBq&QZ7T;Jke
z$nQVsx@9$b!O?jU>ZYlRQZTH$5f8;g;St8|-`F85g|tD9jRvG4=~}icE7*siTystx
zJPfTTNb0@#$AmTW7M9l^^AMKRpEroG_lqaKF}GyXNVt{wFOZ}goVV9olawy%K6Sy#
z|AKw^Z2?p?n+@Nh=IR3G`6kSXd!?i1ntIK&Ja+gZ5WwTuwPp5J@eQa}f`g#H^$Dsw
zhTa#7Tbd#ic)WIo<pzCJXZl^b(dY@34JGq+aCGBh6mUl-5@U5%&(p5#zdpeRj4yap
zXp*E7HG>xED0RZBxL?F?iSvh@=C#|i8k8Gp8UOAv)HOW0DtU$*>ViQWulsH*wWr_!
zs3`h9=|(gha5#Wm4zI5RfpT+C+IcZMig7qRP25B1dZa0`U3Zhw;U5pLe)aHh$tgtb
z=kT}a|FZ!5sM$IPE3=jp-wvD86g=)cUP!~D{fina`4i3_?_V|5OXg6zak5~f>TBFi
z^Gy&EL*hrT)t_RRf7KOq{?dOpgFV7hx$owW2V+fcCJ$F-RTd2L^AzkKmvHA5-)y40
zv_m%`ppB=fJ}do#-H@`69V^rItLF(4^*}wALic$;L0l+v%6J3BE$lk6&!Pt_il3MY
z4_1Cd^{uLw?Rl0tf`iWJg@;!i3X+X;++w?n>?!`h!;~1q{m~KKWuo&81zMOI5Th`T
zszRcu(~Zz+yV|9~*&B<0z`}-nm)YSn$^I4CQetZ9!?i0ABwx6WgRRx{QL|j%H8()+
ztEKG1XpuE01ZE+pnYj#Ge9k&ETg8suPOKhA0=T}+&eF0%eqH&hVz>x?S|l50NMi-Z
zYRw5jbmqN@J`#KGo8`A7f7pLYD=RRTbVLgHYn~|_P!<o8=64S}s$jrIm%z)z<|uoc
zjQx_}na0oq3De@)41GS^<u}J@-dOEx0S}MsL(T??hBmXN7OZXpG$5)sb+4RVNd7oO
zh~phs5(YKuO)LDIr(C95G>y?7WS8Lsp&+@Onsg9j6a5u%shW!zzwcj!k>E=x(EakV
zW`6xAmW!&T?Cz2fjr!m2ViB_jbpXnOgho7;IMt~YYsO%uN!Wop;QA*9h6uEG$(@ku
zJ9$i{W04ocUul%OxX`PD4`)hK18*q}qCxMj8$)#>99IZ>j`u&@mNg#*_V86e)i}!E
z-RpPx#YHeLXm?PnO%i^iEU8DbtpH;|-_HHvX$ncFu}Ac$>>UR5nQx0FqEhVVg-yZt
zTH6(7wDzy0s7N|nzV?5CO5~}twL3=aH=)Pi;8pj?@pvrZ)r;>4O@~+Ysy_os1<TRq
z1S16VSR!<&l{hf1_fN<~()PXYoE~GfJ#7FD&@AuB&NZ<YN?ZzNK?cUJgVcTAXT0Y$
zSQTyI2#ecd&)D!x?fq|tk`@Vg>%Kr+2V22hY~zIVbU^UKt@EBi>`#P1>C1!A=xkc2
z30AoTu0B`Q5&+!w4jW2igv$5BrsSQj@Ac9-pU%t!_;U<r*fcPueiFWDw(~B{dvzxN
z=+k;U@{ONTRNa>JGTgs3$0~nPx}9h4?C#UzOhpKs^Gg5e<<xDdnd(WHYEZ}DGx2#-
zX~OE2&$Yvmi`rO8=oAP=Q*9!Ufqwwk4+`18zr@X%+ZiaNipdn8cUnkds`9x?Xf{(}
z>c_3P&t|N-AEGnA$1_VPd%9Yx`{G_sqhfYeCd>P$^2h6<OBIWwhf<l3Vtld%*HYYS
zvH*Kieb}%C{!z2_i<c@y#yc*=!AHtz9-{VKlxR=V7V5%_NF<P~*6u+~{tOaYRw10>
z{B+as70s-Sb#uWX&uyG|m22r`iGumV?RkKRAW&-frSdq~0K!)}W)8<Kb3wjxF$=Z%
zMN*RAmhqMqAZ}+Eh|h6AX!(S4&1U9XuWX4q4S{zuVo4cBO#yr$21_Vx?j{aIhB^ri
zRi!}5qXcrSG~ZDG5mO@+p{Yvugo75^s}|%dba;A)713tG;e4AYPSKH%<nd5n<rnt`
zVQ>5-U3BSMsiCJ5O)T>?N^yfVWsq_0|BXJBn*eq@Z?xCqA4zjo`2Ipf+wOBr4Cpeu
z2E1~S6Axjf$~E_fV0Z6HQ8U>m@fdHkvm$R-UV#J)_e%<9h#E|D#f4wF<eUDcK|(y7
zkCVF=PA;KBy{uGCQbmp=8Y05qs@_NYk%=AF<pTq;C+}L0a`%WiQ$G*n;U!*Ucqavo
zTT)!FKd=p-7<)?=A!43(XT#AC%WE=Iw>Cf;Svh&YkfSC(>+Ai;cuAxLUg{pd1`81j
zVBm7N;DyguQ{h<nbklenw%tYI1RMRPbI~o?rfw<iHn}m=0p7QFt}1_4h(9ti1dQVY
zVG#^7yBUIUcGwvoqt&^>K-hW=BEE}7!+XXcNijlC&$Z03MQiO#)p_DS${Z~x(5qC0
zSOvWU$F3B25(LPanX(*8W8gQ1&uSQat`zm1FfLM6Kt~~3L#*aN9iq^=V0Ay;Px140
z$mYHltf$n#oRSCRuADd*DzSf$yYmszH`_sf_BTH6ym$oZ(Ri6s^Vhe#UCMv?c=?V?
zT&;JX->dePY}~>4C$ptHjA{P2#S10d_1Kj*L(~m}nxyUJ`083rCAP|q4}pHJEE0$Y
zi9Cf>Irqx#Dlpu0r5(FDuiK<1uM7?9{;F?LL)=x6!okJ0OA(&KiqH&r-%IfLT(xA^
zAXI;QfVZW2<))jV`?Gfm6~Y7|zNRBysX3N`F0IqJhkXn1#YF=)QRxb4AkR43KqX9d
z=M=GMbIxDsB#Jbl|I7X5>tYboi(X;d<vRjRTrnA=P2P+W2HSM*V(GhngYR>?=tFbA
zmzf$Y2z$*b@L(?j#$w?Q<-m*{-(LP3`AOcGjz7;z4KC!|I0B`NMW`J^N4u(eSc%Q^
zl-WuyMGxO764!&dEV#;4J`0U>Aia&g5#vKswpX@a;ba~>vhd~>j|JIZi;;aFUh;W=
z(I?~{y|Yz!q~w5nmy4S77``(C+gg>0`7^wT7t&}|RXgL>k&?HE(VWmotx#k$=Fst`
zR{N+YNH9%lIpG_{W4!sOhpUwYwQFTeDLUs>o!r0;6v?j1y!2X5rKv?cYVs83aSE6S
z<|i+8vVqtZn21`Xa#(JKj|gAhhWsA{WZds&+^^{-Pz3jCe8<h!anThIQ9~ghOwLKG
z@mTgw_}e{+ijT@Wb@rC8;DZ!{1GNLH>EH<axstetL<8~kn|1lU)u4UQWfb-zEjBxr
zB&!#~9ht_z^zx6hblv8ubI{I7MFp8SptchHR%tg9lL8m)Z_nDnzD!(N5n^lSe^1}C
zWK#*craBS5L{omk8)de!{Qv}hD)#-&Xw?p=Ec(?M_07gh!%BiG`twu1*$*SjWV*k~
zEab~9SfxSa=4ny7+N9)!CB%r4rBzq(c{7$WJ%&-|9(VzOn5$?m`Wa7lY&3<<tkd*p
z-fJ#%fcs8f9|=KC>ULMc<jX#CCJD6OiSl>x);9SMlgrkimQjb&5Bnb|<F{QD4G4>f
zCl*n?yobBSh@J*MQ1TF9aTsuNS~bmJlOFSD?4b2OU9Imv>#iEZjDmRE=AE*VF?sK!
zoV@c+@&}YQLVGBcGiw-2xapWNIur4NZeyjvOB~$#?~elSsmd>n?<PEbliwsb>?ir&
zcgDwwgo^S<?WGkh&ZNbO22r=-^*Ee0m3sx?SJgiQf0Qtr>>3Mi#u{NEilzqQ!wpjy
zWrb^t-|8!i*i`qSf3zSS23TIE;(rC_bD?=6|197X7q-0M5p`n<{0<OZSZE6ivxYNT
z1WS*en*q9rUKe9??W;}j-dhKW2CXOUpQXz6ic+T*0w=&;hA9D0L5CcHvKo!1X_I4&
zo{zPlcAwPj3#o?mqfBv@+fSV2r5R8}E|(+t61|(`0_zwXz$nGD_YU1Hf1cR-M-B*Z
zNjr`Iy)9EC9*@UNDRP4@(U93Z@0%?^Z_og;9^JpX{HkzT@}Sw-@=je9G1_0{maCDY
z<fZr>G;+3{hK>f^Inl0P@wd>u!D)RhRML>1y{>ckdG{fgaLz{PW)GSkV5t}w$02fR
zPa1kp;z%50ygxRWzh~76BX7UuJ0Z4``6Mda_e(ba%lcPMiWpqHn}Wmb@0pn;UPivn
zvWZ=^6JCdfLn)QC)-y|rllD`%%=%25jVIu)AJ@2ihYevopj^jBr^uzA$U9wnAa*bX
zY)83CC#XLB=6<t+hZqICr29GG+tU2v0KkklY{#V!cz8rVmt2A2WDZ}Rx&(q3VtHmc
zY_uHEJjV@A5pf-?9>KmfWpI=Go?teOpg5A1#Ln5gXe#VzAq<@Sd`@;{7TZAQYXr0$
z)ihcj;TfFU7{9hA_moip_BCI-xta)zVJaF<Y7QKs3+_U8!3gcfR}^rb=v}>hn#k2$
z*m}#?DyN$1XW=%q3tHgvU8(YSW||@$X9=Tk;>dFpVL^x3+z%Hm8h);cl=9$;m8_a~
zNl+ur6^WraD1mDLC($ZT)Vc->zi{XW>t{b8SX3M^{BMil1`mfn-fWxnd=*o-CebV~
zArznzDyzz^Qpet-`9lJ`U->t>af(&=^4azvvrtEI_{Tux)20qK4~O^(mg9<Xjqa4c
zYcDl#prW(fVgVv>G2Jj}svrnX@LR^Y-5_Ol{TCdzOk%s*r%s)t=^Qq?8@gI?Y;jVY
z0BJQH{$ijyQ!-L|IU};<T{dz4iM**u%hgb%!azG=u%(#oZ}Y;!TZ-qXB-5KG2A~5P
z#zSDh!)l)&$AL-i`3)U@1PuZPj-okahz#Ijfik~PHAYG};O&X0huO0+gW`WlnN@Xk
zLf{7z^8pPSqzD6M<9;=)^T3sW{np?P^{pSFPppdXO+NJwl07}_asDlTE1i0o7O?WL
z&MV~mJ6M6A`b#}0h^sd(a2w++F-p%{&GZxb<Be`S4K^%grX#|kZ2EdbfWuo2orX1P
z-i%dgyThB8Tl!;wzK4Hlp)$6i9CI+I(vR!-k`GVN5?Pyhdd-Mr_Q_Xw;IqzSpU@k}
zM9%(?sILr*s%yhlLSX0^I)?5B=@`1Zdk7Vklun7EV?at$x=Tdq9!dlO5u{;&p}X_U
z`<?4t=g0oP_u4C-b=M=j9!d1=cY8XDNyMxU5MfP#meaggcmY4_{njtw9#hC&{VE3)
zIOU&+(RlkXEA6`pkPx%cva#QE6@z?wjx_$H;Dc>URIfKs_v=@)$>M*E-Fpd?pI3pS
z)uy$q&#IaUDoVg`an{JT4zyC7jr}quQzcvj#IocrJqp+RpW!z)#<dC758oO66=`No
zxk#VAJ7iB*_ORvy>_jkK=W-d<r@fmsE<G~tyTct|#)dR3$lq7-vkl(Bv4RA7K|~$<
zR@-JO-j*oi0<EFDYVazJ;p0j6Gvtg*J!Pqw?Q($DeiS<TVsmq---qOHD9$gsQ^741
zGZd{Ky5Zbq2aHjT*@9o;>A%;F3ME)n13!@sFnXDT-2stYzv8a*jFfG+V<X#&Y7RKb
z3+AkwmqJ~LMF$=|Mx0JJ0Y?l;95KppM3ZB|_-&q9^+y*$!A5tlGRa>=LjYCWPD%aI
z`4pyifwh)VnusgMD&;e<e^5hcNC!R#-@H-R&e!}-5rwB7o$3HQhmkzYHNYo8V}pY+
zTLFru&vmJwFFlLd4r_~+tpF?^ecXGCl9Y#!&SopUSn)&c0yPKi;SFN4g5EaCzJaFb
zkj8w=G-e$l3x)q|)Sn%U!8n;jUqSL~LPRB_-nLa9G)&=O_KdnkVWMvLc2d9#aL+n6
zB(EI<jRY{eb{fu%t6=u_PdEK;!}{Kiekc<pEA=R-QnDXp1+le=gje4(`4~)s`5dCv
zOpcxzep%QZcC%JF>hxj9hc9S?5Ltvzx2)Rir1>s>v$pbHYXpyI`EybO!KN_)g|Q+@
zbdt6Lq6fva`s;NxT+QvT;g%!wWlA8*Lm|N_o`?jkT&tlKoXa8o0Z%Aqp~|E*wh7ds
zNfYHhP))&BCLzCi*ZU8%H{nXZ4v4*4^OZfg@DIY__Aa^|cbpdhC+S~3dFkl|k)mZd
z8SP7Pe#+I(gYqij(iC<gZ<`(tynKksS{%?I(&N)O>fVmV(Erru5W_E#rF(5aJ&-gC
znuWISPEao<#;jo!LGcgo*odu{bx!U~P%8diLDqdb{DCeTX(iSd-`D%vE@}e$pmnhS
z>fq<7)W;;}?wf-_Zj4XQ@=jB?`@_>nDv0p$*q*P4VB2v75)R=vYX1%e_I$#h$qcSZ
zeqc;8W00}Ji7_Txa!)FimTHY6VOzRsTS|mi2Yq(EuMRCozS><Rz%4h{_l&zN5LUSA
zJnPFotbV41A@|VvZ}*a@(zOMIGBpd#k>AN<5C>blmYQei$gjBy4$Ga6mrjS7Jx+?h
zgmQd&Avk10XB5wxL>aZo&Rf-#h1n4No~J*Y5Q9^TktIax=18oiy`fGb+mR8Ql)zlX
zkbvFGeTUOlFMPH1i2j@olBCJ1#i$f3#YUpDIsDO(+xcdXwU+udS!CUHK3=*GA1pt~
z*xKP@mDV&{E9NY+n1^UFyLQj!Ti)VRgL->?<X-_i@~Us}(qT?t+c3Y5dCV>R^IkP^
z)Yum?a<%!kT8vk7+tv%o>euG@St&n&>J}H?)(~zUXuw*l$DL<*Y>wZuF2X$B!Cqm-
zdyP#Hh5TIruI`7(IbUwTVIWy)hbA1ubQdM`RxZ>e`mZ1{UK<HksnrA|m9^Z`M)i<s
z?J5>ZMly>R6{*WfsuiC4#(>=k>>lid7^S{tlbI;)ug7S)JeO+!4obpAlH&$@L~KJp
zY*t1Q%ht<_zugGQ<~7<LOir3!`-ER4Ib%bWDSKZggAeB>Ynt`71pmJ6K`%Gsi9m4U
zWe#;a6Z+b_ytEPhyM-}qe5~@xsO{eB^zqQ$TbZm?m8g(gvZPWR6iHkEXiYL%MJ%I*
zCGsNZ1Sg#E%A&IDsHYjD5yu0LG8mltVB5Q!f+@r2;DY@xNO5j*t-6nwOo}KatA5)8
zSH#8^9AvsTmh2c4m}C8_hfqO0FPJD6U#(S!Tdm5PR*<(h9nUD;SGkgkRi5M4?~c4<
zJm<F&*=?^S!I=VJ=1Z%U{W}k;58W^Poio79n{!c4%49<sR0bxChpXk}vT$YQ99DB?
zg@{)@3W8z;cJY~(t9yzT`xGn}j2Pxykv`w7WoSYOI5rD*YZy{&Bi>w1dL2M1Bo|Oj
zQioUD9RhKZDS54$cYN2NimEQX?nA61kP6!bo9nISytB1Tb|ULvEPfkbJfe6+e^Kw_
zy7WjO>+q_d%l%Kcba|t8^@DddvF2A?#4bPKxHC#a$XJehEUCKCVAMLiMBOlaeOsR-
zfJ-&}^MoJtMi-$b|H;lxkk2&;Y&GKzHI?_TNg^><j18DI3AhX&81Yf+5BBmfjsfW2
zWh(bO_i_d?EAggpyci8%j=0=iF!}hciiB~8+`fgT{p$Bt?o#_+<##bV!K3{G?=srK
zuD6xg!`HYo2S9r}h3YiWgpgIjKMJxw$fVuJy(6O_Pr1^pXFGa)ttQpEt@~LqL3cVs
zJ)5WkB%TuYqVERf_suf}8}UD5(mP-5t$GI6;hHVoP9jsq<*wqP&zz&5loKy%&;>9>
zYTzj<HAve!_Tt@D%aJ$ul>LKaKCkw(&%Xk$ECg{mF`H&sHnh&k6fN35-(Kyzp>po-
z=f(@D2cZEh$xzryKFGwFt7J`6w5x>e&ugjyPNf%&^2$t)7zztzSUS>2ZYW!kQZji+
zxjR*m;LvSEM2U7j6PL)Li6yakH{Oh#nAA^XA77(FuUxkoqUD{;{1_f_2ElP^EnV@^
zM4=w&!0|O&D#gil^EUaxM(p^d9yMG2)ap~eOHp1(<K7jdes252&h<mfiwWp&Dh>Iz
zPAHDoL3r>|k>sb<v^FW8I*#aBx?j20*0g7A%|St^e@VLAV)^Sc6nTK~{()aLq_q@h
z_I4eifkw{xKis<df4H^e2jl0lX?I-NX17#))i;J+cK`+Q$1M1OoOp=|+B$I25d4j3
zosyia$KkGqB+A~|)=VMPWED-3A)~U4j@tX__WfWrL47)$JKFXBr|Qn}q#kFmz>a{`
z<9N6sA{6pC?8{~ve41T5{l4HqOILHTpSCSbn)xe>1*ZqugK-u)rJP$#k<^sqtBkG+
zWuCmnpwH)#>i%BJm98k*AdEY%{OK2Yh1c6KsZx*)w45M(yH2FNK0)gsv*+#W3}QxR
zCl4N?b>MBoE;}GK4rARc3r49#>cMxxdfYp4ks^N%DV&pWPvxz(wTIx|g@53l>q6l`
z`O+NkA)`*@k*FxssV!u13dcr=0umTHsluM&awekS^JXnT_Ncw+StL&kgs3HLuD9;<
z%OwMR{XXDj?a78IYc49iCl~sL7(b~}HU0M=&6BG9cj+x?nZKnxDkXCRIBi7N$&CTY
z2-ni(vOrS~%b~NN4`NcLq&RYoqi<(xJ-a~y|53}k*FGlw=a|gg>W&qm-ch7nMCsvQ
zFIP47xgC}mU;4Wu?iy?l3pDtgh8Fb_hz+qOy@AFAX+_y-*u?may<R~;<V=&7-~G8k
zN8{rhV>a-+gIx9G`#c}bRk7@_?fGD<x2B2u8%w)xd^0x#)Z|meSv^<lXi_<=R&j^)
z8_Lrn<lb}X(3lmk{3Xj$(le`&q>FfViu<CgHI|(SLC;sXqSMdx@dz1l1pD1vrOI&Z
z?@qmv;2dNPScmLsgNpm%J(!bmX@P(T3vm+xbz*x`6q&OP_y__nzF@2&A^GE8BY)4t
z8prsG(zu9noK(hoSuJ(n)^C%#lUl(xY+`z~Nra5f@uVt5fpLJD`CP(EE~UYMQFnUI
zAMVj@^AXYGQkn}{><6aE)uO^Jr6yzk24>Sf`rTH06V*}EQS^;FjNUX?Iv2iwaNVrg
zi<+^r!kV=M6g6O`bbrNwh;nTr#Z#m6Ik8m)XKdlq*y*I0joo&h43Vr#M|c<Y$7`XR
z(Zlc}!_j1Vdns(4YpOk{vH(x7>}m`3+@UJY+CCuH0J1u4P}fj+DzbEygqtl@A3g^f
z7bf5R=BR3lZfZ(OtwW}SmDNcmH^*g&#lN&1JTa=_CuXg3UYyCJeP|eN!20e(ic};$
zigaqKGGYv5JEEv(e{kiFu{0CYdO!P2c|8OPJYs(RN!aw9&bl3Iq?vA1PRf{>3G>0o
zj4S@-3Kl#0fS^@042pK$H$V$azB49r*Y)T&88!xDJGsNa`{AId1ICZN7w|mOGq_Vy
z!?PIEIk_aD661}Kvpy>BjFF#_FOydnWK&65h&72*$8(Ml+1>V_J^X3!n+~~l<?&T-
zP+o-PnP2pwlDSX{;K5!P^Sh>aer_DI2wrSG?jrlliTbbEEe(;z{{toyJsSraWlu8}
zbxKB;`lm4?41BRIQPNrC-`<$z<W`m$an4kQ{3}F(qZ-G(lE6(fb+>AnMgt#pfogW5
zrUVY_=1`iYtMLeYI-Tg`iD~}eTWq@g_v-}x5?HH(t6Awnbg~2<uKwmnf%OaOa%a91
zGt1jw_(y8Y%ZX=}Yqk5b;aL6mvos?Q@oq5xr)k<8hj3XgAE%qyJUG(|f&QrMC*}|0
zYw@a#6zLLV4vjeDtnIMcwo(vBIuc;@Y=V@qvHPF2zY{7T{M;dhq!1KvB8ZIZz*l0?
zQe+_;rm+xxOKI4NivJbt-mZHd+E!TE^pPCf7?Og^dJG}b6?svN+7qN=-e2e|qVlwv
ze09%4cp-be=qM$OQtVw{Un(>A{y5W2Y;()7N0$nG-ew?q=k4a{6q(|WSacuJ`F{lA
z=Jm9;D8f7^2RY*iHkf|wD_ufXvW{)a#4*X%B~j>w?mwgDFV2X-KXiLRYwqq)X!n@(
z>l#q`^+LBI%aM5%)>iy;2rFejf_pL4`iH{jhKv6l(Y@_^ZMx3gr%+v!_<6>C6mX{B
z`E4Z(%CEoO+eg8rgvY1`E(y;<*XPo|g3{c5tbp=^ft&JS;wA>`z{}_d1?=!`IOZWv
z=n|@WTeJ`-yssIZt*55BM&-^^fJVmmx{l5R^QG?IJc}G=B;Pn@j1`erNZ~jM+f)=`
zg%3_8DOK5H{?xFSN};VAO`;!<&`eJcXIU`&o%%Cl(|wIgs?iaOYs#vxq--Xh#`-U{
zmB>1UEP2?PppvzBd1zny&foCq!^F<bZO+~H=bfRv!?Wt_?7YK;tK}~AF|DWD&_{;g
z%Wgl#ZymRz<jMHUE`FW-i`~He5z#AJR`m;-P&0(Sd?>K*D0Gk0#_5>-aiV@d5rFDx
ze%f&$ymCgpJczY<U{-l7ev(akQk{>Vx<%NX6Bl}lKe|-!8(7P=@=fhmd;6cThTB25
z<SEe{%QddZypKN@Q%u5tPaQCZ#h7x`=t+Q<+1@nYT(TvTe1Egm%zi_8VM$|^w!FB=
z4`eKF;9?q}K(z$Y_qZb0n2GnFf7pFEmX7z&z9}Q+>1auK2+1oo;V6A2&KNad`kV7I
zftw3v<HvI;cXbPJL)6E`G=jZssivO~p0uD>Pht-}1_7Y%j)avw31o3bqv|Bw_k`lG
zf~JFpCyltp4^!6OB_4A7Vp}IfE<pt88gna9V?y11TkL)TJcmAiu*pe#$lUq|^Iyjn
z3<^6A=o~ax$+g_n`Fjqma8`3(;stBiayeqK6eCRe^uCUE4P_T0HdCcD6e)W~Y8Ev~
zSo*KtfbZ(9j(wq4o};5K{LlA}O7zbd#3jmyZ5D+&E#Z+0_M^JQ*LmjJERbCTYB`3{
zs*K?}R{j|yp6R`+rt^Oc{(&f1hEKKoUeRstla1v3u(f60@&^|CfAHeOuTKT7m(9V|
zIN{y*2gVsjsF~Y-4qt!-AY*68PBbFAjtxRid<lOz-iqJUZ5G#i&F~-S1`(!*+|E{>
zxOFWCV_=YA5RNXSR4E8mo+R?}c_599TIE?dtN#Y%*_yqJ<2D^_lAa3TO#3>mV1CH$
zrEH>zVOA-r+p3Us*9;q;UwZ3K0c8KCM<Qogv~K_T)QP=uX7@Pj3tB76%BkaCZc+z(
zy`yY!m=wZ8t7^(J@S$hcW69$pxg&2LRieIn3=~j#W}t~OBahy+{?j%uq`-BEnu-O!
zAtWF8yY))!VW5)xp*6bbs<@;GG~j3pd;L71!7J1Z*f>)}TLOeGSy>mp)Ny}scQqHM
z=oeu7oXMj6!MH6wY1C{32(2vHOZ~HcBFX964V!8$b>>m4sg95>|Gt6y{r_nJAZUWk
zZ6<+W6YB&-s#OF!^IfRK+MPe_PmW+|R*z6ZNEXToWAkXd39Pg$5}+&1GlN-NFM77c
z!0Z()s--E)vR@tz>m_{Q9|ooTjxT+`oWaL)ME9sL@bvwxe8;<97F@oyZm1BwTjY7$
z7_wDbKZ&*6Fl;}HRRmzFW`K3{btRFi+4AxyvV~!Wmzux5|3zr<i0IbH<6kP&DOPM%
z==oZ4yoz?P*r9r(5jJ#sOvPErM4}IsZuX@%%9ufGe=ra#1U!Ekcqt;P_r2@i>I~M0
zO|*E<7@pyh^`KVn?3Saq#p8FXZ{s)r*_TT3VZWl6fLojJCa`{&lpc)1pxhvjt?4@m
zU`XoSse%K3iYX_rbiNP@QOoWS@)r&Mvsaj)U{JNR#X>GDObTHBVXLQl>nSn1=5ft_
zTX%d0jIHsfdWv%F6Sr?2GuZjPN3`DI9+a*CbKq+DJ?nkycy-U???gHHjR>RnC-={p
zunS7Y17SXYAe2@W_ZL5V49|Vx`RAGDdG_QrwY5#Mu#0aAB=j$#=^QL6c2e&xCb9@>
z29^YF5!9NT30Q5#K57PFyF#hm0mZdpTjrt#XJiP+*A~Ym&8+QsfrQf(uXoO=xClR2
zH}X_d4lmU<Ttt+a|MBB&{|P(~+%b}lcUuShCduQs-!)$2(8DopYOOPFfc!^D+IjMv
zYb?FBA3##kIaBgwa+j^(w|4X0X1i5aQuz75+5BB|#@`S1_{q^7_JT%ZO&tm(Z|~xN
z_$$k+aqYiG{`7T5ZdrE`F6c2*B=IgET8qL;-qWvFa(BYM^<CRD-d}_}8lO03-FpT$
z=$Rx>sJX0V1$41Yy+aOKKjQh%pvF4gedku^jqQosi*=0>xl~>0{PI-nD@Y09PWjT3
zz(om~mVs)A!RizVCN8cj4a1*;g_|IbYK{EWabzIo0!NRi#_m?1g!%2LKmsEPhDd{)
z3hP0d$as|PGY0zf4o|R<rqWVisV#SsZcL=q%NS{m0G-u!;(XogT<Qyrj6uPEj10&*
z_;`$T*31M<vJ)5f%mBBNr^`*|-fj}eDD4Q94>d~GXo7g+-VMC;Lv^3pp6&B1ApeHA
zbV*Sa$4U@Zu=iGd8+F`$cDQAuN?{_<(XA|ZOc;_GzdGqK!#{UffA1z{ZsXnlHHmmd
zA`b0}LuZ2YVT#b8Q*xpRP#n^_{zw5YHT+=YKcg`B`l1<s{$aG!^(N3p<pxxwcbR1T
z0Lt5<m!b2~+We4)8?$MoSwb0U&X-v{g?=W|6qLGU<I5H_MpLy|shql{aH<j51{+(S
z>>~wc=u^@}5}|KmkzX%z*YDIO_4vRnU><cn>t}iVhI{qh6B{#R{oH`J0XWQoYIckV
zkw9$5@5fCODM~QIlFfa!_l&X44JbN@Q|%2nVX0HfqnfyAwwimh$i!I$uV0*PI4(rJ
zdL3+&M{^ZJ?>I5)33RT}WqThrFy*~-D#^!U9q9guJ;AJPXHJ-r5|c<ot<t>_f5oB7
z-X6<+ZUYtptGU!)9V$7IP_+J>!>v~`BY*zAnLS^itbC!J&T@`mXlmBXf=Jhb<%qRa
zLpb8e<SXdiQEfwvm)Y+^+xyTirS9vytL?j$fK=JzTg-r^&*ry1Gyy}70$kT{Dg4{S
zCwDp7=4Nj0IPhOMj`$A|IehEqo;|bnJUBejtEaZ0+X7k5^QUEd9B2sVK{=Y5E~KVD
zEb@;^9;#H=i4I>}-V5aQo0v%RL*+{`sw+AR0dlsG8b%h`g;w5ONZMUnYh+&G^xG@l
zqiE)^?R~kxs&)onRs~u9l1|ui>Np=Jb1adwILJ4!kSqkkPn8<_OMO)43|Z+?jjtn0
z`5dnWy@4bN$!Hy^l%7OtkLM04M3O-6>ZbZ8$bX#SBr9s*lpe#ZVKS;|X3}LbvV7@|
zA&u=mPIRmXGwd=&b#qSzzP&rsO69sk%M%mC4q2+&wNyjlt21})eE1G{R_YvDrvAX@
zxK*>BtkqqMgQ~PNIT8;7%Fpd=2#<b-&*DsK%@AdH<WVnk^}FV?XyTZ!^J~Ad_8ciI
z|91j#l0CWsw)s#0!Z<&$_j{3*d@}SI@6(tzf@{3NyiUJev<V-zO@}!~{!SM^kenR*
z+$oaD?auX~&}?@&zFr|E1fkiUcIOt#P74|EVDu1Y^pVr@T+0P5-x7lXZb$c7dj9M~
z59$1i#kM#1+;@gf56?J_Gd+HOen9nwcYY5Yif?-*J6C6{OJPQ2&GpQ1{K1mGBH`N;
zS4Q{E(x2;7>#F<v?sbMmHQj^vqT<eQIzuyFB1A|$@bEhtB|2VqAhR-0-T2sP>Fbe5
z!S3;VY+jyUdaaVWzZNWc(!7B3%P$KMh(C1xm=dKj#%g+%C8#!kC2hxrn<s!&OPD1K
z^n}PZckpd<<-=ff#RKA~+-_MV4-YW(+$n+RX`<*S7ZxX`e&h^>U>`D$W|-@B8o`1O
z?jMhw%QtIC#z(rQH}WfgoC@)Gde*1>h8=3Mu|I!rBn~;iX0j1%Tv}#dc>$=SQCix6
zwjotV6VNZjQ-8do_H4vlcHoGqUqA?2vraL~n=avBx|&-;!(E5iF;nMB@la>ZMLOp6
zyQ6k353~JRk^b$>(Z0|jdyG6oyVuM?C+HG?7E^Sp5(V_3#^@(SFKoq78K>-hf?zMR
zUup(8oyI{=t{gV7?eHLd2o#7lnO7>Gwk8-5geo&W9ft+)?nZ6T_jMgpd6pqq@KQH`
zKkI%iADmnVv!0g9fWMh9(0E95%<;3>IWhX*6HjPw0>?Dj83n`6vb8PN%z7jc#jT&(
zc2t*B^U$`&-|0>sK3y}u?byZ8Ec&Oa+hjHx|3;xoLvyNfsW527+=;iK_*4;hQFlgR
zc4k)0f2_Vd@GUbf{}>SW-dw8j+U&LN^rA*Hcm46#(5?ODc+Z&H1G&~;UvY(MnPd6W
z-|;P_<0=P*<<-0CA(puR+*Tfm`a20wG7M0HaLLp_>4MbU1G^z1S7cLqhb}OqQ@_K?
z@G(HR9+R``Uq(n{#?OnGD%W`p+{MXO(UqK68%8G8qJNTjbhHmjo`M`eXh$o_ojF4S
zq$CztvOn_jj&Ip7yy;*)sgNi^brx?GCj%Q!B10jA>!2~V<O@NCOyJZYcCx|Wf^!+#
zIi_cWj`rOr_{F;61%X2hhLxuheDB!ZMCH@DU}{|6o=MUlIeJ~x>@ZJNr3?&U1~BdH
zvGC!rkCR<IygswRumrfBEviA}UguT2bNKfr5+*&t7wS+1grTb;Jo24?vZ4F3IK$Or
zL`iGWNB3+ARKFQ@yNf$NzR`&TRwT*w?@=N{l4y`wh2CEIlS9?ha9|9**{aa_m!&l!
zugB|CnGE&Za7kf`OPw`#nUd6Drjmxy*SR+QWNj3uxvSwH4RE3+m4?}Z-i%18;0b2O
z82FMqp(SBKq9zjSUA$O<Z9f(d<U_tw)&-f3oi$oeQr$;0l&(gv<W;_NNubCONV2ns
zL>f(opA#`Ted`RJ#M61hskSNqd$FA&sTXVy0V;aETSIQ@lbB_nhl?l*;n^bQc*`g7
zV?p)ebVEL-oU|N7A!=1%NnSP<%itu?DFI$rwD>Zpl(ep~v>p#J#|%I47|Bh>x3FOp
z6islgO5fatvc!7R#w~4HtZ2<`G%{PYBVh8vLUE{bXAnHR`6TGF976H5mgq=yW$P~&
zu`nJ_kuV6Rby;2}<?of@e3wZEuo;k#0^4{XjfKgiV3tn)YyD2UwGTt`qX5?9`?_xS
zPevkZv^hG3{4DA1_=iZ3A)(1wtiWl#>^g-}TttW;&p42qF}0-UR$Uq&kh=gG1X_FH
zeI*nUmZb#WxzrafiOH(LZJ!jm9zTWt^o(t<IhEkziJZk|iX}DV@Kg;irPJGc<ZC{h
z^|vIeKHG)}G9=L)`czEs>J=KT+EjtC{SshTyr;MdHE1^*YMiv_OJ)Hy161S1EoNH-
z6!f^QFRyNegXA=pZaiLZRX+B*m5pO9i(l?JI}(My#~-Qu^{b&<18&&fSwEKNgqLcH
zY7_gAM)-7x6roVLDxu6Dda$Tflyjpe{ga{;Be<_A*=9gxV9ybaEO%}S0h>SS6U4oX
ze;2zagXHxKd-uFjtVv8|Y&JBDTSUQZJ<ytCU$^^4BqvR(FBClrf|si)C=~?xU5#H?
zH{)KTD?cUgh#ycxNb=6RRz=Xk&uYKxtChB<L?TTBc8K&EEj@!m(Y}fge{>2aPnZ)W
z7J>_x4V#grHpqVy-8ua%!!TOpt#~n=--&3TP#PDd%!cPR!Q?u+k}y+rcou~cOdY00
z<d|67xzPGa*+$Loo`8v$&t+qjH#99-s+2m==|Z0wT8v>pSlOgfP%Nmr(H+FJz`EWW
z^f{X$B*Efo8!NW!!gwuldmweWdVR#4o<G-Gb|kz~z_e&k?0Z}3!Mr8hxpr4NU1#EJ
zg94yB4DIq`MbpCmC^t(PsNKpRlks~ng5V(NMCV5b-JOz94XF5IV!hj`KO@}>2>Dn>
z#mVH4>G26!r_)h2Hv6$LZR^UhzN&EP7t!O~RQ?Y71~8Fomha-t&735*xPJ+8yHf<G
z(}gKLHoRskPaYOm_w+f=8#kX#rVLl4$YV3HAQ$fhJSA3%`wknw|L6}!1Sy%Z^`2I(
z=xg>1M8$d8!b|J<2-fD>=9ic`9pa-^QqB0`(vRl;<K}GII{1itZ9A&i|BiWdBI&aM
znX0iT0^sN!ssY5l#}Gh&v5wj2p(IU!BqoXbef)`>Zx9h8YL24bUYueS)h6O1txM{N
zNioDY7l<21UZ$Ald}YMwlz_fa_(PM_BHL;{@ko=3OYI}rhR!>ofl$Zj^jyYm`0uqH
z?Q;{nd*~7S?VX%_Zw~z;4NzK?rcUR2l<jBvt?AKK+V>BHe<|F~E=xZ(8A*~pilG-m
zhag736JM=f>J-Ccd048RCF8$<6iTi5zLw%~%p~*-sFM^{u9CYjnSc0KUU9MHMt^23
zoP@8hpfY&r=cQ$61~WXo2B}jc50~5*b)BN#_ZP>grpu_=K|<%7H&?{os0(9&9zAhL
z*nN>Y9ME-+bhG&bP21-Q?-aKEyI-TJbw4j_Xfef=acmzy(hhPnOmz*SFQzRJ!DEgU
zSHBoRZ9D=cQQWWdEv&2Y6;s2`l+HcJf=#0Z`^4p^FibKgma_Ere|(3~rhSG##f6U}
zoe?yF#oUyY2;b`*2+1g&{zAI%ogjG(J!isgt21Z14Uv(WFPMx`5IT|la%xhdq{=HY
z$@F9)LDnB=No3EroijDmWO;^xKLE?8QDJ#V;x1|QN{5U4#kSk=cS4Cr?}=?#)~V$w
z?d4j=i2_v*mUq#;Y{B+)+{>q2qYuT&2FUT3q9IQ|jc)fD3=c2K%j;XLI+yP3C9Och
zL$RFLm8a-u_ipcT)zi<AagxtP-H!B5Ht;sN*RIHL_=BviDt;dpM1|v#pc>*{fg{tq
zjw~xKwxT)wc5BlfGS5`Dev9EAe^7l&`~x(}S3Dxd7+tiwZcZ4lbjSHtu|!8|=nS2B
zNVtPDGAGL1^@-CDP*2H=W99O*=#=)^`l?cU$QEm8j9jx^>8$lX+|=$=wxlRxw|Gie
zt?}b<HXY@F<$SO1VkUb_WmOj#6&lTYxa;vQgHZPoB2*RI1XCkUHa{xXx^KAl+?qmF
zy}MtWs8mQx7jTEpLdAL2Z};x?^4Ew{K6HPEB?Jc9VJ-CEHYO-(*-;HX$I16!4>)wY
z-`g|F50$2!$-##bD*+=(9&48jV7y$PK?R_)m+u0x21X;Z$306a9B2P7b>{Q%>J6P3
z$uoM)ByAsN#d01hb7_WgAb?~!f$F>@VWsN4*gUip%!vW6^Pgtk9<Fq+`?wYIbme|c
zkV;As$uRS|bp`ctaptk9KL4sW^~#)qL5oRTW(bEpXv1x>aNR957Etg6n9Mx7U};br
zL}RSc`@XCIxhq&MzYqGXIftHZh1OOJDxJ^q-rbMtG=Y->55L(v&-^3BZ&lURKfJee
zMn<e?_2#B?(?pkBs?=5EY@I=dh)HyAo1Uv@t3;^wU1zlMFzMA_(VMd3y2`2P8j*>A
zA8kCGsF40irh|?Xrol{0xc<WPoda_kkD2*NFIK<ET!YC}e@Ken)Vm#7w_$o;DEJ_G
z6xog(D2T>U;WsrQ@cC$c6U>2mLzF4;BY8iIxZ`$a=rA`L^XW>w$Cy9SK{2<dRdU;v
z3zKyd_1nBvxhl1&ug!94adGN(RTmS_XKzpoYX;2VE5E2lWY!OAs@9_Z*MqvM(?(gX
zWxgppJxfVeD$-#6w7?H-?C~k}l^W(YU6u!(aQS~t&bV5~?L)D<AvpfdK5k<Zyb|@|
zvD{*q&@^L36<!ohqa|L~?zSj~ERFCLQ<#hxQc6_GxEu{=4dvzxhCYBG&F*Zn#@>Iv
z0WtnPm@1fyXEK!vP*Y)?XUxiIt)Oxg{1+S)ko(SWb7i#8BXso+)3ryRdQCa6pq6C!
z%bAs(KFQ%KIlJ}mPhY1sI;4XShWl`r`|P1^LoRtLITm~Ef1JNdRF(!?s=aF-RUVbO
z2`G{~ds}q$Kz@j%e*97{cv5j**!f4tRfg6Ut`V{r2*m^lbg^+(2RlRpSSSXtRPaSp
zywi-ajSuCk))sRyNPcqiI}I@5BBKT<l~Q9^NhYHRYMJ|YW_5!4IF%0@I(EzdSgA5I
zm(Yaj76F#9aPp(?f>kue(m$(jBa#ZYg%WVK6vhnWxxE?4W;-@!KlP`oa}*ABIi$YI
zG@X4r8^yYpgdI8cNw8gf9~+X&@{NFpnHYuhdSHha+$Gr#KQvD~(gIP4X@;s$el(#P
zRY@tx6BK#w_Fk=Q@b5Bi*{FSehrwsXRFbTrIG4bq^=O6aMlsKr`b?W<U%5lY_1I)f
z0=RbNSKDVNSzo3}Nm@4Bh^y~!!nJY#-0c)<SPBYkpC@7BqwnY3jogY;yr;nM+Cl$x
z<LZaP2%F`O*_eVp&a+Lr1;?TVsW=C^!~;<1S1;yn&5^;`mi4ZkXm7Nx-c3RWWw6ty
ziZ=Nhw~!dgdKu-~j~sq??*?x>5^yk!ed}U+hv@-}jZ^fgVnoDD25s;|HM?J9`#4+=
zH)xYWkp;p*iz*RL7N&M1*C1R@b?nwko@Me_#vi7D2{?sW>K`V~n4?w(=cc74y)Td;
zox6Mx5_Wh>!t*%maU4_3wv?JLrUXi2VB9sc!ROK@XU}ohx@u{;bY73^+aM2}05RxV
zc*jE=pHnde@vIx!P#avO$MZL+sgOsfN40^%2Sc&0iFcmSd~cA#Q#at<z4Iaat9mRW
zmL$Q}fQM+<@O(<mmSVsedLgJG&H$+S?xh4~SwlC#Ai-W&AKL2~mnl+l=J(#5I`}Ku
zbmbOu(o7}CWJVz|7(T+ip-h4+gc~c6i&!+cE*jE(pHc$*Jq;ASjYftq_@4_D$>AbW
zI&@Qfop(4B&<Q@Uz&3K1PDaF!Wh^SDess4ftTamxY{+L+@Da&e{LN=Q&-sS5`sCW=
zl`i=H9)plQuw;(|D;9dk$h>$WqsBK3WbuUgg>xv_<0`yheg)&+F7_NEOn#PJmy)Su
z04e)X81Z&`hda_r(Kl0gnX+VrLKl1UTZ;{xa_|_`0`g8u-8hBo;RW$nt;D#ruZT&@
z0sZpEdjr3g<mT09%65l@-78k%BiC)DWV+a9qC~$tQx1Y-Xc%ZmM?cc*vu-BW)XnhK
z@|a@reae-snNV5`_VBLtktpV@0h4BKe?0*+1MhyhP)4&{|G|>g026inN|uXa{0j0B
zFo}NTAgM%;#jHeJNuN}iCvLNlmf)60X#~>g&qZrA1ou@dig1_i>GKo8?)zlX{As7`
zTnpR7F(@S)jz0%}mAo^h9XM0`eA`uG!YBP&O-P!)I0=vn$>#ehE_Ycc>zE0l?8owa
zv7~Kj39%UA-Hj5TU)+E2pWX2)H6ltM?hPigqLi?*!ZWl|1yXXjM4NdyWsya^dmA%!
zy7*!CgGp<|1k4I1dgudl5!WrLG4cbCi{=C!=7#i%uaA5RM8g{?5}zbx-~Sy5bx=*P
zIR!wk4!ucTd5Ase@5>U;q~z;dDE2iEqvbb(TevaWR$CE)L07Kz+aG#Ev3w8De^fCW
zn7{(s6iVU~+*E24-cSzC{Wq`y>|||~G25>OtSO?KCBG!FcaeF_8mN$(sncF32dXc^
zQ@-yOLd?*93M45xZ@C#d+_AT{fBYtKqId09gNn(HB;Y((SzCGNxJx)-3^X#Tybhx^
z@PNAVco{p7wB_u_74aO!AOV5)I81S>TatkeKJd}y7?3&_r>zFR#jJ~m4v|^Nk+G5K
zU;W+>TY@8z8{szfvZmns@y&lb>b6uWgAg2IW*%{6)(3rkhhdB5aU9yK5NsxC0#D4Z
zubrKBv){bc7$QvJGf<Bhs{YuE*h04d@Re$H=Skpqb&K}YDj^x%IcM+Y{^iH;m4RH5
z@~K}V4#u5rGF8WJuC6zso!opC`6={A#TWa=trhm-1c{Q|qJYBtqp%<NM>NRV35Gkx
zTXi^1ss${{0~vD^TQ&*GJ7%1ff$ddRGhVXhHRJ)rXlFYov<F*Pv)R3);R3K_q5dp;
zB@%cdVG>s?hV$t+--XneW=QSFnBY*(^~+>Uz+OwjBpsVz`ZrIAN&J2y9^}9Al|Nn#
z3>CU!?tV0_T*6x^og|h`_y-xN>mmlQ!GlvUD~lY0xh%X&2%QkQ-#wQbeGE-q{7tD!
z51mirl=w~n5##L(b6Dfx_%(GC1tlTeV12a@g+JpB&BrNig5U8IZ^bkIAX@Hz5O^bJ
z`Ui1hb-d_7Ab!N54&9*0XDG2t-6jdy?Iff>l*aF;l*VzUNmogT<50sD+l+A3AFHs_
zUB@<H$8HNYSX$-`h088t38Ie_?r^fNed?DzU%7Ax>Sk!EX#(RRB<fR|kA9ANd`<7y
z5dM`yhu^}tJqx0lEzClkNqCY}m`yy_tS)Q+R$Mu=XVCI%d`zi%9LlrW=I#FgeKU<v
zS2gv84j+!A>%2N??s;tf`LlD25|j7pM;zx>5;$Ycg4PizMN46TyF!uf8U;y+YY{rq
zk4xYPQ=U>6B@4p@|3{C>ZnaPdWeiYzk?<Y{cJalE_p1$@PDgnd0pQ(}O<3tVL=vTx
zpV3<}86))-hcihDqc~Zu(}_+2{3h<*fWp)cyY=v^EV=PU{7Za@q5J)}I=vW#(N-U#
z;<pDTB|DH7Jm`=B@5Gore3iu(@#n3djOD|}Av=a^T0efBM%|LXix2B7Exd^w{?Lc&
zbIrZ=w}(@NofiWGHPUY=Nx@dsFH#56MVT}}3TBf2@S}NLc(O;_-*yK#B5oK55!HM=
z*)%2`rz-C31KL;2KJ>ex*V6QNVf!R&1NOhH3x5Me=IE#JCvb{Cp4qU23=CU-$*^5+
z<}P#*4`PF^nKnP!=q?`NG`K#Ijf=6}{1Lvan4P9_+Yf)JfDOE~=;7xSla<Yv8$bCd
zV~)o{9qF_Pi1Xk!T$d0}74{jM6i9#&vkR+8XpsBXsFu{fH5*W`Doo_3Pc0ev12OP4
zhoQ)q!T&0{l-1U&2ZvD#+Mnz9CyC*w;S^qxX5av9C{lZer1c4)|AC!M5ccw#di@0s
z(=gjGU&+^VVMpERz#*$omTG!n9aKJ4k>)2uHA8Sxt3iSq#>~DAGJ|}QIUQQx$iQpi
z7H1XCp=k;Et!ipM@Zn18t9)0>50Woa&hX?XFu}U?s33EwUmYdVYubFSyTA-rGHqe$
zOZR0GdWVA><=}6K`NmJY13!hYsL4D`;hRP<RkE}|jPeVt8>PAK0T@`qeqUfrvZ%8y
z^JOAV6m-N3i*azc%8O$Ia|9%RG&GogNg^X0r+F|Q$1bc37#aZqr3}PZn62Fpxn{r7
zXl&qW(*Nkj`SC7*qmpKH=Y<r}c3zzNtBZZhu%0s|eP3@iV2qiJzEm>+Y6D^AtWbQ8
z<Fosx%aKO2R?4@}$U0#FU?nl9JCqUAe)vHc55b`{l90mY;b_HQg%wY7+xo(L5y^aL
zLA~Wc2sU^<6V|1R!(1#<G!^22CV;|OBEIMag!=Lyw$LQc`yb_U`+=W>OL3UBWm+9V
zEMf?5B1BT+C==joK4NeT!H8gyJYN{&XLqSV%9CC+XKpPpC(N2_q~%+%FfHS`DHV(P
zFFj?daZ=>+1U9q8spiQL1LFuN*GjMqdZI7iwkADgBItGX!(WQ*b)7UW+U>|uNSaNP
zqXTnP3a1|==Kc}*_^sk$AXd-p%Wpb$khbuRU68g3)5ZZPGidU8Z}=aglGL=pGY8l>
zSPUqOkd8Zziew29ahXYf1+z)r-F@tcmYzNVhhYvcwXY2?^({|W#E@t3-uiWCj^2p5
z#UtbGPoXgdvu$)Nw1BBnVZ3!r5>EUKrx2zwX1x?_f9PK1BX^2?kaTW=hH7s6sEBqu
z9eGfJ5}%J;$<OMm5#*)p(8X)#a;iFSD-CVQ-`A9>a{z+5OeabeejFhOTIFSrp&+4B
zultIDvW6(Xv96S(NoJe{1|yNd(t6iA9@E;MrEOeP<t+FtT(|QH*C_vP$2FSF<goq8
zo73KR*;;k<&8x;AFzXd6)5-OE0c`)$N>(UtNafW_R7w4c8s`XbhRqO{;sga_^XIac
z0J{bedqQk6$W?$)P$B~Cy5?aalsu$VTaqPegl2Fww{b_GSN?RrV9)XyLg}Ea`m>Er
zi%CPAj@>AI=KpB{B1OnBa0ZARai(6{OkvyG7tDQp<1sBvWFaYwEGx9?MT4v*Tzjkj
z%KlX+zcVUKr)V^Ppl2kZE_nF1Pq2}0oJK)?Xm`UecdPGZ`u5~2Sq9aCN-}0SQK$pN
zmdnZ=&z629E?I_?6`S35s)FTDT2@Jg*_SnO9h{Iaod$<*nChNS8MGuh!_W4J<1J&K
z?ZvUsYRaK1XN<CtqnrEG$WJ~o@I(y-o3(DL9b?2E{aNrotS$!C6z?d()`7&b#9}N?
z_51{8DD(s_{!dsVbr5TZXr=m0@CRWv&q|j3e|P(j*!h7+p}O$X4-hoX$JuZW&WUIT
zS2annjd8BrB4e#Pjf9Mj2lEFmYm3A~Kb(557-IQ^>0br&do`Z=f@Lyh;`D>qjQOx<
z{at<~Cq7wm2FGq!d%4o|bQX`|fXk-Gi-$^q%<xQi&mp1{O8QD#&!?Enm=O<(QHk4~
zD?A8Psb3%|A!ln6xDB&k-@tdojl`T%YFp2}1cOuMYhvyp<|M5AAk%wS4z-+^^y^F3
z?^ctT&!f`P(}vHm*GD>UNn};2#P$J9C1tN(_b(W`uLi*a6p|?%VH!-zm;EG|o~ou?
zEW}cCavMpxmO3T$z7ddpJ=4qn;bK$IaU5Z6fs!^5LS*Ofw)WiV#gM<+WblM&b^)Q`
zdyDX-cgcu=*?cFGjbv``<IeNJdp}zlLOjhCb8NV`#mx^yH)hcFis1p5W?>G~G?4$K
z^U~1yVSlfoI3-H(pJU-uRe=LRU@pI_7ay)eq&msL7Ndz~0*Tq)OW8NH82?RnGrkjA
z83QxQ=$w>B0u<HM2+{#L&kNC`RYnr>nP$4AtWW811-+@1irwy&1=)j&$g9_~=tzv`
zlB60sh-0Ek2)&wRu|-NM=Xv=dM2z`XpQDD>P6A}>A`y<CYI?SPKXA5?<G$`g#!iTv
zV%}{~Xa@Vlty=jqE$8W?GI}#@X40eNZj@qGX)Hb9-Kf)^u;vvOXXH6)iQc?;ju*JG
zA?*J52B9OlQo7tLBwK>goYyBkmML&s`tZT{DGQD-$B0G%m#-Px&O-~~okASe6(m<z
z>PJi)a%@UtJeD;ti3Gq?27-YaVOgF@lguH<9m;P?eaw_)meo}^*9Q`2O`F{?)hKof
zJ)O*al?*2c75KSMpm*Lh)}8u}uUO#AV(!=7G^Vol31qYMGFJM`(R@>?TV-ubf&Y$I
zekBPrB_AcWA5{!dMu9z+G!T@mnmfG;)Lgu=UTF@9n=V?9*&DpHR(6~k^xy)M*|*5j
zm7mDnQO^qjr{lpidsJEYLJg&GxT=`G#r9QXT#Ga!R>5Zc42l5Rzy?Ntazzlyx=_I*
z^bBItNd2KhVAsn1O1C3#J@5D#<(S$~-u;rk=p09b!i~BZvy35yUHPpRWh8*b*oqnt
znS^{sY<z0c6Bmd#X`|jz2iVpSrth}WdU~~8Q7lD5i8;vRL02?jRj3rY=ItLa6!F~U
z=s)0DtH=QY!-o-|NL%O17D0Y(2{33?kW$bKLo3<w_gC*{jTn^}IEZBY(QCSR&i4_7
z+6)jLB4^GRieV~K13N8ZM37{_RuP@K+PgJD7?(|@bU`$Ef4mg}d6<M~l`)u#fPZ~4
zi*>cOC{x(qQXChcmhe1ZmM%^%UG4F@0KoVj$uia!tmnZT;Fly$colNf<+VxlJ&eVx
z`^WY2I|c0&mjIJAucT?iZ3bu(UF!W+I?J_}G|4Hcfr#sSqJ>sZG9-of5`O=2Y^s6>
zi8C2XVn4nVK2TKQN1A}An3qz%%xCx9h&pP@E35T@9z14}{}zH>c)>%`N~yw50km{?
z`)$f0M@*?TUAwzYjr=pP5O{P|xCBq-)*`X<tA`q6NnMZxh6DnaT6&o`6`9ftUrxtj
z>_wrF!=S!n(Z_=>5XX3=wW*663p(qun)k0fJY}-Wcq7uw=;RPn_CGV^sAPz0P+2eh
zdUF{`+U#bf$XkEq_h66|a$P$vbRKP9_){QZ@=G69Y*B6PgYp8=3og6g{+J$^VD#rj
zl?*@L<SFcYad5KsCu0>hYFoVy=_YqX<uNqNFH)?19R)FnZN0`jL{3Tp`GuH99!%Qa
z7L1CD=|@h$UW;SVDvsA%{!V%q?lDkdd;>#^ClyO_+|}q2ISxd(S2g{39#~;f8{BCb
zc5dfAgux2`(==Kfa#XZz*Mn&Oq!rk_zEM{8w*xsV^CKj`Ac^TR!iKP4U@C^ook8^}
zJvEJk+J|Arn5y)QDyL@`A;BQRNpWG7FE>D0B)IwC<YFWW+I@K9-1L;XyTofm<17s}
z0<tGY)?fJ0F-IDOe^Ho}97yAK@i`=R`Eyy1Rx!d!i&e>^)>L7X#vgwyYI5J-m~yLN
zpkd0l?@zD*AsM5v^b3+h8<m?lwPK(Nw9_&$bkX<9{czUyV@p#h^eV6OD9QJ~gXTnj
zu&J$2b?}9G&@iQy>AYHiI;oZQHwsM*b-g6mp=4bYOJd#2ZMSvux}mjY_NmxU2G<`q
zey62%`G48rMp`vH#P<fzBUx~GGE68Xyfik<4c7ekG~x~=974GQ1en-^?Pa$8Tac-s
z_3Jyd$*39fP7f3X<YlW?3X!YZm?YrOjebA0!YqAc@8RaHw*NGqcTC9?Cr$@6oEk^6
zbVBY6xnJ5BsQtbP*JWVm7Nd?5q$M%A{8`l#@II@Wcof^(sA#8BFo~FLq=|w+=#oNM
zwk1D(hCC4g#o_6vB<WpwvLP$j*0SSx3$habJcIYibUp<-L8O{(Go?fp2_#V^dP#XV
zJ@X`AO~uxFoqP>Fj=1t?O4Dki8!$C!ZHotpXuF?Cj6sv(AuSP_x6<wj*2pD)KF=qM
z+qg(S0yPh?53g*4ZNScMj7RbT(!YU{OnsB3&&Y<i2bCz`We*99d$|jt{Ks<mq^(AA
zuEN-f7$&AaFjNooO0CO^c4_1vCQ`#K`3BMlDIG>~&gM@F52MsZjMk;r>cs0&QaOIP
z^LK`$oDQ>np7e@}MS$d9|5bDxmXavYvJq(QUMFvNiQ;=N;<&?DgRz!uMrsovm`gK2
zvXReHC*BKvh?QP{x5ZizkRS2E`kPdoyUcwWkp9SuS-aVJMD{0#3|kj}$Un)APx<YK
zSjcCS(PuNLIKx=|k;<4!wX48S(+(OI)eG7xfz>ozVIj%yFXa0v$v=~tp)`Wu9FkFX
z+$6+c3QGflq%5Uqxc|Sl`h<SF`b9j4thCq53blhjMVid)Q@%15qV$7!l^e;ujD;wH
zlRU(+L4+ctR-Lt^5>zJe73)TkDd^fmD8C}lf^Tl+uXT}qEQjs9<{RRRIAMmx<#AL{
z0xgL{1kB}Eo(yq02`c#k3X%mvWw~;T^K*P6I4#EN@~r)mWW@=kDYVPs&!0z&P$g~V
zP7rpN%5!V3;gWY4wXRI~C7?>mwd$1%<bEGb6hw>7qjVcVPR-AHE}KWQ_0}KtP1PuH
z|01Sj>P8W}m^nqSh2XDgMs=_)mO4ykb16u;?CPh0K_lu8i1)ucAR}<ptu)9(n8(?-
zz1}ehEfo`OG~JYCbFW?K63skvfOJY1=*G%Boc)z--c#(4l^NMl|46xux`PDrc_W4(
zMr-69_3{Q{{9)Oc^v2HsZDO;}f=L=tvps_|@sG~!98OI!Cw0{K4l9c!eZzD4^O1k<
zs5pbF&BNRR;QX~M%9;G0TZt+PLiryaFx=SUQ6s;dlGaD9Ov0sMl*k;GZe$@Bb3UW2
zX-gFi|L?6ouNofRIRGSA6kCJ57O|MH)`uxHl>!|CQ4a8=v(`83*v?#OUzs_LV|Oyb
z|NQd^dA;3I81iPVqt3A+6(^RR?y7q1V1zMUXEDr};72r`1LxNH_(q2#c}YqV*W0yD
zH1+5Qhd)Gjzd!Vztv9N?##FIQQ$994l5EtqDQuxbM8uz+iM)fh|L>J4i<!8>fX4Q+
zZ&NY;GWYNIk`!$aCDCM>(TEGia%M)bmEY%cpTABN(SM~yj)IIGFfc;zq~uhg>+O{y
zO%xIa=EfQ@b|UEy<&I<Z3g66ep@v55!wgG`6yzw_wG%M@O@QSS_h=jIX}TU!N{p~g
z?~!W@VNgpYUv6fcsA*ZHRm8`2r7UTcpBsX%F=>?f!ugr`!dI^9fGVCPJ4w%&KW2&<
zg+mgnAy*z9srEs{_o0_%(!ndmtN|4krKNCX$>whZOQu|PTd!VMy2!Bq|Bn(KN4pA{
zfK)JXg7*`Ipy_E{0j<e(4Sdz_M02m0$F=u3@5B;}{@Z?Iuu&YI<gdJYAB!Nnh*#u_
zm64J9i`O)9D~yq#l<X;w84uLZ-ZGwSWahUc{E4L1UPGL~sRow5js!yLrgV{G>#m%w
z-B}Iq#VJr71p(sNY{RTUxqcjr?D-J@kpwY?L9h*27HqI))rddtu{LJ&Bc1!-dO)*R
zkmuIG<K4@5=Zf`i(@-})gI1#=lWOHX8k#?@Kb*;xN5DNl#Im)k?zr9m?+l~tx3}Wn
zW1Wi5rBWu@g~lwXa5kixypVo#JDO=s@!spVIG4$~D=lSgV_Uc1A>F{Bt}x83mElh8
zsN4iW?QQYlPUU$@IM+mEHI6;ByRBw_<H~@OGSBe&i<9a3C7PXxl5n1>NZ`->R(B)9
zA4l&aJB`E%2JgP<*~lU0)OQ+2gN|ll`jRhVStuOIS+dFN(HE03DCw(953Jtk-o^hr
z5oMvscgXFDD{fR=xr<V6JI<iWF32lg3dnm|(m)44K;ba}b)MmB()cs1h3@|r4P}&v
z8GXFpV#{59TBgHzvI<LTopQo)qGP0<4MK+G!__VLw%(EK#2HzJbn*I4iILGGbc-ez
ziK<vLQIA;6Hi|tjIy>=S<hy!(N|(+<e82zW$#Qga84PX+r8_psdt=G<Y!L_u4nvpN
zthd{)TUAZ9o9|eXTF3H-I)Hv{CN14C;ol8%MZ;`d+i<6Fs;QQrZkNpp!t0HVoC|>C
z<>dl4NxyIe>;K4BsnHo|DQD<vUCji=ZWp`pP1`<Qx@)<IxF(gRFYx_^ZT>mC=|G8%
z{tr!89Trvlv|kaVluoH-=}x6Pm+q2~7U^z~uBBN*N?<8zr5jvYK^74trKG!A;ydr_
z@4GJGj|KLeea<{H_uO;OJU~YT@U@}X52NtGBpZnB)IM|7R=L?3eKe)(a<gMM+WoA=
z6)?Wa;amQY>`-vus1*2eIi=Jv5&eHTelRhqYTe$v3aS(Y*IW4QNNPYJA<7?vN-aCF
z%?#I8fLSQZ(z{0uIYI&yZr~@_QaGT#@0L-X(scT&a1tWZ+AH=kVfu?Yh6wG|xHol;
zOQpY^6Kp7K$OC$A2_YmMyE_p{i15yvLMgBK``icCvx==2w5Zt47_`fZ`Dg##KQuL+
zF-Z|(m5QpLkSjAO#@UZ~-=9Vxi*#%)f#MgmOv+em%~lC9qyn0B42TKWxqoT>qJ7HX
zCyrb-x9bJ+h@Q*77*>lbm5Jy9q<OpYz%xc-8<O^q-?=a>G5monPTH5qgg|*K4M~a}
z%v%;Sbo9oOej%pzggk1LRYR=}!sXgX$PlsB+<)z`(t*>=ni{p{F&XH8_@%3^p~=@&
zYJa2Fh&4G7K&STum*dD5N#QRgVXV$Q9o-rhb<+W~@5eUYxY$K&@NhLC*kLM|cR&BP
zjbfyiD3ZY(XdK7lMB||;@}>3B5{iD3E9Xn9Kmh9Qk>Zc9u+FCY{E^Z+1%QTDg#PE-
za^^*vKn;kKeGapaC+JNMPN_waDR-U+$3%=roYE$H(Ldo{viuyIw>$}FglWo*M3}{K
zy85_*Ep4x*?%#&{L2K8`Ul&pk%Aa6csg3~$_io^D441=`I2D-4*5|7)0|_SxFIT<<
zyW9)YC+*Tw&08Y~b&_KxPs(uaP70;e$EMR8+h|5>_w(})Dn4|Kf=1_Ce0?h!TE0yT
z!e{<k9Mpk9{Rgy|n@Q#B>8m`Z8a%)6XRz)k@`uwfhP-skJwO3U7?*t-abJJp|8o!<
z#c*|vfBz5Z=&~lzdsBbZ*Gjwobo6_N0~<bcZ`%)qRC!;$+R!mCPqiGo7rXO_AD15s
z6$a|Bcx~2Ro#HXqO<q>C(9H%=+RECSq%pM=Tk%aEYW||l1E*}lyJ~`6U{p{T85#Lj
z8-8B#!-`pXr4eg=<J{cRp9z>p&Xj>W3C>EBW7B-x{QsVduH5_H=L$sudT5SsXpE3`
z=Qdv#h48hzL(E5yxW2xAN=<B}NNtG6`!X9r%HeaE{QEHYw_${4mut?>tA`8>y<mZB
zcM5!Yaxw(*=;SelP$~K`qUzRLmJg$d)NA#Uyah=|8(}TO>~7_#IQW2fz4llKkBzUT
za7@Aw!{gaDk9vlG(0tYCSruK#yQMpIjJesl;<?3+Tgu-8ICEKK<ZZ)uKafQF?12sq
zI_{cK%RGWlWB={+FBDWc6au@}dM>8e)s`2oOc;2086VOJ&~IwTjSpdbPl3ITO1b~o
zx?zn!iu@<dR9E3>FYum7|BsYPTR)v|p%79tMQh$yjxg%DyLJzIl?NDem`p}qmGwoQ
zR1v@A_kJfGwqG7ulN_^5_6oHLGDm-TXMZAGTJBnOocwZS`5q@mc<`&Ns}y+wF>MFU
zaUkBj)fr(WPoAQP(!=>HoU;TvahY<QX9sPvtD_h+M#}m!YDL3crtFg=0}AG;Za%=N
z^<=ZZ7E29Xmdh3cf*;)f-v_usZHB%R8+mZK+_c~~gmO3p9Z93%1{(q!<uh{~)DkEl
zZOMS)5lMlP62<*r;^<q04hk_Rx;MYEO70oQp+h*Z=xGiaQkC7Mg^QGMPp}#M2vi4R
zm)OlM-2^f$U5Ic?B3;7TeVi=M@HfNU74k60KB6q+aZoEEwTWjWImKos!cu3hFMPm4
zsNWt1Asc~qTRh#8nl%Oqs<R~(GrJ2>-iINxgwfp9bNOO<ojPq8&CM+gbguse7fUrR
z9{b;M$?zZM=BNP-a&uv2{|-wi8jx5|ls-ztQ{0~s2K5POnidU?=dxu5x>!7Fucf6B
z8pVW6%|1=nugySmvvIu}3ITlpRb7?H35K%owVou*_`g6z)enuTn>SHSBC+R(5}Dgu
zgAIi->ke_&!Y8`QWu@*f&|qw)VFcFU=ZaoKhF!mGM1W#;JE&f3eaWfG$Z2tTVf_sD
z>cHjieNynFkq>=uuMjT|sE;PZqL{t<@ZW7Jl5c+uXiw${_hq6rGp+>>o-9c&m-^}0
z6(>F>x?S`Q=DKS0HHDiV)aV&s&2T0ITKW%fr@wHsWmKXR)=d#hOB@zMSmqFe7fyS7
zi%A(7xvTwm$__H(GD!p2tPADHN<k{xiJ`-~lOgQI;5KkAO)c%!$7}Jse$<~W-9xr1
z-)_6ibX}Jf(MY}Lvv!s)NgvBKj;-mNL3N;p|HZkrf1Z9$7`4@7@N-9Vyd&7ykLL3#
z-gJdJlon3*Wf^2vnW1_<nRYR+h0WY-eQVF=w{~VExXt&y4+IC;AFzZxR5Q+9*sS&E
z90??!IG-KJ4-g=-YX52d+}$FvPK-!X;TYz{Vua$YN-DFHnQz67Ms0JZO=BI0IhPoS
z{Xm(0U!3l8?KVs*Dp9>m=WsH=6}XO)x@0|YMQY@;8rFHvlMg<b-+!6>cy;-koxpK>
z(em3eofVzS-gGCwTVy}l^8eck?FqAl{I@3UM8wE$39g4JRG^c*E*Gypp#ppv#$3PN
zj;N7*b22*22teU1!L~+P+ih)ROS^#>kJOv=LHx-m?@5)rBt?KWyBY|jza;JnolFg1
z>5$Q8+Uu4_OVQby4BrdCO$$HkLa7$1h3oZKSk6O4i<U<hY3Fau3}<_!PLvz7kRM%%
zH&g>RpQQf$6H+Tx6tuD#vAGm@Efk8v4{%Bk2}|vjm7Gx6bMY68pQCSHXy}zK>P7Bc
zEOvC_&>Ju!)>0Au`$nY%4=zojUarCEKZP#V3tfmYF2>AEfDm&f+*2?==R8+AW86Ma
z;}`2bWGbn-k!{ZNH|UpqkGt-$!|A2!p)1##6OXCWHBYAAX^LkU<yK&98XtgS4D#Vj
zW(v|+C%94OAk5h)p;#4-%1+fm{T-Aky7Fh+8NsIYbg>ciet+TuPjF9k4py5~1wUU2
z!|V%pE_7FtNi59i9ioyrGTP<j+eX;n)z4p9^5c|5Z}rNyf80;p-nLFD>km%RCuQ@F
z`8rBoQc4M7A#08>np(YBObq<7ZCWC`eTbel5nNlCP-2D6@l;mOTa-paSB84g*rMF3
zNE#P7_oT_olwjb1z=%aNZ?;8B{QiFMi7In!Esl**`Da(}FW0|}B`<%CK5a5V4bjto
zbXo2g8tQF0H*G-DLsoacj%-b5iRHhmQy0)l_<ziRP#2J~F*7o&S~(xy$ewvQdtVuX
z&$OztY?Krr5PJ9SM8)FOwGe)LC<pgs)Nx-0dbTB$2Jka!dcT$UMYE08V^9iKOo>E5
zh(qh!@H9;;M8z#jL9YA#KYlGdydJ=Hh?+*zmvpB@#qnC|V=3S^`N+{n9rxRbs2Mx7
zWTKL2XvgB%2wz=2y~be699$T2V;Yd%CLP^)AkUC{k~P6}A0C-;GWIh89P`iM1E?l^
z(JWJuKyj;U4pwJxZ|4_OzbDQdp9e2XdDm|ZDgo8&WzvfP?}Xl*&z2?Pun``-DuO7j
zQ(n*f50?*;oFI7%KXod$(ObK#o*ZLim6N2BVh=tpRb)~(P4&XC5QY<&TwaK6-PZ_*
z*{($>+BF!eMW->>-@r7k9^G5@-z8hN8N5pK7FK4*Ek4E>t3E;nBsCx2{nPY3uz?l+
z0aWc*+MmzMw^7!xAnO;)32)M~D$4F^m=3%LdX#e-S6_d>rrtPt5c|e?YXa=4${vJ0
z2kHBq9LumMy>zkhtn@}=#p{$_c2b;14(kBz*43@0I}~R^eW-XhO3>b3?wYX}_cA||
zPr)g<(Qsl48_j%@U^6W3s@TgT-^g_D7v82XJF~a#UkCh@j!?s1Vf@WuI>#sOvM%Gp
z`ucW*jtp>ZFAKmwC>h7y1;{E*gJM;^Xjr-IC`u6MnMG?65UC*7iw>NqTaVV96TCiu
zRm*2|hIa1uKL;ESi$PbCICFHx8k?NnqPTkR*?dZ;7TAh493Qsmfx-sMx<<7Q>&M#$
z*|bzyczyp}7iW*xH3P6~9zTE=oCSp;1K{m-$lUA#Jes7rga?O?K#4&0>S#lMfWvMk
zgfrO5co6-twr=Wnf!ce{S&|v?b5*uAz$zm}$36Oop{jul!e7Gg&sU@FiN9&FqG;MD
zQ5Ir`th^bit6PUuoja^@3vh7^(ZP^o(dp3Zr#uJe@0seMUjg#}!=tjg3{^HWqnC=p
zPv90fbW)W{6L(jK`NgIOd?A=+4X0VGS@WeiQB0#7fw%=)c*_nw@z2G|*|sv;tz+75
zo!K)(lSOOLNI81cuKPo`Sj?vJm4%z;obP{6KY4{J&0+qK^5cX}beny@3`s(M5!28g
zxV!Qes%-4Ag1R{14!sy49TBmV61DRCz8R5rRe{I-*FCW@SE>d5uI4o4sjHo?9nI}@
z>E9;wfA+b+<13^8v?e5|XW@w0DB`_Ms-OvpkaHgw!x(_zE0URv@!zi0#eaxb_5MX+
zsfZQwXZ}ExZzaTfiUDISR3TMWTX5c4RGa<W%XVe?!0&?!8^)z|p|ZKNv#aNGKpDCg
zrEKp#wlHRU|NNwyiEb7@enlHA*~2enuOF?v9@J#VU}JLG8Wl+hXz78l2pwVBNgRhm
zQi?!za>Yoa7>UAMr1y*A<Bp0M6?@D^S<AfMPbWSMQ|tTl0*gRiL90o58MyMH7*ZYl
z{!B$Rm{J$7y@WS220<jR!`%|<EtyeTQjT+i#ji4XmZ4H!%#s8uJ)#&c+u5F4+@~+C
zFWjlr<!q}D0Kv%^$!SZ%pOz%}?a58E=bM;VRYvWD8bK|^z1KWt8NC4Q23Wirzp?5i
zD3OVO^O|ArNoCa#CTHCsz}GTN0n7Cyzgq{16lbo7%7fzcOjZPRG-?syZ)AnY<Vgam
z-xKJ-aB!L2PzPqTmtTfzXOc-<R_Tazgt;Y4@Hx28H^|#}{TRkw728`@X&wLWE*nhc
zCfEi$yT%fSMD1PpkLf^QQ<1a0*&AoJfvQ=fjVTjDVgguXmcm+d>PON)omE@Phes0Y
zXcA~C%NF78hE0`=q(-ezg)`RffXjI=FdHJ*Ng@r#wkwb)z8D^H#QKC12D4;~ChCWL
zDY~;gIQ-;!yE*AB4i4_AEppFsH}~jf{v!2Z=93!?Fo&qd^4Q_(`PWEzttkM`a{yII
zYNO!pn9^S5CqCJ=@6zZm@|W14$1<Fjd0GR+=&2nyp!;WhuqU+1t3EBWW_|8J>)U4D
z8TmRToKm!o6>J&xgKRtz-h!JvrUaFE{&$f@cx;ciRq1{LL$A8OdnF(CIx<z83L}Iz
zSk#j!2UGdNUJL-w9yy4ad?P2Yf9x<MR7Me7k>im=td9dHM(Q_p__jIXKv>8Z4lm7Q
z7!^v(lo>OYjW?3GGRJHsF&V3m&VP>Mu~fOaTtU~aQQ>Z1F18Nisr2>m+S7{u(kpyM
zpbs|s{r32+*0*8T*k_70fRK^;vui?);2UaHwb$xGa-{!K$yID)-%ZfQVlg~+mIf7D
zgv}-!#7WAOF4Q(W#O-2}ID<eUQ9nrP>?r6nUN$%PcuhF}zZM`so@DT!1_?=PDU&0-
zl!|`7bLv|w|GnMufVt7Jz<F83ioc1)|ANX^Dn^Z#>HQBvyd;bU$u7Xdx|_=X9QBwU
zolCTi!>-=8o5ki-p=2=KVZg9c;|q~xE7W{QjY5#wkY+V(o-PJUD+0=$Li!3E4V@|`
zroDB>W-n^3l}Oly^ZtRysLBxbxx2FCc?a@?iU7Q=+kG?X-1ff!&c|hACFlZ3HqFPG
z929)$GCD&!%l6C^ex6&OJZIT4Gu&l;$)5<^z@Ott`-hWv22;u?3|ltN5+2^?!iP2Y
zn+X*<4Z~@>0FJ0}b#ha|1s+N)8`ei%7LEGfvOT0a1_XL3Dk-|!ajaPEFiTPHSacXi
z0f2*QLfn)X7WDv;CSVeSjeI);`HXDml$4Lfv5XpyMuCE>yK&P|RP2zXqC5}K_EZZz
zpK-gZSCr?XT-)bG|6vH-c!X_w$ZPza{M1IUuj*^c)C`*i{9??KcdEeUso&S=8JWt6
zySKm?a=qZ1LwF5lm3r}e@58S?7Dz6VYEW!M?1i7T2Vc6t5&eAb=CwhhPRY|by5;R`
zJRwO1c>@38_-w7>!5Kwn4S-cMOvzB0WprRV=Qav+vu%qPTBBns)>IV7ORx=g=v6q3
zpuQ6bv!xaTtcS-zD(}h37i}F=2uGs$%N<`+=vv=)I?Hy^TNoKRnJ}fdW_xLhM|dmp
zSfXVHz}?$P|GbSMRXqD%O4=eypQg!b<4deX@LJuz`un2PIFKKLpV77M^RC0IcA<L{
zhg1CjySYIF5ckQ>c6J9J+0{$1ib+&(;aT6ueZX=7&PE?zBdI1o5tthUwk-@6_<)@=
zyJ@w8#;}{+3ly2qe}e3%rgVT<_C_91GG0BcM33`EElFgmw-eFJ!$u3!43SCtk0)KD
z@JV&r!$B8ecUB7og=S&sBGIV>29XGGswRQMm9zuu&&5SiPZ)t4^IwWD8~g<9iKi0Z
zdBZq}mTcmZq)-iHI~1DYu<tM!_Ile*Wdqap69K526`ErJ1W-5lu~@z{WLA&P*%ku-
znq9D&MDL3r?1&z&=-Ayg8)@W5!>M+4x!8N6Z~$=Mq3O`uN{U@C(TsF==~!N;1R@{C
z)`O*<iBC_5RCdbyDPAuTYR!h4CjW15H46Nmp$rb=>!$FJf~2<4%S><9-UOu6LD<SU
zb|;@E|1nEy{RX%><9{EUCbRfy7<U!BkJMWjE>M^t#BQ$xNc3&M(K=X$bm!_J!9KBI
zUqJ#7+&I>j*1Je3k=3hrcPoxTA21V?t~3hPgRSO8>Jo5H-j1mnIRqrgr)^uiQNgn=
z-Wc<B{AcWnjKSw>&j>6)D@`NGfR1T`hS%HsH=!gYA9Z9SmE!wd6~u01xq!K(s~R73
zeHj6po2C_o>O6BIp`?`*<1;$O98fkHjXTC9{LWxKqp3qMS6#!f%{sW_JYXU|(xjl4
zq-#=6%fmp(D+(6!O#4rHHE&r(%kl8q9qJ<Z#eZfqo6O@CeP{AJ{?rv-%gapxlT=x+
zV2H*<^e{g@#7ozxsW|%ui=BUJy$2YI24E<>R$YLVP-A(SS_a|MDH~VH_X~f(;S%D)
zm^}ZTBM?L<=~XHnTW`?u%{l~g`~CaWX>8J#A2fk|S5`yBZ~R>!J}*iBcV*tEgXA)R
zXWc4G)9!A8?CkK?`6}i=r)uvlKAJO4QudW_Ff&pl10p~^)CNNqpV(+H3^-Az7l}is
z%)baj^Cf4jOd0KOPB8|0yX-C3wKfQ8hk^}dzUz+Gn(4bsXKYKlQ=6R=te^TXh4w)l
zQW@KLFJhgKDRsVKpp6gW{H1Rcdx(mlCo~)0<G}om63P3hSnYm}V6_8^LFIq1%y<ua
zt|(}*w4od@(~q7x`4|@r(RT0{B~?t(Y!dVJ)6=pgMJH``Pd6|@YJ5VY?`19W(cgAX
zQSJVY)FX#yi>1Y`waJcQ<Qq|V{y_SX!&~bC$@YJaE<*r1nr12B681uHo2Bl7+fRrm
z4k0R1y#{pF{&#7BC*wYUf0i&BP!A3M8q4~=QtzrZJs}}4FT6E8NOpn*oy0}%6SfY{
z#<q(Pj3@2Li1Cvb70f2Za4tOxkyZHfIl3SB^i=simGfWEDsTeB!t8A=o!7#64>b<?
z+HuV%?wW3-Xuuox!gd(Hur$(M;;RKS@Oa^$fRr6b+t(iOUVMur?2Gw0?eeSLVhNA3
zX%}AJ?Bmn^a1mH{JLDs8L$4pz&?60}V9ZqErZ!ZdurZk8@D}>}gl|}iBiL14ZHAcg
z74v&f>PEhoE&~HCHuhal2eI|7ehC-x`2bsuZf&ba2Y?uH|8@`%O(rO>4>-pV_8CKL
z!gMMT;STPWVG87znj#wq%0@+BaDoj|EbrqZOgix<F(CSBlf61$V?jyDq*%^TGJl~Z
z!*SZ_Z%*v$iWxbcvH0K;_~v`+Pk#5k!@?`Zho};wvG~Qr2EgOoPyj-Q63nscdy>!X
z24BP^CoViOG&s3GL1n{G+oil(SdXGeNL_q&4GA=WH2JqA$1s=plZR;Zh@+DzvbZ1b
zvw*=Ppf5*8|6TU!+IQ-5zJB*hi)Wwz*wUqV8`Y%7O73-rtZ7th7sOaz0Xs+%xwJ@e
zA%+WKs~&Rd4iE)+bFYQ;p;q(#1GAfenn4<at#WqNlO5jrcP6HLnLQo9n=!}pAPYk1
z**$3g!z=EfL9Qsg{%JnCP|g{gu79D)XJq1eI5wyC!he@A08#shSkB>Fs9LI1x}S`4
zX~Z-l>n1HXk`HCxTytP4J|Zlmi&#Clx#?9|`nwIvVykgDEs)j8mA#9>W`aK(pSL#v
zj(YQ73_9db8*K?JmNJn%WGy(NA23ItNZ5qyKEhGW8VkdyOa@<^e^WP?At+YeAd&3g
zz((^j?D9ap3|<?j+3`aOb1GY}+#a|(88!UR?G-|QC!WgDH?h!V;;{yqQpi1!`s2FV
zo|sS$R){RzCc5WQ&fr8oRe1!oNkMYeV7)*aD1BO>Iigr<DbBaFRylPQLC=vBBzoX4
z^F-#gr5*vszRS+XJ<@7Sa0vi?b0UXA<ZKMif3N}YyIb!~!@}E^$~>v|FGG9{hxq~d
zO2qhLKi~X^yZNFP)J!_<O<#L)KemqGCcX~n<;9EdLcRwBQQpDhjh-Fdp^Z4D=dvD@
zVDxfTr{5dYcDM%ANKNY!(iuO&cRA0K!^5$dtHH4+=ZlIFuDO+ZMv5}jMW1n)_NiXI
zYHVyBo|m(tr;O4w=$d@pfWAF2@9g4Z$N_!K5auu9W18B5dr?~a^)K|^%Jn>9uEl<i
z>0e?<IfN4%oeBZ=2l*Pen_(?eY%UW#O|eOBuY%A$qV1*}&uxpv_LqrrHH1y0GvmUA
zs*YqUqnhDYBWRoX%uL2;JNrYe$`;xPK&tkT+;(`}c<qc>1`&SE7|S4Mi_*tUM~97Y
z{M|4S^r5Gk>&tga2(#JILCj}z^?23WVD;CDJNe0tYijEhMV;v${}MQ1`PYqJiZqVv
zqCw*OBA^!=CAQnj1`f*^%Q2+@{0*j7<fI>o8mjDk`!big;;$Egrh@KXPt1>oMo%U%
z)t4nS9(Pt_RmV{=kT`g9l}1$4ff{P*PTl**V*CjrNz=5I6~0Z3BLURDY|ajQ;~AJs
zti#@v7El4|^E%_j(|9W~!o^|#KM$ig0>H!B9(QMoh(Yw9Uhe$czmncIKi6IXDsBPc
zUvpT)HlWXF_08F4Y`&h%e(MKP;_>pckO4FZMr!1zv+FhypiI7@-s0=ymshXHsncb?
zS-^Q_JHS+?wPfz0Na)>>M$)S^sfqs=K}5Uztv_2v%y1BocR|cLM|ht^0e~0`q3#r1
z<#3l+3BqF%=6||AZT(ohiwVL<GvsTF2kQpg2c>lCV>NxCspJ5Zrg`pC;!pk^`9feo
zA%(CeOHi1rnGs1;NSh14`itd&Pbpa#=;2*s*Kp2|^2sESWOvDH?3wZHh{|#z5G3l$
z&>@eQ_}<PJ8Yynobh<mGBDAIQ4GCfkGmJ0KC7VOrll7SdqRxEJJJe1ji{eSq>9dbk
z>OfmbPj4<OKcE34j5^ule<N*3C9TmX|C%0e5~1qJpPmr$;0z(?GlpFw0FWsY%a$~s
z-JQLr*~O|+{?cdx;$eVn3`XS-N#vi*st#2j-CGsT*QmDdsov9QFB%v{9Rd7OV{85Q
zsaFc0_(28&UhX~7BvUq7u*SbhH3hNsT)$bgyHf-D&$E<^2&W%opEcxCQ3DwW!u4s9
z(4RD^7HrNzlC}?;v{@vXdG<Zl8Cf=vjqZ1y{VI{)40g3PpSY8<)bV<}!8rI!+5kV-
zhW>pwfL9}X5+RMQfUH<(ATvrUR#Bm@N$8AM?~X$R&~IoBm10&cpZyDlryma#XmWA6
zpQtGPL!Y<MU0tH(Dp)bK{nW<dfA7hMrbwg43r)3Th@6ruCJ<F#wSA}Nk%|JET`^Hy
zhMgb}J{OCveo-wehaay85R!qtTmKYS;UaY@F!Wx&6!I_aC&tBH@b@NJxci&vQVc$7
z`=z7>f(V->R|d4vb97jbt|&Z6qL?Yu;{h9@uSDE!@R>z9LW*%#8OE%bqZkPzGA3am
zK4>EtG79n&%uYU%Yqp7ZDwUg<4PI`gIxDL_v0O9{Q=fePMjvZoba^WIv%@r>0tTS2
z>w!<?O7|oD-OF?#7LvT58;+em&M`4o5P^yrZ1*(QV?Ijr?FsZuz6Ky<(54ZouidQV
zUVJ$}Ub}j1w1f@W>E2N_XQQZmWmd&>?qK${wRgw&>#@!P&AgacUfGV4yk%a!$Bp1a
zptWOy?a%E}rgWpfODD&;h5q1T+xC8LBA}`{OvqwF5|HXmpCpZUv*p4zJ_3_Nh!ebD
zt3N^xWguEZhSlGu2By`w{c^n-WiU99b^?++e*YT4`EKeevcWdfkv=tYWQ}s>D-Bmp
z|9NanXkkoMf}y(kv!@T<ORda~Ppuh&&wz(qA2cE|3Wg@oB#>T!g$v!5r{xY#->Pf}
z8;mU^XLQR_imgw@4GhRJRit#vCX?zI|GnYCBUUop!%qGAR*$OY)QdlDe8RU;?M^}8
zv)=%|7t;Z4ceqPV3AAk%-UuTWP@;<~`2w#~w%bdm&1L*H7|!@`!{!>w#vP2|U&?{w
z;^KPx@g3$P<k&jwFU(VxmiRVBS=XE`csrxI7G!2mAqQBiSIPzVu&{bCvVm^f`j*d;
zF%F)q#L1?4V+~5Mj=WQ8n%#_A;xL1_m6<lv{?AG8c~%HHCCaVz81RSLstE!KK?Fj(
zldtYf$U(yB3k99cI_gZPpV^qIZO{I^?;N)A)?%jAh!fDvh~60gkE8$cPs8<lvtOwv
z*}l90pq95ra}-s6KA=hjE&<g@-_K%kLZq1z@o6Eiv4^anZU7tba+Ulo$pv2i-%o@7
zecS6kHn#gYllJ!n@2Y0^H3Mgcs;(O1zU&BWpr{q^6IULtL{zYDgtI_{3>l^^M>i;z
ze*QhJIpuX>QrjT}YN}%Kvvu3BET{KhUk5ceAMahZH{Kj{=cyVeKiTwXTS&j(7by<h
z_t~Ty*He+wkoN|E+zNg)d2`^h=258^)M+$)xJ$W@-bXjH`|Ux$d(XTiD(cr*o9TG4
zbpxfBXiv`A#*$*&LIerA#i3WY#e9v>y+X(0uRY1=Eq$t7F*@($23{#t%iR7&-TvN7
z$(F;b{C^%6m;74WDbAM<N&VGNwqG?RI^r9W<DcDi=+6uDpoFPDKU7+K*Zk46fQn_m
z{_hb+PATyqjjYT3vR-?0@i_$L#V@2QUYS!M9)ZdM^W|a8C@G}ULhe?aogT~qtQooh
zRK|*jVyZ3Vhht+~)43sQRv;<HBLm|P6$TyeVokm=KXhd9zNAKf)MUbSzo)}W!fL^K
zghoJPj4sQTsgg0VamAMp`tV^&#_Ao;0GRY-vgj<cr_eNMD5n8`f8&d!8~1rY{zRx^
zI|}*cW$xnU$KMO88~iV*5)a%xuf^$b=~|hEGf~3g0K?EdSen1TAFNdSb^OHhGI<0A
z+e})s%3aK->3(K0<LlGDnKH3ix%l?@oIPtS=-Kl`#mm{CZ|)Mu60xlX!iV3!sV_Z2
z<rWqmhVUE*xw{-(e%9g)Swk&d(%@fs^F3S3raxI3i2W?-Z{?16vO_F?CfO;oz2JL%
zJ~qt(_FU+}XwW%dbJy82aQ^ZT*e<Vk^?7^-zbL3nhi&r9w89;ghPhlG7^1$i<{P_}
zFF)WrK5npd;pWYEawu#ZMW9b~Nn6YPc7~1iA-5Iv;Y6jEs*haFoXKjN!+I4+ovz{7
zA2@G61dgCK_K17~$snRLGVr>EU|WON&)kBJ(t~MFlxLcaID=TTChq)x1j|VG%)M<6
zxo1VukqzjqNR4cGNaeMo-rd&;6$Bs283*AFo*n1*++l8t=8sqD)n{;!lF_8Fb|1YP
zu1I(jrWJP9`x2IuQ!BjcymL-VFCZo~gr?B9_WV;g8-j@Tb63+esTKiWY2>tud#$r(
zB%9~y>Eda0P+voRPjp^#0o^!@!QjSeV@Jmr|1;G4oP68Ksm#;CwRn$2Wdr-^Q$x@n
zJ=qm%A0OY2%N3tI;)}{lxg|>}VdjDeNrM)LVb8w(j>IRfF`MrklTQcQoSmJI?Juv>
zl&cdHpDq0i9cOR7n)4FntO0#F^It{PH??iw3%wqQ8Eo`8UD;XHT>R*KOH2qM-#Dbn
z6atLCZV7lHRQ^n{P0~;o{@s?)%0ZmwlzCm`TI@1;{qSNJ2%D{Cp}4@GNn4qvdfm^*
zZdtTb$tI25#l^Oc_UASl_v5nz962*Bu~E9F2^t5k)Rqh52pv{x^@Jw?z>tc6o?PRS
zqXJ(3>E<M9y9~sOn^}Lge59WB#8Y$x$N`Tqjo`hqTl?zvJq{@+?yrHDZrDaJdol-3
zh04-FSU4%#>=rvoZ^Bypte(Yp>{M`A#P9p~ia9-In+6JXlyGn~20kH6kP(hZ9P8y3
zd-hS-KB(t*T7%vS)Nl(Yk4w=G+ZcQ9RLB2hv+UAw4y%$)96$3F*!}3%?I1;^6`xL6
zXisUPigCLxq)&3e^UL)gg}F+Sh09J+8ZtEkN77X2)prD}@HY~jY)k;+v=X?>(s1;`
zCRdVn2A}Tc?%TY|=RuONw{ekd==U8ua1zN{m7N`dmbX+qBcq<|bw5!?!kfxf0SvHz
zJ!tBMJP~3_<T@AM!ouA}>aZ)-UuLNhv8+4MHbn>g4}l|HkN&)LK=pEuBlPOfk@>;y
z1>i}T4$?Ktl(3>8TYV~NFNHAKe%n~^3@zMI1k^wAu9ib;K1|~pr=}q($%IyaX{I@u
zo^jwft4$3*t+6|Or2hK~ov)=sI#W7aT9*D8y-f~m3$g(@3SvjyIe5e?J$Z3W%n+(a
z`{FKD5~3i3yb9;w&Qb8Ft?dj$|1xxO@lq9Jxa6JZiPV;mC@+H#xh>tra#$!|TW8!i
z`*kM<bZwe!UlwzH(%K*$U#9b4XbBY=YUS(y1YdNMBJz9R|7G8wMZ}(BO0DFVs4uVv
z5XRKvkqVZs9?i@%SKHVRfdAbkQ~*xo$qg=u4H@g$|E$v(GO>(_4r8d)7^O-rOcB!O
z2}BwD-UqAGCc9K8C?|_tnoY`Q-C>5Z-a@DV0+=`%yLyDhA<|8PA!<3jp2#vyjY4rj
zWcz)hSk&~y@n^C}$r6RJ;a?jVtlvX8%zY@ouRe<ABLJd;#zF|M$`eZ>CI?WG9>`%?
zPfih2Ut{I5RTY;5Em0i0Oawad`j0<@PX@3BiTu}8*u=?D@<iBx;+UJUxH1K7syDn9
za(yiVRxjirvP!WLw!mT#7orwWNjYx)kfrC5?s#DBmNXD+la0ec{TTdA_L)cg&=Yq&
z{xl<xi+Lixt&*AE9(>fwv3e?W(J^ges(%%Xt3;iSYF<aJ<JMUfxd|PkQ4JomeDdnh
z8kXYl*iwK9%KVd`g|17KCGS$inm=>`Y66pa-E;#$$u+dK{fe7%H;{)=LPdU=Yo1#{
znT<SjW4~6IP{!NfBPir-Wyi{?nASQL*!&ac0<v%9k*A`rCWauy@(z&aj{)C~CnBDq
z9aIrt6tP$e;ph;9rG4R_*qjh?CbLaTEcCt8HNMMyx-3wd!eb-?2jTGZUJF7TZk#TC
zcW>1^{RUb+8Ei<KhF;?Ayd4<+8cawXf@EVE*qtRyo<emX*=5hRW?$(Qwc#^9EpHD0
zkV^n|MQF3d4acjHSE@~&=^HCjnXzS&A7fZ<`uOjUZkegd<KfmBBs|o3U?DL-#ihSV
zQI+Wh=;(oo4z1tLcJ}s{`3#D;!Y`OcY70=!Eymv!VBgnfuOzS6B7`MF@xPa2OvV;8
zXq0nQT(w(~eAd~fS(HD_VmS7rCSHpb5Li6^qSJOe{lm4uQcaSsOMf1;o!`zXJ6f}3
z<a=Kh`q>lp_~cip0ICgG--nox6(8df_l(&Y?0>x{;7-MP@{K1@X)0+Z43*&&EGN;c
z_2hX0;+E(?f)l+wy=z`pI~M5EljzGt%u9B+8=xiusFoJ_B8iQ;o;ka^PP#&^S0&Ox
z>PHMy{VK&KPfo}nrhs8D+rzVNY6kG<E7cB*O2^b72Hgym(oj0|zVx|VI766=drMiR
z9((0Goyo8>7+D*`B5@WD5_VfmWL7H-jQ<$&86ZIrB$adg!+sE3h(i<G)D8$8%)`G<
z2Jh~hP#hkpj;$}cR8*YmvPJI}MQw^+V5OESoXxsu6ljtdIrF(xYYh-x?AE@;@{3Yn
z^T9NVYhVpFeV_NG&3!XdTxpnBJV}9;$U;o)mST^pWxC5N69-W|++|QTZHWBAo5VL$
zY#tdX)sm8OgGCsurg(6cs1i(Vl~M=eX3wwgeOP~hUf?X#P4|@37ZP!2aaT7MG#59X
zS(*77P>KVCnm3VruLRkXlx{e83dsu0%&N@tf9lOKS~EnZe{*MbGfYbZ8z0aYlk^=U
zc?Sh;3*9A*lA#TlRdm17r=`4B5hi5B?|^EpHKfR>T4r#om0H0|Od;`4+ofEj^qh3S
zzy;7~5-d{;dLh_T-&^7YOnJT4BONE62aQA$3boLwZV(b_U-kBMNa%Q2s1oV<jn8Tt
z!}E(V_9-?v5@!3Z-HN&dZZ^&F_r}a#q}CZWT;2^QorQW2k#&*cnt3rPn&L~eWC}z>
zJPK06MA3}N!pd}Nz;Rq=$J%vb@+9$Ri-%1~eyhg^r#f*;NxdBFr@1y1>FVptvH2QD
z-q$RHM=mm~+u8XSS|XeTljZB(z66so$K|VErUybl<|+f{w@s7MaOv?D`&Bg;xsvT>
zuMV{8Kbs7V3RWiC`{-qdp)-8o;tLo0P-^e^W*AM4M<ceAzqmoW9~*)G(<c2QfkBnW
zh1k|)&^{-9+&R0%K1anePEZxa@K4l*F^t`P#<Mb6aIIqdGMOuIW@MNoU&H<p7_qN#
zVvFs+<^znAG1Yv~{rb@x7;|Y<sTVM~TbvZO=GYgoAPgQn*g=w)mV`J&?O;%@C6v(X
z#M(I3Pda_-?emxZ#}b;n@oRw^x3&UC;2!K%s&LsRSFr85dM8qnBFtl|8)RTvW|jD0
z#(hX7afW;0$)<4Z{1CW9JwqDJzJv1#ToT#^fU<i^<uBQs;0NZn&2taG0e^;9;*TWU
z7^8kTe8-|sK@U&{g^Z<BcU0Q(b5e5h7P9UgBuy4-+wdDG6wjRKQTfo$eGQ|>Zac{+
zosm4m|L$2GBINt5->>!bb2i#F0wBM1LWXS7QH8;#>7=c9dUouW*9by}M1o-kJ_VFv
z-UK;z7zc_!-@JV88kQ%cqBH>GQ3`^T6h|>BRU(ij>(w^o7<Eo8){*U9WFL3m<QA_5
zU%A<4iiK<yq~D7{G-L)V7(EP$^aLy6Z*XviX7*p04LY!IPkIpBDo4oZUC(fQY#Gan
z6qbUQ%^~T_E?XFFf1ae-*|yyWd}Ap65MfRwj=yJ7m;kf`&fIjm@B8aRHW!X(14=A;
zB%u(N7p3@o8|Ar2(Z4Ng6O{QbAF2|@dr)_|k2r)LI3Nx(ek+GPbmte|-OB@ZR;`@L
zJ$@BRmoXMjz@!Gg-hU0`O!m@F(Ey}}3~{hKPo9-`5U>eLX*>*RuXv&^HT3m{U4RF%
zP*93)(DT59NX&jdIJ@N~T7Evu&Ubq(v|l?nH@B0txAKx!A}?nff_E*U&bSR~iOq*g
zQ+H{-e;LpI+82P)oXAF)l#fzc$VqaEm9nn&+=_md{zFCq3{AV4JJ?t`dgXR;oQp;V
z(QxMHVmLjnaoC@9(CxnHc5v_iRt2{>ej}A-7VVdvYMN<4(tE5^HjyrU_QNATaaUM)
zy9Z-!WiIn>8#gj^a5Fp2O#li3(Zdez7sKFFL#cMfU0!Jur|;WkKkodq7I^S*yU335
zYpArsy6AKVK*{MYqpB_)2SLFQ(T;H?sQ5($PRG~tGN94V93Tl;gE>DJHcb{co{sEb
zSt9+P>4VNDBVN4S&SI`Fn8+54p*Gb0e=R^FSOdpyyGLrbhd)VR@w%b5*F$lxb)DLF
zONWA1A30u03oirYM9JS0&iW*a4VH9UsZ@zH*tI};tC5-UoTR#+KaCa?^dv2Qi>F>|
z%wp*9vXwhY({}OrYBos#RyN%AY_T`~6DxH29+7xixfO9Xg~(z4cA_ysabFXzp<wxo
z?fndlG`+Xk??5<-Yz0;MB9=o!D5wTo(DYTX`5es?_d$ki`4P;Q&=C;3I?(&Ey{z$Y
zYv8>0{_JJjc9CYWR{q8M&vIKvx8>!ZNW+c$u8XX=t<T6nZ~f*rR+KdTRYDBS+7}9|
zepc%p>(a3?prwmKVwOEJU3D&DK`oJtW(|flG#$D@%it1uyih@;$^SExjC3O4CZ4F<
z-q=L>!?F}(&BN$0b(`q{)jBkZErmif^UsBK<yJPK!;h3>x$in{t5{^ilum1xcJw0~
z$tOjgZwFJgwWX_n^QjY>3A<7gJAkp6Zv$(MpwzbO=sE~tx{;6uDXiSItRx-Ep{k4h
z10O?2sI>)oR!IKX@!Giv_#E!Q0&|gkpN7=O*MG7KsKpc4hq)HUKWP>m{uxyCZIYo0
z{NZJwgfzjhhZAq=`il0snSHN%AtrPgwRdOeMDci25h+Y1S_${Nvy#7@ZieTR7;2n%
zrZQDlD=|)b>t7@~OK$B?9agss5BE4Iq{y_Ix#_-=+&X#wgD=r~VvKLC*+HLs5FMyW
zd5CD-8IF!&OGWzGc0FbG8~k^%)&PW+sP?*)tBI4LD(BEzHbY7SE{n8<zw5ThalA8B
z{4>09dMiN9#-xl76BoT0>Q}zW7LH?tfcL{r6m-8(1v6-F3ED(*sc2#FFOkE#S5Nyy
z$Z$HJ#Tl)BI#(>|Ao`H>TVY==u!_WtQfnZQxydcbg(a(O)JS;6L>>xhA3EL0q$NiA
zc6=1RBhl76L=sKLIF>Cl?731|M}9&p&i&di;z6W7NO$MC3VV{E&~5jAC;mCghHdN2
zB^Ao|d@#dJLRRfunK71@N2GM&yt^A5n!chwu&fZ)F3|6*Ie250s&na>)~!<@d!pK2
zn-odXF14HINM;!DWCDLBvDfm}ESi+nF#5cmdpERW3~a$;_gfE2`0s*2P*EugVcrZ1
zSP})L<Qu`&P+_2HZ-{kh>0jZ{lnM{eK=sL`pW=Pbc8k{!MXbcH+Ld~>St_OFdcY9^
zDRa&m3rFnUo}NLu91e|ds4wNvFqTxJ!m!w|s0^Rb`@@<|KB&+hnDzr_kDWnDW#@j_
zW)N0_VQ)p3Vt;6{6*G)kRPw}^VKqoc=A-i5s+-T0R)S3U`G1jwmBsP)Xa$E@(wjzU
zk2kP_<D*`e6N~Xzrr}?9OvKa)Dbm{p>DJYWnQSHVrJV*D4MT9$`Yu0n%#CiiHBOPf
zz7Be;nPaI&pzS&?$y^dRL-U9f=JoLG5|_{{S&V4+Ih@2L>ki)4;J>!A1E!78_+Fkj
zk21RxCd~U%GdDQDemK)8BsDZb`U-1^_fy=RH*Xu`U9rq_=7co`2cFi?7u@P;pyv=s
z2%UtHT#uaxl(F(th}s|~tX0Zr@P65X4yiXX2YwbkiGjpmu)D_zFyX|jEIC0tv2pbL
z+@Vrnkynecd3t(k%3B+3#OM)A9ahY-y&Y2)i39Gkg9mlJsyN6j9rL5sk!82@qDzrj
zIBx8iH|wjX?=lV%RZ)7pxT!{H{q7)^iKVz+6lAcGzUqVZLdtSO(m>ZS#oFX#2~X(+
zJdjdW>6X99NRsR(%8)->SP&{H!(V!Cw-U0y7XH;^?1oPx_Rv0qL=cM2ba1##-qiys
zI6bgP4v5eSD0f3yz=WcCuICxuWYA|7>T-8WWy}In9az$VGMBZqQ9iKcu&c4*$>eRu
zcTuT6=El2yDn!pqItTwHRVA|;eZSYz3goORZNfOQ^r+|rlO!Af0TXa86^Vbx2yHCo
zK@2;r9b-qCypl2e_vEpphsu<(ETk^I=XoIz<h7BE2SM~*jXm*(^PUL&WYS_S+HEe9
z@v)ir!c)K@6kkTuq`|2!ezY!Zq7IXRMFc+nP4kNy?8B9^l5|M|%_e!X{UKsH?~->*
zC)n5~;WKGs74n}}UYcrJ$b0AUDQh4`D&S({Iq7MBd*uuxt*x{?uy@aX;SmwfC=k6_
zCJIDG^NAqt5)pB3P`qM%mG=7Dv1PQpviMgknrRy33N~hYn<^t0n${2e+<rgXc#d5`
zloIP_nRmVpHdw?)#IZmU&4^p#97De!^VMDDbs``IJAa)|*LP(;clBFl>3R&!z26md
ze9eHY>-}UgtH`|VB?=Tim~6MS5pj5=d<!M(iEsz@IC%8}x%PeRd;Eil&dKTP#_Ud|
zz2C{qzv^n!42i=2TneRPy%=UBeQPSsv&|$~0B<|;GwiSAMbfelz_i<?u)4``CN?4_
zoIO8Zw#V6cVvr>p#5YdCh+b40D7{q;=vl3W1#0*!0q1VuEz{8uZft`2HCNCAAr9N@
z(1W*7XQ&;Za=}Lw#9FFhQRpY^EI$qZt+`?UWldu{U^&j`*TpEXUzm(jkKX1T5xF2r
zZ&1pr5(P~kfdL83#8mWoYRE`g=Fqk??Ua0suwH<X8ZWDE7y5(_DDz%8z%p61L!_#$
zRp)qs=AS5NQq^FZV5UfX>Q|B*8u~^lP@)>LVy5TYVg1QA4tY<WX_~rrqb(vO4~BbJ
zd<sV=TE|wLIm%HTYiaglOz8|^fpLdH&mDve*F{F+V*|$BZFjlFC0ptVG7&@2?<<{d
zA<rOhk41*W>nijgyN{>DisDslPNo)ARY-D>U;4XYW)F+O2hJAm)>eg6j1P>;aikYB
zRZlid-sW_kzoyhhv_I&*+8?L6wx3w`*NLRXZU5{t{W7H&*@JP^{(Fj_*3Zm<VIc<I
zNh_shEQYkas(*M544!*=k<}MT{|Q`)6)eHPQ8_?3vx$fm9z~1mSs2o~y^`$@c91<~
zg#+b-@)Mt|*2Cr22h^^iqDft3D&*ukNXYJYX67z?B!TeUZe#`?k)~+v&kUOsDdeES
z);GI}?B~h9zkT(htI`=IRl#xj5<(v>SUJG7%3rpL!%j1%+Yo-Ibgt#y;)_%}Xe~+l
zwvz2KL?S|Cp#4S(HlH803x{yuQq5>#gXN9W#Ltp<lO5i+b%t-2F{DX`C>gv8K!Col
z-ZTXHXZ|R$r63;cUY^g_csDP(T~;VBx!9^dkF~y*4Cam~tG;?BugnGxXklEXJB^zC
z0Q{<zbKn#x7Rh}<|GDnYIc13F;E)fLu;Gjv#gSA*kWlDTh1YSM!J?MwSxLt@-0o;^
z7O<-sMQV(~I#P{CVJd`dH0JaZlpRw^EJBMV49?1D>i{fH<u&1_9mb+*j?!9%UD%Ll
zP1<CV)i@r)m{UQ~UZqmpgjtN)fle@?IrRbA5kgZ4gl7S>4N-cEi_5ug@(HJCo(_gy
z_sNJmX0=*x^{zpWoy@<`r8rI=>)cBha!J-+Wx5g*>i8%`0a1vf5QJavnz6p-7!1u_
zLU!|!`&Lh4%;55C*S#8|v_*J&L;+!j5Osg=+%#VW@EAbZa;OXYoe*ShG&o{6y0dRK
zcn?T6Cq|rwVj6A=y<QiMuPR)(0D?^|Kt1FSABivO+~t=>rZE`%+p|DypmKAJG^dsH
ziG&(&0c2OZIeU*1lfL<WQwQbrOdA`dmy&m>uTGh4u3*GZP7{%p8^9NVFl%s;CKR0!
zi7-&fo8zcIZrf(zRvh~1XXIQzy)#;23`}C`Njk4&j9Bk;z7mQgmru@p=TZ3Bf14fl
zn!x5g+G!O2_~-3&#}#+ZBG9(H!F94{=_@fSgmx6^I5}OI=iPBo>yf#RnS1Mymto?M
zJ9ue%t5=V?eNL$k;bu|)(@OrmOQDe6n;ZlU<)970d574}Wyp`^5!D;tO5kv*XQz}8
zuKJXO;=7l|MV)4h*<?K(m-&0T%8TiI!&&&qDWFlpU8@=q98Lf^)*BTV4FH!FrVVqZ
z{1|$1a?Ew62GlmqHI%oco$zk(<KeXtxonH3If}KL0rC^Sgo)oZqZRQ{`Y||RKu8Ce
zTm|VjR)KT0-oDc=aZgd;Qlf2}-;$(<c$JF76bNjTsVxIZIXpjqmO%N4eZ1{ve=ySC
zEc^7VAJYr2Y!)Uzg#s1Xb*^y;`vPZn_z_57eV<g|a$qs$YFLe9Q<qLvE)A#Nd~iK9
z`@lF>yVFaRV_bL-=ckI%Dwh}eT#A18fnyF0ZjPm|H!o`fDl1O>`L4ID_#Gt$dhMI1
z);UaNpPimWe$&#PwL7vBC$+Us5&xa^Tr!iOc08jOgP|yiQ?)TgXGhsqbuwH!AL3<b
zs2^hW)4xme$4KsF_vPw?SYVB54kh7X7wVwnc&QC_@EM<O-q%#p9t<|FX~tD}zR#nj
ztFA60(T}!ze6%vi*P0xTlL5Ne3KlKgf7KE)OmT`kuE<JK5@Ce?)70ZV5_8|rb^7DI
zA>#f3{Mc8I!;9O6p6R&LZ@bg_5x<8ebeM0i%CWl&M*(*@e&-f9=`f?_&6%ZH5WdP)
zZb+8kZqcO{nqwnDX}1Xtn%sVvCOy=0eUay(NL)?SY}h-k*JQrJj^XQkOwGv$+%Y^^
zzk=9RNr~#!w3eOFQi?YOeD1jVM_BUa4HihxCKG8<8p568cjygz%JPDQZGCT<YuCyB
z%(O{mRgxKWkM{SUG{_BMBj<d4BhF-NIfti@XAk2g>RmpVfZ6JuJVxIAQxXge>bjzk
z?{_oFudcO^&^>SV^$B0@Tn51=Rm#^hBn+}9AJSN@(S|`7ycIu;>M%FQ3)e>DiEf|Y
zNjhDpxl;v2@4R$Znx8sG^F;0Mzq|FwxxoDRQiU=<dv-GrR-Y6W55p8QDx#1OBrfEU
zv9y&w=uTdihV?PAHjHNHWyx7d=eo_=Gbi5-m{5GN_vWvoV2m`*7mV=dJN(6B_3quf
zZic8wmpQbQZ*W%L(0{xc>gBMq3OZi%`^TF~`^mvwyML6~@|qP!F)<cH#W3nE$@*Hv
zp9oT4eO+BF(#OftOpzon$@zY0p%ZE|VSQlp6K~)jHL$}#C($3A|K$M3MA~nA?vZ*M
z0TVC$ZKc=0Kgj4y=}|s~uDLF}VT}`(IAGSf-?}`VkP7`kBfpmto)ChD&qE;7D5ax1
zlF|PmfX?3~>}j!&0G|&_XX{pH=_83Ig|fxt`%-MN{X>FbnFF3&to4_zA2DeOM_<le
zTyGZO{&4$&IYYDgRfUInqV?mNq|Vbj$uJ+HR2YFiRw=zC#}^`E;um*{9DtVJT1iKY
z(t7;+gsI1k1SrV~zQ(GWqQTnrO3CsOKljg=lAQQQL+)2S5)IATrB9PeT@2nCvWC6j
zf5*-mzd)IpQCp!Bd3Vu=!@z40Fq;(oj(tmKSV@#90~&WT`t$?7LG{|sPO?=m;eu3K
zK%w5_ebvK=Ng*~j#ue)i!>hrw`lqpt^j3R#9Q*tArD;~+?Pyy`m#P$K1(e*+HB5Mp
zY;fzWTP+Ir3zr^2BL}{UvK1GvehfKDsu41R0~GeVQo&}DP}_U*-8#<GgULFlkKHs=
zyxk3S*?*?+FCXo*fDkFP!;Iv_Z%^iE!+&)O*7d}l+J6a}2Q3Y=N1xx!2u;6^vcg)?
z_6b~5Kgq#4eUCQcoy_+qmE!o}QnyLY`s+yB`WY`O>m{z)k*?QsL8*X@!Av6K#fUP3
zB)sAuhP!ft4O~ho?v;|L+ekvAyMr~^Sknvi)X;ssCx}F0&Q{7lqY)_SO<?uByQX<C
z4D!x?brV+LC{X{{J*Nb-W%ts5iq(55A2eI(tY!kSN|-`AeUb~u=DukB#PRq^rH|>O
z3CriM5y^7SO#M{{ye#Fx9`DV5|BAXB(^E!EeX+nyrPGH1701k}%uik`<5!7A1$>Q-
zkEqt^h}(hQN~QY2ebO94t9MpEM%N0;1q4$ea`SPNXCg+}Z}P@?m$1@(m+pH-z)Tq#
z{<k9F_SdlwUh4`vtcs603<7jsrCYFmmc?efGN`e7q=!v2P_dFqwrSV-Mm(W4l-}xi
zY4B-m^}?Lu3n6e0+vCSf^@{&gXpq6y#MC(0OR)rQLQt=xQXPYt>ZRLfAEh94a&d}F
zu^<5?siID9=e9jq)x!81&m>77KVyE~T(m=vT*D|b2gAmjzciYK>lwccBdv(nN=>21
z=Djx7_&=JyGAzpO`<iZnfdPgdI;G_S>Fyd*x)B5gl$36UZls6qk`n0-k(N@rL%Qp|
z{ax?>%UtvE+~@3AYwdmZv0|`{80KQmEqo0pF+FtYe?ImsKj2mRidsL|6_BGuE{@;$
zO6c#*6Jhia?LIAih5@=@AWqC@Mf=TMXcf`Z8=v<xX`k1PBUkeuUA{Zod;!^eUwp(J
z;q>zP%iYR#@n-*XSy)nT!GS}im1D5xh9G)hX6aN*jDXtT{}`}1L!~E)omG8XzkHJ}
zGE_lOTqd%FY9SzK$%e)T9`A!W^T69Mt<Y(_ZB?>4!SYS*Y~9}LpOV^G3aBoyrm!-4
z<C&P^4=Gqyh1~c-BQH6T=t%Tb7>VC^*vW{yTA)qO8Sn5DOtsiWXbC|p+j06OSA$1s
zGg;&a@5RXpNj!QO*m;mJmB8}mEzv`fSahzTJ{jrebVZ~IELlN=tMFhqLcV0>p*`oR
zC3}=7wKD_v5qc~3LVCKRG@tsG9*aCKl)yoIUmsC*630Vaaf6GyCe8n}C{g{nbSO6c
z=;~Dy<q}2DFKsrc%;DpMCGLMDbL;sy6?J9(7x(!n4?*tyj3%zMb*xu5MXp!vo>9`1
z3?T!ZI$E``s+SuqO9<u^1ne7<L}klry7OBObq?@Y&`@o^)$qf5oizqqtDU8-1mS|N
z1AUHuh$MC;or3D+>=s?AI4fvYFSoUn45EYTu@Gk2H}<J)fs$36mf8Ogh1SNn)M+<7
zQ$7zZ)zVz+zikWVck}7lZ3RsUq(gmiRUMYI__vG+;Zq<?wq9Aq-v5*ch1^rRjCjl?
zLS|ks(t-q<xKl<?e3W&>H=phA8Kk{FDVFBHr45uI1-UAYGrS_E{Qd#Ak(5~WH@9pt
z=(hhJ&|xwqm(EMOAyP$s@#D@+T?{<4b_^c-QL8u5MR(4#m?0*mnQyVZk32SPW9g6r
ztBKEtaZJ9WmAwDfXl!1=PjSkHRiSW7@o3B_T`}udrt@x3xiK|UMLli!+JQ^>&*8c)
zGV|HvRgo_19STfa%VBlBj?ouWeE?d|nq>J#sqzYe_UnWp_P0kV9=TMLQ>brooY_;L
zfU^gbv-FGoHU@nBr9vToY&qvI1@lYqhyE9YDP1r;KKCyd(8Lgq-wBCtjpF5GV-;tU
zb?)Z<^zi5<=ZAh{=+jv<=B-+~5m$6LqA;%s;g?Ligi0|lJIbY3K?}y!dXl0lP^M0I
zMV*4e#adC^Qs4fU<W&E%`aA`iA2IZLsWL}uFqMRmTrjew=9$XWrWzg}NPieBf^W>N
z9^Qs7<LPatm$CWS|7t*=RUXS?+*#jvC*}ofe;ihM{3D3YU3{J2G~fniMjN``#1|D=
zS!-#nHr5S(59E<PNo9SVr=z?03<m|ZKFowJ3dkAK+=J_o!VLz6a`fsW>)Ae^ju``H
zylXYzXgiI+3)tWw!>9&gO(pJggTD8rK|_rZD>q;wfG-@ZpSVh=OZfPExsv8V!+}N_
z^V64t73p&75;7wDPvJHJ;(<*=N0Bph>m_gK`&0M^`zVi+uqnP&zuh3jn{#rFQlevC
zaa@n=E{iO5z`+uY`_@VTYqY%&*IF5NcgZx~=b7`uC_aXQOpex{m|by~?DJ|MWFa=t
z79XB8865*$&`LHH^WuK?URNoY5q3cet>69ZLATkZY}ts-R2v3Xq>lc2Hd#h>YihNM
z#i_H3%RfaUaH+y2JuLs<#2^qi(C?X*pQjHl{Ls7_U-emWav4jh)=V30M0>oDCP9sn
zr9XXD@0+DQXzxj6edyOKr8sHFf~M6Iw%2U_@SY>r;K)kSeRAhLqCOWb!O$H}pCBX1
zMx36#(ng+<YFAM#^fIp7cFzJUg3X~OWnf5te}>D9L>}ga-eJn!c4A3U6l@F+Qaw6n
zTVf14uw<*+<aNr2!$f@VZI1a6BRHQi*uV^^X@A05d!Lman5XvEzHNT8<PvV*<jCPq
z%i{XURMLJ*9zDl@AxtfWBWG9V$@wK%BJ~4}{CH#jjUPuw0Fi+V7T%R*bl!WL2z^2f
zt-x1!7=K?t3E>PcSO^+fehHReHpc8TKNde*c~*e<tn%($Oy_87vigKmt4Y~0XbRQo
znZRX^5z%ysL0Gs-18AFsQ%~vfmQ#^n<EeBXeL|%Mf=mN?6h=+<?zl+pxkCqUB3T-j
zBGJ3dCMM|!07A;?N3g4u$GkOC+C<wv0-m16VJ!T)FhB4V>=2OZPnOvkj`%rCHvDcs
zlnli+@-BUGOND9EUF8x|a&<c;SOC56*2@S5IXlXai(7<n=+~4H{Lk|{Zf~FU3R#Mn
z_5{+h-g}{_+6TNY2g}3;Qul7gg?=lD>&uv<$EuP7f*<M-M@A`4+}qn)4l9hwA+cmx
zNFR<*i<hZ`Ws<F4>p0LBz35@`S=wC6iF~KE^f|>cukQQzdwhDk`He_LI{L3U6|)o=
z`?BnyKq=)?5wtqFz8FR+U^LcGMH_;nYi2A8b+ZCm??)_ls*&+9HUy(Zub<A;OgmRO
zCd_CzJd#P!zFGSS572mLt-qwIpzSkwhAi}fhz+Yhi2*M+dA-&(2V2u#l4bWc^=3UW
zM<4Co$&&XjIUm*F_j)sT#(MwM4H^jID001*tEudq)+qqP*FyQ9vQ^Y203$FBbKOT8
z8ZFnA;0U!e;N6uQ;rS1bShB&G+Aj-;ZG;HyGk82@J%EV_!y$Vzf1T%Fyp6~EeQj}S
zz(;ApI*-Hkl13%ga<&^4i#)2myp34j>tArqtNYsi2wM=R|1bSwLB>s_x@}OFG+2?g
z`hu+Rv>WNS9>eLg2()cC9GleWbmKfU|8bcR=jCfIkW^EdNb@tP>hA5lBaTMRw})EQ
z2=aDN4y@xyk=izQRIRi(;t+8ifh5ZQ<M>79hPJMJ_y@gJl@vuZv^V??9Dl96sCWIZ
z+7n603M8FuEn};Z&6<bFI3!xfI>7Kr8If^UH3b+e1|GETXGnvCk~Vf`Fks~<kft=N
zME$h9Hgb2_1g>`G+OVFkKf#|cl`2)B{R7z59S5^FQ8LL+FZeC0AOwydK0G^#YYC#V
z@P#Q6({u0o(_5quSt`P8#;ttb_w7&=9}*M4n4OAkq}E4ikg}e@zgINrq-mopB*Ee4
zV6I8md~KN^7c(C5NQ_{JBa1gn_2$4gMk7wp4&3`bIFmgtm8f6;a^_kVs+9D|>a4uQ
zn5=jIlaeiu_}Ax8YDz8&zLdomt=QUrixO9-i}NqES;Husdf0ziE*QJMCeQ%T53hMw
z+i!j2cg)3GQ78bx43Kg>1aRN?CM_n{&AN1Z*$M_(F4+X>c34;KB1h#jW=2<#hAxX@
zr3+(C^q|?kXPlnuI+e_<J{*ApVv<#L?w2wnk<!yt3bLD09_5FY&-46|)ROh5m49tg
zn;m>ZS5xN1ky3l=b!JbrG;v^@9k(d6iIXJYUY-}|s-h=S_%V6U{ZmI^*+Z3)ly|O1
z6$vIX@|LD8Zv<z(5}z5mEQYT*XHopmxVv8Fv3ZZf(%!u6`;4n^FHrsTjch29OtST&
zaWA~8HKUp9>HG|YbuIeCi40I98e<%23TM9#q(|}U>SJumjw@!<n8!!|E{J_~f*cO5
zGgR|v<0;-u+51Y6O3}g*Mnh}-G#_^+j3zsQka>X;iWSw%Bf?^X&+#<^yco9DS~af}
zz(_Z%dGc}21QT}9Vrj-ea<V%a;J8qdPqeIs_gWmpe_@rx;}&HcnMBe2p)>Iiw1gpR
zg8QWHBH2fNnX5MQ4TepANH?%-z!eI~7D+Iu7C$MXQ_hdW;!Gp=lMJA{j32wW*J+VM
z>|)Bd^TbN?rgn*P9t(TuTD(GLs>Q&2N2#0moZ}SKU>VQH{K}GKEng8~dtXW&0tz+?
ze!Kb?B_^)lO6hglM>R)z5*HeIrFoao<GZUMp4g4RqJO^UvU{?|jcE!yvB($TV^)k%
z#D>kGfu^8ePBKX}WCzfcD@6Xj^%+pP3;2{!_E}aD#O8W^@oAM`jxi-$aIg<A=_L%A
zd6+^etUvVO0aQA{XjpbdhW@&<7r#gc;vVuE{<HlHc#ubv5=ECv=UpK@X)Z28uytT&
zJXNb*k4`5V_s~;jutR#19;0?%M_e}aRW*HJF%V2v5ewM7zSuvb^ZA&I6F%2~?RpRW
z-H>?9I|+apfd9eht7#A)%|uhF4)|)ItR{~$QK$dWZt!Qg=0V;v5=DM6*G<wv;pOew
za}az(-zesot}d>udQmIAi&0KMad7Tfs;-=SFjqr+;_I5ANL0C=kK-l9B)v9DQfv-K
zrMHie-4LwT*Z<F1XkFBaaI(*uy2RTZB~a@jfg#W`cBro)D8hQ<p$ohMi<&*S6uOoQ
zwcK4Zh|oh(K|nyO_ebYtIma@A^@u*HX^eiJz@hLW(0?CHV>AtmzE_D$sk<6bhQS_#
zT!;yCp(tc0GCSIO+9#-ae!@2Gp81)W(Ml85vFT?p-V7{ei^M#f8I7O@MCVb1Xj&_L
z=!Krb<cD(RmT+*JjHUmQPLcD6<9>Kpq^#y%?Hl^A#h$_SPT{SIl-GkfCN34XN-Nc@
z6J8U%nMy{EBr{yfV~*%toiB;Cye3cAP?w&%6(n5FmO3tvMVBv=^jYr+$nUwJB32xf
zBqjgX3jopDmr}Mai-DB>J`z$#I78zcImqfWlp!ww3naLW$Yc|nQGB5dAr3--rD!fK
zZP?+mkh_a2<m7pLdS!eR4(ONGdH)RYQV0o})t8DZ^uokJXtq@Df?E;hh(Jdf8^6m=
zb|7|ZHobzlvMlSQ-7d=yGkCHt7lXT=(yLg7K}ALi`}cwoZTS}>$}yR{wfXEPQRQ=}
zJr$H?VO2ptzud*%2S3@NlU&Zl@>G|+JANm^R==+hi#9i`OzY!frmgLQvjB*20n{$|
zh2lOsHRZJLC}*XdGAxk*=5BJ*H<#I78-lpKUzDF^nSd~HA5;Um92vF9p~^|;_~T}G
zYz2GR&-+h9_wDh9@_$qXeBX)I9%9pfFGJBmQQ>75lG^jDOnhy5tG=*`<1aX$s~m&{
zqW|K!m1;sg!K}_|6O#-pT+MG{;{5QEY(8K)O?*g~z$Aqz6FK`kqYKI<3EO;z7Vjli
zWlR1!{uHN_ftEi}!XUqkl56N?O;j5S%%Dq~Ri65Kmqd?sad1)IjeI=T02d0Cnd5CP
z*ioxfd4oSngThhjT>XV)4G7;z$cYXHTRqtWP{_A%kU>VrD`sd)id_o#bpP?YtP}`S
zEf_ThfHyA*>$=OcSY=Mc6l^2~>8=z0J9Asi`Oy`Lm9Xbkn2|5g6VR~u=S2kRoy8WJ
zK7{d=9$LaWdZP@H<0sYEq~vo7APv>l$(_h923Rb13$~8Q#U*vcXnw4eEOmC8iqkLl
z&D%tx*!ID6ox+1{ZQanzezltnEhCqh8C*qPt{WroA`*CPUJ7{9>p}CEoS##H@`Bba
z{`fVV&OXH6q!K$kqlE*5!lf|lk3bux?lXtnsDX>4C)GwFWdy~NavpmaGA^IoM3E`|
zRKyU?U7c?&O7J<e>d+T~1n3zjPrwd5=u>oLB$--20WJOz-Bne76CsE=)fGav*}DZx
z)cSn*IK7kpfVjGWQJ7$<?&tc8*N-(Ezh`gRrNG?2QHT><q>#gy64qWS<73L))HWNU
zl^k;-*+3bKjKV0h)?;QS8dt-(c0IR?m<-B|lwD5>FJ^x6tg5c=gGJJ^HI9k#$VlQl
zh*!@&Ld``*D%d$|XM&~^SOmeSl;zni<0?M2zsr-(-zAUw5?@aL5?T08E78sbp@oM!
zm38Magvei?ksh$n8Drp!vSpGDyXjgYvgk+CSv9h8`Wn;F)WshKTViHGtq&dMtB$ps
zj~b?*WOO&D(CC9Zb-n-m`OntLeg0ADpY83j)!*q8NqweY8HtO;e_5r^cqn_><P!I$
zm?}NWIh_}o5S0z}QC?Qi_0IxnZiUY!%apa61TNK8Lx4D*eXTla!W!O2W_z(*FZH>|
z@GDvR3V;Qd$-aiCZ`3rqs`jt`!*>E7OyXhoKUk)7<}*z{`v8ZT^1Kv#ZEsxTq%$X$
zGLX^*2N(j}LXs`0Yv0zedx)}yBO~b8+XI*~dyAwE_z~XQXq(K76E729(AmTJI3}8c
zIoNM^0{NNa$_Pg>{?8w>=8x$m#FMhJaJBp!#d4mSVkikHB<iAkeMVwwV^rLZ`M^4~
z*L_mOj8j}gL~a_>!N_3ot#dn3B-F-PnzgaeHR}F;WcFQII8B=wSuB_#22e4WSik$S
z$#ca!9BA{3laVxsft1B~zr80*ad>IfugM|kMS@(_BgRZ9mrk(~1po-rUr;XBJ^F>n
zPeFY>LLaFMjNVW)0OA<9H=s0J{XY)DkfYDOcxsn}6yO#wJ=IoTi~_96Dhn(|5}XwS
zX4nXG4az!e0u~k#OcvD5kIfVtb@&aH1S=AX?US_n(T3Q>v11wgW(cCL&@fHrgtq4v
z)CUjJIGW++sS^Mtl0t6Mj}Fmt5lsq>X6(&Ph@r%}_@S5HN4wM>4<@?e90SNvgNefW
z!oKOS@1$-Ljh>ukBtJ}LO-tW$jWd<-KfaI8ub79wo1(#lmf!&3;qEEh!%>=14_L#H
z@5+{*&-VeG{eLzp`v=-e->CD<5sF!TARbJTsZP&MsC$`Wx$3gD-?#u&H?|j+6xUM~
zDXmx7=?fJRvVEi=ZCT4&x|UIe$OsU9D`OehhKLrE%Qa?=p{M1gmi3DgZFyZw0Zowv
z37vR#VQAiY!NI3_#HS=*DPK=i`9l|}xxb`S(!xhcrW@}P6d6rLc&HJYc%(Xwr}=m6
z?_Todl<T}({I4LUn$v-kMuiA;hlIsP8On7BEZ`rHSVc}rI@s<`4>Z#(K|JZ>kooj!
z4eH&TXhs_bY5@jzGQY6k#Zi`TT)ewU;0B-=0Jr_|z+BcwzD3oAL8R!V`cifH`u}j@
z$yO)e{E+L?95Kqn%gk~tgIVxn;Z4R-+24G8L6kpZCHs0gP&vc}bd!d-qbCl%p!l*#
zY%j>mTPNfUtoVYoV_sWq-86)QR9k~iPDQe~7nzAs5e8fY>&on(F7i4WlEmihug#-n
z=e+EMQFQ@A+N4Zr&i*PYi^HK4f*Ozrt?cY}!oT@emPVo^&;+i1#sbM%LpZ9ey;niZ
z-O`c{Ii(K19+Ib9FeBb!B3rH%rI1S*7Q*XRfheJ};Ap3tEb@5wnNA{0vkXH7#N+MO
zn)5u`O6ntpm$hzrBh{Y~%)&7^G6oEXTi9^^{kB~Cix?sUI!$1cICsMd^wRU?AHxSI
z#m`E$?L)L2xR`3dGJ);w?f*H8G$S0~%BKwZ(usnyf=WG+I$Ftk3K9>mi)e}VKwBT>
z2AN-_|G}r%u+E?<84hKuG(0WBVTZ`a+qI)z238_1aBEV&?{~x`|3;vS$qsH0|L^M1
z`O7n+52%j)XdVx2F=R^lVeHx}b*EeY%KMr3OfVxO%TZsd0OA;8b1Z<X-#>EO7*l>%
zafz&K&K8C)rfV*xv{8=Nt$8`?*{kncsE?-S_nSMwQpovc0?|9=lg%+NmtYWxee?Id
zB<JLgnde&Qs@t8vf+qv-o~$)23+`9>t9}}P@0s=dQavDof_@nTJu7GK1K}Ib@S;|n
zKp<wh49v6F=4HW+MVX1-aS4lwq-Cj(F_j#7swL_Lr)5^{GQMUO2*{3634L`{=pymY
z6j=!iresQ{aq_Xym}&<gkhp_bn)DBIKd38&EoOCo={}Y~Fnr!_ZLILR?M`G%&5B8p
zqR*nNy_AC&QkL?ttPm(x9LxoegS1js8>Cd)XW$khw@RUfrJ+G2#^bXN_h56u2!oG}
zFEu0GgqfTnQX2)>a+&H7@J4j)&U0zf_t9!}SwbThva8p6RqDL%&NdRe=cNSy&R5j{
z``x?nqG_NY7!ky6;5r<gDFyag{QE=#&oSShK;&6cRroMUb(eX`Uc~3`V}SKAtP>gA
zA}-dBl$e-S82BP)4Fw}4;YQtY+IvAbo^&~y){5gKBzr9#zH?&CJH~;Ulcb+|@*qZl
zi-xh)%D1vp6rlj>(RIIRih~n!;je}C@81Ge8Uej)F#AK1Wyo5_y<3H6kob=Dznx1b
z+`6Am;1*%;%Eq~kxHDtJLt)nU6DzAVJk#x(7ok2&cnSCn9`63-T{DQ+4V9g}no*(i
zO{lCxX~0Y!;H3H9)Q^g^?DTb;eymRMSUhQQ+IjQ&E0^93e<n(z^N%94=xK{HQ|VQE
z^a7Yf%juZPTH&|<#GdXd%dEP6;cEm#E&HrtE3B$<`mY)xE{Dq%knWf@DghI{V`^^l
zSY8@%A+f@UAH?kVY{**+RAhl&jev={jVNloXdp@G3jR^igF-&bnIBOj-M3#M&C3G>
zo?AT_$5GcHJc5C_AcB*A77Wsqm0hsibIezlXiAJ!da3v68w}-zj;Y5Vw=`kzi?_Xu
zS$@@Koqi`@dnBTCOgla`1?U75MK4y#>Uu~55B~s_K<(bW{RYddwJ6ju_E5sG{Wxu(
z74}GUT|HYa;p|k~qnwSG4N9qO;Odb;W%EnOKZn846#M2A4jB~b3#xcdq31fIA^D~H
zRhDJoJvgvv`v{KcN~xHeN=iz4@rEQpztX|7A6&Bj4R#bgm{vZE<F_QFS8FzS8gWR^
z2TwM)C<KFSRN;-VP)!hl>f11#z<+r7CnSXv>4`?0LN9Q}z(ky;UvvCwRR}&AYP*Vu
zByb1Aqs$*FXrfW(ox+S^uLVbAk^G7T5-Y!-?k(2GNtM!|d`&#mh%UYd8D$P;AH{ar
zk#ZR|y+;P3=&?VU$YoyE`zsa%QgZe&;hb?V>3=9<7w(GvZN2}?lc>p(2RN%DYi(tE
zj^*(n$E@Lh<AJmKhQm6ON~I_jga1y-F@ipBu^fra>RwlE4Q6msR*MxkaN65rmmi`1
z>VrPs3sWFMbZMqdBP~piNWST33xItg3C*N=W=G}sOe07QgoleYGt2H<P)*oHDr(_T
z&0qqbd{QQf(bQvFWH>q*=i|gBtmZN%riV8>29`ME60@i57Rh6$l;fYU(~3g?HeOom
z$w@9}p8b|ThG!~RQ6X9yCBznT>IFbx!wWCVz_!=4lVv+PzC{pHpPPbQPZRoth*#Ly
zVBLHa3kYjtT(WV~n#e8VA4_6)4PD8_tG|1DP~QpNI!)f>-Q?pUX<o+@uEM6>&IMha
z^2RpqpMf7zjPs(588OI7(>wLc8!d|rqewCVJh1oC#*wnPXDW1mEy(bDG<oqrJ1STv
zauno{;>h8U(ip>z-g^$BoxYIjM<%RmZPI!{nRemr1uU~G*qxZ@$~c-=p<F`O46v&t
zZS56iwaH$|5l$mrxsaJc88$f_uCzDzFA;5U`V!+MjxbXDOLU!RwjQu_8FJRd&Z)0k
zQzlr8N8c<*MLoPcmwlEZdkK5+AQTXucfEoe3~h@R6MDnNKb@yGYlcW8AeyDX`9((<
z+7G*xq5OuIn}g|w!jhW=vC>(q_IY*w2-60)E$&MiR!UF4D@4@=Pu=<0`zOCvl$a{b
zeCqVtkayH2wIGru0~u$rmqKu5i%dAc43QRJ>(hZ64pX3R*{^!{SCYys(e!x%sGB5~
zOx&mFMSALq`pllT>CLZv`EWw>kN?@wCrJv#_{X8uHSnGYBS0b5KGVR70DgNST}Tub
zGiIA0bEy_;$?E+7fs#51yWigTq8az%E{4n$OLeNqp9EwW9@;IEU?`Fg&wByW)$)dV
z>8kbX__4{O>Q|di3rP33^fa3b#`}pTvnxvc3!HC=s+!IdFC{KzFv=n@yGFP;Dex;5
z(xnj*1pm+{yi}*dR@Xqw9?J+ofPkw@BiFP2p(8qojuq}Fq_;{^6Gu-)E1`ldnrk97
zS<7}C)d9E`)^8tY`SxGjHD1%Uc+r3mD688}oW|8E3!+<^mR>`(9#F?P`8d3%6XF^r
z?%#Gv$`J<nqgbTdS%v&RI4=iY6Cl>-MJGNg4bQxDWVMuvH#l&*Fwh_77}|UFN^A0k
zbxDXfJpM%Ikzot$dv%h)pr(uANRm9|`FOh{GiwY-0Fo-)rGH+%F!F(ZYNVvKp2xZP
z3fO8fRse@82QXrtQ_^Rl2ygWRk|aq1O4j6slpKH8jVfYQk{>#C5J<yX^Y)pnj4003
zK&}MX^H+wq5-z(yCWAqXP>7`DNRR+_w-873Dx9+?N(Yu~sMslj4fE2<+^1fy$*)PD
z6p#+Jq?O<PW?J@(&-y3us|?ab?adR!!|9KZ0)QG1LoECL10W8R2_Sos*r6XJW;AnT
zpnF*YYPK!(qgmv977TwJv^UEz&Oz@!-Y1}{%W7$H_xHPBtb{@#_<xI#)#aaGSjI2M
zc7Ht^H^kvEZ9#x2h8X_)M1|;!b4pxCor`UOdd8f$uJ<}ch78{m>OXXgSP-SezA^w1
zCEmV!Im9qt*~Y1$6<m<G)DHwqjIendPb(V>dUBiHmf1?WHp>en;t4#zvoS&mzyvN9
z1hA$B9@t<fHPHI<kRCKKRCW|ET_6d^ywE$Zy3_HzQn~*f&31oGF@gU#T<>S_9ap{e
zwNM~4?tkxp`{=W%f5BpA`T|z_hgF4ouBoU~l7&UCVn<qQ6Xc3u)d4pw1L^uQd+OtV
z*Eu@wn1CgfXV9~QWibAkBYwtPvuGFml6cts;of$5m2q7@hhn5l)658RUL^%7+h@|g
z6IWULeH%iU3roD$VNH?vA=>Jr8>qacSo@`<?G&7+NRYE-NQhZ<CvmzCCMBGrO=LNb
z>C`!+L{w*Vvsp5?Jv=y~XVmWe=xyG`rM5r<#}A&u`br??*;#zYF@OQq_Ox8o;^j1x
zKjeCjivfZPKCd$N_}<)v^eB(01__RwkU*#=?@WNcrikr=g@K7^>fw^C5wCNeOA{Y+
zw+77)Wf9(Jz#%-!F>A)`#x$Ajrm$1QRUS#P2)F8Pk&aMYrlOTjxn3vTm6-VsY|Nd|
z9L~G#a_8ci=%%|Shembx?WkOLsrmim@5_TZg0`RXN7<4kVs&5!bX^O!0ni_~|9vd5
zDjHI}IEd9>2I-O6VxXRUeQO0^%P5$A)n-aQ>f6~UwMnUeaSvz65@p{0ZAude6V~0r
z`ISs_0PmCizB)>v72_l$HdQ=S*eu@OUmT?pDf0*;lK9s^`Vv_W7Eg3qEtKVejb;*J
zwOjFCU!us0vpGX~xW)By@o;ly<^W%q9b53ge+yL?|96KQ$4M=Wl}boX4voh9#P6~~
zCz^PKi!uhN+x4(|Hm|=~)WHh~%~Mm-qZ1#NnRLUhgWKvy<9iGLYAoMEk|Z&JBQQn7
zGODgfLlwNOi>r3IUB#f-11PdV4JvrpoKjoK)L)@rOL7cnnzc5aR5ynjvQ?+Pqbti4
zj)dXJVSSoZ;(dRj_8Zf?`vt~9olcV0EGFAu^LiXo<c7RQv^G&;>Q2TfkjCIildkK#
zmI)P<(aS$mp-D6{fQgnuaeDy-UyjdS#*5K~*1;glRCPzk<Y=vI@E>YnzZ>J*;&;;Q
zEQ!z8hiKlo`updh?kSph#!)~rtYDC<T}r&pA;cuc4^5M%`xg&?^TZR|i3X2Udt!wU
zi7nBhu7N`>iCt2DuM|Wm)yD>@Y28nwJBAWe*A<}Y#UUp1EE$msF4YM!WldvD2%~3f
z>ljU6V;LTOZjjmF5nQ!OmeQQpAl$nLt7<D?7{fF5KZ+oij=(g(WEZSZ>(r2sKDdjA
zrM(%2o*zO|%t_#;=1+iBu^(ClVY!J&^}D|~`PkhEY2(Zxzh8F}{cvPwVVJ`rzG1}q
zHATB)yeuvu-(J(9nqygtIX*x9Uy|aJ99j7y_;aTxJyHLVUxx~bsL&~%tqCvSbrBAQ
z`NTKzUvVRTq<h22Yh8-Rpk1!!*VPDPZxri^g4nKB8_7on_OC@Pvp7~w!Uv_&uU6Pd
zNU^kUv-gKuD&`KlH7Z)%&WkhJI8>u&prMw@tDl#zUZn=dZIh1&<v3=Fot7N^WQfgt
z)qI4#=|w6-iW61J(-xX9GU_?@b7BHsv=OMzQD8itHY<4bpWt$9b{HV_$>Zpx5ZZ*E
z;}6#oT$qn?0i{BRRv0TQPuHfqyX-?){Vy4gxhSPEfE{j(aM0se7N*p7`$(|ck`7_f
zGUC<1@zJp1O`IFznz0UK#G-o2Wn#eN-m||va<cE<PYW^ho81kMi_1OoYV>J|?D@(X
z1EHgp?ry*O`(Fop+Q`UAV3IFK7zInUaeJ!wZuk8wbVc4X=oY5sa$vA=diQxF=gi<k
zTO)QQ@r|$O=C>ms`~ueTV(KU@ywDB1x+6X;!T#L>D>Tv>eXax<5$m^*z#?(S|8Z(p
zq)1(qNH_ZRhtjMkA{gNyY4+K&JtmZjlnPl5sdAv`aGy``@On{gENI{MYZ5#8N0Gct
zInAAeXU=H#uN@!5G;{`s^;x~(ph8<^jP!Ph!dv}?|J1FG2q$umpn(f)oeer<qh4f^
zd`c*tYc-+AlhTxf8Wt;`sQJEs|Bih7>E^`Z<IPY)s=H#}G@>^=a@e6xX&^qbO&WP~
zhCqdP8`98E>dlql{a=h--0^KYQ;vyx;&8cN7AAIl|4w!YQjY(l!KQXJJ`RnAY;zw$
zD;6<|8c@4oWX*OyHAvL6P|vsi3%^0YL;ppxH=?OOVnn=1&<pV{=^=*Fmk?T4&(5+G
z{s#ZD7X$_8CK-5JF5(uO%H^~~R`IfA1rfA`-HmF70O5xr5Bwo$JUA8Fu7PqNN6Coy
z2rc77Tu(~#?+3oMR5bBic$oed@1XH%^do-vBg$WwG^!`Vnemq;y7Unux0`mk`5hT7
zmjJa&(mtvR2O*Qh8{o*@3;6<|Ky6Gj8dr~MXMvyTla5b>T-QeWgI(W2br1KOtZ^Hd
zd*;&kbp7LxL6>p*ehE+a!Iuv~R?{eJ2DYZG8NG}0jjJHp_z$ikytU#bh9LlLVR!*K
zDEnsm8SDM;6o;cVF8$D`R=p~s7+fCxk~|w><!>8Z-?;Q4rx^YKdh#PDN=ukqadRa@
zb`<{UK#ZbyRUh$+bct?W62mJ}11G@A6%Q_a#<-N<x#cN?<VW$mZS;m|&^oWHVuDMK
zWcfV7sV!)T<5xo*Qx2yr)n{afzAYAs`*jaX2Zs^WWw*5AV>XNzD&;+}XzaUstSGr|
zg@oZ%lf(SbT;)dFldQz?FPsn6KTkf+6tHr43Ek7rjQVgYP1RYneY6<TYwLYoKwbgr
zhoIO8%Z1mJN^ve$muqNa8HErT01ya?ctJJ%1%j-P@q2~quTpoJ$0_)S?jQq%<6R)3
zo#hIBsmTZ4ftl8Z=TzjWA{I6*XgbUVECsc<1@&EBuHiGy+84?SFpq!(^vhpSd|<+S
z!_G2O9iaGE!l`6*{XQLaZBWW@^%SGCLCE)vr}||HxY%6N3{GE~kowJ0pXLIfp+8})
zcStacmtq&IkA};}y!)(?<zJ&&@1xc8z#fRv0@Ljs3u6+v8YEeEWG!_(3ahT4_~yFM
zAl}&hB2!wbPZz_Xdz!92;hnaxP0xThP7<3a5F!x}>dh7y1-XOAdL@rV5vAj6<>8>&
zN@=oo-wc!)E+#@NlNzC5zmXoOK<r4=-G0HJ_h|%gkF^zf4?fG~w1j{o82Bak;i!E^
z5s2rG<Dzj~Tx1U|pJK=NNxtrYJdfQcO_jJ{x>mdaa4r=WZS|aoLv2L40ERC5@8xAd
zkKbN)Mi|F9a+I1Rsg_g|WQk3vPnj-}6;zfjZPCOh9y%_hcTdmto@@-sz~s-L%tVpR
zGY4)a_T(;=PC8#fR$MWs;MNu_&Dp!A^=%+UR~lnAnCp+}82tOTT?Wa!<kzy$<UE1>
zxBX&h*a#_{5?;5n7HUZxf<jX@^Qri01u;g{Pg)BkxKI|_U5vG%Su7FY38o;-e$Azn
zkUUA{wa<|6?S-w<$`1T1e+>3;6hGvhCP&f7vSp*N1st(X&94fc8OPEHCw97=7jy4A
z1UytSGqa?$!7hmZnIdY|Mke!6@cJn`{3N;WD?I9@EQvK+)7qZgeQwj!_?IQ+^9yaW
zhmn{+-tCPzjDYq4Abf((2K9iU3iTgfdqObEJKzEGsW@`}b-&AI`Mrmd&I)L8Skt}i
zGKAfmW7g~~xwXF=$^i*{S|fUF6iO(vPmF<jSAC&NHmFQ0zs2M!Ei8pme#Q^|)VpXD
zz<cY!qT_~Do@RmfjP_&G3X}OI;W&#$wIA|G^Mpf&%ixFad&yAkf@n{>xz39e3E-=2
z(+fM0m8r8Frsqu%yGZjpRWm<4j1Um|n;eE<oygjN{8u)9%hTt?yzjVu9j;%}TObsN
zE9fvEVtXedLmgpq*?WXuHa>n%09gV}?81O!X?27z|EHOuKfg(-oZ-XGvDJW^(G?Ct
zZ_Ahql_}ZC$ZEz}TGjcBaXi_`uG~*te$A;+niqRN^H$S7o9KBuT}Z{YMsK#wcNdL~
zt=1g9f!`q8$#5a`iB+M&*E~wO588c}(plNjq%fw%`Z`n_V3R7HSMQFErj!CL!BXrM
zfT48dkXvH;{R%ISuPg1tW&g&-SubW$^f&FGSUo%K@Wcfi9O^!H$K8Eio&+DLrB}PF
zga-g24@YhgB*!QDjgNDgapz<jy2R?H`ta$M8xcpc$$4qe{vLK^thh~DIYyv+k4yU$
zT?qAc%A5g7hgzY$A#%cK-*}*|RdO4bR19g<1!JRJ`rYVny}ay(2aIiMUqZJ^PLzwU
z1?6Y+zsuXN?r{IZ$Ya8hn;w!uRF;Lnwff;3Z5Wn;gyV1CuY8LLT@rw`tKZBVHb^Ru
z|1LE^>LVA6n>z(=5pE;L8`w8gwKeNM>s0I%Lnw^jA`oeyg5|G3zuFO_SLlA2g{hID
zm;?%TIZ>e|D>z#^8CDiNIE+v>QrW~;43E0;VCwT+T8<qXCujLi7Z^_NzeJ+}8q7mF
zc{6BqJv>aq(Xi<SN1E{Zdl*IPfll(MjC_zwY4s#&s&9O7esfJTcLS6utY<Q^RMh=z
z+)U$SFAEy|du)^=W2WDKl^~h?R30ELvchn&1uL0x0FEcKpK+B5xG*X`i79$E5q#}&
z=~u3KfeVzV^9%BqzX3I>4sR#rJj)N(m5ks!Oitd)n}Z-7QQS*Xi8$?F_ZO=E(WI3C
zX@7C{4q%6erHh_?@`f3f0kuF^I*20XpruJ`OfJ4PQ&Zf@{W}VD(RKO!>TbPmGtUc$
zfC`{M2#B+@+jqZAzd_^(d>E5c{+JUwlB0Eud7r721TEqua;ImG;Qx97q$>zm{c2C(
zmd$#?GE3nulchs*`K?_FO`M7!1j(>T+?=F31gzku@<?}FcT((hxwOyjiLZeS**=g?
zJGdg-;DZ!0>XXhwj^2=;n25)mK|$H`shz<paK`o~+le<cDCk)mDn<Di`ZTt2$F*^s
zW%6e@Fep(UPB%COD3fr9-D-$dG8Nh{$?jNkMXeZ!wb{Oy*tJ$+|0TfQe*edy{n$@W
zRAH#TR8Hyr3y8a)*TD-~XAagA5E+0J2R8(Oif88P$WQ@=1RNX(o@(}1HV=%>pQ#V;
zWn96Mwgm-P6B;yRe)!265F!+cnULj^<v92e9#o***fi^C1PXC$-*m-0;||sBRS9|+
zhC+-<%N$4P7$e^CH8auPIYL*C){zX{Qf7}1T^B662mN^l9vXPJ@vVLw7gXV%w8ksf
zbLvx~A>SAj85SgCD64IrHn1sa5o*7Q7quup$gCgx^=*vaoGn~eq*CXd@e7_fTJExt
zk6-#5+23sS^C#N|^3o4#X;lhLA2(B4EkjcAy#bl&N1jGB7!FDAzHX%y55<w|T=PyP
zXdNu|**?ykxX($jtYfu#xoAVp_9ofZAQk7(mh2a}H%6+ghYgUg8m&wy8TMj++y7G|
zJR@+NxxP7*BHK4<+rQg%3XwPuHm{G>EvV)>JEU9P6H%9d;{?G)*^oe*haq=3Lu)1G
z$K2I)fC^WCKvfjoW)eWSmeS6rSOiuqvKidpsDy&KLcaJfEg|+9VxsQRhNYGyltT(`
z{AreiloT6I@rDw~b*{g5s<fFnD)C>+CDY^ier1f+C*r6m{<5D1pr=da#w%v4A2Pt-
zIQSeBs5u~ck|cT#eJuv`Du=HM@*US2n5>J5r5PO_1cgS?G?JHe_$@=ul?l_|WSP;-
zRYZTRLv<5TTaP(GcZqEumoORAP-R?hZ!Xb8g(q}H0^<LcK#C@Bkh2&!W*N?!`23Tu
zR;d4v?sWU8<R|)Smws2UO4gvXp59@w1m$l`qR76=#XVN<2p(RPA_CwF|3702L$1(-
z%qLZfTK8ZO|4=74`Cu6&!o&BI%UgL1v<VENFYT1dk}HCzY|4^9AAi?N|HLhjiNQAY
zeGyqhueJRw2W0=YVs{!Z0(TB>rc8)~n7z((6_E@OQp|s;Rcu&&Qt0y-A<$lXtWG0k
z&Z)s1{e@VzS}xwehGC#dQ?9Y_CPPKtjDNrw*DLpK3v~KiAC(+j*4{>%$;BPp(j_eX
z*ifhexkH>jp2X|VJel=$)`aMjVLmdU9e#K<@H=u2g`0D-<{&nu^LVGDdB$PS?y25e
zZ<qs6z_hVR3L-4DNw%u&Cha*FL;62^f%ske&7XXbpTC1IOZtNxCryXGC^G)229!J+
z^M4W*qGR8%yI3n2=<vcrT~7yjs4B_q{a5&7FBIg}8R&OOBvSf&Cep73l5kFqBZwDw
zF07OLH8Qe^nWNNGTXsuorMwsq`fWTlVSkq%`6z(jF<PN1cStIFDbY=>IfD+l?AZEJ
zJaGk(u!(txE6_ny-f9y?hKi;ftfLeTuXJdmor-B(+eGy&&Mm_JLX(?fc>P%yb?TFi
z-{?resgfWZOyOsd_vYj|E(ngT3-Kvj|6y+xQ+*mM%2*R?G5etk&Cgo2Y3XeYK5J3q
zZN~#+Mp`AMns}79VXx#+uj{sKD+<}=X>)YSqy&T98(%8(Zi~@!c1(P%-1ZbH<%`}q
z>?)rE7XZ46!-!!-Q{8Owlz-(vRbdrWPtSmzQ1_WNuyR;7g(y}!E~52g6_EiHXI)*0
zCLBjOk$x|I>Vn654fPX!2xpOBTgXR~p1QQtjeV={5<PmFU%CtfTUDB}u}WUU{=3ck
z2>q!psEtiH)J0itk4_&nq%c8XvBYhmdtr%pIcKGrCybm}ffVFo77%r<%sOfk?UG$a
z;7=qWo;>Mp#}lK(oMfaDOR|=9BNpZRBLs93w3}GrE&{=2a#0tMKMt-*i4l#sMHz{h
zhKMml{Cv~E04V`d)~LAuW9WbK7b()bl|p_%{Apsr`DE5J9%x6IGo}*TpX}};E#?EJ
zsWjjYXA`@7F?Y?){_|_I%YyPljMev`Z&my1bmCg9-eCY3ag46ad4PJmzhM6-$AJU*
z%%_#5k^<?5<7CuQ)uN|>?#6jpF?8+%sWOZqj2*H?t^Y1jjxNZnb&V7Mf-D@VaSg^y
zrR83)xWlA29C4ds=toHV><)p4+Fj>`XqL=+4R(%huY=lOh)Yn~V*EwQ^bfBBdx46b
zi9EmFEZv%**}G9#kG0dWMWb8r<;ZV|f6?n2s6H3^d+Z&nACIx4>G*9xhFshVbMXb!
z^qUKh-4?k;Q;NMvi|c}hV6@LU5pUu@qF2*L9K1}i5N0*_&&6*hH_a)R|IOtdAmFf>
z03{FPpQhiuoo*4B$8n89J@zLjR-5Rd0)NQ~M8rR9wap}?BVRuR`u*YnyFf)ng=ug&
z1c{NBoa<7Ep3I~}9hR@g*1xB9o!3R6i+gWxt&-b^phrqizQQ_VuQZ=&9WWXDCsNmT
zz514x)SHsdo}`mC+{UwMYh}}MS|~|#^CM-N$HKJdo$w(`D5f2YzWT@Xh^C|r7B$|v
z$$^{_{eV1CO5IZ)9NuAK%B-XuY`PBh^)*ZVZ);ay$3a`hkHVizg(I-5<@I}nHeDLg
z-BhU#yZJfdB6Xw^-J!;NVtUoo*SA`TKPvN2&%6U|$#;ZOYwR&*3tT>a)BI1qA#snw
z4s%(a!yfHlA`m+>1VGjz=-HN_uoCC2Zfqm?VX&8qUJ<lB4kT-dGeQd&Z4$DU7NoKn
zPFHtO=P}7(^E4*q(bvZfQRjBEzN@A)0V_~kciqGeRb_6E!LmYAP@7={=q($#AgJeG
z?Gg}x{j+vlNmsV&BNS4l-Y#78cxg<Phr>-R7W|e0=0rvs$Amd``~qm4u-q|-a;%@n
z$$1yI{)S42sZU&E#HrS^*QG0yeReT-m7M0r{O{}mxtU{$W&`TolM5A1U1nB!O$Km7
zkUb2giQt8%H2V+b-@`1T&eD-6qN=&luNSR;S-)rAG{{V66laG;{0vSPp4^ipebN#C
zxQjPco}a8T1^!60XL!HJ)|T5!9>EQL7E#yjZoRak92it*k$X<gQ$9^JQ$$tm#vPGs
zv399?wu{bGFmd2SOj%0gzkKMUrvqh8-iq~vfIBGPM73@BajyTS*yp^`mUbI2;jv|F
zxpzx%<mXLYKgRLNy*ckV)v&VGF6Iln*fLS{5FwZL9)7HZIzRfy@UFZs8Xi7AXZj{#
zTyZbExb;4QUD-5PrP}>X$T7Ch+{bOfZ`Y<i7p5PVpk~ExKJzk+wkOp0aaW(;4w7G;
zO5O#FZByz>#_;Cy4?K|kT=1w;H(KKMf7;s@ael9C$~J(`pF~VNy2br;dEGB$X1bX$
z($AzJszpemZnva%evQmFCD)PrUs83%%sG>z1Z%mg+~?gg0#eH#>Z~aCv?~x-suf|!
z_0M`|B+=a-8seB{F*|11BUMG-?bUtVZ_VA}e_A>fWDE#J(bsU<Z*_0~E3Upp?%iSX
zq;fk$1{bfNCUT|&6#xoK4L|OPxKB(GjdUZYx$<(YJko<qh^9z(!R=H=Ec8w$ee>A7
z*59h;sPxsd2X8f|qO=&{LVuat0{NU`5o&}aqY#FJj$}!~xCWRa$Ms=w0}={@D}A6-
z?@$v_MbOFj1nCG#9<sF<7t%fN%H?6BJ)Y_j->hvxtM<Pa#Y8)6Oq?#Ub|=1;d?&~c
zew1<VRG|yU71oqg8=^u*EPD)zb-AB+2d`97_w>OjPmH$xE~Jt?F2nDa1g@$8@SLl}
z2g&uqTd6y)(s{RX^p=MDKU5Sqvpd?WOCh~4*#6^?bF~|BFa6~$-?d*=N6!sUpSE_~
zNDO)3&;MD<yzNQLxs?E9P0~vrW|D)T_JBY;gzxwQSb22Ag_L#Go?L~k_sI~Nr4f1W
z#lQXc4QE4^gS78!9Z9J)a|;{r_;_IT=YltKx;VUK)M<pcR1tEg#zn_NHFWPk^;e8m
zqrC2!-tW30St+p-i{iV;zFl~qFbeN5hj#2F^ZjEVR=gq(QqH>5Jx2fh=5?&l*3~KD
zZ^wbC@^B&NUPPo-R__v=Rr$DAreWsqw?&)3V@FE6#h0zBUU;t}uFO{&8MZ(^p^S82
zL-KmJYLexgooVK7k%ZA;g{=3Cws&7RTI%9D@iZC?*qpMFhDe!HjKex`gKicY7Ihoi
z(nhYzJD{SlbjWS=8Ua#S2uATCX8D>IwC3Q=X@b0*OoKgf2swIMn(!9s&V5o4bPLDB
z#&N@hWT1~TSU@oo?cqP!v-t6rqK5Sm<DIdi&zh!f8iUX5RzEo2b}BxK@Vvp>o33qm
zEgbe=m6G*9-JkIZjUq#%!F%@;HQC>ovi{bR&XX%?)a5Eq5*w`w=}V7Rom9WccwAVv
zR_8wd)IdP&4-XGJ@K&sjEj6&nsctByWJTAXK%uU91aeofQ_$F0X<-!oAJ4ZD?@aT~
z%f???pZxebHl{#Q&6@!iep<f}BlGkM$i3%nO7P17r)7h6!?g9_&!Pic9sMxT3Lo@O
zHzK36pFT#IFk7Y1cV_zGtEp9gI6fTMLkXn^12mI!8^tC05R6*<%B}VlP-~jhT17tN
z)Kl@%ZJkPwztnAtb+B*?Hylhwn7-c;QZ%|C*Dc9V92<Rom&|;l@utG^%(i6<+X1pu
z`K)g;LWh}9i`L0L*I^Uy67U0UQnGMpDf1=fUua>|p$ppO4^)@ObXg`ga)DW+vL3P^
zB0Vb_57q}q*zjfha3oGtT%&<o(%@}tQK|P&$T|7xGWkL4`^e6(&|puuy-0uwP-1Mp
zOtKML@z^-dJbV=xe%D4-6U!Sv5Hg}}Q*<=^ecew(SLycFBR|G<iF3e1uXNNFVe4Fl
z_m%ypf@#&d$i%+Tm+Mk7iMNm3of;(*#q!VHsK?UD>mx2^OK4ssf^>SU1L!(mc&Z63
zP!%KH%fh49{V2TRB4C9mZw5WwsL{rhW##nmJ(=RS?D9mZNd>f%3!xP#aj!*9^{KTX
zKr5q05^k{j$|$-x8TPlhPxX$(6e|Q;7B?=e@e*vwKV;$Uz0)-ColYuu)zEzM`0-hw
zquU;6sgQT14sXkEd0nund!UK_D;<frMW^{is}_khwfy$a;bhHs7vI4GD?*3j!|RP+
zf`Z2vfiJnK@BC*ihS~_+nIu##h0{|Uk~x`=ziRq<eTZ++AoR?_Xyle8B>8#h+P_6;
zsmh*UEUt-8m2~x}VGdr#qdTwVkskjHW^oz(l?aVbtx6sxrkTw=jMB97JA5Z7e+eR&
zd=<;`<7msyNUj@(@)V#^$Kb;-qKB@&tk<-MR>9?x%Vx0iIb_hH$nblBuWuu?noHVk
z;Qr^@{FOBqWRM2+wk-u;<>-dAly>2wk#+D(k?nl_ivzj+N?apeZPPS{a^ak=Z$I<R
z;8$W4D`{$a78aSwS?SJnm=`i5Mcx7f4=udg*D<^vlvaLgK=npf?@SpnA3hCWQ2u|3
zUK^K#%b@(XdbgZB*s$tMBoF6CGa~s!DAy??o(j3r{XAzmXjXa{_9jKtpPzyke`@`W
z%X|Mc*e|b+<+>UYQSJ})_;2KXT+1fy<Y@{3iisMXxGIC+yHq3--7VdFpqbvM7HRL8
z6pZzUu|7W;2@`g7hs^xJ5e4P>KPMkH_DK>;(M|qeo9XQRaw*d*nDL<mo_C*#87((o
z@P!0?kz3VrkPnKr;tH(-o_R6nvoJG9kuwB6dIpI0&A$u^P_%H(?NR;>*F+xnJ-hs2
z5J;F5VH7m+`xEOl<EER+eS|?_j~JZfJwAIgRmG-+L-Z|^)eo)m29}uVm-#~hA+e7W
z8}JawI62)PJ7O=GOoH8X;;S&mI+tOD=j&kJ2ls^H);*;!{6R>g*c%UC7Yf4(%HDYz
zF3XsIjf51w=a#-W26}Iv;hG#pKm1<(3KAM5C$z5zMvLBLoThJo{HRCo*6?NM&L4f_
z)~reN6t7(4J)aW39CK2js>4g6zY6?0I!DRS{;Qa;z#s*M7~bhmPa1w#WokgbC=QEy
z0fpT*fO(IX&}8B#T@(7`BCf|C(sC=bPEDg9!3l=Tf-a>(dFt=vs{SZV2>HeEZjTM5
zn|%UjCWI`dp2TI5M8UY|r^LtEMY)*7!*<p{S`V^=^SkRvC4gvAwK1+cn7$CrR=6tM
zFO50l%?J#A@R}7*?xNtSJwxDfDBz|?zjXUNTa|5&u_@O&OCtBq?C|#M-}B#l$qGa$
z!h)Wr5r{?CKW)a!kfqNKw9+Fd+`(Bbz}vZ9^P_yo3QK{%-|l;_Q-ra1{)lois~sm;
z>R>7M(8nl5xtFHqQ73~#DKI4h0~Ea(w&-L&G^DABeEvE1KIv=B&2Nc;P6W=sJvjzI
zZSS_AFw3~cdb8L*1HFPD(%g|pRdZ1fXSl#ja*DJk#WJ|!MC7ZAa<06MblDk)vzZY)
z*POvZ{lHsgG-Uo#qIrDdxd0t`GpF~&?YQe7?c><X!;aq%|7QL%FX2e~IHw%i(G0ON
zwTZr^B=oO8{#Y4HGSZlI^R!JrV`M+LnKFL*PBM=My7;g9@RfJ;H3xVDP7GXEC;*)3
z{+D>7f$GR;dHASi-Pva{@2&G)<cj9rLwW;j8d3$$;qwrRrs@4gm%t;pg%3%tt+B@G
zeS#J%eGm&++1r?;@EALFWR75xI;2~TVJuUhzE}~skg1V%g=O9vY>n6F^e5Z+!JjTb
z#8j%fS)>E*gnKGVVaNa7bV$x}RyOAwCEk+$&SCFDSB@Y#fsTwsW~-2sOkMFwWL3tU
zJf9nk{f@54a;D-KBbL|rPTIG5m92$F%KZNV(JC(0)hkw@QIrpO5D6Di%`emBwJAEx
z-J_6!$5IaCugqf*WZ-g4VzDUFFb_bcvs&a8tEd>yiT4hqx>;kI0+Wp6+T=wa9|u9h
z<7NRB-S!c>AS<55zqX=l)>3Er$Y8#XA2!DPkD<MFdpy{IX2E*6{<^&#w)t(Sx86~y
z*4||Gf!t?VeBOoQ7;7%FZC~4jztleE7S7{WjWIuhm!6lYb{Ny2x6&pf6$Dnw?TVEm
zS3<B#-@jd$x2{-3C5;cm-zCQ(zGRGfIaI1=a9y(^Z8B@EyuDKpwK-wZ&$9C0GRFLo
zFm+D}WgYLYDE!2p7WWS}-HrP0<{W+Z^9cI?IsRXeE6$%;|8i+!V&eGT-d<FfD->gm
zdc96X4N6z8ULz$|KS-jJct+XJy<D@l2G#;wEPqIA+pH({DMlz5vREJq^`Ua?w<rBA
zRBnY4N;e!dWrs}dr^T>>rMTV<Nwv*KjP(pJU}C{_4WFN7<zl5;`;-x53^B@9i;Gc;
z0R__f2DPFj#iO~!W66Ho=8P1Sz#2TTlB8GO1WRI)9QGjT7`W^^_E$!*5=+^_z(n-C
zcq#ALM&{o0ilwvUZp5*q4^i4v=||p}Ug7f=k|1J4q|rU$NF(HHvCwQ#;!3^qfr%4%
z?W7&)WG0U68j25A(D~Bp__|$?lW$Ljysr`a%{e@Zcr0<%nJ+j3Ur7LQzp}qsuy4n?
z%JE?v#D}S~I9-oPjblxcrL)#*I|W+z;rf?R-7g0cmisOR^2$Iazf!5z9z#I4B*s`8
zM(rC=C;4#R@%onKjg}VQhb*c+%07Ip`1^l%jD;^iefM!=%-0do6V@ppx8G-sc^O`!
zaq`hhwf5s^7b~Owm`J>cN?`UX+!p$#EYcd&=-0DiyMmQM$SZy1|MeB9`(!|T8f`xZ
z(pRe2>@mcq^^f3lWpn%cxFFgmCj!R)zEr9;BF(<b*N<1OUdyTKN}*^9bU#?8@6JOH
z-Y*whBA@S35XD=ZexV&!%4fy>so69tt|G_xdijisTF{b<46)HeZ!S)`?phiq;;TGJ
zvR<5*-Pds|4|fbA4c?BGSDWaA0=|C*8QY6Y0gBUZ8Dl;%mz*ZmK93vQuhlj7a+VTW
zG5i=r@`~p2ON+;n<LJfLbtr)~U_c-f>c4~u-9j^pDfYZ^q|(XBUQdDs$lzIl!Q@FE
zleM!@y+pOezFEycfg=MEmvW*N$Ubuh19(`UW51}l#05WGVQO#ZV=rUU>M~@G1+jX%
zDZROD#T!-#ysE#L<BZdt7D58nGLF@FA3=`nSCWT(R7~>OS81Ozh)X(KuYKOLlDZ{=
zR5>0N97>lWJR2693^>{J`vg}1xUXl0+R)jL><a}#O)7_IRgw2i#M2v4E_xZ~{&8zp
zFTt4Ef}azwnzPc!M_yew#@s3XUlsjTG`GjnvQ)<5x@0Wkds%n|T*Wyp8IdB1Yxf3x
ze#h6Lj&aKPdJsFyI5vImAIR^CC_=z00h_$}5Gln(MZyfeYfI>>%c9+zBGr$M`zB+|
z+pwP>30cj36f4fY>z)_wvePFT6GR-_Ca~mw(hey+rA}u*|8(^oU#@=TXO+BCC|W|`
zkN1)P?^AEN?{B~2=+xBYx8uGNYmo@|YL|6G5&K?m#dA~apPw)35S6ri9yjYh%G)+Q
zL2N}WW^ONZWq2{Dh3yxhK#z)>I9BLp?8_=NFG&{1c0nxsM%hC2Re{AGrhHCN64G<?
z8-z00-ev*ur;OcLNoB`#MJrx>D{Lot+$`W(>b<5|93r(AJr`5;di}q{ql;lz>{Sdt
z21$8H^SQ#}v1H%;3C976dc8jGeK2$oYL%1COt4)h;bLE;vgjFu-9&&9w*6D$rBzp&
zomj1z#qpX}m^miVP4#;_Xm_R8>ZeEe{9&4EPk0Zc7Bq96T9xBl=?#Q)j*5#p+Z<n*
zq)V(aVF&VO6Z<_U$bg&(#t)>FTEy7CU=eC2r#ZfC&s=gkVfD*dw5`Au_T)K=W1h2K
zx5wTp0=s_;^^(N8@DeFIuBA)m#`usRWDL|yAGaqD=(~HVdZc)HUttLGohyl-9|cKP
z7KBj&{jkpi%~E^G2NeCC5dVh;^41t9^5!FAjJb^I?d{WIJTrl{AdK+oy1vvH^SxpF
zk>5LO|G#03d6#%;)vA*C=DFnbABq3%ij<9hhmYHV^7_@UZ2s)#R_)iecf1^-LZOI>
zqel*#xtG0^emjeWSl@5H52jqIX6t^;3kFTwI5@uh2jUYvw~FEzO(~-Ae2Tp=*Dl04
zMfS6!t^6GBS)A4mWQ-=`2G6<8iiNQtWy*Dq!nm4oPp?d)Ma;E@=jEn|V=l`APC}jb
z1}sWN&O@x2fN?>n=@_ecKJp?l7eqMl+<)pzU-{tf?w*W)Ru+#Xn;LhD{$p(;SnUnV
zFz6PxxY9NKs6l&gpbD(v1pCyqCCj#yFEfyb_asNR3FX+nNu%7pQmx$sG9WD#FV^Rp
zPK(5YZV5prSK#BE`Oo#rpTSuDgzJ@QCICf0t%?*W#<GLJYK$oa7E7FZKz?1mKvED9
zkt?M_J=h?Q(8e^5H;Ys`_DG{u$%2P?boBuRz5f>lL{P+WkS9~PqQ-+f&jsNf^()4h
z?;&3&GEPTTH;KGwC6@!H<03`x1SSQuP%2RXc8_BW+ig7#f{M&a|0Q0tiH`-Od*sna
zd5j|;c}0r2AI9%_n2i(TaTez7(&LH=&>KSC$m%$$*gC<%IL62CiT0fVX%(!pW5U7P
zljoZ(y6~__4<@4Mr|?~LO$gKGw!$-Ojqk4#X#<y_V%rpi)&=a-rBM8rm1>O)V*ij<
zz(rw%C9K*;5m>MBy#6lh|Knmz5J5Rvsn(KWOwjROX^i<!W6TY-{V|w2r$LUAiT^*K
zG4dCmTmSUl-rlb{j;l~8TELB)x6D&t_#zQlpMUbRw`P3??~a9sbcD+4IDX=I-`TL+
zkoKfewXE{$a@Fbp!pdM%sBc<XR4wBfF)ZL`_exZIt@vOwM0K)mDa}(ov?a-N+Pi4)
zvRJ^AkZLL-rhY%h{x3Py$aWQCgYQ#b#B&8aV+)88xXSO{6N$v)ZfobN7%b2j`<5=Z
zXjNp(1V8F+)gLH<HG~MMJl8B_O4e!%fm3X7bw!W~`zGbWJ7~#LUo?V1BCG7lJ^pu^
z)3=EN9)4UwM|$79%2lG?2b?!eaK8hk^w*&jYjy=xs*RooIZ<{Icj&h)%kMoE@K_0C
zuK+VjaZ)cU)!M&9FmWQPPe82X(U&>2la)><t(7_US$aggmVp^IE0pNGKVXb`yD^5U
z_`QJNi+G;{mji(tNEHt1xRAiC7*j<{(w`HA1Fi6Uuu`p2Wu2#mIq0S*NMz#CR*T71
zb2``cO0{;;x}MI|w7KN;FIK9xm!QI(>^B~pydC>{l&x~rlm_@e8Dl;zRwzG%>t2AW
zhb!<EK!QmUkkjm3zET+wMC{#WpLZVWsP{wlMQX1#kuoZeQ?$mVGr$$<q;p~=EG3k0
zCEQ!sNPW}l+p}mZx8aw?ds83_AF)=*!%{LO{(hxedyl8cggrsaSiyNtv`Gg0N5yB(
z;`#*R;L6o&A9oZ0P$(1~!i}4^sL;bh@WyhHJ2qJ~?^qz@^g8~&$FWXup&;%HTiuM~
zx{NIP<G_MtGw>BGY8Om+M76D1)jXcH_cTi|MjWHu0qcyG;7VadTfLQ-f&XTV`Dy+~
zq)e_O&MjhJSv<d8q?HYMtr~H0rCR$QjFBTf>lj5ZU`x&SDe2lUdM_^?OKSDX0fQ1)
z9R_Z?PE*c7gYcayb+Q7BC+#dP$fyNY>saYpjg>>}!(PGcRX%Z%^XNU^GqbqPn=yze
zyA=LBCZtaEBCZ|)03SL@L_t&^3M=KY<XHKnhxw0TKPN=hGu}QW`t1ZOXsrE~alO+x
zzgfq5$lzzorDDTAe*pi+3BEFdG?<AXVM{UvK#mjgghG%+$Glfc&P64^PYXXPm+^iY
z=Xf0bKz;hVO{fsbFY0&THRUy}H@C|@mvR1KK19ES_oc*r)!JrV5N7|IIPV3lJlT~B
zr2%_JRPFijTypyT*7ijH{f02>T@rHrjQ!xp_xnIl%CCzRP8+P)dYv)mrZMKdSP|t3
zo%G4dAsF9Bpb3(88D{l4kXQ?X?7aXr((hSm7da0x$b=oWd)o1S#;R<XMZbBC(}9<Q
z&^rY}mixrD==(H&Z^55K`jYt+bb)`~m8;j7Y>yMzD-??MK+liIu3WuF%9*8y9=u;p
zmY1#P{(%KT+|RO28mb>qh{MmP9Oa=DvOihjbzeGoFM)V;kd+K6C~%`v{er66&GaFz
zTeFLrtXek?b$rQsUbL*f>jZ>NM8>K>n8tH|2_Ba6JHm?oc_@vgWkAt-&i#ck=GEf=
z%kaT!%xmy{<9Ww#_Om|}{edc_Mhi+{bskFyUZSeAG!3CtGF&3I-F>!VkQoC^{zyY=
z`CyWhS`OZ}tW`6YGNC6yl~9+kdGiBk=K|W$sOD9v)*g-sRKDbRt)Yy75~Yj@_^hpH
z;VNcm6Psg8V()}Sy3C4nM<Yzi?fr7vrA$%eYf%&u^oEZB-p_zcShlXwWw9ThZx#_y
z{|w~71rTc|@t!0nGN@$tq}W#OOIB&S!7HA!A1c+_r$yDeeLz1;g6=p><Bd@6#KjC0
zML9tZ+No`YFavH2r>)9}Sy44{%SC*$0X+KXFO4x@E#A(GKvxE|`|ce$9uqinB6b<$
zbq14*SgE4s_IHQ2sab>&S0eh`*5~tle?yy@*rs;Bt5U7KQH*mo%uk6RzKnEFzUO2e
z9%pQ*yW@ENeFVRox&E$+fmDI<_pk&)%~nNi;k}o@hm43+Qg<XdMYK|_eF1^^pBiJ{
zD7O7GB3<d;fC__4A!hOlg`zv4`_SW($O0jMQY@adT!_U1t{i=eJptSFddf>H3H;v|
z^Wg#atcrXt;JG!^qj)tNvZ!H?wVo4Lmi-6Mhg{V*^D%5bRvj_m8Bw+OS@GVwRP%e_
zdj2aEiXno2fjbPj5)z_rX%(~Lfi-Im%52Nc8eWEh)2uiJtx~PMm(sQ7lGASv+YZ6o
zvLvKHfLy}FPWDH^WpawoUl0@cW=+8(jmNF(J!SlGkg_C{E+tt>htj;nZgLTU%722N
zjl^Z~_mT)MTne3FVhV!fyg7(1`Cv!ivwu`Z1Ppcna~fpHeIS3N;dKtzT^goKPmfa+
zoa0yzNPh+Kxm75W<TapR`zMSsU&8^5!<S8_N;c9Oajb{UZOOe{H8!Z!C;gS=`PzZ!
z|6+{UHpU!s^rvr}g?)!9W$_7d4i_wv#}1}0i?lv!=MPzV`@!|5zGLr_F!Okf-x>T|
z7q5Q=@3|B?VHJ90keLN>@^$Rf0uKpqe~9tNo@!qp!*%p|(O6ZfxM~pyRqlWLK6V%t
z4Sx;i`1g!4pMu|+jU|k)14SLs=j&*fO(rp+=DUX;IA@H@`*0mo=3u2yy7vENE;;=H
zkNGUCFrI>%@lP?nQ)uU`I4-5iZa}#e^wpKC*RoH3wtwj&3WcHpNn{lsdhq^2mwbno
zT|QR~q0llijtPOixYCt-Nw2aK5Vw{+)fnye<e^-PRKY<ZW-r{?pINq)LLG%g3ULa*
zi<}w+rAZDxnwRm6JK$Rxfvkv+voiL<eq`~N2ZZIvm!N=SyMP7Zg|!T?F`fI4Cc+98
zDMk$XCGIZLxCwWPR4R*V8~lLc-}7AvJaX2<@tww)KZ&!UYX!s=Bse^7<a?ndm@U&!
zi9$?oEa@p@-}KT@^th@TGPZ<bB;%_51{De$QP1PynHBFx`I+&#Nz!md1h0*%MO>9e
z1aC5c%R9V?3{14k)@1&8M9Stm6Hkuz^L0PYl^?~qmOX@ng#4}?msRO3rJJoNVqCNc
z##P7a-pinjV;`}fG{$@rJV;!rlI-R5E|hEjIuMg&r2YNIm{(YvoE2)NGWwAx2?tgO
zLB2@>i^;`C^*Sqkj#Kl3$p8qCGw{RuDqPDgD0qIy(MPKh1XLQtdr_FoOZ;nVJj!jE
z%#<Enbb_y&OHQwI+tH^f{LF~7!Z48U(~n!{k;gv&1JXwSm?tHUwk_~#Z>3s$hl9Z4
zd!4UuGVtCg<Gz$ffq}A6IX2+9x7q2ShQGgJ($gT#KU4n8Pi=4SkZ;}-UwO7>x8-8_
z3tzf;_>hZ(RVWmFM3B0E^%H+_@vb}XG?SB)zLu=K{%c>}bZjW@VLnw&-dgC4I)-id
z9cd<R1MyGZ=kJN<!sCcvyP^7*$KhvfR2D=E_v6^=q&SPwT_)&e(T|CU*wK!On^6^a
z3fzVwV)UjIh{DW2&Sy(V-E35}CbCfwUHpAG<#_Jj!UCB>p%@}4fz?H{YbGfOi4xQp
zW&4H>f@B2`hmJ~??Yw-C8F){C2UFVe>f*om5dtCA@=`(`UyNYtXKc?KKu%KAZ2m3G
zp$~W;gpqU>io)ca@f?0rN>SAq^9OUu>F@Jw&w=YXJ9tA1qH&N|=RFmBY{UElRv(wb
zOk#<gfr={9$Q0MZ4hrQ8$_iFA$t?Znkmkpx^>KnuXXQi(G3vcG!W^7&NhlI&yE{Ca
zN)NSn3;!=yK(;R}RhWD8*dk@n-xr@}r5+JN|Iiro-{rlAS7|-RTD2!MLIG947%ISY
zm{VYEqtknR{pCqxqx8XlU#Zsq7Sw7k<J9Zccg>mpY2hf&^%spX@4(MLLNGcnCvPAW
zUtg)#9zylVrycu~6_ZvR_lJ4w`$J@DsR@Pcl1PhWa60UPw9~1{iErQD_V4T^(=MAX
zg+ehZ;EJ+1on?`dn2+hP6eniOh_pmf%e)`Y@MT|0pHNz_2w%lnYjIU#(^8%{4YY|>
zG~TwMO>xefRnV=NI`;*LH}8u&_TsVR^4dmKzSQw^<iboo|6FOs;n|)S$n(vRejHzd
z7_(#TGTz@5`wUg&%q6FfeE#Xb{#8_BRwxuh0wu5p8|-~^G9u-tV`+nf2uUJ{RTUFO
zra1bkBuT*-o46kOMUV$umhV&<ua_}dF1XY@5(rM0*q|C@Bwfp^qRQCop+air8aw+0
z0R^XQ%89g1IBePd!{z;BO6}wNzb&D^IOU6fGl9eXN9v+i;-@4&JKKz07=0Vt=oam=
z)9+5h7{3%T_HH7`nn9`}9s6Inh&N$+o)!T$k5OK^c7g5<zh{j3&z7o>y`b#v$oJt>
zj`Qaf#IyJzCD#v%Qqwl>a8+8WRBOK#Qu*#JA$?_FR=y<8<9%33-^Fw4sAHQ~<G%Q_
z!bn}NrgB>AcMEdx!<B07UXju)(`OpKcY^q`2~3G~o4gX{@&*&O$3-e~5tEM$RDY!0
z`!AJh?KPxMv-W*gM1Lt1iV=oG%__<B6E6Qv+^_i=v+64rBZ2#qtYph+{Lbf_T_^^N
zO19$LK1MlCTVj9<v-o11l4F5}EAz4{dY@-v2inwA3{Zin0_-G~Y|9BoM69(Wiuvp#
z!SEbi;`xt7R%V29P8NJgA(pn|Zd+k^0DJZNGp%)jLZRpvl)xHn@J%;m6IiYI30PIZ
zy5dD39wbGPYBMWV%~~-sm4Q!Vk{gi*#TCppt^9fRtcs)HISH~trok<VoO*t(3<Bc4
zc>Nhk5MduKH4K-`m%d<WYqGxTBin?>4p$@cD$m3un#m3Oq5iN~MJ@RJuTX)fX;mS{
zYZi*K_d-#aF}DNe(>Rv}M*u=(#@q1sg0TWsPNl%=TS<@=pLd+Y7FPbs@Ey7*WE}ZO
zLnd{2EZPJhDZhTcQmy@;7<&b@@tZ-eIEhV%A6On~ikl%TsJFOJg!c(oqq9{+bie;z
zV~kJ1XF$r3Lg}N}pRE`oyqx-uebCJ9Ye*zgosESBUI<aDNyzWy!!fs^F}wznGYit_
zm#oxL`J8(%k8M;Dyn&V1OYj~ua*`9Vl3x@gG^;T86<9#uT;_Wa#^|?;w9}(#>#QIP
zGaZ`jDHMt!f~881D$3yU-^_~VdZEP_uNa|lvY%gVnbYGr*K8&1X3mQjXgcPNBUC_z
zP8rV&Kify57C&QRCB!156X54nt8sk_#FtA+*sw0ZLRwnPCCI|yZjOtsO6CiWs-6Xp
zeTG7z7-A@aHK4%6=n`(oHkrU_X3hEu<b~pZCh{4_q}l1^WPK(pq`$Ill02{<7^GtO
z5GIUy$8kBmZ`q;XVUyy^aZ)nTcLfprWrezew<iV0Rz!gK7EC1V)T{$$l<2pAE?&CK
zCg#4XZSe1*Ki>r3CR_T>^8CQ3O7<BZ=sa0YLlJWzh~S{8Qmx%%y)VlUpYx?TR;sm6
z7-Jp~FD-di%QEn{dd^lkSpkMY3feox|7@)73xrDcDxCB`v&{dANc^WrIXqLT)?Nu>
zkIASR(QltK#=MFMh8R_ABxHp^8*C!atB-k6C6UOZ9}!jl3cjGdQmxI0DSvQJ$U9PM
z`TO@c#xMC`eFGFuyze}!US@gK68DiAYjP5$q?1O%JjN4HpK+StIuugB7O^j5k@WWc
zXB{cD#~shDO)Fj7E_QGTRf_iJlGDFvy~pX0Qi*i}-?ylH%_iE+6Gor<>=gQy$IC0N
z_xT>ghP?k(pUn!zSfT*$$NOTW!MWZqSQV4^LBwuea?9gkl@kjok_<T~$re!<ry25W
z)9fz-llCaR*%Uw30%^o#PN!@z(%4@otBvylPN&lSI#u=335u(vS$Ye|gk(Z|hARcS
zil6Xet+ej|UXLt;Wf6z*qO-FSbcmOnLZKL7Oz6cKOu(F(9uBteQ{F1gz7IqMfiQ4E
z-l%8nhp#AI{{+m6eMo_zN?F_52E{hshRNLHjy7^&^eFZvLBR}}9A_*NgA^2XM_cWc
zOJ6{+0KX^~{uTU=WZ!2&47h?TJCHffpQDE?ofB5=_5s1)4MASXcJr|>I*7{mh`(pp
z7@Lkk1PM*R@!+R@{FH+jqPBd|bM6wm*8lVJ`XI2qi0k@0=pQ?vvHR=0tpC}+?SH`a
zT;`Q%;8}-hEsdW)=j(5M{;IWaKHp2$abJbszChdc`fq@YDu5`l3DAB0jCK)ON~?yy
zVT}1skp^)Q0Xu&-jdLe2rzai9Cc^b&AWZg=SDx=5kw$Nn<Q2ds^A|yq(f5kdLlf4@
zFDshZBW(qwIpar!$3-6Rp9CpHzaN-OPSZ8}3s3(_T;gktq5@yRd+T_QCN19xv;B|Z
z^$%dRnpa|-_dXb{f7M)aI%!>-bO`Ch*ClKc)=Z@*89e`%^`3NS&kMC-hy#{Fp%`{J
zQnd3>WX2&Md?{NQ+%FIK>fxUMC=|4@7H#khJCAMU;=q<;v5{5&n|Lngt$odM{D4?x
zNfbLVh{>Ep17hOkX4OfwSc5p4o^PEZhjMtnbu5bk&#6S4zPVY%5eIBC);6jgCu&+H
zuP_GAV=my@$Nw5<5eMU4UW0GM__>JKls}h+O55f4`hix?L7`Ce7rM$jd{|<0aVfoZ
zs9fwc9)ky3n3Px&M^wdQ)}x4F$+uhubFFM~8F-h<$(FyDM0LG=L4hks0GYu`ni-iU
z=a_Vs@jz$ANf`!^C8Q0p0_A&PCjAo;D|~Nqq;R^(J1*1Q@G+u?wWqXy3TEGLW!0BR
zJtNZKh^)wm2mHM_?g|1wmsirfW5Uel|7%EP;uN$6u0ANPJGJKzjWKT!n`eburik{+
zab=SNc^rfL=*#?uW850-YgwdKtz%m`7BeumH^OjVUVmP3oiw+{P!{BfR0kGg1dQAG
zJ`vIIcPrHzRWO|Me3naXXE2`L3_qiHdp>_rRJJSPT;Aj`I)8F5IsH13W>6M>OC=9U
z8)k%l9166<@U%+VEAL`5v=8XRyP-HtV_e!38TtMB!H{;Gl!`s{-8@X|50oQE8^77n
zCQehHfwxr-_sW!guN3<tvXBVuA93^*-_LTQS`~v<hW`|vRz{59-*%9*j1{!=ClN%;
z&;BF)&fq%o_|y`E3dM+GYHIR9TAZm=YZq9hZ587vhNy6j&llAbFQpvBP(EgHnKJIv
z#;X2%fJl;FSFS3~xSu|X=P9dx<#{YOxiHWdc)SmrDd^!vo1|Elug~Pf#J}0y+mr7t
z43OuSoG<)Cj?XQtkMkZIhZ9(U%L*%r$Fa|6gsR$(XA1aE{g{<4@y(Ny6VLAM?it0%
zVc_p2iq@e7){ucqC_7pm$6Pyy8$Smwu1Tky+>>&oXs5u5(zG&N&qa*!<oX++&XGix
zJu%&kQSMp7p0G;-BQI70nFuOblTgm5=VOPOkc$0gppbYQ&Yz7`Z9`}y0<#5CntETm
z(SN+Ec><|&`wY2xmC_jVAV2u$lGE3qy2^rx_=u;?mqG#}?t@K1j1XQ!#?ttkf`o6x
zY8JOsdV-ZfVjPfpi_a{htvQSdIaWhcV7cO;Opz2u{LTqJx5=~1|0)7!gX?)&lwf8u
ziz^k<xVeH=@q0jU+KMKLc__v{Clp>$pkw}xXp^f--yfLUqzk8XE?~?oIKB@=`u!DF
zBz<@llwOo>`E!+OEw1;*vKZUPv5LBi)DIWo9k%kj2{M(<(&>A2wTVIiOkVw%=)doQ
zs*$S-QNa5yW6Y-=B;69mlTEsEbwD=QPhm{`6g<Fqf_X!1^Jm7GU&rU<#FGdsy7zt`
zQaTOD&<A9D@Li{Kc?4rG)Oc2*Pz*J=*u+`SbCA9DT#BUCCha3!3mo&-qL>r72l8jq
zp6bCaY~q<ei<m)HxizAqqO<^L+fNGZ+YBRutcr#uEJifcW6cs#h%sZSyAW%oI^=WZ
z#qQqTZ-p$J^$bv0*<N9r!W(k}&tG12;(|*LBmUy&;3_;LUk$HR_I0IwJ-{R)d%3#C
z5@L-5^WKR>;x%@vfnp@k)ihc~f1w0ck0AV$(ruV)hM^FW4%2|;6=T0glj93w@--h4
zTrsj1c||@opR#<eK8lAnSJc^ZkwsABY{m&<!~-smc1luZQ??lajjfQHi4(k8$T{}I
zWFQ~!G|n&2q=>K2(I<8gleSk@@z5=~RBi~IeCKdE?Apm7%}T+`3PnnmE4yF}XM|q`
zdy}0(DkdAyQ+tU-_@HHg?;>Aa%Dr25yRGj_v5m=n4gLz>Bwk~JW#8&Dd=~OJ*L%<x
zL{dzn`d=9S2z~J^RBrc#eAcB<tl>Be?sHickLzUHeZgw2y5&b@gWh|sc<oBWIWaPh
zHXnBtn}`8Uci?k64f4&H+lnl@CjV|OIsHTC_E>!uq%e(PB6EL+JgyK7A90k#{!MJV
z4x&wtr@Z6d`DyF_1uK2#tBf(<VtwYHu)mCW?Tb)AQp)dlh}Wkv#`i5e2;a1<5?Bhw
zct9$6I4;4xAPW*GBdW4pLghc_<jkss%07#}iCoihXcOd4-Xh;}GPM!=X^JQkDTiUt
z8TJt~jtX`RUZ_2Rcx_fF<<B{)FtgXvY>1+_naOg?6KgD-lst<(CGp!;ZMY+b^KpT(
zdm28j3m}%tBD)~WT)iNkftOHpWftdgfGm{$w)`57|I>&K-jBujqT{%oN-b3=6g`3x
zSP>B9KrV){IR_yw(x7CS^}1x<v(|(@cFBJ((cUgG29?&bAcihPY+@hpQ?6=w2+S66
zeh1DA?>)xB1Sa1!LH!dD0eMF{k<)m*3AnC7!vHmApvIL_IM~ed>K_7)vWrB*wJusj
zz<LO|D7}&jVxph%kU(c#{scbK4%g=#9_&nBeMYti<652zS94=EURs<JDb!v-I@d3R
zjLT&QdGK8@@1%Xb-x%|D%hc7L*nh<y2MBbzOm@kVrhk*QebHg^xo>=eMBIeZjn3&;
zk$Prhi&vVt%^+N!@SHz`$H6KlDqPx}Nt2@p#-y?*>E2av-3-RZ5h1C!QO0T}<KI`R
zwLdq;d^@hofnp0ln4)i)$$GKva!Bejo!>85uT=$c#h^5UU&lR`uG5Aw<`~Ap*W)vf
zfz-3fD~Ty6`dIPA_oQb*GBOGF-IZ$Xec~SQv(|nj@%LqXXR;1*@8uzBKw*Z$wqjJF
zP>d#Szz*Rfm1>O&Zy1m-XW)y(>ZLdZZ1UFqhSaB<h!4++iTf5}+Ax(;qxeZPidX?#
zIW{kg;uxz!a*T81^uug~syEJkQ&#BX=hP<1D_1`+h&aV*kRKP8kL!Z(8i>ltc9EG=
zJ_2n2++&)2uUc(pr%=X1Dxaf_A&Zt8UiM4IiVHKs(!|^M()I-Vkc&>_{dokQS9XQw
zuwqUMg<=4q1Xc(@GVo1qA!4<NKkKUm7v~1KWCb6q%~Vb4l&Z@nhD4P)V^?d#N=gdt
z-m(=o*mf3#P&v%E0hM<os6StiiF&HZ6hpahB4_Le)@6^$DF<C-Cck2laju|Y#ri*w
zpwV!O5XMnPsPC3V+EfN*f!BPiicF%S>dTU7<GiRK$9`ZtP(HcHt2`d!D@YBD8ZVEA
z5vWx0&w43mjq^J|^32&@O2P=9#>ama9vehvJS_;HQ}}#!E;;>^7;Cnw=uaGKvLAx-
ziSP)>FyjcSF9{QH=}UA9gq18Dz~{@P;Z4W)HRJMas#I$~WBD#fJO^Uz6~foSK#9Wv
z*l&8ab%D2E661pLIF7}>S-*noWDxjs@P7*GsPBY_)nR-OuZz|Fe+_>x2KCu58)JSP
z{rQMc-PvP9g2=$P<Jd&LeVwH~GdTXDxPHgPd#{4J>Ayv!Qr|!yvmf2Vq96r>3!6xD
z_#*B@F5Cop^((ly*`8r8FG9w1^nLbB4s-Pv`y|ZAOrcN=De8OmbyjNA;sL70N*t*8
z8w2ljd?bhXi~T;Gi--<v^1adH-Psn{|5Dg6M4@r~FyaT#;V2R-C$az<X=^bRVe_cq
z?QFB9fGeSDwY^}=<kY{dRBO)&Usn5?xJ<`SHldu8mA1QHZO=~MbSyGUWAzd~&kO&{
zuFnB8F%{mtfVOO<_cMMNG3h$&04QE2zp(S$2kI3mh7k7mS5pW_7)oG8;}J~sq9mpz
z>$m-$e2|1y#gjO0Kd8BCnTMd@6vc%WSIm6iG(nrJF~+UTk*3NVNGy3wJ>`B}?>)~-
zlZc=t3K_I9?NUW~OioW&ilu^B(fK?P5{~P@B-s`o&^fCr;%N@paE(H*lO$0+lIs(=
zJ`Z4Ag$1T5v5m|4+a7<<gtg59-?(nP?oiecQScCgS^9aOhqV32P~a3CDRJy8Bv+ti
zg~jvu`Bkj2J}T1BWdA>B?RN$wQsC_bb)peuYz~AXx0lF<_f)F24<gM$UI$i7?IJ~z
zNuw>Kv^gt^8e`rSLSEH91PS-ID`0&cNTM>v=Ub2h>cR{v8NS+j@8cjDzhV(sgl`jn
zvscQdNI!iq2&c;&jDo!Wevm+~c92&Lm*S1_&2!1=->+0_w><B!;`&H68mj^9@6=_>
zpQH$a%^r8}g5mxT;yd^)xWD`hW6VqOnU6UXf4t8vA1P0L_NmX--jhfq=>D-&uh*ZO
zoSb-#shb;#i9|B!_qy5K+1*{mCQ}m=6ZcL`Bwl9fW_NdQZ;iH@m`L0^k(hXyNrXPX
zZtd*s29-M}CMLdSVj?jwV}sq@y+5tj>o*dK#L+||k*wG2)yc_;*V)&pUa$Y<?(W``
zlM@pQi5K@FvF-4zdv0eZXn#;HeR6W*)d`cB+N;<9dLof{sY#fZgtU1d>h=0FySsbd
zYM0dZiHXF^6N$tz-)HLe`d{zv?tOA{a$+%I5_j#@_rAEdSFZ+rI5F{Rq0Vz`Y&LiI
z_WojWa^j8lTYG!;&+YB)J?-h21AE%t-TMSxJG6Uta&jUdPj_~9|9EO@@(n1UAaP@7
zXZO!|GUfvv-z3b@-QB%?>9JFjlgAt5qi!~X{+~#^3j6(2{v7o~B9XYeUN<-Bd>sAz
zDtaG^=>~Rp_g3rm`lfw6dOeW{#;uziw#N6=)Z}qKcN%MZd-V-`FTOUBNW8pWuYZ}&
zEoe(3aj&nhc|2QvkTQw=1iRgCbNl<z@HQk8YIkq1eEWU+ZYGYRf2O7;CtuHfySuyh
zXMA1FiylQKzRy&uwf{SAT9a55Ty{J+?6F}S_6nSip2Bm0@6Rbo%Ha9%NR*#c2<%0}
zRY&0-@<DnZp0QDWm__(MmLtfwH15X-D)Cw88LJ8-V8?lx5GBxpIPVjl^WtZ-R10&g
z!QM_z)m@Xe7e!xH$4)^k(Eb&kbtRi%t5j=8@tJ3^;CRE_j`>|Na6!bVH=wNkw6#A{
zTRkIQd%JZ_?zQGDM-jJt694}ekL%Ap{nw;C|4=vkNzr{Q9!nP2HmYa1V8P<CWXlyj
z6@!mNz3%m1Rwxt-g+ifFC=?3CkYR13%4)FU;<2PEQXE*@sLq0PV-oveB0~nMkqU)k
zAC$mSC=?2XLZMJ76bi-I0VGxm%AHOZ*-|JJ9flHE3WY+UP$(1%g+ifFC=`mpg)!#;
Y2Nof~%K!voHUIzs07*qoM6N<$f;YTGq5uE@

literal 0
HcmV?d00001

diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/index.tsx
index ca8c22be9c34c..777c2353226c4 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/index.tsx
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/index.tsx
@@ -5,9 +5,74 @@
  */
 
 import React from 'react';
-import { useConfig } from '../../hooks';
+import styled from 'styled-components';
+import { EuiFlexGroup, EuiFlexItem, EuiText, EuiImage } from '@elastic/eui';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { PLUGIN_ID } from '../../constants';
+import { WithHeaderLayout } from '../../layouts';
+import { useConfig, useCore } from '../../hooks';
+
+const ImageWrapper = styled.div`
+  margin-bottom: -62px;
+`;
 
 export const EPMApp: React.FunctionComponent = () => {
   const { epm } = useConfig();
-  return epm.enabled ? <div>hello world - epm app</div> : null;
+  const { http } = useCore();
+
+  if (!epm.enabled) {
+    return null;
+  }
+
+  return (
+    <WithHeaderLayout
+      leftColumn={
+        <EuiFlexGroup direction="column" gutterSize="m" justifyContent="center">
+          <EuiFlexItem grow={false}>
+            <EuiText>
+              <h1>
+                <FormattedMessage
+                  id="xpack.ingestManager.epm.pageTitle"
+                  defaultMessage="Elastic Package Manager"
+                />
+              </h1>
+            </EuiText>
+          </EuiFlexItem>
+          <EuiFlexItem grow={false}>
+            <EuiText color="subdued">
+              <p>
+                <FormattedMessage
+                  id="xpack.ingestManager.epm.pageSubtitle"
+                  defaultMessage="Browse packages for popular apps and services."
+                />
+              </p>
+            </EuiText>
+          </EuiFlexItem>
+        </EuiFlexGroup>
+      }
+      rightColumn={
+        <ImageWrapper>
+          <EuiImage
+            alt="Illustration of computer"
+            url={http.basePath.prepend(
+              `/plugins/${PLUGIN_ID}/applications/ingest_manager/sections/epm/assets/illustration_kibana_getting_started@2x.png`
+            )}
+          />
+        </ImageWrapper>
+      }
+      tabs={[
+        {
+          id: 'all_packages',
+          name: 'All packages',
+          isSelected: true,
+        },
+        {
+          id: 'installed_packages',
+          name: 'Installed packages',
+        },
+      ]}
+    >
+      hello world - fleet app
+    </WithHeaderLayout>
+  );
 };
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/index.tsx
index 978414769004d..c4e8c576a1d7d 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/index.tsx
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/index.tsx
@@ -4,9 +4,53 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 import React from 'react';
+import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { WithHeaderLayout } from '../../layouts';
 import { useConfig } from '../../hooks';
 
 export const FleetApp: React.FunctionComponent = () => {
   const { fleet } = useConfig();
-  return fleet.enabled ? <div>hello world - fleet app</div> : null;
+  if (!fleet.enabled) {
+    return null;
+  }
+
+  return (
+    <WithHeaderLayout
+      leftColumn={
+        <EuiFlexGroup direction="column" gutterSize="m">
+          <EuiFlexItem>
+            <EuiText>
+              <h1>
+                <FormattedMessage id="xpack.ingestManager.fleet.pageTitle" defaultMessage="Fleet" />
+              </h1>
+            </EuiText>
+          </EuiFlexItem>
+          <EuiFlexItem>
+            <EuiText color="subdued">
+              <p>
+                <FormattedMessage
+                  id="xpack.ingestManager.fleet.pageSubtitle"
+                  defaultMessage="Manage and deploy configuration updates to a group of agents of any size."
+                />
+              </p>
+            </EuiText>
+          </EuiFlexItem>
+        </EuiFlexGroup>
+      }
+      tabs={[
+        {
+          id: 'agents',
+          name: 'Agents',
+          isSelected: true,
+        },
+        {
+          id: 'enrollment_keys',
+          name: 'Enrollment keys',
+        },
+      ]}
+    >
+      hello world - fleet app
+    </WithHeaderLayout>
+  );
 };
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx
index da4a78a39e2fe..ea6b045f504ec 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx
@@ -4,7 +4,37 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 import React from 'react';
+import { EuiText, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { WithHeaderLayout } from '../../layouts';
 
 export const IngestManagerOverview: React.FunctionComponent = () => {
-  return <div>Ingest manager overview page</div>;
+  return (
+    <WithHeaderLayout
+      leftColumn={
+        <EuiFlexGroup direction="column" gutterSize="m">
+          <EuiFlexItem>
+            <EuiText>
+              <h1>
+                <FormattedMessage
+                  id="xpack.ingestManager.overviewPageTitle"
+                  defaultMessage="Ingest Manager"
+                />
+              </h1>
+            </EuiText>
+          </EuiFlexItem>
+          <EuiFlexItem>
+            <EuiText color="subdued">
+              <p>
+                <FormattedMessage
+                  id="xpack.ingestManager.overviewPageSubtitle"
+                  defaultMessage="Lorem ipsum some description about ingest manager."
+                />
+              </p>
+            </EuiText>
+          </EuiFlexItem>
+        </EuiFlexGroup>
+      }
+    />
+  );
 };

From 63d5e382d888728f6a96eede327c61eaae271989 Mon Sep 17 00:00:00 2001
From: Peter Pisljar <peter.pisljar@gmail.com>
Date: Mon, 24 Feb 2020 13:40:33 -0500
Subject: [PATCH 148/174] removes extraHandlers (#58336)

---
 .../public/embeddable/visualize_embeddable.ts               | 4 +---
 src/plugins/expressions/public/loader.ts                    | 6 +++---
 src/plugins/expressions/public/render.ts                    | 4 ++--
 src/plugins/expressions/public/types/index.ts               | 2 +-
 4 files changed, 7 insertions(+), 9 deletions(-)

diff --git a/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts
index 72a0ef72b5693..32bbae13b79b8 100644
--- a/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts
+++ b/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts
@@ -369,9 +369,7 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut
         query: this.input.query,
         filters: this.input.filters,
       },
-      extraHandlers: {
-        uiState: this.uiState,
-      },
+      uiState: this.uiState,
     };
     this.expression = await buildPipeline(this.vis, {
       searchSource: this.savedVisualization.searchSource,
diff --git a/src/plugins/expressions/public/loader.ts b/src/plugins/expressions/public/loader.ts
index 4600922e076fa..fbe2f37c648d6 100644
--- a/src/plugins/expressions/public/loader.ts
+++ b/src/plugins/expressions/public/loader.ts
@@ -167,7 +167,7 @@ export class ExpressionLoader {
   };
 
   private render(data: Data): void {
-    this.renderHandler.render(data, this.params.extraHandlers);
+    this.renderHandler.render(data, this.params.uiState);
   }
 
   private setParams(params?: IExpressionLoaderParams) {
@@ -182,8 +182,8 @@ export class ExpressionLoader {
         this.params.searchContext || {}
       ) as any;
     }
-    if (params.extraHandlers && this.params) {
-      this.params.extraHandlers = params.extraHandlers;
+    if (params.uiState && this.params) {
+      this.params.uiState = params.uiState;
     }
     if (params.variables && this.params) {
       this.params.variables = params.variables;
diff --git a/src/plugins/expressions/public/render.ts b/src/plugins/expressions/public/render.ts
index 86e360f8135e7..ad4d16bcd1323 100644
--- a/src/plugins/expressions/public/render.ts
+++ b/src/plugins/expressions/public/render.ts
@@ -94,7 +94,7 @@ export class ExpressionRenderHandler {
     };
   }
 
-  render = async (data: any, extraHandlers: IExpressionRendererExtraHandlers = {}) => {
+  render = async (data: any, uiState: any = {}) => {
     if (!data || typeof data !== 'object') {
       return this.handleRenderError(new Error('invalid data provided to the expression renderer'));
     }
@@ -119,7 +119,7 @@ export class ExpressionRenderHandler {
         .get(data.as)!
         .render(this.element, data.value, {
           ...this.handlers,
-          ...extraHandlers,
+          uiState,
         } as any);
     } catch (e) {
       return this.handleRenderError(e);
diff --git a/src/plugins/expressions/public/types/index.ts b/src/plugins/expressions/public/types/index.ts
index c77698d3661c2..b5781ef213fd0 100644
--- a/src/plugins/expressions/public/types/index.ts
+++ b/src/plugins/expressions/public/types/index.ts
@@ -48,7 +48,7 @@ export interface IExpressionLoaderParams {
   disableCaching?: boolean;
   customFunctions?: [];
   customRenderers?: [];
-  extraHandlers?: Record<string, any>;
+  uiState?: unknown;
   inspectorAdapters?: Adapters;
   onRenderError?: RenderErrorHandlerFnType;
 }

From d1df0e5da5a642b889bbe626a5ebaad8bf973f9a Mon Sep 17 00:00:00 2001
From: Ahmad Bamieh <ahmadbamieh@gmail.com>
Date: Mon, 24 Feb 2020 20:47:32 +0200
Subject: [PATCH 149/174] [Telemetry] Server backpressure mechanism (#57556)

* finish backpressure mechanism

* usage fetch method send OPTIONS before sending POST

* add unit test for get_telemetry_failure_details

* get currentVersion in constructor

* fix type check

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../core_plugins/telemetry/mappings.json      |  7 ++
 .../core_plugins/telemetry/server/fetcher.ts  | 68 ++++++++++---
 .../get_telemetry_failure_details.test.ts     | 96 +++++++++++++++++++
 .../get_telemetry_failure_details.ts          | 45 +++++++++
 .../server/telemetry_config/index.ts          |  4 +
 .../server/telemetry_repository/index.ts      |  2 +
 src/plugins/telemetry/public/plugin.ts        | 12 +--
 7 files changed, 215 insertions(+), 19 deletions(-)
 create mode 100644 src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_failure_details.test.ts
 create mode 100644 src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_failure_details.ts

diff --git a/src/legacy/core_plugins/telemetry/mappings.json b/src/legacy/core_plugins/telemetry/mappings.json
index a88372a5578e8..fa9cc93d6363a 100644
--- a/src/legacy/core_plugins/telemetry/mappings.json
+++ b/src/legacy/core_plugins/telemetry/mappings.json
@@ -17,6 +17,13 @@
       },
       "userHasSeenNotice": {
         "type": "boolean"
+      },
+      "reportFailureCount": {
+        "type": "integer"
+      },
+      "reportFailureVersion": {
+        "ignore_above": 256,
+        "type": "keyword"
       }
     }
   }
diff --git a/src/legacy/core_plugins/telemetry/server/fetcher.ts b/src/legacy/core_plugins/telemetry/server/fetcher.ts
index 6e16328c4abd8..d30ee10066813 100644
--- a/src/legacy/core_plugins/telemetry/server/fetcher.ts
+++ b/src/legacy/core_plugins/telemetry/server/fetcher.ts
@@ -21,19 +21,26 @@ import moment from 'moment';
 // @ts-ignore
 import fetch from 'node-fetch';
 import { telemetryCollectionManager } from './collection_manager';
-import { getTelemetryOptIn, getTelemetrySendUsageFrom } from './telemetry_config';
+import {
+  getTelemetryOptIn,
+  getTelemetrySendUsageFrom,
+  getTelemetryFailureDetails,
+} from './telemetry_config';
 import { getTelemetrySavedObject, updateTelemetrySavedObject } from './telemetry_repository';
 import { REPORT_INTERVAL_MS } from '../common/constants';
 
 export class FetcherTask {
-  private readonly checkDurationMs = 60 * 1000 * 5;
+  private readonly initialCheckDelayMs = 60 * 1000 * 5;
+  private readonly checkIntervalMs = 60 * 1000 * 60 * 12;
   private intervalId?: NodeJS.Timeout;
   private lastReported?: number;
+  private currentVersion: string;
   private isSending = false;
   private server: any;
 
   constructor(server: any) {
     this.server = server;
+    this.currentVersion = this.server.config().get('pkg.version');
   }
 
   private getInternalRepository = () => {
@@ -52,6 +59,9 @@ export class FetcherTask {
     const allowChangingOptInStatus = config.get('telemetry.allowChangingOptInStatus');
     const configTelemetryOptIn = config.get('telemetry.optIn');
     const telemetryUrl = config.get('telemetry.url') as string;
+    const { failureCount, failureVersion } = await getTelemetryFailureDetails({
+      telemetrySavedObject,
+    });
 
     return {
       telemetryOptIn: getTelemetryOptIn({
@@ -65,6 +75,8 @@ export class FetcherTask {
         configTelemetrySendUsageFrom,
       }),
       telemetryUrl,
+      failureCount,
+      failureVersion,
     };
   };
 
@@ -72,11 +84,31 @@ export class FetcherTask {
     const internalRepository = this.getInternalRepository();
     this.lastReported = Date.now();
     updateTelemetrySavedObject(internalRepository, {
+      reportFailureCount: 0,
       lastReported: this.lastReported,
     });
   };
 
-  private shouldSendReport = ({ telemetryOptIn, telemetrySendUsageFrom }: any) => {
+  private updateReportFailure = async ({ failureCount }: { failureCount: number }) => {
+    const internalRepository = this.getInternalRepository();
+
+    updateTelemetrySavedObject(internalRepository, {
+      reportFailureCount: failureCount + 1,
+      reportFailureVersion: this.currentVersion,
+    });
+  };
+
+  private shouldSendReport = ({
+    telemetryOptIn,
+    telemetrySendUsageFrom,
+    reportFailureCount,
+    currentVersion,
+    reportFailureVersion,
+  }: any) => {
+    if (reportFailureCount > 2 && reportFailureVersion === currentVersion) {
+      return false;
+    }
+
     if (telemetryOptIn && telemetrySendUsageFrom === 'server') {
       if (!this.lastReported || Date.now() - this.lastReported > REPORT_INTERVAL_MS) {
         return true;
@@ -98,6 +130,14 @@ export class FetcherTask {
 
   private sendTelemetry = async (url: string, cluster: any): Promise<void> => {
     this.server.log(['debug', 'telemetry', 'fetcher'], `Sending usage stats.`);
+    /**
+     * send OPTIONS before sending usage data.
+     * OPTIONS is less intrusive as it does not contain any payload and is used here to check if the endpoint is reachable.
+     */
+    await fetch(url, {
+      method: 'options',
+    });
+
     await fetch(url, {
       method: 'post',
       body: cluster,
@@ -108,21 +148,23 @@ export class FetcherTask {
     if (this.isSending) {
       return;
     }
-    try {
-      const telemetryConfig = await this.getCurrentConfigs();
-      if (!this.shouldSendReport(telemetryConfig)) {
-        return;
-      }
+    const telemetryConfig = await this.getCurrentConfigs();
+    if (!this.shouldSendReport(telemetryConfig)) {
+      return;
+    }
 
-      // mark that we are working so future requests are ignored until we're done
+    try {
       this.isSending = true;
       const clusters = await this.fetchTelemetry();
+      const { telemetryUrl } = telemetryConfig;
       for (const cluster of clusters) {
-        await this.sendTelemetry(telemetryConfig.telemetryUrl, cluster);
+        await this.sendTelemetry(telemetryUrl, cluster);
       }
 
       await this.updateLastReported();
     } catch (err) {
+      await this.updateReportFailure(telemetryConfig);
+
       this.server.log(
         ['warning', 'telemetry', 'fetcher'],
         `Error sending telemetry usage data: ${err}`
@@ -132,8 +174,12 @@ export class FetcherTask {
   };
 
   public start = () => {
-    this.intervalId = setInterval(() => this.sendIfDue(), this.checkDurationMs);
+    setTimeout(() => {
+      this.sendIfDue();
+      this.intervalId = setInterval(() => this.sendIfDue(), this.checkIntervalMs);
+    }, this.initialCheckDelayMs);
   };
+
   public stop = () => {
     if (this.intervalId) {
       clearInterval(this.intervalId);
diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_failure_details.test.ts b/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_failure_details.test.ts
new file mode 100644
index 0000000000000..c92696838e8e8
--- /dev/null
+++ b/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_failure_details.test.ts
@@ -0,0 +1,96 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { getTelemetryFailureDetails } from './get_telemetry_failure_details';
+
+describe('getTelemetryFailureDetails: get details about server usage fetcher failures', () => {
+  it('returns `failureCount: 0` and `failureVersion: undefined` when telemetry does not have any custom configs in saved Object', () => {
+    const telemetrySavedObject = null;
+    const failureDetails = getTelemetryFailureDetails({ telemetrySavedObject });
+    expect(failureDetails).toStrictEqual({
+      failureVersion: undefined,
+      failureCount: 0,
+    });
+  });
+
+  it('returns telemetryFailureCount and reportFailureVersion from telemetry saved Object', () => {
+    const telemetrySavedObject = {
+      reportFailureCount: 12,
+      reportFailureVersion: '8.0.0',
+    };
+    const failureDetails = getTelemetryFailureDetails({ telemetrySavedObject });
+    expect(failureDetails).toStrictEqual({
+      failureVersion: '8.0.0',
+      failureCount: 12,
+    });
+  });
+
+  it('returns `failureCount: 0` on malformed reportFailureCount telemetry saved Object', () => {
+    const failureVersion = '8.0.0';
+    expect(
+      getTelemetryFailureDetails({
+        telemetrySavedObject: {
+          reportFailureCount: null,
+          reportFailureVersion: failureVersion,
+        } as any,
+      })
+    ).toStrictEqual({ failureVersion, failureCount: 0 });
+    expect(
+      getTelemetryFailureDetails({
+        telemetrySavedObject: {
+          reportFailureCount: undefined,
+          reportFailureVersion: failureVersion,
+        },
+      })
+    ).toStrictEqual({ failureVersion, failureCount: 0 });
+    expect(
+      getTelemetryFailureDetails({
+        telemetrySavedObject: {
+          reportFailureCount: 'not_a_number',
+          reportFailureVersion: failureVersion,
+        } as any,
+      })
+    ).toStrictEqual({ failureVersion, failureCount: 0 });
+  });
+
+  it('returns `failureVersion: undefined` on malformed reportFailureCount telemetry saved Object', () => {
+    const failureCount = 0;
+    expect(
+      getTelemetryFailureDetails({
+        telemetrySavedObject: {
+          reportFailureVersion: null,
+          reportFailureCount: failureCount,
+        } as any,
+      })
+    ).toStrictEqual({ failureCount, failureVersion: undefined });
+    expect(
+      getTelemetryFailureDetails({
+        telemetrySavedObject: { reportFailureVersion: undefined, reportFailureCount: failureCount },
+      })
+    ).toStrictEqual({ failureCount, failureVersion: undefined });
+    expect(
+      getTelemetryFailureDetails({
+        telemetrySavedObject: {
+          reportFailureVersion: 123,
+          reportFailureCount: failureCount,
+        } as any,
+      })
+    ).toStrictEqual({ failureCount, failureVersion: undefined });
+  });
+});
diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_failure_details.ts b/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_failure_details.ts
new file mode 100644
index 0000000000000..2952fa96a5cf3
--- /dev/null
+++ b/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_failure_details.ts
@@ -0,0 +1,45 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { TelemetrySavedObject } from '../telemetry_repository/get_telemetry_saved_object';
+
+interface GetTelemetryFailureDetailsConfig {
+  telemetrySavedObject: TelemetrySavedObject;
+}
+
+export interface TelemetryFailureDetails {
+  failureCount: number;
+  failureVersion?: string;
+}
+
+export function getTelemetryFailureDetails({
+  telemetrySavedObject,
+}: GetTelemetryFailureDetailsConfig): TelemetryFailureDetails {
+  if (!telemetrySavedObject) {
+    return {
+      failureVersion: undefined,
+      failureCount: 0,
+    };
+  }
+  const { reportFailureCount, reportFailureVersion } = telemetrySavedObject;
+
+  return {
+    failureCount: typeof reportFailureCount === 'number' ? reportFailureCount : 0,
+    failureVersion: typeof reportFailureVersion === 'string' ? reportFailureVersion : undefined,
+  };
+}
diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_config/index.ts b/src/legacy/core_plugins/telemetry/server/telemetry_config/index.ts
index ab30dac1c3666..bf9855ce7538e 100644
--- a/src/legacy/core_plugins/telemetry/server/telemetry_config/index.ts
+++ b/src/legacy/core_plugins/telemetry/server/telemetry_config/index.ts
@@ -21,3 +21,7 @@ export { replaceTelemetryInjectedVars } from './replace_injected_vars';
 export { getTelemetryOptIn } from './get_telemetry_opt_in';
 export { getTelemetrySendUsageFrom } from './get_telemetry_send_usage_from';
 export { getTelemetryAllowChangingOptInStatus } from './get_telemetry_allow_changing_opt_in_status';
+export {
+  getTelemetryFailureDetails,
+  TelemetryFailureDetails,
+} from './get_telemetry_failure_details';
diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_repository/index.ts b/src/legacy/core_plugins/telemetry/server/telemetry_repository/index.ts
index b9ba2ce5573c3..f1735d1bb2866 100644
--- a/src/legacy/core_plugins/telemetry/server/telemetry_repository/index.ts
+++ b/src/legacy/core_plugins/telemetry/server/telemetry_repository/index.ts
@@ -27,4 +27,6 @@ export interface TelemetrySavedObjectAttributes {
   lastReported?: number;
   telemetryAllowChangingOptInStatus?: boolean;
   userHasSeenNotice?: boolean;
+  reportFailureCount?: number;
+  reportFailureVersion?: string;
 }
diff --git a/src/plugins/telemetry/public/plugin.ts b/src/plugins/telemetry/public/plugin.ts
index 7ba51cacd1949..9cfb4ca1ec395 100644
--- a/src/plugins/telemetry/public/plugin.ts
+++ b/src/plugins/telemetry/public/plugin.ts
@@ -54,9 +54,6 @@ export class TelemetryPlugin implements Plugin<TelemetryPluginSetup, TelemetryPl
     }
 
     const telemetryBanner = injectedMetadata.getInjectedVar('telemetryBanner') as boolean;
-    const sendUsageFrom = injectedMetadata.getInjectedVar('telemetrySendUsageFrom') as
-      | 'browser'
-      | 'server';
 
     this.telemetryNotifications = new TelemetryNotifications({
       overlays,
@@ -69,7 +66,7 @@ export class TelemetryPlugin implements Plugin<TelemetryPluginSetup, TelemetryPl
         return;
       }
 
-      this.maybeStartTelemetryPoller({ sendUsageFrom });
+      this.maybeStartTelemetryPoller();
       if (telemetryBanner) {
         this.maybeShowOptedInNotificationBanner();
         this.maybeShowOptInBanner();
@@ -87,13 +84,12 @@ export class TelemetryPlugin implements Plugin<TelemetryPluginSetup, TelemetryPl
     return anonymousPaths.isAnonymous(window.location.pathname);
   }
 
-  private maybeStartTelemetryPoller({ sendUsageFrom }: { sendUsageFrom: string }) {
+  private maybeStartTelemetryPoller() {
     if (!this.telemetrySender) {
       return;
     }
-    if (sendUsageFrom === 'browser') {
-      this.telemetrySender.startChecking();
-    }
+
+    this.telemetrySender.startChecking();
   }
 
   private maybeShowOptedInNotificationBanner() {

From 7bec52a964f4cf0f9ff5a5dd95b0a5b349c98016 Mon Sep 17 00:00:00 2001
From: Nathan L Smith <nathan.smith@elastic.co>
Date: Mon, 24 Feb 2020 13:24:46 -0600
Subject: [PATCH 150/174] Show blank lines instead of N/A on service map
 popovers (#57014)

* Show blank lines instead of N/A on service map popovers

Because RUM agents never show CPU or memory usage, we don't want to always show the metric with N/A. If a metric is null, just hide the lines.

Break up the display and fetching components and update the popover stories to show the list.

Update some types.

* Fix metric typings
---
 .../app/ServiceMap/Popover/Contents.tsx       |  4 +-
 .../ServiceMap/Popover/Popover.stories.tsx    | 86 +++++++++--------
 .../Popover/ServiceMetricFetcher.tsx          | 41 +++++++++
 .../ServiceMap/Popover/ServiceMetricList.tsx  | 92 ++++++-------------
 x-pack/plugins/apm/common/service_map.ts      |  9 ++
 .../get_service_map_service_node_info.ts      | 31 ++++---
 6 files changed, 147 insertions(+), 116 deletions(-)
 create mode 100644 x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricFetcher.tsx

diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx
index 378ad9509c217..f1c53673c8755 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx
@@ -14,7 +14,7 @@ import cytoscape from 'cytoscape';
 import React from 'react';
 import { Buttons } from './Buttons';
 import { Info } from './Info';
-import { ServiceMetricList } from './ServiceMetricList';
+import { ServiceMetricFetcher } from './ServiceMetricFetcher';
 
 const popoverMinWidth = 280;
 
@@ -49,7 +49,7 @@ export function Contents({
       </EuiFlexItem>
       <EuiFlexItem>
         {isService ? (
-          <ServiceMetricList serviceName={selectedNodeServiceName} />
+          <ServiceMetricFetcher serviceName={selectedNodeServiceName} />
         ) : (
           <Info {...selectedNodeData} />
         )}
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Popover.stories.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Popover.stories.tsx
index b26488c5ef7de..e5962afd76eb8 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Popover.stories.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Popover.stories.tsx
@@ -6,44 +6,50 @@
 
 import { storiesOf } from '@storybook/react';
 import React from 'react';
-import {
-  ApmPluginContext,
-  ApmPluginContextValue
-} from '../../../../context/ApmPluginContext';
-import { Contents } from './Contents';
+import { ServiceMetricList } from './ServiceMetricList';
 
-const selectedNodeData = {
-  id: 'opbeans-node',
-  label: 'opbeans-node',
-  href:
-    '#/services/opbeans-node/service-map?rangeFrom=now-24h&rangeTo=now&refreshPaused=true&refreshInterval=0',
-  agentName: 'nodejs',
-  type: 'service'
-};
-
-storiesOf('app/ServiceMap/Popover/Contents', module).add(
-  'example',
-  () => {
-    return (
-      <ApmPluginContext.Provider
-        value={
-          ({ core: { notifications: {} } } as unknown) as ApmPluginContextValue
-        }
-      >
-        <Contents
-          selectedNodeData={selectedNodeData}
-          isService={true}
-          label="opbeans-node"
-          onFocusClick={() => {}}
-          selectedNodeServiceName="opbeans-node"
-        />
-      </ApmPluginContext.Provider>
-    );
-  },
-  {
-    info: {
-      propTablesExclude: [ApmPluginContext.Provider],
-      source: false
-    }
-  }
-);
+storiesOf('app/ServiceMap/Popover/ServiceMetricList', module)
+  .add('example', () => (
+    <ServiceMetricList
+      avgErrorsPerMinute={15.738888706725826}
+      avgTransactionDuration={61634.38905590272}
+      avgRequestsPerMinute={164.47222031860858}
+      avgCpuUsage={0.32809666568309237}
+      avgMemoryUsage={0.5504868173242986}
+      numInstances={2}
+      isLoading={false}
+    />
+  ))
+  .add('loading', () => (
+    <ServiceMetricList
+      avgErrorsPerMinute={null}
+      avgTransactionDuration={null}
+      avgRequestsPerMinute={null}
+      avgCpuUsage={null}
+      avgMemoryUsage={null}
+      numInstances={1}
+      isLoading={true}
+    />
+  ))
+  .add('some null values', () => (
+    <ServiceMetricList
+      avgErrorsPerMinute={7.615972134074397}
+      avgTransactionDuration={238792.54809512055}
+      avgRequestsPerMinute={8.439583235652972}
+      avgCpuUsage={null}
+      avgMemoryUsage={null}
+      numInstances={1}
+      isLoading={false}
+    />
+  ))
+  .add('all null values', () => (
+    <ServiceMetricList
+      avgErrorsPerMinute={null}
+      avgTransactionDuration={null}
+      avgRequestsPerMinute={null}
+      avgCpuUsage={null}
+      avgMemoryUsage={null}
+      numInstances={1}
+      isLoading={false}
+    />
+  ));
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricFetcher.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricFetcher.tsx
new file mode 100644
index 0000000000000..b0a5e892b5a7e
--- /dev/null
+++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricFetcher.tsx
@@ -0,0 +1,41 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { ServiceNodeMetrics } from '../../../../../../../../plugins/apm/common/service_map';
+import { useFetcher } from '../../../../hooks/useFetcher';
+import { useUrlParams } from '../../../../hooks/useUrlParams';
+import { ServiceMetricList } from './ServiceMetricList';
+
+interface ServiceMetricFetcherProps {
+  serviceName: string;
+}
+
+export function ServiceMetricFetcher({
+  serviceName
+}: ServiceMetricFetcherProps) {
+  const {
+    urlParams: { start, end, environment }
+  } = useUrlParams();
+
+  const { data = {} as ServiceNodeMetrics, status } = useFetcher(
+    callApmApi => {
+      if (serviceName && start && end) {
+        return callApmApi({
+          pathname: '/api/apm/service-map/service/{serviceName}',
+          params: { path: { serviceName }, query: { start, end, environment } }
+        });
+      }
+    },
+    [serviceName, start, end, environment],
+    {
+      preservePreviousData: false
+    }
+  );
+  const isLoading = status === 'loading';
+
+  return <ServiceMetricList {...data} isLoading={isLoading} />;
+}
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricList.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricList.tsx
index e91eb5e006d82..50ce918ea7037 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricList.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricList.tsx
@@ -5,26 +5,23 @@
  */
 
 import {
+  EuiBadge,
   EuiFlexGroup,
-  EuiLoadingSpinner,
   EuiFlexItem,
-  EuiBadge
+  EuiLoadingSpinner
 } from '@elastic/eui';
 import lightTheme from '@elastic/eui/dist/eui_theme_light.json';
 import { i18n } from '@kbn/i18n';
 import { isNumber } from 'lodash';
 import React from 'react';
 import styled from 'styled-components';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { ServiceNodeMetrics } from '../../../../../../../../plugins/apm/server/lib/service_map/get_service_map_service_node_info';
+import { ServiceNodeMetrics } from '../../../../../../../../plugins/apm/common/service_map';
 import {
   asDuration,
   asPercent,
   toMicroseconds,
   tpmUnit
 } from '../../../../utils/formatters';
-import { useUrlParams } from '../../../../hooks/useUrlParams';
-import { useFetcher } from '../../../../hooks/useFetcher';
 
 function LoadingSpinner() {
   return (
@@ -51,53 +48,19 @@ const ItemDescription = styled('td')`
   text-align: right;
 `;
 
-const na = i18n.translate('xpack.apm.serviceMap.NotAvailableMetric', {
-  defaultMessage: 'N/A'
-});
-
-interface MetricListProps {
-  serviceName: string;
+interface ServiceMetricListProps extends ServiceNodeMetrics {
+  isLoading: boolean;
 }
 
-export function ServiceMetricList({ serviceName }: MetricListProps) {
-  const {
-    urlParams: { start, end, environment }
-  } = useUrlParams();
-
-  const { data = {} as ServiceNodeMetrics, status } = useFetcher(
-    callApmApi => {
-      if (serviceName && start && end) {
-        return callApmApi({
-          pathname: '/api/apm/service-map/service/{serviceName}',
-          params: {
-            path: {
-              serviceName
-            },
-            query: {
-              start,
-              end,
-              environment
-            }
-          }
-        });
-      }
-    },
-    [serviceName, start, end, environment],
-    {
-      preservePreviousData: false
-    }
-  );
-
-  const {
-    avgTransactionDuration,
-    avgRequestsPerMinute,
-    avgErrorsPerMinute,
-    avgCpuUsage,
-    avgMemoryUsage,
-    numInstances
-  } = data;
-  const isLoading = status === 'loading';
-
+export function ServiceMetricList({
+  avgTransactionDuration,
+  avgRequestsPerMinute,
+  avgErrorsPerMinute,
+  avgCpuUsage,
+  avgMemoryUsage,
+  numInstances,
+  isLoading
+}: ServiceMetricListProps) {
   const listItems = [
     {
       title: i18n.translate(
@@ -108,7 +71,7 @@ export function ServiceMetricList({ serviceName }: MetricListProps) {
       ),
       description: isNumber(avgTransactionDuration)
         ? asDuration(toMicroseconds(avgTransactionDuration, 'milliseconds'))
-        : na
+        : null
     },
     {
       title: i18n.translate(
@@ -119,7 +82,7 @@ export function ServiceMetricList({ serviceName }: MetricListProps) {
       ),
       description: isNumber(avgRequestsPerMinute)
         ? `${avgRequestsPerMinute.toFixed(2)} ${tpmUnit('request')}`
-        : na
+        : null
     },
     {
       title: i18n.translate(
@@ -128,13 +91,13 @@ export function ServiceMetricList({ serviceName }: MetricListProps) {
           defaultMessage: 'Errors per minute (avg.)'
         }
       ),
-      description: avgErrorsPerMinute?.toFixed(2) ?? na
+      description: avgErrorsPerMinute?.toFixed(2)
     },
     {
       title: i18n.translate('xpack.apm.serviceMap.avgCpuUsagePopoverMetric', {
         defaultMessage: 'CPU usage (avg.)'
       }),
-      description: isNumber(avgCpuUsage) ? asPercent(avgCpuUsage, 1) : na
+      description: isNumber(avgCpuUsage) ? asPercent(avgCpuUsage, 1) : null
     },
     {
       title: i18n.translate(
@@ -143,7 +106,9 @@ export function ServiceMetricList({ serviceName }: MetricListProps) {
           defaultMessage: 'Memory usage (avg.)'
         }
       ),
-      description: isNumber(avgMemoryUsage) ? asPercent(avgMemoryUsage, 1) : na
+      description: isNumber(avgMemoryUsage)
+        ? asPercent(avgMemoryUsage, 1)
+        : null
     }
   ];
   return isLoading ? (
@@ -165,12 +130,15 @@ export function ServiceMetricList({ serviceName }: MetricListProps) {
 
       <table>
         <tbody>
-          {listItems.map(({ title, description }) => (
-            <ItemRow key={title}>
-              <ItemTitle>{title}</ItemTitle>
-              <ItemDescription>{description}</ItemDescription>
-            </ItemRow>
-          ))}
+          {listItems.map(
+            ({ title, description }) =>
+              description && (
+                <ItemRow key={title}>
+                  <ItemTitle>{title}</ItemTitle>
+                  <ItemDescription>{description}</ItemDescription>
+                </ItemRow>
+              )
+          )}
         </tbody>
       </table>
     </>
diff --git a/x-pack/plugins/apm/common/service_map.ts b/x-pack/plugins/apm/common/service_map.ts
index fbaa489c45039..528aec2f70ad9 100644
--- a/x-pack/plugins/apm/common/service_map.ts
+++ b/x-pack/plugins/apm/common/service_map.ts
@@ -21,3 +21,12 @@ export interface Connection {
   source: ConnectionNode;
   destination: ConnectionNode;
 }
+
+export interface ServiceNodeMetrics {
+  numInstances: number;
+  avgMemoryUsage: number | null;
+  avgCpuUsage: number | null;
+  avgTransactionDuration: number | null;
+  avgRequestsPerMinute: number | null;
+  avgErrorsPerMinute: number | null;
+}
diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts
index 6c4d540103cec..0fe825e8ace35 100644
--- a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts
+++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts
@@ -18,7 +18,6 @@ import {
   SERVICE_NODE_NAME
 } from '../../../common/elasticsearch_fieldnames';
 import { percentMemoryUsedScript } from '../metrics/by_agent/shared/memory';
-import { PromiseReturnType } from '../../../typings/common';
 
 interface Options {
   setup: Setup & SetupTimeRange;
@@ -32,10 +31,6 @@ interface TaskParameters {
   filter: ESFilter[];
 }
 
-export type ServiceNodeMetrics = PromiseReturnType<
-  typeof getServiceMapServiceNodeInfo
->;
-
 export async function getServiceMapServiceNodeInfo({
   serviceName,
   environment,
@@ -112,7 +107,10 @@ async function getTransactionMetrics({
   setup,
   filter,
   minutes
-}: TaskParameters) {
+}: TaskParameters): Promise<{
+  avgTransactionDuration: number | null;
+  avgRequestsPerMinute: number | null;
+}> {
   const { indices, client } = setup;
 
   const response = await client.search({
@@ -140,13 +138,16 @@ async function getTransactionMetrics({
   });
 
   return {
-    avgTransactionDuration: response.aggregations?.duration.value,
+    avgTransactionDuration: response.aggregations?.duration.value ?? null,
     avgRequestsPerMinute:
       response.hits.total.value > 0 ? response.hits.total.value / minutes : null
   };
 }
 
-async function getCpuMetrics({ setup, filter }: TaskParameters) {
+async function getCpuMetrics({
+  setup,
+  filter
+}: TaskParameters): Promise<{ avgCpuUsage: number | null }> {
   const { indices, client } = setup;
 
   const response = await client.search({
@@ -180,11 +181,14 @@ async function getCpuMetrics({ setup, filter }: TaskParameters) {
   });
 
   return {
-    avgCpuUsage: response.aggregations?.avgCpuUsage.value
+    avgCpuUsage: response.aggregations?.avgCpuUsage.value ?? null
   };
 }
 
-async function getMemoryMetrics({ setup, filter }: TaskParameters) {
+async function getMemoryMetrics({
+  setup,
+  filter
+}: TaskParameters): Promise<{ avgMemoryUsage: number | null }> {
   const { client, indices } = setup;
   const response = await client.search({
     index: indices['apm_oss.metricsIndices'],
@@ -221,11 +225,14 @@ async function getMemoryMetrics({ setup, filter }: TaskParameters) {
   });
 
   return {
-    avgMemoryUsage: response.aggregations?.avgMemoryUsage.value
+    avgMemoryUsage: response.aggregations?.avgMemoryUsage.value ?? null
   };
 }
 
-async function getNumInstances({ setup, filter }: TaskParameters) {
+async function getNumInstances({
+  setup,
+  filter
+}: TaskParameters): Promise<{ numInstances: number }> {
   const { client, indices } = setup;
   const response = await client.search({
     index: indices['apm_oss.transactionIndices'],

From 858fe2e9251e748bfbcd85623f22d9c1eac71076 Mon Sep 17 00:00:00 2001
From: Maggie Ghamry <46542915+maggieghamry@users.noreply.github.com>
Date: Mon, 24 Feb 2020 14:44:19 -0500
Subject: [PATCH 151/174] [Canvas] Toggles footer editable controls when you
 turn off edit mode #52786 (#58044)

* Bug fix update

update to toggle footer edit controls "off" when locking edit controls.

* Update to toggle

Update to toggle, so that only the "Expression Editor" and tray are hidden when locking controls.

* Update to toggle logic

Added canUserWrite property, and added a condition to ensure the "page" tray still shows once the Expression editor is locked

* Update to property definition

Update to consolidate isWriteable and canUserWrite into one variable instead of two.

* Adding Test (in progress) code

Adding Toolbar test code (so far) - needs to be completed to address nested compononet storybook issue

* Adding issue link

Adding issue link in TODO comments

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../toolbar/__examples__/toolbar.stories.tsx  | 39 +++++++++++++++++++
 .../canvas/public/components/toolbar/index.js |  3 ++
 .../public/components/toolbar/toolbar.tsx     | 11 +++++-
 3 files changed, 51 insertions(+), 2 deletions(-)
 create mode 100644 x-pack/legacy/plugins/canvas/public/components/toolbar/__examples__/toolbar.stories.tsx

diff --git a/x-pack/legacy/plugins/canvas/public/components/toolbar/__examples__/toolbar.stories.tsx b/x-pack/legacy/plugins/canvas/public/components/toolbar/__examples__/toolbar.stories.tsx
new file mode 100644
index 0000000000000..5907c932ddabb
--- /dev/null
+++ b/x-pack/legacy/plugins/canvas/public/components/toolbar/__examples__/toolbar.stories.tsx
@@ -0,0 +1,39 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+/*
+ TODO: uncomment and fix this test to address storybook errors as a result of nested component dependencies - https://github.com/elastic/kibana/issues/58289
+ */
+
+/*
+import { action } from '@storybook/addon-actions';
+import { storiesOf } from '@storybook/react';
+import React from 'react';
+import { Toolbar } from '../toolbar';
+
+storiesOf('components/Toolbar', module)
+  .addDecorator(story => (
+    <div
+      style={{
+        width: '200px',
+      }}
+    >
+      {story()}
+    </div>
+  ))
+  .add('with null metric', () => (
+    <Toolbar
+      setTray={action('setTray')}
+      nextPage={action('nextPage')}
+      previousPage={action('previousPage')}
+      setShowWorkpadManager={action('setShowWorkpadManager')}
+      selectedPageNumber={1}
+      totalPages={1}
+      showWorkpadManager={false}
+      isWriteable={true}
+    />
+  ));
+*/
diff --git a/x-pack/legacy/plugins/canvas/public/components/toolbar/index.js b/x-pack/legacy/plugins/canvas/public/components/toolbar/index.js
index c834304739a4c..294a44ba0415a 100644
--- a/x-pack/legacy/plugins/canvas/public/components/toolbar/index.js
+++ b/x-pack/legacy/plugins/canvas/public/components/toolbar/index.js
@@ -7,12 +7,14 @@
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import { pure, compose, withState, getContext, withHandlers } from 'recompose';
+import { canUserWrite } from '../../state/selectors/app';
 
 import {
   getWorkpad,
   getWorkpadName,
   getSelectedPageIndex,
   getSelectedElement,
+  isWriteable,
 } from '../../state/selectors/workpad';
 
 import { Toolbar as Component } from './toolbar';
@@ -23,6 +25,7 @@ const mapStateToProps = state => ({
   totalPages: getWorkpad(state).pages.length,
   selectedPageNumber: getSelectedPageIndex(state) + 1,
   selectedElement: getSelectedElement(state),
+  isWriteable: isWriteable(state) && canUserWrite(state),
 });
 
 export const Toolbar = compose(
diff --git a/x-pack/legacy/plugins/canvas/public/components/toolbar/toolbar.tsx b/x-pack/legacy/plugins/canvas/public/components/toolbar/toolbar.tsx
index 089f021ccdc32..0f8204e6bc261 100644
--- a/x-pack/legacy/plugins/canvas/public/components/toolbar/toolbar.tsx
+++ b/x-pack/legacy/plugins/canvas/public/components/toolbar/toolbar.tsx
@@ -39,7 +39,8 @@ enum TrayType {
 
 interface Props {
   workpadName: string;
-
+  isWriteable: boolean;
+  canUserWrite: boolean;
   tray: TrayType | null;
   setTray: (tray: TrayType | null) => void;
 
@@ -66,12 +67,17 @@ export const Toolbar = (props: Props) => {
     totalPages,
     showWorkpadManager,
     setShowWorkpadManager,
+    isWriteable,
   } = props;
 
   const elementIsSelected = Boolean(selectedElement);
 
   const done = () => setTray(null);
 
+  if (!isWriteable && tray === TrayType.expression) {
+    done();
+  }
+
   const showHideTray = (exp: TrayType) => {
     if (tray && tray === exp) {
       return done();
@@ -135,7 +141,7 @@ export const Toolbar = (props: Props) => {
             />
           </EuiFlexItem>
           <EuiFlexItem />
-          {elementIsSelected && (
+          {elementIsSelected && isWriteable && (
             <EuiFlexItem grow={false}>
               <EuiButtonEmpty
                 color="text"
@@ -166,4 +172,5 @@ Toolbar.propTypes = {
   selectedElement: PropTypes.object,
   showWorkpadManager: PropTypes.bool.isRequired,
   setShowWorkpadManager: PropTypes.func.isRequired,
+  isWriteable: PropTypes.bool.isRequired,
 };

From 783c7f9a40670a50263d5835bd9f1e3e34482863 Mon Sep 17 00:00:00 2001
From: Tiago Costa <tiagoffcc@hotmail.com>
Date: Mon, 24 Feb 2020 20:09:38 +0000
Subject: [PATCH 152/174] chore(NA): remove empty filter check from thread
 loader pool config getter (#58385)

---
 src/optimize/base_optimizer.js | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/src/optimize/base_optimizer.js b/src/optimize/base_optimizer.js
index a833204eaa0e2..a94f251c292f9 100644
--- a/src/optimize/base_optimizer.js
+++ b/src/optimize/base_optimizer.js
@@ -152,11 +152,7 @@ export default class BaseOptimizer {
 
   getThreadLoaderPoolConfig() {
     // Calculate the node options from the NODE_OPTIONS env var
-    const parsedNodeOptions = process.env.NODE_OPTIONS
-      ? // thread-loader could not receive empty string as options
-        // or it would break that's why we need to filter here
-        process.env.NODE_OPTIONS.split(/\s/).filter(opt => !!opt)
-      : [];
+    const parsedNodeOptions = process.env.NODE_OPTIONS ? process.env.NODE_OPTIONS.split(/\s/) : [];
 
     return {
       name: 'optimizer-thread-loader-main-pool',

From 77fe83e7db27b410cd1eb63084c22ca7bb32d278 Mon Sep 17 00:00:00 2001
From: Brandon Kobel <brandon.kobel@elastic.co>
Date: Mon, 24 Feb 2020 13:04:30 -0800
Subject: [PATCH 153/174] Remove restriction that route must start with `/api`
 to use api authorization (#58351)

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../authorization/api_authorization.test.ts   | 36 +++++--------------
 .../server/authorization/api_authorization.ts |  4 +--
 2 files changed, 10 insertions(+), 30 deletions(-)

diff --git a/x-pack/plugins/security/server/authorization/api_authorization.test.ts b/x-pack/plugins/security/server/authorization/api_authorization.test.ts
index a5902f251b082..409f998cfe8d2 100644
--- a/x-pack/plugins/security/server/authorization/api_authorization.test.ts
+++ b/x-pack/plugins/security/server/authorization/api_authorization.test.ts
@@ -15,27 +15,7 @@ import {
 import { authorizationMock } from './index.mock';
 
 describe('initAPIAuthorization', () => {
-  test(`route that doesn't start with "/api/" continues`, async () => {
-    const mockHTTPSetup = coreMock.createSetup().http;
-    initAPIAuthorization(
-      mockHTTPSetup,
-      authorizationMock.create(),
-      loggingServiceMock.create().get()
-    );
-
-    const [[postAuthHandler]] = mockHTTPSetup.registerOnPostAuth.mock.calls;
-
-    const mockRequest = httpServerMock.createKibanaRequest({ method: 'get', path: '/app/foo' });
-    const mockResponse = httpServerMock.createResponseFactory();
-    const mockPostAuthToolkit = httpServiceMock.createOnPostAuthToolkit();
-
-    await postAuthHandler(mockRequest, mockResponse, mockPostAuthToolkit);
-
-    expect(mockResponse.notFound).not.toHaveBeenCalled();
-    expect(mockPostAuthToolkit.next).toHaveBeenCalledTimes(1);
-  });
-
-  test(`protected route that starts with "/api/", but "mode.useRbacForRequest()" returns false continues`, async () => {
+  test(`protected route when "mode.useRbacForRequest()" returns false continues`, async () => {
     const mockHTTPSetup = coreMock.createSetup().http;
     const mockAuthz = authorizationMock.create();
     initAPIAuthorization(mockHTTPSetup, mockAuthz, loggingServiceMock.create().get());
@@ -44,7 +24,7 @@ describe('initAPIAuthorization', () => {
 
     const mockRequest = httpServerMock.createKibanaRequest({
       method: 'get',
-      path: '/api/foo',
+      path: '/foo/bar',
       routeTags: ['access:foo'],
     });
     const mockResponse = httpServerMock.createResponseFactory();
@@ -59,7 +39,7 @@ describe('initAPIAuthorization', () => {
     expect(mockAuthz.mode.useRbacForRequest).toHaveBeenCalledWith(mockRequest);
   });
 
-  test(`unprotected route that starts with "/api/", but "mode.useRbacForRequest()" returns true continues`, async () => {
+  test(`unprotected route when "mode.useRbacForRequest()" returns true continues`, async () => {
     const mockHTTPSetup = coreMock.createSetup().http;
     const mockAuthz = authorizationMock.create();
     initAPIAuthorization(mockHTTPSetup, mockAuthz, loggingServiceMock.create().get());
@@ -68,7 +48,7 @@ describe('initAPIAuthorization', () => {
 
     const mockRequest = httpServerMock.createKibanaRequest({
       method: 'get',
-      path: '/api/foo',
+      path: '/foo/bar',
       routeTags: ['not-access:foo'],
     });
     const mockResponse = httpServerMock.createResponseFactory();
@@ -83,7 +63,7 @@ describe('initAPIAuthorization', () => {
     expect(mockAuthz.mode.useRbacForRequest).toHaveBeenCalledWith(mockRequest);
   });
 
-  test(`protected route that starts with "/api/", "mode.useRbacForRequest()" returns true and user is authorized continues`, async () => {
+  test(`protected route when "mode.useRbacForRequest()" returns true and user is authorized continues`, async () => {
     const mockHTTPSetup = coreMock.createSetup().http;
     const mockAuthz = authorizationMock.create({ version: '1.0.0-zeta1' });
     initAPIAuthorization(mockHTTPSetup, mockAuthz, loggingServiceMock.create().get());
@@ -93,7 +73,7 @@ describe('initAPIAuthorization', () => {
     const headers = { authorization: 'foo' };
     const mockRequest = httpServerMock.createKibanaRequest({
       method: 'get',
-      path: '/api/foo',
+      path: '/foo/bar',
       headers,
       routeTags: ['access:foo'],
     });
@@ -118,7 +98,7 @@ describe('initAPIAuthorization', () => {
     expect(mockAuthz.mode.useRbacForRequest).toHaveBeenCalledWith(mockRequest);
   });
 
-  test(`protected route that starts with "/api/", "mode.useRbacForRequest()" returns true and user isn't authorized responds with a 404`, async () => {
+  test(`protected route when "mode.useRbacForRequest()" returns true and user isn't authorized responds with a 404`, async () => {
     const mockHTTPSetup = coreMock.createSetup().http;
     const mockAuthz = authorizationMock.create({ version: '1.0.0-zeta1' });
     initAPIAuthorization(mockHTTPSetup, mockAuthz, loggingServiceMock.create().get());
@@ -128,7 +108,7 @@ describe('initAPIAuthorization', () => {
     const headers = { authorization: 'foo' };
     const mockRequest = httpServerMock.createKibanaRequest({
       method: 'get',
-      path: '/api/foo',
+      path: '/foo/bar',
       headers,
       routeTags: ['access:foo'],
     });
diff --git a/x-pack/plugins/security/server/authorization/api_authorization.ts b/x-pack/plugins/security/server/authorization/api_authorization.ts
index b280cc74c230f..cc672fbc69e06 100644
--- a/x-pack/plugins/security/server/authorization/api_authorization.ts
+++ b/x-pack/plugins/security/server/authorization/api_authorization.ts
@@ -13,8 +13,8 @@ export function initAPIAuthorization(
   logger: Logger
 ) {
   http.registerOnPostAuth(async (request, response, toolkit) => {
-    // if the api doesn't start with "/api/" or we aren't using RBAC for this request, just continue
-    if (!request.url.path!.startsWith('/api/') || !mode.useRbacForRequest(request)) {
+    // if we aren't using RBAC for this request, just continue
+    if (!mode.useRbacForRequest(request)) {
       return toolkit.next();
     }
 

From 7e087633d26dfebe5cf262bbfde2cd4c29770464 Mon Sep 17 00:00:00 2001
From: Lukas Olson <olson.lukas@gmail.com>
Date: Mon, 24 Feb 2020 14:44:03 -0700
Subject: [PATCH 154/174] Remove unused indexPattern:fieldMapping:lookBack
 advanced setting (#58147)

* Remove unused indexPattern:fieldMapping:lookBack advanced setting

* Remove unused translations

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 docs/management/advanced-options.asciidoc             |  2 --
 src/legacy/core_plugins/kibana/ui_setting_defaults.js | 11 -----------
 x-pack/plugins/translations/translations/ja-JP.json   |  2 --
 x-pack/plugins/translations/translations/zh-CN.json   |  2 --
 4 files changed, 17 deletions(-)

diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc
index 80c9053dc5ae6..9d4052bbd0156 100644
--- a/docs/management/advanced-options.asciidoc
+++ b/docs/management/advanced-options.asciidoc
@@ -62,8 +62,6 @@ mentioned use "\_default_".
 `histogram:maxBars`:: Date histograms are not generated with more bars than the value of this property, scaling values
 when necessary.
 `history:limit`:: In fields that have history, such as query inputs, show this many recent values.
-`indexPattern:fieldMapping:lookBack`:: For index patterns containing timestamps in their names,
-look for this many recent matching patterns from which to query the field mapping.
 `indexPattern:placeholder`:: The default placeholder value to use in Management > Index Patterns > Create Index Pattern.
 `metaFields`:: Fields that exist outside of `_source`. Kibana merges these fields
 into the document when displaying it.
diff --git a/src/legacy/core_plugins/kibana/ui_setting_defaults.js b/src/legacy/core_plugins/kibana/ui_setting_defaults.js
index f92694eabe58d..c0628b72c2ce7 100644
--- a/src/legacy/core_plugins/kibana/ui_setting_defaults.js
+++ b/src/legacy/core_plugins/kibana/ui_setting_defaults.js
@@ -690,17 +690,6 @@ export function getUiSettingDefaults() {
           'The maximum height that a cell in a table should occupy. Set to 0 to disable truncation',
       }),
     },
-    'indexPattern:fieldMapping:lookBack': {
-      name: i18n.translate('kbn.advancedSettings.indexPattern.recentMatchingTitle', {
-        defaultMessage: 'Recent matching patterns',
-      }),
-      value: 5,
-      description: i18n.translate('kbn.advancedSettings.indexPattern.recentMatchingText', {
-        defaultMessage:
-          'For index patterns containing timestamps in their names, look for this many recent matching ' +
-          'patterns from which to query the field mapping',
-      }),
-    },
     'format:defaultTypeMap': {
       name: i18n.translate('kbn.advancedSettings.format.defaultTypeMapTitle', {
         defaultMessage: 'Field type format name',
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 78bb39dd22dea..dc97a96d4fcba 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -878,8 +878,6 @@
     "kbn.advancedSettings.histogram.maxBarsTitle": "最高バー数",
     "kbn.advancedSettings.historyLimitText": "履歴があるフィールド (例: クエリインプット) に個の数の最近の値が表示されます",
     "kbn.advancedSettings.historyLimitTitle": "履歴制限数",
-    "kbn.advancedSettings.indexPattern.recentMatchingText": "名前にタイムスタンプが含まれているインデックスパターンで、フィールドマッチングをクエリする最近の一致したパターンが、この数検索されます",
-    "kbn.advancedSettings.indexPattern.recentMatchingTitle": "最近一致したパターン",
     "kbn.advancedSettings.indexPatternPlaceholderText": "「管理 > インデックスパターン > インデックスパターンを作成」で使用される「インデックスパターン名」フィールドのプレースホルダーです。",
     "kbn.advancedSettings.indexPatternPlaceholderTitle": "インデックスパターンのプレースホルダー",
     "kbn.advancedSettings.maxBucketsText": "1 つのデータソースが返せるバケットの最大数です",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index fc9dacf0e50f7..2532cdb0c4d07 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -878,8 +878,6 @@
     "kbn.advancedSettings.histogram.maxBarsTitle": "最大条形数",
     "kbn.advancedSettings.historyLimitText": "在具有历史记录(例如查询输入)的字段中,显示此数目的最近值",
     "kbn.advancedSettings.historyLimitTitle": "历史记录限制",
-    "kbn.advancedSettings.indexPattern.recentMatchingText": "对于名称中包含时间戳的索引模式,寻找此数目的最近匹配模式,以从其中查询字段映射",
-    "kbn.advancedSettings.indexPattern.recentMatchingTitle": "最近匹配模式",
     "kbn.advancedSettings.indexPatternPlaceholderText": "在“管理 > 索引模式 > 创建索引模式”中“索引模式名称”的占位符。",
     "kbn.advancedSettings.indexPatternPlaceholderTitle": "索引模式占位符",
     "kbn.advancedSettings.maxBucketsText": "单个数据源可以返回的最大存储桶数目",

From 13eacb51f0fd030f123c05e68c545bdb99f6ac70 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= <sorenlouv@gmail.com>
Date: Mon, 24 Feb 2020 23:43:40 +0100
Subject: [PATCH 155/174] [APM] Stabilize agent configuration API (#57767)

---
 docs/apm/api.asciidoc                         | 260 ++++++++++++++++++
 docs/apm/index.asciidoc                       |   2 +
 .../AddEditFlyout/DeleteButton.tsx            |  10 +-
 .../AddEditFlyout/index.tsx                   |   2 +-
 .../AddEditFlyout/saveConfig.ts               |  30 +-
 x-pack/legacy/plugins/apm/readme.md           |  22 ++
 .../index.test.ts                             |  41 +++
 .../agent_configuration_intake_rt/index.ts    |  26 ++
 .../apm/server/lib/helpers/es_client.ts       |   7 +-
 .../lib/services/get_service_node_metadata.ts |   4 +-
 .../__snapshots__/queries.test.ts.snap        | 234 ++++++++++------
 .../configuration_types.d.ts                  |  19 +-
 .../create_or_update_configuration.ts         |  22 +-
 .../find_exact_configuration.ts               |  46 ++++
 .../get_agent_name_by_service.ts              |   6 +-
 .../agent_configuration/queries.test.ts       | 146 ++++++----
 .../{search.ts => search_configurations.ts}   |  35 ++-
 .../apm/server/routes/create_apm_api.ts       |   6 +-
 .../routes/settings/agent_configuration.ts    | 137 ++++-----
 .../apis/apm/agent_configuration.ts           | 163 ++++++-----
 .../apis/apm/feature_controls.ts              |  42 +--
 x-pack/test/api_integration/apis/apm/index.ts |   2 +-
 x-pack/test/functional/apps/apm/index.ts      |   2 +-
 23 files changed, 894 insertions(+), 370 deletions(-)
 create mode 100644 docs/apm/api.asciidoc
 create mode 100644 x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.test.ts
 create mode 100644 x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.ts
 create mode 100644 x-pack/plugins/apm/server/lib/settings/agent_configuration/find_exact_configuration.ts
 rename x-pack/plugins/apm/server/lib/settings/agent_configuration/{search.ts => search_configurations.ts} (68%)

diff --git a/docs/apm/api.asciidoc b/docs/apm/api.asciidoc
new file mode 100644
index 0000000000000..b520cc46bef8d
--- /dev/null
+++ b/docs/apm/api.asciidoc
@@ -0,0 +1,260 @@
+[role="xpack"]
+[[apm-api]]
+== API
+
+Some APM app features are provided via a REST API:
+
+* <<agent-config-api>>
+
+TIP: Kibana provides additional <<api,REST APIs>>,
+and general information on <<using-api,how to use APIs>>.
+
+////
+*******************************************************
+////
+
+[[agent-config-api]]
+=== Agent Configuration API
+
+The Agent configuration API allows you to fine-tune your APM agent configuration,
+without needing to redeploy your application.
+
+The following Agent configuration APIs are available:
+
+* <<apm-update-config>> to create or update an Agent configuration
+* <<apm-delete-config>> to delete an Agent configuration.
+* <<apm-list-config>> to list all Agent configurations.
+* <<apm-search-config>> to search for an Agent configuration.
+
+////
+*******************************************************
+////
+
+[[apm-update-config]]
+==== Create or update configuration
+
+[[apm-update-config-req]]
+===== Request
+
+`PUT /api/apm/settings/agent-configuration`
+
+[[apm-update-config-req-body]]
+===== Request body
+
+`service`::
+(required, object) Service identifying the configuration to create or update.
+
+`name` :::
+  (required, string) Name of service
+
+`environment` :::
+  (optional, string) Environment of service
+
+`settings`::
+(required) Key/value object with settings and their corresponding value.
+
+`agent_name`::
+(optional) The agent name is used by the UI to determine which settings to display.
+
+
+[[apm-update-config-example]]
+===== Example
+
+[source,console]
+--------------------------------------------------
+PUT /api/apm/settings/agent-configuration
+{
+    "service" : {
+        "name" : "frontend",
+        "environment" : "production"
+    },
+    "settings" : {
+        "transaction_sample_rate" : 0.4,
+        "capture_body" : "off",
+        "transaction_max_spans" : 500
+    },
+    "agent_name": "nodejs"
+}
+--------------------------------------------------
+
+////
+*******************************************************
+////
+
+
+[[apm-delete-config]]
+==== Delete configuration
+
+[[apm-delete-config-req]]
+===== Request
+
+`DELETE /api/apm/settings/agent-configuration`
+
+[[apm-delete-config-req-body]]
+===== Request body
+`service`::
+(required, object) Service identifying the configuration to delete
+
+`name` :::
+  (required, string) Name of service
+
+`environment` :::
+  (optional, string) Environment of service
+
+
+[[apm-delete-config-example]]
+===== Example
+
+[source,console]
+--------------------------------------------------
+DELETE /api/apm/settings/agent-configuration
+{
+    "service" : {
+        "name" : "frontend",
+        "environment": "production"
+    }
+}
+--------------------------------------------------
+
+////
+*******************************************************
+////
+
+
+[[apm-list-config]]
+==== List configuration
+
+
+[[apm-list-config-req]]
+===== Request
+
+`GET  /api/apm/settings/agent-configuration`
+
+[[apm-list-config-body]]
+===== Response body
+
+[source,js]
+--------------------------------------------------
+[
+  {
+    "agent_name": "go",
+    "service": {
+      "name": "opbeans-go",
+      "environment": "production"
+    },
+    "settings": {
+      "transaction_sample_rate": 1,
+      "capture_body": "off",
+      "transaction_max_spans": 200
+    },
+    "@timestamp": 1581934104843,
+    "applied_by_agent": false,
+    "etag": "1e58c178efeebae15c25c539da740d21dee422fc"
+  },
+  {
+    "agent_name": "go",
+    "service": {
+      "name": "opbeans-go"
+    },
+    "settings": {
+      "transaction_sample_rate": 1,
+      "capture_body": "off",
+      "transaction_max_spans": 300
+    },
+    "@timestamp": 1581934111727,
+    "applied_by_agent": false,
+    "etag": "3eed916d3db434d9fb7f039daa681c7a04539a64"
+  },
+  {
+    "agent_name": "nodejs",
+    "service": {
+      "name": "frontend"
+    },
+    "settings": {
+      "transaction_sample_rate": 1,
+    },
+    "@timestamp": 1582031336265,
+    "applied_by_agent": false,
+    "etag": "5080ed25785b7b19f32713681e79f46996801a5b"
+  }
+]
+--------------------------------------------------
+
+[[apm-list-config-example]]
+===== Example
+
+[source,console]
+--------------------------------------------------
+GET  /api/apm/settings/agent-configuration
+--------------------------------------------------
+
+////
+*******************************************************
+////
+
+
+[[apm-search-config]]
+==== Search configuration
+
+[[apm-search-config-req]]
+===== Request
+
+`POST /api/apm/settings/agent-configuration/search`
+
+[[apm-search-config-req-body]]
+===== Request body
+
+`service`::
+(required, object) Service identifying the configuration.
+
+`name` :::
+  (required, string) Name of service
+
+`environment` :::
+  (optional, string) Environment of service
+
+`etag`::
+(required) etag is sent by the agent to indicate the etag of the last successfully applied configuration. If the etag matches an existing configuration its `applied_by_agent` property will be set to `true`. Every time a configuration is edited `applied_by_agent` is reset to `false`.
+
+[[apm-search-config-body]]
+===== Response body
+
+[source,js]
+--------------------------------------------------
+{
+  "_index": ".apm-agent-configuration",
+  "_id": "CIaqXXABmQCdPphWj8EJ",
+  "_score": 2,
+  "_source": {
+    "agent_name": "nodejs",
+    "service": {
+      "name": "frontend"
+    },
+    "settings": {
+      "transaction_sample_rate": 1,
+    },
+    "@timestamp": 1582031336265,
+    "applied_by_agent": false,
+    "etag": "5080ed25785b7b19f32713681e79f46996801a5b"
+  }
+}
+--------------------------------------------------
+
+[[apm-search-config-example]]
+===== Example
+
+[source,console]
+--------------------------------------------------
+POST /api/apm/settings/agent-configuration/search
+{
+    "etag" : "1e58c178efeebae15c25c539da740d21dee422fc",
+    "service" : {
+        "name" : "frontend",
+        "environment": "production"
+    }
+}
+--------------------------------------------------
+
+////
+*******************************************************
+////
diff --git a/docs/apm/index.asciidoc b/docs/apm/index.asciidoc
index 7eb7278cf0358..d3f0dc5b7f11f 100644
--- a/docs/apm/index.asciidoc
+++ b/docs/apm/index.asciidoc
@@ -24,3 +24,5 @@ include::getting-started.asciidoc[]
 include::bottlenecks.asciidoc[]
 
 include::using-the-apm-ui.asciidoc[]
+
+include::api.asciidoc[]
diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/DeleteButton.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/DeleteButton.tsx
index 496147b02589b..1564f1ae746a9 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/DeleteButton.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/DeleteButton.tsx
@@ -51,12 +51,18 @@ async function deleteConfig(
 ) {
   try {
     await callApmApi({
-      pathname: '/api/apm/settings/agent-configuration/{configurationId}',
+      pathname: '/api/apm/settings/agent-configuration',
       method: 'DELETE',
       params: {
-        path: { configurationId: selectedConfig.id }
+        body: {
+          service: {
+            name: selectedConfig.service.name,
+            environment: selectedConfig.service.environment
+          }
+        }
       }
     });
+
     toasts.addSuccess({
       title: i18n.translate(
         'xpack.apm.settings.agentConf.flyout.deleteSection.deleteConfigSucceededTitle',
diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx
index 653dedea733f2..c77617fbb424f 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx
@@ -135,8 +135,8 @@ export function AddEditFlyout({
       sampleRate,
       captureBody,
       transactionMaxSpans,
-      configurationId: selectedConfig ? selectedConfig.id : undefined,
       agentName,
+      isExistingConfig: Boolean(selectedConfig),
       toasts,
       trackApmEvent
     });
diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts
index 19934cafb4694..d36120a054795 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts
+++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts
@@ -27,8 +27,8 @@ export async function saveConfig({
   sampleRate,
   captureBody,
   transactionMaxSpans,
-  configurationId,
   agentName,
+  isExistingConfig,
   toasts,
   trackApmEvent
 }: {
@@ -38,8 +38,8 @@ export async function saveConfig({
   sampleRate: string;
   captureBody: string;
   transactionMaxSpans: string;
-  configurationId?: string;
   agentName?: string;
+  isExistingConfig: boolean;
   toasts: NotificationsStart['toasts'];
   trackApmEvent: UiTracker;
 }) {
@@ -64,24 +64,14 @@ export async function saveConfig({
       settings
     };
 
-    if (configurationId) {
-      await callApmApi({
-        pathname: '/api/apm/settings/agent-configuration/{configurationId}',
-        method: 'PUT',
-        params: {
-          path: { configurationId },
-          body: configuration
-        }
-      });
-    } else {
-      await callApmApi({
-        pathname: '/api/apm/settings/agent-configuration/new',
-        method: 'POST',
-        params: {
-          body: configuration
-        }
-      });
-    }
+    await callApmApi({
+      pathname: '/api/apm/settings/agent-configuration',
+      method: 'PUT',
+      params: {
+        query: { overwrite: isExistingConfig },
+        body: configuration
+      }
+    });
 
     toasts.addSuccess({
       title: i18n.translate(
diff --git a/x-pack/legacy/plugins/apm/readme.md b/x-pack/legacy/plugins/apm/readme.md
index 2106243d12aea..a513249c296db 100644
--- a/x-pack/legacy/plugins/apm/readme.md
+++ b/x-pack/legacy/plugins/apm/readme.md
@@ -71,6 +71,28 @@ node scripts/jest.js plugins/apm --watch
 node scripts/jest.js plugins/apm --updateSnapshot
 ```
 
+### Functional tests
+
+**Start server**
+`node scripts/functional_tests_server --config x-pack/test/functional/config.js`
+
+**Run tests**
+`node scripts/functional_test_runner --config x-pack/test/functional/config.js --grep='APM specs'`
+
+APM tests are located in `x-pack/test/functional/apps/apm`.
+For debugging access Elasticsearch on http://localhost:9220` (elastic/changeme)
+
+### API integration tests
+
+**Start server**
+`node scripts/functional_tests_server --config x-pack/test/api_integration/config.js`
+
+**Run tests**
+`node scripts/functional_test_runner --config x-pack/test/api_integration/config.js --grep='APM specs'`
+
+APM tests are located in `x-pack/test/api_integration/apis/apm`.
+For debugging access Elasticsearch on http://localhost:9220` (elastic/changeme)
+
 ### Linting
 
 _Note: Run the following commands from `kibana/`._
diff --git a/x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.test.ts b/x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.test.ts
new file mode 100644
index 0000000000000..4c9dc78eb41e9
--- /dev/null
+++ b/x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.test.ts
@@ -0,0 +1,41 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { agentConfigurationIntakeRt } from './index';
+import { isRight } from 'fp-ts/lib/Either';
+
+describe('agentConfigurationIntakeRt', () => {
+  it('is valid when required parameters are given', () => {
+    const config = {
+      service: {},
+      settings: {}
+    };
+
+    expect(isConfigValid(config)).toBe(true);
+  });
+
+  it('is valid when required and optional parameters are given', () => {
+    const config = {
+      service: { name: 'my-service', environment: 'my-environment' },
+      settings: {
+        transaction_sample_rate: 0.5,
+        capture_body: 'foo',
+        transaction_max_spans: 10
+      }
+    };
+
+    expect(isConfigValid(config)).toBe(true);
+  });
+
+  it('is invalid when required parameters are not given', () => {
+    const config = {};
+    expect(isConfigValid(config)).toBe(false);
+  });
+});
+
+function isConfigValid(config: any) {
+  return isRight(agentConfigurationIntakeRt.decode(config));
+}
diff --git a/x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.ts b/x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.ts
new file mode 100644
index 0000000000000..32a2832b5eaf3
--- /dev/null
+++ b/x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.ts
@@ -0,0 +1,26 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import * as t from 'io-ts';
+import { transactionSampleRateRt } from '../transaction_sample_rate_rt';
+import { transactionMaxSpansRt } from '../transaction_max_spans_rt';
+
+export const serviceRt = t.partial({
+  name: t.string,
+  environment: t.string
+});
+
+export const agentConfigurationIntakeRt = t.intersection([
+  t.partial({ agent_name: t.string }),
+  t.type({
+    service: serviceRt,
+    settings: t.partial({
+      transaction_sample_rate: transactionSampleRateRt,
+      capture_body: t.string,
+      transaction_max_spans: transactionMaxSpansRt
+    })
+  })
+]);
diff --git a/x-pack/plugins/apm/server/lib/helpers/es_client.ts b/x-pack/plugins/apm/server/lib/helpers/es_client.ts
index 8ada02d085631..86eb1dba507f0 100644
--- a/x-pack/plugins/apm/server/lib/helpers/es_client.ts
+++ b/x-pack/plugins/apm/server/lib/helpers/es_client.ts
@@ -7,9 +7,10 @@
 /* eslint-disable no-console */
 import {
   IndexDocumentParams,
-  IndicesCreateParams,
   IndicesDeleteParams,
-  SearchParams
+  SearchParams,
+  IndicesCreateParams,
+  DeleteDocumentResponse
 } from 'elasticsearch';
 import { cloneDeep, isString, merge, uniqueId } from 'lodash';
 import { KibanaRequest } from 'src/core/server';
@@ -188,7 +189,7 @@ export function getESClient(
     index: <Body>(params: APMIndexDocumentParams<Body>) => {
       return withTime(() => callMethod('index', params));
     },
-    delete: (params: IndicesDeleteParams) => {
+    delete: (params: IndicesDeleteParams): Promise<DeleteDocumentResponse> => {
       return withTime(() => callMethod('delete', params));
     },
     indicesCreate: (params: IndicesCreateParams) => {
diff --git a/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts b/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts
index 7120d3bca6c25..ccd8b123e23e2 100644
--- a/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts
@@ -58,8 +58,8 @@ export async function getServiceNodeMetadata({
   const response = await client.search(query);
 
   return {
-    host: response.aggregations?.host.buckets[0].key || NOT_AVAILABLE_LABEL,
+    host: response.aggregations?.host.buckets[0]?.key || NOT_AVAILABLE_LABEL,
     containerId:
-      response.aggregations?.containerId.buckets[0].key || NOT_AVAILABLE_LABEL
+      response.aggregations?.containerId.buckets[0]?.key || NOT_AVAILABLE_LABEL
   };
 }
diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/settings/agent_configuration/__snapshots__/queries.test.ts.snap
index 542fdd99e2635..db34b4d5d20b5 100644
--- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/__snapshots__/queries.test.ts.snap
+++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/__snapshots__/queries.test.ts.snap
@@ -1,6 +1,90 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`agent configuration queries fetches all environments 1`] = `
+exports[`agent configuration queries findExactConfiguration find configuration by service.environment 1`] = `
+Object {
+  "body": Object {
+    "query": Object {
+      "bool": Object {
+        "filter": Array [
+          Object {
+            "bool": Object {
+              "must_not": Array [
+                Object {
+                  "exists": Object {
+                    "field": "service.name",
+                  },
+                },
+              ],
+            },
+          },
+          Object {
+            "term": Object {
+              "service.environment": "bar",
+            },
+          },
+        ],
+      },
+    },
+  },
+  "index": "myIndex",
+}
+`;
+
+exports[`agent configuration queries findExactConfiguration find configuration by service.name 1`] = `
+Object {
+  "body": Object {
+    "query": Object {
+      "bool": Object {
+        "filter": Array [
+          Object {
+            "term": Object {
+              "service.name": "foo",
+            },
+          },
+          Object {
+            "bool": Object {
+              "must_not": Array [
+                Object {
+                  "exists": Object {
+                    "field": "service.environment",
+                  },
+                },
+              ],
+            },
+          },
+        ],
+      },
+    },
+  },
+  "index": "myIndex",
+}
+`;
+
+exports[`agent configuration queries findExactConfiguration find configuration by service.name and service.environment 1`] = `
+Object {
+  "body": Object {
+    "query": Object {
+      "bool": Object {
+        "filter": Array [
+          Object {
+            "term": Object {
+              "service.name": "foo",
+            },
+          },
+          Object {
+            "term": Object {
+              "service.environment": "bar",
+            },
+          },
+        ],
+      },
+    },
+  },
+  "index": "myIndex",
+}
+`;
+
+exports[`agent configuration queries getAllEnvironments fetches all environments 1`] = `
 Object {
   "body": Object {
     "aggs": Object {
@@ -41,14 +125,79 @@ Object {
 }
 `;
 
-exports[`agent configuration queries fetches configurations 1`] = `
+exports[`agent configuration queries getExistingEnvironmentsForService fetches unavailable environments 1`] = `
+Object {
+  "body": Object {
+    "aggs": Object {
+      "environments": Object {
+        "terms": Object {
+          "field": "service.environment",
+          "missing": "ALL_OPTION_VALUE",
+          "size": 50,
+        },
+      },
+    },
+    "query": Object {
+      "bool": Object {
+        "filter": Array [
+          Object {
+            "term": Object {
+              "service.name": "foo",
+            },
+          },
+        ],
+      },
+    },
+    "size": 0,
+  },
+  "index": "myIndex",
+}
+`;
+
+exports[`agent configuration queries getServiceNames fetches service names 1`] = `
+Object {
+  "body": Object {
+    "aggs": Object {
+      "services": Object {
+        "terms": Object {
+          "field": "service.name",
+          "size": 50,
+        },
+      },
+    },
+    "query": Object {
+      "bool": Object {
+        "filter": Array [
+          Object {
+            "terms": Object {
+              "processor.event": Array [
+                "transaction",
+                "error",
+                "metric",
+              ],
+            },
+          },
+        ],
+      },
+    },
+    "size": 0,
+  },
+  "index": Array [
+    "myIndex",
+    "myIndex",
+    "myIndex",
+  ],
+}
+`;
+
+exports[`agent configuration queries listConfigurations fetches configurations 1`] = `
 Object {
   "index": "myIndex",
   "size": 200,
 }
 `;
 
-exports[`agent configuration queries fetches filtered configurations with an environment 1`] = `
+exports[`agent configuration queries searchConfigurations fetches filtered configurations with an environment 1`] = `
 Object {
   "body": Object {
     "query": Object {
@@ -60,9 +209,7 @@ Object {
               "boost": 2,
               "filter": Object {
                 "term": Object {
-                  "service.name": Object {
-                    "value": "foo",
-                  },
+                  "service.name": "foo",
                 },
               },
             },
@@ -72,9 +219,7 @@ Object {
               "boost": 1,
               "filter": Object {
                 "term": Object {
-                  "service.environment": Object {
-                    "value": "bar",
-                  },
+                  "service.environment": "bar",
                 },
               },
             },
@@ -109,7 +254,7 @@ Object {
 }
 `;
 
-exports[`agent configuration queries fetches filtered configurations without an environment 1`] = `
+exports[`agent configuration queries searchConfigurations fetches filtered configurations without an environment 1`] = `
 Object {
   "body": Object {
     "query": Object {
@@ -121,9 +266,7 @@ Object {
               "boost": 2,
               "filter": Object {
                 "term": Object {
-                  "service.name": Object {
-                    "value": "foo",
-                  },
+                  "service.name": "foo",
                 },
               },
             },
@@ -157,68 +300,3 @@ Object {
   "index": "myIndex",
 }
 `;
-
-exports[`agent configuration queries fetches service names 1`] = `
-Object {
-  "body": Object {
-    "aggs": Object {
-      "services": Object {
-        "terms": Object {
-          "field": "service.name",
-          "size": 50,
-        },
-      },
-    },
-    "query": Object {
-      "bool": Object {
-        "filter": Array [
-          Object {
-            "terms": Object {
-              "processor.event": Array [
-                "transaction",
-                "error",
-                "metric",
-              ],
-            },
-          },
-        ],
-      },
-    },
-    "size": 0,
-  },
-  "index": Array [
-    "myIndex",
-    "myIndex",
-    "myIndex",
-  ],
-}
-`;
-
-exports[`agent configuration queries fetches unavailable environments 1`] = `
-Object {
-  "body": Object {
-    "aggs": Object {
-      "environments": Object {
-        "terms": Object {
-          "field": "service.environment",
-          "missing": "ALL_OPTION_VALUE",
-          "size": 50,
-        },
-      },
-    },
-    "query": Object {
-      "bool": Object {
-        "filter": Array [
-          Object {
-            "term": Object {
-              "service.name": "foo",
-            },
-          },
-        ],
-      },
-    },
-    "size": 0,
-  },
-  "index": "myIndex",
-}
-`;
diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/configuration_types.d.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/configuration_types.d.ts
index ea8f50c90c1d3..ddbe6892c5441 100644
--- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/configuration_types.d.ts
+++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/configuration_types.d.ts
@@ -4,18 +4,15 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-export interface AgentConfiguration {
+import t from 'io-ts';
+import { agentConfigurationIntakeRt } from '../../../../common/runtime_types/agent_configuration_intake_rt';
+
+export type AgentConfigurationIntake = t.TypeOf<
+  typeof agentConfigurationIntakeRt
+>;
+export type AgentConfiguration = {
   '@timestamp': number;
   applied_by_agent?: boolean;
   etag?: string;
   agent_name?: string;
-  service: {
-    name?: string;
-    environment?: string;
-  };
-  settings: {
-    transaction_sample_rate?: number;
-    capture_body?: string;
-    transaction_max_spans?: number;
-  };
-}
+} & AgentConfigurationIntake;
diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/create_or_update_configuration.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/create_or_update_configuration.ts
index 5a67f78de6f65..74fcc61dde863 100644
--- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/create_or_update_configuration.ts
+++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/create_or_update_configuration.ts
@@ -6,19 +6,19 @@
 
 import hash from 'object-hash';
 import { Setup } from '../../helpers/setup_request';
-import { AgentConfiguration } from './configuration_types';
+import {
+  AgentConfiguration,
+  AgentConfigurationIntake
+} from './configuration_types';
 import { APMIndexDocumentParams } from '../../helpers/es_client';
 
 export async function createOrUpdateConfiguration({
   configurationId,
-  configuration,
+  configurationIntake,
   setup
 }: {
   configurationId?: string;
-  configuration: Omit<
-    AgentConfiguration,
-    '@timestamp' | 'applied_by_agent' | 'etag'
-  >;
+  configurationIntake: AgentConfigurationIntake;
   setup: Setup;
 }) {
   const { internalClient, indices } = setup;
@@ -27,15 +27,15 @@ export async function createOrUpdateConfiguration({
     refresh: true,
     index: indices.apmAgentConfigurationIndex,
     body: {
-      agent_name: configuration.agent_name,
+      agent_name: configurationIntake.agent_name,
       service: {
-        name: configuration.service.name,
-        environment: configuration.service.environment
+        name: configurationIntake.service.name,
+        environment: configurationIntake.service.environment
       },
-      settings: configuration.settings,
+      settings: configurationIntake.settings,
       '@timestamp': Date.now(),
       applied_by_agent: false,
-      etag: hash(configuration)
+      etag: hash(configurationIntake)
     }
   };
 
diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/find_exact_configuration.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/find_exact_configuration.ts
new file mode 100644
index 0000000000000..eea409882f876
--- /dev/null
+++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/find_exact_configuration.ts
@@ -0,0 +1,46 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import {
+  SERVICE_NAME,
+  SERVICE_ENVIRONMENT
+} from '../../../../common/elasticsearch_fieldnames';
+import { Setup } from '../../helpers/setup_request';
+import { AgentConfiguration } from './configuration_types';
+import { ESSearchHit } from '../../../../typings/elasticsearch';
+
+export async function findExactConfiguration({
+  service,
+  setup
+}: {
+  service: AgentConfiguration['service'];
+  setup: Setup;
+}) {
+  const { internalClient, indices } = setup;
+
+  const serviceNameFilter = service.name
+    ? { term: { [SERVICE_NAME]: service.name } }
+    : { bool: { must_not: [{ exists: { field: SERVICE_NAME } }] } };
+
+  const environmentFilter = service.environment
+    ? { term: { [SERVICE_ENVIRONMENT]: service.environment } }
+    : { bool: { must_not: [{ exists: { field: SERVICE_ENVIRONMENT } }] } };
+
+  const params = {
+    index: indices.apmAgentConfigurationIndex,
+    body: {
+      query: {
+        bool: { filter: [serviceNameFilter, environmentFilter] }
+      }
+    }
+  };
+
+  const resp = await internalClient.search<AgentConfiguration, typeof params>(
+    params
+  );
+
+  return resp.hits.hits[0] as ESSearchHit<AgentConfiguration> | undefined;
+}
diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts
index dccf8b110d082..a9af1f6174fd5 100644
--- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts
+++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts
@@ -48,8 +48,6 @@ export async function getAgentNameByService({
   };
 
   const { aggregations } = await client.search(params);
-  const agentName = aggregations?.agent_names.buckets[0].key as
-    | string
-    | undefined;
-  return { agentName };
+  const agentName = aggregations?.agent_names.buckets[0]?.key;
+  return agentName as string | undefined;
 }
diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/queries.test.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/queries.test.ts
index a82d148781ad8..b951b7f350eed 100644
--- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/queries.test.ts
+++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/queries.test.ts
@@ -8,11 +8,12 @@ import { getAllEnvironments } from './get_environments/get_all_environments';
 import { getExistingEnvironmentsForService } from './get_environments/get_existing_environments_for_service';
 import { getServiceNames } from './get_service_names';
 import { listConfigurations } from './list_configurations';
-import { searchConfigurations } from './search';
+import { searchConfigurations } from './search_configurations';
 import {
   SearchParamsMock,
   inspectSearchParams
 } from '../../../../../../legacy/plugins/apm/public/utils/testHelpers';
+import { findExactConfiguration } from './find_exact_configuration';
 
 describe('agent configuration queries', () => {
   let mock: SearchParamsMock;
@@ -21,68 +22,117 @@ describe('agent configuration queries', () => {
     mock.teardown();
   });
 
-  it('fetches all environments', async () => {
-    mock = await inspectSearchParams(setup =>
-      getAllEnvironments({
-        serviceName: 'foo',
-        setup
-      })
-    );
+  describe('getAllEnvironments', () => {
+    it('fetches all environments', async () => {
+      mock = await inspectSearchParams(setup =>
+        getAllEnvironments({
+          serviceName: 'foo',
+          setup
+        })
+      );
 
-    expect(mock.params).toMatchSnapshot();
+      expect(mock.params).toMatchSnapshot();
+    });
   });
 
-  it('fetches unavailable environments', async () => {
-    mock = await inspectSearchParams(setup =>
-      getExistingEnvironmentsForService({
-        serviceName: 'foo',
-        setup
-      })
-    );
+  describe('getExistingEnvironmentsForService', () => {
+    it('fetches unavailable environments', async () => {
+      mock = await inspectSearchParams(setup =>
+        getExistingEnvironmentsForService({
+          serviceName: 'foo',
+          setup
+        })
+      );
 
-    expect(mock.params).toMatchSnapshot();
+      expect(mock.params).toMatchSnapshot();
+    });
   });
 
-  it('fetches service names', async () => {
-    mock = await inspectSearchParams(setup =>
-      getServiceNames({
-        setup
-      })
-    );
+  describe('getServiceNames', () => {
+    it('fetches service names', async () => {
+      mock = await inspectSearchParams(setup =>
+        getServiceNames({
+          setup
+        })
+      );
 
-    expect(mock.params).toMatchSnapshot();
+      expect(mock.params).toMatchSnapshot();
+    });
   });
 
-  it('fetches configurations', async () => {
-    mock = await inspectSearchParams(setup =>
-      listConfigurations({
-        setup
-      })
-    );
+  describe('listConfigurations', () => {
+    it('fetches configurations', async () => {
+      mock = await inspectSearchParams(setup =>
+        listConfigurations({
+          setup
+        })
+      );
 
-    expect(mock.params).toMatchSnapshot();
+      expect(mock.params).toMatchSnapshot();
+    });
   });
 
-  it('fetches filtered configurations without an environment', async () => {
-    mock = await inspectSearchParams(setup =>
-      searchConfigurations({
-        serviceName: 'foo',
-        setup
-      })
-    );
+  describe('searchConfigurations', () => {
+    it('fetches filtered configurations without an environment', async () => {
+      mock = await inspectSearchParams(setup =>
+        searchConfigurations({
+          service: {
+            name: 'foo'
+          },
+          setup
+        })
+      );
 
-    expect(mock.params).toMatchSnapshot();
+      expect(mock.params).toMatchSnapshot();
+    });
+
+    it('fetches filtered configurations with an environment', async () => {
+      mock = await inspectSearchParams(setup =>
+        searchConfigurations({
+          service: {
+            name: 'foo',
+            environment: 'bar'
+          },
+          setup
+        })
+      );
+
+      expect(mock.params).toMatchSnapshot();
+    });
   });
 
-  it('fetches filtered configurations with an environment', async () => {
-    mock = await inspectSearchParams(setup =>
-      searchConfigurations({
-        serviceName: 'foo',
-        environment: 'bar',
-        setup
-      })
-    );
+  describe('findExactConfiguration', () => {
+    it('find configuration by service.name', async () => {
+      mock = await inspectSearchParams(setup =>
+        findExactConfiguration({
+          service: { name: 'foo' },
+          setup
+        })
+      );
+
+      expect(mock.params).toMatchSnapshot();
+    });
+
+    it('find configuration by service.environment', async () => {
+      mock = await inspectSearchParams(setup =>
+        findExactConfiguration({
+          service: { environment: 'bar' },
+          setup
+        })
+      );
+
+      expect(mock.params).toMatchSnapshot();
+    });
+
+    it('find configuration by service.name and service.environment', async () => {
+      mock = await inspectSearchParams(setup =>
+        findExactConfiguration({
+          service: { name: 'foo', environment: 'bar' },
+          setup
+        })
+      );
 
-    expect(mock.params).toMatchSnapshot();
+      expect(mock.params).toMatchSnapshot();
+    });
   });
 });
diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/search.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/search_configurations.ts
similarity index 68%
rename from x-pack/plugins/apm/server/lib/settings/agent_configuration/search.ts
rename to x-pack/plugins/apm/server/lib/settings/agent_configuration/search_configurations.ts
index 766baead006b6..9bbdc96a3a797 100644
--- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/search.ts
+++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/search_configurations.ts
@@ -12,29 +12,39 @@ import { Setup } from '../../helpers/setup_request';
 import { AgentConfiguration } from './configuration_types';
 
 export async function searchConfigurations({
-  serviceName,
-  environment,
+  service,
   setup
 }: {
-  serviceName: string;
-  environment?: string;
+  service: AgentConfiguration['service'];
   setup: Setup;
 }) {
   const { internalClient, indices } = setup;
-  const environmentFilter = environment
+
+  // In the following `constant_score` is being used to disable IDF calculation (where frequency of a term influences scoring).
+  // Additionally a boost has been added to service.name to ensure it scores higher.
+  // If there is tie between a config with a matching service.name and a config with a matching environment, the config that matches service.name wins
+  const serviceNameFilter = service.name
+    ? [
+        {
+          constant_score: {
+            filter: { term: { [SERVICE_NAME]: service.name } },
+            boost: 2
+          }
+        }
+      ]
+    : [];
+
+  const environmentFilter = service.environment
     ? [
         {
           constant_score: {
-            filter: { term: { [SERVICE_ENVIRONMENT]: { value: environment } } },
+            filter: { term: { [SERVICE_ENVIRONMENT]: service.environment } },
             boost: 1
           }
         }
       ]
     : [];
 
-  // In the following `constant_score` is being used to disable IDF calculation (where frequency of a term influences scoring)
-  // Additionally a boost has been added to service.name to ensure it scores higher
-  // if there is tie between a config with a matching service.name and a config with a matching environment
   const params = {
     index: indices.apmAgentConfigurationIndex,
     body: {
@@ -42,12 +52,7 @@ export async function searchConfigurations({
         bool: {
           minimum_should_match: 2,
           should: [
-            {
-              constant_score: {
-                filter: { term: { [SERVICE_NAME]: { value: serviceName } } },
-                boost: 2
-              }
-            },
+            ...serviceNameFilter,
             ...environmentFilter,
             { bool: { must_not: [{ exists: { field: SERVICE_NAME } }] } },
             { bool: { must_not: [{ exists: { field: SERVICE_ENVIRONMENT } }] } }
diff --git a/x-pack/plugins/apm/server/routes/create_apm_api.ts b/x-pack/plugins/apm/server/routes/create_apm_api.ts
index f65e271389938..21392edbb2c48 100644
--- a/x-pack/plugins/apm/server/routes/create_apm_api.ts
+++ b/x-pack/plugins/apm/server/routes/create_apm_api.ts
@@ -23,11 +23,10 @@ import {
 import {
   agentConfigurationRoute,
   agentConfigurationSearchRoute,
-  createAgentConfigurationRoute,
   deleteAgentConfigurationRoute,
   listAgentConfigurationEnvironmentsRoute,
   listAgentConfigurationServicesRoute,
-  updateAgentConfigurationRoute,
+  createOrUpdateAgentConfigurationRoute,
   agentConfigurationAgentNameRoute
 } from './settings/agent_configuration';
 import {
@@ -83,11 +82,10 @@ const createApmApi = () => {
     .add(agentConfigurationAgentNameRoute)
     .add(agentConfigurationRoute)
     .add(agentConfigurationSearchRoute)
-    .add(createAgentConfigurationRoute)
     .add(deleteAgentConfigurationRoute)
     .add(listAgentConfigurationEnvironmentsRoute)
     .add(listAgentConfigurationServicesRoute)
-    .add(updateAgentConfigurationRoute)
+    .add(createOrUpdateAgentConfigurationRoute)
 
     // APM indices
     .add(apmIndexSettingsRoute)
diff --git a/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts b/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts
index ddd6a27025131..83b845b1fc436 100644
--- a/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts
+++ b/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts
@@ -9,15 +9,19 @@ import Boom from 'boom';
 import { setupRequest } from '../../lib/helpers/setup_request';
 import { getServiceNames } from '../../lib/settings/agent_configuration/get_service_names';
 import { createOrUpdateConfiguration } from '../../lib/settings/agent_configuration/create_or_update_configuration';
-import { searchConfigurations } from '../../lib/settings/agent_configuration/search';
+import { searchConfigurations } from '../../lib/settings/agent_configuration/search_configurations';
+import { findExactConfiguration } from '../../lib/settings/agent_configuration/find_exact_configuration';
 import { listConfigurations } from '../../lib/settings/agent_configuration/list_configurations';
 import { getEnvironments } from '../../lib/settings/agent_configuration/get_environments';
 import { deleteConfiguration } from '../../lib/settings/agent_configuration/delete_configuration';
 import { createRoute } from '../create_route';
-import { transactionSampleRateRt } from '../../../common/runtime_types/transaction_sample_rate_rt';
-import { transactionMaxSpansRt } from '../../../common/runtime_types/transaction_max_spans_rt';
 import { getAgentNameByService } from '../../lib/settings/agent_configuration/get_agent_name_by_service';
 import { markAppliedByAgent } from '../../lib/settings/agent_configuration/mark_applied_by_agent';
+import {
+  serviceRt,
+  agentConfigurationIntakeRt
+} from '../../../common/runtime_types/agent_configuration_intake_rt';
+import { jsonRt } from '../../../common/runtime_types/json_rt';
 
 // get list of configurations
 export const agentConfigurationRoute = createRoute(core => ({
@@ -31,20 +35,34 @@ export const agentConfigurationRoute = createRoute(core => ({
 // delete configuration
 export const deleteAgentConfigurationRoute = createRoute(() => ({
   method: 'DELETE',
-  path: '/api/apm/settings/agent-configuration/{configurationId}',
+  path: '/api/apm/settings/agent-configuration',
   options: {
     tags: ['access:apm', 'access:apm_write']
   },
   params: {
-    path: t.type({
-      configurationId: t.string
+    body: t.type({
+      service: serviceRt
     })
   },
   handler: async ({ context, request }) => {
     const setup = await setupRequest(context, request);
-    const { configurationId } = context.params.path;
+    const { service } = context.params.body;
+
+    const config = await findExactConfiguration({ service, setup });
+    if (!config) {
+      context.logger.info(
+        `Config was not found for ${service.name}/${service.environment}`
+      );
+
+      throw Boom.notFound();
+    }
+
+    context.logger.info(
+      `Deleting config ${service.name}/${service.environment} (${config._id})`
+    );
+
     return await deleteConfiguration({
-      configurationId,
+      configurationId: config._id,
       setup
     });
   }
@@ -62,23 +80,6 @@ export const listAgentConfigurationServicesRoute = createRoute(() => ({
   }
 }));
 
-const agentPayloadRt = t.intersection([
-  t.partial({ agent_name: t.string }),
-  t.type({
-    service: t.intersection([
-      t.partial({ name: t.string }),
-      t.partial({ environment: t.string })
-    ])
-  }),
-  t.type({
-    settings: t.intersection([
-      t.partial({ transaction_sample_rate: transactionSampleRateRt }),
-      t.partial({ capture_body: t.string }),
-      t.partial({ transaction_max_spans: transactionMaxSpansRt })
-    ])
-  })
-]);
-
 // get environments for service
 export const listAgentConfigurationEnvironmentsRoute = createRoute(() => ({
   path: '/api/apm/settings/agent-configuration/environments',
@@ -102,55 +103,47 @@ export const agentConfigurationAgentNameRoute = createRoute(() => ({
     const setup = await setupRequest(context, request);
     const { serviceName } = context.params.query;
     const agentName = await getAgentNameByService({ serviceName, setup });
-    return agentName;
+    return { agentName };
   }
 }));
 
-export const createAgentConfigurationRoute = createRoute(() => ({
-  method: 'POST',
-  path: '/api/apm/settings/agent-configuration/new',
-  params: {
-    body: agentPayloadRt
-  },
+export const createOrUpdateAgentConfigurationRoute = createRoute(() => ({
+  method: 'PUT',
+  path: '/api/apm/settings/agent-configuration',
   options: {
     tags: ['access:apm', 'access:apm_write']
   },
+  params: {
+    query: t.partial({ overwrite: jsonRt.pipe(t.boolean) }),
+    body: agentConfigurationIntakeRt
+  },
   handler: async ({ context, request }) => {
     const setup = await setupRequest(context, request);
-    const configuration = context.params.body;
+    const { body, query } = context.params;
 
-    // TODO: Remove logger. Only added temporarily to debug flaky test (https://github.com/elastic/kibana/issues/51764)
-    context.logger.info(
-      `Hitting: /api/apm/settings/agent-configuration/new with ${configuration.service.name}/${configuration.service.environment}`
-    );
-    const res = await createOrUpdateConfiguration({
-      configuration,
+    // if the config already exists, it is fetched and updated
+    // this is to avoid creating two configs with identical service params
+    const config = await findExactConfiguration({
+      service: body.service,
       setup
     });
-    context.logger.info(`Created agent configuration`);
 
-    return res;
-  }
-}));
+    // if the config exists ?overwrite=true is required
+    if (config && !query.overwrite) {
+      throw Boom.badRequest(
+        `A configuration already exists for "${body.service.name}/${body.service.environment}. Use ?overwrite=true to overwrite the existing configuration.`
+      );
+    }
+
+    context.logger.info(
+      `${config ? 'Updating' : 'Creating'} config ${body.service.name}/${
+        body.service.environment
+      }`
+    );
 
-export const updateAgentConfigurationRoute = createRoute(() => ({
-  method: 'PUT',
-  path: '/api/apm/settings/agent-configuration/{configurationId}',
-  options: {
-    tags: ['access:apm', 'access:apm_write']
-  },
-  params: {
-    path: t.type({
-      configurationId: t.string
-    }),
-    body: agentPayloadRt
-  },
-  handler: async ({ context, request }) => {
-    const setup = await setupRequest(context, request);
-    const { configurationId } = context.params.path;
     return await createOrUpdateConfiguration({
-      configurationId,
-      configuration: context.params.body,
+      configurationId: config?._id,
+      configurationIntake: body,
       setup
     });
   }
@@ -162,41 +155,33 @@ export const agentConfigurationSearchRoute = createRoute(core => ({
   path: '/api/apm/settings/agent-configuration/search',
   params: {
     body: t.type({
-      service: t.intersection([
-        t.type({ name: t.string }),
-        t.partial({ environment: t.string })
-      ]),
+      service: serviceRt,
       etag: t.string
     })
   },
   handler: async ({ context, request }) => {
-    const { body } = context.params;
-
-    // TODO: Remove logger. Only added temporarily to debug flaky test (https://github.com/elastic/kibana/issues/51764)
-    context.logger.info(
-      `Hitting: /api/apm/settings/agent-configuration/search for ${body.service.name}/${body.service.environment}`
-    );
+    const { service, etag } = context.params.body;
 
     const setup = await setupRequest(context, request);
     const config = await searchConfigurations({
-      serviceName: body.service.name,
-      environment: body.service.environment,
+      service,
       setup
     });
 
     if (!config) {
       context.logger.info(
-        `Config was not found for ${body.service.name}/${body.service.environment}`
+        `Config was not found for ${service.name}/${service.environment}`
       );
-      throw new Boom('Not found', { statusCode: 404 });
+      throw Boom.notFound();
     }
 
     context.logger.info(
-      `Config was found for ${body.service.name}/${body.service.environment}`
+      `Config was found for ${service.name}/${service.environment}`
     );
 
     // update `applied_by_agent` field if etags match
-    if (body.etag === config._source.etag && !config._source.applied_by_agent) {
+    // this happens in the background and doesn't block the response
+    if (etag === config._source.etag && !config._source.applied_by_agent) {
       markAppliedByAgent({ id: config._id, body: config._source, setup });
     }
 
diff --git a/x-pack/test/api_integration/apis/apm/agent_configuration.ts b/x-pack/test/api_integration/apis/apm/agent_configuration.ts
index 12e08869fa586..959a0c97acfa3 100644
--- a/x-pack/test/api_integration/apis/apm/agent_configuration.ts
+++ b/x-pack/test/api_integration/apis/apm/agent_configuration.ts
@@ -5,6 +5,7 @@
  */
 
 import expect from '@kbn/expect';
+import { AgentConfigurationIntake } from '../../../../plugins/apm/server/lib/settings/agent_configuration/configuration_types';
 import { FtrProviderContext } from '../../ftr_provider_context';
 
 export default function agentConfigurationTests({ getService }: FtrProviderContext) {
@@ -18,108 +19,122 @@ export default function agentConfigurationTests({ getService }: FtrProviderConte
       .set('kbn-xsrf', 'foo');
   }
 
-  let createdConfigIds: any[] = [];
-  async function createConfiguration(configuration: any) {
+  async function createConfiguration(config: AgentConfigurationIntake) {
+    log.debug('creating configuration', config.service);
     const res = await supertest
-      .post(`/api/apm/settings/agent-configuration/new`)
-      .send(configuration)
+      .put(`/api/apm/settings/agent-configuration`)
+      .send(config)
       .set('kbn-xsrf', 'foo');
 
-    createdConfigIds.push(res.body._id);
+    throwOnError(res);
 
     return res;
   }
 
-  function deleteCreatedConfigurations() {
-    const promises = Promise.all(createdConfigIds.map(deleteConfiguration));
-    return promises;
+  async function updateConfiguration(config: AgentConfigurationIntake) {
+    log.debug('updating configuration', config.service);
+    const res = await supertest
+      .put(`/api/apm/settings/agent-configuration?overwrite=true`)
+      .send(config)
+      .set('kbn-xsrf', 'foo');
+
+    throwOnError(res);
+
+    return res;
   }
 
-  function deleteConfiguration(configurationId: string) {
-    return supertest
-      .delete(`/api/apm/settings/agent-configuration/${configurationId}`)
-      .set('kbn-xsrf', 'foo')
-      .then((response: any) => {
-        createdConfigIds = createdConfigIds.filter(id => id === configurationId);
-        return response;
-      });
+  async function deleteConfiguration({ service }: AgentConfigurationIntake) {
+    log.debug('deleting configuration', service);
+    const res = await supertest
+      .delete(`/api/apm/settings/agent-configuration`)
+      .send({ service })
+      .set('kbn-xsrf', 'foo');
+
+    throwOnError(res);
+
+    return res;
+  }
+
+  function throwOnError(res: any) {
+    const { statusCode, req, body } = res;
+    if (statusCode !== 200) {
+      throw new Error(`
+      Endpoint: ${req.method} ${req.path}
+      Service: ${JSON.stringify(res.request._data.service)}
+      Status code: ${statusCode}
+      Response: ${body.message}`);
+    }
   }
 
   describe('agent configuration', () => {
     describe('when creating one configuration', () => {
-      let createdConfigId: string;
+      const newConfig = {
+        service: {},
+        settings: { transaction_sample_rate: 0.55 },
+      };
 
-      const parameters = {
+      const searchParams = {
         service: { name: 'myservice', environment: 'development' },
         etag: '7312bdcc34999629a3d39df24ed9b2a7553c0c39',
       };
 
       before(async () => {
-        log.debug('creating agent configuration');
-
-        // all / all
-        const { body } = await createConfiguration({
-          service: {},
-          settings: { transaction_sample_rate: 0.1 },
-        });
-
-        createdConfigId = body._id;
+        await createConfiguration(newConfig);
       });
 
-      it('returns the created configuration', async () => {
-        const { statusCode, body } = await searchConfigurations(parameters);
-
+      it('can find the created config', async () => {
+        const { statusCode, body } = await searchConfigurations(searchParams);
         expect(statusCode).to.equal(200);
-        expect(body._id).to.equal(createdConfigId);
+        expect(body._source.service).to.eql({});
+        expect(body._source.settings).to.eql({ transaction_sample_rate: 0.55 });
       });
 
-      it('succesfully deletes the configuration', async () => {
-        await deleteConfiguration(createdConfigId);
+      it('can update the created config', async () => {
+        await updateConfiguration({ service: {}, settings: { transaction_sample_rate: 0.85 } });
 
-        const { statusCode } = await searchConfigurations(parameters);
+        const { statusCode, body } = await searchConfigurations(searchParams);
+        expect(statusCode).to.equal(200);
+        expect(body._source.service).to.eql({});
+        expect(body._source.settings).to.eql({ transaction_sample_rate: 0.85 });
+      });
 
+      it('can delete the created config', async () => {
+        await deleteConfiguration(newConfig);
+        const { statusCode } = await searchConfigurations(searchParams);
         expect(statusCode).to.equal(404);
       });
     });
 
-    describe('when creating four configurations', () => {
-      before(async () => {
-        log.debug('creating agent configuration');
-
-        // all / all
-        await createConfiguration({
+    describe('when creating multiple configurations', () => {
+      const configs = [
+        {
           service: {},
           settings: { transaction_sample_rate: 0.1 },
-        });
-
-        // my_service / all
-        await createConfiguration({
+        },
+        {
           service: { name: 'my_service' },
           settings: { transaction_sample_rate: 0.2 },
-        });
-
-        // all / production
-        await createConfiguration({
-          service: { environment: 'production' },
+        },
+        {
+          service: { name: 'my_service', environment: 'development' },
           settings: { transaction_sample_rate: 0.3 },
-        });
-
-        // all / production
-        await createConfiguration({
-          service: { environment: 'development' },
+        },
+        {
+          service: { environment: 'production' },
           settings: { transaction_sample_rate: 0.4 },
-        });
-
-        // my_service / production
-        await createConfiguration({
-          service: { name: 'my_service', environment: 'development' },
+        },
+        {
+          service: { environment: 'development' },
           settings: { transaction_sample_rate: 0.5 },
-        });
+        },
+      ];
+
+      before(async () => {
+        await Promise.all(configs.map(config => createConfiguration(config)));
       });
 
       after(async () => {
-        log.debug('deleting agent configurations');
-        await deleteCreatedConfigurations();
+        await Promise.all(configs.map(config => deleteConfiguration(config)));
       });
 
       const agentsRequests = [
@@ -127,20 +142,24 @@ export default function agentConfigurationTests({ getService }: FtrProviderConte
           service: { name: 'non_existing_service', environment: 'non_existing_env' },
           expectedSettings: { transaction_sample_rate: 0.1 },
         },
+        {
+          service: { name: 'my_service', environment: 'non_existing_env' },
+          expectedSettings: { transaction_sample_rate: 0.2 },
+        },
         {
           service: { name: 'my_service', environment: 'production' },
           expectedSettings: { transaction_sample_rate: 0.2 },
         },
         {
-          service: { name: 'non_existing_service', environment: 'production' },
+          service: { name: 'my_service', environment: 'development' },
           expectedSettings: { transaction_sample_rate: 0.3 },
         },
         {
-          service: { name: 'non_existing_service', environment: 'development' },
+          service: { name: 'non_existing_service', environment: 'production' },
           expectedSettings: { transaction_sample_rate: 0.4 },
         },
         {
-          service: { name: 'my_service', environment: 'development' },
+          service: { name: 'non_existing_service', environment: 'development' },
           expectedSettings: { transaction_sample_rate: 0.5 },
         },
       ];
@@ -159,18 +178,18 @@ export default function agentConfigurationTests({ getService }: FtrProviderConte
     });
 
     describe('when an agent retrieves a configuration', () => {
+      const config = {
+        service: { name: 'myservice', environment: 'development' },
+        settings: { transaction_sample_rate: 0.9 },
+      };
+
       before(async () => {
         log.debug('creating agent configuration');
-
-        await createConfiguration({
-          service: { name: 'myservice', environment: 'development' },
-          settings: { transaction_sample_rate: 0.9 },
-        });
+        await createConfiguration(config);
       });
 
       after(async () => {
-        log.debug('deleting agent configurations');
-        await deleteCreatedConfigurations();
+        await deleteConfiguration(config);
       });
 
       it(`should have 'applied_by_agent=false' on first request`, async () => {
diff --git a/x-pack/test/api_integration/apis/apm/feature_controls.ts b/x-pack/test/api_integration/apis/apm/feature_controls.ts
index ec2bbca23ddd2..3c5314d0d3261 100644
--- a/x-pack/test/api_integration/apis/apm/feature_controls.ts
+++ b/x-pack/test/api_integration/apis/apm/feature_controls.ts
@@ -4,8 +4,6 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-/* eslint-disable no-console */
-
 import expect from '@kbn/expect';
 import { FtrProviderContext } from '../../ftr_provider_context';
 
@@ -14,8 +12,8 @@ export default function featureControlsTests({ getService }: FtrProviderContext)
   const supertestWithoutAuth = getService('supertestWithoutAuth');
   const security = getService('security');
   const spaces = getService('spaces');
-  const log = getService('log');
   const es = getService('legacyEs');
+  const log = getService('log');
 
   const start = encodeURIComponent(new Date(Date.now() - 10000).toISOString());
   const end = encodeURIComponent(new Date().toISOString());
@@ -33,7 +31,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext)
   interface Endpoint {
     req: {
       url: string;
-      method?: 'get' | 'post' | 'delete';
+      method?: 'get' | 'post' | 'delete' | 'put';
       body?: any;
     };
     expectForbidden: (result: any) => void;
@@ -148,7 +146,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext)
           index: '.apm-agent-configuration',
         });
 
-        console.warn(JSON.stringify(res, null, 2));
+        log.error(JSON.stringify(res, null, 2));
       },
     },
   ];
@@ -196,7 +194,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext)
 
     const { statusCode, req } = response;
     if (statusCode !== 200) {
-      log.debug(`Endpoint: ${req.method} ${req.path}
+      throw new Error(`Endpoint: ${req.method} ${req.path}
       Status code: ${statusCode}
       Response: ${response.body.message}`);
     }
@@ -216,9 +214,9 @@ export default function featureControlsTests({ getService }: FtrProviderContext)
     spaceId?: string;
   }) {
     for (const endpoint of endpoints) {
-      console.log(`Requesting: ${endpoint.req.url}. Expecting: ${expectation}`);
+      log.info(`Requesting: ${endpoint.req.url}. Expecting: ${expectation}`);
       const result = await executeAsUser(endpoint.req, username, password, spaceId);
-      console.log(`Responded: ${endpoint.req.url}`);
+      log.info(`Responded: ${endpoint.req.url}`);
 
       try {
         if (expectation === 'forbidden') {
@@ -244,26 +242,28 @@ export default function featureControlsTests({ getService }: FtrProviderContext)
   }
 
   describe('apm feature controls', () => {
-    let res: any;
+    const config = {
+      service: { name: 'test-service' },
+      settings: { transaction_sample_rate: 0.5 },
+    };
     before(async () => {
-      console.log(`Creating agent configuration`);
-      res = await executeAsAdmin({
-        method: 'post',
-        url: '/api/apm/settings/agent-configuration/new',
-        body: {
-          service: { name: 'test-service' },
-          settings: { transaction_sample_rate: 0.5 },
-        },
+      log.info(`Creating agent configuration`);
+      await executeAsAdmin({
+        method: 'put',
+        url: '/api/apm/settings/agent-configuration',
+        body: config,
       });
-      console.log(`Agent configuration created`);
+      log.info(`Agent configuration created`);
     });
 
     after(async () => {
-      console.log('deleting agent configuration');
-      const configurationId = res.body._id;
+      log.info('deleting agent configuration');
       await executeAsAdmin({
         method: 'delete',
-        url: `/api/apm/settings/agent-configuration/${configurationId}`,
+        url: `/api/apm/settings/agent-configuration`,
+        body: {
+          service: config.service,
+        },
       });
     });
 
diff --git a/x-pack/test/api_integration/apis/apm/index.ts b/x-pack/test/api_integration/apis/apm/index.ts
index c49d577537048..6f41f4abfecc3 100644
--- a/x-pack/test/api_integration/apis/apm/index.ts
+++ b/x-pack/test/api_integration/apis/apm/index.ts
@@ -7,7 +7,7 @@
 import { FtrProviderContext } from '../../ftr_provider_context';
 
 export default function apmApiIntegrationTests({ loadTestFile }: FtrProviderContext) {
-  describe('APM', () => {
+  describe('APM specs', () => {
     loadTestFile(require.resolve('./feature_controls'));
     loadTestFile(require.resolve('./agent_configuration'));
   });
diff --git a/x-pack/test/functional/apps/apm/index.ts b/x-pack/test/functional/apps/apm/index.ts
index 945af09183f03..bf254f9b9b419 100644
--- a/x-pack/test/functional/apps/apm/index.ts
+++ b/x-pack/test/functional/apps/apm/index.ts
@@ -6,7 +6,7 @@
 import { FtrProviderContext } from '../../ftr_provider_context';
 
 export default function({ loadTestFile }: FtrProviderContext) {
-  describe('APM', function() {
+  describe('APM specs', function() {
     this.tags('ciGroup6');
     loadTestFile(require.resolve('./feature_controls'));
   });

From 5eefdbb84266e2af1998c35d4876f18bc13d1fe0 Mon Sep 17 00:00:00 2001
From: Andrew Cholakian <andrew@andrewvc.com>
Date: Mon, 24 Feb 2020 16:46:58 -0600
Subject: [PATCH 156/174] [Uptime] Use scripted metric for snapshot calculation
 (#58247) (#58389)

Fixes #58079

This is an improved version of #58078

Note, this is a bugfix targeting 7.6.1 . I've decided to open this PR directly against 7.6 in the interest of time. We can forward-port this to 7.x / master later.

This patch improves the handling of timespans with snapshot counts. This feature originally worked, but suffered a regression when we increased the default timespan in the query context to 5m. This means that without this patch the counts you get are the maximum total number of monitors that were down over the past 5m, which is not really that useful.

We now use a scripted metric to always count precisely the number of up/down monitors. On my box this could process 400k summary docs in ~600ms. This should scale as shards are added.

I attempted to keep memory usage relatively slow by using simple maps of strings.
---
 .../lib/requests/get_snapshot_counts.ts       | 194 +++++++++++++-----
 .../apis/uptime/rest/snapshot.ts              | 105 +++++-----
 2 files changed, 192 insertions(+), 107 deletions(-)

diff --git a/x-pack/legacy/plugins/uptime/server/lib/requests/get_snapshot_counts.ts b/x-pack/legacy/plugins/uptime/server/lib/requests/get_snapshot_counts.ts
index 6236971146015..8d84c0f4d6769 100644
--- a/x-pack/legacy/plugins/uptime/server/lib/requests/get_snapshot_counts.ts
+++ b/x-pack/legacy/plugins/uptime/server/lib/requests/get_snapshot_counts.ts
@@ -6,7 +6,7 @@
 
 import { UMElasticsearchQueryFn } from '../adapters';
 import { Snapshot } from '../../../common/runtime_types';
-import { QueryContext, MonitorGroupIterator } from './search';
+import { QueryContext } from './search';
 import { CONTEXT_DEFAULTS, INDEX_NAMES } from '../../../common/constants';
 
 export interface GetSnapshotCountParams {
@@ -16,49 +16,6 @@ export interface GetSnapshotCountParams {
   statusFilter?: string;
 }
 
-const fastStatusCount = async (context: QueryContext): Promise<Snapshot> => {
-  const params = {
-    index: INDEX_NAMES.HEARTBEAT,
-    body: {
-      size: 0,
-      query: { bool: { filter: await context.dateAndCustomFilters() } },
-      aggs: {
-        unique: {
-          // We set the precision threshold to 40k which is the max precision supported by cardinality
-          cardinality: { field: 'monitor.id', precision_threshold: 40000 },
-        },
-        down: {
-          filter: { range: { 'summary.down': { gt: 0 } } },
-          aggs: {
-            unique: { cardinality: { field: 'monitor.id', precision_threshold: 40000 } },
-          },
-        },
-      },
-    },
-  };
-
-  const statistics = await context.search(params);
-  const total = statistics.aggregations.unique.value;
-  const down = statistics.aggregations.down.unique.value;
-
-  return {
-    total,
-    down,
-    up: total - down,
-  };
-};
-
-const slowStatusCount = async (context: QueryContext, status: string): Promise<number> => {
-  const downContext = context.clone();
-  downContext.statusFilter = status;
-  const iterator = new MonitorGroupIterator(downContext);
-  let count = 0;
-  while (await iterator.next()) {
-    count++;
-  }
-  return count;
-};
-
 export const getSnapshotCount: UMElasticsearchQueryFn<GetSnapshotCountParams, Snapshot> = async ({
   callES,
   dateRangeStart,
@@ -81,18 +38,7 @@ export const getSnapshotCount: UMElasticsearchQueryFn<GetSnapshotCountParams, Sn
   );
 
   // Calculate the total, up, and down counts.
-  const counts = await fastStatusCount(context);
-
-  // Check if the last count was accurate, if not, we need to perform a slower count with the
-  // MonitorGroupsIterator.
-  if (!(await context.hasTimespan())) {
-    // Figure out whether 'up' or 'down' is more common. It's faster to count the lower cardinality
-    // one then use subtraction to figure out its opposite.
-    const [leastCommonStatus, mostCommonStatus]: Array<'up' | 'down'> =
-      counts.up > counts.down ? ['down', 'up'] : ['up', 'down'];
-    counts[leastCommonStatus] = await slowStatusCount(context, leastCommonStatus);
-    counts[mostCommonStatus] = counts.total - counts[leastCommonStatus];
-  }
+  const counts = await statusCount(context);
 
   return {
     total: statusFilter ? counts[statusFilter] : counts.total,
@@ -100,3 +46,139 @@ export const getSnapshotCount: UMElasticsearchQueryFn<GetSnapshotCountParams, Sn
     down: statusFilter === 'up' ? 0 : counts.down,
   };
 };
+
+const statusCount = async (context: QueryContext): Promise<Snapshot> => {
+  const res = await context.search({
+    index: INDEX_NAMES.HEARTBEAT,
+    body: statusCountBody(await context.dateAndCustomFilters()),
+  });
+
+  return res.aggregations.counts.value;
+};
+
+const statusCountBody = (filters: any): any => {
+  return {
+    size: 0,
+    query: {
+      bool: {
+        filter: [
+          {
+            exists: {
+              field: 'summary',
+            },
+          },
+          filters,
+        ],
+      },
+    },
+    aggs: {
+      counts: {
+        scripted_metric: {
+          init_script: 'state.locStatus = new HashMap(); state.totalDocs = 0;',
+          map_script: `
+          def loc = doc["observer.geo.name"].size() == 0 ? "" : doc["observer.geo.name"][0];
+
+          // One concern here is memory since we could build pretty gigantic maps. I've opted to
+          // stick to a simple <String,String> map to reduce memory overhead. This means we do
+          // a little string parsing to treat these strings as records that stay lexicographically
+          // sortable (which is important later).
+          // We encode the ID and location as $id.len:$id$loc
+          String id = doc["monitor.id"][0];
+          String idLenDelim = Integer.toHexString(id.length()) + ":" + id;
+          String idLoc = loc == null ? idLenDelim : idLenDelim + loc;
+          
+          String status = doc["summary.down"][0] > 0 ? "d" : "u";
+          String timeAndStatus = doc["@timestamp"][0].toInstant().toEpochMilli().toString() + status;
+          state.locStatus[idLoc] = timeAndStatus;
+          state.totalDocs++;
+        `,
+          combine_script: `
+          return state;
+        `,
+          reduce_script: `
+          // Use a treemap since it's traversable in sorted order.
+          // This is important later.
+          TreeMap locStatus = new TreeMap();
+          long totalDocs = 0;
+          int uniqueIds = 0;
+          for (state in states) {
+            totalDocs += state.totalDocs;
+            for (entry in state.locStatus.entrySet()) {
+              // Update the value for the given key if we have a more recent check from this location.
+              locStatus.merge(entry.getKey(), entry.getValue(), (a,b) -> a.compareTo(b) > 0 ? a : b)
+            }
+          }
+          
+          HashMap locTotals = new HashMap();
+          int total = 0;
+          int down = 0;
+          String curId = "";
+          boolean curIdDown = false;
+          // We now iterate through our tree map in order, which means records for a given ID
+          // always are encountered one after the other. This saves us having to make an intermediate
+          // map.
+          for (entry in locStatus.entrySet()) {
+            String idLoc = entry.getKey();
+            String timeStatus = entry.getValue();
+
+            // Parse the length delimited id/location strings described in the map section
+            int colonIndex = idLoc.indexOf(":");
+            int idEnd = Integer.parseInt(idLoc.substring(0, colonIndex), 16) + colonIndex + 1;
+            String id = idLoc.substring(colonIndex + 1, idEnd);
+            String loc = idLoc.substring(idEnd, idLoc.length());
+            String status = timeStatus.substring(timeStatus.length() - 1);
+            
+            // Here we increment counters for the up/down key per location
+            // We also create a new hashmap in locTotals if we've never seen this location
+            // before.
+            locTotals.compute(loc, (k,v) -> {
+              HashMap res = v;
+              if (v == null) {
+                res = new HashMap();
+                res.put('up', 0);
+                res.put('down', 0);
+              }
+              
+              if (status == 'u') {
+                res.up++;
+              } else {
+                res.down++;
+              }
+
+              return res;
+            });
+            
+            
+            // We've encountered a new ID
+            if (curId != id) {
+              total++;
+              curId = id;
+              if (status == "d") {
+                curIdDown = true;
+                down++;
+              } else {
+                curIdDown = false;
+              }
+            } else if (!curIdDown) {
+              if (status == "d") {
+                curIdDown = true;
+                down++;
+              } else {
+                curIdDown = false;
+              }
+            }
+          }
+          
+          Map result = new HashMap();
+          result.total = total;
+          result.location_totals = locTotals;
+          result.up = total - down;
+          result.down = down;
+          result.totalDocs = totalDocs;
+          return result;
+        `,
+        },
+      },
+    },
+  };
+};
diff --git a/x-pack/test/api_integration/apis/uptime/rest/snapshot.ts b/x-pack/test/api_integration/apis/uptime/rest/snapshot.ts
index b0d97837c770f..20fe59d149ae8 100644
--- a/x-pack/test/api_integration/apis/uptime/rest/snapshot.ts
+++ b/x-pack/test/api_integration/apis/uptime/rest/snapshot.ts
@@ -34,66 +34,69 @@ export default function({ getService }: FtrProviderContext) {
       let dateRange: { start: string; end: string };
 
       [true, false].forEach(async (includeTimespan: boolean) => {
-        describe(`with timespans ${includeTimespan ? 'included' : 'missing'}`, async () => {
-          before(async () => {
-            const promises: Array<Promise<any>> = [];
-
-            // When includeTimespan is false we have to remove the values there.
-            let mogrify = (d: any) => d;
-            if ((includeTimespan = false)) {
-              mogrify = (d: any): any => {
-                d.monitor.delete('timespan');
+        [true, false].forEach(async (includeObserver: boolean) => {
+          describe(`with timespans=${includeTimespan} and observer=${includeObserver}`, async () => {
+            before(async () => {
+              const promises: Array<Promise<any>> = [];
+
+              const mogrify = (d: any) => {
+                if (!includeTimespan) {
+                  delete d.monitor.timespan;
+                }
+                if (!includeObserver) {
+                  delete d.observer;
+                }
                 return d;
               };
-            }
-
-            const makeMonitorChecks = async (monitorId: string, status: 'up' | 'down') => {
-              return makeChecksWithStatus(
-                getService('legacyEs'),
-                monitorId,
-                checksPerMonitor,
-                numIps,
-                scheduleEvery,
-                {},
-                status,
-                mogrify
-              );
-            };
 
-            for (let i = 0; i < numUpMonitors; i++) {
-              promises.push(makeMonitorChecks(`up-${i}`, 'up'));
-            }
-            for (let i = 0; i < numDownMonitors; i++) {
-              promises.push(makeMonitorChecks(`down-${i}`, 'down'));
-            }
+              const makeMonitorChecks = async (monitorId: string, status: 'up' | 'down') => {
+                return makeChecksWithStatus(
+                  getService('legacyEs'),
+                  monitorId,
+                  checksPerMonitor,
+                  numIps,
+                  scheduleEvery,
+                  {},
+                  status,
+                  mogrify
+                );
+              };
 
-            const allResults = await Promise.all(promises);
-            dateRange = getChecksDateRange(allResults);
-          });
+              for (let i = 0; i < numUpMonitors; i++) {
+                promises.push(makeMonitorChecks(`up-${i}`, 'up'));
+              }
+              for (let i = 0; i < numDownMonitors; i++) {
+                promises.push(makeMonitorChecks(`down-${i}`, 'down'));
+              }
 
-          it('will count all statuses correctly', async () => {
-            const apiResponse = await supertest.get(
-              `/api/uptime/snapshot/count?dateRangeStart=${dateRange.start}&dateRangeEnd=${dateRange.end}`
-            );
+              const allResults = await Promise.all(promises);
+              dateRange = getChecksDateRange(allResults);
+            });
 
-            expectFixtureEql(apiResponse.body, 'snapshot');
-          });
+            it('will count all statuses correctly', async () => {
+              const apiResponse = await supertest.get(
+                `/api/uptime/snapshot/count?dateRangeStart=${dateRange.start}&dateRangeEnd=${dateRange.end}`
+              );
 
-          it('will fetch a monitor snapshot filtered by down status', async () => {
-            const statusFilter = 'down';
-            const apiResponse = await supertest.get(
-              `/api/uptime/snapshot/count?dateRangeStart=${dateRange.start}&dateRangeEnd=${dateRange.end}&statusFilter=${statusFilter}`
-            );
+              expectFixtureEql(apiResponse.body, 'snapshot');
+            });
 
-            expectFixtureEql(apiResponse.body, 'snapshot_filtered_by_down');
-          });
+            it('will fetch a monitor snapshot filtered by down status', async () => {
+              const statusFilter = 'down';
+              const apiResponse = await supertest.get(
+                `/api/uptime/snapshot/count?dateRangeStart=${dateRange.start}&dateRangeEnd=${dateRange.end}&statusFilter=${statusFilter}`
+              );
 
-          it('will fetch a monitor snapshot filtered by up status', async () => {
-            const statusFilter = 'up';
-            const apiResponse = await supertest.get(
-              `/api/uptime/snapshot/count?dateRangeStart=${dateRange.start}&dateRangeEnd=${dateRange.end}&statusFilter=${statusFilter}`
-            );
-            expectFixtureEql(apiResponse.body, 'snapshot_filtered_by_up');
+              expectFixtureEql(apiResponse.body, 'snapshot_filtered_by_down');
+            });
+
+            it('will fetch a monitor snapshot filtered by up status', async () => {
+              const statusFilter = 'up';
+              const apiResponse = await supertest.get(
+                `/api/uptime/snapshot/count?dateRangeStart=${dateRange.start}&dateRangeEnd=${dateRange.end}&statusFilter=${statusFilter}`
+              );
+              expectFixtureEql(apiResponse.body, 'snapshot_filtered_by_up');
+            });
           });
         });
       });

From a00ebc6f187ffbd920b0e2bf3f92483239ed0c0f Mon Sep 17 00:00:00 2001
From: Justin Kambic <justin.kambic@elastic.co>
Date: Mon, 24 Feb 2020 18:44:52 -0500
Subject: [PATCH 157/174] [Uptime] Delete useless try...catch blocks (#58263)

* Delete useless try...catch blocks.

* Delete unneeded function.

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../plugins/uptime/public/hooks/use_telemetry.ts    | 10 +++-------
 .../plugins/uptime/server/lib/helper/index.ts       |  1 -
 .../uptime/server/lib/helper/parse_filter_query.ts  | 13 -------------
 .../server/lib/requests/get_ping_histogram.ts       |  4 ++--
 4 files changed, 5 insertions(+), 23 deletions(-)
 delete mode 100644 x-pack/legacy/plugins/uptime/server/lib/helper/parse_filter_query.ts

diff --git a/x-pack/legacy/plugins/uptime/public/hooks/use_telemetry.ts b/x-pack/legacy/plugins/uptime/public/hooks/use_telemetry.ts
index 15f276174e2cf..7eb18404decfd 100644
--- a/x-pack/legacy/plugins/uptime/public/hooks/use_telemetry.ts
+++ b/x-pack/legacy/plugins/uptime/public/hooks/use_telemetry.ts
@@ -22,13 +22,9 @@ const getApiPath = (page?: UptimePage) => {
 };
 
 const logPageLoad = async (fetch: HttpHandler, page?: UptimePage) => {
-  try {
-    await fetch(getApiPath(page), {
-      method: 'POST',
-    });
-  } catch (e) {
-    throw e;
-  }
+  await fetch(getApiPath(page), {
+    method: 'POST',
+  });
 };
 
 export const useUptimeTelemetry = (page?: UptimePage) => {
diff --git a/x-pack/legacy/plugins/uptime/server/lib/helper/index.ts b/x-pack/legacy/plugins/uptime/server/lib/helper/index.ts
index 0dcbc0a424b5e..1607c36f1d1b7 100644
--- a/x-pack/legacy/plugins/uptime/server/lib/helper/index.ts
+++ b/x-pack/legacy/plugins/uptime/server/lib/helper/index.ts
@@ -7,6 +7,5 @@
 export { getFilterClause } from './get_filter_clause';
 export { parseRelativeDate } from './get_histogram_interval';
 export { getHistogramIntervalFormatted } from './get_histogram_interval_formatted';
-export { parseFilterQuery } from './parse_filter_query';
 export { assertCloseTo } from './assert_close_to';
 export { objectValuesToArrays } from './object_to_array';
diff --git a/x-pack/legacy/plugins/uptime/server/lib/helper/parse_filter_query.ts b/x-pack/legacy/plugins/uptime/server/lib/helper/parse_filter_query.ts
deleted file mode 100644
index 4c73ec53af9b9..0000000000000
--- a/x-pack/legacy/plugins/uptime/server/lib/helper/parse_filter_query.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-export const parseFilterQuery = (query?: string | null) => {
-  try {
-    return query ? JSON.parse(query) : null;
-  } catch {
-    return null;
-  }
-};
diff --git a/x-pack/legacy/plugins/uptime/server/lib/requests/get_ping_histogram.ts b/x-pack/legacy/plugins/uptime/server/lib/requests/get_ping_histogram.ts
index 3b448dc31659b..7b8ca4708255c 100644
--- a/x-pack/legacy/plugins/uptime/server/lib/requests/get_ping_histogram.ts
+++ b/x-pack/legacy/plugins/uptime/server/lib/requests/get_ping_histogram.ts
@@ -5,7 +5,7 @@
  */
 
 import { UMElasticsearchQueryFn } from '../adapters';
-import { parseFilterQuery, getFilterClause } from '../helper';
+import { getFilterClause } from '../helper';
 import { INDEX_NAMES, QUERY } from '../../../common/constants';
 import { HistogramQueryResult } from './types';
 import { HistogramResult } from '../../../common/types';
@@ -27,7 +27,7 @@ export const getPingHistogram: UMElasticsearchQueryFn<
   GetPingHistogramParams,
   HistogramResult
 > = async ({ callES, from, to, filters, monitorId, statusFilter }) => {
-  const boolFilters = parseFilterQuery(filters);
+  const boolFilters = filters ? JSON.parse(filters) : null;
   const additionalFilters = [];
   if (monitorId) {
     additionalFilters.push({ match: { 'monitor.id': monitorId } });

From 33334132eaacde693caa3f7b6a337309abd784bb Mon Sep 17 00:00:00 2001
From: Spencer <email@spalger.com>
Date: Mon, 24 Feb 2020 17:07:36 -0700
Subject: [PATCH 158/174] [kbn/optimizer] disable parallelization in terser
 plugin (#58396)

* [kbn/optimizer] disable parallelization in terer plugin

* use more workers when building the dist
---
 .../kbn-optimizer/src/optimizer/optimizer_config.ts   | 11 ++++++++++-
 packages/kbn-optimizer/src/worker/webpack.config.ts   |  1 +
 2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts
index a258e1010fce3..1c8ae265bf6bb 100644
--- a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts
+++ b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts
@@ -25,6 +25,15 @@ import { Bundle, WorkerConfig } from '../common';
 import { findKibanaPlatformPlugins, KibanaPlatformPlugin } from './kibana_platform_plugins';
 import { getBundles } from './get_bundles';
 
+function pickMaxWorkerCount(dist: boolean) {
+  // don't break if cpus() returns nothing, or an empty array
+  const cpuCount = Math.max(Os.cpus()?.length, 1);
+  // if we're buiding the dist then we can use more of the system's resources to get things done a little quicker
+  const maxWorkers = dist ? cpuCount - 1 : Math.ceil(cpuCount / 3);
+  // ensure we always have at least two workers
+  return Math.max(maxWorkers, 2);
+}
+
 interface Options {
   /** absolute path to root of the repo/build */
   repoRoot: string;
@@ -110,7 +119,7 @@ export class OptimizerConfig {
 
     const maxWorkerCount = process.env.KBN_OPTIMIZER_MAX_WORKERS
       ? parseInt(process.env.KBN_OPTIMIZER_MAX_WORKERS, 10)
-      : options.maxWorkerCount ?? Math.max(Math.ceil(Math.max(Os.cpus()?.length, 1) / 3), 2);
+      : options.maxWorkerCount ?? pickMaxWorkerCount(dist);
     if (typeof maxWorkerCount !== 'number' || !Number.isFinite(maxWorkerCount)) {
       throw new TypeError('worker count must be a number');
     }
diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts
index 22b927d4638d7..3c6ae78bc4d91 100644
--- a/packages/kbn-optimizer/src/worker/webpack.config.ts
+++ b/packages/kbn-optimizer/src/worker/webpack.config.ts
@@ -252,6 +252,7 @@ export function getWebpackConfig(bundle: Bundle, worker: WorkerConfig) {
           cache: false,
           sourceMap: false,
           extractComments: false,
+          parallel: false,
           terserOptions: {
             compress: false,
             mangle: false,

From 277b38079ee55a5ae088a3a4c6bacf127f5c8968 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Loix?= <sebastien.loix@elastic.co>
Date: Tue, 25 Feb 2020 13:12:27 +0530
Subject: [PATCH 159/174] [Index management] Move to new platform "plugins"
 folder (#58109)

---
 x-pack/.i18nrc.json                           |  2 +-
 .../cross_cluster_replication/index.js        |  7 +-
 .../components/detail_panel/detail_panel.js   |  2 +-
 .../components/detail_panel/detail_panel.js   |  7 +-
 .../public/extend_index_management/index.js   |  9 ++-
 .../__jest__/extend_index_management.test.js  |  6 +-
 .../index_lifecycle_management/plugin.ts      |  8 +-
 .../public/legacy.ts                          |  4 +-
 .../components/policy_table/policy_table.js   |  2 +-
 .../np_ready/extend_index_management/index.js |  3 +-
 .../legacy/plugins/index_management/index.ts  | 29 +------
 .../index_management/public/index.html        |  3 -
 .../legacy/plugins/remote_clusters/index.ts   |  4 +-
 x-pack/legacy/plugins/rollup/index.ts         |  4 +-
 x-pack/legacy/plugins/rollup/public/legacy.ts |  2 -
 x-pack/legacy/plugins/rollup/public/plugin.ts | 20 +++--
 x-pack/legacy/plugins/rollup/server/plugin.ts | 10 +--
 .../rollup/server/rollup_data_enricher.ts     |  7 +-
 x-pack/legacy/plugins/rollup/server/types.ts  |  1 -
 .../client_integration/helpers/constants.ts   |  0
 .../helpers/home.helpers.ts                   |  6 +-
 .../helpers/http_requests.ts                  |  0
 .../client_integration/helpers/index.ts       |  2 +-
 .../helpers/setup_environment.tsx             |  3 +-
 .../helpers/template_clone.helpers.ts         |  4 +-
 .../helpers/template_create.helpers.ts        |  4 +-
 .../helpers/template_edit.helpers.ts          |  4 +-
 .../helpers/template_form.helpers.ts          |  2 +-
 .../__jest__/client_integration/home.test.ts  |  0
 .../template_clone.test.tsx                   |  0
 .../template_create.test.tsx                  |  0
 .../client_integration/template_edit.test.tsx |  0
 .../__snapshots__/index_table.test.js.snap    |  0
 .../__jest__/components/index_table.test.js   |  4 +-
 .../__snapshots__/flatten_object.test.js.snap |  0
 .../__jest__/lib/flatten_object.test.js       |  0
 .../plugins/index_management/__mocks__/ace.js |  0
 .../__mocks__/ui/documentation_links.js       |  0
 .../index_management/__mocks__/ui/notify.js   |  0
 .../common/constants/api_base_path.ts         |  0
 .../common/constants/base_path.ts             |  0
 .../common/constants/index.ts                 |  0
 .../common/constants/index_statuses.ts        |  0
 .../common/constants/invalid_characters.ts    |  0
 .../common/constants/plugin.ts                |  2 +-
 .../common/constants/ui_metric.ts             |  0
 .../plugins/index_management/common/index.ts  |  0
 .../index_management/common/lib/index.ts      |  0
 .../common/lib/template_serialization.ts      |  0
 .../index_management/common/types/index.ts    |  0
 .../common/types/templates.ts                 |  0
 x-pack/plugins/index_management/kibana.json   | 15 ++++
 .../public/application/app.tsx                |  2 +-
 .../public/application/app_context.tsx        |  4 +-
 .../public/application/components/index.ts    |  0
 .../client_integration/helpers/index.ts       |  2 +-
 .../helpers/mappings_editor.helpers.ts        |  2 +-
 .../mappings_editor.test.tsx                  |  0
 .../components/mappings_editor/_index.scss    |  0
 .../mappings_editor/components/_index.scss    |  0
 .../mappings_editor/components/code_block.tsx |  0
 .../configuration_form/configuration_form.tsx |  6 +-
 .../configuration_form_schema.tsx             |  0
 .../dynamic_mapping_section.tsx               |  0
 .../dynamic_mapping_section/index.ts          |  0
 .../components/configuration_form/index.ts    |  0
 .../meta_field_section/index.ts               |  0
 .../meta_field_section/meta_field_section.tsx |  0
 .../configuration_form/routing_section.tsx    |  0
 .../source_field_section/index.ts             |  0
 .../source_field_section.tsx                  |  0
 .../components/document_fields/_index.scss    |  0
 .../document_fields/document_fields.tsx       | 11 ++-
 .../document_fields_header.tsx                |  0
 .../editor_toggle_controls.tsx                |  0
 .../field_parameters/analyzer_parameter.tsx   |  0
 .../analyzer_parameter_selects.tsx            | 20 +++--
 .../field_parameters/analyzers_parameter.tsx  |  0
 .../field_parameters/boost_parameter.tsx      |  0
 .../coerce_number_parameter.tsx               |  0
 .../coerce_shape_parameter.tsx                |  0
 .../field_parameters/copy_to_parameter.tsx    |  0
 .../field_parameters/doc_values_parameter.tsx |  0
 .../field_parameters/dynamic_parameter.tsx    |  0
 .../eager_global_ordinals_parameter.tsx       |  0
 .../field_parameters/enabled_parameter.tsx    |  0
 .../fielddata_frequency_filter_absolute.tsx   |  0
 .../fielddata_frequency_filter_percentage.tsx |  0
 .../field_parameters/fielddata_parameter.tsx  |  0
 .../field_parameters/format_parameter.tsx     |  0
 .../field_parameters/ignore_malformed.tsx     |  0
 .../ignore_z_value_parameter.tsx              |  0
 .../document_fields/field_parameters/index.ts |  0
 .../field_parameters/index_parameter.tsx      |  0
 .../field_parameters/locale_parameter.tsx     |  0
 .../max_shingle_size_parameter.tsx            |  0
 .../field_parameters/name_parameter.tsx       |  0
 .../field_parameters/norms_parameter.tsx      |  0
 .../field_parameters/null_value_parameter.tsx |  0
 .../orientation_parameter.tsx                 |  0
 .../field_parameters/path_parameter.tsx       |  0
 .../field_parameters/relations_parameter.tsx  |  0
 .../field_parameters/similarity_parameter.tsx |  0
 .../split_queries_on_whitespace_parameter.tsx |  0
 .../field_parameters/store_parameter.tsx      |  0
 .../term_vector_parameter.tsx                 |  0
 .../field_parameters/type_parameter.tsx       |  0
 .../fields/_field_list_item.scss              |  0
 .../document_fields/fields/_index.scss        |  0
 .../fields/create_field/create_field.tsx      | 80 +++++++++++--------
 .../fields/create_field/index.ts              |  0
 .../required_parameters_forms/alias_type.tsx  |  0
 .../dense_vector_type.tsx                     |  0
 .../required_parameters_forms/index.ts        |  0
 .../scaled_float_type.tsx                     |  0
 .../token_count_type.tsx                      |  0
 .../fields/delete_field_provider.tsx          |  0
 .../edit_field/_edit_field_form_row.scss      |  0
 .../fields/edit_field/_index.scss             |  0
 .../advanced_parameters_section.tsx           |  0
 .../edit_field/basic_parameters_section.tsx   |  0
 .../fields/edit_field/edit_field.tsx          |  0
 .../edit_field/edit_field_container.tsx       |  4 +-
 .../fields/edit_field/edit_field_form_row.tsx |  0
 .../edit_field/edit_field_header_form.tsx     |  0
 .../edit_field/field_description_section.tsx  |  0
 .../fields/edit_field/index.ts                |  0
 .../edit_field/update_field_provider.tsx      |  0
 .../fields/field_types/alias_type.tsx         |  0
 .../fields/field_types/binary_type.tsx        |  0
 .../fields/field_types/boolean_type.tsx       |  0
 .../fields/field_types/completion_type.tsx    |  0
 .../fields/field_types/date_type.tsx          |  0
 .../fields/field_types/dense_vector_type.tsx  |  0
 .../fields/field_types/flattened_type.tsx     |  0
 .../fields/field_types/geo_point_type.tsx     |  0
 .../fields/field_types/geo_shape_type.tsx     |  0
 .../fields/field_types/index.ts               |  0
 .../fields/field_types/ip_type.tsx            |  0
 .../fields/field_types/join_type.tsx          |  0
 .../fields/field_types/keyword_type.tsx       |  0
 .../fields/field_types/nested_type.tsx        |  0
 .../fields/field_types/numeric_type.tsx       |  0
 .../fields/field_types/object_type.tsx        |  0
 .../fields/field_types/range_type.tsx         |  0
 .../fields/field_types/search_as_you_type.tsx |  0
 .../fields/field_types/shape_type.tsx         |  0
 .../fields/field_types/text_type.tsx          |  0
 .../fields/field_types/token_count_type.tsx   |  0
 .../document_fields/fields/fields_list.tsx    |  0
 .../fields/fields_list_item.tsx               |  0
 .../fields/fields_list_item_container.tsx     | 10 +--
 .../document_fields/fields/index.ts           |  0
 .../modal_confirmation_delete_fields.tsx      |  0
 .../document_fields/fields_json_editor.tsx    |  0
 .../document_fields/fields_tree_editor.tsx    | 12 +--
 .../components/document_fields/index.ts       |  0
 .../document_fields/search_fields/index.ts    |  0
 .../search_fields/search_result.tsx           |  0
 .../search_fields/search_result_item.tsx      |  0
 .../components/fields_tree.tsx                |  0
 .../mappings_editor/components/index.ts       |  0
 .../components/load_mappings/index.ts         |  0
 .../load_mappings/load_from_json_button.tsx   |  0
 .../load_mappings_provider.test.tsx           |  2 +-
 .../load_mappings/load_mappings_provider.tsx  |  0
 .../components/multiple_mappings_warning.tsx  |  0
 .../components/templates_form/index.ts        |  0
 .../templates_form/templates_form.tsx         |  6 +-
 .../templates_form/templates_form_schema.ts   |  0
 .../mappings_editor/components/tree/index.ts  |  0
 .../mappings_editor/components/tree/tree.tsx  |  0
 .../components/tree/tree_item.tsx             |  0
 .../constants/data_types_definition.tsx       |  0
 .../constants/default_values.ts               |  0
 .../constants/field_options.tsx               |  0
 .../constants/field_options_i18n.ts           |  0
 .../mappings_editor/constants/index.ts        |  0
 .../constants/mappings_editor.ts              |  0
 .../constants/parameters_definition.tsx       |  0
 .../components/mappings_editor/index.ts       |  0
 .../index_settings_context.tsx                |  0
 .../mappings_editor/lib/error_reporter.ts     |  0
 .../lib/extract_mappings_definition.test.ts   |  0
 .../lib/extract_mappings_definition.ts        |  0
 .../components/mappings_editor/lib/index.ts   |  0
 .../lib/mappings_validator.test.ts            |  0
 .../mappings_editor/lib/mappings_validator.ts |  0
 .../mappings_editor/lib/search_fields.test.ts |  0
 .../mappings_editor/lib/search_fields.tsx     |  0
 .../mappings_editor/lib/serializers.ts        |  0
 .../mappings_editor/lib/utils.test.ts         |  0
 .../components/mappings_editor/lib/utils.ts   |  0
 .../mappings_editor/lib/validators.ts         |  0
 .../mappings_editor/mappings_editor.tsx       |  2 +-
 .../mappings_editor/mappings_state.tsx        |  4 +-
 .../components/mappings_editor/reducer.ts     |  0
 .../mappings_editor/shared_imports.ts         |  8 +-
 .../components/mappings_editor/types.ts       |  0
 .../application/components/no_match/index.ts  |  0
 .../components/no_match/no_match.tsx          |  0
 .../components/page_error/index.ts            |  0
 .../page_error/page_error_forbidden.tsx       |  0
 .../application/components/section_error.tsx  |  0
 .../components/section_loading.tsx            |  0
 .../components/template_delete_modal.tsx      |  0
 .../components/template_form/index.ts         |  0
 .../components/template_form/steps/index.ts   |  0
 .../template_form/steps/step_aliases.tsx      |  0
 .../template_form/steps/step_logistics.tsx    | 15 +---
 .../template_form/steps/step_mappings.tsx     |  0
 .../template_form/steps/step_review.tsx       |  2 +-
 .../template_form/steps/step_settings.tsx     |  0
 .../template_form/steps/use_json_step.ts      | 14 ++--
 .../template_form/template_form.tsx           | 34 ++++----
 .../template_form/template_form_schemas.tsx   |  5 +-
 .../template_form/template_steps.tsx          |  0
 .../components/template_form/types.ts         |  0
 .../constants/detail_panel_tabs.ts            |  0
 .../public/application/constants/index.ts     |  0
 .../constants/refresh_intervals.ts            |  0
 .../public/application/index.tsx              |  2 +-
 .../public/application/lib/ace.js             |  6 +-
 .../public/application/lib/edit_settings.js   |  0
 .../public/application/lib/flatten_object.js  |  0
 .../application/lib/flatten_panel_tree.js     |  0
 .../application/lib/index_status_labels.js    |  0
 .../lib/manage_angular_lifecycle.ts           |  0
 .../public/application/lib/render_badges.js   |  0
 .../public/application/sections/home/home.tsx |  0
 .../public/application/sections/home/index.ts |  0
 .../detail_panel/detail_panel.container.js    |  0
 .../index_list/detail_panel/detail_panel.js   |  0
 .../edit_settings_json.container.js           |  0
 .../edit_settings_json/edit_settings_json.js  |  0
 .../detail_panel/edit_settings_json/index.js  |  0
 .../home/index_list/detail_panel/index.js     |  0
 .../detail_panel/show_json/index.js           |  0
 .../show_json/show_json.container.js          |  0
 .../detail_panel/show_json/show_json.js       |  0
 .../index_list/detail_panel/summary/index.js  |  0
 .../detail_panel/summary/summary.container.js |  0
 .../detail_panel/summary/summary.js           |  0
 .../sections/home/index_list/index.ts         |  0
 .../index_actions_context_menu/index.js       |  0
 .../index_actions_context_menu.container.js   |  0
 .../index_actions_context_menu.js             |  0
 .../sections/home/index_list/index_list.d.ts  |  0
 .../sections/home/index_list/index_list.js    |  0
 .../home/index_list/index_table/index.js      |  0
 .../index_table/index_table.container.js      |  0
 .../index_list/index_table/index_table.js     |  0
 .../sections/home/template_list/index.ts      |  0
 .../template_list/template_details/index.ts   |  0
 .../template_details/tabs/index.ts            |  0
 .../template_details/tabs/tab_aliases.tsx     |  0
 .../template_details/tabs/tab_mappings.tsx    |  0
 .../template_details/tabs/tab_settings.tsx    |  0
 .../template_details/tabs/tab_summary.tsx     |  0
 .../template_details/template_details.tsx     |  4 +-
 .../home/template_list/template_list.tsx      |  6 +-
 .../template_list/template_table/index.ts     |  0
 .../template_table/template_table.tsx         |  0
 .../sections/template_clone/index.ts          |  0
 .../template_clone/template_clone.tsx         |  4 +-
 .../sections/template_create/index.ts         |  0
 .../template_create/template_create.tsx       |  0
 .../sections/template_edit/index.ts           |  0
 .../sections/template_edit/template_edit.tsx  |  4 +-
 .../public/application/services/api.ts        |  4 +-
 .../application/services/breadcrumbs.ts       |  2 +-
 .../application/services/documentation.ts     |  2 +-
 .../application/services/health_to_color.ts   |  0
 .../public/application/services/http.ts       |  2 +-
 .../public/application/services/index.ts      |  2 +-
 .../public/application/services/navigation.ts |  0
 .../application/services/notification.ts      |  2 +-
 .../public/application/services/routing.ts    |  0
 .../public/application/services/sort_table.ts |  0
 .../public/application/services/ui_metric.ts  |  6 +-
 .../application/services/use_request.ts       |  0
 .../store/actions/clear_cache_indices.js      |  0
 .../store/actions/clear_row_status.js         |  0
 .../store/actions/close_indices.js            |  0
 .../store/actions/delete_indices.js           |  0
 .../application/store/actions/detail_panel.js |  0
 .../store/actions/edit_index_settings.js      |  0
 .../store/actions/extension_action.js         |  0
 .../store/actions/flush_indices.js            |  0
 .../store/actions/forcemerge_indices.js       |  0
 .../store/actions/freeze_indices.js           |  0
 .../public/application/store/actions/index.js |  0
 .../store/actions/load_index_data.js          |  0
 .../application/store/actions/load_indices.js |  0
 .../application/store/actions/open_indices.js |  0
 .../store/actions/refresh_indices.js          |  0
 .../store/actions/reload_indices.js           |  0
 .../application/store/actions/table_state.js  |  0
 .../store/actions/unfreeze_indices.js         |  0
 .../store/actions/update_index_settings.js    |  0
 .../public/application/store/index.ts         |  0
 .../store/reducers/detail_panel.js            |  0
 .../application/store/reducers/index.js       |  0
 .../store/reducers/index_management.js        |  0
 .../application/store/reducers/indices.js     |  0
 .../application/store/reducers/row_status.js  |  0
 .../application/store/reducers/table_state.js |  0
 .../application/store/selectors/index.d.ts    |  0
 .../application/store/selectors/index.js      |  0
 .../public/application/store/store.d.ts       |  0
 .../public/application/store/store.js         |  0
 .../index_management/public/index.scss        |  0
 .../plugins/index_management/public/index.ts  | 11 +--
 .../plugins/index_management/public/mocks.ts  |  0
 .../plugins/index_management/public/plugin.ts | 10 +--
 .../services/extensions_service.mock.ts       |  0
 .../public/services/extensions_service.ts     |  0
 .../index_management/public/services/index.ts |  0
 .../index_management/public/shared_imports.ts | 33 ++++++++
 .../plugins/index_management/public/types.ts  |  0
 .../index_management/server/config.ts}        | 14 ++--
 .../plugins/index_management/server/index.ts  | 10 +++
 .../server/lib/fetch_aliases.test.ts          |  0
 .../server/lib/fetch_aliases.ts               |  0
 .../server/lib/fetch_indices.ts               |  0
 .../server/lib/get_managed_templates.ts       |  0
 .../server/lib/is_es_error.ts                 |  0
 .../plugins/index_management/server/plugin.ts |  4 +-
 .../server/routes/api/index.ts                |  0
 .../server/routes/api/indices/index.ts        |  0
 .../api/indices/register_clear_cache_route.ts |  0
 .../api/indices/register_close_route.ts       |  0
 .../api/indices/register_delete_route.ts      |  0
 .../api/indices/register_flush_route.ts       |  0
 .../api/indices/register_forcemerge_route.ts  |  0
 .../api/indices/register_freeze_route.ts      |  0
 .../api/indices/register_indices_routes.ts    |  0
 .../routes/api/indices/register_list_route.ts |  0
 .../routes/api/indices/register_open_route.ts |  0
 .../api/indices/register_refresh_route.ts     |  0
 .../api/indices/register_reload_route.ts      |  0
 .../api/indices/register_unfreeze_route.ts    |  0
 .../server/routes/api/mapping/index.ts        |  0
 .../api/mapping/register_mapping_route.ts     |  0
 .../server/routes/api/settings/index.ts       |  0
 .../api/settings/register_load_route.ts       |  0
 .../api/settings/register_settings_routes.ts  |  0
 .../api/settings/register_update_route.ts     |  0
 .../server/routes/api/stats/index.ts          |  0
 .../routes/api/stats/register_stats_route.ts  |  0
 .../server/routes/api/templates/index.ts      |  0
 .../api/templates/register_create_route.ts    |  0
 .../api/templates/register_delete_route.ts    |  0
 .../api/templates/register_get_routes.ts      |  0
 .../api/templates/register_template_routes.ts |  0
 .../api/templates/register_update_route.ts    |  0
 .../routes/api/templates/validate_schemas.ts  |  0
 .../index_management/server/routes/helpers.ts |  0
 .../index_management/server/routes/index.ts   |  0
 .../index_management/server/services/index.ts |  0
 .../server/services/index_data_enricher.ts    |  0
 .../server/services/license.ts                |  5 +-
 .../plugins/index_management/server/types.ts  |  2 +-
 .../index_management/test/fixtures/index.ts   |  0
 .../test/fixtures/template.ts                 |  2 +-
 365 files changed, 297 insertions(+), 261 deletions(-)
 delete mode 100644 x-pack/legacy/plugins/index_management/public/index.html
 rename x-pack/{legacy => }/plugins/index_management/__jest__/client_integration/helpers/constants.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts (96%)
 rename x-pack/{legacy => }/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/__jest__/client_integration/helpers/index.ts (95%)
 rename x-pack/{legacy => }/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx (95%)
 rename x-pack/{legacy => }/plugins/index_management/__jest__/client_integration/helpers/template_clone.helpers.ts (85%)
 rename x-pack/{legacy => }/plugins/index_management/__jest__/client_integration/helpers/template_create.helpers.ts (84%)
 rename x-pack/{legacy => }/plugins/index_management/__jest__/client_integration/helpers/template_edit.helpers.ts (85%)
 rename x-pack/{legacy => }/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts (99%)
 rename x-pack/{legacy => }/plugins/index_management/__jest__/client_integration/home.test.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/__jest__/client_integration/template_clone.test.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/__jest__/client_integration/template_create.test.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/__jest__/client_integration/template_edit.test.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/__jest__/components/__snapshots__/index_table.test.js.snap (100%)
 rename x-pack/{legacy => }/plugins/index_management/__jest__/components/index_table.test.js (98%)
 rename x-pack/{legacy => }/plugins/index_management/__jest__/lib/__snapshots__/flatten_object.test.js.snap (100%)
 rename x-pack/{legacy => }/plugins/index_management/__jest__/lib/flatten_object.test.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/__mocks__/ace.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/__mocks__/ui/documentation_links.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/__mocks__/ui/notify.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/common/constants/api_base_path.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/common/constants/base_path.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/common/constants/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/common/constants/index_statuses.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/common/constants/invalid_characters.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/common/constants/plugin.ts (86%)
 rename x-pack/{legacy => }/plugins/index_management/common/constants/ui_metric.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/common/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/common/lib/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/common/lib/template_serialization.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/common/types/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/common/types/templates.ts (100%)
 create mode 100644 x-pack/plugins/index_management/kibana.json
 rename x-pack/{legacy => }/plugins/index_management/public/application/app.tsx (98%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/app_context.tsx (90%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts (90%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.ts (85%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/_index.scss (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/_index.scss (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/code_block.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form.tsx (98%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form_schema.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/dynamic_mapping_section.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/meta_field_section.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/routing_section.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/source_field_section.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/_index.scss (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields.tsx (91%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields_header.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/editor_toggle_controls.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter_selects.tsx (86%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzers_parameter.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/boost_parameter.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_number_parameter.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_shape_parameter.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/copy_to_parameter.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/doc_values_parameter.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/dynamic_parameter.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/eager_global_ordinals_parameter.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/enabled_parameter.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_absolute.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_percentage.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_parameter.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/format_parameter.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_malformed.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_z_value_parameter.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index_parameter.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/locale_parameter.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/max_shingle_size_parameter.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/name_parameter.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/norms_parameter.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/null_value_parameter.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/orientation_parameter.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/relations_parameter.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/similarity_parameter.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/split_queries_on_whitespace_parameter.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/store_parameter.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/term_vector_parameter.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/type_parameter.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_field_list_item.scss (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_index.scss (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx (83%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/alias_type.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/dense_vector_type.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/scaled_float_type.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/token_count_type.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/delete_field_provider.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_edit_field_form_row.scss (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_index.scss (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/advanced_parameters_section.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/basic_parameters_section.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx (97%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_form_row.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/field_description_section.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/update_field_provider.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/alias_type.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/binary_type.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/boolean_type.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/completion_type.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/date_type.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/dense_vector_type.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/flattened_type.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_point_type.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_shape_type.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/ip_type.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/join_type.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/keyword_type.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/nested_type.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/numeric_type.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/object_type.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/range_type.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/search_as_you_type.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/shape_type.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/text_type.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/token_count_type.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx (92%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/modal_confirmation_delete_fields.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields_json_editor.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields_tree_editor.tsx (88%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result_item.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/fields_tree.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_from_json_button.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx (98%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/multiple_mappings_warning.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/templates_form/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form.tsx (98%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form_schema.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/tree/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/tree/tree.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/components/tree/tree_item.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/constants/default_values.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/constants/field_options.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/constants/field_options_i18n.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/constants/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/constants/mappings_editor.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/index_settings_context.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/lib/error_reporter.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.test.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/lib/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.test.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.test.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/lib/serializers.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/lib/validators.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/mappings_editor.tsx (98%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/mappings_state.tsx (98%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/reducer.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts (72%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/mappings_editor/types.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/no_match/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/no_match/no_match.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/page_error/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/page_error/page_error_forbidden.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/section_error.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/section_loading.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/template_delete_modal.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/template_form/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/template_form/steps/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/template_form/steps/step_aliases.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx (94%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/template_form/steps/step_mappings.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/template_form/steps/step_review.tsx (98%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/template_form/steps/step_settings.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts (83%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/template_form/template_form.tsx (92%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx (96%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/template_form/template_steps.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/components/template_form/types.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/constants/detail_panel_tabs.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/constants/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/constants/refresh_intervals.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/index.tsx (94%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/lib/ace.js (94%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/lib/edit_settings.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/lib/flatten_object.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/lib/flatten_panel_tree.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/lib/index_status_labels.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/lib/manage_angular_lifecycle.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/lib/render_badges.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/home.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.container.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.container.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/index.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/detail_panel/index.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/index.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.container.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/index.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.container.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.container.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/index_list.d.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/index_list.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/index_table/index.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.container.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/template_list/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/template_list/template_details/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_aliases.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_mappings.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_settings.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx (98%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/template_list/template_list.tsx (97%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/template_list/template_table/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/template_clone/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/template_clone/template_clone.tsx (96%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/template_create/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/template_create/template_create.tsx (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/template_edit/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/sections/template_edit/template_edit.tsx (96%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/services/api.ts (98%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/services/breadcrumbs.ts (96%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/services/documentation.ts (98%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/services/health_to_color.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/services/http.ts (88%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/services/index.ts (96%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/services/navigation.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/services/notification.ts (93%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/services/routing.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/services/sort_table.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/services/ui_metric.ts (90%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/services/use_request.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/clear_cache_indices.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/clear_row_status.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/close_indices.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/delete_indices.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/detail_panel.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/edit_index_settings.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/extension_action.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/flush_indices.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/forcemerge_indices.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/freeze_indices.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/index.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/load_index_data.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/load_indices.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/open_indices.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/refresh_indices.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/reload_indices.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/table_state.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/unfreeze_indices.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/actions/update_index_settings.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/reducers/detail_panel.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/reducers/index.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/reducers/index_management.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/reducers/indices.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/reducers/row_status.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/reducers/table_state.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/selectors/index.d.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/selectors/index.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/store.d.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/application/store/store.js (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/index.scss (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/index.ts (66%)
 rename x-pack/{legacy => }/plugins/index_management/public/mocks.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/plugin.ts (90%)
 rename x-pack/{legacy => }/plugins/index_management/public/services/extensions_service.mock.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/services/extensions_service.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/public/services/index.ts (100%)
 create mode 100644 x-pack/plugins/index_management/public/shared_imports.ts
 rename x-pack/{legacy => }/plugins/index_management/public/types.ts (100%)
 rename x-pack/{legacy/plugins/index_management/public/shared_imports.ts => plugins/index_management/server/config.ts} (52%)
 rename x-pack/{legacy => }/plugins/index_management/server/index.ts (67%)
 rename x-pack/{legacy => }/plugins/index_management/server/lib/fetch_aliases.test.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/lib/fetch_aliases.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/lib/fetch_indices.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/lib/get_managed_templates.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/lib/is_es_error.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/plugin.ts (94%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/indices/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/indices/register_clear_cache_route.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/indices/register_close_route.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/indices/register_delete_route.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/indices/register_flush_route.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/indices/register_forcemerge_route.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/indices/register_freeze_route.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/indices/register_indices_routes.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/indices/register_list_route.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/indices/register_open_route.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/indices/register_refresh_route.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/indices/register_reload_route.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/indices/register_unfreeze_route.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/mapping/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/mapping/register_mapping_route.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/settings/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/settings/register_load_route.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/settings/register_settings_routes.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/settings/register_update_route.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/stats/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/stats/register_stats_route.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/templates/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/templates/register_create_route.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/templates/register_delete_route.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/templates/register_get_routes.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/templates/register_template_routes.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/templates/register_update_route.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/api/templates/validate_schemas.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/helpers.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/routes/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/services/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/services/index_data_enricher.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/server/services/license.ts (89%)
 rename x-pack/{legacy => }/plugins/index_management/server/types.ts (92%)
 rename x-pack/{legacy => }/plugins/index_management/test/fixtures/index.ts (100%)
 rename x-pack/{legacy => }/plugins/index_management/test/fixtures/template.ts (88%)

diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json
index f22f7e98d3b8a..b1cb9075434e8 100644
--- a/x-pack/.i18nrc.json
+++ b/x-pack/.i18nrc.json
@@ -16,7 +16,7 @@
     "xpack.fileUpload": "legacy/plugins/file_upload",
     "xpack.graph": ["legacy/plugins/graph", "plugins/graph"],
     "xpack.grokDebugger": "legacy/plugins/grokdebugger",
-    "xpack.idxMgmt": "legacy/plugins/index_management",
+    "xpack.idxMgmt": "plugins/index_management",
     "xpack.indexLifecycleMgmt": "legacy/plugins/index_lifecycle_management",
     "xpack.infra": "plugins/infra",
     "xpack.ingestManager": "plugins/ingest_manager",
diff --git a/x-pack/legacy/plugins/cross_cluster_replication/index.js b/x-pack/legacy/plugins/cross_cluster_replication/index.js
index 1b5f42fc5107e..1b3aafcad5c0f 100644
--- a/x-pack/legacy/plugins/cross_cluster_replication/index.js
+++ b/x-pack/legacy/plugins/cross_cluster_replication/index.js
@@ -49,13 +49,12 @@ export function crossClusterReplication(kibana) {
     init: function initCcrPlugin(server) {
       registerLicenseChecker(server);
       registerRoutes(server);
-
       if (
         server.config().get('xpack.ccr.ui.enabled') &&
-        server.plugins.index_management &&
-        server.plugins.index_management.addIndexManagementDataEnricher
+        server.newPlatform.setup.plugins.indexManagement &&
+        server.newPlatform.setup.plugins.indexManagement.indexDataEnricher
       ) {
-        server.plugins.index_management.addIndexManagementDataEnricher(ccrDataEnricher);
+        server.newPlatform.setup.plugins.indexManagement.indexDataEnricher.add(ccrDataEnricher);
       }
     },
   });
diff --git a/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/detail_panel/detail_panel.js b/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/detail_panel/detail_panel.js
index 16ed0a7d695ad..7b31ffa5024b7 100644
--- a/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/detail_panel/detail_panel.js
+++ b/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/detail_panel/detail_panel.js
@@ -7,7 +7,7 @@
 import React, { Component } from 'react';
 import PropTypes from 'prop-types';
 import { FormattedMessage } from '@kbn/i18n/react';
-import { getIndexListUri } from '../../../../../../../../index_management/public/application/services/navigation';
+import { getIndexListUri } from '../../../../../../../../../../plugins/index_management/public';
 
 import {
   EuiButtonEmpty,
diff --git a/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/follower_indices_list/components/detail_panel/detail_panel.js b/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/follower_indices_list/components/detail_panel/detail_panel.js
index e3bda2e67097d..2ad118d28f38d 100644
--- a/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/follower_indices_list/components/detail_panel/detail_panel.js
+++ b/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/follower_indices_list/components/detail_panel/detail_panel.js
@@ -7,8 +7,6 @@
 import React, { Component, Fragment } from 'react';
 import PropTypes from 'prop-types';
 import { FormattedMessage } from '@kbn/i18n/react';
-import { getIndexListUri } from '../../../../../../../../index_management/public/application/services/navigation';
-
 import {
   EuiButton,
   EuiButtonEmpty,
@@ -31,12 +29,11 @@ import {
   EuiTextColor,
   EuiTitle,
 } from '@elastic/eui';
-
 import 'brace/theme/textmate';
 
-import { ContextMenu } from '../context_menu';
-
+import { getIndexListUri } from '../../../../../../../../../../plugins/index_management/public';
 import { API_STATUS } from '../../../../../constants';
+import { ContextMenu } from '../context_menu';
 
 export class DetailPanel extends Component {
   static propTypes = {
diff --git a/x-pack/legacy/plugins/cross_cluster_replication/public/extend_index_management/index.js b/x-pack/legacy/plugins/cross_cluster_replication/public/extend_index_management/index.js
index 809a7c3e87b75..c44918c500849 100644
--- a/x-pack/legacy/plugins/cross_cluster_replication/public/extend_index_management/index.js
+++ b/x-pack/legacy/plugins/cross_cluster_replication/public/extend_index_management/index.js
@@ -4,11 +4,12 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 import { i18n } from '@kbn/i18n';
-import { extensionsService } from '../../../index_management/public';
+import { npSetup } from 'ui/new_platform';
 import { get } from 'lodash';
 
 const propertyPath = 'isFollowerIndex';
-export const followerBadgeExtension = {
+
+const followerBadgeExtension = {
   matchIndex: index => {
     return get(index, propertyPath);
   },
@@ -19,4 +20,6 @@ export const followerBadgeExtension = {
   filterExpression: 'isFollowerIndex:true',
 };
 
-extensionsService.addBadge(followerBadgeExtension);
+if (npSetup.plugins.indexManagement) {
+  npSetup.plugins.indexManagement.extensionsService.addBadge(followerBadgeExtension);
+}
diff --git a/x-pack/legacy/plugins/index_lifecycle_management/__jest__/extend_index_management.test.js b/x-pack/legacy/plugins/index_lifecycle_management/__jest__/extend_index_management.test.js
index bcbae7e093f46..d2619778617c3 100644
--- a/x-pack/legacy/plugins/index_lifecycle_management/__jest__/extend_index_management.test.js
+++ b/x-pack/legacy/plugins/index_lifecycle_management/__jest__/extend_index_management.test.js
@@ -27,8 +27,10 @@ initHttp(axios.create({ adapter: axiosXhrAdapter }), path => path);
 initUiMetric(() => () => {});
 
 jest.mock('ui/new_platform');
-jest.mock('../../index_management/public', async () => {
-  const { indexManagementMock } = await import('../../index_management/public/mocks.ts');
+jest.mock('../../../../plugins/index_management/public', async () => {
+  const { indexManagementMock } = await import(
+    '../../../../plugins/index_management/public/mocks.ts'
+  );
   return indexManagementMock.createSetup();
 });
 
diff --git a/x-pack/legacy/plugins/index_lifecycle_management/plugin.ts b/x-pack/legacy/plugins/index_lifecycle_management/plugin.ts
index 8d7f937039203..38d1bea45ce07 100644
--- a/x-pack/legacy/plugins/index_lifecycle_management/plugin.ts
+++ b/x-pack/legacy/plugins/index_lifecycle_management/plugin.ts
@@ -41,14 +41,14 @@ export class Plugin {
     registerPoliciesRoutes(server);
     registerTemplatesRoutes(server);
 
-    const serverPlugins = server.plugins as any;
+    const serverPlugins = server.newPlatform.setup.plugins as any;
 
     if (
       server.config().get('xpack.ilm.ui.enabled') &&
-      serverPlugins.index_management &&
-      serverPlugins.index_management.addIndexManagementDataEnricher
+      serverPlugins.indexManagement &&
+      serverPlugins.indexManagement.indexDataEnricher
     ) {
-      serverPlugins.index_management.addIndexManagementDataEnricher(indexLifecycleDataEnricher);
+      serverPlugins.indexManagement.indexDataEnricher.add(indexLifecycleDataEnricher);
     }
   }
 }
diff --git a/x-pack/legacy/plugins/index_lifecycle_management/public/legacy.ts b/x-pack/legacy/plugins/index_lifecycle_management/public/legacy.ts
index 3c21a644c0f78..f7f924add2c51 100644
--- a/x-pack/legacy/plugins/index_lifecycle_management/public/legacy.ts
+++ b/x-pack/legacy/plugins/index_lifecycle_management/public/legacy.ts
@@ -20,7 +20,9 @@ import { addAllExtensions } from './np_ready/extend_index_management';
 if (chrome.getInjected('ilmUiEnabled')) {
   // We have to initialize this outside of the NP lifecycle, otherwise these extensions won't
   // be available in Index Management unless the user visits ILM first.
-  addAllExtensions();
+  if ((npSetup.plugins as any).indexManagement) {
+    addAllExtensions((npSetup.plugins as any).indexManagement.extensionsService);
+  }
 
   // This method handles the cleanup needed when route is scope is destroyed.  It also prevents Angular
   // from destroying scope when route changes and both old route and new route are this same route.
diff --git a/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/application/sections/policy_table/components/policy_table/policy_table.js b/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/application/sections/policy_table/components/policy_table/policy_table.js
index 83d32eae1097d..903161fe094fc 100644
--- a/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/application/sections/policy_table/components/policy_table/policy_table.js
+++ b/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/application/sections/policy_table/components/policy_table/policy_table.js
@@ -37,7 +37,7 @@ import {
 
 import { RIGHT_ALIGNMENT } from '@elastic/eui/lib/services';
 
-import { getIndexListUri } from '../../../../../../../../index_management/public/application/services/navigation';
+import { getIndexListUri } from '../../../../../../../../../../plugins/index_management/public';
 import { BASE_PATH } from '../../../../../../../common/constants';
 import { UIM_EDIT_CLICK } from '../../../../constants';
 import { getPolicyPath } from '../../../../services/navigation';
diff --git a/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/extend_index_management/index.js b/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/extend_index_management/index.js
index 0e662b78b2c18..69658d31695bc 100644
--- a/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/extend_index_management/index.js
+++ b/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/extend_index_management/index.js
@@ -9,7 +9,6 @@ import { get, every, any } from 'lodash';
 import { i18n } from '@kbn/i18n';
 import { EuiSearchBar } from '@elastic/eui';
 
-import { extensionsService } from '../../../../index_management/public';
 import { init as initUiMetric } from '../application/services/ui_metric';
 import { init as initNotification } from '../application/services/notification';
 import { retryLifecycleForIndex } from '../application/services/api';
@@ -238,7 +237,7 @@ export const ilmFilterExtension = indices => {
   }
 };
 
-export const addAllExtensions = () => {
+export const addAllExtensions = extensionsService => {
   extensionsService.addAction(retryLifecycleActionExtension);
   extensionsService.addAction(removeLifecyclePolicyActionExtension);
   extensionsService.addAction(addLifecyclePolicyActionExtension);
diff --git a/x-pack/legacy/plugins/index_management/index.ts b/x-pack/legacy/plugins/index_management/index.ts
index c92b38c0d94be..9eba98a526d2b 100644
--- a/x-pack/legacy/plugins/index_management/index.ts
+++ b/x-pack/legacy/plugins/index_management/index.ts
@@ -4,36 +4,9 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { resolve } from 'path';
-import { Legacy } from 'kibana';
-import { PLUGIN } from './common/constants';
-import { plugin as initServerPlugin, Dependencies } from './server';
-
-export type ServerFacade = Legacy.Server;
-
 export function indexManagement(kibana: any) {
   return new kibana.Plugin({
-    id: PLUGIN.id,
+    id: 'index_management',
     configPrefix: 'xpack.index_management',
-    publicDir: resolve(__dirname, 'public'),
-    require: ['kibana', 'elasticsearch', 'xpack_main'],
-
-    uiExports: {
-      styleSheetPaths: resolve(__dirname, 'public/index.scss'),
-      managementSections: ['plugins/index_management'],
-    },
-
-    init(server: ServerFacade) {
-      const coreSetup = server.newPlatform.setup.core;
-      const coreInitializerContext = server.newPlatform.coreContext;
-      const pluginsSetup: Dependencies = {
-        licensing: server.newPlatform.setup.plugins.licensing as any,
-      };
-
-      const serverPlugin = initServerPlugin(coreInitializerContext as any);
-      const serverPublicApi = serverPlugin.setup(coreSetup, pluginsSetup);
-
-      server.expose('addIndexManagementDataEnricher', serverPublicApi.indexDataEnricher.add);
-    },
   });
 }
diff --git a/x-pack/legacy/plugins/index_management/public/index.html b/x-pack/legacy/plugins/index_management/public/index.html
deleted file mode 100644
index 0e9ac6fa0bc97..0000000000000
--- a/x-pack/legacy/plugins/index_management/public/index.html
+++ /dev/null
@@ -1,3 +0,0 @@
-<kbn-management-app section="elasticsearch/index_management">
-  <div id="indexManagementReactRoot"></div>
-</kbn-management-app>
diff --git a/x-pack/legacy/plugins/remote_clusters/index.ts b/x-pack/legacy/plugins/remote_clusters/index.ts
index 37b2224f8d7c2..439cb818d8a56 100644
--- a/x-pack/legacy/plugins/remote_clusters/index.ts
+++ b/x-pack/legacy/plugins/remote_clusters/index.ts
@@ -5,6 +5,8 @@
  */
 
 import { resolve } from 'path';
+import { Legacy } from 'kibana';
+
 import { PLUGIN } from './common';
 
 export function remoteClusters(kibana: any) {
@@ -28,7 +30,7 @@ export function remoteClusters(kibana: any) {
         enabled: Joi.boolean().default(true),
       }).default();
     },
-    isEnabled(config: any) {
+    isEnabled(config: Legacy.KibanaConfig) {
       return (
         config.get('xpack.remote_clusters.enabled') && config.get('xpack.index_management.enabled')
       );
diff --git a/x-pack/legacy/plugins/rollup/index.ts b/x-pack/legacy/plugins/rollup/index.ts
index 7548af23b3aae..2c8363cc397f4 100644
--- a/x-pack/legacy/plugins/rollup/index.ts
+++ b/x-pack/legacy/plugins/rollup/index.ts
@@ -40,7 +40,7 @@ export function rollup(kibana: any) {
     },
     init(server: any) {
       const { core: coreSetup, plugins } = server.newPlatform.setup;
-      const { usageCollection, metrics } = plugins;
+      const { usageCollection, metrics, indexManagement } = plugins;
 
       const rollupSetup = (plugins.rollup as unknown) as RollupSetup;
 
@@ -54,11 +54,11 @@ export function rollup(kibana: any) {
       rollupPluginInstance.setup(coreSetup, {
         usageCollection,
         metrics,
+        indexManagement,
         __LEGACY: {
           plugins: {
             xpack_main: server.plugins.xpack_main,
             rollup: server.plugins[PLUGIN.ID],
-            index_management: server.plugins.index_management,
           },
         },
       });
diff --git a/x-pack/legacy/plugins/rollup/public/legacy.ts b/x-pack/legacy/plugins/rollup/public/legacy.ts
index 64eb1f6436389..e3e663ac7b0f4 100644
--- a/x-pack/legacy/plugins/rollup/public/legacy.ts
+++ b/x-pack/legacy/plugins/rollup/public/legacy.ts
@@ -10,7 +10,6 @@ import { aggTypeFieldFilters } from 'ui/agg_types';
 import { addSearchStrategy } from '../../../../../src/plugins/data/public';
 import { RollupPlugin } from './plugin';
 import { setup as management } from '../../../../../src/legacy/core_plugins/management/public/legacy';
-import { extensionsService } from '../../index_management/public';
 
 const plugin = new RollupPlugin();
 
@@ -20,7 +19,6 @@ export const setup = plugin.setup(npSetup.core, {
     aggTypeFilters,
     aggTypeFieldFilters,
     addSearchStrategy,
-    indexManagementExtensions: extensionsService,
     managementLegacy: management,
   },
 });
diff --git a/x-pack/legacy/plugins/rollup/public/plugin.ts b/x-pack/legacy/plugins/rollup/public/plugin.ts
index 90d7e2d9d0191..a01383f4733ef 100644
--- a/x-pack/legacy/plugins/rollup/public/plugin.ts
+++ b/x-pack/legacy/plugins/rollup/public/plugin.ts
@@ -27,7 +27,7 @@ import {
 // @ts-ignore
 import { CRUD_APP_BASE_PATH } from './crud_app/constants';
 import { ManagementSetup } from '../../../../../src/plugins/management/public';
-import { IndexMgmtSetup } from '../../index_management/public';
+import { IndexMgmtSetup } from '../../../../plugins/index_management/public';
 // @ts-ignore
 import { setEsBaseAndXPackBase, setHttp } from './crud_app/services';
 import { setNotifications, setFatalErrors } from './kibana_services';
@@ -39,30 +39,28 @@ export interface RollupPluginSetupDependencies {
     aggTypeFieldFilters: AggTypeFieldFilters;
     addSearchStrategy: (searchStrategy: SearchStrategyProvider) => void;
     managementLegacy: ManagementSetupLegacy;
-    indexManagementExtensions: IndexMgmtSetup['extensionsService'];
   };
   home?: HomePublicPluginSetup;
   management: ManagementSetup;
+  indexManagement?: IndexMgmtSetup;
 }
 
 export class RollupPlugin implements Plugin {
   setup(
     core: CoreSetup,
     {
-      __LEGACY: {
-        aggTypeFilters,
-        aggTypeFieldFilters,
-        addSearchStrategy,
-        managementLegacy,
-        indexManagementExtensions,
-      },
+      __LEGACY: { aggTypeFilters, aggTypeFieldFilters, addSearchStrategy, managementLegacy },
       home,
       management,
+      indexManagement,
     }: RollupPluginSetupDependencies
   ) {
     setFatalErrors(core.fatalErrors);
-    indexManagementExtensions.addBadge(rollupBadgeExtension);
-    indexManagementExtensions.addToggle(rollupToggleExtension);
+
+    if (indexManagement) {
+      indexManagement.extensionsService.addBadge(rollupBadgeExtension);
+      indexManagement.extensionsService.addToggle(rollupToggleExtension);
+    }
 
     const isRollupIndexPatternsEnabled = core.uiSettings.get(CONFIG_ROLLUPS);
 
diff --git a/x-pack/legacy/plugins/rollup/server/plugin.ts b/x-pack/legacy/plugins/rollup/server/plugin.ts
index 52b1e31af4eb2..090cb8a47377a 100644
--- a/x-pack/legacy/plugins/rollup/server/plugin.ts
+++ b/x-pack/legacy/plugins/rollup/server/plugin.ts
@@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n';
 
 import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
 import { VisTypeTimeseriesSetup } from 'src/plugins/vis_type_timeseries/server';
+import { IndexMgmtSetup } from '../../../../plugins/index_management/server';
 import { registerLicenseChecker } from '../../../server/lib/register_license_checker';
 import { PLUGIN } from '../common';
 import { ServerShim, RouteDependencies } from './types';
@@ -38,10 +39,12 @@ export class RollupsServerPlugin implements Plugin<void, void, any, any> {
       __LEGACY: serverShim,
       usageCollection,
       metrics,
+      indexManagement,
     }: {
       __LEGACY: ServerShim;
       usageCollection?: UsageCollectionSetup;
       metrics?: VisTypeTimeseriesSetup;
+      indexManagement?: IndexMgmtSetup;
     }
   ) {
     const elasticsearch = await elasticsearchService.adminClient;
@@ -76,11 +79,8 @@ export class RollupsServerPlugin implements Plugin<void, void, any, any> {
         });
     }
 
-    if (
-      serverShim.plugins.index_management &&
-      serverShim.plugins.index_management.addIndexManagementDataEnricher
-    ) {
-      serverShim.plugins.index_management.addIndexManagementDataEnricher(rollupDataEnricher);
+    if (indexManagement && indexManagement.indexDataEnricher) {
+      indexManagement.indexDataEnricher.add(rollupDataEnricher);
     }
 
     if (metrics) {
diff --git a/x-pack/legacy/plugins/rollup/server/rollup_data_enricher.ts b/x-pack/legacy/plugins/rollup/server/rollup_data_enricher.ts
index 7c5e160c54a31..ad621f2d9ba80 100644
--- a/x-pack/legacy/plugins/rollup/server/rollup_data_enricher.ts
+++ b/x-pack/legacy/plugins/rollup/server/rollup_data_enricher.ts
@@ -4,14 +4,11 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-interface Index {
-  name: string;
-  [key: string]: unknown;
-}
+import { Index } from '../../../../plugins/index_management/server';
 
 export const rollupDataEnricher = async (indicesList: Index[], callWithRequest: any) => {
   if (!indicesList || !indicesList.length) {
-    return indicesList;
+    return Promise.resolve(indicesList);
   }
 
   const params = {
diff --git a/x-pack/legacy/plugins/rollup/server/types.ts b/x-pack/legacy/plugins/rollup/server/types.ts
index 62a4841133cff..bcc6770e9b8ee 100644
--- a/x-pack/legacy/plugins/rollup/server/types.ts
+++ b/x-pack/legacy/plugins/rollup/server/types.ts
@@ -11,7 +11,6 @@ export interface ServerShim {
   plugins: {
     xpack_main: XPackMainPlugin;
     rollup: any;
-    index_management: any;
   };
 }
 
diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/constants.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/constants.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/constants.ts
rename to x-pack/plugins/index_management/__jest__/client_integration/helpers/constants.ts
diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts
similarity index 96%
rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts
rename to x-pack/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts
index e5f0b25d89c3e..7e3e1fba9c44a 100644
--- a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts
+++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts
@@ -12,10 +12,10 @@ import {
   TestBedConfig,
   findTestSubject,
   nextTick,
-} from '../../../../../../test_utils';
-import { IndexManagementHome } from '../../../public/application/sections/home';
+} from '../../../../../test_utils';
+import { IndexManagementHome } from '../../../public/application/sections/home'; // eslint-disable-line @kbn/eslint/no-restricted-paths
 import { BASE_PATH } from '../../../common/constants';
-import { indexManagementStore } from '../../../public/application/store';
+import { indexManagementStore } from '../../../public/application/store'; // eslint-disable-line @kbn/eslint/no-restricted-paths
 import { Template } from '../../../common/types';
 import { WithAppDependencies, services } from './setup_environment';
 
diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts
rename to x-pack/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts
diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/index.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/index.ts
similarity index 95%
rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/index.ts
rename to x-pack/plugins/index_management/__jest__/client_integration/helpers/index.ts
index 6dce4453a67f9..66021b531919a 100644
--- a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/index.ts
+++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/index.ts
@@ -9,7 +9,7 @@ import { setup as templateCreateSetup } from './template_create.helpers';
 import { setup as templateCloneSetup } from './template_clone.helpers';
 import { setup as templateEditSetup } from './template_edit.helpers';
 
-export { nextTick, getRandomString, findTestSubject, TestBed } from '../../../../../../test_utils';
+export { nextTick, getRandomString, findTestSubject, TestBed } from '../../../../../test_utils';
 
 export { setupEnvironment } from './setup_environment';
 
diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx b/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx
similarity index 95%
rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx
rename to x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx
index 0212efe1f322d..1eaf7efd17395 100644
--- a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx
+++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx
@@ -3,6 +3,7 @@
  * or more contributor license agreements. Licensed under the Elastic License;
  * you may not use this file except in compliance with the Elastic License.
  */
+/* eslint-disable @kbn/eslint/no-restricted-paths */
 import React from 'react';
 import axios from 'axios';
 import axiosXhrAdapter from 'axios/lib/adapters/xhr';
@@ -10,7 +11,7 @@ import axiosXhrAdapter from 'axios/lib/adapters/xhr';
 import {
   notificationServiceMock,
   docLinksServiceMock,
-} from '../../../../../../../src/core/public/mocks';
+} from '../../../../../../src/core/public/mocks';
 import { AppContextProvider } from '../../../public/application/app_context';
 import { httpService } from '../../../public/application/services/http';
 import { breadcrumbService } from '../../../public/application/services/breadcrumbs';
diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_clone.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_clone.helpers.ts
similarity index 85%
rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_clone.helpers.ts
rename to x-pack/plugins/index_management/__jest__/client_integration/helpers/template_clone.helpers.ts
index cd1b67c08d934..36498b99ba143 100644
--- a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_clone.helpers.ts
+++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_clone.helpers.ts
@@ -4,9 +4,9 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { registerTestBed, TestBedConfig } from '../../../../../../test_utils';
+import { registerTestBed, TestBedConfig } from '../../../../../test_utils';
 import { BASE_PATH } from '../../../common/constants';
-import { TemplateClone } from '../../../public/application/sections/template_clone';
+import { TemplateClone } from '../../../public/application/sections/template_clone'; // eslint-disable-line @kbn/eslint/no-restricted-paths
 import { formSetup } from './template_form.helpers';
 import { TEMPLATE_NAME } from './constants';
 import { WithAppDependencies } from './setup_environment';
diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_create.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_create.helpers.ts
similarity index 84%
rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_create.helpers.ts
rename to x-pack/plugins/index_management/__jest__/client_integration/helpers/template_create.helpers.ts
index 8e62bc25d6bd1..14a44968a93c3 100644
--- a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_create.helpers.ts
+++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_create.helpers.ts
@@ -4,9 +4,9 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { registerTestBed, TestBedConfig } from '../../../../../../test_utils';
+import { registerTestBed, TestBedConfig } from '../../../../../test_utils';
 import { BASE_PATH } from '../../../common/constants';
-import { TemplateCreate } from '../../../public/application/sections/template_create';
+import { TemplateCreate } from '../../../public/application/sections/template_create'; // eslint-disable-line @kbn/eslint/no-restricted-paths
 import { formSetup, TestSubjects } from './template_form.helpers';
 import { WithAppDependencies } from './setup_environment';
 
diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_edit.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_edit.helpers.ts
similarity index 85%
rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_edit.helpers.ts
rename to x-pack/plugins/index_management/__jest__/client_integration/helpers/template_edit.helpers.ts
index cb1025234b48e..af5fa8b79ecad 100644
--- a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_edit.helpers.ts
+++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_edit.helpers.ts
@@ -4,9 +4,9 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { registerTestBed, TestBedConfig } from '../../../../../../test_utils';
+import { registerTestBed, TestBedConfig } from '../../../../../test_utils';
 import { BASE_PATH } from '../../../common/constants';
-import { TemplateEdit } from '../../../public/application/sections/template_edit';
+import { TemplateEdit } from '../../../public/application/sections/template_edit'; // eslint-disable-line @kbn/eslint/no-restricted-paths
 import { formSetup, TestSubjects } from './template_form.helpers';
 import { TEMPLATE_NAME } from './constants';
 import { WithAppDependencies } from './setup_environment';
diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts
similarity index 99%
rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts
rename to x-pack/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts
index a7c87723b33fb..134c67c278b22 100644
--- a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts
+++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/template_form.helpers.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { TestBed, SetupFunc, UnwrapPromise } from '../../../../../../test_utils';
+import { TestBed, SetupFunc, UnwrapPromise } from '../../../../../test_utils';
 import { Template } from '../../../common/types';
 import { nextTick } from './index';
 
diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/home.test.ts b/x-pack/plugins/index_management/__jest__/client_integration/home.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/home.test.ts
rename to x-pack/plugins/index_management/__jest__/client_integration/home.test.ts
diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/template_clone.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/template_clone.test.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/template_clone.test.tsx
rename to x-pack/plugins/index_management/__jest__/client_integration/template_clone.test.tsx
diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/template_create.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/template_create.test.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/template_create.test.tsx
rename to x-pack/plugins/index_management/__jest__/client_integration/template_create.test.tsx
diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/template_edit.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/template_edit.test.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/__jest__/client_integration/template_edit.test.tsx
rename to x-pack/plugins/index_management/__jest__/client_integration/template_edit.test.tsx
diff --git a/x-pack/legacy/plugins/index_management/__jest__/components/__snapshots__/index_table.test.js.snap b/x-pack/plugins/index_management/__jest__/components/__snapshots__/index_table.test.js.snap
similarity index 100%
rename from x-pack/legacy/plugins/index_management/__jest__/components/__snapshots__/index_table.test.js.snap
rename to x-pack/plugins/index_management/__jest__/components/__snapshots__/index_table.test.js.snap
diff --git a/x-pack/legacy/plugins/index_management/__jest__/components/index_table.test.js b/x-pack/plugins/index_management/__jest__/components/index_table.test.js
similarity index 98%
rename from x-pack/legacy/plugins/index_management/__jest__/components/index_table.test.js
rename to x-pack/plugins/index_management/__jest__/components/index_table.test.js
index 2b4fd89436458..15c3ef0b84562 100644
--- a/x-pack/legacy/plugins/index_management/__jest__/components/index_table.test.js
+++ b/x-pack/plugins/index_management/__jest__/components/index_table.test.js
@@ -20,13 +20,13 @@ import { setUiMetricService } from '../../public/application/services/api';
 import { indexManagementStore } from '../../public/application/store';
 import { setExtensionsService } from '../../public/application/store/selectors';
 import { BASE_PATH, API_BASE_PATH } from '../../common/constants';
-import { mountWithIntl } from '../../../../../test_utils/enzyme_helpers';
+import { mountWithIntl } from '../../../../test_utils/enzyme_helpers';
 import { ExtensionsService } from '../../public/services';
 import sinon from 'sinon';
 import { findTestSubject } from '@elastic/eui/lib/test';
 
 /* eslint-disable @kbn/eslint/no-restricted-paths */
-import { notificationServiceMock } from '../../../../../../src/core/public/notifications/notifications_service.mock';
+import { notificationServiceMock } from '../../../../../src/core/public/notifications/notifications_service.mock';
 
 jest.mock('ui/new_platform');
 
diff --git a/x-pack/legacy/plugins/index_management/__jest__/lib/__snapshots__/flatten_object.test.js.snap b/x-pack/plugins/index_management/__jest__/lib/__snapshots__/flatten_object.test.js.snap
similarity index 100%
rename from x-pack/legacy/plugins/index_management/__jest__/lib/__snapshots__/flatten_object.test.js.snap
rename to x-pack/plugins/index_management/__jest__/lib/__snapshots__/flatten_object.test.js.snap
diff --git a/x-pack/legacy/plugins/index_management/__jest__/lib/flatten_object.test.js b/x-pack/plugins/index_management/__jest__/lib/flatten_object.test.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/__jest__/lib/flatten_object.test.js
rename to x-pack/plugins/index_management/__jest__/lib/flatten_object.test.js
diff --git a/x-pack/legacy/plugins/index_management/__mocks__/ace.js b/x-pack/plugins/index_management/__mocks__/ace.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/__mocks__/ace.js
rename to x-pack/plugins/index_management/__mocks__/ace.js
diff --git a/x-pack/legacy/plugins/index_management/__mocks__/ui/documentation_links.js b/x-pack/plugins/index_management/__mocks__/ui/documentation_links.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/__mocks__/ui/documentation_links.js
rename to x-pack/plugins/index_management/__mocks__/ui/documentation_links.js
diff --git a/x-pack/legacy/plugins/index_management/__mocks__/ui/notify.js b/x-pack/plugins/index_management/__mocks__/ui/notify.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/__mocks__/ui/notify.js
rename to x-pack/plugins/index_management/__mocks__/ui/notify.js
diff --git a/x-pack/legacy/plugins/index_management/common/constants/api_base_path.ts b/x-pack/plugins/index_management/common/constants/api_base_path.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/common/constants/api_base_path.ts
rename to x-pack/plugins/index_management/common/constants/api_base_path.ts
diff --git a/x-pack/legacy/plugins/index_management/common/constants/base_path.ts b/x-pack/plugins/index_management/common/constants/base_path.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/common/constants/base_path.ts
rename to x-pack/plugins/index_management/common/constants/base_path.ts
diff --git a/x-pack/legacy/plugins/index_management/common/constants/index.ts b/x-pack/plugins/index_management/common/constants/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/common/constants/index.ts
rename to x-pack/plugins/index_management/common/constants/index.ts
diff --git a/x-pack/legacy/plugins/index_management/common/constants/index_statuses.ts b/x-pack/plugins/index_management/common/constants/index_statuses.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/common/constants/index_statuses.ts
rename to x-pack/plugins/index_management/common/constants/index_statuses.ts
diff --git a/x-pack/legacy/plugins/index_management/common/constants/invalid_characters.ts b/x-pack/plugins/index_management/common/constants/invalid_characters.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/common/constants/invalid_characters.ts
rename to x-pack/plugins/index_management/common/constants/invalid_characters.ts
diff --git a/x-pack/legacy/plugins/index_management/common/constants/plugin.ts b/x-pack/plugins/index_management/common/constants/plugin.ts
similarity index 86%
rename from x-pack/legacy/plugins/index_management/common/constants/plugin.ts
rename to x-pack/plugins/index_management/common/constants/plugin.ts
index 2cd137f62d3db..e25f537edcf8d 100644
--- a/x-pack/legacy/plugins/index_management/common/constants/plugin.ts
+++ b/x-pack/plugins/index_management/common/constants/plugin.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { LicenseType } from '../../../../../plugins/licensing/common/types';
+import { LicenseType } from '../../../licensing/common/types';
 
 const basicLicense: LicenseType = 'basic';
 
diff --git a/x-pack/legacy/plugins/index_management/common/constants/ui_metric.ts b/x-pack/plugins/index_management/common/constants/ui_metric.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/common/constants/ui_metric.ts
rename to x-pack/plugins/index_management/common/constants/ui_metric.ts
diff --git a/x-pack/legacy/plugins/index_management/common/index.ts b/x-pack/plugins/index_management/common/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/common/index.ts
rename to x-pack/plugins/index_management/common/index.ts
diff --git a/x-pack/legacy/plugins/index_management/common/lib/index.ts b/x-pack/plugins/index_management/common/lib/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/common/lib/index.ts
rename to x-pack/plugins/index_management/common/lib/index.ts
diff --git a/x-pack/legacy/plugins/index_management/common/lib/template_serialization.ts b/x-pack/plugins/index_management/common/lib/template_serialization.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/common/lib/template_serialization.ts
rename to x-pack/plugins/index_management/common/lib/template_serialization.ts
diff --git a/x-pack/legacy/plugins/index_management/common/types/index.ts b/x-pack/plugins/index_management/common/types/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/common/types/index.ts
rename to x-pack/plugins/index_management/common/types/index.ts
diff --git a/x-pack/legacy/plugins/index_management/common/types/templates.ts b/x-pack/plugins/index_management/common/types/templates.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/common/types/templates.ts
rename to x-pack/plugins/index_management/common/types/templates.ts
diff --git a/x-pack/plugins/index_management/kibana.json b/x-pack/plugins/index_management/kibana.json
new file mode 100644
index 0000000000000..7387a042988c0
--- /dev/null
+++ b/x-pack/plugins/index_management/kibana.json
@@ -0,0 +1,15 @@
+{
+  "id": "indexManagement",
+  "version": "kibana",
+  "server": true,
+  "ui": true,
+  "requiredPlugins": [
+    "home",
+    "licensing",
+    "management"
+  ],
+  "optionalPlugins": [
+    "usageCollection"
+  ],
+  "configPath": ["xpack", "index_management"]
+}
diff --git a/x-pack/legacy/plugins/index_management/public/application/app.tsx b/x-pack/plugins/index_management/public/application/app.tsx
similarity index 98%
rename from x-pack/legacy/plugins/index_management/public/application/app.tsx
rename to x-pack/plugins/index_management/public/application/app.tsx
index 3b475f3baba04..83997dd6ece18 100644
--- a/x-pack/legacy/plugins/index_management/public/application/app.tsx
+++ b/x-pack/plugins/index_management/public/application/app.tsx
@@ -16,7 +16,7 @@ import { useServices } from './app_context';
 
 export const App = () => {
   const { uiMetricService } = useServices();
-  useEffect(() => uiMetricService.trackMetric('loaded', UIM_APP_LOAD), []);
+  useEffect(() => uiMetricService.trackMetric('loaded', UIM_APP_LOAD), [uiMetricService]);
 
   return (
     <HashRouter>
diff --git a/x-pack/legacy/plugins/index_management/public/application/app_context.tsx b/x-pack/plugins/index_management/public/application/app_context.tsx
similarity index 90%
rename from x-pack/legacy/plugins/index_management/public/application/app_context.tsx
rename to x-pack/plugins/index_management/public/application/app_context.tsx
index 12e0d362a2930..2bb618ad8efce 100644
--- a/x-pack/legacy/plugins/index_management/public/application/app_context.tsx
+++ b/x-pack/plugins/index_management/public/application/app_context.tsx
@@ -5,9 +5,9 @@
  */
 
 import React, { createContext, useContext } from 'react';
-import { CoreStart } from '../../../../../../src/core/public';
+import { CoreStart } from '../../../../../src/core/public';
 
-import { UsageCollectionSetup } from '../../../../../../src/plugins/usage_collection/public';
+import { UsageCollectionSetup } from '../../../../../src/plugins/usage_collection/public';
 import { IndexMgmtMetricsType } from '../types';
 import { UiMetricService, NotificationService, HttpService } from './services';
 import { ExtensionsService } from '../services';
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/index.ts b/x-pack/plugins/index_management/public/application/components/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/index.ts
rename to x-pack/plugins/index_management/public/application/components/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts
similarity index 90%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts
index e3313bfba56fd..6d64cb73da4bd 100644
--- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts
@@ -10,7 +10,7 @@ export {
   getRandomString,
   findTestSubject,
   TestBed,
-} from '../../../../../../../../../../test_utils';
+} from '../../../../../../../../../test_utils';
 
 export const componentHelpers = {
   mappingsEditor: { setup: mappingsEditorSetup },
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.ts
similarity index 85%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.ts
index dcee956130a29..acb416654023e 100644
--- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.ts
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { registerTestBed } from '../../../../../../../../../../test_utils';
+import { registerTestBed } from '../../../../../../../../../test_utils';
 import { MappingsEditor } from '../../../mappings_editor';
 
 export const setup = (props: any) =>
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/_index.scss b/x-pack/plugins/index_management/public/application/components/mappings_editor/_index.scss
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/_index.scss
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/_index.scss
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/_index.scss b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/_index.scss
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/_index.scss
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/_index.scss
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/code_block.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/code_block.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/code_block.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/code_block.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form.tsx
similarity index 98%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form.tsx
index 0c5c9e2a15b75..9b0b8420f9ea9 100644
--- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form.tsx
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form.tsx
@@ -110,7 +110,7 @@ export const ConfigurationForm = React.memo(({ defaultValue }: Props) => {
       });
     });
     return subscription.unsubscribe;
-  }, [form]);
+  }, [form, dispatch]);
 
   useEffect(() => {
     if (didMountRef.current) {
@@ -121,14 +121,14 @@ export const ConfigurationForm = React.memo(({ defaultValue }: Props) => {
       // Avoid reseting the form on component mount.
       didMountRef.current = true;
     }
-  }, [defaultValue]);
+  }, [defaultValue, form]);
 
   useEffect(() => {
     return () => {
       // On unmount => save in the state a snapshot of the current form data.
       dispatch({ type: 'configuration.save' });
     };
-  }, []);
+  }, [dispatch]);
 
   return (
     <Form form={form} isInvalid={form.isSubmitted && !form.isValid} error={form.getErrors()}>
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form_schema.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form_schema.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form_schema.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form_schema.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/dynamic_mapping_section.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/dynamic_mapping_section.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/dynamic_mapping_section.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/dynamic_mapping_section.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/index.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/index.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/index.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/meta_field_section.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/meta_field_section.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/meta_field_section.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/meta_field_section.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/routing_section.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/routing_section.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/routing_section.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/routing_section.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/index.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/source_field_section.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/source_field_section.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/source_field_section.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/source_field_section.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/_index.scss b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/_index.scss
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/_index.scss
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/_index.scss
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields.tsx
similarity index 91%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields.tsx
index 378d669dee69c..400de4052afa4 100644
--- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields.tsx
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields.tsx
@@ -24,7 +24,7 @@ export const DocumentFields = React.memo(() => {
     if (editorType === 'json') {
       return deNormalize(fields);
     }
-  }, [editorType]);
+  }, [editorType, fields]);
 
   const editor =
     editorType === 'json' ? (
@@ -41,9 +41,12 @@ export const DocumentFields = React.memo(() => {
     return <EditFieldContainer field={field} allFields={fields.byId} />;
   };
 
-  const onSearchChange = useCallback((value: string) => {
-    dispatch({ type: 'search:update', value });
-  }, []);
+  const onSearchChange = useCallback(
+    (value: string) => {
+      dispatch({ type: 'search:update', value });
+    },
+    [dispatch]
+  );
 
   const searchTerm = search.term.trim();
 
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields_header.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields_header.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields_header.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields_header.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/editor_toggle_controls.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/editor_toggle_controls.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/editor_toggle_controls.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/editor_toggle_controls.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter_selects.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter_selects.tsx
similarity index 86%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter_selects.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter_selects.tsx
index de3d70db31af4..a91231352c168 100644
--- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter_selects.tsx
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter_selects.tsx
@@ -56,15 +56,21 @@ export const AnalyzerParameterSelects = ({
     });
 
     return subscription.unsubscribe;
-  }, [form]);
+  }, [form, onChange]);
 
-  const getSubOptionsMeta = (mainValue: string) =>
-    mapOptionsToSubOptions !== undefined ? mapOptionsToSubOptions[mainValue] : undefined;
+  const getSubOptionsMeta = useCallback(
+    (mainValue: string) =>
+      mapOptionsToSubOptions !== undefined ? mapOptionsToSubOptions[mainValue] : undefined,
+    [mapOptionsToSubOptions]
+  );
 
-  const onMainValueChange = useCallback((mainValue: unknown) => {
-    const subOptionsMeta = getSubOptionsMeta(mainValue as string);
-    form.setFieldValue('sub', subOptionsMeta ? subOptionsMeta.options[0].value : undefined);
-  }, []);
+  const onMainValueChange = useCallback(
+    (mainValue: unknown) => {
+      const subOptionsMeta = getSubOptionsMeta(mainValue as string);
+      form.setFieldValue('sub', subOptionsMeta ? subOptionsMeta.options[0].value : undefined);
+    },
+    [form, getSubOptionsMeta]
+  );
 
   const renderSelect = (field: FieldHook, opts: Options) => {
     const isSuperSelect = areOptionsSuperSelect(opts);
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzers_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzers_parameter.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzers_parameter.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzers_parameter.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/boost_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/boost_parameter.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/boost_parameter.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/boost_parameter.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_number_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_number_parameter.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_number_parameter.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_number_parameter.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_shape_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_shape_parameter.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_shape_parameter.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_shape_parameter.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/copy_to_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/copy_to_parameter.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/copy_to_parameter.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/copy_to_parameter.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/doc_values_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/doc_values_parameter.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/doc_values_parameter.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/doc_values_parameter.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/dynamic_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/dynamic_parameter.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/dynamic_parameter.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/dynamic_parameter.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/eager_global_ordinals_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/eager_global_ordinals_parameter.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/eager_global_ordinals_parameter.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/eager_global_ordinals_parameter.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/enabled_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/enabled_parameter.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/enabled_parameter.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/enabled_parameter.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_absolute.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_absolute.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_absolute.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_absolute.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_percentage.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_percentage.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_percentage.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_percentage.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_parameter.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_parameter.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_parameter.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/format_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/format_parameter.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/format_parameter.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/format_parameter.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_malformed.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_malformed.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_malformed.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_malformed.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_z_value_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_z_value_parameter.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_z_value_parameter.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_z_value_parameter.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index_parameter.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index_parameter.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index_parameter.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/locale_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/locale_parameter.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/locale_parameter.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/locale_parameter.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/max_shingle_size_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/max_shingle_size_parameter.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/max_shingle_size_parameter.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/max_shingle_size_parameter.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/name_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/name_parameter.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/name_parameter.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/name_parameter.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/norms_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/norms_parameter.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/norms_parameter.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/norms_parameter.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/null_value_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/null_value_parameter.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/null_value_parameter.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/null_value_parameter.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/orientation_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/orientation_parameter.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/orientation_parameter.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/orientation_parameter.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/relations_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/relations_parameter.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/relations_parameter.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/relations_parameter.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/similarity_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/similarity_parameter.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/similarity_parameter.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/similarity_parameter.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/split_queries_on_whitespace_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/split_queries_on_whitespace_parameter.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/split_queries_on_whitespace_parameter.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/split_queries_on_whitespace_parameter.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/store_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/store_parameter.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/store_parameter.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/store_parameter.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/term_vector_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/term_vector_parameter.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/term_vector_parameter.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/term_vector_parameter.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/type_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/type_parameter.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/type_parameter.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/type_parameter.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_field_list_item.scss b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_field_list_item.scss
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_field_list_item.scss
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_field_list_item.scss
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_index.scss b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_index.scss
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_index.scss
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_index.scss
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx
similarity index 83%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx
index 5f1b8c0df770e..60b025ce644ef 100644
--- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx
@@ -66,7 +66,7 @@ export const CreateField = React.memo(function CreateFieldComponent({
     });
 
     return subscription.unsubscribe;
-  }, [form]);
+  }, [form, dispatch]);
 
   const cancel = () => {
     dispatch({ type: 'documentField.changeStatus', value: 'idle' });
@@ -108,43 +108,53 @@ export const CreateField = React.memo(function CreateFieldComponent({
    *
    * @param type The selected field type
    */
-  const getSubTypeMeta = (
-    type: MainType
-  ): { subTypeLabel?: string; subTypeOptions?: ComboBoxOption[] } => {
-    const typeDefinition = TYPE_DEFINITION[type];
-    const hasSubTypes = typeDefinition !== undefined && typeDefinition.subTypes;
-
-    let subTypeOptions = hasSubTypes
-      ? typeDefinition
-          .subTypes!.types.map(subType => TYPE_DEFINITION[subType])
-          .map(
-            subType => ({ value: subType.value as SubType, label: subType.label } as ComboBoxOption)
-          )
-      : undefined;
-
-    if (hasSubTypes) {
-      if (isMultiField) {
-        // If it is a multi-field, we need to filter out non-allowed types
-        subTypeOptions = filterTypesForMultiField<SubType>(subTypeOptions!);
-      } else if (isRootLevelField === false) {
-        subTypeOptions = filterTypesForNonRootFields(subTypeOptions!);
+  const getSubTypeMeta = useCallback(
+    (
+      type: MainType
+    ): {
+      subTypeLabel?: string;
+      subTypeOptions?: ComboBoxOption[];
+    } => {
+      const typeDefinition = TYPE_DEFINITION[type];
+      const hasSubTypes = typeDefinition !== undefined && typeDefinition.subTypes;
+
+      let subTypeOptions = hasSubTypes
+        ? typeDefinition
+            .subTypes!.types.map(subType => TYPE_DEFINITION[subType])
+            .map(
+              subType =>
+                ({ value: subType.value as SubType, label: subType.label } as ComboBoxOption)
+            )
+        : undefined;
+
+      if (hasSubTypes) {
+        if (isMultiField) {
+          // If it is a multi-field, we need to filter out non-allowed types
+          subTypeOptions = filterTypesForMultiField<SubType>(subTypeOptions!);
+        } else if (isRootLevelField === false) {
+          subTypeOptions = filterTypesForNonRootFields(subTypeOptions!);
+        }
       }
-    }
 
-    return {
-      subTypeOptions,
-      subTypeLabel: hasSubTypes ? typeDefinition.subTypes!.label : undefined,
-    };
-  };
+      return {
+        subTypeOptions,
+        subTypeLabel: hasSubTypes ? typeDefinition.subTypes!.label : undefined,
+      };
+    },
+    [isMultiField, isRootLevelField]
+  );
 
-  const onTypeChange = (nextType: ComboBoxOption[]) => {
-    form.setFieldValue('type', nextType);
+  const onTypeChange = useCallback(
+    (nextType: ComboBoxOption[]) => {
+      form.setFieldValue('type', nextType);
 
-    if (nextType.length) {
-      const { subTypeOptions } = getSubTypeMeta(nextType[0].value as MainType);
-      form.setFieldValue('subType', subTypeOptions ? [subTypeOptions[0]] : undefined);
-    }
-  };
+      if (nextType.length) {
+        const { subTypeOptions } = getSubTypeMeta(nextType[0].value as MainType);
+        form.setFieldValue('subType', subTypeOptions ? [subTypeOptions[0]] : undefined);
+      }
+    },
+    [form, getSubTypeMeta]
+  );
 
   const renderFormFields = useCallback(
     ({ type }) => {
@@ -208,7 +218,7 @@ export const CreateField = React.memo(function CreateFieldComponent({
         </EuiFlexItem>
       );
     },
-    [form, isMultiField]
+    [getSubTypeMeta, isMultiField, isRootLevelField, onTypeChange]
   );
 
   const renderFormActions = () => (
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/index.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/alias_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/alias_type.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/alias_type.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/alias_type.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/dense_vector_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/dense_vector_type.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/dense_vector_type.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/dense_vector_type.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/index.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/scaled_float_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/scaled_float_type.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/scaled_float_type.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/scaled_float_type.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/token_count_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/token_count_type.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/token_count_type.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/token_count_type.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/delete_field_provider.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/delete_field_provider.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/delete_field_provider.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/delete_field_provider.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_edit_field_form_row.scss b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_edit_field_form_row.scss
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_edit_field_form_row.scss
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_edit_field_form_row.scss
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_index.scss b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_index.scss
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_index.scss
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_index.scss
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/advanced_parameters_section.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/advanced_parameters_section.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/advanced_parameters_section.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/advanced_parameters_section.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/basic_parameters_section.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/basic_parameters_section.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/basic_parameters_section.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/basic_parameters_section.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx
similarity index 97%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx
index 284ae8803acb5..1f77584439568 100644
--- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx
@@ -32,11 +32,11 @@ export const EditFieldContainer = React.memo(({ field, allFields }: Props) => {
     });
 
     return subscription.unsubscribe;
-  }, [form]);
+  }, [form, dispatch]);
 
   const exitEdit = useCallback(() => {
     dispatch({ type: 'documentField.changeStatus', value: 'idle' });
-  }, []);
+  }, [dispatch]);
 
   return <EditField form={form} field={field} allFields={allFields} exitEdit={exitEdit} />;
 });
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_form_row.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_form_row.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_form_row.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_form_row.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/field_description_section.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/field_description_section.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/field_description_section.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/field_description_section.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/index.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/update_field_provider.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/update_field_provider.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/update_field_provider.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/update_field_provider.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/alias_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/alias_type.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/alias_type.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/alias_type.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/binary_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/binary_type.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/binary_type.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/binary_type.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/boolean_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/boolean_type.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/boolean_type.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/boolean_type.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/completion_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/completion_type.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/completion_type.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/completion_type.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/date_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/date_type.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/date_type.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/date_type.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/dense_vector_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/dense_vector_type.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/dense_vector_type.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/dense_vector_type.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/flattened_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/flattened_type.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/flattened_type.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/flattened_type.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_point_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_point_type.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_point_type.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_point_type.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_shape_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_shape_type.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_shape_type.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_shape_type.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/ip_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/ip_type.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/ip_type.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/ip_type.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/join_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/join_type.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/join_type.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/join_type.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/keyword_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/keyword_type.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/keyword_type.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/keyword_type.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/nested_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/nested_type.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/nested_type.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/nested_type.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/numeric_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/numeric_type.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/numeric_type.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/numeric_type.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/object_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/object_type.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/object_type.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/object_type.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/range_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/range_type.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/range_type.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/range_type.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/search_as_you_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/search_as_you_type.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/search_as_you_type.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/search_as_you_type.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/shape_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/shape_type.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/shape_type.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/shape_type.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/text_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/text_type.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/text_type.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/text_type.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/token_count_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/token_count_type.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/token_count_type.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/token_count_type.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx
similarity index 92%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx
index cff2d294fead9..55093e606cfa1 100644
--- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx
@@ -23,7 +23,7 @@ export const FieldsListItemContainer = ({ fieldId, treeDepth, isLastItem }: Prop
     fields: { byId, maxNestedDepth },
   } = useMappingsState();
 
-  const getField = (id: string) => byId[id];
+  const getField = useCallback((id: string) => byId[id], [byId]);
 
   const field: NormalizedField = getField(fieldId);
   const { childFields } = field;
@@ -33,7 +33,7 @@ export const FieldsListItemContainer = ({ fieldId, treeDepth, isLastItem }: Prop
   const areActionButtonsVisible = status === 'idle';
   const childFieldsArray = useMemo(
     () => (childFields !== undefined ? childFields.map(getField) : []),
-    [childFields]
+    [childFields, getField]
   );
 
   const addField = useCallback(() => {
@@ -41,18 +41,18 @@ export const FieldsListItemContainer = ({ fieldId, treeDepth, isLastItem }: Prop
       type: 'documentField.createField',
       value: fieldId,
     });
-  }, [fieldId]);
+  }, [fieldId, dispatch]);
 
   const editField = useCallback(() => {
     dispatch({
       type: 'documentField.editField',
       value: fieldId,
     });
-  }, [fieldId]);
+  }, [fieldId, dispatch]);
 
   const toggleExpand = useCallback(() => {
     dispatch({ type: 'field.toggleExpand', value: { fieldId } });
-  }, [fieldId]);
+  }, [fieldId, dispatch]);
 
   return (
     <FieldsListItem
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/index.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/modal_confirmation_delete_fields.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/modal_confirmation_delete_fields.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/modal_confirmation_delete_fields.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/modal_confirmation_delete_fields.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields_json_editor.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields_json_editor.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields_json_editor.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields_json_editor.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields_tree_editor.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields_tree_editor.tsx
similarity index 88%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields_tree_editor.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields_tree_editor.tsx
index f85482c22e3b1..cb0016e967c42 100644
--- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields_tree_editor.tsx
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields_tree_editor.tsx
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import React, { useEffect, useMemo } from 'react';
+import React, { useEffect, useMemo, useCallback } from 'react';
 import { EuiButtonEmpty, EuiSpacer } from '@elastic/eui';
 import { i18n } from '@kbn/i18n';
 
@@ -18,12 +18,12 @@ export const DocumentFieldsTreeEditor = () => {
     documentFields: { status, fieldToAddFieldTo },
   } = useMappingsState();
 
-  const getField = (fieldId: string) => byId[fieldId];
-  const fields = useMemo(() => rootLevelFields.map(getField), [rootLevelFields]);
+  const getField = useCallback((fieldId: string) => byId[fieldId], [byId]);
+  const fields = useMemo(() => rootLevelFields.map(getField), [rootLevelFields, getField]);
 
-  const addField = () => {
+  const addField = useCallback(() => {
     dispatch({ type: 'documentField.createField' });
-  };
+  }, [dispatch]);
 
   useEffect(() => {
     /**
@@ -32,7 +32,7 @@ export const DocumentFieldsTreeEditor = () => {
     if (status === 'idle' && fields.length === 0) {
       addField();
     }
-  }, [fields, status]);
+  }, [addField, fields, status]);
 
   const renderCreateField = () => {
     // The "fieldToAddFieldTo" is undefined when adding to the top level "properties" object.
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/index.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/index.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result_item.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result_item.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result_item.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result_item.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/fields_tree.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/fields_tree.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/fields_tree.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/fields_tree.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/index.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/index.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_from_json_button.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_from_json_button.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_from_json_button.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_from_json_button.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx
similarity index 98%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx
index a9433d3a7530f..f8e3eca7898d2 100644
--- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx
@@ -19,7 +19,7 @@ jest.mock('@elastic/eui', () => ({
   ),
 }));
 
-import { registerTestBed, nextTick, TestBed } from '../../../../../../../../../test_utils';
+import { registerTestBed, nextTick, TestBed } from '../../../../../../../../test_utils';
 import { LoadMappingsProvider } from './load_mappings_provider';
 
 const ComponentToTest = ({ onJson }: { onJson: () => void }) => (
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/multiple_mappings_warning.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/multiple_mappings_warning.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/multiple_mappings_warning.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/multiple_mappings_warning.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/templates_form/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/templates_form/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/templates_form/index.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/templates_form/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form.tsx
similarity index 98%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form.tsx
index 471217108ba6f..f32fcb3956e1c 100644
--- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form.tsx
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form.tsx
@@ -69,7 +69,7 @@ export const TemplatesForm = React.memo(({ defaultValue }: Props) => {
       });
     });
     return subscription.unsubscribe;
-  }, [form]);
+  }, [form, dispatch]);
 
   useEffect(() => {
     if (didMountRef.current) {
@@ -80,14 +80,14 @@ export const TemplatesForm = React.memo(({ defaultValue }: Props) => {
       // Avoid reseting the form on component mount.
       didMountRef.current = true;
     }
-  }, [defaultValue]);
+  }, [defaultValue, form]);
 
   useEffect(() => {
     return () => {
       // On unmount => save in the state a snapshot of the current form data.
       dispatch({ type: 'templates.save' });
     };
-  }, []);
+  }, [dispatch]);
 
   return (
     <>
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form_schema.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form_schema.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form_schema.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form_schema.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/tree/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/tree/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/tree/index.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/tree/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/tree/tree.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/tree/tree.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/tree/tree.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/tree/tree.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/tree/tree_item.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/tree/tree_item.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/tree/tree_item.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/components/tree/tree_item.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/default_values.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/default_values.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/default_values.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/constants/default_values.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/field_options.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/field_options.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/field_options.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/constants/field_options.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/field_options_i18n.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/field_options_i18n.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/field_options_i18n.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/constants/field_options_i18n.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/index.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/constants/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/mappings_editor.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/mappings_editor.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/mappings_editor.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/constants/mappings_editor.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/index.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/index_settings_context.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/index_settings_context.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/index_settings_context.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/index_settings_context.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/error_reporter.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/error_reporter.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/error_reporter.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/error_reporter.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.test.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.test.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.test.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/index.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.test.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.test.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.test.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.test.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.test.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.test.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/serializers.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/serializers.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/serializers.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/serializers.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/validators.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/validators.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/validators.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/lib/validators.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/mappings_editor.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_editor.tsx
similarity index 98%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/mappings_editor.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_editor.tsx
index d79a023386e8d..b6345a7140e15 100644
--- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/mappings_editor.tsx
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_editor.tsx
@@ -78,7 +78,7 @@ export const MappingsEditor = React.memo(({ onUpdate, defaultValue, indexSetting
         isValid: true,
       });
     }
-  }, [multipleMappingsDeclared]);
+  }, [multipleMappingsDeclared, onUpdate, defaultValue]);
 
   const changeTab = async (tab: TabName, state: State) => {
     if (selectedTab === 'advanced') {
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/mappings_state.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_state.tsx
similarity index 98%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/mappings_state.tsx
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_state.tsx
index 65a1aa2858d14..247bd183baddf 100644
--- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/mappings_state.tsx
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/mappings_state.tsx
@@ -168,7 +168,7 @@ export const MappingsState = React.memo(({ children, onUpdate, defaultValue }: P
       },
       isValid: state.isValid,
     });
-  }, [state]);
+  }, [state, onUpdate]);
 
   useEffect(() => {
     /**
@@ -187,7 +187,7 @@ export const MappingsState = React.memo(({ children, onUpdate, defaultValue }: P
     } else {
       didMountRef.current = true;
     }
-  }, [defaultValue]);
+  }, [defaultValue, parsedFieldsDefaultValue]);
 
   return (
     <StateContext.Provider value={state}>
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/reducer.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/reducer.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/reducer.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/reducer.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts
similarity index 72%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts
index e99d8840d57df..2979015c07455 100644
--- a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts
@@ -24,7 +24,7 @@ export {
   VALIDATION_TYPES,
   ValidationFunc,
   ValidationFuncArg,
-} from '../../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib';
+} from '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib';
 
 export {
   CheckBoxField,
@@ -39,14 +39,14 @@ export {
   ComboBoxField,
   ToggleField,
   JsonEditorField,
-} from '../../../../../../../../src/plugins/es_ui_shared/static/forms/components';
+} from '../../../../../../../src/plugins/es_ui_shared/static/forms/components';
 
 export {
   fieldFormatters,
   fieldValidators,
-} from '../../../../../../../../src/plugins/es_ui_shared/static/forms/helpers';
+} from '../../../../../../../src/plugins/es_ui_shared/static/forms/helpers';
 
 export {
   JsonEditor,
   OnJsonEditorUpdateHandler,
-} from '../../../../../../../../src/plugins/es_ui_shared/public';
+} from '../../../../../../../src/plugins/es_ui_shared/public';
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/types.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/types.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/types.ts
rename to x-pack/plugins/index_management/public/application/components/mappings_editor/types.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/no_match/index.ts b/x-pack/plugins/index_management/public/application/components/no_match/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/no_match/index.ts
rename to x-pack/plugins/index_management/public/application/components/no_match/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/no_match/no_match.tsx b/x-pack/plugins/index_management/public/application/components/no_match/no_match.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/no_match/no_match.tsx
rename to x-pack/plugins/index_management/public/application/components/no_match/no_match.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/page_error/index.ts b/x-pack/plugins/index_management/public/application/components/page_error/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/page_error/index.ts
rename to x-pack/plugins/index_management/public/application/components/page_error/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/page_error/page_error_forbidden.tsx b/x-pack/plugins/index_management/public/application/components/page_error/page_error_forbidden.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/page_error/page_error_forbidden.tsx
rename to x-pack/plugins/index_management/public/application/components/page_error/page_error_forbidden.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/section_error.tsx b/x-pack/plugins/index_management/public/application/components/section_error.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/section_error.tsx
rename to x-pack/plugins/index_management/public/application/components/section_error.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/section_loading.tsx b/x-pack/plugins/index_management/public/application/components/section_loading.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/section_loading.tsx
rename to x-pack/plugins/index_management/public/application/components/section_loading.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_delete_modal.tsx b/x-pack/plugins/index_management/public/application/components/template_delete_modal.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/template_delete_modal.tsx
rename to x-pack/plugins/index_management/public/application/components/template_delete_modal.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/index.ts b/x-pack/plugins/index_management/public/application/components/template_form/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/index.ts
rename to x-pack/plugins/index_management/public/application/components/template_form/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/index.ts b/x-pack/plugins/index_management/public/application/components/template_form/steps/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/index.ts
rename to x-pack/plugins/index_management/public/application/components/template_form/steps/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_aliases.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_aliases.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_aliases.tsx
rename to x-pack/plugins/index_management/public/application/components/template_form/steps/step_aliases.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx
similarity index 94%
rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx
rename to x-pack/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx
index dd8d49a569042..2f6e055b5d0c6 100644
--- a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx
+++ b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx
@@ -7,15 +7,8 @@ import React, { useEffect } from 'react';
 import { EuiFlexGroup, EuiFlexItem, EuiTitle, EuiButtonEmpty, EuiSpacer } from '@elastic/eui';
 import { FormattedMessage } from '@kbn/i18n/react';
 import { i18n } from '@kbn/i18n';
-import {
-  useForm,
-  Form,
-  getUseField,
-} from '../../../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib';
-import {
-  getFormRow,
-  Field,
-} from '../../../../../../../../../src/plugins/es_ui_shared/static/forms/components';
+
+import { useForm, Form, getUseField, getFormRow, Field } from '../../../../shared_imports';
 import { documentationService } from '../../../services/documentation';
 import { StepProps } from '../types';
 import { schemas, nameConfig, nameConfigWithoutValidations } from '../template_form_schemas';
@@ -80,11 +73,11 @@ export const StepLogistics: React.FunctionComponent<StepProps> = ({
 
   useEffect(() => {
     onStepValidityChange(form.isValid);
-  }, [form.isValid]);
+  }, [form.isValid, onStepValidityChange]);
 
   useEffect(() => {
     setDataGetter(form.submit);
-  }, [form]);
+  }, [form.submit, setDataGetter]);
 
   const { name, indexPatterns, order, version } = fieldsMeta;
 
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_mappings.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_mappings.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_mappings.tsx
rename to x-pack/plugins/index_management/public/application/components/template_form/steps/step_mappings.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_review.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx
similarity index 98%
rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_review.tsx
rename to x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx
index 09172bf5cd0ca..09da43b83c3c5 100644
--- a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_review.tsx
+++ b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx
@@ -20,7 +20,7 @@ import {
   EuiCodeBlock,
 } from '@elastic/eui';
 import { FormattedMessage } from '@kbn/i18n/react';
-import { serializers } from '../../../../../../../../../src/plugins/es_ui_shared/static/forms/helpers';
+import { serializers } from '../../../../shared_imports';
 
 import { serializeTemplate } from '../../../../../common/lib/template_serialization';
 import { Template } from '../../../../../common/types';
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_settings.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_settings.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_settings.tsx
rename to x-pack/plugins/index_management/public/application/components/template_form/steps/step_settings.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts b/x-pack/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts
similarity index 83%
rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts
rename to x-pack/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts
index ae16a2e9263ff..fbe479ea0cf23 100644
--- a/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts
+++ b/x-pack/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts
@@ -4,10 +4,10 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { useEffect, useState } from 'react';
+import { useEffect, useState, useCallback } from 'react';
 import { i18n } from '@kbn/i18n';
 
-import { isJSON } from '../../../../../../../../../src/plugins/es_ui_shared/static/validators/string';
+import { isJSON } from '../../../../shared_imports';
 import { StepProps } from '../types';
 
 interface Parameters {
@@ -29,7 +29,7 @@ export const useJsonStep = ({
   const [content, setContent] = useState<string>(stringifyJson(defaultValue));
   const [error, setError] = useState<string | null>(null);
 
-  const validateContent = () => {
+  const validateContent = useCallback(() => {
     // We allow empty string as it will be converted to "{}""
     const isValid = content.trim() === '' ? true : isJSON(content);
     if (!isValid) {
@@ -42,20 +42,20 @@ export const useJsonStep = ({
       setError(null);
     }
     return isValid;
-  };
+  }, [content]);
 
-  const dataGetter = () => {
+  const dataGetter = useCallback(() => {
     const isValid = validateContent();
     const value = isValid && content.trim() !== '' ? JSON.parse(content) : {};
     const data = { [prop]: value };
     return Promise.resolve({ isValid, data });
-  };
+  }, [content, validateContent, prop]);
 
   useEffect(() => {
     const isValid = validateContent();
     onStepValidityChange(isValid);
     setDataGetter(dataGetter);
-  }, [content]);
+  }, [content, dataGetter, onStepValidityChange, setDataGetter, validateContent]);
 
   return {
     content,
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/template_form.tsx b/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx
similarity index 92%
rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/template_form.tsx
rename to x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx
index 6a76e1d203b70..58be9b2c63365 100644
--- a/x-pack/legacy/plugins/index_management/public/application/components/template_form/template_form.tsx
+++ b/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx
@@ -3,7 +3,7 @@
  * or more contributor license agreements. Licensed under the Elastic License;
  * you may not use this file except in compliance with the Elastic License.
  */
-import React, { Fragment, useState, useRef } from 'react';
+import React, { Fragment, useState, useRef, useCallback } from 'react';
 import { FormattedMessage } from '@kbn/i18n/react';
 import {
   EuiButton,
@@ -14,7 +14,7 @@ import {
   EuiSpacer,
 } from '@elastic/eui';
 
-import { serializers } from '../../../../../../../../src/plugins/es_ui_shared/static/forms/helpers';
+import { serializers } from '../../../shared_imports';
 import { Template } from '../../../../common/types';
 import { TemplateSteps } from './template_steps';
 import { StepAliases, StepLogistics, StepMappings, StepSettings, StepReview } from './steps';
@@ -70,19 +70,25 @@ export const TemplateForm: React.FunctionComponent<Props> = ({
   const CurrentStepComponent = stepComponentMap[currentStep];
   const isStepValid = validation[currentStep].isValid;
 
-  const setStepDataGetter = (stepDataGetter: DataGetterFunc) => {
-    stepsDataGetters.current[currentStep] = stepDataGetter;
-  };
+  const setStepDataGetter = useCallback(
+    (stepDataGetter: DataGetterFunc) => {
+      stepsDataGetters.current[currentStep] = stepDataGetter;
+    },
+    [currentStep]
+  );
 
-  const onStepValidityChange = (isValid: boolean | undefined) => {
-    setValidation(prev => ({
-      ...prev,
-      [currentStep]: {
-        isValid,
-        errors: {},
-      },
-    }));
-  };
+  const onStepValidityChange = useCallback(
+    (isValid: boolean | undefined) => {
+      setValidation(prev => ({
+        ...prev,
+        [currentStep]: {
+          isValid,
+          errors: {},
+        },
+      }));
+    },
+    [currentStep]
+  );
 
   const validateAndGetDataFromCurrentStep = async () => {
     const validateAndGetData = stepsDataGetters.current[currentStep];
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx b/x-pack/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx
similarity index 96%
rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx
rename to x-pack/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx
index ed2616cc64e38..9ff73b71adf50 100644
--- a/x-pack/legacy/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx
+++ b/x-pack/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx
@@ -13,12 +13,9 @@ import {
   FIELD_TYPES,
   VALIDATION_TYPES,
   FieldConfig,
-} from '../../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib';
-
-import {
   fieldFormatters,
   fieldValidators,
-} from '../../../../../../../../src/plugins/es_ui_shared/static/forms/helpers';
+} from '../../../shared_imports';
 
 import {
   INVALID_INDEX_PATTERN_CHARS,
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/template_steps.tsx b/x-pack/plugins/index_management/public/application/components/template_form/template_steps.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/template_steps.tsx
rename to x-pack/plugins/index_management/public/application/components/template_form/template_steps.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/components/template_form/types.ts b/x-pack/plugins/index_management/public/application/components/template_form/types.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/components/template_form/types.ts
rename to x-pack/plugins/index_management/public/application/components/template_form/types.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/constants/detail_panel_tabs.ts b/x-pack/plugins/index_management/public/application/constants/detail_panel_tabs.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/constants/detail_panel_tabs.ts
rename to x-pack/plugins/index_management/public/application/constants/detail_panel_tabs.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/constants/index.ts b/x-pack/plugins/index_management/public/application/constants/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/constants/index.ts
rename to x-pack/plugins/index_management/public/application/constants/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/constants/refresh_intervals.ts b/x-pack/plugins/index_management/public/application/constants/refresh_intervals.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/constants/refresh_intervals.ts
rename to x-pack/plugins/index_management/public/application/constants/refresh_intervals.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/index.tsx b/x-pack/plugins/index_management/public/application/index.tsx
similarity index 94%
rename from x-pack/legacy/plugins/index_management/public/application/index.tsx
rename to x-pack/plugins/index_management/public/application/index.tsx
index b9859903f1434..5850cb8d42f1a 100644
--- a/x-pack/legacy/plugins/index_management/public/application/index.tsx
+++ b/x-pack/plugins/index_management/public/application/index.tsx
@@ -8,7 +8,7 @@ import React from 'react';
 import { Provider } from 'react-redux';
 import { render, unmountComponentAtNode } from 'react-dom';
 
-import { CoreStart } from '../../../../../../src/core/public';
+import { CoreStart } from '../../../../../src/core/public';
 
 import { AppContextProvider, AppDependencies } from './app_context';
 import { App } from './app';
diff --git a/x-pack/legacy/plugins/index_management/public/application/lib/ace.js b/x-pack/plugins/index_management/public/application/lib/ace.js
similarity index 94%
rename from x-pack/legacy/plugins/index_management/public/application/lib/ace.js
rename to x-pack/plugins/index_management/public/application/lib/ace.js
index b9620dfbdb120..3b37c8fb8870e 100644
--- a/x-pack/legacy/plugins/index_management/public/application/lib/ace.js
+++ b/x-pack/plugins/index_management/public/application/lib/ace.js
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import ace from 'ace';
+import brace from 'brace';
 import 'brace/ext/language_tools';
 
 const splitTokens = line => {
@@ -43,14 +43,14 @@ const wordCompleter = words => {
 };
 
 export const createAceEditor = (div, value, readOnly = true, autocompleteArray) => {
-  const editor = ace.edit(div);
+  const editor = brace.edit(div);
   editor.$blockScrolling = Infinity;
   editor.setValue(value, -1);
   const session = editor.getSession();
   session.setUseWrapMode(true);
   session.setMode('ace/mode/json');
   if (autocompleteArray) {
-    const languageTools = ace.acequire('ace/ext/language_tools');
+    const languageTools = brace.acequire('ace/ext/language_tools');
     const autocompleter = wordCompleter(autocompleteArray);
     languageTools.setCompleters([autocompleter]);
   }
diff --git a/x-pack/legacy/plugins/index_management/public/application/lib/edit_settings.js b/x-pack/plugins/index_management/public/application/lib/edit_settings.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/lib/edit_settings.js
rename to x-pack/plugins/index_management/public/application/lib/edit_settings.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/lib/flatten_object.js b/x-pack/plugins/index_management/public/application/lib/flatten_object.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/lib/flatten_object.js
rename to x-pack/plugins/index_management/public/application/lib/flatten_object.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/lib/flatten_panel_tree.js b/x-pack/plugins/index_management/public/application/lib/flatten_panel_tree.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/lib/flatten_panel_tree.js
rename to x-pack/plugins/index_management/public/application/lib/flatten_panel_tree.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/lib/index_status_labels.js b/x-pack/plugins/index_management/public/application/lib/index_status_labels.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/lib/index_status_labels.js
rename to x-pack/plugins/index_management/public/application/lib/index_status_labels.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/lib/manage_angular_lifecycle.ts b/x-pack/plugins/index_management/public/application/lib/manage_angular_lifecycle.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/lib/manage_angular_lifecycle.ts
rename to x-pack/plugins/index_management/public/application/lib/manage_angular_lifecycle.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/lib/render_badges.js b/x-pack/plugins/index_management/public/application/lib/render_badges.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/lib/render_badges.js
rename to x-pack/plugins/index_management/public/application/lib/render_badges.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/home.tsx b/x-pack/plugins/index_management/public/application/sections/home/home.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/home.tsx
rename to x-pack/plugins/index_management/public/application/sections/home/home.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index.ts b/x-pack/plugins/index_management/public/application/sections/home/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index.ts
rename to x-pack/plugins/index_management/public/application/sections/home/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.container.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.container.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.container.js
rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.container.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.js
rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.container.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.container.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.container.js
rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.container.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.js
rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/index.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/index.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/index.js
rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/index.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/index.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/index.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/index.js
rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/index.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/index.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/index.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/index.js
rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/index.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.container.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.container.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.container.js
rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.container.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.js
rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/index.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/index.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/index.js
rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/index.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.container.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.container.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.container.js
rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.container.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.js
rename to x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index.ts b/x-pack/plugins/index_management/public/application/sections/home/index_list/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index.ts
rename to x-pack/plugins/index_management/public/application/sections/home/index_list/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index.js
rename to x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.container.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.container.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.container.js
rename to x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.container.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js
rename to x-pack/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_list.d.ts b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_list.d.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_list.d.ts
rename to x-pack/plugins/index_management/public/application/sections/home/index_list/index_list.d.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_list.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_list.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_list.js
rename to x-pack/plugins/index_management/public/application/sections/home/index_list/index_list.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_table/index.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_table/index.js
rename to x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.container.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.container.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.container.js
rename to x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.container.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js
rename to x-pack/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/index.ts b/x-pack/plugins/index_management/public/application/sections/home/template_list/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/index.ts
rename to x-pack/plugins/index_management/public/application/sections/home/template_list/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/index.ts b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/index.ts
rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/index.ts b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/index.ts
rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_aliases.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_aliases.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_aliases.tsx
rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_aliases.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_mappings.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_mappings.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_mappings.tsx
rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_mappings.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_settings.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_settings.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_settings.tsx
rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_settings.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx
rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx
similarity index 98%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx
rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx
index ced8bd97e744b..9c31b0d650449 100644
--- a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx
@@ -32,7 +32,7 @@ import {
 } from '../../../../../../common/constants';
 import { Template } from '../../../../../../common/types';
 import { TemplateDeleteModal, SectionLoading, SectionError, Error } from '../../../../components';
-import { loadIndexTemplate } from '../../../../services/api';
+import { useLoadIndexTemplate } from '../../../../services/api';
 import { decodePath } from '../../../../services/routing';
 import { SendRequestResponse } from '../../../../../shared_imports';
 import { useServices } from '../../../../app_context';
@@ -103,7 +103,7 @@ export const TemplateDetails: React.FunctionComponent<Props> = ({
 }) => {
   const { uiMetricService } = useServices();
   const decodedTemplateName = decodePath(templateName);
-  const { error, data: templateDetails, isLoading } = loadIndexTemplate(decodedTemplateName);
+  const { error, data: templateDetails, isLoading } = useLoadIndexTemplate(decodedTemplateName);
   // TS complains if we use destructuring here. Fixed in 3.6.0 (https://github.com/microsoft/TypeScript/pull/31711).
   const isManaged = templateDetails ? templateDetails.isManaged : undefined;
   const [templateToDelete, setTemplateToDelete] = useState<Array<Template['name']>>([]);
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_list.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx
similarity index 97%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_list.tsx
rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx
index 71c32e2e0177f..ffdb224f16271 100644
--- a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_list.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx
@@ -18,7 +18,7 @@ import {
 } from '@elastic/eui';
 import { SectionError, SectionLoading, Error } from '../../../components';
 import { TemplateTable } from './template_table';
-import { loadIndexTemplates } from '../../../services/api';
+import { useLoadIndexTemplates } from '../../../services/api';
 import { Template } from '../../../../../common/types';
 import { useServices } from '../../../app_context';
 import {
@@ -40,7 +40,7 @@ export const TemplateList: React.FunctionComponent<RouteComponentProps<MatchPara
   history,
 }) => {
   const { uiMetricService } = useServices();
-  const { error, isLoading, data: templates, sendRequest: reload } = loadIndexTemplates();
+  const { error, isLoading, data: templates, sendRequest: reload } = useLoadIndexTemplates();
 
   let content;
 
@@ -68,7 +68,7 @@ export const TemplateList: React.FunctionComponent<RouteComponentProps<MatchPara
   // Track component loaded
   useEffect(() => {
     uiMetricService.trackMetric('loaded', UIM_TEMPLATE_LIST_LOAD);
-  }, []);
+  }, [uiMetricService]);
 
   if (isLoading) {
     content = (
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_table/index.ts b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_table/index.ts
rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx
rename to x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/template_clone/index.ts b/x-pack/plugins/index_management/public/application/sections/template_clone/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/template_clone/index.ts
rename to x-pack/plugins/index_management/public/application/sections/template_clone/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/template_clone/template_clone.tsx b/x-pack/plugins/index_management/public/application/sections/template_clone/template_clone.tsx
similarity index 96%
rename from x-pack/legacy/plugins/index_management/public/application/sections/template_clone/template_clone.tsx
rename to x-pack/plugins/index_management/public/application/sections/template_clone/template_clone.tsx
index 6659be5a2cf4e..cf6ca3c065777 100644
--- a/x-pack/legacy/plugins/index_management/public/application/sections/template_clone/template_clone.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/template_clone/template_clone.tsx
@@ -11,7 +11,7 @@ import { TemplateForm, SectionLoading, SectionError, Error } from '../../compone
 import { breadcrumbService } from '../../services/breadcrumbs';
 import { decodePath, getTemplateDetailsLink } from '../../services/routing';
 import { Template } from '../../../../common/types';
-import { saveTemplate, loadIndexTemplate } from '../../services/api';
+import { saveTemplate, useLoadIndexTemplate } from '../../services/api';
 
 interface MatchParams {
   name: string;
@@ -27,7 +27,7 @@ export const TemplateClone: React.FunctionComponent<RouteComponentProps<MatchPar
   const [isSaving, setIsSaving] = useState<boolean>(false);
   const [saveError, setSaveError] = useState<any>(null);
 
-  const { error: templateToCloneError, data: templateToClone, isLoading } = loadIndexTemplate(
+  const { error: templateToCloneError, data: templateToClone, isLoading } = useLoadIndexTemplate(
     decodedTemplateName
   );
 
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/template_create/index.ts b/x-pack/plugins/index_management/public/application/sections/template_create/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/template_create/index.ts
rename to x-pack/plugins/index_management/public/application/sections/template_create/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/template_create/template_create.tsx b/x-pack/plugins/index_management/public/application/sections/template_create/template_create.tsx
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/template_create/template_create.tsx
rename to x-pack/plugins/index_management/public/application/sections/template_create/template_create.tsx
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/template_edit/index.ts b/x-pack/plugins/index_management/public/application/sections/template_edit/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/sections/template_edit/index.ts
rename to x-pack/plugins/index_management/public/application/sections/template_edit/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/template_edit/template_edit.tsx b/x-pack/plugins/index_management/public/application/sections/template_edit/template_edit.tsx
similarity index 96%
rename from x-pack/legacy/plugins/index_management/public/application/sections/template_edit/template_edit.tsx
rename to x-pack/plugins/index_management/public/application/sections/template_edit/template_edit.tsx
index 69e446528a68d..1e9d5f294de34 100644
--- a/x-pack/legacy/plugins/index_management/public/application/sections/template_edit/template_edit.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/template_edit/template_edit.tsx
@@ -8,7 +8,7 @@ import { RouteComponentProps } from 'react-router-dom';
 import { FormattedMessage } from '@kbn/i18n/react';
 import { EuiPageBody, EuiPageContent, EuiTitle, EuiSpacer, EuiCallOut } from '@elastic/eui';
 import { breadcrumbService } from '../../services/breadcrumbs';
-import { loadIndexTemplate, updateTemplate } from '../../services/api';
+import { useLoadIndexTemplate, updateTemplate } from '../../services/api';
 import { decodePath, getTemplateDetailsLink } from '../../services/routing';
 import { SectionLoading, SectionError, TemplateForm, Error } from '../../components';
 import { Template } from '../../../../common/types';
@@ -27,7 +27,7 @@ export const TemplateEdit: React.FunctionComponent<RouteComponentProps<MatchPara
   const [isSaving, setIsSaving] = useState<boolean>(false);
   const [saveError, setSaveError] = useState<any>(null);
 
-  const { error, data: template, isLoading } = loadIndexTemplate(decodedTemplateName);
+  const { error, data: template, isLoading } = useLoadIndexTemplate(decodedTemplateName);
 
   useEffect(() => {
     breadcrumbService.setBreadcrumbs('templateEdit');
diff --git a/x-pack/legacy/plugins/index_management/public/application/services/api.ts b/x-pack/plugins/index_management/public/application/services/api.ts
similarity index 98%
rename from x-pack/legacy/plugins/index_management/public/application/services/api.ts
rename to x-pack/plugins/index_management/public/application/services/api.ts
index 642fd441b353a..88887f40972e4 100644
--- a/x-pack/legacy/plugins/index_management/public/application/services/api.ts
+++ b/x-pack/plugins/index_management/public/application/services/api.ts
@@ -200,7 +200,7 @@ export async function loadIndexData(type: string, indexName: string) {
   }
 }
 
-export function loadIndexTemplates() {
+export function useLoadIndexTemplates() {
   return useRequest({
     path: `${API_BASE_PATH}/templates`,
     method: 'get',
@@ -220,7 +220,7 @@ export async function deleteTemplates(names: Array<Template['name']>) {
   return result;
 }
 
-export function loadIndexTemplate(name: Template['name']) {
+export function useLoadIndexTemplate(name: Template['name']) {
   return useRequest({
     path: `${API_BASE_PATH}/templates/${encodeURIComponent(name)}`,
     method: 'get',
diff --git a/x-pack/legacy/plugins/index_management/public/application/services/breadcrumbs.ts b/x-pack/plugins/index_management/public/application/services/breadcrumbs.ts
similarity index 96%
rename from x-pack/legacy/plugins/index_management/public/application/services/breadcrumbs.ts
rename to x-pack/plugins/index_management/public/application/services/breadcrumbs.ts
index 299491756560e..8128ccd545dce 100644
--- a/x-pack/legacy/plugins/index_management/public/application/services/breadcrumbs.ts
+++ b/x-pack/plugins/index_management/public/application/services/breadcrumbs.ts
@@ -5,7 +5,7 @@
  */
 import { i18n } from '@kbn/i18n';
 import { BASE_PATH } from '../../../common/constants';
-import { ManagementAppMountParams } from '../../../../../../../src/plugins/management/public';
+import { ManagementAppMountParams } from '../../../../../../src/plugins/management/public';
 
 type SetBreadcrumbs = ManagementAppMountParams['setBreadcrumbs'];
 
diff --git a/x-pack/legacy/plugins/index_management/public/application/services/documentation.ts b/x-pack/plugins/index_management/public/application/services/documentation.ts
similarity index 98%
rename from x-pack/legacy/plugins/index_management/public/application/services/documentation.ts
rename to x-pack/plugins/index_management/public/application/services/documentation.ts
index e0f261e586b1e..fdf07c43a0c8b 100644
--- a/x-pack/legacy/plugins/index_management/public/application/services/documentation.ts
+++ b/x-pack/plugins/index_management/public/application/services/documentation.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { DocLinksStart } from '../../../../../../../src/core/public';
+import { DocLinksStart } from '../../../../../../src/core/public';
 import { DataType } from '../components/mappings_editor/types';
 import { TYPE_DEFINITION } from '../components/mappings_editor/constants';
 
diff --git a/x-pack/legacy/plugins/index_management/public/application/services/health_to_color.ts b/x-pack/plugins/index_management/public/application/services/health_to_color.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/services/health_to_color.ts
rename to x-pack/plugins/index_management/public/application/services/health_to_color.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/services/http.ts b/x-pack/plugins/index_management/public/application/services/http.ts
similarity index 88%
rename from x-pack/legacy/plugins/index_management/public/application/services/http.ts
rename to x-pack/plugins/index_management/public/application/services/http.ts
index a6973c263f00f..931e5fcd21898 100644
--- a/x-pack/legacy/plugins/index_management/public/application/services/http.ts
+++ b/x-pack/plugins/index_management/public/application/services/http.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { HttpSetup } from '../../../../../../../src/core/public';
+import { HttpSetup } from '../../../../../../src/core/public';
 
 export class HttpService {
   private client: any;
diff --git a/x-pack/legacy/plugins/index_management/public/application/services/index.ts b/x-pack/plugins/index_management/public/application/services/index.ts
similarity index 96%
rename from x-pack/legacy/plugins/index_management/public/application/services/index.ts
rename to x-pack/plugins/index_management/public/application/services/index.ts
index 78ff8cb5c314a..2334d32adf131 100644
--- a/x-pack/legacy/plugins/index_management/public/application/services/index.ts
+++ b/x-pack/plugins/index_management/public/application/services/index.ts
@@ -21,7 +21,7 @@ export {
   loadIndexStats,
   loadIndexMapping,
   loadIndexData,
-  loadIndexTemplates,
+  useLoadIndexTemplates,
 } from './api';
 export { healthToColor } from './health_to_color';
 export { sortTable } from './sort_table';
diff --git a/x-pack/legacy/plugins/index_management/public/application/services/navigation.ts b/x-pack/plugins/index_management/public/application/services/navigation.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/services/navigation.ts
rename to x-pack/plugins/index_management/public/application/services/navigation.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/services/notification.ts b/x-pack/plugins/index_management/public/application/services/notification.ts
similarity index 93%
rename from x-pack/legacy/plugins/index_management/public/application/services/notification.ts
rename to x-pack/plugins/index_management/public/application/services/notification.ts
index 0971ca77c004b..82b9de2272747 100644
--- a/x-pack/legacy/plugins/index_management/public/application/services/notification.ts
+++ b/x-pack/plugins/index_management/public/application/services/notification.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { NotificationsStart } from '../../../../../../../src/core/public';
+import { NotificationsStart } from '../../../../../../src/core/public';
 
 export class NotificationService {
   private _toasts: any;
diff --git a/x-pack/legacy/plugins/index_management/public/application/services/routing.ts b/x-pack/plugins/index_management/public/application/services/routing.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/services/routing.ts
rename to x-pack/plugins/index_management/public/application/services/routing.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/services/sort_table.ts b/x-pack/plugins/index_management/public/application/services/sort_table.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/services/sort_table.ts
rename to x-pack/plugins/index_management/public/application/services/sort_table.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/services/ui_metric.ts b/x-pack/plugins/index_management/public/application/services/ui_metric.ts
similarity index 90%
rename from x-pack/legacy/plugins/index_management/public/application/services/ui_metric.ts
rename to x-pack/plugins/index_management/public/application/services/ui_metric.ts
index 7c87ec9509085..73d2ef5aced86 100644
--- a/x-pack/legacy/plugins/index_management/public/application/services/ui_metric.ts
+++ b/x-pack/plugins/index_management/public/application/services/ui_metric.ts
@@ -4,7 +4,8 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 import { UiStatsMetricType } from '@kbn/analytics';
-import { UsageCollectionSetup } from '../../../../../../../src/plugins/usage_collection/public';
+
+import { UsageCollectionSetup } from '../../../../../../src/plugins/usage_collection/public';
 import { IndexMgmtMetricsType } from '../../types';
 
 let uiMetricService: UiMetricService<IndexMgmtMetricsType>;
@@ -23,7 +24,8 @@ export class UiMetricService<T extends string> {
 
   private track(type: T, name: string) {
     if (!this.usageCollection) {
-      throw Error('UiMetricService not initialized.');
+      // Usage collection might have been disabled in Kibana config.
+      return;
     }
     this.usageCollection.reportUiStats(this.appName, type as UiStatsMetricType, name);
   }
diff --git a/x-pack/legacy/plugins/index_management/public/application/services/use_request.ts b/x-pack/plugins/index_management/public/application/services/use_request.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/services/use_request.ts
rename to x-pack/plugins/index_management/public/application/services/use_request.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/clear_cache_indices.js b/x-pack/plugins/index_management/public/application/store/actions/clear_cache_indices.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/actions/clear_cache_indices.js
rename to x-pack/plugins/index_management/public/application/store/actions/clear_cache_indices.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/clear_row_status.js b/x-pack/plugins/index_management/public/application/store/actions/clear_row_status.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/actions/clear_row_status.js
rename to x-pack/plugins/index_management/public/application/store/actions/clear_row_status.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/close_indices.js b/x-pack/plugins/index_management/public/application/store/actions/close_indices.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/actions/close_indices.js
rename to x-pack/plugins/index_management/public/application/store/actions/close_indices.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/delete_indices.js b/x-pack/plugins/index_management/public/application/store/actions/delete_indices.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/actions/delete_indices.js
rename to x-pack/plugins/index_management/public/application/store/actions/delete_indices.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/detail_panel.js b/x-pack/plugins/index_management/public/application/store/actions/detail_panel.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/actions/detail_panel.js
rename to x-pack/plugins/index_management/public/application/store/actions/detail_panel.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/edit_index_settings.js b/x-pack/plugins/index_management/public/application/store/actions/edit_index_settings.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/actions/edit_index_settings.js
rename to x-pack/plugins/index_management/public/application/store/actions/edit_index_settings.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/extension_action.js b/x-pack/plugins/index_management/public/application/store/actions/extension_action.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/actions/extension_action.js
rename to x-pack/plugins/index_management/public/application/store/actions/extension_action.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/flush_indices.js b/x-pack/plugins/index_management/public/application/store/actions/flush_indices.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/actions/flush_indices.js
rename to x-pack/plugins/index_management/public/application/store/actions/flush_indices.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/forcemerge_indices.js b/x-pack/plugins/index_management/public/application/store/actions/forcemerge_indices.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/actions/forcemerge_indices.js
rename to x-pack/plugins/index_management/public/application/store/actions/forcemerge_indices.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/freeze_indices.js b/x-pack/plugins/index_management/public/application/store/actions/freeze_indices.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/actions/freeze_indices.js
rename to x-pack/plugins/index_management/public/application/store/actions/freeze_indices.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/index.js b/x-pack/plugins/index_management/public/application/store/actions/index.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/actions/index.js
rename to x-pack/plugins/index_management/public/application/store/actions/index.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/load_index_data.js b/x-pack/plugins/index_management/public/application/store/actions/load_index_data.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/actions/load_index_data.js
rename to x-pack/plugins/index_management/public/application/store/actions/load_index_data.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/load_indices.js b/x-pack/plugins/index_management/public/application/store/actions/load_indices.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/actions/load_indices.js
rename to x-pack/plugins/index_management/public/application/store/actions/load_indices.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/open_indices.js b/x-pack/plugins/index_management/public/application/store/actions/open_indices.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/actions/open_indices.js
rename to x-pack/plugins/index_management/public/application/store/actions/open_indices.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/refresh_indices.js b/x-pack/plugins/index_management/public/application/store/actions/refresh_indices.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/actions/refresh_indices.js
rename to x-pack/plugins/index_management/public/application/store/actions/refresh_indices.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/reload_indices.js b/x-pack/plugins/index_management/public/application/store/actions/reload_indices.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/actions/reload_indices.js
rename to x-pack/plugins/index_management/public/application/store/actions/reload_indices.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/table_state.js b/x-pack/plugins/index_management/public/application/store/actions/table_state.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/actions/table_state.js
rename to x-pack/plugins/index_management/public/application/store/actions/table_state.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/unfreeze_indices.js b/x-pack/plugins/index_management/public/application/store/actions/unfreeze_indices.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/actions/unfreeze_indices.js
rename to x-pack/plugins/index_management/public/application/store/actions/unfreeze_indices.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/actions/update_index_settings.js b/x-pack/plugins/index_management/public/application/store/actions/update_index_settings.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/actions/update_index_settings.js
rename to x-pack/plugins/index_management/public/application/store/actions/update_index_settings.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/index.ts b/x-pack/plugins/index_management/public/application/store/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/index.ts
rename to x-pack/plugins/index_management/public/application/store/index.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/reducers/detail_panel.js b/x-pack/plugins/index_management/public/application/store/reducers/detail_panel.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/reducers/detail_panel.js
rename to x-pack/plugins/index_management/public/application/store/reducers/detail_panel.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/reducers/index.js b/x-pack/plugins/index_management/public/application/store/reducers/index.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/reducers/index.js
rename to x-pack/plugins/index_management/public/application/store/reducers/index.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/reducers/index_management.js b/x-pack/plugins/index_management/public/application/store/reducers/index_management.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/reducers/index_management.js
rename to x-pack/plugins/index_management/public/application/store/reducers/index_management.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/reducers/indices.js b/x-pack/plugins/index_management/public/application/store/reducers/indices.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/reducers/indices.js
rename to x-pack/plugins/index_management/public/application/store/reducers/indices.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/reducers/row_status.js b/x-pack/plugins/index_management/public/application/store/reducers/row_status.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/reducers/row_status.js
rename to x-pack/plugins/index_management/public/application/store/reducers/row_status.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/reducers/table_state.js b/x-pack/plugins/index_management/public/application/store/reducers/table_state.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/reducers/table_state.js
rename to x-pack/plugins/index_management/public/application/store/reducers/table_state.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/selectors/index.d.ts b/x-pack/plugins/index_management/public/application/store/selectors/index.d.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/selectors/index.d.ts
rename to x-pack/plugins/index_management/public/application/store/selectors/index.d.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/selectors/index.js b/x-pack/plugins/index_management/public/application/store/selectors/index.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/selectors/index.js
rename to x-pack/plugins/index_management/public/application/store/selectors/index.js
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/store.d.ts b/x-pack/plugins/index_management/public/application/store/store.d.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/store.d.ts
rename to x-pack/plugins/index_management/public/application/store/store.d.ts
diff --git a/x-pack/legacy/plugins/index_management/public/application/store/store.js b/x-pack/plugins/index_management/public/application/store/store.js
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/application/store/store.js
rename to x-pack/plugins/index_management/public/application/store/store.js
diff --git a/x-pack/legacy/plugins/index_management/public/index.scss b/x-pack/plugins/index_management/public/index.scss
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/index.scss
rename to x-pack/plugins/index_management/public/index.scss
diff --git a/x-pack/legacy/plugins/index_management/public/index.ts b/x-pack/plugins/index_management/public/index.ts
similarity index 66%
rename from x-pack/legacy/plugins/index_management/public/index.ts
rename to x-pack/plugins/index_management/public/index.ts
index 16e7bf21aee98..6bb921ef648f3 100644
--- a/x-pack/legacy/plugins/index_management/public/index.ts
+++ b/x-pack/plugins/index_management/public/index.ts
@@ -3,19 +3,14 @@
  * or more contributor license agreements. Licensed under the Elastic License;
  * you may not use this file except in compliance with the Elastic License.
  */
-import { npSetup } from 'ui/new_platform';
-
+import './index.scss';
 import { IndexMgmtUIPlugin, IndexMgmtSetup } from './plugin';
 
 /** @public */
-export { IndexMgmtSetup };
-
 export const plugin = () => {
   return new IndexMgmtUIPlugin();
 };
 
-// Temp. To be removed after moving to the "plugins" folder
-
-const { extensionsService } = plugin().setup(npSetup.core, npSetup.plugins);
+export { IndexMgmtSetup };
 
-export { extensionsService };
+export { getIndexListUri } from './application/services/navigation';
diff --git a/x-pack/legacy/plugins/index_management/public/mocks.ts b/x-pack/plugins/index_management/public/mocks.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/mocks.ts
rename to x-pack/plugins/index_management/public/mocks.ts
diff --git a/x-pack/legacy/plugins/index_management/public/plugin.ts b/x-pack/plugins/index_management/public/plugin.ts
similarity index 90%
rename from x-pack/legacy/plugins/index_management/public/plugin.ts
rename to x-pack/plugins/index_management/public/plugin.ts
index 539324766cf95..c1b26fe3ca56b 100644
--- a/x-pack/legacy/plugins/index_management/public/plugin.ts
+++ b/x-pack/plugins/index_management/public/plugin.ts
@@ -5,10 +5,10 @@
  */
 import { i18n } from '@kbn/i18n';
 
-import { CoreSetup } from '../../../../../src/core/public';
-import { UsageCollectionSetup } from '../../../../../src/plugins/usage_collection/public';
-import { ManagementSetup } from '../../../../../src/plugins/management/public';
-import { UIM_APP_NAME } from '../common/constants';
+import { CoreSetup } from '../../../../src/core/public';
+import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/public';
+import { ManagementSetup } from '../../../../src/plugins/management/public';
+import { UIM_APP_NAME, PLUGIN } from '../common/constants';
 
 import { AppDependencies } from './application';
 import { httpService } from './application/services/http';
@@ -52,7 +52,7 @@ export class IndexMgmtUIPlugin {
     this.uiMetricService.setup(usageCollection);
 
     management.sections.getSection('elasticsearch')!.registerApp({
-      id: 'index_management',
+      id: PLUGIN.id,
       title: i18n.translate('xpack.idxMgmt.appTitle', { defaultMessage: 'Index Management' }),
       order: 1,
       mount: async ({ element, setBreadcrumbs }) => {
diff --git a/x-pack/legacy/plugins/index_management/public/services/extensions_service.mock.ts b/x-pack/plugins/index_management/public/services/extensions_service.mock.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/services/extensions_service.mock.ts
rename to x-pack/plugins/index_management/public/services/extensions_service.mock.ts
diff --git a/x-pack/legacy/plugins/index_management/public/services/extensions_service.ts b/x-pack/plugins/index_management/public/services/extensions_service.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/services/extensions_service.ts
rename to x-pack/plugins/index_management/public/services/extensions_service.ts
diff --git a/x-pack/legacy/plugins/index_management/public/services/index.ts b/x-pack/plugins/index_management/public/services/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/services/index.ts
rename to x-pack/plugins/index_management/public/services/index.ts
diff --git a/x-pack/plugins/index_management/public/shared_imports.ts b/x-pack/plugins/index_management/public/shared_imports.ts
new file mode 100644
index 0000000000000..cd2964df23d9b
--- /dev/null
+++ b/x-pack/plugins/index_management/public/shared_imports.ts
@@ -0,0 +1,33 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export {
+  SendRequestConfig,
+  SendRequestResponse,
+  UseRequestConfig,
+  sendRequest,
+  useRequest,
+} from '../../../../src/plugins/es_ui_shared/public/request/np_ready_request';
+
+export {
+  FormSchema,
+  FIELD_TYPES,
+  VALIDATION_TYPES,
+  FieldConfig,
+  useForm,
+  Form,
+  getUseField,
+} from '../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib';
+
+export {
+  fieldFormatters,
+  fieldValidators,
+  serializers,
+} from '../../../../src/plugins/es_ui_shared/static/forms/helpers';
+
+export { getFormRow, Field } from '../../../../src/plugins/es_ui_shared/static/forms/components';
+
+export { isJSON } from '../../../../src/plugins/es_ui_shared/static/validators/string';
diff --git a/x-pack/legacy/plugins/index_management/public/types.ts b/x-pack/plugins/index_management/public/types.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/public/types.ts
rename to x-pack/plugins/index_management/public/types.ts
diff --git a/x-pack/legacy/plugins/index_management/public/shared_imports.ts b/x-pack/plugins/index_management/server/config.ts
similarity index 52%
rename from x-pack/legacy/plugins/index_management/public/shared_imports.ts
rename to x-pack/plugins/index_management/server/config.ts
index cbc4dde7448ff..5f03575d3ff43 100644
--- a/x-pack/legacy/plugins/index_management/public/shared_imports.ts
+++ b/x-pack/plugins/index_management/server/config.ts
@@ -4,10 +4,10 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-export {
-  SendRequestConfig,
-  SendRequestResponse,
-  UseRequestConfig,
-  sendRequest,
-  useRequest,
-} from '../../../../../src/plugins/es_ui_shared/public/request/np_ready_request';
+import { schema, TypeOf } from '@kbn/config-schema';
+
+export const configSchema = schema.object({
+  enabled: schema.boolean({ defaultValue: true }),
+});
+
+export type IndexManagementConfig = TypeOf<typeof configSchema>;
diff --git a/x-pack/legacy/plugins/index_management/server/index.ts b/x-pack/plugins/index_management/server/index.ts
similarity index 67%
rename from x-pack/legacy/plugins/index_management/server/index.ts
rename to x-pack/plugins/index_management/server/index.ts
index 866b374740d3b..e4102711708cb 100644
--- a/x-pack/legacy/plugins/index_management/server/index.ts
+++ b/x-pack/plugins/index_management/server/index.ts
@@ -5,8 +5,18 @@
  */
 
 import { PluginInitializerContext } from 'src/core/server';
+
 import { IndexMgmtServerPlugin } from './plugin';
+import { configSchema } from './config';
 
 export const plugin = (ctx: PluginInitializerContext) => new IndexMgmtServerPlugin(ctx);
 
+export const config = {
+  schema: configSchema,
+};
+
+/** @public */
 export { Dependencies } from './types';
+export { IndexMgmtSetup } from './plugin';
+export { Index } from './types';
+export { IndexManagementConfig } from './config';
diff --git a/x-pack/legacy/plugins/index_management/server/lib/fetch_aliases.test.ts b/x-pack/plugins/index_management/server/lib/fetch_aliases.test.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/lib/fetch_aliases.test.ts
rename to x-pack/plugins/index_management/server/lib/fetch_aliases.test.ts
diff --git a/x-pack/legacy/plugins/index_management/server/lib/fetch_aliases.ts b/x-pack/plugins/index_management/server/lib/fetch_aliases.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/lib/fetch_aliases.ts
rename to x-pack/plugins/index_management/server/lib/fetch_aliases.ts
diff --git a/x-pack/legacy/plugins/index_management/server/lib/fetch_indices.ts b/x-pack/plugins/index_management/server/lib/fetch_indices.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/lib/fetch_indices.ts
rename to x-pack/plugins/index_management/server/lib/fetch_indices.ts
diff --git a/x-pack/legacy/plugins/index_management/server/lib/get_managed_templates.ts b/x-pack/plugins/index_management/server/lib/get_managed_templates.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/lib/get_managed_templates.ts
rename to x-pack/plugins/index_management/server/lib/get_managed_templates.ts
diff --git a/x-pack/legacy/plugins/index_management/server/lib/is_es_error.ts b/x-pack/plugins/index_management/server/lib/is_es_error.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/lib/is_es_error.ts
rename to x-pack/plugins/index_management/server/lib/is_es_error.ts
diff --git a/x-pack/legacy/plugins/index_management/server/plugin.ts b/x-pack/plugins/index_management/server/plugin.ts
similarity index 94%
rename from x-pack/legacy/plugins/index_management/server/plugin.ts
rename to x-pack/plugins/index_management/server/plugin.ts
index 95d27e1cf16ba..a0a9151cdb71f 100644
--- a/x-pack/legacy/plugins/index_management/server/plugin.ts
+++ b/x-pack/plugins/index_management/server/plugin.ts
@@ -24,8 +24,8 @@ export class IndexMgmtServerPlugin implements Plugin<IndexMgmtSetup, void, any,
   private readonly logger: Logger;
   private readonly indexDataEnricher: IndexDataEnricher;
 
-  constructor({ logger }: PluginInitializerContext) {
-    this.logger = logger.get();
+  constructor(initContext: PluginInitializerContext) {
+    this.logger = initContext.logger.get();
     this.apiRoutes = new ApiRoutes();
     this.license = new License();
     this.indexDataEnricher = new IndexDataEnricher();
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/index.ts b/x-pack/plugins/index_management/server/routes/api/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/index.ts
rename to x-pack/plugins/index_management/server/routes/api/index.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/indices/index.ts b/x-pack/plugins/index_management/server/routes/api/indices/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/indices/index.ts
rename to x-pack/plugins/index_management/server/routes/api/indices/index.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/indices/register_clear_cache_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_clear_cache_route.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/indices/register_clear_cache_route.ts
rename to x-pack/plugins/index_management/server/routes/api/indices/register_clear_cache_route.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/indices/register_close_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_close_route.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/indices/register_close_route.ts
rename to x-pack/plugins/index_management/server/routes/api/indices/register_close_route.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/indices/register_delete_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_delete_route.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/indices/register_delete_route.ts
rename to x-pack/plugins/index_management/server/routes/api/indices/register_delete_route.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/indices/register_flush_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_flush_route.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/indices/register_flush_route.ts
rename to x-pack/plugins/index_management/server/routes/api/indices/register_flush_route.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/indices/register_forcemerge_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_forcemerge_route.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/indices/register_forcemerge_route.ts
rename to x-pack/plugins/index_management/server/routes/api/indices/register_forcemerge_route.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/indices/register_freeze_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_freeze_route.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/indices/register_freeze_route.ts
rename to x-pack/plugins/index_management/server/routes/api/indices/register_freeze_route.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/indices/register_indices_routes.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_indices_routes.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/indices/register_indices_routes.ts
rename to x-pack/plugins/index_management/server/routes/api/indices/register_indices_routes.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/indices/register_list_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_list_route.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/indices/register_list_route.ts
rename to x-pack/plugins/index_management/server/routes/api/indices/register_list_route.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/indices/register_open_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_open_route.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/indices/register_open_route.ts
rename to x-pack/plugins/index_management/server/routes/api/indices/register_open_route.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/indices/register_refresh_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_refresh_route.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/indices/register_refresh_route.ts
rename to x-pack/plugins/index_management/server/routes/api/indices/register_refresh_route.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/indices/register_reload_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_reload_route.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/indices/register_reload_route.ts
rename to x-pack/plugins/index_management/server/routes/api/indices/register_reload_route.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/indices/register_unfreeze_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_unfreeze_route.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/indices/register_unfreeze_route.ts
rename to x-pack/plugins/index_management/server/routes/api/indices/register_unfreeze_route.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/mapping/index.ts b/x-pack/plugins/index_management/server/routes/api/mapping/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/mapping/index.ts
rename to x-pack/plugins/index_management/server/routes/api/mapping/index.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/mapping/register_mapping_route.ts b/x-pack/plugins/index_management/server/routes/api/mapping/register_mapping_route.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/mapping/register_mapping_route.ts
rename to x-pack/plugins/index_management/server/routes/api/mapping/register_mapping_route.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/settings/index.ts b/x-pack/plugins/index_management/server/routes/api/settings/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/settings/index.ts
rename to x-pack/plugins/index_management/server/routes/api/settings/index.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/settings/register_load_route.ts b/x-pack/plugins/index_management/server/routes/api/settings/register_load_route.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/settings/register_load_route.ts
rename to x-pack/plugins/index_management/server/routes/api/settings/register_load_route.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/settings/register_settings_routes.ts b/x-pack/plugins/index_management/server/routes/api/settings/register_settings_routes.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/settings/register_settings_routes.ts
rename to x-pack/plugins/index_management/server/routes/api/settings/register_settings_routes.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/settings/register_update_route.ts b/x-pack/plugins/index_management/server/routes/api/settings/register_update_route.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/settings/register_update_route.ts
rename to x-pack/plugins/index_management/server/routes/api/settings/register_update_route.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/stats/index.ts b/x-pack/plugins/index_management/server/routes/api/stats/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/stats/index.ts
rename to x-pack/plugins/index_management/server/routes/api/stats/index.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/stats/register_stats_route.ts b/x-pack/plugins/index_management/server/routes/api/stats/register_stats_route.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/stats/register_stats_route.ts
rename to x-pack/plugins/index_management/server/routes/api/stats/register_stats_route.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/templates/index.ts b/x-pack/plugins/index_management/server/routes/api/templates/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/templates/index.ts
rename to x-pack/plugins/index_management/server/routes/api/templates/index.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/templates/register_create_route.ts b/x-pack/plugins/index_management/server/routes/api/templates/register_create_route.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/templates/register_create_route.ts
rename to x-pack/plugins/index_management/server/routes/api/templates/register_create_route.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/templates/register_delete_route.ts b/x-pack/plugins/index_management/server/routes/api/templates/register_delete_route.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/templates/register_delete_route.ts
rename to x-pack/plugins/index_management/server/routes/api/templates/register_delete_route.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/templates/register_get_routes.ts b/x-pack/plugins/index_management/server/routes/api/templates/register_get_routes.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/templates/register_get_routes.ts
rename to x-pack/plugins/index_management/server/routes/api/templates/register_get_routes.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/templates/register_template_routes.ts b/x-pack/plugins/index_management/server/routes/api/templates/register_template_routes.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/templates/register_template_routes.ts
rename to x-pack/plugins/index_management/server/routes/api/templates/register_template_routes.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/templates/register_update_route.ts b/x-pack/plugins/index_management/server/routes/api/templates/register_update_route.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/templates/register_update_route.ts
rename to x-pack/plugins/index_management/server/routes/api/templates/register_update_route.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/api/templates/validate_schemas.ts b/x-pack/plugins/index_management/server/routes/api/templates/validate_schemas.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/api/templates/validate_schemas.ts
rename to x-pack/plugins/index_management/server/routes/api/templates/validate_schemas.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/helpers.ts b/x-pack/plugins/index_management/server/routes/helpers.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/helpers.ts
rename to x-pack/plugins/index_management/server/routes/helpers.ts
diff --git a/x-pack/legacy/plugins/index_management/server/routes/index.ts b/x-pack/plugins/index_management/server/routes/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/routes/index.ts
rename to x-pack/plugins/index_management/server/routes/index.ts
diff --git a/x-pack/legacy/plugins/index_management/server/services/index.ts b/x-pack/plugins/index_management/server/services/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/services/index.ts
rename to x-pack/plugins/index_management/server/services/index.ts
diff --git a/x-pack/legacy/plugins/index_management/server/services/index_data_enricher.ts b/x-pack/plugins/index_management/server/services/index_data_enricher.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/server/services/index_data_enricher.ts
rename to x-pack/plugins/index_management/server/services/index_data_enricher.ts
diff --git a/x-pack/legacy/plugins/index_management/server/services/license.ts b/x-pack/plugins/index_management/server/services/license.ts
similarity index 89%
rename from x-pack/legacy/plugins/index_management/server/services/license.ts
rename to x-pack/plugins/index_management/server/services/license.ts
index fc284a0e3eb65..c490aa7b57ed9 100644
--- a/x-pack/legacy/plugins/index_management/server/services/license.ts
+++ b/x-pack/plugins/index_management/server/services/license.ts
@@ -11,9 +11,8 @@ import {
   RequestHandlerContext,
 } from 'kibana/server';
 
-import { LicensingPluginSetup } from '../../../../../plugins/licensing/server';
-import { LicenseType } from '../../../../../plugins/licensing/common/types';
-import { LICENSE_CHECK_STATE } from '../../../../../plugins/licensing/common/types';
+import { LicensingPluginSetup } from '../../../licensing/server';
+import { LicenseType, LICENSE_CHECK_STATE } from '../../../licensing/common/types';
 
 export interface LicenseStatus {
   isValid: boolean;
diff --git a/x-pack/legacy/plugins/index_management/server/types.ts b/x-pack/plugins/index_management/server/types.ts
similarity index 92%
rename from x-pack/legacy/plugins/index_management/server/types.ts
rename to x-pack/plugins/index_management/server/types.ts
index fbc39b88a462e..b7b1c57b6f04e 100644
--- a/x-pack/legacy/plugins/index_management/server/types.ts
+++ b/x-pack/plugins/index_management/server/types.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 import { ScopedClusterClient, IRouter } from 'src/core/server';
-import { LicensingPluginSetup } from '../../../../plugins/licensing/server';
+import { LicensingPluginSetup } from '../../licensing/server';
 import { License, IndexDataEnricher } from './services';
 import { isEsError } from './lib/is_es_error';
 
diff --git a/x-pack/legacy/plugins/index_management/test/fixtures/index.ts b/x-pack/plugins/index_management/test/fixtures/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/index_management/test/fixtures/index.ts
rename to x-pack/plugins/index_management/test/fixtures/index.ts
diff --git a/x-pack/legacy/plugins/index_management/test/fixtures/template.ts b/x-pack/plugins/index_management/test/fixtures/template.ts
similarity index 88%
rename from x-pack/legacy/plugins/index_management/test/fixtures/template.ts
rename to x-pack/plugins/index_management/test/fixtures/template.ts
index 31bfae2d5821f..19a6e0c257647 100644
--- a/x-pack/legacy/plugins/index_management/test/fixtures/template.ts
+++ b/x-pack/plugins/index_management/test/fixtures/template.ts
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { getRandomString, getRandomNumber } from '../../../../../test_utils';
+import { getRandomString, getRandomNumber } from '../../../../test_utils';
 import { Template } from '../../common/types';
 
 export const getTemplate = ({

From 1a15d3ae83f5f279672596b656b1529fb2184bd1 Mon Sep 17 00:00:00 2001
From: MadameSheema <snootchie.boochies@gmail.com>
Date: Tue, 25 Feb 2020 09:05:22 +0100
Subject: [PATCH 160/174] [SIEM] Upgrades cypress to version 4.0.2 (#58400)

* upgrades cypress to version 4.0.2

* fixes failing tests
---
 .../timeline_search_or_filter.spec.ts         |   5 +-
 .../cypress/integration/url_state.spec.ts     |   5 +-
 .../cypress/tasks/hosts/authentications.ts    |   4 +
 .../cypress/tasks/hosts/uncommon_processes.ts |   4 +
 .../plugins/siem/cypress/tasks/timeline.ts    |   5 +-
 .../components/fields_browser/header.test.tsx |   2 -
 x-pack/package.json                           |   2 +-
 yarn.lock                                     | 301 ++++++++----------
 8 files changed, 159 insertions(+), 169 deletions(-)

diff --git a/x-pack/legacy/plugins/siem/cypress/integration/timeline_search_or_filter.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/timeline_search_or_filter.spec.ts
index c06fd69a558a4..f738ff792049a 100644
--- a/x-pack/legacy/plugins/siem/cypress/integration/timeline_search_or_filter.spec.ts
+++ b/x-pack/legacy/plugins/siem/cypress/integration/timeline_search_or_filter.spec.ts
@@ -24,6 +24,9 @@ describe('timeline search or filter KQL bar', () => {
 
     cy.get(SERVER_SIDE_EVENT_COUNT)
       .invoke('text')
-      .should('be.above', 0);
+      .then(strCount => {
+        const intCount = +strCount;
+        cy.wrap(intCount).should('be.above', 0);
+      });
   });
 });
diff --git a/x-pack/legacy/plugins/siem/cypress/integration/url_state.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/url_state.spec.ts
index cabdb98fa5b67..11c0562eb3638 100644
--- a/x-pack/legacy/plugins/siem/cypress/integration/url_state.spec.ts
+++ b/x-pack/legacy/plugins/siem/cypress/integration/url_state.spec.ts
@@ -236,7 +236,10 @@ describe('url state', () => {
 
     cy.get(SERVER_SIDE_EVENT_COUNT)
       .invoke('text')
-      .should('be.above', 0);
+      .then(strCount => {
+        const intCount = +strCount;
+        cy.wrap(intCount).should('be.above', 0);
+      });
 
     const bestTimelineName = 'The Best Timeline';
     addNameToTimeline(bestTimelineName);
diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/hosts/authentications.ts b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/authentications.ts
index f5f15150e8ac3..ce3767a340376 100644
--- a/x-pack/legacy/plugins/siem/cypress/tasks/hosts/authentications.ts
+++ b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/authentications.ts
@@ -5,7 +5,11 @@
  */
 
 import { AUTHENTICATIONS_TABLE } from '../../screens/hosts/authentications';
+import { REFRESH_BUTTON } from '../../screens/siem_header';
 
 export const waitForAuthenticationsToBeLoaded = () => {
   cy.get(AUTHENTICATIONS_TABLE).should('exist');
+  cy.get(REFRESH_BUTTON)
+    .invoke('text')
+    .should('not.equal', 'Updating');
 };
diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/hosts/uncommon_processes.ts b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/uncommon_processes.ts
index c44249acdd964..a28a7df07c3f8 100644
--- a/x-pack/legacy/plugins/siem/cypress/tasks/hosts/uncommon_processes.ts
+++ b/x-pack/legacy/plugins/siem/cypress/tasks/hosts/uncommon_processes.ts
@@ -5,7 +5,11 @@
  */
 
 import { UNCOMMON_PROCESSES_TABLE } from '../../screens/hosts/uncommon_processes';
+import { REFRESH_BUTTON } from '../../screens/siem_header';
 
 export const waitForUncommonProcessesToBeLoaded = () => {
   cy.get(UNCOMMON_PROCESSES_TABLE).should('exist');
+  cy.get(REFRESH_BUTTON)
+    .invoke('text')
+    .should('not.equal', 'Updating');
 };
diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/timeline.ts b/x-pack/legacy/plugins/siem/cypress/tasks/timeline.ts
index 76acdad990a7e..c218d5153356b 100644
--- a/x-pack/legacy/plugins/siem/cypress/tasks/timeline.ts
+++ b/x-pack/legacy/plugins/siem/cypress/tasks/timeline.ts
@@ -65,7 +65,10 @@ export const populateTimeline = () => {
   executeTimelineKQL(hostExistsQuery);
   cy.get(SERVER_SIDE_EVENT_COUNT)
     .invoke('text')
-    .should('be.above', 0);
+    .then(strCount => {
+      const intCount = +strCount;
+      cy.wrap(intCount).should('be.above', 0);
+    });
 };
 
 export const uncheckTimestampToggleField = () => {
diff --git a/x-pack/legacy/plugins/siem/public/components/fields_browser/header.test.tsx b/x-pack/legacy/plugins/siem/public/components/fields_browser/header.test.tsx
index 42689065354d0..2abc2fd1046e0 100644
--- a/x-pack/legacy/plugins/siem/public/components/fields_browser/header.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/fields_browser/header.test.tsx
@@ -6,11 +6,9 @@
 
 import { mount } from 'enzyme';
 import React from 'react';
-
 import { mockBrowserFields } from '../../containers/source/mock';
 import { TestProviders } from '../../mock';
 import { defaultHeaders } from '../timeline/body/column_headers/default_headers';
-
 import { Header } from './header';
 
 const timelineId = 'test';
diff --git a/x-pack/package.json b/x-pack/package.json
index 551e466893f93..f76b0182ea228 100644
--- a/x-pack/package.json
+++ b/x-pack/package.json
@@ -117,7 +117,7 @@
     "cheerio": "0.22.0",
     "commander": "3.0.2",
     "copy-webpack-plugin": "^5.0.4",
-    "cypress": "^3.6.1",
+    "cypress": "^4.0.2",
     "cypress-multi-reporters": "^1.2.3",
     "enzyme": "^3.11.0",
     "enzyme-adapter-react-16": "^1.15.2",
diff --git a/yarn.lock b/yarn.lock
index 8ea23c17b8b8b..f46e869909e2e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7023,13 +7023,6 @@ async@2.4.0:
   dependencies:
     lodash "^4.14.0"
 
-async@2.6.1, async@^2.6.0, async@^2.6.1:
-  version "2.6.1"
-  resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610"
-  integrity sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==
-  dependencies:
-    lodash "^4.17.10"
-
 async@^2.0.0, async@^2.1.4:
   version "2.6.0"
   resolved "https://registry.yarnpkg.com/async/-/async-2.6.0.tgz#61a29abb6fcc026fea77e56d1c6ec53a795951f4"
@@ -7037,6 +7030,13 @@ async@^2.0.0, async@^2.1.4:
   dependencies:
     lodash "^4.14.0"
 
+async@^2.6.0, async@^2.6.1:
+  version "2.6.1"
+  resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610"
+  integrity sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==
+  dependencies:
+    lodash "^4.17.10"
+
 async@^2.6.3:
   version "2.6.3"
   resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
@@ -7044,6 +7044,11 @@ async@^2.6.3:
   dependencies:
     lodash "^4.17.14"
 
+async@^3.1.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720"
+  integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==
+
 async@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/async/-/async-1.0.0.tgz#f8fc04ca3a13784ade9e1641af98578cfbd647a9"
@@ -7909,6 +7914,11 @@ bluebird@3.5.5, bluebird@^3.5.0, bluebird@^3.5.1, bluebird@^3.5.5:
   resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f"
   integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==
 
+bluebird@3.7.2:
+  version "3.7.2"
+  resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
+  integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
+
 bluebird@^3.3.0, bluebird@^3.3.1:
   version "3.5.1"
   resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9"
@@ -8536,12 +8546,10 @@ cacheable-request@^2.1.1:
     normalize-url "2.0.1"
     responselike "1.0.2"
 
-cachedir@1.3.0:
-  version "1.3.0"
-  resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-1.3.0.tgz#5e01928bf2d95b5edd94b0942188246740e0dbc4"
-  integrity sha512-O1ji32oyON9laVPJL1IZ5bmwd2cB46VfpxkDequezH+15FDzzVddEyrGEeX4WusDSqKxdyFdDQDEG1yo1GoWkg==
-  dependencies:
-    os-homedir "^1.0.1"
+cachedir@2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.3.0.tgz#0c75892a052198f0b21c7c1804d8331edfcae0e8"
+  integrity sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==
 
 caching-transform@^3.0.2:
   version "3.0.2"
@@ -8806,6 +8814,14 @@ chalk@2.4.2, chalk@^2.3.2, chalk@^2.4.2, chalk@~2.4.1:
     escape-string-regexp "^1.0.5"
     supports-color "^5.3.0"
 
+chalk@3.0.0, chalk@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
+  integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
+  dependencies:
+    ansi-styles "^4.1.0"
+    supports-color "^7.1.0"
+
 chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
@@ -8835,14 +8851,6 @@ chalk@^2.3.0:
     escape-string-regexp "^1.0.5"
     supports-color "^5.2.0"
 
-chalk@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
-  integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
-  dependencies:
-    ansi-styles "^4.1.0"
-    supports-color "^7.1.0"
-
 chalk@~0.4.0:
   version "0.4.0"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.4.0.tgz#5199a3ddcd0c1efe23bc08c1b027b06176e0c64f"
@@ -9087,11 +9095,6 @@ ci-info@^1.0.0:
   resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.1.2.tgz#03561259db48d0474c8bdc90f5b47b068b6bbfb4"
   integrity sha512-uTGIPNx/nSpBdsF6xnseRXLLtfr9VLqkz8ZqHXr3Y7b6SftyRxBGjwMtJj1OhNbmlc1wZzLNAlAcvyIiE8a6ZA==
 
-ci-info@^1.5.0:
-  version "1.6.0"
-  resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497"
-  integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==
-
 ci-info@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
@@ -9596,11 +9599,6 @@ commander@2, commander@2.19.0, commander@^2.11.0, commander@^2.12.2:
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
   integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==
 
-commander@2.15.1:
-  version "2.15.1"
-  resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
-  integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==
-
 commander@2.17.x, commander@~2.17.1:
   version "2.17.1"
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
@@ -9611,6 +9609,11 @@ commander@3.0.2:
   resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e"
   integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==
 
+commander@4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.0.tgz#545983a0603fe425bc672d66c9e3c89c42121a83"
+  integrity sha512-NIQrwvv9V39FHgGFm36+U9SMQzbiHvU79k+iADraJTpmrFFfx7Ds0IvDoAdZsDrknlkRk14OYoWXb57uTh7/sw==
+
 commander@^2.13.0, commander@^2.15.1, commander@^2.16.0, commander@^2.19.0:
   version "2.20.0"
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422"
@@ -10646,41 +10649,42 @@ cypress-multi-reporters@^1.2.3:
     debug "^4.1.1"
     lodash "^4.17.11"
 
-cypress@^3.6.1:
-  version "3.6.1"
-  resolved "https://registry.yarnpkg.com/cypress/-/cypress-3.6.1.tgz#4420957923879f60b7a5146ccbf81841a149b653"
-  integrity sha512-6n0oqENdz/oQ7EJ6IgESNb2M7Bo/70qX9jSJsAziJTC3kICfEMmJUlrAnP9bn+ut24MlXQST5nRXhUP5nRIx6A==
+cypress@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/cypress/-/cypress-4.0.2.tgz#ede194d7bc73fb449f8de553c9e1db4ca15309ef"
+  integrity sha512-WRzxOoSd+TxyXKa7Zi9orz3ii5VW7yhhVYstCU+EpOKfPan9x5Ww2Clucmy4H/W0GHUYAo7GYFZRD33ZCSNBQA==
   dependencies:
     "@cypress/listr-verbose-renderer" "0.4.1"
     "@cypress/xvfb" "1.2.4"
     "@types/sizzle" "2.3.2"
     arch "2.1.1"
-    bluebird "3.5.0"
-    cachedir "1.3.0"
-    chalk "2.4.2"
+    bluebird "3.7.2"
+    cachedir "2.3.0"
+    chalk "3.0.0"
     check-more-types "2.24.0"
-    commander "2.15.1"
+    commander "4.1.0"
     common-tags "1.8.0"
-    debug "3.2.6"
-    execa "0.10.0"
+    debug "4.1.1"
+    eventemitter2 "4.1.2"
+    execa "3.3.0"
     executable "4.1.1"
     extract-zip "1.6.7"
-    fs-extra "5.0.0"
-    getos "3.1.1"
-    is-ci "1.2.1"
+    fs-extra "8.1.0"
+    getos "3.1.4"
+    is-ci "2.0.0"
     is-installed-globally "0.1.0"
     lazy-ass "1.6.0"
-    listr "0.12.0"
+    listr "0.14.3"
     lodash "4.17.15"
-    log-symbols "2.2.0"
+    log-symbols "3.0.0"
     minimist "1.2.0"
     moment "2.24.0"
-    ramda "0.24.1"
+    ramda "0.26.1"
     request "2.88.0"
     request-progress "3.0.0"
-    supports-color "5.5.0"
+    supports-color "7.1.0"
     tmp "0.1.0"
-    untildify "3.0.3"
+    untildify "4.0.0"
     url "0.11.0"
     yauzl "2.10.0"
 
@@ -11065,7 +11069,7 @@ debug@4.1.0:
   dependencies:
     ms "^2.1.1"
 
-debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
+debug@4.1.1, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
   integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
@@ -13028,6 +13032,11 @@ event-target-shim@^5.0.0:
   resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
   integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
 
+eventemitter2@4.1.2:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-4.1.2.tgz#0e1a8477af821a6ef3995b311bf74c23a5247f15"
+  integrity sha1-DhqEd6+CGm7zmVsxG/dMI6UkfxU=
+
 eventemitter2@~0.4.13:
   version "0.4.14"
   resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-0.4.14.tgz#8f61b75cde012b2e9eb284d4545583b5643b61ab"
@@ -13078,19 +13087,6 @@ exec-sh@^0.3.2:
   resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.2.tgz#6738de2eb7c8e671d0366aea0b0db8c6f7d7391b"
   integrity sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg==
 
-execa@0.10.0, execa@^0.10.0:
-  version "0.10.0"
-  resolved "https://registry.yarnpkg.com/execa/-/execa-0.10.0.tgz#ff456a8f53f90f8eccc71a96d11bdfc7f082cb50"
-  integrity sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==
-  dependencies:
-    cross-spawn "^6.0.0"
-    get-stream "^3.0.0"
-    is-stream "^1.1.0"
-    npm-run-path "^2.0.0"
-    p-finally "^1.0.0"
-    signal-exit "^3.0.0"
-    strip-eof "^1.0.0"
-
 execa@1.0.0, execa@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
@@ -13104,6 +13100,22 @@ execa@1.0.0, execa@^1.0.0:
     signal-exit "^3.0.0"
     strip-eof "^1.0.0"
 
+execa@3.3.0:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/execa/-/execa-3.3.0.tgz#7e348eef129a1937f21ecbbd53390942653522c1"
+  integrity sha512-j5Vit5WZR/cbHlqU97+qcnw9WHRCIL4V1SVe75VcHcD1JRBdt8fv0zw89b7CQHQdUHTt2VjuhcF5ibAgVOxqpg==
+  dependencies:
+    cross-spawn "^7.0.0"
+    get-stream "^5.0.0"
+    human-signals "^1.1.1"
+    is-stream "^2.0.0"
+    merge-stream "^2.0.0"
+    npm-run-path "^4.0.0"
+    onetime "^5.1.0"
+    p-finally "^2.0.0"
+    signal-exit "^3.0.2"
+    strip-final-newline "^2.0.0"
+
 execa@^0.1.1:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/execa/-/execa-0.1.1.tgz#b09c2a9309bc0ef0501479472db3180f8d4c3edd"
@@ -13113,6 +13125,19 @@ execa@^0.1.1:
     object-assign "^4.0.1"
     strip-eof "^1.0.0"
 
+execa@^0.10.0:
+  version "0.10.0"
+  resolved "https://registry.yarnpkg.com/execa/-/execa-0.10.0.tgz#ff456a8f53f90f8eccc71a96d11bdfc7f082cb50"
+  integrity sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==
+  dependencies:
+    cross-spawn "^6.0.0"
+    get-stream "^3.0.0"
+    is-stream "^1.1.0"
+    npm-run-path "^2.0.0"
+    p-finally "^1.0.0"
+    signal-exit "^3.0.0"
+    strip-eof "^1.0.0"
+
 execa@^0.4.0:
   version "0.4.0"
   resolved "https://registry.yarnpkg.com/execa/-/execa-0.4.0.tgz#4eb6467a36a095fabb2970ff9d5e3fb7bce6ebc3"
@@ -14321,12 +14346,12 @@ fs-exists-sync@^0.1.0:
   resolved "https://registry.yarnpkg.com/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add"
   integrity sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=
 
-fs-extra@5.0.0:
-  version "5.0.0"
-  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd"
-  integrity sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==
+fs-extra@8.1.0, fs-extra@^8.0.1:
+  version "8.1.0"
+  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
+  integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
   dependencies:
-    graceful-fs "^4.1.2"
+    graceful-fs "^4.2.0"
     jsonfile "^4.0.0"
     universalify "^0.1.0"
 
@@ -14368,15 +14393,6 @@ fs-extra@^7.0.0, fs-extra@^7.0.1, fs-extra@~7.0.1:
     jsonfile "^4.0.0"
     universalify "^0.1.0"
 
-fs-extra@^8.0.1:
-  version "8.1.0"
-  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
-  integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
-  dependencies:
-    graceful-fs "^4.2.0"
-    jsonfile "^4.0.0"
-    universalify "^0.1.0"
-
 fs-minipass@^1.2.5:
   version "1.2.5"
   resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d"
@@ -14703,12 +14719,12 @@ getopts@^2.2.5:
   resolved "https://registry.yarnpkg.com/getopts/-/getopts-2.2.5.tgz#67a0fe471cacb9c687d817cab6450b96dde8313b"
   integrity sha512-9jb7AW5p3in+IiJWhQiZmmwkpLaR/ccTWdWQCtZM66HJcHHLegowh4q4tSD7gouUyeNvFWRavfK9GXosQHDpFA==
 
-getos@3.1.1:
-  version "3.1.1"
-  resolved "https://registry.yarnpkg.com/getos/-/getos-3.1.1.tgz#967a813cceafee0156b0483f7cffa5b3eff029c5"
-  integrity sha512-oUP1rnEhAr97rkitiszGP9EgDVYnmchgFzfqRzSkgtfv7ai6tEi7Ko8GgjNXts7VLWEqrTWyhsOKLe5C5b/Zkg==
+getos@3.1.4:
+  version "3.1.4"
+  resolved "https://registry.yarnpkg.com/getos/-/getos-3.1.4.tgz#29cdf240ed10a70c049add7b6f8cb08c81876faf"
+  integrity sha512-UORPzguEB/7UG5hqiZai8f0vQ7hzynMQyJLxStoQ8dPGAcmgsfXOPA4iE/fGtweHYkK+z4zc9V0g+CIFRf5HYw==
   dependencies:
-    async "2.6.1"
+    async "^3.1.0"
 
 getos@^3.1.0:
   version "3.1.0"
@@ -17152,12 +17168,12 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.1.5:
   resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab"
   integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==
 
-is-ci@1.2.1:
-  version "1.2.1"
-  resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.2.1.tgz#e3779c8ee17fccf428488f6e281187f2e632841c"
-  integrity sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==
+is-ci@2.0.0, is-ci@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c"
+  integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==
   dependencies:
-    ci-info "^1.5.0"
+    ci-info "^2.0.0"
 
 is-ci@^1.0.10:
   version "1.1.0"
@@ -17166,13 +17182,6 @@ is-ci@^1.0.10:
   dependencies:
     ci-info "^1.0.0"
 
-is-ci@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c"
-  integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==
-  dependencies:
-    ci-info "^2.0.0"
-
 is-data-descriptor@^0.1.4:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
@@ -19254,20 +19263,6 @@ listr-update-renderer@0.5.0, listr-update-renderer@^0.5.0:
     log-update "^2.3.0"
     strip-ansi "^3.0.1"
 
-listr-update-renderer@^0.2.0:
-  version "0.2.0"
-  resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.2.0.tgz#ca80e1779b4e70266807e8eed1ad6abe398550f9"
-  integrity sha1-yoDhd5tOcCZoB+ju0a1qvjmFUPk=
-  dependencies:
-    chalk "^1.1.3"
-    cli-truncate "^0.2.1"
-    elegant-spinner "^1.0.1"
-    figures "^1.7.0"
-    indent-string "^3.0.0"
-    log-symbols "^1.0.2"
-    log-update "^1.0.2"
-    strip-ansi "^3.0.1"
-
 listr-update-renderer@^0.4.0:
   version "0.4.0"
   resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.4.0.tgz#344d980da2ca2e8b145ba305908f32ae3f4cc8a7"
@@ -19302,28 +19297,6 @@ listr-verbose-renderer@^0.5.0:
     date-fns "^1.27.2"
     figures "^2.0.0"
 
-listr@0.12.0:
-  version "0.12.0"
-  resolved "https://registry.yarnpkg.com/listr/-/listr-0.12.0.tgz#6bce2c0f5603fa49580ea17cd6a00cc0e5fa451a"
-  integrity sha1-a84sD1YD+klYDqF81qAMwOX6RRo=
-  dependencies:
-    chalk "^1.1.3"
-    cli-truncate "^0.2.1"
-    figures "^1.7.0"
-    indent-string "^2.1.0"
-    is-promise "^2.1.0"
-    is-stream "^1.1.0"
-    listr-silent-renderer "^1.1.1"
-    listr-update-renderer "^0.2.0"
-    listr-verbose-renderer "^0.4.0"
-    log-symbols "^1.0.2"
-    log-update "^1.0.2"
-    ora "^0.2.3"
-    p-map "^1.1.1"
-    rxjs "^5.0.0-beta.11"
-    stream-to-observable "^0.1.0"
-    strip-ansi "^3.0.1"
-
 listr@0.14.3:
   version "0.14.3"
   resolved "https://registry.yarnpkg.com/listr/-/listr-0.14.3.tgz#2fea909604e434be464c50bddba0d496928fa586"
@@ -19824,6 +19797,13 @@ log-symbols@2.2.0, log-symbols@^2.0.0, log-symbols@^2.1.0, log-symbols@^2.2.0:
   dependencies:
     chalk "^2.0.1"
 
+log-symbols@3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4"
+  integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==
+  dependencies:
+    chalk "^2.4.2"
+
 log-symbols@^1.0.1, log-symbols@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18"
@@ -23983,21 +23963,16 @@ railroad-diagrams@^1.0.0:
   resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e"
   integrity sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=
 
-ramda@0.24.1:
-  version "0.24.1"
-  resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.24.1.tgz#c3b7755197f35b8dc3502228262c4c91ddb6b857"
-  integrity sha1-w7d1UZfzW43DUCIoJixMkd22uFc=
+ramda@0.26.1, ramda@^0.26, ramda@^0.26.1:
+  version "0.26.1"
+  resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06"
+  integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==
 
 ramda@^0.21.0:
   version "0.21.0"
   resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.21.0.tgz#a001abedb3ff61077d4ff1d577d44de77e8d0a35"
   integrity sha1-oAGr7bP/YQd9T/HVd9RN536NCjU=
 
-ramda@^0.26, ramda@^0.26.1:
-  version "0.26.1"
-  resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06"
-  integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==
-
 randexp@0.4.6:
   version "0.4.6"
   resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3"
@@ -26375,7 +26350,7 @@ rxjs@6.5.2:
   dependencies:
     tslib "^1.9.0"
 
-rxjs@^5.0.0-beta.11, rxjs@^5.5.2:
+rxjs@^5.5.2:
   version "5.5.12"
   resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.12.tgz#6fa61b8a77c3d793dbaf270bee2f43f652d741cc"
   integrity sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==
@@ -27815,11 +27790,6 @@ stream-spigot@~2.1.2:
   dependencies:
     readable-stream "~1.1.0"
 
-stream-to-observable@^0.1.0:
-  version "0.1.0"
-  resolved "https://registry.yarnpkg.com/stream-to-observable/-/stream-to-observable-0.1.0.tgz#45bf1d9f2d7dc09bed81f1c307c430e68b84cffe"
-  integrity sha1-Rb8dny19wJvtgfHDB8Qw5ouEz/4=
-
 streamroller@0.7.0:
   version "0.7.0"
   resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-0.7.0.tgz#a1d1b7cf83d39afb0d63049a5acbf93493bdf64b"
@@ -28291,13 +28261,6 @@ supertest@^3.1.0:
     methods "~1.1.2"
     superagent "3.8.2"
 
-supports-color@5.5.0, supports-color@^5.0.0, supports-color@^5.4.0, supports-color@^5.5.0:
-  version "5.5.0"
-  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
-  integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
-  dependencies:
-    has-flag "^3.0.0"
-
 supports-color@6.0.0:
   version "6.0.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a"
@@ -28312,6 +28275,13 @@ supports-color@6.1.0, supports-color@^6.0.0, supports-color@^6.1.0:
   dependencies:
     has-flag "^3.0.0"
 
+supports-color@7.1.0, supports-color@^7.1.0:
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1"
+  integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==
+  dependencies:
+    has-flag "^4.0.0"
+
 supports-color@^0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-0.2.0.tgz#d92de2694eb3f67323973d7ae3d8b55b4c22190a"
@@ -28329,6 +28299,13 @@ supports-color@^3.1.0, supports-color@^3.1.2, supports-color@^3.2.3:
   dependencies:
     has-flag "^1.0.0"
 
+supports-color@^5.0.0, supports-color@^5.4.0, supports-color@^5.5.0:
+  version "5.5.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
+  integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
+  dependencies:
+    has-flag "^3.0.0"
+
 supports-color@^5.2.0, supports-color@^5.3.0:
   version "5.3.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.3.0.tgz#5b24ac15db80fa927cf5227a4a33fd3c4c7676c0"
@@ -28343,13 +28320,6 @@ supports-color@^7.0.0:
   dependencies:
     has-flag "^4.0.0"
 
-supports-color@^7.1.0:
-  version "7.1.0"
-  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1"
-  integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==
-  dependencies:
-    has-flag "^4.0.0"
-
 supports-hyperlinks@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-1.0.1.tgz#71daedf36cc1060ac5100c351bb3da48c29c0ef7"
@@ -30241,10 +30211,10 @@ unstated@^2.1.1:
   dependencies:
     create-react-context "^0.1.5"
 
-untildify@3.0.3, untildify@^3.0.3:
-  version "3.0.3"
-  resolved "https://registry.yarnpkg.com/untildify/-/untildify-3.0.3.tgz#1e7b42b140bcfd922b22e70ca1265bfe3634c7c9"
-  integrity sha512-iSk/J8efr8uPT/Z4eSUywnqyrQU7DSdMfdqK4iWEaUVVmcP5JcnpRqmVMwcwcnmI1ATFNgC5V90u09tBynNFKA==
+untildify@4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b"
+  integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==
 
 untildify@^2.0.0:
   version "2.1.0"
@@ -30253,6 +30223,11 @@ untildify@^2.0.0:
   dependencies:
     os-homedir "^1.0.0"
 
+untildify@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/untildify/-/untildify-3.0.3.tgz#1e7b42b140bcfd922b22e70ca1265bfe3634c7c9"
+  integrity sha512-iSk/J8efr8uPT/Z4eSUywnqyrQU7DSdMfdqK4iWEaUVVmcP5JcnpRqmVMwcwcnmI1ATFNgC5V90u09tBynNFKA==
+
 unzip-response@^1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-1.0.2.tgz#b984f0877fc0a89c2c773cc1ef7b5b232b5b06fe"

From a60b25f9ad45cbc8efb5f362d30996c0d49baa2b Mon Sep 17 00:00:00 2001
From: Joe Reuter <johannes.reuter@elastic.co>
Date: Tue, 25 Feb 2020 09:05:53 +0100
Subject: [PATCH 161/174] fix short url in spaces (#58313)

---
 src/plugins/share/server/routes/goto.ts | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/src/plugins/share/server/routes/goto.ts b/src/plugins/share/server/routes/goto.ts
index 5c3a4e441099f..0c5b74915e58a 100644
--- a/src/plugins/share/server/routes/goto.ts
+++ b/src/plugins/share/server/routes/goto.ts
@@ -23,6 +23,7 @@ import { schema } from '@kbn/config-schema';
 import { shortUrlAssertValid } from './lib/short_url_assert_valid';
 import { ShortUrlLookupService } from './lib/short_url_lookup';
 import { getGotoPath } from '../../common/short_url_routes';
+import { modifyUrl } from '../../../../core/utils';
 
 export const createGotoRoute = ({
   router,
@@ -49,9 +50,16 @@ export const createGotoRoute = ({
       const uiSettings = context.core.uiSettings.client;
       const stateStoreInSessionStorage = await uiSettings.get('state:storeInSessionStorage');
       if (!stateStoreInSessionStorage) {
+        const basePath = http.basePath.get(request);
+
+        const prependedUrl = modifyUrl(url, parts => {
+          if (!parts.hostname && parts.pathname && parts.pathname.startsWith('/')) {
+            parts.pathname = `${basePath}${parts.pathname}`;
+          }
+        });
         return response.redirected({
           headers: {
-            location: http.basePath.prepend(url),
+            location: prependedUrl,
           },
         });
       }

From 1a96ff3c302c08ea049245a05c716ecd43ca0485 Mon Sep 17 00:00:00 2001
From: Robert Oskamp <robert.oskamp@elastic.co>
Date: Tue, 25 Feb 2020 11:00:10 +0100
Subject: [PATCH 162/174] [ML] Functional tests - stabilize typing during df
 analytics creation (#58227)

This PR makes the typing in data frame analytics tests more robust.
---
 .../data_frame_analytics_creation.ts          | 24 ++++++++++++-------
 x-pack/test/functional/services/ml.ts         |  5 +++-
 2 files changed, 19 insertions(+), 10 deletions(-)

diff --git a/x-pack/test/functional/services/machine_learning/data_frame_analytics_creation.ts b/x-pack/test/functional/services/machine_learning/data_frame_analytics_creation.ts
index b4e455ebaa63f..96dc8993c3d35 100644
--- a/x-pack/test/functional/services/machine_learning/data_frame_analytics_creation.ts
+++ b/x-pack/test/functional/services/machine_learning/data_frame_analytics_creation.ts
@@ -6,10 +6,12 @@
 import expect from '@kbn/expect';
 
 import { FtrProviderContext } from '../../ftr_provider_context';
+import { MlCommon } from './common';
 
-export function MachineLearningDataFrameAnalyticsCreationProvider({
-  getService,
-}: FtrProviderContext) {
+export function MachineLearningDataFrameAnalyticsCreationProvider(
+  { getService }: FtrProviderContext,
+  mlCommon: MlCommon
+) {
   const testSubjects = getService('testSubjects');
   const comboBox = getService('comboBox');
   const retry = getService('retry');
@@ -85,14 +87,14 @@ export function MachineLearningDataFrameAnalyticsCreationProvider({
     },
 
     async setJobId(jobId: string) {
-      await testSubjects.setValue('mlAnalyticsCreateJobFlyoutJobIdInput', jobId, {
+      await mlCommon.setValueWithChecks('mlAnalyticsCreateJobFlyoutJobIdInput', jobId, {
         clearWithKeyboard: true,
       });
       await this.assertJobIdValue(jobId);
     },
 
     async setJobDescription(jobDescription: string) {
-      await testSubjects.setValue('mlDFAnalyticsJobCreationJobDescription', jobDescription, {
+      await mlCommon.setValueWithChecks('mlDFAnalyticsJobCreationJobDescription', jobDescription, {
         clearWithKeyboard: true,
       });
       await this.assertJobDescriptionValue(jobDescription);
@@ -136,9 +138,13 @@ export function MachineLearningDataFrameAnalyticsCreationProvider({
     },
 
     async setDestIndex(destIndex: string) {
-      await testSubjects.setValue('mlAnalyticsCreateJobFlyoutDestinationIndexInput', destIndex, {
-        clearWithKeyboard: true,
-      });
+      await mlCommon.setValueWithChecks(
+        'mlAnalyticsCreateJobFlyoutDestinationIndexInput',
+        destIndex,
+        {
+          clearWithKeyboard: true,
+        }
+      );
       await this.assertDestIndexValue(destIndex);
     },
 
@@ -248,7 +254,7 @@ export function MachineLearningDataFrameAnalyticsCreationProvider({
     },
 
     async setModelMemory(modelMemory: string) {
-      await testSubjects.setValue('mlAnalyticsCreateJobFlyoutModelMemoryInput', modelMemory, {
+      await mlCommon.setValueWithChecks('mlAnalyticsCreateJobFlyoutModelMemoryInput', modelMemory, {
         clearWithKeyboard: true,
       });
       await this.assertModelMemoryValue(modelMemory);
diff --git a/x-pack/test/functional/services/ml.ts b/x-pack/test/functional/services/ml.ts
index 2660a90662dec..354e0907375ca 100644
--- a/x-pack/test/functional/services/ml.ts
+++ b/x-pack/test/functional/services/ml.ts
@@ -42,7 +42,10 @@ export function MachineLearningProvider(context: FtrProviderContext) {
   const api = MachineLearningAPIProvider(context);
   const customUrls = MachineLearningCustomUrlsProvider(context);
   const dataFrameAnalytics = MachineLearningDataFrameAnalyticsProvider(context, api);
-  const dataFrameAnalyticsCreation = MachineLearningDataFrameAnalyticsCreationProvider(context);
+  const dataFrameAnalyticsCreation = MachineLearningDataFrameAnalyticsCreationProvider(
+    context,
+    common
+  );
   const dataFrameAnalyticsTable = MachineLearningDataFrameAnalyticsTableProvider(context);
   const dataVisualizer = MachineLearningDataVisualizerProvider(context);
   const dataVisualizerIndexBased = MachineLearningDataVisualizerIndexBasedProvider(context);

From 737205fb9b0c5b27ff06331598802a2e57365ffb Mon Sep 17 00:00:00 2001
From: Maryia Lapata <mary.lopato@gmail.com>
Date: Tue, 25 Feb 2020 13:19:02 +0300
Subject: [PATCH 163/174] Move src/legacy/ui/public/notify/app_redirect to
 kibana_legacy (#58127)

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../core_plugins/kibana/public/kibana.js      |  6 ++--
 src/legacy/ui/public/notify/index.js          |  1 -
 .../notify/app_redirect/app_redirect.test.ts} | 32 +++++++++++--------
 .../notify/app_redirect/app_redirect.ts}      |  9 +++---
 .../public/notify/app_redirect/index.ts}      |  0
 .../kibana_legacy/public/notify/index.ts      |  1 +
 .../dashboard_mode/public/dashboard_viewer.js |  6 ++--
 .../plugins/graph/public/application.ts       |  2 +-
 .../plugins/graph/public/legacy_imports.ts    |  2 --
 9 files changed, 33 insertions(+), 26 deletions(-)
 rename src/{legacy/ui/public/notify/app_redirect/app_redirect.test.js => plugins/kibana_legacy/public/notify/app_redirect/app_redirect.test.ts} (75%)
 rename src/{legacy/ui/public/notify/app_redirect/app_redirect.js => plugins/kibana_legacy/public/notify/app_redirect/app_redirect.ts} (82%)
 rename src/{legacy/ui/public/notify/app_redirect/index.js => plugins/kibana_legacy/public/notify/app_redirect/index.ts} (100%)

diff --git a/src/legacy/core_plugins/kibana/public/kibana.js b/src/legacy/core_plugins/kibana/public/kibana.js
index d77fc780f4cc2..384c6bd80ec33 100644
--- a/src/legacy/core_plugins/kibana/public/kibana.js
+++ b/src/legacy/core_plugins/kibana/public/kibana.js
@@ -53,7 +53,7 @@ import './management';
 import './dev_tools';
 import 'ui/agg_response';
 import 'ui/agg_types';
-import { showAppRedirectNotification } from 'ui/notify';
+import { showAppRedirectNotification } from '../../../../plugins/kibana_legacy/public';
 import 'leaflet';
 import { localApplicationService } from './local_application_service';
 
@@ -68,4 +68,6 @@ routes.otherwise({
   redirectTo: `/${config.defaultAppId || 'discover'}`,
 });
 
-uiModules.get('kibana').run(showAppRedirectNotification);
+uiModules
+  .get('kibana')
+  .run($location => showAppRedirectNotification($location, npSetup.core.notifications.toasts));
diff --git a/src/legacy/ui/public/notify/index.js b/src/legacy/ui/public/notify/index.js
index f7526f3b8f8fd..7ec6a394d7e88 100644
--- a/src/legacy/ui/public/notify/index.js
+++ b/src/legacy/ui/public/notify/index.js
@@ -19,5 +19,4 @@
 
 export { fatalError, addFatalErrorCallback } from './fatal_error';
 export { toastNotifications } from './toasts';
-export { addAppRedirectMessageToUrl, showAppRedirectNotification } from './app_redirect';
 export { banners } from './banners';
diff --git a/src/legacy/ui/public/notify/app_redirect/app_redirect.test.js b/src/plugins/kibana_legacy/public/notify/app_redirect/app_redirect.test.ts
similarity index 75%
rename from src/legacy/ui/public/notify/app_redirect/app_redirect.test.js
rename to src/plugins/kibana_legacy/public/notify/app_redirect/app_redirect.test.ts
index a23aabe6ad88e..efb1393ff0b16 100644
--- a/src/legacy/ui/public/notify/app_redirect/app_redirect.test.js
+++ b/src/plugins/kibana_legacy/public/notify/app_redirect/app_redirect.test.ts
@@ -17,17 +17,12 @@
  * under the License.
  */
 
+import { ILocationService } from 'angular';
+import { ToastsStart } from '../../../../../core/public';
 import { addAppRedirectMessageToUrl, showAppRedirectNotification } from './app_redirect';
 
 let isToastAdded = false;
-
-jest.mock('../toasts', () => ({
-  toastNotifications: {
-    addDanger: () => {
-      isToastAdded = true;
-    },
-  },
-}));
+const toasts: ToastsStart = {} as ToastsStart;
 
 describe('addAppRedirectMessageToUrl', () => {
   test('adds a message to the URL', () => {
@@ -39,20 +34,29 @@ describe('addAppRedirectMessageToUrl', () => {
 describe('showAppRedirectNotification', () => {
   beforeEach(() => {
     isToastAdded = false;
+    toasts.addDanger = (): any => {
+      isToastAdded = true;
+    };
   });
 
   test(`adds a toast when there's a message in the URL`, () => {
-    showAppRedirectNotification({
-      search: () => ({ app_redirect_message: 'redirect message' }),
-    });
+    showAppRedirectNotification(
+      {
+        search: () => ({ app_redirect_message: 'redirect message' }),
+      } as ILocationService,
+      toasts
+    );
 
     expect(isToastAdded).toBe(true);
   });
 
   test(`doesn't add a toast when there's no message in the URL`, () => {
-    showAppRedirectNotification({
-      search: () => ({ app_redirect_message: '' }),
-    });
+    showAppRedirectNotification(
+      {
+        search: () => ({ app_redirect_message: '' }),
+      } as ILocationService,
+      toasts
+    );
 
     expect(isToastAdded).toBe(false);
   });
diff --git a/src/legacy/ui/public/notify/app_redirect/app_redirect.js b/src/plugins/kibana_legacy/public/notify/app_redirect/app_redirect.ts
similarity index 82%
rename from src/legacy/ui/public/notify/app_redirect/app_redirect.js
rename to src/plugins/kibana_legacy/public/notify/app_redirect/app_redirect.ts
index a92e5401e5e75..e79ab4b2fbc6d 100644
--- a/src/legacy/ui/public/notify/app_redirect/app_redirect.js
+++ b/src/plugins/kibana_legacy/public/notify/app_redirect/app_redirect.ts
@@ -17,12 +17,13 @@
  * under the License.
  */
 
+import { ILocationService } from 'angular';
 import { modifyUrl } from '../../../../../core/utils';
-import { toastNotifications } from '../toasts';
+import { ToastsStart } from '../../../../../core/public';
 
 const APP_REDIRECT_MESSAGE_PARAM = 'app_redirect_message';
 
-export function addAppRedirectMessageToUrl(url, message) {
+export function addAppRedirectMessageToUrl(url: string, message: string) {
   return modifyUrl(url, urlParts => {
     urlParts.hash = modifyUrl(urlParts.hash || '', hashParts => {
       hashParts.query[APP_REDIRECT_MESSAGE_PARAM] = message;
@@ -32,7 +33,7 @@ export function addAppRedirectMessageToUrl(url, message) {
 
 // If an app needs to redirect, e.g. due to an expired license, it can surface a message via
 // the URL query params.
-export function showAppRedirectNotification($location) {
+export function showAppRedirectNotification($location: ILocationService, toasts: ToastsStart) {
   const queryString = $location.search();
 
   if (!queryString[APP_REDIRECT_MESSAGE_PARAM]) {
@@ -42,5 +43,5 @@ export function showAppRedirectNotification($location) {
   const message = queryString[APP_REDIRECT_MESSAGE_PARAM];
   $location.search(APP_REDIRECT_MESSAGE_PARAM, null);
 
-  toastNotifications.addDanger(message);
+  toasts.addDanger(message);
 }
diff --git a/src/legacy/ui/public/notify/app_redirect/index.js b/src/plugins/kibana_legacy/public/notify/app_redirect/index.ts
similarity index 100%
rename from src/legacy/ui/public/notify/app_redirect/index.js
rename to src/plugins/kibana_legacy/public/notify/app_redirect/index.ts
diff --git a/src/plugins/kibana_legacy/public/notify/index.ts b/src/plugins/kibana_legacy/public/notify/index.ts
index 6aa4e36ab7227..b6f29876c2737 100644
--- a/src/plugins/kibana_legacy/public/notify/index.ts
+++ b/src/plugins/kibana_legacy/public/notify/index.ts
@@ -18,3 +18,4 @@
  */
 export * from './toasts';
 export * from './lib';
+export { addAppRedirectMessageToUrl, showAppRedirectNotification } from './app_redirect';
diff --git a/x-pack/legacy/plugins/dashboard_mode/public/dashboard_viewer.js b/x-pack/legacy/plugins/dashboard_mode/public/dashboard_viewer.js
index 8ca023aa90cf1..e0e49fe59daf4 100644
--- a/x-pack/legacy/plugins/dashboard_mode/public/dashboard_viewer.js
+++ b/x-pack/legacy/plugins/dashboard_mode/public/dashboard_viewer.js
@@ -37,7 +37,7 @@ import 'plugins/kibana/dashboard/legacy';
 import { npStart } from 'ui/new_platform';
 import { localApplicationService } from 'plugins/kibana/local_application_service';
 
-import { showAppRedirectNotification } from 'ui/notify';
+import { showAppRedirectNotification } from '../../../../../src/plugins/kibana_legacy/public';
 import { DashboardConstants, createDashboardEditUrl } from 'plugins/kibana/dashboard';
 
 npStart.plugins.kibanaLegacy.dashboardConfig.turnHideWriteControlsOn();
@@ -51,7 +51,9 @@ chrome.setRootController('kibana', function() {
   npStart.core.chrome.navLinks.showOnly('kibana:dashboard');
 });
 
-uiModules.get('kibana').run(showAppRedirectNotification);
+uiModules
+  .get('kibana')
+  .run($location => showAppRedirectNotification($location, npStart.core.notifications.toasts));
 
 /**
  * If there is a configured `kibana.defaultAppId`, and it is a dashboard ID, we'll
diff --git a/x-pack/legacy/plugins/graph/public/application.ts b/x-pack/legacy/plugins/graph/public/application.ts
index 80a797b7f0724..7bd18f841b478 100644
--- a/x-pack/legacy/plugins/graph/public/application.ts
+++ b/x-pack/legacy/plugins/graph/public/application.ts
@@ -24,7 +24,6 @@ import {
   configureAppAngularModule,
   createTopNavDirective,
   createTopNavHelper,
-  addAppRedirectMessageToUrl,
 } from './legacy_imports';
 // @ts-ignore
 import { initGraphApp } from './app';
@@ -37,6 +36,7 @@ import { checkLicense } from '../../../../plugins/graph/common/check_license';
 import { NavigationPublicPluginStart as NavigationStart } from '../../../../../src/plugins/navigation/public';
 import { createSavedWorkspacesLoader } from './services/persistence/saved_workspace_loader';
 import { Storage } from '../../../../../src/plugins/kibana_utils/public';
+import { addAppRedirectMessageToUrl } from '../../../../../src/plugins/kibana_legacy/public';
 
 /**
  * These are dependencies of the Graph app besides the base dependencies
diff --git a/x-pack/legacy/plugins/graph/public/legacy_imports.ts b/x-pack/legacy/plugins/graph/public/legacy_imports.ts
index ac518d34551db..84fafdb580abe 100644
--- a/x-pack/legacy/plugins/graph/public/legacy_imports.ts
+++ b/x-pack/legacy/plugins/graph/public/legacy_imports.ts
@@ -8,6 +8,4 @@ import 'ace';
 
 // @ts-ignore
 export { createTopNavDirective, createTopNavHelper } from 'ui/kbn_top_nav/kbn_top_nav';
-// @ts-ignore
-export { addAppRedirectMessageToUrl } from 'ui/notify';
 export { configureAppAngularModule } from '../../../../../src/plugins/kibana_legacy/public';

From 5910e83722ae3c211731f9671e6ceee438d75aca Mon Sep 17 00:00:00 2001
From: Joe Reuter <johannes.reuter@elastic.co>
Date: Tue, 25 Feb 2020 11:27:25 +0100
Subject: [PATCH 164/174] hide welcome screen for cloud (#58371)

---
 x-pack/test/functional/page_objects/security_page.js | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/x-pack/test/functional/page_objects/security_page.js b/x-pack/test/functional/page_objects/security_page.js
index 3bcba7cbd1696..5889a374e443e 100644
--- a/x-pack/test/functional/page_objects/security_page.js
+++ b/x-pack/test/functional/page_objects/security_page.js
@@ -30,6 +30,11 @@ export function SecurityPageProvider({ getService, getPageObjects }) {
       const rawDataTabLocator = 'a[id=rawdata-tab]';
 
       await PageObjects.common.navigateToApp('login');
+
+      // ensure welcome screen won't be shown. This is relevant for environments which don't allow
+      // to use the yml setting, e.g. cloud
+      await browser.setLocalStorageItem('home:welcome:show', 'false');
+
       await testSubjects.setValue('loginUsername', username);
       await testSubjects.setValue('loginPassword', password);
       await testSubjects.click('loginSubmit');

From 506268c926d18f27133d93f419c0b594eca609aa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?=
 <alejandro.haro@elastic.co>
Date: Tue, 25 Feb 2020 10:31:35 +0000
Subject: [PATCH 165/174] [Telemetry] Separate the license retrieval from the
 stats in the usage collectors (#57332)

* [Telemetry] Merge OSS and XPack usage collectors

* Create X-Pack collector again

* Separate the license retrieval from the stats

* Fix telemetry tests with new fields

* Use Promise.all to retrieve license and stats at the same time

* Fix moment mock

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../telemetry/server/collection_manager.ts    |   56 +-
 ...et_cluster_info.js => get_cluster_info.ts} |   24 +-
 .../telemetry_collection/get_local_license.ts |   90 ++
 .../telemetry_collection/get_local_stats.ts   |   26 +-
 .../server/telemetry_collection/index.ts      |    1 -
 .../register_collection.ts                    |    2 +
 .../public/services/telemetry_service.test.ts |   10 +
 .../public/services/telemetry_service.ts      |    5 +-
 .../get_all_stats.test.ts                     |   12 +-
 .../telemetry_collection/get_all_stats.ts     |   41 +-
 .../telemetry_collection/get_es_stats.ts      |   11 +-
 .../telemetry_collection/get_licenses.test.ts |   84 ++
 .../telemetry_collection/get_licenses.ts      |   84 ++
 .../register_monitoring_collection.ts         |    2 +
 .../get_stats_with_xpack.test.ts.snap         |  118 ++
 .../__tests__/get_xpack.js                    |  105 +-
 .../server/telemetry_collection/constants.ts  |    1 +
 .../get_stats_with_xpack.test.ts              |  113 ++
 .../get_stats_with_xpack.ts                   |   28 +-
 .../server/telemetry_collection/get_xpack.js  |   85 --
 .../server/telemetry_collection/get_xpack.ts  |   25 +
 .../register_xpack_collection.ts              |    2 +
 .../apis/telemetry/fixtures/basiccluster.json |    7 +
 .../apis/telemetry/fixtures/multicluster.json | 1002 ++++++++++++++++-
 24 files changed, 1671 insertions(+), 263 deletions(-)
 rename src/legacy/core_plugins/telemetry/server/telemetry_collection/{get_cluster_info.js => get_cluster_info.ts} (61%)
 create mode 100644 src/legacy/core_plugins/telemetry/server/telemetry_collection/get_local_license.ts
 create mode 100644 x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_licenses.test.ts
 create mode 100644 x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_licenses.ts
 create mode 100644 x-pack/legacy/plugins/xpack_main/server/telemetry_collection/__snapshots__/get_stats_with_xpack.test.ts.snap
 create mode 100644 x-pack/legacy/plugins/xpack_main/server/telemetry_collection/get_stats_with_xpack.test.ts
 delete mode 100644 x-pack/legacy/plugins/xpack_main/server/telemetry_collection/get_xpack.js
 create mode 100644 x-pack/legacy/plugins/xpack_main/server/telemetry_collection/get_xpack.ts

diff --git a/src/legacy/core_plugins/telemetry/server/collection_manager.ts b/src/legacy/core_plugins/telemetry/server/collection_manager.ts
index 0394dea343adf..715ca56e290a2 100644
--- a/src/legacy/core_plugins/telemetry/server/collection_manager.ts
+++ b/src/legacy/core_plugins/telemetry/server/collection_manager.ts
@@ -20,6 +20,7 @@
 import { encryptTelemetry } from './collectors';
 import { CallCluster } from '../../elasticsearch';
 import { UsageCollectionSetup } from '../../../../plugins/usage_collection/server';
+import { ESLicense } from './telemetry_collection/get_local_license';
 
 export type EncryptedStatsGetterConfig = { unencrypted: false } & {
   server: any;
@@ -45,22 +46,38 @@ export interface StatsCollectionConfig {
   end: string | number;
 }
 
+export interface BasicStatsPayload {
+  timestamp: string;
+  cluster_uuid: string;
+  cluster_name: string;
+  version: string;
+  cluster_stats: object;
+  collection?: string;
+  stack_stats: object;
+}
+
 export type StatsGetterConfig = UnencryptedStatsGetterConfig | EncryptedStatsGetterConfig;
 export type ClusterDetailsGetter = (config: StatsCollectionConfig) => Promise<ClusterDetails[]>;
-export type StatsGetter = (
+export type StatsGetter<T extends BasicStatsPayload = BasicStatsPayload> = (
+  clustersDetails: ClusterDetails[],
+  config: StatsCollectionConfig
+) => Promise<T[]>;
+export type LicenseGetter = (
   clustersDetails: ClusterDetails[],
   config: StatsCollectionConfig
-) => Promise<any[]>;
+) => Promise<{ [clusterUuid: string]: ESLicense | undefined }>;
 
-interface CollectionConfig {
+interface CollectionConfig<T extends BasicStatsPayload> {
   title: string;
   priority: number;
   esCluster: string;
-  statsGetter: StatsGetter;
+  statsGetter: StatsGetter<T>;
   clusterDetailsGetter: ClusterDetailsGetter;
+  licenseGetter: LicenseGetter;
 }
 interface Collection {
   statsGetter: StatsGetter;
+  licenseGetter: LicenseGetter;
   clusterDetailsGetter: ClusterDetailsGetter;
   esCluster: string;
   title: string;
@@ -70,8 +87,15 @@ export class TelemetryCollectionManager {
   private usageGetterMethodPriority = -1;
   private collections: Collection[] = [];
 
-  public setCollection = (collectionConfig: CollectionConfig) => {
-    const { title, priority, esCluster, statsGetter, clusterDetailsGetter } = collectionConfig;
+  public setCollection = <T extends BasicStatsPayload>(collectionConfig: CollectionConfig<T>) => {
+    const {
+      title,
+      priority,
+      esCluster,
+      statsGetter,
+      clusterDetailsGetter,
+      licenseGetter,
+    } = collectionConfig;
 
     if (typeof priority !== 'number') {
       throw new Error('priority must be set.');
@@ -88,10 +112,14 @@ export class TelemetryCollectionManager {
         throw Error('esCluster name must be set for the getCluster method.');
       }
       if (!clusterDetailsGetter) {
-        throw Error('Cluser UUIds method is not set.');
+        throw Error('Cluster UUIds method is not set.');
+      }
+      if (!licenseGetter) {
+        throw Error('License getter method not set.');
       }
 
       this.collections.unshift({
+        licenseGetter,
         statsGetter,
         clusterDetailsGetter,
         esCluster,
@@ -141,7 +169,19 @@ export class TelemetryCollectionManager {
       return;
     }
 
-    return await collection.statsGetter(clustersDetails, statsCollectionConfig);
+    const [stats, licenses] = await Promise.all([
+      collection.statsGetter(clustersDetails, statsCollectionConfig),
+      collection.licenseGetter(clustersDetails, statsCollectionConfig),
+    ]);
+
+    return stats.map(stat => {
+      const license = licenses[stat.cluster_uuid];
+      return {
+        ...(license ? { license } : {}),
+        ...stat,
+        collectionSource: collection.title,
+      };
+    });
   };
 
   public getOptInStats = async (optInStatus: boolean, config: StatsGetterConfig) => {
diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_cluster_info.js b/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_cluster_info.ts
similarity index 61%
rename from src/legacy/core_plugins/telemetry/server/telemetry_collection/get_cluster_info.js
rename to src/legacy/core_plugins/telemetry/server/telemetry_collection/get_cluster_info.ts
index 2e4ed0b36ed26..67812457ed4ec 100644
--- a/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_cluster_info.js
+++ b/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_cluster_info.ts
@@ -17,14 +17,32 @@
  * under the License.
  */
 
+import { CallCluster } from 'src/legacy/core_plugins/elasticsearch';
+
+// This can be removed when the ES client improves the types
+export interface ESClusterInfo {
+  cluster_uuid: string;
+  cluster_name: string;
+  version: {
+    number: string;
+    build_flavor: string;
+    build_type: string;
+    build_hash: string;
+    build_date: string;
+    build_snapshot?: boolean;
+    lucene_version: string;
+    minimum_wire_compatibility_version: string;
+    minimum_index_compatibility_version: string;
+  };
+}
+
 /**
  * Get the cluster info from the connected cluster.
  *
  * This is the equivalent to GET /
  *
  * @param {function} callCluster The callWithInternalUser handler (exposed for testing)
- * @return {Promise} The response from Elasticsearch.
  */
-export function getClusterInfo(callCluster) {
-  return callCluster('info');
+export function getClusterInfo(callCluster: CallCluster) {
+  return callCluster<ESClusterInfo>('info');
 }
diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_local_license.ts b/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_local_license.ts
new file mode 100644
index 0000000000000..589392ffb6095
--- /dev/null
+++ b/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_local_license.ts
@@ -0,0 +1,90 @@
+/*
+ * 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 { CallCluster } from 'src/legacy/core_plugins/elasticsearch';
+import { LicenseGetter } from '../collection_manager';
+
+// From https://www.elastic.co/guide/en/elasticsearch/reference/current/get-license.html
+export interface ESLicense {
+  status: string;
+  uid: string;
+  type: string;
+  issue_date: string;
+  issue_date_in_millis: number;
+  expiry_date: string;
+  expirty_date_in_millis: number;
+  max_nodes: number;
+  issued_to: string;
+  issuer: string;
+  start_date_in_millis: number;
+}
+let cachedLicense: ESLicense | undefined;
+
+function fetchLicense(callCluster: CallCluster, local: boolean) {
+  return callCluster<{ license: ESLicense }>('transport.request', {
+    method: 'GET',
+    path: '/_license',
+    query: {
+      local,
+      // For versions >= 7.6 and < 8.0, this flag is needed otherwise 'platinum' is returned for 'enterprise' license.
+      accept_enterprise: 'true',
+    },
+  });
+}
+
+/**
+ * Get the cluster's license from the connected node.
+ *
+ * This is the equivalent of GET /_license?local=true .
+ *
+ * Like any X-Pack related API, X-Pack must installed for this to work.
+ */
+async function getLicenseFromLocalOrMaster(callCluster: CallCluster) {
+  // Fetching the local license is cheaper than getting it from the master and good enough
+  const { license } = await fetchLicense(callCluster, true).catch(async err => {
+    if (cachedLicense) {
+      try {
+        // Fallback to the master node's license info
+        const response = await fetchLicense(callCluster, false);
+        return response;
+      } catch (masterError) {
+        if (masterError.statusCode === 404) {
+          // If the master node does not have a license, we can assume there is no license
+          cachedLicense = undefined;
+        } else {
+          // Any other errors from the master node, throw and do not send any telemetry
+          throw err;
+        }
+      }
+    }
+    return { license: void 0 };
+  });
+
+  if (license) {
+    cachedLicense = license;
+  }
+  return license;
+}
+
+export const getLocalLicense: LicenseGetter = async (clustersDetails, { callCluster }) => {
+  const license = await getLicenseFromLocalOrMaster(callCluster);
+
+  // It should be called only with 1 cluster element in the clustersDetails array, but doing reduce just in case.
+  return clustersDetails.reduce((acc, { clusterUuid }) => ({ ...acc, [clusterUuid]: license }), {});
+};
diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_local_stats.ts b/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_local_stats.ts
index 8adb6d237bee8..d99710deb1cbc 100644
--- a/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_local_stats.ts
+++ b/src/legacy/core_plugins/telemetry/server/telemetry_collection/get_local_stats.ts
@@ -17,11 +17,8 @@
  * under the License.
  */
 
-import { get, omit } from 'lodash';
-// @ts-ignore
-import { getClusterInfo } from './get_cluster_info';
+import { getClusterInfo, ESClusterInfo } from './get_cluster_info';
 import { getClusterStats } from './get_cluster_stats';
-// @ts-ignore
 import { getKibana, handleKibanaStats, KibanaUsageStats } from './get_kibana';
 import { StatsGetter } from '../collection_manager';
 
@@ -33,20 +30,19 @@ import { StatsGetter } from '../collection_manager';
  * @param {Object} clusterInfo Cluster info (GET /)
  * @param {Object} clusterStats Cluster stats (GET /_cluster/stats)
  * @param {Object} kibana The Kibana Usage stats
- * @return {Object} A combined object containing the different responses.
  */
 export function handleLocalStats(
   server: any,
-  clusterInfo: any,
-  clusterStats: any,
+  { cluster_name, cluster_uuid, version }: ESClusterInfo,
+  { _nodes, cluster_name: clusterName, ...clusterStats }: any,
   kibana: KibanaUsageStats
 ) {
   return {
     timestamp: new Date().toISOString(),
-    cluster_uuid: get(clusterInfo, 'cluster_uuid'),
-    cluster_name: get(clusterInfo, 'cluster_name'),
-    version: get(clusterInfo, 'version.number'),
-    cluster_stats: omit(clusterStats, '_nodes', 'cluster_name'),
+    cluster_uuid,
+    cluster_name,
+    version: version.number,
+    cluster_stats: clusterStats,
     collection: 'local',
     stack_stats: {
       kibana: handleKibanaStats(server, kibana),
@@ -54,14 +50,12 @@ export function handleLocalStats(
   };
 }
 
+export type TelemetryLocalStats = ReturnType<typeof handleLocalStats>;
+
 /**
  * Get statistics for all products joined by Elasticsearch cluster.
- *
- * @param {Object} server The Kibana server instance used to call ES as the internal user
- * @param {function} callCluster The callWithInternalUser handler (exposed for testing)
- * @return {Promise} The object containing the current Elasticsearch cluster's telemetry.
  */
-export const getLocalStats: StatsGetter = async (clustersDetails, config) => {
+export const getLocalStats: StatsGetter<TelemetryLocalStats> = async (clustersDetails, config) => {
   const { server, callCluster, usageCollection } = config;
 
   return await Promise.all(
diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_collection/index.ts b/src/legacy/core_plugins/telemetry/server/telemetry_collection/index.ts
index 7f228dbc5e6f6..9ac94216c21bc 100644
--- a/src/legacy/core_plugins/telemetry/server/telemetry_collection/index.ts
+++ b/src/legacy/core_plugins/telemetry/server/telemetry_collection/index.ts
@@ -17,7 +17,6 @@
  * under the License.
  */
 
-// @ts-ignore
 export { getLocalStats } from './get_local_stats';
 export { getClusterUuids } from './get_cluster_stats';
 export { registerCollection } from './register_collection';
diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_collection/register_collection.ts b/src/legacy/core_plugins/telemetry/server/telemetry_collection/register_collection.ts
index faf8e9de79194..6580b47dba08e 100644
--- a/src/legacy/core_plugins/telemetry/server/telemetry_collection/register_collection.ts
+++ b/src/legacy/core_plugins/telemetry/server/telemetry_collection/register_collection.ts
@@ -39,6 +39,7 @@
 import { telemetryCollectionManager } from '../collection_manager';
 import { getLocalStats } from './get_local_stats';
 import { getClusterUuids } from './get_cluster_stats';
+import { getLocalLicense } from './get_local_license';
 
 export function registerCollection() {
   telemetryCollectionManager.setCollection({
@@ -47,5 +48,6 @@ export function registerCollection() {
     priority: 0,
     statsGetter: getLocalStats,
     clusterDetailsGetter: getClusterUuids,
+    licenseGetter: getLocalLicense,
   });
 }
diff --git a/src/plugins/telemetry/public/services/telemetry_service.test.ts b/src/plugins/telemetry/public/services/telemetry_service.test.ts
index 0ebcd52f1423c..0a49b0ae3084e 100644
--- a/src/plugins/telemetry/public/services/telemetry_service.test.ts
+++ b/src/plugins/telemetry/public/services/telemetry_service.test.ts
@@ -26,9 +26,18 @@ const mockSubtract = jest.fn().mockImplementation(() => {
   };
 });
 
+const mockClone = jest.fn().mockImplementation(() => {
+  return {
+    clone: mockClone,
+    subtract: mockSubtract,
+    toISOString: jest.fn(),
+  };
+});
+
 jest.mock('moment', () => {
   return jest.fn().mockImplementation(() => {
     return {
+      clone: mockClone,
       subtract: mockSubtract,
       toISOString: jest.fn(),
     };
@@ -43,6 +52,7 @@ describe('TelemetryService', () => {
       expect(telemetryService['http'].post).toBeCalledWith('/api/telemetry/v2/clusters/_stats', {
         body: JSON.stringify({ unencrypted: false, timeRange: {} }),
       });
+      expect(mockClone).toBeCalled();
       expect(mockSubtract).toBeCalledWith(20, 'minutes');
     });
   });
diff --git a/src/plugins/telemetry/public/services/telemetry_service.ts b/src/plugins/telemetry/public/services/telemetry_service.ts
index 073886e7d1327..cb91451bd8ef4 100644
--- a/src/plugins/telemetry/public/services/telemetry_service.ts
+++ b/src/plugins/telemetry/public/services/telemetry_service.ts
@@ -92,7 +92,10 @@ export class TelemetryService {
       body: JSON.stringify({
         unencrypted,
         timeRange: {
-          min: now.subtract(20, 'minutes').toISOString(),
+          min: now
+            .clone() // Need to clone it to avoid mutation (and max being the same value)
+            .subtract(20, 'minutes')
+            .toISOString(),
           max: now.toISOString(),
         },
       }),
diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_all_stats.test.ts b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_all_stats.test.ts
index 470642f9dd8a3..dcc7924fe171a 100644
--- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_all_stats.test.ts
+++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_all_stats.test.ts
@@ -5,7 +5,7 @@
  */
 
 import sinon from 'sinon';
-import { addStackStats, getAllStats, handleAllStats } from './get_all_stats';
+import { getStackStats, getAllStats, handleAllStats } from './get_all_stats';
 import { ESClusterStats } from './get_es_stats';
 import { KibanaStats } from './get_kibana_stats';
 import { ClustersHighLevelStats } from './get_high_level_stats';
@@ -223,7 +223,8 @@ describe('get_all_stats', () => {
         beats: {},
       });
 
-      expect(clusters).toStrictEqual(expectedClusters);
+      const [a, b, c] = expectedClusters;
+      expect(clusters).toStrictEqual([a, b, { ...c, stack_stats: {} }]);
     });
 
     it('handles no clusters response', () => {
@@ -233,9 +234,8 @@ describe('get_all_stats', () => {
     });
   });
 
-  describe('addStackStats', () => {
+  describe('getStackStats', () => {
     it('searches for clusters', () => {
-      const cluster = { cluster_uuid: 'a' };
       const stats = {
         a: {
           count: 2,
@@ -250,9 +250,7 @@ describe('get_all_stats', () => {
         },
       };
 
-      addStackStats(cluster as ESClusterStats, stats, 'xyz');
-
-      expect((cluster as any).stack_stats.xyz).toStrictEqual(stats.a);
+      expect(getStackStats('a', stats, 'xyz')).toStrictEqual({ xyz: stats.a });
     });
   });
 });
diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_all_stats.ts b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_all_stats.ts
index aa5e937387daf..a6ed5254dabd5 100644
--- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_all_stats.ts
+++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_all_stats.ts
@@ -61,38 +61,31 @@ export function handleAllStats(
   }
 ) {
   return clusters.map(cluster => {
-    // if they are using Kibana or Logstash, then add it to the cluster details under cluster.stack_stats
-    addStackStats(cluster, kibana, KIBANA_SYSTEM_ID);
-    addStackStats(cluster, logstash, LOGSTASH_SYSTEM_ID);
-    addStackStats(cluster, beats, BEATS_SYSTEM_ID);
-    mergeXPackStats(cluster, kibana, 'graph_workspace', 'graph'); // copy graph_workspace info out of kibana, merge it into stack_stats.xpack.graph
+    const stats = {
+      ...cluster,
+      stack_stats: {
+        ...cluster.stack_stats,
+        // if they are using Kibana or Logstash, then add it to the cluster details under cluster.stack_stats
+        ...getStackStats(cluster.cluster_uuid, kibana, KIBANA_SYSTEM_ID),
+        ...getStackStats(cluster.cluster_uuid, logstash, LOGSTASH_SYSTEM_ID),
+        ...getStackStats(cluster.cluster_uuid, beats, BEATS_SYSTEM_ID),
+      },
+    };
 
-    return cluster;
+    mergeXPackStats(stats, kibana, 'graph_workspace', 'graph'); // copy graph_workspace info out of kibana, merge it into stack_stats.xpack.graph
+
+    return stats;
   });
 }
 
-/**
- * Add product data to the {@code cluster}, only if it exists for the current {@code cluster}.
- *
- * @param {Object} cluster The current Elasticsearch cluster stats
- * @param {Object} allProductStats Product stats, keyed by Cluster UUID
- * @param {String} product The product name being added (e.g., 'kibana' or 'logstash')
- */
-export function addStackStats<T extends { [clusterUuid: string]: K }, K>(
-  cluster: ESClusterStats & { stack_stats?: { [product: string]: K } },
+export function getStackStats<T extends { [clusterUuid: string]: K }, K>(
+  clusterUuid: string,
   allProductStats: T,
   product: string
 ) {
-  const productStats = allProductStats[cluster.cluster_uuid];
-
+  const productStats = allProductStats[clusterUuid];
   // Don't add it if they're not using (or configured to report stats) this product for this cluster
-  if (productStats) {
-    if (!cluster.stack_stats) {
-      cluster.stack_stats = {};
-    }
-
-    cluster.stack_stats[product] = productStats;
-  }
+  return productStats ? { [product]: productStats } : {};
 }
 
 export function mergeXPackStats<T extends { [clusterUuid: string]: unknown }>(
diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_es_stats.ts b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_es_stats.ts
index f0ae1163d3f52..2f2fffd3f0823 100644
--- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_es_stats.ts
+++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_es_stats.ts
@@ -48,11 +48,6 @@ export function fetchElasticsearchStats(
       'hits.hits._source.timestamp',
       'hits.hits._source.cluster_name',
       'hits.hits._source.version',
-      'hits.hits._source.license.status', // license data only includes necessary fields to drive UI
-      'hits.hits._source.license.type',
-      'hits.hits._source.license.issue_date',
-      'hits.hits._source.license.expiry_date',
-      'hits.hits._source.license.expiry_date_in_millis',
       'hits.hits._source.cluster_stats',
       'hits.hits._source.stack_stats',
     ],
@@ -79,7 +74,11 @@ export function fetchElasticsearchStats(
 
 export interface ESClusterStats {
   cluster_uuid: string;
-  type: 'cluster_stats';
+  cluster_name: string;
+  timestamp: string;
+  version: string;
+  cluster_stats: object;
+  stack_stats?: object;
 }
 
 /**
diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_licenses.test.ts b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_licenses.test.ts
new file mode 100644
index 0000000000000..bb8326ce0b63a
--- /dev/null
+++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_licenses.test.ts
@@ -0,0 +1,84 @@
+/*
+ * 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 sinon from 'sinon';
+import { getLicenses, handleLicenses, fetchLicenses } from './get_licenses';
+
+describe('get_licenses', () => {
+  const callWith = sinon.stub();
+  const size = 123;
+  const server = {
+    config: sinon.stub().returns({
+      get: sinon
+        .stub()
+        .withArgs('xpack.monitoring.elasticsearch.index_pattern')
+        .returns('.monitoring-es-N-*')
+        .withArgs('xpack.monitoring.max_bucket_size')
+        .returns(size),
+    }),
+  };
+  const response = {
+    hits: {
+      hits: [
+        { _id: 'abc', _source: { cluster_uuid: 'abc', license: { type: 'basic' } } },
+        { _id: 'xyz', _source: { cluster_uuid: 'xyz', license: { type: 'basic' } } },
+        { _id: '123', _source: { cluster_uuid: '123' } },
+      ],
+    },
+  };
+  const expectedClusters = response.hits.hits.map(hit => hit._source);
+  const clusterUuids = expectedClusters.map(cluster => ({ clusterUuid: cluster.cluster_uuid }));
+  const expectedLicenses = {
+    abc: { type: 'basic' },
+    xyz: { type: 'basic' },
+    '123': void 0,
+  };
+
+  describe('getLicenses', () => {
+    it('returns clusters', async () => {
+      callWith.withArgs('search').returns(Promise.resolve(response));
+
+      expect(
+        await getLicenses(clusterUuids, { server, callCluster: callWith } as any)
+      ).toStrictEqual(expectedLicenses);
+    });
+  });
+
+  describe('fetchLicenses', () => {
+    it('searches for clusters', async () => {
+      callWith.returns(response);
+
+      expect(
+        await fetchLicenses(
+          server,
+          callWith,
+          clusterUuids.map(({ clusterUuid }) => clusterUuid)
+        )
+      ).toStrictEqual(response);
+    });
+  });
+
+  describe('handleLicenses', () => {
+    // filterPath makes it easy to ignore anything unexpected because it will come back empty
+    it('handles unexpected response', () => {
+      const clusters = handleLicenses({} as any);
+
+      expect(clusters).toStrictEqual({});
+    });
+
+    it('handles valid response', () => {
+      const clusters = handleLicenses(response as any);
+
+      expect(clusters).toStrictEqual(expectedLicenses);
+    });
+
+    it('handles no hits response', () => {
+      const clusters = handleLicenses({ hits: { hits: [] } } as any);
+
+      expect(clusters).toStrictEqual({});
+    });
+  });
+});
diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_licenses.ts b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_licenses.ts
new file mode 100644
index 0000000000000..7364227e7dc92
--- /dev/null
+++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_licenses.ts
@@ -0,0 +1,84 @@
+/*
+ * 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 {
+  StatsCollectionConfig,
+  LicenseGetter,
+} from 'src/legacy/core_plugins/telemetry/server/collection_manager';
+import { SearchResponse } from 'elasticsearch';
+import { ESLicense } from 'src/legacy/core_plugins/telemetry/server/telemetry_collection/get_local_license';
+import { INDEX_PATTERN_ELASTICSEARCH } from '../../common/constants';
+
+/**
+ * Get statistics for all selected Elasticsearch clusters.
+ */
+export const getLicenses: LicenseGetter = async (clustersDetails, { server, callCluster }) => {
+  const clusterUuids = clustersDetails.map(({ clusterUuid }) => clusterUuid);
+  const response = await fetchLicenses(server, callCluster, clusterUuids);
+  return handleLicenses(response);
+};
+
+/**
+ * Fetch the Elasticsearch stats.
+ *
+ * @param {Object} server The server instance
+ * @param {function} callCluster The callWithRequest or callWithInternalUser handler
+ * @param {Array} clusterUuids Cluster UUIDs to limit the request against
+ *
+ * Returns the response for the aggregations to fetch details for the product.
+ */
+export function fetchLicenses(
+  server: StatsCollectionConfig['server'],
+  callCluster: StatsCollectionConfig['callCluster'],
+  clusterUuids: string[]
+) {
+  const config = server.config();
+  const params = {
+    index: INDEX_PATTERN_ELASTICSEARCH,
+    size: config.get('monitoring.ui.max_bucket_size'),
+    ignoreUnavailable: true,
+    filterPath: ['hits.hits._source.cluster_uuid', 'hits.hits._source.license'],
+    body: {
+      query: {
+        bool: {
+          filter: [
+            /*
+             * Note: Unlike most places, we don't care about the old _type: cluster_stats because it would NOT
+             * have the license in it (that used to be in the .monitoring-data-2 index in cluster_info)
+             */
+            { term: { type: 'cluster_stats' } },
+            { terms: { cluster_uuid: clusterUuids } },
+          ],
+        },
+      },
+      collapse: { field: 'cluster_uuid' },
+      sort: { timestamp: { order: 'desc' } },
+    },
+  };
+
+  return callCluster('search', params);
+}
+
+export interface ESClusterStatsWithLicense {
+  cluster_uuid: string;
+  type: 'cluster_stats';
+  license?: ESLicense;
+}
+
+/**
+ * Extract the cluster stats for each cluster.
+ */
+export function handleLicenses(response: SearchResponse<ESClusterStatsWithLicense>) {
+  const clusters = response.hits?.hits || [];
+
+  return clusters.reduce(
+    (acc, { _source }) => ({
+      ...acc,
+      [_source.cluster_uuid]: _source.license,
+    }),
+    {}
+  );
+}
diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/register_monitoring_collection.ts b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/register_monitoring_collection.ts
index f0fda5229cb5c..0b14eb05f796f 100644
--- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/register_monitoring_collection.ts
+++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/register_monitoring_collection.ts
@@ -7,6 +7,7 @@
 import { telemetryCollectionManager } from '../../../../../../src/legacy/core_plugins/telemetry/server';
 import { getAllStats } from './get_all_stats';
 import { getClusterUuids } from './get_cluster_uuids';
+import { getLicenses } from './get_licenses';
 
 export function registerMonitoringCollection() {
   telemetryCollectionManager.setCollection({
@@ -15,5 +16,6 @@ export function registerMonitoringCollection() {
     priority: 2,
     statsGetter: getAllStats,
     clusterDetailsGetter: getClusterUuids,
+    licenseGetter: getLicenses,
   });
 }
diff --git a/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/__snapshots__/get_stats_with_xpack.test.ts.snap b/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/__snapshots__/get_stats_with_xpack.test.ts.snap
new file mode 100644
index 0000000000000..1a70504dc9391
--- /dev/null
+++ b/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/__snapshots__/get_stats_with_xpack.test.ts.snap
@@ -0,0 +1,118 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Telemetry Collection: Get Aggregated Stats OSS-like telemetry (no license nor X-Pack telemetry) 1`] = `
+Array [
+  Object {
+    "cluster_name": "test",
+    "cluster_stats": Object {},
+    "cluster_uuid": "test",
+    "collection": "local",
+    "stack_stats": Object {
+      "kibana": Object {
+        "count": 1,
+        "great": "googlymoogly",
+        "indices": 1,
+        "os": Object {
+          "platformReleases": Array [
+            Object {
+              "count": 1,
+              "platformRelease": "iv",
+            },
+          ],
+          "platforms": Array [
+            Object {
+              "count": 1,
+              "platform": "rocky",
+            },
+          ],
+        },
+        "plugins": Object {
+          "clouds": Object {
+            "chances": 95,
+          },
+          "localization": Object {
+            "integrities": Object {},
+            "labelsCount": 0,
+            "locale": "en",
+          },
+          "rain": Object {
+            "chances": 2,
+          },
+          "snow": Object {
+            "chances": 0,
+          },
+          "sun": Object {
+            "chances": 5,
+          },
+        },
+        "versions": Array [
+          Object {
+            "count": 1,
+            "version": "8675309",
+          },
+        ],
+      },
+    },
+    "version": "8.0.0",
+  },
+]
+`;
+
+exports[`Telemetry Collection: Get Aggregated Stats X-Pack telemetry (license + X-Pack) 1`] = `
+Array [
+  Object {
+    "cluster_name": "test",
+    "cluster_stats": Object {},
+    "cluster_uuid": "test",
+    "collection": "local",
+    "stack_stats": Object {
+      "kibana": Object {
+        "count": 1,
+        "great": "googlymoogly",
+        "indices": 1,
+        "os": Object {
+          "platformReleases": Array [
+            Object {
+              "count": 1,
+              "platformRelease": "iv",
+            },
+          ],
+          "platforms": Array [
+            Object {
+              "count": 1,
+              "platform": "rocky",
+            },
+          ],
+        },
+        "plugins": Object {
+          "clouds": Object {
+            "chances": 95,
+          },
+          "localization": Object {
+            "integrities": Object {},
+            "labelsCount": 0,
+            "locale": "en",
+          },
+          "rain": Object {
+            "chances": 2,
+          },
+          "snow": Object {
+            "chances": 0,
+          },
+          "sun": Object {
+            "chances": 5,
+          },
+        },
+        "versions": Array [
+          Object {
+            "count": 1,
+            "version": "8675309",
+          },
+        ],
+      },
+      "xpack": Object {},
+    },
+    "version": "8.0.0",
+  },
+]
+`;
diff --git a/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/__tests__/get_xpack.js b/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/__tests__/get_xpack.js
index eca130b4d7465..eb03701fd195b 100644
--- a/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/__tests__/get_xpack.js
+++ b/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/__tests__/get_xpack.js
@@ -8,42 +8,7 @@ import expect from '@kbn/expect';
 import sinon from 'sinon';
 
 import { TIMEOUT } from '../constants';
-import { getXPackLicense, getXPackUsage, getXPack, handleXPack } from '../get_xpack';
-
-function mockGetXPackLicense(callCluster, license, req) {
-  callCluster
-    .withArgs(req, 'transport.request', {
-      method: 'GET',
-      path: '/_license',
-      query: {
-        local: 'true',
-        accept_enterprise: 'true',
-      },
-    })
-    .returns(
-      license.then(
-        response => ({ license: response }),
-        () => {} // Catch error so that we don't emit UnhandledPromiseRejectionWarning for tests with invalid license
-      )
-    );
-
-  callCluster
-    .withArgs('transport.request', {
-      method: 'GET',
-      path: '/_license',
-      query: {
-        local: 'true',
-        accept_enterprise: 'true',
-      },
-    })
-    // conveniently wraps the passed in license object as { license: response }, like it really is
-    .returns(
-      license.then(
-        response => ({ license: response }),
-        () => {} // Catch error so that we don't emit UnhandledPromiseRejectionWarning for tests with invalid license
-      )
-    );
-}
+import { getXPackUsage } from '../get_xpack';
 
 function mockGetXPackUsage(callCluster, usage, req) {
   callCluster
@@ -67,31 +32,7 @@ function mockGetXPackUsage(callCluster, usage, req) {
     .returns(usage);
 }
 
-/**
- * Mock getXPack responses.
- *
- * @param {Function} callCluster Sinon function mock.
- * @param {Promise} license Promised license response.
- * @param {Promise} usage Promised usage response.
- * @param {Object} usage reqeust object.
- */
-export function mockGetXPack(callCluster, license, usage, req) {
-  mockGetXPackLicense(callCluster, license, req);
-  mockGetXPackUsage(callCluster, usage, req);
-}
-
 describe('get_xpack', () => {
-  describe('getXPackLicense', () => {
-    it('uses callCluster to get /_license API', async () => {
-      const response = { type: 'basic' };
-      const callCluster = sinon.stub();
-
-      mockGetXPackLicense(callCluster, Promise.resolve(response));
-
-      expect(await getXPackLicense(callCluster)).to.eql(response);
-    });
-  });
-
   describe('getXPackUsage', () => {
     it('uses callCluster to get /_xpack/usage API', () => {
       const response = Promise.resolve({});
@@ -102,48 +43,4 @@ describe('get_xpack', () => {
       expect(getXPackUsage(callCluster)).to.be(response);
     });
   });
-
-  describe('handleXPack', () => {
-    it('uses data as expected', () => {
-      const license = { fake: 'data' };
-      const usage = { also: 'fake', nested: { object: { data: [{ field: 1 }, { field: 2 }] } } };
-
-      expect(handleXPack(license, usage)).to.eql({ license, stack_stats: { xpack: usage } });
-    });
-  });
-
-  describe('getXPack', () => {
-    it('returns the formatted response object', async () => {
-      const license = { fancy: 'license' };
-      const xpack = { also: 'fancy' };
-
-      const callCluster = sinon.stub();
-
-      mockGetXPack(callCluster, Promise.resolve(license), Promise.resolve(xpack));
-
-      const data = await getXPack(callCluster);
-
-      expect(data).to.eql({ license, xpack });
-    });
-
-    it('returns empty object upon license failure', async () => {
-      const callCluster = sinon.stub();
-
-      mockGetXPack(callCluster, Promise.reject(new Error()), Promise.resolve({ also: 'fancy' }));
-
-      const data = await getXPack(callCluster);
-
-      expect(data).to.eql({});
-    });
-
-    it('returns empty object upon usage failure', async () => {
-      const callCluster = sinon.stub();
-
-      mockGetXPack(callCluster, Promise.resolve({ fancy: 'license' }), Promise.reject(new Error()));
-
-      const data = await getXPack(callCluster);
-
-      expect(data).to.eql({});
-    });
-  });
 });
diff --git a/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/constants.ts b/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/constants.ts
index c89fbe416a0cc..b6f1aabab95c4 100644
--- a/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/constants.ts
+++ b/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/constants.ts
@@ -7,4 +7,5 @@
 /**
  * The timeout used by each request, whenever a timeout can be specified.
  */
+
 export const TIMEOUT = '30s';
diff --git a/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/get_stats_with_xpack.test.ts b/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/get_stats_with_xpack.test.ts
new file mode 100644
index 0000000000000..b85cbd9661022
--- /dev/null
+++ b/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/get_stats_with_xpack.test.ts
@@ -0,0 +1,113 @@
+/*
+ * 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 { getStatsWithXpack } from './get_stats_with_xpack';
+
+const kibana = {
+  kibana: {
+    great: 'googlymoogly',
+    versions: [{ version: '8675309', count: 1 }],
+  },
+  kibana_stats: {
+    os: {
+      platform: 'rocky',
+      platformRelease: 'iv',
+    },
+  },
+  localization: {
+    locale: 'en',
+    labelsCount: 0,
+    integrities: {},
+  },
+  sun: { chances: 5 },
+  clouds: { chances: 95 },
+  rain: { chances: 2 },
+  snow: { chances: 0 },
+};
+
+const getMockServer = (getCluster = jest.fn()) => ({
+  log(tags: string[], message: string) {
+    // eslint-disable-next-line no-console
+    console.log({ tags, message });
+  },
+  config() {
+    return {
+      get(item: string) {
+        switch (item) {
+          case 'pkg.version':
+            return '8675309-snapshot';
+          default:
+            throw Error(`unexpected config.get('${item}') received.`);
+        }
+      },
+    };
+  },
+  plugins: {
+    elasticsearch: { getCluster },
+  },
+});
+
+const mockUsageCollection = (kibanaUsage = kibana) => ({
+  bulkFetch: () => kibanaUsage,
+  toObject: (data: any) => data,
+});
+
+describe('Telemetry Collection: Get Aggregated Stats', () => {
+  test('OSS-like telemetry (no license nor X-Pack telemetry)', async () => {
+    const callCluster = jest.fn(async (method: string, options: { path?: string }) => {
+      switch (method) {
+        case 'transport.request':
+          if (options.path === '/_license' || options.path === '/_xpack/usage') {
+            // eslint-disable-next-line no-throw-literal
+            throw { statusCode: 404 };
+          }
+          return {};
+        case 'info':
+          return { cluster_uuid: 'test', cluster_name: 'test', version: { number: '8.0.0' } };
+        default:
+          return {};
+      }
+    });
+    const usageCollection = mockUsageCollection();
+    const server = getMockServer();
+
+    const stats = await getStatsWithXpack([{ clusterUuid: '1234' }], {
+      callCluster,
+      usageCollection,
+      server,
+    } as any);
+    expect(stats.map(({ timestamp, ...rest }) => rest)).toMatchSnapshot();
+  });
+
+  test('X-Pack telemetry (license + X-Pack)', async () => {
+    const callCluster = jest.fn(async (method: string, options: { path?: string }) => {
+      switch (method) {
+        case 'transport.request':
+          if (options.path === '/_license') {
+            return {
+              license: { type: 'basic' },
+            };
+          }
+          if (options.path === '/_xpack/usage') {
+            return {};
+          }
+        case 'info':
+          return { cluster_uuid: 'test', cluster_name: 'test', version: { number: '8.0.0' } };
+        default:
+          return {};
+      }
+    });
+    const usageCollection = mockUsageCollection();
+    const server = getMockServer();
+
+    const stats = await getStatsWithXpack([{ clusterUuid: '1234' }], {
+      callCluster,
+      usageCollection,
+      server,
+    } as any);
+    expect(stats.map(({ timestamp, ...rest }) => rest)).toMatchSnapshot();
+  });
+});
diff --git a/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/get_stats_with_xpack.ts b/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/get_stats_with_xpack.ts
index 41076d96231c9..ea7465f66f120 100644
--- a/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/get_stats_with_xpack.ts
+++ b/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/get_stats_with_xpack.ts
@@ -4,19 +4,33 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-// @ts-ignore
-import { getXPack } from './get_xpack';
-import { getLocalStats } from '../../../../../../src/legacy/core_plugins/telemetry/server/telemetry_collection';
 import { StatsGetter } from '../../../../../../src/legacy/core_plugins/telemetry/server/collection_manager';
+import {
+  getLocalStats,
+  TelemetryLocalStats,
+} from '../../../../../../src/legacy/core_plugins/telemetry/server/telemetry_collection/get_local_stats';
+import { getXPackUsage } from './get_xpack';
 
-export const getStatsWithXpack: StatsGetter = async function(clustersDetails, config) {
+export type TelemetryAggregatedStats = TelemetryLocalStats & {
+  stack_stats: { xpack?: object };
+};
+
+export const getStatsWithXpack: StatsGetter<TelemetryAggregatedStats> = async function(
+  clustersDetails,
+  config
+) {
   const { callCluster } = config;
   const clustersLocalStats = await getLocalStats(clustersDetails, config);
-  const { license, xpack } = await getXPack(callCluster);
+  const xpack = await getXPackUsage(callCluster).catch(() => undefined); // We want to still report something (and do not lose the license) even when this method fails.
 
   return clustersLocalStats.map(localStats => {
-    localStats.license = license;
-    localStats.stack_stats.xpack = xpack;
+    if (xpack) {
+      return {
+        ...localStats,
+        stack_stats: { ...localStats.stack_stats, xpack },
+      };
+    }
+
     return localStats;
   });
 };
diff --git a/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/get_xpack.js b/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/get_xpack.js
deleted file mode 100644
index aaeb890981aa1..0000000000000
--- a/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/get_xpack.js
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { TIMEOUT } from './constants';
-
-/**
- * Get the cluster stats from the connected cluster.
- *
- * This is the equivalent of GET /_license?local=true .
- *
- * Like any X-Pack related API, X-Pack must installed for this to work.
- *
- * @param {function} callCluster The callWithInternalUser handler (exposed for testing)
- * @return {Promise} The response from Elasticsearch.
- */
-export function getXPackLicense(callCluster) {
-  return callCluster('transport.request', {
-    method: 'GET',
-    path: '/_license',
-    query: {
-      // Fetching the local license is cheaper than getting it from the master and good enough
-      local: 'true',
-      // For versions >= 7.6 and < 8.0, this flag is needed otherwise 'platinum' is returned for 'enterprise' license.
-      accept_enterprise: 'true',
-    },
-  }).then(({ license }) => license);
-}
-
-/**
- * Get the cluster stats from the connected cluster.
- *
- * This is the equivalent of GET /_xpack/usage?master_timeout=${TIMEOUT}
- *
- * Like any X-Pack related API, X-Pack must installed for this to work.
- *
- * @param {function} callCluster The callWithInternalUser handler (exposed for testing)
- * @return {Promise} The response from Elasticsearch equivalent to GET /_cluster/stats.
- */
-export function getXPackUsage(callCluster) {
-  return callCluster('transport.request', {
-    method: 'GET',
-    path: '/_xpack/usage',
-    query: {
-      master_timeout: TIMEOUT,
-    },
-  });
-}
-
-/**
- * Combine the X-Pack responses into a single response as Monitoring does already.
- *
- * @param {Object} license The license returned from /_license
- * @param {Object} usage The usage details returned from /_xpack/usage
- * @return {Object} An object containing both the license and usage.
- */
-export function handleXPack(license, usage) {
-  return {
-    license,
-    stack_stats: {
-      xpack: usage,
-    },
-  };
-}
-
-/**
- * Combine all X-Pack requests as a singular request that is ignored upon failure.
- *
- * @param {function} callCluster The callWithInternalUser handler (exposed for testing)
- * @return {Promise}
- */
-export function getXPack(callCluster) {
-  return Promise.all([getXPackLicense(callCluster), getXPackUsage(callCluster)])
-    .then(([license, xpack]) => {
-      return {
-        license,
-        xpack,
-      };
-    })
-    .catch(() => {
-      return {};
-    });
-}
diff --git a/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/get_xpack.ts b/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/get_xpack.ts
new file mode 100644
index 0000000000000..9b69540007e5f
--- /dev/null
+++ b/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/get_xpack.ts
@@ -0,0 +1,25 @@
+/*
+ * 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 { CallCluster } from 'src/legacy/core_plugins/elasticsearch';
+import { TIMEOUT } from './constants';
+
+/**
+ * Get the cluster stats from the connected cluster.
+ *
+ * This is the equivalent of GET /_xpack/usage?master_timeout=${TIMEOUT}
+ *
+ * Like any X-Pack related API, X-Pack must installed for this to work.
+ */
+export function getXPackUsage(callCluster: CallCluster) {
+  return callCluster('transport.request', {
+    method: 'GET',
+    path: '/_xpack/usage',
+    query: {
+      master_timeout: TIMEOUT,
+    },
+  });
+}
diff --git a/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/register_xpack_collection.ts b/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/register_xpack_collection.ts
index 57faf2da90d09..04445d7bde7d7 100644
--- a/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/register_xpack_collection.ts
+++ b/x-pack/legacy/plugins/xpack_main/server/telemetry_collection/register_xpack_collection.ts
@@ -4,6 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
+import { getLocalLicense } from '../../../../../../src/legacy/core_plugins/telemetry/server/telemetry_collection/get_local_license';
 import { telemetryCollectionManager } from '../../../../../../src/legacy/core_plugins/telemetry/server';
 import { getClusterUuids } from '../../../../../../src/legacy/core_plugins/telemetry/server/telemetry_collection';
 import { getStatsWithXpack } from './get_stats_with_xpack';
@@ -15,5 +16,6 @@ export function registerMonitoringCollection() {
     priority: 1,
     statsGetter: getStatsWithXpack,
     clusterDetailsGetter: getClusterUuids,
+    licenseGetter: getLocalLicense,
   });
 }
diff --git a/x-pack/test/api_integration/apis/telemetry/fixtures/basiccluster.json b/x-pack/test/api_integration/apis/telemetry/fixtures/basiccluster.json
index 5c3c8cfcab7a6..a0097f53ac93b 100644
--- a/x-pack/test/api_integration/apis/telemetry/fixtures/basiccluster.json
+++ b/x-pack/test/api_integration/apis/telemetry/fixtures/basiccluster.json
@@ -1,6 +1,7 @@
 [
   {
     "cluster_name": "docker-cluster",
+    "collectionSource": "monitoring",
     "cluster_stats": {
       "indices": {
         "completion": {
@@ -161,6 +162,12 @@
     },
     "cluster_uuid": "ooEYzl3fSL222Y6eVm7SAA",
     "license": {
+      "uid": "79dc3adb-e85e-4cef-a985-9b74eb6c07c1",
+      "issue_date_in_millis": 1532383643540,
+      "issued_to": "docker-cluster",
+      "issuer": "elasticsearch",
+      "max_nodes": 1000,
+      "start_date_in_millis": -1,
       "issue_date": "2018-07-23T22:07:23.540Z",
       "status": "active",
       "type": "basic"
diff --git a/x-pack/test/api_integration/apis/telemetry/fixtures/multicluster.json b/x-pack/test/api_integration/apis/telemetry/fixtures/multicluster.json
index 2e6a75ee75972..6cc9c55157b28 100644
--- a/x-pack/test/api_integration/apis/telemetry/fixtures/multicluster.json
+++ b/x-pack/test/api_integration/apis/telemetry/fixtures/multicluster.json
@@ -1 +1,1001 @@
-[{"cluster_uuid":"6d-9tDFTRe-qT5GoBytdlQ","timestamp":"2017-08-15T22:10:59.952Z","cluster_name":"clustertwo","version":"7.0.0-alpha1","license":{"status":"active","type":"basic","issue_date":"2014-09-29T00:00:00.000Z","expiry_date":"2030-08-29T23:59:59.999Z","expiry_date_in_millis":1914278399999},"cluster_stats":{"timestamp":1502835059952,"status":"green","indices":{"count":1,"shards":{"total":1,"primaries":1,"replication":0,"index":{"shards":{"min":1,"max":1,"avg":1},"primaries":{"min":1,"max":1,"avg":1},"replication":{"min":0,"max":0,"avg":0}}},"docs":{"count":8,"deleted":0},"store":{"size_in_bytes":34095},"fielddata":{"memory_size_in_bytes":0,"evictions":0},"query_cache":{"memory_size_in_bytes":0,"total_count":0,"hit_count":0,"miss_count":0,"cache_size":0,"cache_count":0,"evictions":0},"completion":{"size_in_bytes":0},"segments":{"count":8,"memory_in_bytes":13852,"terms_memory_in_bytes":10412,"stored_fields_memory_in_bytes":2496,"term_vectors_memory_in_bytes":0,"norms_memory_in_bytes":256,"points_memory_in_bytes":8,"doc_values_memory_in_bytes":680,"index_writer_memory_in_bytes":0,"version_map_memory_in_bytes":0,"fixed_bit_set_memory_in_bytes":0,"max_unsafe_auto_id_timestamp":-1,"file_sizes":{}}},"nodes":{"count":{"total":1,"data":1,"coordinating_only":0,"master":1,"ingest":1},"versions":["7.0.0-alpha1"],"os":{"available_processors":4,"allocated_processors":1,"names":[{"name":"Mac OS X","count":1}],"mem":{"total_in_bytes":17179869184,"free_in_bytes":183799808,"used_in_bytes":16996069376,"free_percent":1,"used_percent":99}},"process":{"cpu":{"percent":0},"open_file_descriptors":{"min":163,"max":163,"avg":163}},"jvm":{"max_uptime_in_millis":701043,"versions":[{"vm_version":"25.121-b13","count":1,"vm_vendor":"Oracle Corporation","version":"1.8.0_121","vm_name":"Java HotSpot(TM) 64-Bit Server VM"}],"mem":{"heap_used_in_bytes":204299464,"heap_max_in_bytes":628555776},"threads":40},"fs":{"total_in_bytes":499065712640,"free_in_bytes":200665341952,"available_in_bytes":200403197952},"plugins":[{"classname":"org.elasticsearch.xpack.XPackPlugin","name":"x-pack","description":"Elasticsearch Expanded Pack Plugin","version":"7.0.0-alpha1","has_native_controller":true}],"network_types":{"transport_types":{"security4":1},"http_types":{"security4":1}}}},"stack_stats":{"xpack":{"security":{"available":false,"enabled":true,"realms":{"file":{"available":false,"enabled":false},"ldap":{"available":false,"enabled":false},"native":{"available":false,"enabled":false},"active_directory":{"available":false,"enabled":false},"pki":{"available":false,"enabled":false}},"roles":{"native":{"size":1,"dls":false,"fls":false},"file":{"size":0,"dls":false,"fls":false}},"role_mapping":{"native":{"size":0,"enabled":0}},"ssl":{"http":{"enabled":false}},"audit":{"outputs":["logfile"],"enabled":false},"ipfilter":{"http":false,"transport":false},"anonymous":{"enabled":false}},"monitoring":{"available":true,"enabled":true,"enabled_exporters":{"http":1}},"watcher":{"available":false,"enabled":true,"execution":{"actions":{"_all":{"total":0,"total_time_in_ms":0}}}},"graph":{"available":false,"enabled":true},"ml":{"available":false,"enabled":true,"jobs":{"_all":{"count":0,"detectors":{"total":0,"min":0,"avg":0,"max":0},"model_size":{"total":0,"min":0,"avg":0,"max":0}}},"datafeeds":{"_all":{"count":0}}},"logstash":{"available":false,"enabled":true},"ccr":{"auto_follow_patterns_count":0,"available":true,"follower_indices_count":0,"enabled":true}}}},{"cluster_uuid":"lOF8kofiS_2DX58o9mXJ1Q","timestamp":"2017-08-15T22:10:54.610Z","cluster_name":"monitoring-one","version":"7.0.0-alpha1","license":{"status":"active","type":"trial","issue_date":"2017-08-15T21:58:28.997Z","expiry_date":"2017-09-14T21:58:28.997Z","expiry_date_in_millis":1505426308997},"cluster_stats":{"timestamp":1502835054610,"status":"yellow","indices":{"count":8,"shards":{"total":8,"primaries":8,"replication":0,"index":{"shards":{"min":1,"max":1,"avg":1},"primaries":{"min":1,"max":1,"avg":1},"replication":{"min":0,"max":0,"avg":0}}},"docs":{"count":3997,"deleted":69},"store":{"size_in_bytes":2647163},"fielddata":{"memory_size_in_bytes":2104,"evictions":0},"query_cache":{"memory_size_in_bytes":0,"total_count":0,"hit_count":0,"miss_count":0,"cache_size":0,"cache_count":0,"evictions":0},"completion":{"size_in_bytes":0},"segments":{"count":36,"memory_in_bytes":278961,"terms_memory_in_bytes":166031,"stored_fields_memory_in_bytes":11544,"term_vectors_memory_in_bytes":0,"norms_memory_in_bytes":6784,"points_memory_in_bytes":3250,"doc_values_memory_in_bytes":91352,"index_writer_memory_in_bytes":205347,"version_map_memory_in_bytes":26362,"fixed_bit_set_memory_in_bytes":992,"max_unsafe_auto_id_timestamp":-1,"file_sizes":{}}},"nodes":{"count":{"total":1,"data":1,"coordinating_only":0,"master":1,"ingest":1},"versions":["7.0.0-alpha1"],"os":{"available_processors":4,"allocated_processors":1,"names":[{"name":"Mac OS X","count":1}],"mem":{"total_in_bytes":17179869184,"free_in_bytes":86732800,"used_in_bytes":17093136384,"free_percent":1,"used_percent":99}},"process":{"cpu":{"percent":2},"open_file_descriptors":{"min":178,"max":178,"avg":178}},"jvm":{"max_uptime_in_millis":761002,"versions":[{"vm_version":"25.121-b13","count":1,"vm_vendor":"Oracle Corporation","version":"1.8.0_121","vm_name":"Java HotSpot(TM) 64-Bit Server VM"}],"mem":{"heap_used_in_bytes":133041176,"heap_max_in_bytes":628555776},"threads":42},"fs":{"total_in_bytes":499065712640,"free_in_bytes":200665792512,"available_in_bytes":200403648512},"plugins":[{"classname":"org.elasticsearch.xpack.XPackPlugin","name":"x-pack","description":"Elasticsearch Expanded Pack Plugin","version":"7.0.0-alpha1","has_native_controller":true}],"network_types":{"transport_types":{"security4":1},"http_types":{"security4":1}}}},"stack_stats":{"xpack":{"security":{"available":true,"enabled":true,"realms":{"file":{"name":["default_file"],"available":true,"size":[0],"enabled":true,"order":[2147483647]},"ldap":{"available":true,"enabled":false},"native":{"name":["default_native"],"available":true,"size":[2],"enabled":true,"order":[2147483647]},"active_directory":{"available":true,"enabled":false},"pki":{"available":true,"enabled":false}},"roles":{"native":{"size":1,"dls":false,"fls":false},"file":{"size":0,"dls":false,"fls":false}},"role_mapping":{"native":{"size":0,"enabled":0}},"ssl":{"http":{"enabled":false}},"audit":{"outputs":["logfile"],"enabled":false},"ipfilter":{"http":false,"transport":false},"anonymous":{"enabled":false}},"monitoring":{"available":true,"enabled":true,"enabled_exporters":{"local":1}},"watcher":{"available":true,"enabled":true,"execution":{"actions":{"index":{"total":14,"total_time_in_ms":158},"_all":{"total":110,"total_time_in_ms":2245},"email":{"total":14,"total_time_in_ms":3}}}},"graph":{"available":true,"enabled":true},"ml":{"available":true,"enabled":true,"jobs":{"_all":{"count":0,"detectors":{"total":0,"min":0,"avg":0,"max":0},"model_size":{"total":0,"min":0,"avg":0,"max":0}}},"datafeeds":{"_all":{"count":0}}},"logstash":{"available":true,"enabled":true},"ccr":{"auto_follow_patterns_count":0,"available":true,"follower_indices_count":0,"enabled":true}}}},{"cluster_uuid":"TkHOX_-1TzWwbROwQJU5IA","timestamp":"2017-08-15T22:10:52.642Z","cluster_name":"clusterone","version":"7.0.0-alpha1","license":{"status":"active","type":"trial","issue_date":"2017-08-15T21:58:47.135Z","expiry_date":"2017-09-14T21:58:47.135Z","expiry_date_in_millis":1505426327135},"cluster_stats":{"timestamp":1502835052641,"status":"green","indices":{"count":5,"shards":{"total":26,"primaries":13,"replication":1,"index":{"shards":{"min":2,"max":10,"avg":5.2},"primaries":{"min":1,"max":5,"avg":2.6},"replication":{"min":1,"max":1,"avg":1}}},"docs":{"count":150,"deleted":0},"store":{"size_in_bytes":4838464},"fielddata":{"memory_size_in_bytes":0,"evictions":0},"query_cache":{"memory_size_in_bytes":0,"total_count":0,"hit_count":0,"miss_count":0,"cache_size":0,"cache_count":0,"evictions":0},"completion":{"size_in_bytes":0},"segments":{"count":76,"memory_in_bytes":1907922,"terms_memory_in_bytes":1595112,"stored_fields_memory_in_bytes":23744,"term_vectors_memory_in_bytes":0,"norms_memory_in_bytes":197184,"points_memory_in_bytes":3818,"doc_values_memory_in_bytes":88064,"index_writer_memory_in_bytes":7006184,"version_map_memory_in_bytes":260,"fixed_bit_set_memory_in_bytes":0,"max_unsafe_auto_id_timestamp":1502834982386,"file_sizes":{}}},"nodes":{"count":{"total":2,"data":2,"coordinating_only":0,"master":2,"ingest":2},"versions":["7.0.0-alpha1"],"os":{"available_processors":8,"allocated_processors":2,"names":[{"name":"Mac OS X","count":2}],"mem":{"total_in_bytes":34359738368,"free_in_bytes":332099584,"used_in_bytes":34027638784,"free_percent":1,"used_percent":99}},"process":{"cpu":{"percent":2},"open_file_descriptors":{"min":218,"max":237,"avg":227}},"jvm":{"max_uptime_in_millis":741786,"versions":[{"vm_version":"25.121-b13","count":2,"vm_vendor":"Oracle Corporation","version":"1.8.0_121","vm_name":"Java HotSpot(TM) 64-Bit Server VM"}],"mem":{"heap_used_in_bytes":465621856,"heap_max_in_bytes":1257111552},"threads":92},"fs":{"total_in_bytes":499065712640,"free_in_bytes":200666353664,"available_in_bytes":200404209664},"plugins":[{"classname":"org.elasticsearch.xpack.XPackPlugin","name":"x-pack","description":"Elasticsearch Expanded Pack Plugin","version":"7.0.0-alpha1","has_native_controller":true}],"network_types":{"transport_types":{"security4":2},"http_types":{"security4":2}}}},"stack_stats":{"xpack":{"security":{"available":true,"enabled":true,"realms":{"file":{"name":["default_file"],"available":true,"size":[0],"enabled":true,"order":[2147483647]},"ldap":{"available":true,"enabled":false},"native":{"name":["default_native"],"available":true,"size":[1],"enabled":true,"order":[2147483647]},"active_directory":{"available":true,"enabled":false},"pki":{"available":true,"enabled":false}},"roles":{"native":{"size":1,"dls":false,"fls":false},"file":{"size":0,"dls":false,"fls":false}},"role_mapping":{"native":{"size":0,"enabled":0}},"ssl":{"http":{"enabled":false}},"audit":{"outputs":["logfile"],"enabled":false},"ipfilter":{"http":false,"transport":false},"anonymous":{"enabled":false}},"monitoring":{"available":true,"enabled":true,"enabled_exporters":{"http":1}},"watcher":{"available":true,"enabled":true,"execution":{"actions":{"_all":{"total":0,"total_time_in_ms":0}}}},"graph":{"available":true,"enabled":true,"graph_workspace":{"total":0}},"ml":{"available":true,"enabled":true,"jobs":{"_all":{"count":3,"detectors":{"total":3,"min":1,"avg":1,"max":1},"model_size":{"total":0,"min":0,"avg":0,"max":0}},"opened":{"count":1,"detectors":{"total":1,"min":1,"avg":1,"max":1},"model_size":{"total":0,"min":0,"avg":0,"max":0}},"closed":{"count":2,"detectors":{"total":2,"min":1,"avg":1,"max":1},"model_size":{"total":0,"min":0,"avg":0,"max":0}}},"datafeeds":{"_all":{"count":0}}},"logstash":{"available":true,"enabled":true},"ccr":{"auto_follow_patterns_count":0,"available":true,"follower_indices_count":0,"enabled":true}},"kibana":{"count":1,"versions":[{"version":"7.0.0-alpha1","count":1}],"os":{"platforms":[],"platformReleases":[],"distros":[],"distroReleases":[]},"dashboard":{"total":0},"visualization":{"total":0},"search":{"total":0},"index_pattern":{"total":0},"graph_workspace":{"total":0},"timelion_sheet":{"total":0},"indices":1,"plugins":{}},"logstash":{"count":1,"versions":[{"version":"7.0.0-alpha1","count":1}],"os":{"platforms":[],"platformReleases":[],"distros":[],"distroReleases":[]}}}}]
+[
+  {
+    "cluster_uuid": "6d-9tDFTRe-qT5GoBytdlQ",
+    "collectionSource": "monitoring",
+    "timestamp": "2017-08-15T22:10:59.952Z",
+    "cluster_name": "clustertwo",
+    "version": "7.0.0-alpha1",
+    "license": {
+      "status": "active",
+      "type": "basic",
+      "issue_date": "2014-09-29T00:00:00.000Z",
+      "expiry_date": "2030-08-29T23:59:59.999Z",
+      "expiry_date_in_millis": 1914278399999,
+      "issue_date_in_millis": 1411948800000,
+      "issued_to": "issuedTo",
+      "issuer": "issuer",
+      "max_nodes": 1,
+      "uid": "893361dc-9749-4997-93cb-802e3d7fa4a8",
+      "hkey": null
+    },
+    "cluster_stats": {
+      "timestamp": 1502835059952,
+      "status": "green",
+      "indices": {
+        "count": 1,
+        "shards": {
+          "total": 1,
+          "primaries": 1,
+          "replication": 0,
+          "index": {
+            "shards": {
+              "min": 1,
+              "max": 1,
+              "avg": 1
+            },
+            "primaries": {
+              "min": 1,
+              "max": 1,
+              "avg": 1
+            },
+            "replication": {
+              "min": 0,
+              "max": 0,
+              "avg": 0
+            }
+          }
+        },
+        "docs": {
+          "count": 8,
+          "deleted": 0
+        },
+        "store": {
+          "size_in_bytes": 34095
+        },
+        "fielddata": {
+          "memory_size_in_bytes": 0,
+          "evictions": 0
+        },
+        "query_cache": {
+          "memory_size_in_bytes": 0,
+          "total_count": 0,
+          "hit_count": 0,
+          "miss_count": 0,
+          "cache_size": 0,
+          "cache_count": 0,
+          "evictions": 0
+        },
+        "completion": {
+          "size_in_bytes": 0
+        },
+        "segments": {
+          "count": 8,
+          "memory_in_bytes": 13852,
+          "terms_memory_in_bytes": 10412,
+          "stored_fields_memory_in_bytes": 2496,
+          "term_vectors_memory_in_bytes": 0,
+          "norms_memory_in_bytes": 256,
+          "points_memory_in_bytes": 8,
+          "doc_values_memory_in_bytes": 680,
+          "index_writer_memory_in_bytes": 0,
+          "version_map_memory_in_bytes": 0,
+          "fixed_bit_set_memory_in_bytes": 0,
+          "max_unsafe_auto_id_timestamp": -1,
+          "file_sizes": {}
+        }
+      },
+      "nodes": {
+        "count": {
+          "total": 1,
+          "data": 1,
+          "coordinating_only": 0,
+          "master": 1,
+          "ingest": 1
+        },
+        "versions": [
+          "7.0.0-alpha1"
+        ],
+        "os": {
+          "available_processors": 4,
+          "allocated_processors": 1,
+          "names": [
+            {
+              "name": "Mac OS X",
+              "count": 1
+            }
+          ],
+          "mem": {
+            "total_in_bytes": 17179869184,
+            "free_in_bytes": 183799808,
+            "used_in_bytes": 16996069376,
+            "free_percent": 1,
+            "used_percent": 99
+          }
+        },
+        "process": {
+          "cpu": {
+            "percent": 0
+          },
+          "open_file_descriptors": {
+            "min": 163,
+            "max": 163,
+            "avg": 163
+          }
+        },
+        "jvm": {
+          "max_uptime_in_millis": 701043,
+          "versions": [
+            {
+              "vm_version": "25.121-b13",
+              "count": 1,
+              "vm_vendor": "Oracle Corporation",
+              "version": "1.8.0_121",
+              "vm_name": "Java HotSpot(TM) 64-Bit Server VM"
+            }
+          ],
+          "mem": {
+            "heap_used_in_bytes": 204299464,
+            "heap_max_in_bytes": 628555776
+          },
+          "threads": 40
+        },
+        "fs": {
+          "total_in_bytes": 499065712640,
+          "free_in_bytes": 200665341952,
+          "available_in_bytes": 200403197952
+        },
+        "plugins": [
+          {
+            "classname": "org.elasticsearch.xpack.XPackPlugin",
+            "name": "x-pack",
+            "description": "Elasticsearch Expanded Pack Plugin",
+            "version": "7.0.0-alpha1",
+            "has_native_controller": true
+          }
+        ],
+        "network_types": {
+          "transport_types": {
+            "security4": 1
+          },
+          "http_types": {
+            "security4": 1
+          }
+        }
+      }
+    },
+    "stack_stats": {
+      "xpack": {
+        "security": {
+          "available": false,
+          "enabled": true,
+          "realms": {
+            "file": {
+              "available": false,
+              "enabled": false
+            },
+            "ldap": {
+              "available": false,
+              "enabled": false
+            },
+            "native": {
+              "available": false,
+              "enabled": false
+            },
+            "active_directory": {
+              "available": false,
+              "enabled": false
+            },
+            "pki": {
+              "available": false,
+              "enabled": false
+            }
+          },
+          "roles": {
+            "native": {
+              "size": 1,
+              "dls": false,
+              "fls": false
+            },
+            "file": {
+              "size": 0,
+              "dls": false,
+              "fls": false
+            }
+          },
+          "role_mapping": {
+            "native": {
+              "size": 0,
+              "enabled": 0
+            }
+          },
+          "ssl": {
+            "http": {
+              "enabled": false
+            }
+          },
+          "audit": {
+            "outputs": [
+              "logfile"
+            ],
+            "enabled": false
+          },
+          "ipfilter": {
+            "http": false,
+            "transport": false
+          },
+          "anonymous": {
+            "enabled": false
+          }
+        },
+        "monitoring": {
+          "available": true,
+          "enabled": true,
+          "enabled_exporters": {
+            "http": 1
+          }
+        },
+        "watcher": {
+          "available": false,
+          "enabled": true,
+          "execution": {
+            "actions": {
+              "_all": {
+                "total": 0,
+                "total_time_in_ms": 0
+              }
+            }
+          }
+        },
+        "graph": {
+          "available": false,
+          "enabled": true
+        },
+        "ml": {
+          "available": false,
+          "enabled": true,
+          "jobs": {
+            "_all": {
+              "count": 0,
+              "detectors": {
+                "total": 0,
+                "min": 0,
+                "avg": 0,
+                "max": 0
+              },
+              "model_size": {
+                "total": 0,
+                "min": 0,
+                "avg": 0,
+                "max": 0
+              }
+            }
+          },
+          "datafeeds": {
+            "_all": {
+              "count": 0
+            }
+          }
+        },
+        "logstash": {
+          "available": false,
+          "enabled": true
+        },
+        "ccr": {
+          "auto_follow_patterns_count": 0,
+          "available": true,
+          "follower_indices_count": 0,
+          "enabled": true
+        }
+      }
+    }
+  },
+  {
+    "cluster_uuid": "lOF8kofiS_2DX58o9mXJ1Q",
+    "collectionSource": "monitoring",
+    "timestamp": "2017-08-15T22:10:54.610Z",
+    "cluster_name": "monitoring-one",
+    "version": "7.0.0-alpha1",
+    "license": {
+      "status": "active",
+      "type": "trial",
+      "issue_date": "2017-08-15T21:58:28.997Z",
+      "expiry_date": "2017-09-14T21:58:28.997Z",
+      "expiry_date_in_millis": 1505426308997,
+      "issue_date_in_millis": 1502834308997,
+      "issued_to": "monitoring-one",
+      "issuer": "elasticsearch",
+      "max_nodes": 1000,
+      "start_date_in_millis": -1,
+      "uid": "e5f6e897-0db5-4042-ad7c-6628ddc91691",
+      "hkey": null
+    },
+    "cluster_stats": {
+      "timestamp": 1502835054610,
+      "status": "yellow",
+      "indices": {
+        "count": 8,
+        "shards": {
+          "total": 8,
+          "primaries": 8,
+          "replication": 0,
+          "index": {
+            "shards": {
+              "min": 1,
+              "max": 1,
+              "avg": 1
+            },
+            "primaries": {
+              "min": 1,
+              "max": 1,
+              "avg": 1
+            },
+            "replication": {
+              "min": 0,
+              "max": 0,
+              "avg": 0
+            }
+          }
+        },
+        "docs": {
+          "count": 3997,
+          "deleted": 69
+        },
+        "store": {
+          "size_in_bytes": 2647163
+        },
+        "fielddata": {
+          "memory_size_in_bytes": 2104,
+          "evictions": 0
+        },
+        "query_cache": {
+          "memory_size_in_bytes": 0,
+          "total_count": 0,
+          "hit_count": 0,
+          "miss_count": 0,
+          "cache_size": 0,
+          "cache_count": 0,
+          "evictions": 0
+        },
+        "completion": {
+          "size_in_bytes": 0
+        },
+        "segments": {
+          "count": 36,
+          "memory_in_bytes": 278961,
+          "terms_memory_in_bytes": 166031,
+          "stored_fields_memory_in_bytes": 11544,
+          "term_vectors_memory_in_bytes": 0,
+          "norms_memory_in_bytes": 6784,
+          "points_memory_in_bytes": 3250,
+          "doc_values_memory_in_bytes": 91352,
+          "index_writer_memory_in_bytes": 205347,
+          "version_map_memory_in_bytes": 26362,
+          "fixed_bit_set_memory_in_bytes": 992,
+          "max_unsafe_auto_id_timestamp": -1,
+          "file_sizes": {}
+        }
+      },
+      "nodes": {
+        "count": {
+          "total": 1,
+          "data": 1,
+          "coordinating_only": 0,
+          "master": 1,
+          "ingest": 1
+        },
+        "versions": [
+          "7.0.0-alpha1"
+        ],
+        "os": {
+          "available_processors": 4,
+          "allocated_processors": 1,
+          "names": [
+            {
+              "name": "Mac OS X",
+              "count": 1
+            }
+          ],
+          "mem": {
+            "total_in_bytes": 17179869184,
+            "free_in_bytes": 86732800,
+            "used_in_bytes": 17093136384,
+            "free_percent": 1,
+            "used_percent": 99
+          }
+        },
+        "process": {
+          "cpu": {
+            "percent": 2
+          },
+          "open_file_descriptors": {
+            "min": 178,
+            "max": 178,
+            "avg": 178
+          }
+        },
+        "jvm": {
+          "max_uptime_in_millis": 761002,
+          "versions": [
+            {
+              "vm_version": "25.121-b13",
+              "count": 1,
+              "vm_vendor": "Oracle Corporation",
+              "version": "1.8.0_121",
+              "vm_name": "Java HotSpot(TM) 64-Bit Server VM"
+            }
+          ],
+          "mem": {
+            "heap_used_in_bytes": 133041176,
+            "heap_max_in_bytes": 628555776
+          },
+          "threads": 42
+        },
+        "fs": {
+          "total_in_bytes": 499065712640,
+          "free_in_bytes": 200665792512,
+          "available_in_bytes": 200403648512
+        },
+        "plugins": [
+          {
+            "classname": "org.elasticsearch.xpack.XPackPlugin",
+            "name": "x-pack",
+            "description": "Elasticsearch Expanded Pack Plugin",
+            "version": "7.0.0-alpha1",
+            "has_native_controller": true
+          }
+        ],
+        "network_types": {
+          "transport_types": {
+            "security4": 1
+          },
+          "http_types": {
+            "security4": 1
+          }
+        }
+      }
+    },
+    "stack_stats": {
+      "xpack": {
+        "security": {
+          "available": true,
+          "enabled": true,
+          "realms": {
+            "file": {
+              "name": [
+                "default_file"
+              ],
+              "available": true,
+              "size": [
+                0
+              ],
+              "enabled": true,
+              "order": [
+                2147483647
+              ]
+            },
+            "ldap": {
+              "available": true,
+              "enabled": false
+            },
+            "native": {
+              "name": [
+                "default_native"
+              ],
+              "available": true,
+              "size": [
+                2
+              ],
+              "enabled": true,
+              "order": [
+                2147483647
+              ]
+            },
+            "active_directory": {
+              "available": true,
+              "enabled": false
+            },
+            "pki": {
+              "available": true,
+              "enabled": false
+            }
+          },
+          "roles": {
+            "native": {
+              "size": 1,
+              "dls": false,
+              "fls": false
+            },
+            "file": {
+              "size": 0,
+              "dls": false,
+              "fls": false
+            }
+          },
+          "role_mapping": {
+            "native": {
+              "size": 0,
+              "enabled": 0
+            }
+          },
+          "ssl": {
+            "http": {
+              "enabled": false
+            }
+          },
+          "audit": {
+            "outputs": [
+              "logfile"
+            ],
+            "enabled": false
+          },
+          "ipfilter": {
+            "http": false,
+            "transport": false
+          },
+          "anonymous": {
+            "enabled": false
+          }
+        },
+        "monitoring": {
+          "available": true,
+          "enabled": true,
+          "enabled_exporters": {
+            "local": 1
+          }
+        },
+        "watcher": {
+          "available": true,
+          "enabled": true,
+          "execution": {
+            "actions": {
+              "index": {
+                "total": 14,
+                "total_time_in_ms": 158
+              },
+              "_all": {
+                "total": 110,
+                "total_time_in_ms": 2245
+              },
+              "email": {
+                "total": 14,
+                "total_time_in_ms": 3
+              }
+            }
+          }
+        },
+        "graph": {
+          "available": true,
+          "enabled": true
+        },
+        "ml": {
+          "available": true,
+          "enabled": true,
+          "jobs": {
+            "_all": {
+              "count": 0,
+              "detectors": {
+                "total": 0,
+                "min": 0,
+                "avg": 0,
+                "max": 0
+              },
+              "model_size": {
+                "total": 0,
+                "min": 0,
+                "avg": 0,
+                "max": 0
+              }
+            }
+          },
+          "datafeeds": {
+            "_all": {
+              "count": 0
+            }
+          }
+        },
+        "logstash": {
+          "available": true,
+          "enabled": true
+        },
+        "ccr": {
+          "auto_follow_patterns_count": 0,
+          "available": true,
+          "follower_indices_count": 0,
+          "enabled": true
+        }
+      }
+    }
+  },
+  {
+    "cluster_uuid": "TkHOX_-1TzWwbROwQJU5IA",
+    "collectionSource": "monitoring",
+    "timestamp": "2017-08-15T22:10:52.642Z",
+    "cluster_name": "clusterone",
+    "version": "7.0.0-alpha1",
+    "license": {
+      "status": "active",
+      "type": "trial",
+      "issue_date": "2017-08-15T21:58:47.135Z",
+      "expiry_date": "2017-09-14T21:58:47.135Z",
+      "expiry_date_in_millis": 1505426327135,
+      "issue_date_in_millis": 1502834327135,
+      "issued_to": "clusterone",
+      "issuer": "elasticsearch",
+      "max_nodes": 1000,
+      "start_date_in_millis": -1,
+      "uid": "e5e99511-0928-41a3-97b0-ec77fa5e3b4c",
+      "hkey": null
+    },
+    "cluster_stats": {
+      "timestamp": 1502835052641,
+      "status": "green",
+      "indices": {
+        "count": 5,
+        "shards": {
+          "total": 26,
+          "primaries": 13,
+          "replication": 1,
+          "index": {
+            "shards": {
+              "min": 2,
+              "max": 10,
+              "avg": 5.2
+            },
+            "primaries": {
+              "min": 1,
+              "max": 5,
+              "avg": 2.6
+            },
+            "replication": {
+              "min": 1,
+              "max": 1,
+              "avg": 1
+            }
+          }
+        },
+        "docs": {
+          "count": 150,
+          "deleted": 0
+        },
+        "store": {
+          "size_in_bytes": 4838464
+        },
+        "fielddata": {
+          "memory_size_in_bytes": 0,
+          "evictions": 0
+        },
+        "query_cache": {
+          "memory_size_in_bytes": 0,
+          "total_count": 0,
+          "hit_count": 0,
+          "miss_count": 0,
+          "cache_size": 0,
+          "cache_count": 0,
+          "evictions": 0
+        },
+        "completion": {
+          "size_in_bytes": 0
+        },
+        "segments": {
+          "count": 76,
+          "memory_in_bytes": 1907922,
+          "terms_memory_in_bytes": 1595112,
+          "stored_fields_memory_in_bytes": 23744,
+          "term_vectors_memory_in_bytes": 0,
+          "norms_memory_in_bytes": 197184,
+          "points_memory_in_bytes": 3818,
+          "doc_values_memory_in_bytes": 88064,
+          "index_writer_memory_in_bytes": 7006184,
+          "version_map_memory_in_bytes": 260,
+          "fixed_bit_set_memory_in_bytes": 0,
+          "max_unsafe_auto_id_timestamp": 1502834982386,
+          "file_sizes": {}
+        }
+      },
+      "nodes": {
+        "count": {
+          "total": 2,
+          "data": 2,
+          "coordinating_only": 0,
+          "master": 2,
+          "ingest": 2
+        },
+        "versions": [
+          "7.0.0-alpha1"
+        ],
+        "os": {
+          "available_processors": 8,
+          "allocated_processors": 2,
+          "names": [
+            {
+              "name": "Mac OS X",
+              "count": 2
+            }
+          ],
+          "mem": {
+            "total_in_bytes": 34359738368,
+            "free_in_bytes": 332099584,
+            "used_in_bytes": 34027638784,
+            "free_percent": 1,
+            "used_percent": 99
+          }
+        },
+        "process": {
+          "cpu": {
+            "percent": 2
+          },
+          "open_file_descriptors": {
+            "min": 218,
+            "max": 237,
+            "avg": 227
+          }
+        },
+        "jvm": {
+          "max_uptime_in_millis": 741786,
+          "versions": [
+            {
+              "vm_version": "25.121-b13",
+              "count": 2,
+              "vm_vendor": "Oracle Corporation",
+              "version": "1.8.0_121",
+              "vm_name": "Java HotSpot(TM) 64-Bit Server VM"
+            }
+          ],
+          "mem": {
+            "heap_used_in_bytes": 465621856,
+            "heap_max_in_bytes": 1257111552
+          },
+          "threads": 92
+        },
+        "fs": {
+          "total_in_bytes": 499065712640,
+          "free_in_bytes": 200666353664,
+          "available_in_bytes": 200404209664
+        },
+        "plugins": [
+          {
+            "classname": "org.elasticsearch.xpack.XPackPlugin",
+            "name": "x-pack",
+            "description": "Elasticsearch Expanded Pack Plugin",
+            "version": "7.0.0-alpha1",
+            "has_native_controller": true
+          }
+        ],
+        "network_types": {
+          "transport_types": {
+            "security4": 2
+          },
+          "http_types": {
+            "security4": 2
+          }
+        }
+      }
+    },
+    "stack_stats": {
+      "xpack": {
+        "security": {
+          "available": true,
+          "enabled": true,
+          "realms": {
+            "file": {
+              "name": [
+                "default_file"
+              ],
+              "available": true,
+              "size": [
+                0
+              ],
+              "enabled": true,
+              "order": [
+                2147483647
+              ]
+            },
+            "ldap": {
+              "available": true,
+              "enabled": false
+            },
+            "native": {
+              "name": [
+                "default_native"
+              ],
+              "available": true,
+              "size": [
+                1
+              ],
+              "enabled": true,
+              "order": [
+                2147483647
+              ]
+            },
+            "active_directory": {
+              "available": true,
+              "enabled": false
+            },
+            "pki": {
+              "available": true,
+              "enabled": false
+            }
+          },
+          "roles": {
+            "native": {
+              "size": 1,
+              "dls": false,
+              "fls": false
+            },
+            "file": {
+              "size": 0,
+              "dls": false,
+              "fls": false
+            }
+          },
+          "role_mapping": {
+            "native": {
+              "size": 0,
+              "enabled": 0
+            }
+          },
+          "ssl": {
+            "http": {
+              "enabled": false
+            }
+          },
+          "audit": {
+            "outputs": [
+              "logfile"
+            ],
+            "enabled": false
+          },
+          "ipfilter": {
+            "http": false,
+            "transport": false
+          },
+          "anonymous": {
+            "enabled": false
+          }
+        },
+        "monitoring": {
+          "available": true,
+          "enabled": true,
+          "enabled_exporters": {
+            "http": 1
+          }
+        },
+        "watcher": {
+          "available": true,
+          "enabled": true,
+          "execution": {
+            "actions": {
+              "_all": {
+                "total": 0,
+                "total_time_in_ms": 0
+              }
+            }
+          }
+        },
+        "graph": {
+          "available": true,
+          "enabled": true,
+          "graph_workspace": {
+            "total": 0
+          }
+        },
+        "ml": {
+          "available": true,
+          "enabled": true,
+          "jobs": {
+            "_all": {
+              "count": 3,
+              "detectors": {
+                "total": 3,
+                "min": 1,
+                "avg": 1,
+                "max": 1
+              },
+              "model_size": {
+                "total": 0,
+                "min": 0,
+                "avg": 0,
+                "max": 0
+              }
+            },
+            "opened": {
+              "count": 1,
+              "detectors": {
+                "total": 1,
+                "min": 1,
+                "avg": 1,
+                "max": 1
+              },
+              "model_size": {
+                "total": 0,
+                "min": 0,
+                "avg": 0,
+                "max": 0
+              }
+            },
+            "closed": {
+              "count": 2,
+              "detectors": {
+                "total": 2,
+                "min": 1,
+                "avg": 1,
+                "max": 1
+              },
+              "model_size": {
+                "total": 0,
+                "min": 0,
+                "avg": 0,
+                "max": 0
+              }
+            }
+          },
+          "datafeeds": {
+            "_all": {
+              "count": 0
+            }
+          }
+        },
+        "logstash": {
+          "available": true,
+          "enabled": true
+        },
+        "ccr": {
+          "auto_follow_patterns_count": 0,
+          "available": true,
+          "follower_indices_count": 0,
+          "enabled": true
+        }
+      },
+      "kibana": {
+        "count": 1,
+        "versions": [
+          {
+            "version": "7.0.0-alpha1",
+            "count": 1
+          }
+        ],
+        "os": {
+          "platforms": [],
+          "platformReleases": [],
+          "distros": [],
+          "distroReleases": []
+        },
+        "dashboard": {
+          "total": 0
+        },
+        "visualization": {
+          "total": 0
+        },
+        "search": {
+          "total": 0
+        },
+        "index_pattern": {
+          "total": 0
+        },
+        "graph_workspace": {
+          "total": 0
+        },
+        "timelion_sheet": {
+          "total": 0
+        },
+        "indices": 1,
+        "plugins": {}
+      },
+      "logstash": {
+        "count": 1,
+        "versions": [
+          {
+            "version": "7.0.0-alpha1",
+            "count": 1
+          }
+        ],
+        "os": {
+          "platforms": [],
+          "platformReleases": [],
+          "distros": [],
+          "distroReleases": []
+        }
+      }
+    }
+  }
+]

From 900a8290fb9b544e958089adc03aeee34786b7f0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Felix=20St=C3=BCrmer?= <weltenwort@users.noreply.github.com>
Date: Tue, 25 Feb 2020 11:52:37 +0100
Subject: [PATCH 166/174] [Logs UI] Unskip and stabilitize log column
 configuration tests (#58392)

This attempts to make the log column configuration tests more robust to inconsistent DOM and interaction timing.

fixes #58059
---
 .../apps/infra/logs_source_configuration.ts   |  3 +-
 .../infra_source_configuration_form.ts        | 30 +++++++++++++------
 2 files changed, 22 insertions(+), 11 deletions(-)

diff --git a/x-pack/test/functional/apps/infra/logs_source_configuration.ts b/x-pack/test/functional/apps/infra/logs_source_configuration.ts
index 1dfbe3526ce40..ecad5a40ec42e 100644
--- a/x-pack/test/functional/apps/infra/logs_source_configuration.ts
+++ b/x-pack/test/functional/apps/infra/logs_source_configuration.ts
@@ -15,8 +15,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
   const pageObjects = getPageObjects(['common', 'infraLogs']);
   const retry = getService('retry');
 
-  // FLAKY: https://github.com/elastic/kibana/issues/58059
-  describe.skip('Logs Source Configuration', function() {
+  describe('Logs Source Configuration', function() {
     this.tags('smoke');
 
     before(async () => {
diff --git a/x-pack/test/functional/services/infra_source_configuration_form.ts b/x-pack/test/functional/services/infra_source_configuration_form.ts
index ab61d5232fa1c..dbae6f00f75a2 100644
--- a/x-pack/test/functional/services/infra_source_configuration_form.ts
+++ b/x-pack/test/functional/services/infra_source_configuration_form.ts
@@ -36,25 +36,37 @@ export function InfraSourceConfigurationFormProvider({ getService }: FtrProvider
       return await testSubjects.find('~addLogColumnPopover');
     },
     async addTimestampLogColumn() {
-      await (await this.getAddLogColumnButton()).click();
+      // try to open the popover
+      const popover = await retry.try(async () => {
+        await (await this.getAddLogColumnButton()).click();
+        return this.getAddLogColumnPopover();
+      });
+
+      // try to select the timestamp field
       await retry.try(async () => {
-        await (
-          await testSubjects.findDescendant(
-            '~addTimestampLogColumn',
-            await this.getAddLogColumnPopover()
-          )
-        ).click();
+        await (await testSubjects.findDescendant('~addTimestampLogColumn', popover)).click();
       });
+
+      // wait for timestamp panel to show up
+      await testSubjects.findDescendant('~systemLogColumnPanel:Timestamp', await this.getForm());
     },
     async addFieldLogColumn(fieldName: string) {
-      await (await this.getAddLogColumnButton()).click();
+      // try to open the popover
+      const popover = await retry.try(async () => {
+        await (await this.getAddLogColumnButton()).click();
+        return this.getAddLogColumnPopover();
+      });
+
+      // try to select the given field
       await retry.try(async () => {
-        const popover = await this.getAddLogColumnPopover();
         await (await testSubjects.findDescendant('~fieldSearchInput', popover)).type(fieldName);
         await (
           await testSubjects.findDescendant(`~addFieldLogColumn:${fieldName}`, popover)
         ).click();
       });
+
+      // wait for field panel to show up
+      await testSubjects.findDescendant(`~fieldLogColumnPanel:${fieldName}`, await this.getForm());
     },
     async getLogColumnPanels(): Promise<WebElementWrapper[]> {
       return await testSubjects.findAllDescendant('~logColumnPanel', await this.getForm());

From 31cc2015b73e1035acf409373890ffef7d475b60 Mon Sep 17 00:00:00 2001
From: David Roberts <dave.roberts@elastic.co>
Date: Tue, 25 Feb 2020 12:18:35 +0000
Subject: [PATCH 167/174] [ML] Use event.timezone instead of beat.timezone in
 file upload (#58447)

This is because beat.timezone was renamed to event.timezone in
elastic/beats#9458

The corresponding file structure finder change is
elastic/elasticsearch#52720
---
 .../components/import_view/importer/importer.js           | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/importer.js b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/importer.js
index 710fa49e64167..27899a58beed2 100644
--- a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/importer.js
+++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/importer.js
@@ -148,17 +148,17 @@ function populateFailures(error, failures, chunkCount) {
   }
 }
 
-// The file structure endpoint sets the timezone to be {{ beat.timezone }}
+// The file structure endpoint sets the timezone to be {{ event.timezone }}
 // as that's the variable Filebeat would send the client timezone in.
 // In this data import function the UI is effectively performing the role of Filebeat,
 // i.e. doing basic parsing, processing and conversion to JSON before forwarding to the ingest pipeline.
 // But it's not sending every single field that Filebeat would add, so the ingest pipeline
-// cannot look for a beat.timezone variable in each input record.
-// Therefore we need to replace {{ beat.timezone }} with the actual browser timezone
+// cannot look for a event.timezone variable in each input record.
+// Therefore we need to replace {{ event.timezone }} with the actual browser timezone
 function updatePipelineTimezone(ingestPipeline) {
   if (ingestPipeline !== undefined && ingestPipeline.processors && ingestPipeline.processors) {
     const dateProcessor = ingestPipeline.processors.find(
-      p => p.date !== undefined && p.date.timezone === '{{ beat.timezone }}'
+      p => p.date !== undefined && p.date.timezone === '{{ event.timezone }}'
     );
 
     if (dateProcessor) {

From 6fc6cb8b363b4a1ea7e07ce8d491874ca4fbb864 Mon Sep 17 00:00:00 2001
From: Robert Oskamp <robert.oskamp@elastic.co>
Date: Tue, 25 Feb 2020 13:22:29 +0100
Subject: [PATCH 168/174] [ML] Functional tests - adjust classification model
 memory (#58445)

This PR increases the model memory setting in the classification creation test.
---
 .../data_frame_analytics/classification_creation.ts             | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/x-pack/test/functional/apps/machine_learning/data_frame_analytics/classification_creation.ts b/x-pack/test/functional/apps/machine_learning/data_frame_analytics/classification_creation.ts
index 798a04cae3740..1bcdeef394c00 100644
--- a/x-pack/test/functional/apps/machine_learning/data_frame_analytics/classification_creation.ts
+++ b/x-pack/test/functional/apps/machine_learning/data_frame_analytics/classification_creation.ts
@@ -36,7 +36,7 @@ export default function({ getService }: FtrProviderContext) {
         },
         dependentVariable: 'y',
         trainingPercent: '20',
-        modelMemory: '105mb',
+        modelMemory: '200mb',
         createIndexPattern: true,
         expected: {
           row: {

From db276d18945246421068e480c7dd5322487cb276 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez?=
 <alejandro.fernandez@elastic.co>
Date: Tue, 25 Feb 2020 13:39:13 +0100
Subject: [PATCH 169/174] =?UTF-8?q?[Logs=20/=20Metrics=20UI]=20Remove=20pa?=
 =?UTF-8?q?th=20prefix=20from=20ViewSourceConfigur=E2=80=A6=20(#58238)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

In the NP the routes have already the app name on them. Adding it to
this button made the app name double

    /app/metrics/metrics/settings
    /app/logs/logs/settings

The button only needs to go to the settings page within the app where
it's being loaded, so we can safely drop the prefix.

Closes #58233
---
 .../public/components/source_configuration/index.ts    |  5 +----
 .../view_source_configuration_button.tsx               |  9 +--------
 .../public/pages/infrastructure/snapshot/index.tsx     | 10 ++--------
 .../pages/logs/stream/page_no_indices_content.tsx      | 10 ++--------
 .../public/pages/metrics/components/invalid_node.tsx   | 10 ++--------
 5 files changed, 8 insertions(+), 36 deletions(-)

diff --git a/x-pack/plugins/infra/public/components/source_configuration/index.ts b/x-pack/plugins/infra/public/components/source_configuration/index.ts
index 4879a53ca329d..98825567cc204 100644
--- a/x-pack/plugins/infra/public/components/source_configuration/index.ts
+++ b/x-pack/plugins/infra/public/components/source_configuration/index.ts
@@ -5,7 +5,4 @@
  */
 
 export { SourceConfigurationSettings } from './source_configuration_settings';
-export {
-  ViewSourceConfigurationButton,
-  ViewSourceConfigurationButtonHrefBase,
-} from './view_source_configuration_button';
+export { ViewSourceConfigurationButton } from './view_source_configuration_button';
diff --git a/x-pack/plugins/infra/public/components/source_configuration/view_source_configuration_button.tsx b/x-pack/plugins/infra/public/components/source_configuration/view_source_configuration_button.tsx
index 9b584b2ef3bd0..9c3a40fb7ecf0 100644
--- a/x-pack/plugins/infra/public/components/source_configuration/view_source_configuration_button.tsx
+++ b/x-pack/plugins/infra/public/components/source_configuration/view_source_configuration_button.tsx
@@ -8,23 +8,16 @@ import { EuiButton } from '@elastic/eui';
 import React from 'react';
 import { Route } from 'react-router-dom';
 
-export enum ViewSourceConfigurationButtonHrefBase {
-  infrastructure = 'infrastructure',
-  logs = 'logs',
-}
-
 interface ViewSourceConfigurationButtonProps {
   'data-test-subj'?: string;
-  hrefBase: ViewSourceConfigurationButtonHrefBase;
   children: React.ReactNode;
 }
 
 export const ViewSourceConfigurationButton = ({
   'data-test-subj': dataTestSubj,
-  hrefBase,
   children,
 }: ViewSourceConfigurationButtonProps) => {
-  const href = `/${hrefBase}/settings`;
+  const href = '/settings';
 
   return (
     <Route
diff --git a/x-pack/plugins/infra/public/pages/infrastructure/snapshot/index.tsx b/x-pack/plugins/infra/public/pages/infrastructure/snapshot/index.tsx
index 7f3be965af8db..ba0e9b436e4e7 100644
--- a/x-pack/plugins/infra/public/pages/infrastructure/snapshot/index.tsx
+++ b/x-pack/plugins/infra/public/pages/infrastructure/snapshot/index.tsx
@@ -18,10 +18,7 @@ import { ColumnarPage } from '../../../components/page';
 
 import { SourceErrorPage } from '../../../components/source_error_page';
 import { SourceLoadingPage } from '../../../components/source_loading_page';
-import {
-  ViewSourceConfigurationButton,
-  ViewSourceConfigurationButtonHrefBase,
-} from '../../../components/source_configuration';
+import { ViewSourceConfigurationButton } from '../../../components/source_configuration';
 import { Source } from '../../../containers/source';
 import { WithWaffleFilterUrlState } from '../../../containers/waffle/with_waffle_filters';
 import { WithWaffleOptionsUrlState } from '../../../containers/waffle/with_waffle_options';
@@ -92,10 +89,7 @@ export const SnapshotPage = () => {
               </EuiFlexItem>
               {uiCapabilities?.infrastructure?.configureSource ? (
                 <EuiFlexItem>
-                  <ViewSourceConfigurationButton
-                    data-test-subj="configureSourceButton"
-                    hrefBase={ViewSourceConfigurationButtonHrefBase.infrastructure}
-                  >
+                  <ViewSourceConfigurationButton data-test-subj="configureSourceButton">
                     {i18n.translate('xpack.infra.configureSourceActionLabel', {
                       defaultMessage: 'Change source configuration',
                     })}
diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_no_indices_content.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_no_indices_content.tsx
index 1294007240027..739bad5689a96 100644
--- a/x-pack/plugins/infra/public/pages/logs/stream/page_no_indices_content.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/stream/page_no_indices_content.tsx
@@ -10,10 +10,7 @@ import { identity } from 'fp-ts/lib/function';
 import React from 'react';
 
 import { NoIndices } from '../../../components/empty_states/no_indices';
-import {
-  ViewSourceConfigurationButton,
-  ViewSourceConfigurationButtonHrefBase,
-} from '../../../components/source_configuration';
+import { ViewSourceConfigurationButton } from '../../../components/source_configuration';
 import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
 
 export const LogsPageNoIndicesContent = () => {
@@ -49,10 +46,7 @@ export const LogsPageNoIndicesContent = () => {
           </EuiFlexItem>
           {canConfigureSource ? (
             <EuiFlexItem>
-              <ViewSourceConfigurationButton
-                data-test-subj="configureSourceButton"
-                hrefBase={ViewSourceConfigurationButtonHrefBase.logs}
-              >
+              <ViewSourceConfigurationButton data-test-subj="configureSourceButton">
                 {i18n.translate('xpack.infra.configureSourceActionLabel', {
                   defaultMessage: 'Change source configuration',
                 })}
diff --git a/x-pack/plugins/infra/public/pages/metrics/components/invalid_node.tsx b/x-pack/plugins/infra/public/pages/metrics/components/invalid_node.tsx
index fde3b61de50b5..43f684cd5a585 100644
--- a/x-pack/plugins/infra/public/pages/metrics/components/invalid_node.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/components/invalid_node.tsx
@@ -10,10 +10,7 @@ import { identity } from 'fp-ts/lib/function';
 import React from 'react';
 
 import { euiStyled } from '../../../../../observability/public';
-import {
-  ViewSourceConfigurationButton,
-  ViewSourceConfigurationButtonHrefBase,
-} from '../../../components/source_configuration';
+import { ViewSourceConfigurationButton } from '../../../components/source_configuration';
 import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
 
 interface InvalidNodeErrorProps {
@@ -59,10 +56,7 @@ export const InvalidNodeError: React.FunctionComponent<InvalidNodeErrorProps> =
             </EuiButton>
           </EuiFlexItem>
           <EuiFlexItem>
-            <ViewSourceConfigurationButton
-              data-test-subj="configureSourceButton"
-              hrefBase={ViewSourceConfigurationButtonHrefBase.infrastructure}
-            >
+            <ViewSourceConfigurationButton data-test-subj="configureSourceButton">
               <FormattedMessage
                 id="xpack.infra.configureSourceActionLabel"
                 defaultMessage="Change source configuration"

From 71d6c22114b25110ef0740ca7f883ff4569ed683 Mon Sep 17 00:00:00 2001
From: Andrew Cholakian <andrew@andrewvc.com>
Date: Tue, 25 Feb 2020 07:18:04 -0600
Subject: [PATCH 170/174] [Uptime] Improve refresh handling when generating
 test data (#58285)

When generating test data we refresh excessively, this can fill up the
ES queues and break the tests if we run massive tests. I originally ran
into this with https://github.com/elastic/kibana/pull/58078/ which I
closed due to finding a better approach.

While none of our current tests have the scale to expose this problem,
we certainly will add tests that do later, so we should keep this
change.

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 .../uptime/graphql/helpers/make_checks.ts     | 54 +++++++++++++------
 1 file changed, 38 insertions(+), 16 deletions(-)

diff --git a/x-pack/test/api_integration/apis/uptime/graphql/helpers/make_checks.ts b/x-pack/test/api_integration/apis/uptime/graphql/helpers/make_checks.ts
index f89905f0da04f..4d3167b14b86f 100644
--- a/x-pack/test/api_integration/apis/uptime/graphql/helpers/make_checks.ts
+++ b/x-pack/test/api_integration/apis/uptime/graphql/helpers/make_checks.ts
@@ -13,7 +13,8 @@ export const makePing = async (
   es: any,
   monitorId: string,
   fields: { [key: string]: any },
-  mogrify: (doc: any) => any
+  mogrify: (doc: any) => any,
+  refresh: boolean = true
 ) => {
   const baseDoc = {
     tcp: {
@@ -103,7 +104,7 @@ export const makePing = async (
 
   await es.index({
     index: INDEX_NAME,
-    refresh: true,
+    refresh,
     body: doc,
   });
 
@@ -115,7 +116,8 @@ export const makeCheck = async (
   monitorId: string,
   numIps: number,
   fields: { [key: string]: any },
-  mogrify: (doc: any) => any
+  mogrify: (doc: any) => any,
+  refresh: boolean = true
 ) => {
   const cgFields = {
     monitor: {
@@ -137,11 +139,16 @@ export const makeCheck = async (
     if (i === numIps - 1) {
       pingFields.summary = summary;
     }
-    const doc = await makePing(es, monitorId, pingFields, mogrify);
+    const doc = await makePing(es, monitorId, pingFields, mogrify, false);
     docs.push(doc);
     // @ts-ignore
     summary[doc.monitor.status]++;
   }
+
+  if (refresh) {
+    es.indices.refresh();
+  }
+
   return docs;
 };
 
@@ -152,7 +159,8 @@ export const makeChecks = async (
   numIps: number,
   every: number, // number of millis between checks
   fields: { [key: string]: any } = {},
-  mogrify: (doc: any) => any = d => d
+  mogrify: (doc: any) => any = d => d,
+  refresh: boolean = true
 ) => {
   const checks = [];
   const oldestTime = new Date().getTime() - numChecks * every;
@@ -169,7 +177,11 @@ export const makeChecks = async (
         },
       },
     });
-    checks.push(await makeCheck(es, monitorId, numIps, fields, mogrify));
+    checks.push(await makeCheck(es, monitorId, numIps, fields, mogrify, false));
+  }
+
+  if (refresh) {
+    es.indices.refresh();
   }
 
   return checks;
@@ -183,19 +195,29 @@ export const makeChecksWithStatus = async (
   every: number,
   fields: { [key: string]: any } = {},
   status: 'up' | 'down',
-  mogrify: (doc: any) => any = d => d
+  mogrify: (doc: any) => any = d => d,
+  refresh: boolean = true
 ) => {
   const oppositeStatus = status === 'up' ? 'down' : 'up';
 
-  return await makeChecks(es, monitorId, numChecks, numIps, every, fields, d => {
-    d.monitor.status = status;
-    if (d.summary) {
-      d.summary[status] += d.summary[oppositeStatus];
-      d.summary[oppositeStatus] = 0;
-    }
-
-    return mogrify(d);
-  });
+  return await makeChecks(
+    es,
+    monitorId,
+    numChecks,
+    numIps,
+    every,
+    fields,
+    d => {
+      d.monitor.status = status;
+      if (d.summary) {
+        d.summary[status] += d.summary[oppositeStatus];
+        d.summary[oppositeStatus] = 0;
+      }
+
+      return mogrify(d);
+    },
+    refresh
+  );
 };
 
 // Helper for processing a list of checks to find the time picker bounds.

From b756cc3915fe62d405f006fd32bf8678f4c5e402 Mon Sep 17 00:00:00 2001
From: James Gowdy <jgowdy@elastic.co>
Date: Tue, 25 Feb 2020 13:39:50 +0000
Subject: [PATCH 171/174] [ML] Adding filebeat config to file dataviz (#58152)

* [ML] Adding filebeat config to file dataviz

* adding extra help text

* removing commented out code

* adding extra blank line to processors section

* cleaning up types

* moving hosts line out of function

* typo in config text

* updating config based on review

* tiny refactor

* translating paths text
---
 .../ml/common/types/file_datavisualizer.ts    |  31 +++
 .../plugins/ml/public/application/app.tsx     |   4 +
 .../contexts/kibana/kibana_context.ts         |   2 +
 .../filebeat_config_flyout/filebeat_config.ts |  71 +++++++
 .../filebeat_config_flyout.tsx                | 162 +++++++++++++++
 .../filebeat_config_flyout/index.ts           |   7 +
 .../components/import_view/import_view.js     |  24 +++
 .../import_view/importer/message_importer.js  |   5 +
 .../results_links/{index.js => index.ts}      |   0
 .../components/results_links/results_links.js | 182 -----------------
 .../results_links/results_links.tsx           | 190 ++++++++++++++++++
 .../application/util/dependency_cache.ts      |  10 +
 x-pack/legacy/plugins/ml/public/legacy.ts     |   4 +-
 x-pack/legacy/plugins/ml/public/plugin.ts     |   3 +-
 .../file_data_visualizer.ts                   |  27 +--
 15 files changed, 513 insertions(+), 209 deletions(-)
 create mode 100644 x-pack/legacy/plugins/ml/common/types/file_datavisualizer.ts
 create mode 100644 x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/filebeat_config.ts
 create mode 100644 x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/filebeat_config_flyout.tsx
 create mode 100644 x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/index.ts
 rename x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/results_links/{index.js => index.ts} (100%)
 delete mode 100644 x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/results_links/results_links.js
 create mode 100644 x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/results_links/results_links.tsx

diff --git a/x-pack/legacy/plugins/ml/common/types/file_datavisualizer.ts b/x-pack/legacy/plugins/ml/common/types/file_datavisualizer.ts
new file mode 100644
index 0000000000000..bc03f82673a1f
--- /dev/null
+++ b/x-pack/legacy/plugins/ml/common/types/file_datavisualizer.ts
@@ -0,0 +1,31 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export interface FindFileStructureResponse {
+  charset: string;
+  has_header_row: boolean;
+  has_byte_order_marker: boolean;
+  format: string;
+  field_stats: {
+    [fieldName: string]: {
+      count: number;
+      cardinality: number;
+      top_hits: Array<{ count: number; value: any }>;
+    };
+  };
+  sample_start: string;
+  num_messages_analyzed: number;
+  mappings: {
+    [fieldName: string]: {
+      type: string;
+    };
+  };
+  quote: string;
+  delimiter: string;
+  need_client_timezone: boolean;
+  num_lines_analyzed: number;
+  column_names: string[];
+}
diff --git a/x-pack/legacy/plugins/ml/public/application/app.tsx b/x-pack/legacy/plugins/ml/public/application/app.tsx
index 24cbfbfb346dd..add27193deb77 100644
--- a/x-pack/legacy/plugins/ml/public/application/app.tsx
+++ b/x-pack/legacy/plugins/ml/public/application/app.tsx
@@ -12,6 +12,7 @@ import 'ace';
 import { AppMountParameters, CoreStart } from 'kibana/public';
 
 import { DataPublicPluginStart } from 'src/plugins/data/public';
+import { SecurityPluginSetup } from '../../../../../plugins/security/public';
 
 import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
 import { setDependencyCache, clearCache } from './util/dependency_cache';
@@ -20,6 +21,7 @@ import { MlRouter } from './routing';
 
 export interface MlDependencies extends AppMountParameters {
   data: DataPublicPluginStart;
+  security: SecurityPluginSetup;
   __LEGACY: {
     XSRF: string;
     APP_URL: string;
@@ -49,6 +51,7 @@ const App: FC<AppProps> = ({ coreStart, deps }) => {
     APP_URL: deps.__LEGACY.APP_URL,
     application: coreStart.application,
     http: coreStart.http,
+    security: deps.security,
   });
   deps.onAppLeave(actions => {
     clearCache();
@@ -64,6 +67,7 @@ const App: FC<AppProps> = ({ coreStart, deps }) => {
   const services = {
     appName: 'ML',
     data: deps.data,
+    security: deps.security,
     ...coreStart,
   };
 
diff --git a/x-pack/legacy/plugins/ml/public/application/contexts/kibana/kibana_context.ts b/x-pack/legacy/plugins/ml/public/application/contexts/kibana/kibana_context.ts
index aaf539322809b..5fcd7c5473d3b 100644
--- a/x-pack/legacy/plugins/ml/public/application/contexts/kibana/kibana_context.ts
+++ b/x-pack/legacy/plugins/ml/public/application/contexts/kibana/kibana_context.ts
@@ -10,9 +10,11 @@ import {
   useKibana,
   KibanaReactContextValue,
 } from '../../../../../../../../src/plugins/kibana_react/public';
+import { SecurityPluginSetup } from '../../../../../../../plugins/security/public';
 
 interface StartPlugins {
   data: DataPublicPluginStart;
+  security: SecurityPluginSetup;
 }
 export type StartServices = CoreStart & StartPlugins;
 // eslint-disable-next-line react-hooks/rules-of-hooks
diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/filebeat_config.ts b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/filebeat_config.ts
new file mode 100644
index 0000000000000..3344cdf991e6b
--- /dev/null
+++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/filebeat_config.ts
@@ -0,0 +1,71 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { i18n } from '@kbn/i18n';
+import { FindFileStructureResponse } from '../../../../../../common/types/file_datavisualizer';
+
+export function createFilebeatConfig(
+  index: string,
+  results: FindFileStructureResponse,
+  ingestPipelineId: string,
+  username: string | null
+) {
+  return [
+    'filebeat.inputs:',
+    '- type: log',
+    ...getPaths(),
+    ...getEncoding(results),
+    ...getExcludeLines(results),
+    ...getMultiline(results),
+    '',
+    ...getProcessors(results),
+    'output.elasticsearch:',
+    '  hosts: ["<es_url>"]',
+    ...getUserDetails(username),
+    `  index: "${index}"`,
+    `  pipeline: "${ingestPipelineId}"`,
+    '',
+    'setup:',
+    '  template.enabled: false',
+    '  ilm.enabled: false',
+  ].join('\n');
+}
+
+function getPaths() {
+  const txt = i18n.translate('xpack.ml.fileDatavisualizer.fileBeatConfig.paths', {
+    defaultMessage: 'add path to your files here',
+  });
+  return ['  paths:', `  - '<${txt}>'`];
+}
+
+function getEncoding(results: any) {
+  return results.charset !== 'UTF-8' ? [`  encoding: ${results.charset}`] : [];
+}
+
+function getExcludeLines(results: any) {
+  return results.exclude_lines_pattern !== undefined
+    ? [`  exclude_lines: ['${results.exclude_lines_pattern.replace(/'/g, "''")}']`]
+    : [];
+}
+
+function getMultiline(results: any) {
+  return results.multiline_start_pattern !== undefined
+    ? [
+        '  multiline:',
+        `    pattern: '${results.multiline_start_pattern.replace(/'/g, "''")}'`,
+        '    match: after',
+        '    negate: true',
+      ]
+    : [];
+}
+
+function getProcessors(results: any) {
+  return results.need_client_timezone === true ? ['processors:', '- add_locale: ~', ''] : [];
+}
+
+function getUserDetails(username: string | null) {
+  return username !== null ? [`  username: "${username}"`, '  password: "<password>"'] : [];
+}
diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/filebeat_config_flyout.tsx b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/filebeat_config_flyout.tsx
new file mode 100644
index 0000000000000..30fc74acbabf4
--- /dev/null
+++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/filebeat_config_flyout.tsx
@@ -0,0 +1,162 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { FC, useState, useEffect } from 'react';
+import { FormattedMessage } from '@kbn/i18n/react';
+import {
+  EuiFlyout,
+  EuiFlyoutFooter,
+  EuiFlexGroup,
+  EuiFlexItem,
+  EuiButton,
+  EuiButtonEmpty,
+  EuiTitle,
+  EuiFlyoutBody,
+  EuiSpacer,
+  EuiCodeBlock,
+  EuiCode,
+  EuiCopy,
+} from '@elastic/eui';
+import { createFilebeatConfig } from './filebeat_config';
+import { useMlKibana } from '../../../../contexts/kibana';
+import { FindFileStructureResponse } from '../../../../../../common/types/file_datavisualizer';
+
+export enum EDITOR_MODE {
+  HIDDEN,
+  READONLY,
+  EDITABLE,
+}
+interface Props {
+  index: string;
+  results: FindFileStructureResponse;
+  indexPatternId: string;
+  ingestPipelineId: string;
+  closeFlyout(): void;
+}
+export const FilebeatConfigFlyout: FC<Props> = ({
+  index,
+  results,
+  indexPatternId,
+  ingestPipelineId,
+  closeFlyout,
+}) => {
+  const [fileBeatConfig, setFileBeatConfig] = useState('');
+  const [username, setUsername] = useState<string | null>(null);
+  const {
+    services: { security },
+  } = useMlKibana();
+
+  useEffect(() => {
+    security.authc.getCurrentUser().then(user => {
+      setUsername(user.username === undefined ? null : user.username);
+    });
+  }, []);
+
+  useEffect(() => {
+    const config = createFilebeatConfig(index, results, ingestPipelineId, username);
+    setFileBeatConfig(config);
+  }, [username]);
+
+  return (
+    <EuiFlyout onClose={closeFlyout} hideCloseButton size={'m'}>
+      <EuiFlyoutBody>
+        <EuiFlexGroup>
+          <Contents value={fileBeatConfig} username={username} index={index} />
+        </EuiFlexGroup>
+      </EuiFlyoutBody>
+      <EuiFlyoutFooter>
+        <EuiFlexGroup justifyContent="spaceBetween">
+          <EuiFlexItem grow={false}>
+            <EuiButtonEmpty iconType="cross" onClick={closeFlyout} flush="left">
+              <FormattedMessage
+                id="xpack.ml.fileDatavisualizer.fileBeatConfigFlyout.closeButton"
+                defaultMessage="Close"
+              />
+            </EuiButtonEmpty>
+          </EuiFlexItem>
+          <EuiFlexItem grow={false}>
+            <EuiCopy textToCopy={fileBeatConfig}>
+              {copy => (
+                <EuiButton onClick={copy}>
+                  <FormattedMessage
+                    id="xpack.ml.fileDatavisualizer.fileBeatConfigFlyout.copyButton"
+                    defaultMessage="Copy to clipboard"
+                  />
+                </EuiButton>
+              )}
+            </EuiCopy>
+          </EuiFlexItem>
+        </EuiFlexGroup>
+      </EuiFlyoutFooter>
+    </EuiFlyout>
+  );
+};
+
+const Contents: FC<{
+  value: string;
+  index: string;
+  username: string | null;
+}> = ({ value, index, username }) => {
+  return (
+    <EuiFlexItem>
+      <EuiTitle size="s">
+        <h5>
+          <FormattedMessage
+            id="xpack.ml.fileDatavisualizer.resultsLinks.fileBeatConfigTitle"
+            defaultMessage="Filebeat configuration"
+          />
+        </h5>
+      </EuiTitle>
+      <EuiSpacer size="s" />
+      <p>
+        <FormattedMessage
+          id="xpack.ml.fileDatavisualizer.resultsLinks.fileBeatConfigTopText1"
+          defaultMessage="Additional data can be uploaded to the {index} index using Filebeat."
+          values={{ index: <EuiCode>{index}</EuiCode> }}
+        />
+      </p>
+      <p>
+        <FormattedMessage
+          id="xpack.ml.fileDatavisualizer.resultsLinks.fileBeatConfigTopText2"
+          defaultMessage="Modify {filebeatYml} to set the connection information:"
+          values={{ filebeatYml: <EuiCode>filebeat.yml</EuiCode> }}
+        />
+      </p>
+
+      <EuiSpacer size="s" />
+
+      <EuiCodeBlock language="bash">{value}</EuiCodeBlock>
+
+      <EuiSpacer size="s" />
+      <p>
+        {username === null ? (
+          <FormattedMessage
+            id="xpack.ml.fileDatavisualizer.resultsLinks.fileBeatConfigBottomTextNoUsername"
+            defaultMessage="Where {esUrl} is the URL of Elasticsearch."
+            values={{
+              esUrl: <EuiCode>{'<es_url>'}</EuiCode>,
+            }}
+          />
+        ) : (
+          <FormattedMessage
+            id="xpack.ml.fileDatavisualizer.resultsLinks.fileBeatConfigBottomText"
+            defaultMessage="Where {password} is the password of the {user} user, {esUrl} is the URL of Elasticsearch."
+            values={{
+              user: <EuiCode>{username}</EuiCode>,
+              password: <EuiCode>{'<password>'}</EuiCode>,
+              esUrl: <EuiCode>{'<es_url>'}</EuiCode>,
+            }}
+          />
+        )}
+      </p>
+    </EuiFlexItem>
+  );
+};
diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/index.ts b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/index.ts
new file mode 100644
index 0000000000000..9286b92c2ab97
--- /dev/null
+++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/filebeat_config_flyout/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 { FilebeatConfigFlyout } from './filebeat_config_flyout';
diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js
index beb5918e277ae..bdfc27099a185 100644
--- a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js
+++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js
@@ -20,6 +20,7 @@ import {
 import { i18n } from '@kbn/i18n';
 import { importerFactory } from './importer';
 import { ResultsLinks } from '../results_links';
+import { FilebeatConfigFlyout } from '../filebeat_config_flyout';
 import { ImportProgress, IMPORT_STATUS } from '../import_progress';
 import { ImportErrors } from '../import_errors';
 import { ImportSummary } from '../import_summary';
@@ -64,6 +65,7 @@ const DEFAULT_STATE = {
   indexNameError: '',
   indexPatternNameError: '',
   timeFieldName: undefined,
+  isFilebeatFlyoutVisible: false,
 };
 
 export class ImportView extends Component {
@@ -384,6 +386,16 @@ export class ImportView extends Component {
     });
   };
 
+  showFilebeatFlyout = () => {
+    this.setState({ isFilebeatFlyoutVisible: true });
+    this.props.hideBottomBar();
+  };
+
+  closeFilebeatFlyout = () => {
+    this.setState({ isFilebeatFlyoutVisible: false });
+    this.props.showBottomBar();
+  };
+
   async loadIndexNames() {
     const indices = await ml.getIndices();
     const indexNames = indices.map(i => i.name);
@@ -424,6 +436,7 @@ export class ImportView extends Component {
       indexNameError,
       indexPatternNameError,
       timeFieldName,
+      isFilebeatFlyoutVisible,
     } = this.state;
 
     const createPipeline = pipelineString !== '';
@@ -549,7 +562,18 @@ export class ImportView extends Component {
                       indexPatternId={indexPatternId}
                       timeFieldName={timeFieldName}
                       createIndexPattern={createIndexPattern}
+                      showFilebeatFlyout={this.showFilebeatFlyout}
                     />
+
+                    {isFilebeatFlyoutVisible && (
+                      <FilebeatConfigFlyout
+                        index={index}
+                        results={this.props.results}
+                        indexPatternId={indexPatternId}
+                        ingestPipelineId={ingestPipelineId}
+                        closeFlyout={this.closeFilebeatFlyout}
+                      />
+                    )}
                   </React.Fragment>
                 )}
               </EuiPanel>
diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/message_importer.js b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/message_importer.js
index 840248817945a..c2d3ac69f0963 100644
--- a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/message_importer.js
+++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/message_importer.js
@@ -77,6 +77,11 @@ export class MessageImporter extends Importer {
       if (this.multilineStartRegex === null || line.match(this.multilineStartRegex) !== null) {
         this.addMessage(data, message);
         message = '';
+      } else if (data.length === 0) {
+        // discard everything before the first line that is considered the first line of a message
+        // as it could be left over partial data from a spilt or rolled over log,
+        // or could be a blank line after the header in a csv file
+        return '';
       } else {
         message += '\n';
       }
diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/results_links/index.js b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/results_links/index.ts
similarity index 100%
rename from x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/results_links/index.js
rename to x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/results_links/index.ts
diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/results_links/results_links.js b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/results_links/results_links.js
deleted file mode 100644
index aaebca2f58963..0000000000000
--- a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/results_links/results_links.js
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { FormattedMessage } from '@kbn/i18n/react';
-import React, { Component } from 'react';
-
-import { EuiFlexGroup, EuiFlexItem, EuiCard, EuiIcon } from '@elastic/eui';
-
-import moment from 'moment';
-
-import { ml } from '../../../../services/ml_api_service';
-import { isFullLicense } from '../../../../license/check_license';
-import { checkPermission } from '../../../../privilege/check_privilege';
-import { mlNodesAvailable } from '../../../../ml_nodes_check/check_ml_nodes';
-import { withKibana } from '../../../../../../../../../../src/plugins/kibana_react/public';
-
-const RECHECK_DELAY_MS = 3000;
-
-class ResultsLinksUI extends Component {
-  constructor(props) {
-    super(props);
-
-    this.state = {
-      from: 'now-30m',
-      to: 'now',
-    };
-
-    this.recheckTimeout = null;
-    this.showCreateJobLink = true;
-  }
-
-  componentDidMount() {
-    this.showCreateJobLink = checkPermission('canCreateJob') && mlNodesAvailable();
-    // if this data has a time field,
-    // find the start and end times
-    if (this.props.timeFieldName !== undefined) {
-      this.updateTimeValues();
-    }
-  }
-
-  componentWillUnmount() {
-    clearTimeout(this.recheckTimeout);
-  }
-
-  async updateTimeValues(recheck = true) {
-    const { index, timeFieldName } = this.props;
-
-    const { from, to } = await getFullTimeRange(index, timeFieldName);
-    this.setState({
-      from: from === null ? this.state.from : from,
-      to: to === null ? this.state.to : to,
-    });
-
-    // these links may have been drawn too quickly for the index to be ready
-    // to give us the correct start and end times.
-    // especially if the data was small.
-    // so if the start and end were null, try again in 3s
-    // the timeout is cleared when this component unmounts. just in case the user
-    // resets the form or navigates away within 3s
-    if (recheck && (from === null || to === null)) {
-      this.recheckTimeout = setTimeout(() => {
-        this.updateTimeValues(false);
-      }, RECHECK_DELAY_MS);
-    }
-  }
-
-  render() {
-    const { index, indexPatternId, timeFieldName, createIndexPattern } = this.props;
-
-    const { from, to } = this.state;
-
-    const _g =
-      this.props.timeFieldName !== undefined
-        ? `&_g=(time:(from:'${from}',mode:quick,to:'${to}'))`
-        : '';
-
-    const { basePath } = this.props.kibana.services.http;
-    return (
-      <EuiFlexGroup gutterSize="l">
-        {createIndexPattern && (
-          <EuiFlexItem>
-            <EuiCard
-              icon={<EuiIcon size="xxl" type={`discoverApp`} />}
-              title={
-                <FormattedMessage
-                  id="xpack.ml.fileDatavisualizer.resultsLinks.viewIndexInDiscoverTitle"
-                  defaultMessage="View index in Discover"
-                />
-              }
-              description=""
-              href={`${basePath.get()}/app/kibana#/discover?&_a=(index:'${indexPatternId}')${_g}`}
-            />
-          </EuiFlexItem>
-        )}
-
-        {isFullLicense() === true &&
-          timeFieldName !== undefined &&
-          this.showCreateJobLink &&
-          createIndexPattern && (
-            <EuiFlexItem>
-              <EuiCard
-                icon={<EuiIcon size="xxl" type={`machineLearningApp`} />}
-                title={
-                  <FormattedMessage
-                    id="xpack.ml.fileDatavisualizer.resultsLinks.createNewMLJobTitle"
-                    defaultMessage="Create new ML job"
-                  />
-                }
-                description=""
-                href={`#/jobs/new_job/step/job_type?index=${indexPatternId}${_g}`}
-              />
-            </EuiFlexItem>
-          )}
-
-        {createIndexPattern && (
-          <EuiFlexItem>
-            <EuiCard
-              icon={<EuiIcon size="xxl" type={`dataVisualizer`} />}
-              title={
-                <FormattedMessage
-                  id="xpack.ml.fileDatavisualizer.resultsLinks.openInDataVisualizerTitle"
-                  defaultMessage="Open in Data Visualizer"
-                />
-              }
-              description=""
-              href={`#/jobs/new_job/datavisualizer?index=${indexPatternId}${_g}`}
-            />
-          </EuiFlexItem>
-        )}
-
-        <EuiFlexItem>
-          <EuiCard
-            icon={<EuiIcon size="xxl" type={`managementApp`} />}
-            title={
-              <FormattedMessage
-                id="xpack.ml.fileDatavisualizer.resultsLinks.indexManagementTitle"
-                defaultMessage="Index Management"
-              />
-            }
-            description=""
-            href={`${basePath.get()}/app/kibana#/management/elasticsearch/index_management/indices/filter/${index}`}
-          />
-        </EuiFlexItem>
-
-        <EuiFlexItem>
-          <EuiCard
-            icon={<EuiIcon size="xxl" type={`managementApp`} />}
-            title={
-              <FormattedMessage
-                id="xpack.ml.fileDatavisualizer.resultsLinks.indexPatternManagementTitle"
-                defaultMessage="Index Pattern Management"
-              />
-            }
-            description=""
-            href={`${basePath.get()}/app/kibana#/management/kibana/index_patterns/${
-              createIndexPattern ? indexPatternId : ''
-            }`}
-          />
-        </EuiFlexItem>
-      </EuiFlexGroup>
-    );
-  }
-}
-
-export const ResultsLinks = withKibana(ResultsLinksUI);
-
-async function getFullTimeRange(index, timeFieldName) {
-  const query = { bool: { must: [{ query_string: { analyze_wildcard: true, query: '*' } }] } };
-  const resp = await ml.getTimeFieldRange({
-    index,
-    timeFieldName,
-    query,
-  });
-
-  return {
-    from: moment(resp.start.epoch).toISOString(),
-    to: moment(resp.end.epoch).toISOString(),
-  };
-}
diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/results_links/results_links.tsx b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/results_links/results_links.tsx
new file mode 100644
index 0000000000000..debadba19051b
--- /dev/null
+++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/results_links/results_links.tsx
@@ -0,0 +1,190 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { FC, useState, useEffect } from 'react';
+import moment from 'moment';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { EuiFlexGroup, EuiFlexItem, EuiCard, EuiIcon } from '@elastic/eui';
+import { ml } from '../../../../services/ml_api_service';
+import { isFullLicense } from '../../../../license/check_license';
+import { checkPermission } from '../../../../privilege/check_privilege';
+import { mlNodesAvailable } from '../../../../ml_nodes_check/check_ml_nodes';
+import { useMlKibana } from '../../../../contexts/kibana';
+
+const RECHECK_DELAY_MS = 3000;
+
+interface Props {
+  index: string;
+  indexPatternId: string;
+  timeFieldName?: string;
+  createIndexPattern: boolean;
+  showFilebeatFlyout(): void;
+}
+
+export const ResultsLinks: FC<Props> = ({
+  index,
+  indexPatternId,
+  timeFieldName,
+  createIndexPattern,
+  showFilebeatFlyout,
+}) => {
+  const [duration, setDuration] = useState({
+    from: 'now-30m',
+    to: 'now',
+  });
+  const [showCreateJobLink, setShowCreateJobLink] = useState(false);
+  const [globalStateString, setGlobalStateString] = useState('');
+  const {
+    services: {
+      http: { basePath },
+    },
+  } = useMlKibana();
+
+  useEffect(() => {
+    setShowCreateJobLink(checkPermission('canCreateJob') && mlNodesAvailable());
+    updateTimeValues();
+  }, []);
+
+  useEffect(() => {
+    const _g =
+      timeFieldName !== undefined
+        ? `&_g=(time:(from:'${duration.from}',mode:quick,to:'${duration.to}'))`
+        : '';
+    setGlobalStateString(_g);
+  }, [duration]);
+
+  async function updateTimeValues(recheck = true) {
+    if (timeFieldName !== undefined) {
+      const { from, to } = await getFullTimeRange(index, timeFieldName);
+      setDuration({
+        from: from === null ? duration.from : from,
+        to: to === null ? duration.to : to,
+      });
+
+      // these links may have been drawn too quickly for the index to be ready
+      // to give us the correct start and end times.
+      // especially if the data was small.
+      // so if the start and end were null, try again in 3s
+      if (recheck && (from === null || to === null)) {
+        setTimeout(() => {
+          updateTimeValues(false);
+        }, RECHECK_DELAY_MS);
+      }
+    }
+  }
+
+  return (
+    <EuiFlexGroup gutterSize="l">
+      {createIndexPattern && (
+        <EuiFlexItem>
+          <EuiCard
+            icon={<EuiIcon size="xxl" type={`discoverApp`} />}
+            title={
+              <FormattedMessage
+                id="xpack.ml.fileDatavisualizer.resultsLinks.viewIndexInDiscoverTitle"
+                defaultMessage="View index in Discover"
+              />
+            }
+            description=""
+            href={`${basePath.get()}/app/kibana#/discover?&_a=(index:'${indexPatternId}')${globalStateString}`}
+          />
+        </EuiFlexItem>
+      )}
+
+      {isFullLicense() === true &&
+        timeFieldName !== undefined &&
+        showCreateJobLink &&
+        createIndexPattern && (
+          <EuiFlexItem>
+            <EuiCard
+              icon={<EuiIcon size="xxl" type={`machineLearningApp`} />}
+              title={
+                <FormattedMessage
+                  id="xpack.ml.fileDatavisualizer.resultsLinks.createNewMLJobTitle"
+                  defaultMessage="Create new ML job"
+                />
+              }
+              description=""
+              href={`#/jobs/new_job/step/job_type?index=${indexPatternId}${globalStateString}`}
+            />
+          </EuiFlexItem>
+        )}
+
+      {createIndexPattern && (
+        <EuiFlexItem>
+          <EuiCard
+            icon={<EuiIcon size="xxl" type={`dataVisualizer`} />}
+            title={
+              <FormattedMessage
+                id="xpack.ml.fileDatavisualizer.resultsLinks.openInDataVisualizerTitle"
+                defaultMessage="Open in Data Visualizer"
+              />
+            }
+            description=""
+            href={`#/jobs/new_job/datavisualizer?index=${indexPatternId}${globalStateString}`}
+          />
+        </EuiFlexItem>
+      )}
+
+      <EuiFlexItem>
+        <EuiCard
+          icon={<EuiIcon size="xxl" type={`managementApp`} />}
+          title={
+            <FormattedMessage
+              id="xpack.ml.fileDatavisualizer.resultsLinks.indexManagementTitle"
+              defaultMessage="Index Management"
+            />
+          }
+          description=""
+          href={`${basePath.get()}/app/kibana#/management/elasticsearch/index_management/indices/filter/${index}`}
+        />
+      </EuiFlexItem>
+
+      <EuiFlexItem>
+        <EuiCard
+          icon={<EuiIcon size="xxl" type={`managementApp`} />}
+          title={
+            <FormattedMessage
+              id="xpack.ml.fileDatavisualizer.resultsLinks.indexPatternManagementTitle"
+              defaultMessage="Index Pattern Management"
+            />
+          }
+          description=""
+          href={`${basePath.get()}/app/kibana#/management/kibana/index_patterns/${
+            createIndexPattern ? indexPatternId : ''
+          }`}
+        />
+      </EuiFlexItem>
+      <EuiFlexItem>
+        <EuiCard
+          icon={<EuiIcon size="xxl" type={`filebeatApp`} />}
+          title={
+            <FormattedMessage
+              id="xpack.ml.fileDatavisualizer.resultsLinks.fileBeatConfig"
+              defaultMessage="Create Filebeat configuration"
+            />
+          }
+          description=""
+          onClick={showFilebeatFlyout}
+        />
+      </EuiFlexItem>
+    </EuiFlexGroup>
+  );
+};
+
+async function getFullTimeRange(index: string, timeFieldName: string) {
+  const query = { bool: { must: [{ query_string: { analyze_wildcard: true, query: '*' } }] } };
+  const resp = await ml.getTimeFieldRange({
+    index,
+    timeFieldName,
+    query,
+  });
+
+  return {
+    from: moment(resp.start.epoch).toISOString(),
+    to: moment(resp.end.epoch).toISOString(),
+  };
+}
diff --git a/x-pack/legacy/plugins/ml/public/application/util/dependency_cache.ts b/x-pack/legacy/plugins/ml/public/application/util/dependency_cache.ts
index 8857485a58644..f837d90dba8fe 100644
--- a/x-pack/legacy/plugins/ml/public/application/util/dependency_cache.ts
+++ b/x-pack/legacy/plugins/ml/public/application/util/dependency_cache.ts
@@ -20,6 +20,7 @@ import {
   ChromeRecentlyAccessed,
   IBasePath,
 } from 'kibana/public';
+import { SecurityPluginSetup } from '../../../../../../plugins/security/public';
 
 export interface DependencyCache {
   timefilter: TimefilterSetup | null;
@@ -38,6 +39,7 @@ export interface DependencyCache {
   APP_URL: string | null;
   application: ApplicationStart | null;
   http: HttpStart | null;
+  security: SecurityPluginSetup | null;
 }
 
 const cache: DependencyCache = {
@@ -57,6 +59,7 @@ const cache: DependencyCache = {
   APP_URL: null,
   application: null,
   http: null,
+  security: null,
 };
 
 export function setDependencyCache(deps: Partial<DependencyCache>) {
@@ -189,6 +192,13 @@ export function getHttp() {
   return cache.http;
 }
 
+export function getSecurity() {
+  if (cache.security === null) {
+    throw new Error("security hasn't been initialized");
+  }
+  return cache.security;
+}
+
 export function clearCache() {
   console.log('clearing dependency cache'); // eslint-disable-line no-console
   Object.keys(cache).forEach(k => {
diff --git a/x-pack/legacy/plugins/ml/public/legacy.ts b/x-pack/legacy/plugins/ml/public/legacy.ts
index bf431f0986d68..40a1afa06b5a6 100644
--- a/x-pack/legacy/plugins/ml/public/legacy.ts
+++ b/x-pack/legacy/plugins/ml/public/legacy.ts
@@ -6,14 +6,16 @@
 
 import chrome from 'ui/chrome';
 import { npSetup, npStart } from 'ui/new_platform';
-
 import { PluginInitializerContext } from 'src/core/public';
+import { SecurityPluginSetup } from '../../../../plugins/security/public';
+
 import { plugin } from '.';
 
 const pluginInstance = plugin({} as PluginInitializerContext);
 
 export const setup = pluginInstance.setup(npSetup.core, {
   data: npStart.plugins.data,
+  security: ((npSetup.plugins as unknown) as { security: SecurityPluginSetup }).security, // security isn't in the PluginsSetup interface, but does exist
   __LEGACY: {
     XSRF: chrome.getXsrfToken(),
     // @ts-ignore getAppUrl is missing from chrome's definition
diff --git a/x-pack/legacy/plugins/ml/public/plugin.ts b/x-pack/legacy/plugins/ml/public/plugin.ts
index 79af300bce4ec..cb39b31a32b14 100644
--- a/x-pack/legacy/plugins/ml/public/plugin.ts
+++ b/x-pack/legacy/plugins/ml/public/plugin.ts
@@ -8,7 +8,7 @@ import { Plugin, CoreStart, CoreSetup } from 'src/core/public';
 import { MlDependencies } from './application/app';
 
 export class MlPlugin implements Plugin<Setup, Start> {
-  setup(core: CoreSetup, { data, __LEGACY }: MlDependencies) {
+  setup(core: CoreSetup, { data, security, __LEGACY }: MlDependencies) {
     core.application.register({
       id: 'ml',
       title: 'Machine learning',
@@ -21,6 +21,7 @@ export class MlPlugin implements Plugin<Setup, Start> {
           onAppLeave: params.onAppLeave,
           data,
           __LEGACY,
+          security,
         });
       },
     });
diff --git a/x-pack/legacy/plugins/ml/server/models/file_data_visualizer/file_data_visualizer.ts b/x-pack/legacy/plugins/ml/server/models/file_data_visualizer/file_data_visualizer.ts
index fd5b5221393fc..9f30f609c60b6 100644
--- a/x-pack/legacy/plugins/ml/server/models/file_data_visualizer/file_data_visualizer.ts
+++ b/x-pack/legacy/plugins/ml/server/models/file_data_visualizer/file_data_visualizer.ts
@@ -6,6 +6,7 @@
 
 import Boom from 'boom';
 import { RequestHandlerContext } from 'kibana/server';
+import { FindFileStructureResponse } from '../../../common/types/file_datavisualizer';
 
 export type InputData = any[];
 
@@ -20,31 +21,7 @@ export type FormattedOverrides = InputOverrides & {
 };
 
 export interface AnalysisResult {
-  results: {
-    charset: string;
-    has_header_row: boolean;
-    has_byte_order_marker: boolean;
-    format: string;
-    field_stats: {
-      [fieldName: string]: {
-        count: number;
-        cardinality: number;
-        top_hits: Array<{ count: number; value: any }>;
-      };
-    };
-    sample_start: string;
-    num_messages_analyzed: number;
-    mappings: {
-      [fieldName: string]: {
-        type: string;
-      };
-    };
-    quote: string;
-    delimiter: string;
-    need_client_timezone: boolean;
-    num_lines_analyzed: number;
-    column_names: string[];
-  };
+  results: FindFileStructureResponse;
   overrides?: FormattedOverrides;
 }
 

From afb5d8fa5847cdb7e4f7d6d8742a73486815007f Mon Sep 17 00:00:00 2001
From: Nathan L Smith <nathan.smith@elastic.co>
Date: Tue, 25 Feb 2020 07:43:46 -0600
Subject: [PATCH 172/174] Fix service map popover transaction duration (#58422)

It's already microseconds, so not converting it fixes it.

Checked services to see if all metrics match now and they do!

Fixes #55679
---
 .../app/ServiceMap/Popover/ServiceMetricList.tsx         | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricList.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricList.tsx
index 50ce918ea7037..3a6b4c5ebcaac 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricList.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricList.tsx
@@ -16,12 +16,7 @@ import { isNumber } from 'lodash';
 import React from 'react';
 import styled from 'styled-components';
 import { ServiceNodeMetrics } from '../../../../../../../../plugins/apm/common/service_map';
-import {
-  asDuration,
-  asPercent,
-  toMicroseconds,
-  tpmUnit
-} from '../../../../utils/formatters';
+import { asDuration, asPercent, tpmUnit } from '../../../../utils/formatters';
 
 function LoadingSpinner() {
   return (
@@ -70,7 +65,7 @@ export function ServiceMetricList({
         }
       ),
       description: isNumber(avgTransactionDuration)
-        ? asDuration(toMicroseconds(avgTransactionDuration, 'milliseconds'))
+        ? asDuration(avgTransactionDuration)
         : null
     },
     {

From db05fb6738cd906bf231a28d58d5c17d6efe201c Mon Sep 17 00:00:00 2001
From: Vadim Dalecky <streamich@users.noreply.github.com>
Date: Tue, 25 Feb 2020 15:00:28 +0100
Subject: [PATCH 173/174] Don't mutate error message (#58452)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: 🐛 don't mutate error object in-place

This avoids mutating error thrown by an expression function in-place.
The error might not even be an object, in which case mutating it will
throw.

* test: 💍 capture in test that thrown error is not mutated

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
---
 src/plugins/expressions/common/execution/execution.test.ts | 1 +
 src/plugins/expressions/common/execution/execution.ts      | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/plugins/expressions/common/execution/execution.test.ts b/src/plugins/expressions/common/execution/execution.test.ts
index f6ff9efca848b..4776204a8ab2f 100644
--- a/src/plugins/expressions/common/execution/execution.test.ts
+++ b/src/plugins/expressions/common/execution/execution.test.ts
@@ -630,6 +630,7 @@ describe('Execution', () => {
           },
         });
         expect(node2.debug?.rawError).toBeInstanceOf(Error);
+        expect(node2.debug?.rawError).toEqual(new Error('foo'));
       });
 
       test('sets .debug object to expected shape', async () => {
diff --git a/src/plugins/expressions/common/execution/execution.ts b/src/plugins/expressions/common/execution/execution.ts
index 7e7df822724ae..272448870e817 100644
--- a/src/plugins/expressions/common/execution/execution.ts
+++ b/src/plugins/expressions/common/execution/execution.ts
@@ -230,8 +230,8 @@ export class Execution<
         input = output;
       } catch (rawError) {
         const timeEnd: number = this.params.debug ? performance.now() : 0;
-        rawError.message = `[${fnName}] > ${rawError.message}`;
         const error = createError(rawError) as ExpressionValueError;
+        error.error.message = `[${fnName}] > ${error.error.message}`;
 
         if (this.params.debug) {
           (link as ExpressionAstFunction).debug = {

From 418c44a47ed7c5b6807e31aa4be24f4dbc9d6b5b Mon Sep 17 00:00:00 2001
From: patrykkopycinski <patryk.kopycinski@elastic.co>
Date: Tue, 25 Feb 2020 15:00:50 +0100
Subject: [PATCH 174/174] [SIEM] Fix unnecessary re-renders on the Overview
 page (#56587)

---
 .../__snapshots__/barchart.test.tsx.snap      |   2 -
 .../components/charts/areachart.test.tsx      |   4 +-
 .../public/components/charts/areachart.tsx    |   4 -
 .../components/charts/barchart.test.tsx       |  11 +-
 .../siem/public/components/charts/common.tsx  |  11 +-
 .../drag_and_drop/draggable_wrapper.tsx       |   4 +-
 .../events_viewer/events_viewer.tsx           |  10 +-
 .../public/components/events_viewer/index.tsx |  18 +-
 .../siem/public/components/flyout/index.tsx   |  55 +++---
 .../siem/public/components/inspect/index.tsx  |   8 +-
 .../public/components/navigation/index.tsx    |   7 +-
 .../hosts/authentications_table/index.tsx     |   6 +-
 .../page/hosts/hosts_table/index.tsx          |   1 +
 .../components/page/hosts/kpi_hosts/index.tsx |   1 +
 .../hosts/uncommon_process_table/index.tsx    |   6 +-
 .../page/network/kpi_network/index.tsx        |   1 +
 .../network/network_dns_table/columns.tsx     |   3 +-
 .../page/network/network_dns_table/index.tsx  |  10 +-
 .../page/network/network_http_table/index.tsx |   6 +-
 .../network_top_countries_table/index.tsx     |   5 +-
 .../network_top_n_flow_table/index.tsx        |   5 +-
 .../page/network/tls_table/index.tsx          |  10 +-
 .../page/network/users_table/index.tsx        |  13 +-
 .../overview/overview_host_stats/index.tsx    |   2 -
 .../overview/overview_network_stats/index.tsx |   6 +-
 .../public/components/query_bar/index.tsx     |   6 +-
 .../public/components/search_bar/index.tsx    |   6 +-
 .../source_destination_ip.tsx                 |   5 +-
 .../__snapshots__/index.test.tsx.snap         |   3 -
 .../__snapshots__/timeline.test.tsx.snap      |   2 +-
 .../components/timeline/header/index.tsx      |   8 +-
 .../siem/public/components/timeline/index.tsx |  12 +-
 .../components/timeline/query_bar/index.tsx   |   5 +-
 .../timeline/search_or_filter/index.tsx       |  21 ++-
 .../public/components/timeline/timeline.tsx   |   4 -
 .../public/components/url_state/index.tsx     |   4 +-
 .../components/url_state/use_url_state.tsx    |   7 +-
 .../rules/fetch_index_patterns.tsx            |   5 +-
 .../public/containers/global_time/index.tsx   |  18 +-
 .../containers/query_template_paginated.tsx   |   4 +-
 .../siem/public/containers/source/index.tsx   |   1 +
 .../signals_histogram.tsx                     |  29 +--
 .../rules/components/query_bar/index.tsx      |  12 +-
 .../rules/components/rule_status/index.tsx    |   4 +-
 .../components/step_about_rule/index.tsx      |   4 +-
 .../components/step_define_rule/index.tsx     |  13 +-
 .../components/step_schedule_rule/index.tsx   |   4 +-
 .../pages/hosts/details/details_tabs.tsx      |  45 ++---
 .../siem/public/pages/hosts/hosts_tabs.tsx    |  75 ++++----
 .../network/navigation/network_routes.tsx     | 171 +++++++++---------
 x-pack/legacy/plugins/siem/public/routes.tsx  |   8 +-
 .../siem/public/utils/kql/use_update_kql.tsx  |   4 +-
 .../siem/public/utils/route/spy_routes.tsx    |   3 +-
 x-pack/package.json                           |   1 +
 x-pack/tsconfig.json                          |   2 +-
 yarn.lock                                     |   9 +-
 56 files changed, 374 insertions(+), 330 deletions(-)

diff --git a/x-pack/legacy/plugins/siem/public/components/charts/__snapshots__/barchart.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/charts/__snapshots__/barchart.test.tsx.snap
index 12b9afb661da1..c330676e9219e 100644
--- a/x-pack/legacy/plugins/siem/public/components/charts/__snapshots__/barchart.test.tsx.snap
+++ b/x-pack/legacy/plugins/siem/public/components/charts/__snapshots__/barchart.test.tsx.snap
@@ -1,5 +1,3 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`BarChartBaseComponent render with customized configs should 2 render BarSeries 1`] = `[Function]`;
-
 exports[`BarChartBaseComponent render with default configs if no customized configs given should 2 render BarSeries 1`] = `[Function]`;
diff --git a/x-pack/legacy/plugins/siem/public/components/charts/areachart.test.tsx b/x-pack/legacy/plugins/siem/public/components/charts/areachart.test.tsx
index 27f0222b96b77..3c2de28ae423c 100644
--- a/x-pack/legacy/plugins/siem/public/components/charts/areachart.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/charts/areachart.test.tsx
@@ -331,7 +331,7 @@ describe('AreaChart', () => {
     });
 
     it(`should render area chart`, () => {
-      expect(shallowWrapper.find('WrappedByAutoSizer')).toHaveLength(1);
+      expect(shallowWrapper.find('AreaChartBase')).toHaveLength(1);
       expect(shallowWrapper.find('ChartPlaceHolder')).toHaveLength(0);
     });
   });
@@ -344,7 +344,7 @@ describe('AreaChart', () => {
       });
 
       it(`should render a chart place holder`, () => {
-        expect(shallowWrapper.find('WrappedByAutoSizer')).toHaveLength(0);
+        expect(shallowWrapper.find('AreaChartBase')).toHaveLength(0);
         expect(shallowWrapper.find('ChartPlaceHolder')).toHaveLength(1);
       });
     }
diff --git a/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx b/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx
index b66cc77e30aad..f3b2b736ed87d 100644
--- a/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx
@@ -146,8 +146,4 @@ export const AreaChartComponent: React.FC<AreaChartComponentProps> = ({ areaChar
   );
 };
 
-AreaChartComponent.displayName = 'AreaChartComponent';
-
 export const AreaChart = React.memo(AreaChartComponent);
-
-AreaChart.displayName = 'AreaChart';
diff --git a/x-pack/legacy/plugins/siem/public/components/charts/barchart.test.tsx b/x-pack/legacy/plugins/siem/public/components/charts/barchart.test.tsx
index 0b6635b04d380..272c41833f368 100644
--- a/x-pack/legacy/plugins/siem/public/components/charts/barchart.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/charts/barchart.test.tsx
@@ -9,7 +9,7 @@ import React from 'react';
 
 import { BarChartBaseComponent, BarChartComponent } from './barchart';
 import { ChartSeriesData } from './common';
-import { BarSeries, ScaleType, Axis } from '@elastic/charts';
+import { Chart, BarSeries, Axis, ScaleType } from '@elastic/charts';
 
 jest.mock('../../lib/kibana');
 
@@ -139,7 +139,7 @@ describe('BarChartBaseComponent', () => {
     });
 
     it('should render two bar series', () => {
-      expect(shallowWrapper.find('Chart')).toHaveLength(1);
+      expect(shallowWrapper.find(Chart)).toHaveLength(1);
     });
   });
 
@@ -167,7 +167,6 @@ describe('BarChartBaseComponent', () => {
     });
 
     it(`should ${mockBarChartData.length} render BarSeries`, () => {
-      expect(shallow).toMatchSnapshot();
       expect(shallowWrapper.find(BarSeries)).toHaveLength(mockBarChartData.length);
     });
 
@@ -265,7 +264,7 @@ describe('BarChartBaseComponent', () => {
     });
 
     it('should not render without height and width', () => {
-      expect(shallowWrapper.find('Chart')).toHaveLength(0);
+      expect(shallowWrapper.find(Chart)).toHaveLength(0);
     });
   });
 });
@@ -278,7 +277,7 @@ describe.each(chartDataSets)('BarChart with valid data [%o]', data => {
   });
 
   it(`should render chart`, () => {
-    expect(shallowWrapper.find('WrappedByAutoSizer')).toHaveLength(1);
+    expect(shallowWrapper.find('BarChartBase')).toHaveLength(1);
     expect(shallowWrapper.find('ChartPlaceHolder')).toHaveLength(0);
   });
 });
@@ -291,7 +290,7 @@ describe.each(chartHolderDataSets)('BarChart with invalid data [%o]', data => {
   });
 
   it(`should render a ChartPlaceHolder`, () => {
-    expect(shallowWrapper.find('WrappedByAutoSizer')).toHaveLength(0);
+    expect(shallowWrapper.find('BarChartBase')).toHaveLength(0);
     expect(shallowWrapper.find('ChartPlaceHolder')).toHaveLength(1);
   });
 });
diff --git a/x-pack/legacy/plugins/siem/public/components/charts/common.tsx b/x-pack/legacy/plugins/siem/public/components/charts/common.tsx
index 03b412f575646..7377bcbe7050f 100644
--- a/x-pack/legacy/plugins/siem/public/components/charts/common.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/charts/common.tsx
@@ -16,7 +16,9 @@ import {
   TickFormatter,
   Position,
 } from '@elastic/charts';
+import React, { useMemo } from 'react';
 import styled from 'styled-components';
+
 import { useUiSetting } from '../../lib/kibana';
 import { DEFAULT_DARK_MODE } from '../../../common/constants';
 
@@ -54,7 +56,7 @@ export interface ChartSeriesData {
   color?: string | undefined;
 }
 
-export const WrappedByAutoSizer = styled.div<{ height?: string }>`
+const WrappedByAutoSizerComponent = styled.div<{ height?: string }>`
   ${style =>
     `
     height: ${style.height != null ? style.height : defaultChartHeight};
@@ -66,7 +68,9 @@ export const WrappedByAutoSizer = styled.div<{ height?: string }>`
   }
 `;
 
-WrappedByAutoSizer.displayName = 'WrappedByAutoSizer';
+WrappedByAutoSizerComponent.displayName = 'WrappedByAutoSizer';
+
+export const WrappedByAutoSizer = React.memo(WrappedByAutoSizerComponent);
 
 export enum SeriesType {
   BAR = 'bar',
@@ -96,8 +100,9 @@ const theme: PartialTheme = {
 export const useTheme = () => {
   const isDarkMode = useUiSetting<boolean>(DEFAULT_DARK_MODE);
   const defaultTheme = isDarkMode ? DARK_THEME : LIGHT_THEME;
+  const themeValue = useMemo(() => mergeWithDefaultTheme(theme, defaultTheme), []);
 
-  return mergeWithDefaultTheme(theme, defaultTheme);
+  return themeValue;
 };
 
 export const chartDefaultSettings = {
diff --git a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.tsx b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.tsx
index cf958bfd75d3b..7d84403b87f8d 100644
--- a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.tsx
@@ -4,7 +4,6 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { isEqual } from 'lodash/fp';
 import React, { createContext, useContext, useEffect } from 'react';
 import {
   Draggable,
@@ -14,6 +13,7 @@ import {
 } from 'react-beautiful-dnd';
 import { connect, ConnectedProps } from 'react-redux';
 import styled from 'styled-components';
+import deepEqual from 'fast-deep-equal';
 
 import { EuiPortal } from '@elastic/eui';
 import { dragAndDropActions } from '../../store/drag_and_drop';
@@ -122,7 +122,7 @@ const DraggableWrapperComponent = React.memo<Props>(
   },
   (prevProps, nextProps) => {
     return (
-      isEqual(prevProps.dataProvider, nextProps.dataProvider) &&
+      deepEqual(prevProps.dataProvider, nextProps.dataProvider) &&
       prevProps.render !== nextProps.render &&
       prevProps.truncate === nextProps.truncate
     );
diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx
index 2a4d08ea214bc..a913186d9ad3b 100644
--- a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx
@@ -5,10 +5,10 @@
  */
 
 import { EuiPanel } from '@elastic/eui';
-import deepEqual from 'fast-deep-equal';
-import { getOr, isEmpty, isEqual, union } from 'lodash/fp';
+import { getOr, isEmpty, union } from 'lodash/fp';
 import React, { useMemo } from 'react';
 import styled from 'styled-components';
+import deepEqual from 'fast-deep-equal';
 import useResizeObserver from 'use-resize-observer/polyfilled';
 
 import { BrowserFields } from '../../containers/source';
@@ -228,7 +228,7 @@ const EventsViewerComponent: React.FC<Props> = ({
 export const EventsViewer = React.memo(
   EventsViewerComponent,
   (prevProps, nextProps) =>
-    isEqual(prevProps.browserFields, nextProps.browserFields) &&
+    deepEqual(prevProps.browserFields, nextProps.browserFields) &&
     prevProps.columns === nextProps.columns &&
     prevProps.dataProviders === nextProps.dataProviders &&
     prevProps.deletedEventIds === nextProps.deletedEventIds &&
@@ -241,9 +241,9 @@ export const EventsViewer = React.memo(
     prevProps.itemsPerPage === nextProps.itemsPerPage &&
     prevProps.itemsPerPageOptions === nextProps.itemsPerPageOptions &&
     prevProps.kqlMode === nextProps.kqlMode &&
-    isEqual(prevProps.query, nextProps.query) &&
+    deepEqual(prevProps.query, nextProps.query) &&
     prevProps.start === nextProps.start &&
     prevProps.sort === nextProps.sort &&
-    isEqual(prevProps.timelineTypeContext, nextProps.timelineTypeContext) &&
+    deepEqual(prevProps.timelineTypeContext, nextProps.timelineTypeContext) &&
     prevProps.utilityBar === nextProps.utilityBar
 );
diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx
index 762ae8497dadb..9b31be40dd955 100644
--- a/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx
@@ -4,9 +4,9 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { isEqual } from 'lodash/fp';
 import React, { useCallback, useMemo, useEffect } from 'react';
 import { connect, ConnectedProps } from 'react-redux';
+import deepEqual from 'fast-deep-equal';
 
 import { inputsModel, inputsSelectors, State, timelineSelectors } from '../../store';
 import { inputsActions, timelineActions } from '../../store/actions';
@@ -197,23 +197,23 @@ export const StatefulEventsViewer = connector(
     StatefulEventsViewerComponent,
     (prevProps, nextProps) =>
       prevProps.id === nextProps.id &&
-      isEqual(prevProps.columns, nextProps.columns) &&
-      isEqual(prevProps.dataProviders, nextProps.dataProviders) &&
+      deepEqual(prevProps.columns, nextProps.columns) &&
+      deepEqual(prevProps.dataProviders, nextProps.dataProviders) &&
       prevProps.deletedEventIds === nextProps.deletedEventIds &&
       prevProps.end === nextProps.end &&
-      isEqual(prevProps.filters, nextProps.filters) &&
+      deepEqual(prevProps.filters, nextProps.filters) &&
       prevProps.isLive === nextProps.isLive &&
       prevProps.itemsPerPage === nextProps.itemsPerPage &&
-      isEqual(prevProps.itemsPerPageOptions, nextProps.itemsPerPageOptions) &&
+      deepEqual(prevProps.itemsPerPageOptions, nextProps.itemsPerPageOptions) &&
       prevProps.kqlMode === nextProps.kqlMode &&
-      isEqual(prevProps.query, nextProps.query) &&
-      isEqual(prevProps.sort, nextProps.sort) &&
+      deepEqual(prevProps.query, nextProps.query) &&
+      deepEqual(prevProps.sort, nextProps.sort) &&
       prevProps.start === nextProps.start &&
-      isEqual(prevProps.pageFilters, nextProps.pageFilters) &&
+      deepEqual(prevProps.pageFilters, nextProps.pageFilters) &&
       prevProps.showCheckboxes === nextProps.showCheckboxes &&
       prevProps.showRowRenderers === nextProps.showRowRenderers &&
       prevProps.start === nextProps.start &&
-      isEqual(prevProps.timelineTypeContext, nextProps.timelineTypeContext) &&
+      deepEqual(prevProps.timelineTypeContext, nextProps.timelineTypeContext) &&
       prevProps.utilityBar === nextProps.utilityBar
   )
 );
diff --git a/x-pack/legacy/plugins/siem/public/components/flyout/index.tsx b/x-pack/legacy/plugins/siem/public/components/flyout/index.tsx
index eb41773bb21c8..22fc9f27ce26c 100644
--- a/x-pack/legacy/plugins/siem/public/components/flyout/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/flyout/index.tsx
@@ -6,7 +6,7 @@
 
 import { EuiBadge } from '@elastic/eui';
 import { defaultTo, getOr } from 'lodash/fp';
-import React from 'react';
+import React, { useCallback } from 'react';
 import { connect, ConnectedProps } from 'react-redux';
 import styled from 'styled-components';
 
@@ -58,28 +58,39 @@ export const FlyoutComponent = React.memo<Props>(
     timelineId,
     usersViewing,
     width,
-  }) => (
-    <>
-      <Visible show={show}>
-        <Pane
-          flyoutHeight={flyoutHeight}
-          headerHeight={headerHeight}
-          onClose={() => showTimeline({ id: timelineId, show: false })}
+  }) => {
+    const handleClose = useCallback(() => showTimeline({ id: timelineId, show: false }), [
+      showTimeline,
+      timelineId,
+    ]);
+    const handleOpen = useCallback(() => showTimeline({ id: timelineId, show: true }), [
+      showTimeline,
+      timelineId,
+    ]);
+
+    return (
+      <>
+        <Visible show={show}>
+          <Pane
+            flyoutHeight={flyoutHeight}
+            headerHeight={headerHeight}
+            onClose={handleClose}
+            timelineId={timelineId}
+            usersViewing={usersViewing}
+            width={width}
+          >
+            {children}
+          </Pane>
+        </Visible>
+        <FlyoutButton
+          dataProviders={dataProviders!}
+          show={!show}
           timelineId={timelineId}
-          usersViewing={usersViewing}
-          width={width}
-        >
-          {children}
-        </Pane>
-      </Visible>
-      <FlyoutButton
-        dataProviders={dataProviders!}
-        show={!show}
-        timelineId={timelineId}
-        onOpen={() => showTimeline({ id: timelineId, show: true })}
-      />
-    </>
-  )
+          onOpen={handleOpen}
+        />
+      </>
+    );
+  }
 );
 
 FlyoutComponent.displayName = 'FlyoutComponent';
diff --git a/x-pack/legacy/plugins/siem/public/components/inspect/index.tsx b/x-pack/legacy/plugins/siem/public/components/inspect/index.tsx
index 405a8f060948e..d6f8143745356 100644
--- a/x-pack/legacy/plugins/siem/public/components/inspect/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/inspect/index.tsx
@@ -5,7 +5,7 @@
  */
 
 import { EuiButtonEmpty, EuiButtonIcon } from '@elastic/eui';
-import { getOr } from 'lodash/fp';
+import { getOr, omit } from 'lodash/fp';
 import React, { useCallback } from 'react';
 import { connect } from 'react-redux';
 import { ActionCreator } from 'typescript-fsa';
@@ -162,7 +162,11 @@ const makeMapStateToProps = () => {
   const getGlobalQuery = inputsSelectors.globalQueryByIdSelector();
   const getTimelineQuery = inputsSelectors.timelineQueryByIdSelector();
   const mapStateToProps = (state: State, { inputId = 'global', queryId }: OwnProps) => {
-    return inputId === 'global' ? getGlobalQuery(state, queryId) : getTimelineQuery(state, queryId);
+    const props =
+      inputId === 'global' ? getGlobalQuery(state, queryId) : getTimelineQuery(state, queryId);
+    // refetch caused unnecessary component rerender and it was even not used
+    const propsWithoutRefetch = omit('refetch', props);
+    return propsWithoutRefetch;
   };
   return mapStateToProps;
 };
diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx
index e40cc887ab5ff..8a754cb47475f 100644
--- a/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx
@@ -4,11 +4,10 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import isEqual from 'lodash/fp/isEqual';
-import deepEqual from 'fast-deep-equal';
 import React, { useEffect } from 'react';
 import { connect } from 'react-redux';
 import { compose } from 'redux';
+import deepEqual from 'fast-deep-equal';
 
 import { useKibana } from '../../lib/kibana';
 import { RouteSpyState } from '../../utils/route/types';
@@ -81,8 +80,8 @@ export const SiemNavigationRedux = compose<
     (prevProps, nextProps) =>
       prevProps.pathName === nextProps.pathName &&
       prevProps.search === nextProps.search &&
-      isEqual(prevProps.navTabs, nextProps.navTabs) &&
-      isEqual(prevProps.urlState, nextProps.urlState) &&
+      deepEqual(prevProps.navTabs, nextProps.navTabs) &&
+      deepEqual(prevProps.urlState, nextProps.urlState) &&
       deepEqual(prevProps.state, nextProps.state)
   )
 );
diff --git a/x-pack/legacy/plugins/siem/public/components/page/hosts/authentications_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/hosts/authentications_table/index.tsx
index 853ba7ae23414..678faff7654db 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/hosts/authentications_table/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/page/hosts/authentications_table/index.tsx
@@ -7,7 +7,7 @@
 /* eslint-disable react/display-name */
 
 import { has } from 'lodash/fp';
-import React, { useCallback } from 'react';
+import React, { useCallback, useMemo } from 'react';
 import { connect, ConnectedProps } from 'react-redux';
 
 import { hostsActions } from '../../../../store/hosts';
@@ -100,10 +100,12 @@ const AuthenticationTableComponent = React.memo<AuthenticationTableProps>(
       [type, updateTableActivePage]
     );
 
+    const columns = useMemo(() => getAuthenticationColumnsCurated(type), [type]);
+
     return (
       <PaginatedTable
         activePage={activePage}
-        columns={getAuthenticationColumnsCurated(type)}
+        columns={columns}
         dataTestSubj={`table-${tableType}`}
         headerCount={totalCount}
         headerTitle={i18n.AUTHENTICATIONS}
diff --git a/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/index.tsx
index 0f56ef53ac081..f09834d87e423 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/index.tsx
@@ -7,6 +7,7 @@
 import React, { useMemo, useCallback } from 'react';
 import { connect, ConnectedProps } from 'react-redux';
 import { IIndexPattern } from 'src/plugins/data/public';
+
 import { hostsActions } from '../../../../store/actions';
 import {
   Direction,
diff --git a/x-pack/legacy/plugins/siem/public/components/page/hosts/kpi_hosts/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/hosts/kpi_hosts/index.tsx
index 73cd79c064c95..65d5924821844 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/hosts/kpi_hosts/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/page/hosts/kpi_hosts/index.tsx
@@ -7,6 +7,7 @@
 import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui';
 import React from 'react';
 import styled from 'styled-components';
+
 import { KpiHostsData, KpiHostDetailsData } from '../../../../graphql/types';
 import { StatItemsComponent, StatItemsProps, useKpiMatrixStatus } from '../../../stat_items';
 import { kpiHostsMapping } from './kpi_hosts_mapping';
diff --git a/x-pack/legacy/plugins/siem/public/components/page/hosts/uncommon_process_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/hosts/uncommon_process_table/index.tsx
index 12744ddafa396..2e59afcba4ac8 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/hosts/uncommon_process_table/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/page/hosts/uncommon_process_table/index.tsx
@@ -6,7 +6,7 @@
 
 /* eslint-disable react/display-name */
 
-import React, { useCallback } from 'react';
+import React, { useCallback, useMemo } from 'react';
 import { connect, ConnectedProps } from 'react-redux';
 
 import { hostsActions } from '../../../../store/actions';
@@ -98,10 +98,12 @@ const UncommonProcessTableComponent = React.memo<UncommonProcessTableProps>(
       [type, updateTableActivePage]
     );
 
+    const columns = useMemo(() => getUncommonColumnsCurated(type), [type]);
+
     return (
       <PaginatedTable
         activePage={activePage}
-        columns={getUncommonColumnsCurated(type)}
+        columns={columns}
         dataTestSubj={`table-${tableType}`}
         headerCount={totalCount}
         headerTitle={i18n.UNCOMMON_PROCESSES}
diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/kpi_network/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/kpi_network/index.tsx
index 876df35b7414a..e81c65fbc6afb 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/network/kpi_network/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/page/network/kpi_network/index.tsx
@@ -15,6 +15,7 @@ import {
 } from '@elastic/eui';
 import styled from 'styled-components';
 import { chunk as _chunk } from 'lodash/fp';
+
 import {
   StatItemsComponent,
   StatItemsProps,
diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/network_dns_table/columns.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/network_dns_table/columns.tsx
index b1c1b26cd498d..83a902d7bbde4 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/network/network_dns_table/columns.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/page/network/network_dns_table/columns.tsx
@@ -8,7 +8,6 @@ import numeral from '@elastic/numeral';
 import React from 'react';
 
 import { NetworkDnsFields, NetworkDnsItem } from '../../../../graphql/types';
-import { networkModel } from '../../../../store';
 import { DragEffects, DraggableWrapper } from '../../../drag_and_drop/draggable_wrapper';
 import { escapeDataProviderId } from '../../../drag_and_drop/helpers';
 import { defaultToEmptyTag, getEmptyTagValue } from '../../../empty_value';
@@ -26,7 +25,7 @@ export type NetworkDnsColumns = [
   Columns<NetworkDnsItem['dnsBytesOut']>
 ];
 
-export const getNetworkDnsColumns = (type: networkModel.NetworkType): NetworkDnsColumns => [
+export const getNetworkDnsColumns = (): NetworkDnsColumns => [
   {
     field: `node.${NetworkDnsFields.dnsName}`,
     name: i18n.REGISTERED_DOMAIN,
diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/network_dns_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/network_dns_table/index.tsx
index f3fe98936a55d..c1dd96c5c96f9 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/network/network_dns_table/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/page/network/network_dns_table/index.tsx
@@ -4,9 +4,9 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { isEqual } from 'lodash/fp';
-import React, { useCallback } from 'react';
+import React, { useCallback, useMemo } from 'react';
 import { connect, ConnectedProps } from 'react-redux';
+import deepEqual from 'fast-deep-equal';
 
 import { networkActions } from '../../../../store/actions';
 import {
@@ -93,7 +93,7 @@ export const NetworkDnsTableComponent = React.memo<NetworkDnsTableProps>(
             field: criteria.sort.field.split('.')[1] as NetworkDnsFields,
             direction: criteria.sort.direction as Direction,
           };
-          if (!isEqual(newDnsSortField, sort)) {
+          if (!deepEqual(newDnsSortField, sort)) {
             updateNetworkTable({
               networkType: type,
               tableType,
@@ -115,10 +115,12 @@ export const NetworkDnsTableComponent = React.memo<NetworkDnsTableProps>(
       [type, updateNetworkTable, isPtrIncluded]
     );
 
+    const columns = useMemo(() => getNetworkDnsColumns(), []);
+
     return (
       <PaginatedTable
         activePage={activePage}
-        columns={getNetworkDnsColumns(type)}
+        columns={columns}
         dataTestSubj={`table-${tableType}`}
         headerCount={totalCount}
         headerSupplement={
diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/index.tsx
index 87038eb6aaeeb..6a8b1308f1d36 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/index.tsx
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import React, { useCallback } from 'react';
+import React, { useCallback, useMemo } from 'react';
 import { connect, ConnectedProps } from 'react-redux';
 
 import { networkActions } from '../../../../store/actions';
@@ -99,10 +99,12 @@ const NetworkHttpTableComponent: React.FC<NetworkHttpTableProps> = ({
 
   const sorting = { field: `node.${NetworkHttpFields.requestCount}`, direction: sort.direction };
 
+  const columns = useMemo(() => getNetworkHttpColumns(tableType), [tableType]);
+
   return (
     <PaginatedTable
       activePage={activePage}
-      columns={getNetworkHttpColumns(tableType)}
+      columns={columns}
       dataTestSubj={`table-${tableType}`}
       headerCount={totalCount}
       headerTitle={i18n.HTTP_REQUESTS}
diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/network_top_countries_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/network_top_countries_table/index.tsx
index 37355a37fa6d7..30f7d5ad82390 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/network/network_top_countries_table/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/page/network/network_top_countries_table/index.tsx
@@ -4,9 +4,10 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { isEqual, last } from 'lodash/fp';
+import { last } from 'lodash/fp';
 import React, { useCallback, useMemo } from 'react';
 import { connect, ConnectedProps } from 'react-redux';
+import deepEqual from 'fast-deep-equal';
 import { IIndexPattern } from 'src/plugins/data/public';
 
 import { networkActions } from '../../../../store/actions';
@@ -125,7 +126,7 @@ const NetworkTopCountriesTableComponent = React.memo<NetworkTopCountriesTablePro
             field: lastField as NetworkTopTablesFields,
             direction: newSortDirection as Direction,
           };
-          if (!isEqual(newTopCountriesSort, sort)) {
+          if (!deepEqual(newTopCountriesSort, sort)) {
             updateNetworkTable({
               networkType: type,
               tableType,
diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/network_top_n_flow_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/network_top_n_flow_table/index.tsx
index c4f6dad0a47ed..8e49db04a546c 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/network/network_top_n_flow_table/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/page/network/network_top_n_flow_table/index.tsx
@@ -3,9 +3,10 @@
  * or more contributor license agreements. Licensed under the Elastic License;
  * you may not use this file except in compliance with the Elastic License.
  */
-import { isEqual, last } from 'lodash/fp';
+import { last } from 'lodash/fp';
 import React, { useCallback, useMemo } from 'react';
 import { connect, ConnectedProps } from 'react-redux';
+import deepEqual from 'fast-deep-equal';
 
 import { networkActions } from '../../../../store/actions';
 import {
@@ -96,7 +97,7 @@ const NetworkTopNFlowTableComponent: React.FC<NetworkTopNFlowTableProps> = ({
           field: field as NetworkTopTablesFields,
           direction: newSortDirection as Direction,
         };
-        if (!isEqual(newTopNFlowSort, sort)) {
+        if (!deepEqual(newTopNFlowSort, sort)) {
           updateNetworkTable({
             networkType: type,
             tableType,
diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/tls_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/tls_table/index.tsx
index 77abae68b76bf..d1512699cc709 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/network/tls_table/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/page/network/tls_table/index.tsx
@@ -4,9 +4,9 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { isEqual } from 'lodash/fp';
-import React, { useCallback } from 'react';
+import React, { useCallback, useMemo } from 'react';
 import { connect, ConnectedProps } from 'react-redux';
+import deepEqual from 'fast-deep-equal';
 
 import { networkActions } from '../../../../store/network';
 import { TlsEdges, TlsSortField, TlsFields, Direction } from '../../../../graphql/types';
@@ -91,7 +91,7 @@ const TlsTableComponent = React.memo<TlsTableProps>(
             field: getSortFromString(splitField[splitField.length - 1]),
             direction: criteria.sort.direction as Direction,
           };
-          if (!isEqual(newTlsSort, sort)) {
+          if (!deepEqual(newTlsSort, sort)) {
             updateNetworkTable({
               networkType: type,
               tableType,
@@ -103,10 +103,12 @@ const TlsTableComponent = React.memo<TlsTableProps>(
       [sort, type, tableType, updateNetworkTable]
     );
 
+    const columns = useMemo(() => getTlsColumns(tlsTableId), [tlsTableId]);
+
     return (
       <PaginatedTable
         activePage={activePage}
-        columns={getTlsColumns(tlsTableId)}
+        columns={columns}
         dataTestSubj={`table-${tableType}`}
         showMorePagesIndicator={showMorePagesIndicator}
         headerCount={totalCount}
diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/users_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/users_table/index.tsx
index bc7ef2314add4..b585b835f31cd 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/network/users_table/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/page/network/users_table/index.tsx
@@ -4,9 +4,9 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { isEqual } from 'lodash/fp';
-import React, { useCallback } from 'react';
+import React, { useCallback, useMemo } from 'react';
 import { connect, ConnectedProps } from 'react-redux';
+import deepEqual from 'fast-deep-equal';
 
 import { networkActions } from '../../../../store/network';
 import {
@@ -97,7 +97,7 @@ const UsersTableComponent = React.memo<UsersTableProps>(
             field: getSortFromString(splitField[splitField.length - 1]),
             direction: criteria.sort.direction as Direction,
           };
-          if (!isEqual(newUsersSort, sort)) {
+          if (!deepEqual(newUsersSort, sort)) {
             updateNetworkTable({
               networkType: type,
               tableType,
@@ -109,10 +109,15 @@ const UsersTableComponent = React.memo<UsersTableProps>(
       [sort, type, updateNetworkTable]
     );
 
+    const columns = useMemo(() => getUsersColumns(flowTarget, usersTableId), [
+      flowTarget,
+      usersTableId,
+    ]);
+
     return (
       <PaginatedTable
         activePage={activePage}
-        columns={getUsersColumns(flowTarget, usersTableId)}
+        columns={columns}
         dataTestSubj={`table-${tableType}`}
         showMorePagesIndicator={showMorePagesIndicator}
         headerCount={totalCount}
diff --git a/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host_stats/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host_stats/index.tsx
index b811a3615b148..4756e4c826574 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host_stats/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host_stats/index.tsx
@@ -266,6 +266,4 @@ const OverviewHostStatsComponent: React.FC<OverviewHostProps> = ({ data, loading
   );
 };
 
-OverviewHostStatsComponent.displayName = 'OverviewHostStatsComponent';
-
 export const OverviewHostStats = React.memo(OverviewHostStatsComponent);
diff --git a/x-pack/legacy/plugins/siem/public/components/page/overview/overview_network_stats/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/overview/overview_network_stats/index.tsx
index 260b1d6895140..ca947c29bc382 100644
--- a/x-pack/legacy/plugins/siem/public/components/page/overview/overview_network_stats/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/page/overview/overview_network_stats/index.tsx
@@ -130,7 +130,7 @@ const AccordionContent = styled.div`
   margin-top: 8px;
 `;
 
-export const OverviewNetworkStats = React.memo<OverviewNetworkProps>(({ data, loading }) => {
+const OverviewNetworkStatsComponent: React.FC<OverviewNetworkProps> = ({ data, loading }) => {
   const allNetworkStats = getOverviewNetworkStats(data);
   const allNetworkStatsCount = allNetworkStats.reduce((total, stat) => total + stat.count, 0);
 
@@ -190,6 +190,6 @@ export const OverviewNetworkStats = React.memo<OverviewNetworkProps>(({ data, lo
       })}
     </NetworkStatsContainer>
   );
-});
+};
 
-OverviewNetworkStats.displayName = 'OverviewNetworkStats';
+export const OverviewNetworkStats = React.memo(OverviewNetworkStatsComponent);
diff --git a/x-pack/legacy/plugins/siem/public/components/query_bar/index.tsx b/x-pack/legacy/plugins/siem/public/components/query_bar/index.tsx
index 03a8143c89517..557d389aefee9 100644
--- a/x-pack/legacy/plugins/siem/public/components/query_bar/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/query_bar/index.tsx
@@ -4,8 +4,8 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { isEqual } from 'lodash/fp';
 import React, { memo, useState, useEffect, useMemo, useCallback } from 'react';
+import deepEqual from 'fast-deep-equal';
 
 import {
   Filter,
@@ -64,7 +64,7 @@ export const QueryBar = memo<QueryBarComponentProps>(
 
     const onQuerySubmit = useCallback(
       (payload: { dateRange: TimeRange; query?: Query }) => {
-        if (payload.query != null && !isEqual(payload.query, filterQuery)) {
+        if (payload.query != null && !deepEqual(payload.query, filterQuery)) {
           onSubmitQuery(payload.query);
         }
       },
@@ -73,7 +73,7 @@ export const QueryBar = memo<QueryBarComponentProps>(
 
     const onQueryChange = useCallback(
       (payload: { dateRange: TimeRange; query?: Query }) => {
-        if (payload.query != null && !isEqual(payload.query, draftQuery)) {
+        if (payload.query != null && !deepEqual(payload.query, draftQuery)) {
           setDraftQuery(payload.query);
           onChangedQuery(payload.query);
         }
diff --git a/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx b/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx
index cb5729ad8e26e..2513004af84dd 100644
--- a/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx
@@ -4,12 +4,13 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { getOr, isEqual, set } from 'lodash/fp';
+import { getOr, set } from 'lodash/fp';
 import React, { memo, useEffect, useCallback, useMemo } from 'react';
 import { connect, ConnectedProps } from 'react-redux';
 import { Dispatch } from 'redux';
 import { Subscription } from 'rxjs';
 import styled from 'styled-components';
+import deepEqual from 'fast-deep-equal';
 import { FilterManager, IIndexPattern, TimeRange, Query, Filter } from 'src/plugins/data/public';
 import { SavedQuery } from 'src/legacy/core_plugins/data/public';
 
@@ -60,7 +61,6 @@ const SearchBarComponent = memo<SiemSearchBarProps & PropsFromRedux>(
     setSavedQuery,
     setSearchBarFilter,
     start,
-    timelineId,
     toStr,
     updateSearch,
     dataTestSubj,
@@ -108,7 +108,7 @@ const SearchBarComponent = memo<SiemSearchBarProps & PropsFromRedux>(
           updateSearchBar.start = payload.dateRange.from;
         }
 
-        if (payload.query != null && !isEqual(payload.query, filterQuery)) {
+        if (payload.query != null && !deepEqual(payload.query, filterQuery)) {
           isStateUpdated = true;
           updateSearchBar = set('query', payload.query, updateSearchBar);
         }
diff --git a/x-pack/legacy/plugins/siem/public/components/source_destination/source_destination_ip.tsx b/x-pack/legacy/plugins/siem/public/components/source_destination/source_destination_ip.tsx
index 33159387214e4..b8192cce11e5a 100644
--- a/x-pack/legacy/plugins/siem/public/components/source_destination/source_destination_ip.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/source_destination/source_destination_ip.tsx
@@ -5,8 +5,9 @@
  */
 
 import { EuiBadge, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
-import { isEmpty, isEqual, uniqWith } from 'lodash/fp';
+import { isEmpty, uniqWith } from 'lodash/fp';
 import React from 'react';
+import deepEqual from 'fast-deep-equal';
 
 import { DESTINATION_IP_FIELD_NAME, SOURCE_IP_FIELD_NAME } from '../ip';
 import { DESTINATION_PORT_FIELD_NAME, SOURCE_PORT_FIELD_NAME, Port } from '../port';
@@ -115,7 +116,7 @@ const IpAdressesWithPorts = React.memo<{
 
   return (
     <EuiFlexGroup gutterSize="none">
-      {uniqWith(isEqual, ipPortPairs).map(
+      {uniqWith(deepEqual, ipPortPairs).map(
         ipPortPair =>
           ipPortPair.ip != null && (
             <EuiFlexItem grow={false} key={ipPortPair.ip}>
diff --git a/x-pack/legacy/plugins/siem/public/components/stat_items/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/stat_items/__snapshots__/index.test.tsx.snap
index 69596ba8f3325..ef077ece19f92 100644
--- a/x-pack/legacy/plugins/siem/public/components/stat_items/__snapshots__/index.test.tsx.snap
+++ b/x-pack/legacy/plugins/siem/public/components/stat_items/__snapshots__/index.test.tsx.snap
@@ -94,7 +94,6 @@ exports[`Stat Items Component disable charts it renders the default widget 1`] =
                                 isInspected={false}
                                 loading={false}
                                 queryId="statItems"
-                                refetch={null}
                                 selectedInspectIndex={0}
                                 setIsInspected={[Function]}
                                 title="KPI HOSTS"
@@ -328,7 +327,6 @@ exports[`Stat Items Component disable charts it renders the default widget 2`] =
                                 isInspected={false}
                                 loading={false}
                                 queryId="statItems"
-                                refetch={null}
                                 selectedInspectIndex={0}
                                 setIsInspected={[Function]}
                                 title="KPI HOSTS"
@@ -632,7 +630,6 @@ exports[`Stat Items Component rendering kpis with charts it renders the default
                               isInspected={false}
                               loading={false}
                               queryId="statItems"
-                              refetch={null}
                               selectedInspectIndex={0}
                               setIsInspected={[Function]}
                               title="KPI UNIQUE_PRIVATE_IPS"
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/__snapshots__/timeline.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/timeline/__snapshots__/timeline.test.tsx.snap
index 3fcd258b79147..372930ee3167d 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/__snapshots__/timeline.test.tsx.snap
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/__snapshots__/timeline.test.tsx.snap
@@ -8,7 +8,7 @@ exports[`Timeline rendering renders correctly against snapshot 1`] = `
   justifyContent="flexStart"
 >
   <WrappedByAutoSizer>
-    <TimelineHeader
+    <Memo(TimelineHeaderComponent)
       browserFields={
         Object {
           "agent": Object {
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/header/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/header/index.tsx
index 7e570d613ca5a..81eef0efbfa5b 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/header/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/header/index.tsx
@@ -47,7 +47,7 @@ const TimelineHeaderContainer = styled.div`
 
 TimelineHeaderContainer.displayName = 'TimelineHeaderContainer';
 
-export const TimelineHeaderComponent = ({
+export const TimelineHeaderComponent: React.FC<Props> = ({
   browserFields,
   id,
   indexPattern,
@@ -60,7 +60,7 @@ export const TimelineHeaderComponent = ({
   onToggleDataProviderExcluded,
   show,
   showCallOutUnauthorizedMsg,
-}: Props) => (
+}) => (
   <TimelineHeaderContainer data-test-subj="timelineHeader">
     {showCallOutUnauthorizedMsg && (
       <EuiCallOut
@@ -91,8 +91,4 @@ export const TimelineHeaderComponent = ({
   </TimelineHeaderContainer>
 );
 
-TimelineHeaderComponent.displayName = 'TimelineHeaderComponent';
-
 export const TimelineHeader = React.memo(TimelineHeaderComponent);
-
-TimelineHeader.displayName = 'TimelineHeader';
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/index.tsx
index d782d0366f041..0ce6bc16f1325 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/index.tsx
@@ -4,9 +4,9 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { isEqual } from 'lodash/fp';
 import React, { useEffect, useCallback, useMemo } from 'react';
 import { connect, ConnectedProps } from 'react-redux';
+import deepEqual from 'fast-deep-equal';
 
 import { WithSource } from '../../containers/source';
 import { useSignalIndex } from '../../containers/detection_engine/signals/use_signal_index';
@@ -215,11 +215,11 @@ const StatefulTimelineComponent = React.memo<Props>(
       prevProps.show === nextProps.show &&
       prevProps.showCallOutUnauthorizedMsg === nextProps.showCallOutUnauthorizedMsg &&
       prevProps.start === nextProps.start &&
-      isEqual(prevProps.columns, nextProps.columns) &&
-      isEqual(prevProps.dataProviders, nextProps.dataProviders) &&
-      isEqual(prevProps.filters, nextProps.filters) &&
-      isEqual(prevProps.itemsPerPageOptions, nextProps.itemsPerPageOptions) &&
-      isEqual(prevProps.sort, nextProps.sort)
+      deepEqual(prevProps.columns, nextProps.columns) &&
+      deepEqual(prevProps.dataProviders, nextProps.dataProviders) &&
+      deepEqual(prevProps.filters, nextProps.filters) &&
+      deepEqual(prevProps.itemsPerPageOptions, nextProps.itemsPerPageOptions) &&
+      deepEqual(prevProps.sort, nextProps.sort)
     );
   }
 );
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/query_bar/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/query_bar/index.tsx
index 96b8df6d8ada7..7f662cdb2f1b4 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/query_bar/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/query_bar/index.tsx
@@ -4,9 +4,10 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { isEqual, isEmpty } from 'lodash/fp';
+import { isEmpty } from 'lodash/fp';
 import React, { memo, useCallback, useState, useEffect } from 'react';
 import { Subscription } from 'rxjs';
+import deepEqual from 'fast-deep-equal';
 
 import {
   IIndexPattern,
@@ -127,7 +128,7 @@ export const QueryBarTimeline = memo<QueryBarTimelineComponentProps>(
       const filterWithoutDropArea = filterManager
         .getFilters()
         .filter((f: Filter) => f.meta.controlledBy !== timelineFilterDropArea);
-      if (!isEqual(filters, filterWithoutDropArea)) {
+      if (!deepEqual(filters, filterWithoutDropArea)) {
         filterManager.setFilters(filters);
       }
     }, [filters]);
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/search_or_filter/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/search_or_filter/index.tsx
index d1904fd5d9aac..87061bdbb5d02 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/search_or_filter/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/search_or_filter/index.tsx
@@ -4,10 +4,11 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { getOr, isEqual } from 'lodash/fp';
+import { getOr } from 'lodash/fp';
 import React, { useCallback } from 'react';
 import { connect, ConnectedProps } from 'react-redux';
 import { Dispatch } from 'redux';
+import deepEqual from 'fast-deep-equal';
 
 import { Filter, IIndexPattern } from '../../../../../../../../src/plugins/data/public';
 import { BrowserFields } from '../../../containers/source';
@@ -152,15 +153,15 @@ const StatefulSearchOrFilterComponent = React.memo<Props>(
       prevProps.isRefreshPaused === nextProps.isRefreshPaused &&
       prevProps.refreshInterval === nextProps.refreshInterval &&
       prevProps.timelineId === nextProps.timelineId &&
-      isEqual(prevProps.browserFields, nextProps.browserFields) &&
-      isEqual(prevProps.dataProviders, nextProps.dataProviders) &&
-      isEqual(prevProps.filters, nextProps.filters) &&
-      isEqual(prevProps.filterQuery, nextProps.filterQuery) &&
-      isEqual(prevProps.filterQueryDraft, nextProps.filterQueryDraft) &&
-      isEqual(prevProps.indexPattern, nextProps.indexPattern) &&
-      isEqual(prevProps.kqlMode, nextProps.kqlMode) &&
-      isEqual(prevProps.savedQueryId, nextProps.savedQueryId) &&
-      isEqual(prevProps.timelineId, nextProps.timelineId)
+      deepEqual(prevProps.browserFields, nextProps.browserFields) &&
+      deepEqual(prevProps.dataProviders, nextProps.dataProviders) &&
+      deepEqual(prevProps.filters, nextProps.filters) &&
+      deepEqual(prevProps.filterQuery, nextProps.filterQuery) &&
+      deepEqual(prevProps.filterQueryDraft, nextProps.filterQueryDraft) &&
+      deepEqual(prevProps.indexPattern, nextProps.indexPattern) &&
+      deepEqual(prevProps.kqlMode, nextProps.kqlMode) &&
+      deepEqual(prevProps.savedQueryId, nextProps.savedQueryId) &&
+      deepEqual(prevProps.timelineId, nextProps.timelineId)
     );
   }
 );
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx
index c9ff0296a40e2..58bbbef328ddf 100644
--- a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx
@@ -229,8 +229,4 @@ export const TimelineComponent: React.FC<Props> = ({
   );
 };
 
-TimelineComponent.displayName = 'TimelineComponent';
-
 export const Timeline = React.memo(TimelineComponent);
-
-Timeline.displayName = 'Timeline';
diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/index.tsx b/x-pack/legacy/plugins/siem/public/components/url_state/index.tsx
index e656ec3496d8d..294e41a1faa7b 100644
--- a/x-pack/legacy/plugins/siem/public/components/url_state/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/url_state/index.tsx
@@ -4,10 +4,10 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { isEqual } from 'lodash/fp';
 import React from 'react';
 import { compose, Dispatch } from 'redux';
 import { connect } from 'react-redux';
+import deepEqual from 'fast-deep-equal';
 
 import { timelineActions } from '../../store/actions';
 import { RouteSpyState } from '../../utils/route/types';
@@ -39,7 +39,7 @@ export const UrlStateRedux = compose<React.ComponentClass<UrlStateProps & RouteS
   React.memo(
     UrlStateContainer,
     (prevProps, nextProps) =>
-      prevProps.pathName === nextProps.pathName && isEqual(prevProps.urlState, nextProps.urlState)
+      prevProps.pathName === nextProps.pathName && deepEqual(prevProps.urlState, nextProps.urlState)
   )
 );
 
diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/use_url_state.tsx b/x-pack/legacy/plugins/siem/public/components/url_state/use_url_state.tsx
index deaf9bbf5011d..a7704e0e86970 100644
--- a/x-pack/legacy/plugins/siem/public/components/url_state/use_url_state.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/url_state/use_url_state.tsx
@@ -4,8 +4,9 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { isEqual, difference, isEmpty } from 'lodash/fp';
+import { difference, isEmpty } from 'lodash/fp';
 import { useEffect, useRef, useState } from 'react';
+import deepEqual from 'fast-deep-equal';
 
 import { useKibana } from '../../lib/kibana';
 import { useApolloClient } from '../../utils/apollo_context';
@@ -77,7 +78,7 @@ export const useUrlStateHooks = ({
           const updatedUrlStateString =
             getParamFromQueryString(getQueryStringFromLocation(mySearch), urlKey) ??
             newUrlStateString;
-          if (isInitializing || !isEqual(updatedUrlStateString, newUrlStateString)) {
+          if (isInitializing || !deepEqual(updatedUrlStateString, newUrlStateString)) {
             urlStateToUpdate = [
               ...urlStateToUpdate,
               {
@@ -157,7 +158,7 @@ export const useUrlStateHooks = ({
     if (isInitializing && pageName != null && pageName !== '') {
       handleInitialize(type);
       setIsInitializing(false);
-    } else if (!isEqual(urlState, prevProps.urlState) && !isInitializing) {
+    } else if (!deepEqual(urlState, prevProps.urlState) && !isInitializing) {
       let mySearch = search;
       URL_STATE_KEYS[type].forEach((urlKey: KeyUrlState) => {
         if (
diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/fetch_index_patterns.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/fetch_index_patterns.tsx
index b7ad41b8ba1bb..06c4d1054bca4 100644
--- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/fetch_index_patterns.tsx
+++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/fetch_index_patterns.tsx
@@ -4,8 +4,9 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { isEmpty, isEqual, get } from 'lodash/fp';
+import { isEmpty, get } from 'lodash/fp';
 import { useEffect, useState, Dispatch, SetStateAction } from 'react';
+import deepEqual from 'fast-deep-equal';
 
 import { IIndexPattern } from '../../../../../../../../src/plugins/data/public';
 import {
@@ -41,7 +42,7 @@ export const useFetchIndexPatterns = (defaultIndices: string[] = []): Return =>
   const [, dispatchToaster] = useStateToaster();
 
   useEffect(() => {
-    if (!isEqual(defaultIndices, indices)) {
+    if (!deepEqual(defaultIndices, indices)) {
       setIndices(defaultIndices);
     }
   }, [defaultIndices, indices]);
diff --git a/x-pack/legacy/plugins/siem/public/containers/global_time/index.tsx b/x-pack/legacy/plugins/siem/public/containers/global_time/index.tsx
index caf597d02c835..4632e9aee3fdd 100644
--- a/x-pack/legacy/plugins/siem/public/containers/global_time/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/containers/global_time/index.tsx
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import React, { useState, useEffect } from 'react';
+import React, { useCallback, useState, useEffect } from 'react';
 import { connect, ConnectedProps } from 'react-redux';
 
 import { inputsModel, inputsSelectors, State } from '../../store';
@@ -41,6 +41,17 @@ export const GlobalTimeComponent: React.FC<GlobalTimeProps> = ({
 }) => {
   const [isInitializing, setIsInitializing] = useState(true);
 
+  const setQuery = useCallback(
+    ({ id, inspect, loading, refetch }: SetQuery) =>
+      setGlobalQuery({ inputId: 'global', id, inspect, loading, refetch }),
+    [setGlobalQuery]
+  );
+
+  const deleteQuery = useCallback(
+    ({ id }: { id: string }) => deleteOneQuery({ inputId: 'global', id }),
+    [deleteOneQuery]
+  );
+
   useEffect(() => {
     if (isInitializing) {
       setIsInitializing(false);
@@ -56,9 +67,8 @@ export const GlobalTimeComponent: React.FC<GlobalTimeProps> = ({
         isInitializing,
         from,
         to,
-        setQuery: ({ id, inspect, loading, refetch }: SetQuery) =>
-          setGlobalQuery({ inputId: 'global', id, inspect, loading, refetch }),
-        deleteQuery: ({ id }: { id: string }) => deleteOneQuery({ inputId: 'global', id }),
+        setQuery,
+        deleteQuery,
       })}
     </>
   );
diff --git a/x-pack/legacy/plugins/siem/public/containers/query_template_paginated.tsx b/x-pack/legacy/plugins/siem/public/containers/query_template_paginated.tsx
index 4d6ab757fdea7..db618f216d83e 100644
--- a/x-pack/legacy/plugins/siem/public/containers/query_template_paginated.tsx
+++ b/x-pack/legacy/plugins/siem/public/containers/query_template_paginated.tsx
@@ -5,10 +5,10 @@
  */
 
 import { ApolloQueryResult, NetworkStatus } from 'apollo-client';
-import { isEqual } from 'lodash/fp';
 import memoizeOne from 'memoize-one';
 import React from 'react';
 import { FetchMoreOptions, FetchMoreQueryOptions, OperationVariables } from 'react-apollo';
+import deepEqual from 'fast-deep-equal';
 
 import { ESQuery } from '../../common/typed_json';
 import { inputsModel } from '../store/model';
@@ -85,7 +85,7 @@ export class QueryTemplatePaginated<
   public isItAValidLoading(loading: boolean, variables: TVariables, networkStatus: NetworkStatus) {
     if (
       !this.myLoading &&
-      (!isEqual(variables, this.queryVariables) || networkStatus === NetworkStatus.refetch) &&
+      (!deepEqual(variables, this.queryVariables) || networkStatus === NetworkStatus.refetch) &&
       loading
     ) {
       this.myLoading = true;
diff --git a/x-pack/legacy/plugins/siem/public/containers/source/index.tsx b/x-pack/legacy/plugins/siem/public/containers/source/index.tsx
index 0336e4a9a977b..e454421ca955d 100644
--- a/x-pack/legacy/plugins/siem/public/containers/source/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/containers/source/index.tsx
@@ -10,6 +10,7 @@ import { Query } from 'react-apollo';
 import React, { useEffect, useMemo, useState } from 'react';
 import memoizeOne from 'memoize-one';
 import { IIndexPattern } from 'src/plugins/data/public';
+
 import { useUiSetting$ } from '../../lib/kibana';
 
 import { DEFAULT_INDEX_KEY } from '../../../common/constants';
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/signals_histogram.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/signals_histogram.tsx
index 92f6740e4d767..2d9b1ee844b4b 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/signals_histogram.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/signals_histogram.tsx
@@ -12,9 +12,11 @@ import {
   HistogramBarSeries,
   Position,
   Settings,
+  ChartSizeArray,
 } from '@elastic/charts';
-import React from 'react';
+import React, { useMemo } from 'react';
 import { EuiProgress } from '@elastic/eui';
+
 import { useTheme } from '../../../../components/charts/common';
 import { histogramDateTimeFormatter } from '../../../../components/utils';
 import { HistogramData } from './types';
@@ -43,6 +45,14 @@ export const SignalsHistogram = React.memo<HistogramSignalsProps>(
   }) => {
     const theme = useTheme();
 
+    const chartSize: ChartSizeArray = useMemo(() => ['100%', chartHeight], [chartHeight]);
+    const xAxisId = useMemo(() => getAxisId('signalsHistogramAxisX'), []);
+    const yAxisId = useMemo(() => getAxisId('signalsHistogramAxisY'), []);
+    const id = useMemo(() => getSpecId('signalsHistogram'), []);
+    const yAccessors = useMemo(() => ['y'], []);
+    const splitSeriesAccessors = useMemo(() => ['g'], []);
+    const tickFormat = useMemo(() => histogramDateTimeFormatter([from, to]), [from, to]);
+
     return (
       <>
         {loading && (
@@ -54,7 +64,7 @@ export const SignalsHistogram = React.memo<HistogramSignalsProps>(
           />
         )}
 
-        <Chart size={['100%', chartHeight]}>
+        <Chart size={chartSize}>
           <Settings
             legendPosition={legendPosition}
             onBrushEnd={updateDateRange}
@@ -62,21 +72,17 @@ export const SignalsHistogram = React.memo<HistogramSignalsProps>(
             theme={theme}
           />
 
-          <Axis
-            id={getAxisId('signalsHistogramAxisX')}
-            position="bottom"
-            tickFormat={histogramDateTimeFormatter([from, to])}
-          />
+          <Axis id={xAxisId} position="bottom" tickFormat={tickFormat} />
 
-          <Axis id={getAxisId('signalsHistogramAxisY')} position="left" />
+          <Axis id={yAxisId} position="left" />
 
           <HistogramBarSeries
-            id={getSpecId('signalsHistogram')}
+            id={id}
             xScaleType="time"
             yScaleType="linear"
             xAccessor="x"
-            yAccessors={['y']}
-            splitSeriesAccessors={['g']}
+            yAccessors={yAccessors}
+            splitSeriesAccessors={splitSeriesAccessors}
             data={data}
           />
         </Chart>
@@ -84,4 +90,5 @@ export const SignalsHistogram = React.memo<HistogramSignalsProps>(
     );
   }
 );
+
 SignalsHistogram.displayName = 'SignalsHistogram';
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/query_bar/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/query_bar/index.tsx
index 88795f9195e68..fbe854c1ee346 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/query_bar/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/query_bar/index.tsx
@@ -5,10 +5,10 @@
  */
 
 import { EuiFormRow, EuiMutationObserver } from '@elastic/eui';
-import { isEqual } from 'lodash/fp';
 import React, { useCallback, useEffect, useMemo, useState } from 'react';
 import { Subscription } from 'rxjs';
 import styled from 'styled-components';
+import deepEqual from 'fast-deep-equal';
 
 import {
   Filter,
@@ -99,7 +99,7 @@ export const QueryBarDefineRule = ({
             const newFilters = filterManager.getFilters();
             const { filters } = field.value as FieldValueQueryBar;
 
-            if (!isEqual(filters, newFilters)) {
+            if (!deepEqual(filters, newFilters)) {
               field.setValue({ ...(field.value as FieldValueQueryBar), filters: newFilters });
             }
           }
@@ -117,10 +117,10 @@ export const QueryBarDefineRule = ({
     let isSubscribed = true;
     async function updateFilterQueryFromValue() {
       const { filters, query, saved_id: savedId } = field.value as FieldValueQueryBar;
-      if (!isEqual(query, queryDraft)) {
+      if (!deepEqual(query, queryDraft)) {
         setQueryDraft(query);
       }
-      if (!isEqual(filters, filterManager.getFilters())) {
+      if (!deepEqual(filters, filterManager.getFilters())) {
         filterManager.setFilters(filters);
       }
       if (
@@ -148,7 +148,7 @@ export const QueryBarDefineRule = ({
   const onSubmitQuery = useCallback(
     (newQuery: Query, timefilter?: SavedQueryTimeFilter) => {
       const { query } = field.value as FieldValueQueryBar;
-      if (!isEqual(query, newQuery)) {
+      if (!deepEqual(query, newQuery)) {
         field.setValue({ ...(field.value as FieldValueQueryBar), query: newQuery });
       }
     },
@@ -158,7 +158,7 @@ export const QueryBarDefineRule = ({
   const onChangedQuery = useCallback(
     (newQuery: Query) => {
       const { query } = field.value as FieldValueQueryBar;
-      if (!isEqual(query, newQuery)) {
+      if (!deepEqual(query, newQuery)) {
         field.setValue({ ...(field.value as FieldValueQueryBar), query: newQuery });
       }
     },
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_status/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_status/index.tsx
index 2c9173cbeb694..ac457d7345c29 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_status/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_status/index.tsx
@@ -12,8 +12,8 @@ import {
   EuiLoadingSpinner,
   EuiText,
 } from '@elastic/eui';
-import { isEqual } from 'lodash/fp';
 import React, { memo, useCallback, useEffect, useState } from 'react';
+import deepEqual from 'fast-deep-equal';
 
 import { useRuleStatus, RuleInfoStatus } from '../../../../../containers/detection_engine/rules';
 import { FormattedDate } from '../../../../../components/formatted_date';
@@ -43,7 +43,7 @@ const RuleStatusComponent: React.FC<RuleStatusProps> = ({ ruleId, ruleEnabled })
   }, [fetchRuleStatus, myRuleEnabled, ruleId, ruleEnabled, setMyRuleEnabled]);
 
   useEffect(() => {
-    if (!isEqual(currentStatus, ruleStatus?.current_status)) {
+    if (!deepEqual(currentStatus, ruleStatus?.current_status)) {
       setCurrentStatus(ruleStatus?.current_status ?? null);
     }
   }, [currentStatus, ruleStatus, setCurrentStatus]);
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx
index 45da7d081333e..431d793d6e68a 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx
@@ -13,9 +13,9 @@ import {
   EuiSpacer,
   EuiButtonEmpty,
 } from '@elastic/eui';
-import { isEqual } from 'lodash/fp';
 import React, { FC, memo, useCallback, useEffect, useState } from 'react';
 import styled from 'styled-components';
+import deepEqual from 'fast-deep-equal';
 
 import { setFieldValue } from '../../helpers';
 import { RuleStepProps, RuleStep, AboutStepRule } from '../../types';
@@ -103,7 +103,7 @@ const StepAboutRuleComponent: FC<StepAboutRuleProps> = ({
 
   useEffect(() => {
     const { isNew, ...initDefaultValue } = myStepData;
-    if (defaultValues != null && !isEqual(initDefaultValue, defaultValues)) {
+    if (defaultValues != null && !deepEqual(initDefaultValue, defaultValues)) {
       const myDefaultValues = {
         ...defaultValues,
         isNew: false,
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx
index 920a9f2dfe56c..773eb44efb26c 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx
@@ -11,9 +11,10 @@ import {
   EuiFlexItem,
   EuiButton,
 } from '@elastic/eui';
-import { isEmpty, isEqual } from 'lodash/fp';
+import { isEmpty } from 'lodash/fp';
 import React, { FC, memo, useCallback, useState, useEffect } from 'react';
 import styled from 'styled-components';
+import deepEqual from 'fast-deep-equal';
 
 import { IIndexPattern } from '../../../../../../../../../../src/plugins/data/public';
 import { useFetchIndexPatterns } from '../../../../../containers/detection_engine/rules';
@@ -126,9 +127,9 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({
   useEffect(() => {
     if (indicesConfig != null && defaultValues != null) {
       const myDefaultValues = getStepDefaultValue(indicesConfig, defaultValues);
-      if (!isEqual(myDefaultValues, myStepData)) {
+      if (!deepEqual(myDefaultValues, myStepData)) {
         setMyStepData(myDefaultValues);
-        setLocalUseIndicesConfig(isEqual(myDefaultValues.index, indicesConfig));
+        setLocalUseIndicesConfig(deepEqual(myDefaultValues.index, indicesConfig));
         setFieldValue(form, schema, myDefaultValues);
       }
     }
@@ -212,13 +213,13 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({
           <FormDataProvider pathsToWatch="index">
             {({ index }) => {
               if (index != null) {
-                if (isEqual(index, indicesConfig) && !localUseIndicesConfig) {
+                if (deepEqual(index, indicesConfig) && !localUseIndicesConfig) {
                   setLocalUseIndicesConfig(true);
                 }
-                if (!isEqual(index, indicesConfig) && localUseIndicesConfig) {
+                if (!deepEqual(index, indicesConfig) && localUseIndicesConfig) {
                   setLocalUseIndicesConfig(false);
                 }
-                if (index != null && !isEmpty(index) && !isEqual(index, mylocalIndicesConfig)) {
+                if (index != null && !isEmpty(index) && !deepEqual(index, mylocalIndicesConfig)) {
                   setMyLocalIndicesConfig(index);
                 }
               }
diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_schedule_rule/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_schedule_rule/index.tsx
index cfbb0a622c721..2e2c7e068dd85 100644
--- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_schedule_rule/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_schedule_rule/index.tsx
@@ -5,8 +5,8 @@
  */
 
 import { EuiHorizontalRule, EuiFlexGroup, EuiFlexItem, EuiButton } from '@elastic/eui';
-import { isEqual } from 'lodash/fp';
 import React, { FC, memo, useCallback, useEffect, useState } from 'react';
+import deepEqual from 'fast-deep-equal';
 
 import { setFieldValue } from '../../helpers';
 import { RuleStep, RuleStepProps, ScheduleStepRule } from '../../types';
@@ -62,7 +62,7 @@ const StepScheduleRuleComponent: FC<StepScheduleRuleProps> = ({
 
   useEffect(() => {
     const { isNew, ...initDefaultValue } = myStepData;
-    if (defaultValues != null && !isEqual(initDefaultValue, defaultValues)) {
+    if (defaultValues != null && !deepEqual(initDefaultValue, defaultValues)) {
       const myDefaultValues = {
         ...defaultValues,
         isNew: false,
diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.tsx
index 41eb620850a7f..f5efd9248029d 100644
--- a/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.tsx
@@ -73,32 +73,25 @@ export const HostDetailsTabs = React.memo<HostDetailsTabsProps>(
 
     return (
       <Switch>
-        <Route
-          path={`${hostDetailsPagePath}/:tabName(${HostsTableType.authentications})`}
-          render={() => <AuthenticationsQueryTabBody {...tabProps} />}
-        />
-        <Route
-          path={`${hostDetailsPagePath}/:tabName(${HostsTableType.hosts})`}
-          render={() => <HostsQueryTabBody {...tabProps} />}
-        />
-        <Route
-          path={`${hostDetailsPagePath}/:tabName(${HostsTableType.uncommonProcesses})`}
-          render={() => <UncommonProcessQueryTabBody {...tabProps} />}
-        />
-        <Route
-          path={`${hostDetailsPagePath}/:tabName(${HostsTableType.anomalies})`}
-          render={() => (
-            <AnomaliesQueryTabBody {...tabProps} AnomaliesTableComponent={AnomaliesHostTable} />
-          )}
-        />
-        <Route
-          path={`${hostDetailsPagePath}/:tabName(${HostsTableType.events})`}
-          render={() => <EventsQueryTabBody {...tabProps} pageFilters={pageFilters} />}
-        />
-        <Route
-          path={`${hostDetailsPagePath}/:tabName(${HostsTableType.alerts})`}
-          render={() => <HostAlertsQueryTabBody {...tabProps} pageFilters={pageFilters} />}
-        />
+        <Route path={`${hostDetailsPagePath}/:tabName(${HostsTableType.authentications})`}>
+          <AuthenticationsQueryTabBody {...tabProps} />
+        </Route>
+        <Route path={`${hostDetailsPagePath}/:tabName(${HostsTableType.hosts})`}>
+          <HostsQueryTabBody {...tabProps} />
+        </Route>
+        <Route path={`${hostDetailsPagePath}/:tabName(${HostsTableType.uncommonProcesses})`}>
+          <UncommonProcessQueryTabBody {...tabProps} />
+        </Route>
+        <Route path={`${hostDetailsPagePath}/:tabName(${HostsTableType.anomalies})`}>
+          <AnomaliesQueryTabBody {...tabProps} AnomaliesTableComponent={AnomaliesHostTable} />
+        </Route>
+
+        <Route path={`${hostDetailsPagePath}/:tabName(${HostsTableType.events})`}>
+          <EventsQueryTabBody {...tabProps} pageFilters={pageFilters} />
+        </Route>
+        <Route path={`${hostDetailsPagePath}/:tabName(${HostsTableType.alerts})`}>
+          <HostAlertsQueryTabBody {...tabProps} pageFilters={pageFilters} />
+        </Route>
       </Switch>
     );
   }
diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts_tabs.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts_tabs.tsx
index 0b83710a13293..80c35e5563c1d 100644
--- a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts_tabs.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts_tabs.tsx
@@ -4,7 +4,7 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import React, { memo } from 'react';
+import React, { memo, useCallback } from 'react';
 import { Route, Switch } from 'react-router-dom';
 
 import { HostsTabsProps } from './types';
@@ -22,7 +22,7 @@ import {
 } from './navigation';
 import { HostAlertsQueryTabBody } from './navigation/alerts_query_tab_body';
 
-const HostsTabs = memo<HostsTabsProps>(
+export const HostsTabs = memo<HostsTabsProps>(
   ({
     deleteQuery,
     filterQuery,
@@ -44,49 +44,48 @@ const HostsTabs = memo<HostsTabsProps>(
       startDate: from,
       type,
       indexPattern,
-      narrowDateRange: (score: Anomaly, interval: string) => {
-        const fromTo = scoreIntervalToDateTime(score, interval);
-        setAbsoluteRangeDatePicker({
-          id: 'global',
-          from: fromTo.from,
-          to: fromTo.to,
-        });
-      },
+      narrowDateRange: useCallback(
+        (score: Anomaly, interval: string) => {
+          const fromTo = scoreIntervalToDateTime(score, interval);
+          setAbsoluteRangeDatePicker({
+            id: 'global',
+            from: fromTo.from,
+            to: fromTo.to,
+          });
+        },
+        [setAbsoluteRangeDatePicker]
+      ),
+      updateDateRange: useCallback(
+        (min: number, max: number) => {
+          setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max });
+        },
+        [setAbsoluteRangeDatePicker]
+      ),
     };
 
     return (
       <Switch>
-        <Route
-          path={`${hostsPagePath}/:tabName(${HostsTableType.hosts})`}
-          render={() => <HostsQueryTabBody {...tabProps} />}
-        />
-        <Route
-          path={`${hostsPagePath}/:tabName(${HostsTableType.authentications})`}
-          render={() => <AuthenticationsQueryTabBody {...tabProps} />}
-        />
-        <Route
-          path={`${hostsPagePath}/:tabName(${HostsTableType.uncommonProcesses})`}
-          render={() => <UncommonProcessQueryTabBody {...tabProps} />}
-        />
-        <Route
-          path={`${hostsPagePath}/:tabName(${HostsTableType.anomalies})`}
-          render={() => (
-            <AnomaliesQueryTabBody {...tabProps} AnomaliesTableComponent={AnomaliesHostTable} />
-          )}
-        />
-        <Route
-          path={`${hostsPagePath}/:tabName(${HostsTableType.events})`}
-          render={() => <EventsQueryTabBody {...tabProps} />}
-        />
-        <Route
-          path={`${hostsPagePath}/:tabName(${HostsTableType.alerts})`}
-          render={() => <HostAlertsQueryTabBody {...tabProps} />}
-        />
+        <Route path={`${hostsPagePath}/:tabName(${HostsTableType.hosts})`}>
+          <HostsQueryTabBody {...tabProps} />
+        </Route>
+        <Route path={`${hostsPagePath}/:tabName(${HostsTableType.authentications})`}>
+          <AuthenticationsQueryTabBody {...tabProps} />
+        </Route>
+        <Route path={`${hostsPagePath}/:tabName(${HostsTableType.uncommonProcesses})`}>
+          <UncommonProcessQueryTabBody {...tabProps} />
+        </Route>
+        <Route path={`${hostsPagePath}/:tabName(${HostsTableType.anomalies})`}>
+          <AnomaliesQueryTabBody {...tabProps} AnomaliesTableComponent={AnomaliesHostTable} />
+        </Route>
+        <Route path={`${hostsPagePath}/:tabName(${HostsTableType.events})`}>
+          <EventsQueryTabBody {...tabProps} />
+        </Route>
+        <Route path={`${hostsPagePath}/:tabName(${HostsTableType.alerts})`}>
+          <HostAlertsQueryTabBody {...tabProps} />
+        </Route>
       </Switch>
     );
   }
 );
 
 HostsTabs.displayName = 'HostsTabs';
-
-export { HostsTabs };
diff --git a/x-pack/legacy/plugins/siem/public/pages/network/navigation/network_routes.tsx b/x-pack/legacy/plugins/siem/public/pages/network/navigation/network_routes.tsx
index 23a619db97ee4..b6b54b68ac06a 100644
--- a/x-pack/legacy/plugins/siem/public/pages/network/navigation/network_routes.tsx
+++ b/x-pack/legacy/plugins/siem/public/pages/network/navigation/network_routes.tsx
@@ -23,77 +23,82 @@ import { TlsQueryTabBody } from './tls_query_tab_body';
 import { Anomaly } from '../../../components/ml/types';
 import { NetworkAlertsQueryTabBody } from './alerts_query_tab_body';
 
-export const NetworkRoutes = ({
-  networkPagePath,
-  type,
-  to,
-  filterQuery,
-  isInitializing,
-  from,
-  indexPattern,
-  setQuery,
-  setAbsoluteRangeDatePicker,
-}: NetworkRoutesProps) => {
-  const narrowDateRange = useCallback(
-    (score: Anomaly, interval: string) => {
-      const fromTo = scoreIntervalToDateTime(score, interval);
-      setAbsoluteRangeDatePicker({
-        id: 'global',
-        from: fromTo.from,
-        to: fromTo.to,
-      });
-    },
-    [setAbsoluteRangeDatePicker]
-  );
+export const NetworkRoutes = React.memo<NetworkRoutesProps>(
+  ({
+    networkPagePath,
+    type,
+    to,
+    filterQuery,
+    isInitializing,
+    from,
+    indexPattern,
+    setQuery,
+    setAbsoluteRangeDatePicker,
+  }) => {
+    const narrowDateRange = useCallback(
+      (score: Anomaly, interval: string) => {
+        const fromTo = scoreIntervalToDateTime(score, interval);
+        setAbsoluteRangeDatePicker({
+          id: 'global',
+          from: fromTo.from,
+          to: fromTo.to,
+        });
+      },
+      [setAbsoluteRangeDatePicker]
+    );
+    const updateDateRange = useCallback(
+      (min: number, max: number) => {
+        setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max });
+      },
+      [setAbsoluteRangeDatePicker]
+    );
 
-  const networkAnomaliesFilterQuery = {
-    bool: {
-      should: [
-        {
-          exists: {
-            field: 'source.ip',
+    const networkAnomaliesFilterQuery = {
+      bool: {
+        should: [
+          {
+            exists: {
+              field: 'source.ip',
+            },
           },
-        },
-        {
-          exists: {
-            field: 'destination.ip',
+          {
+            exists: {
+              field: 'destination.ip',
+            },
           },
-        },
-      ],
-      minimum_should_match: 1,
-    },
-  };
+        ],
+        minimum_should_match: 1,
+      },
+    };
 
-  const commonProps = {
-    startDate: from,
-    endDate: to,
-    skip: isInitializing,
-    type,
-    narrowDateRange,
-    setQuery,
-    filterQuery,
-  };
+    const commonProps = {
+      startDate: from,
+      endDate: to,
+      skip: isInitializing,
+      type,
+      narrowDateRange,
+      setQuery,
+      filterQuery,
+    };
 
-  const tabProps = {
-    ...commonProps,
-    indexPattern,
-  };
+    const tabProps = {
+      ...commonProps,
+      indexPattern,
+      updateDateRange,
+    };
 
-  const anomaliesProps = {
-    ...commonProps,
-    anomaliesFilterQuery: networkAnomaliesFilterQuery,
-    AnomaliesTableComponent: AnomaliesNetworkTable,
-  };
+    const anomaliesProps = {
+      ...commonProps,
+      anomaliesFilterQuery: networkAnomaliesFilterQuery,
+      AnomaliesTableComponent: AnomaliesNetworkTable,
+    };
 
-  return (
-    <Switch>
-      <Route
-        path={`${networkPagePath}/:tabName(${NetworkRouteType.dns})`}
-        render={() => <DnsQueryTabBody {...tabProps} />}
-      />
-      <Route
-        path={`${networkPagePath}/:tabName(${NetworkRouteType.flows})`}
-        render={() => (
+    return (
+      <Switch>
+        <Route path={`${networkPagePath}/:tabName(${NetworkRouteType.dns})`}>
+          <DnsQueryTabBody {...tabProps} />
+        </Route>
+        <Route path={`${networkPagePath}/:tabName(${NetworkRouteType.flows})`}>
           <>
             <ConditionalFlexGroup direction="column">
               <EuiFlexItem>
@@ -118,31 +123,25 @@ export const NetworkRoutes = ({
               </EuiFlexItem>
             </ConditionalFlexGroup>
           </>
-        )}
-      />
-      <Route
-        path={`${networkPagePath}/:tabName(${NetworkRouteType.http})`}
-        render={() => <HttpQueryTabBody {...tabProps} />}
-      />
-      <Route
-        path={`${networkPagePath}/:tabName(${NetworkRouteType.tls})`}
-        render={() => <TlsQueryTabBody {...tabProps} flowTarget={FlowTargetSourceDest.source} />}
-      />
-      <Route
-        path={`${networkPagePath}/:tabName(${NetworkRouteType.anomalies})`}
-        render={() => (
+        </Route>
+        <Route path={`${networkPagePath}/:tabName(${NetworkRouteType.http})`}>
+          <HttpQueryTabBody {...tabProps} />
+        </Route>
+        <Route path={`${networkPagePath}/:tabName(${NetworkRouteType.tls})`}>
+          <TlsQueryTabBody {...tabProps} flowTarget={FlowTargetSourceDest.source} />
+        </Route>
+        <Route path={`${networkPagePath}/:tabName(${NetworkRouteType.anomalies})`}>
           <AnomaliesQueryTabBody
             {...anomaliesProps}
             AnomaliesTableComponent={AnomaliesNetworkTable}
           />
-        )}
-      />
-      <Route
-        path={`${networkPagePath}/:tabName(${NetworkRouteType.alerts})`}
-        render={() => <NetworkAlertsQueryTabBody {...tabProps} />}
-      />
-    </Switch>
-  );
-};
+        </Route>
+        <Route path={`${networkPagePath}/:tabName(${NetworkRouteType.alerts})`}>
+          <NetworkAlertsQueryTabBody {...tabProps} />
+        </Route>
+      </Switch>
+    );
+  }
+);
 
 NetworkRoutes.displayName = 'NetworkRoutes';
diff --git a/x-pack/legacy/plugins/siem/public/routes.tsx b/x-pack/legacy/plugins/siem/public/routes.tsx
index cbb58a473e8ea..a989fa9873435 100644
--- a/x-pack/legacy/plugins/siem/public/routes.tsx
+++ b/x-pack/legacy/plugins/siem/public/routes.tsx
@@ -20,8 +20,12 @@ const PageRouterComponent: FC<RouterProps> = ({ history }) => (
   <ManageRoutesSpy>
     <Router history={history}>
       <Switch>
-        <Route path="/" render={() => <HomePage />} />
-        <Route render={() => <NotFoundPage />} />
+        <Route path="/">
+          <HomePage />
+        </Route>
+        <Route>
+          <NotFoundPage />
+        </Route>
       </Switch>
     </Router>
   </ManageRoutesSpy>
diff --git a/x-pack/legacy/plugins/siem/public/utils/kql/use_update_kql.tsx b/x-pack/legacy/plugins/siem/public/utils/kql/use_update_kql.tsx
index 213b881bd2084..af993588f7e0d 100644
--- a/x-pack/legacy/plugins/siem/public/utils/kql/use_update_kql.tsx
+++ b/x-pack/legacy/plugins/siem/public/utils/kql/use_update_kql.tsx
@@ -4,9 +4,9 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 
-import { isEqual } from 'lodash/fp';
 import { Dispatch } from 'redux';
 import { IIndexPattern } from 'src/plugins/data/public';
+import deepEqual from 'fast-deep-equal';
 
 import { KueryFilterQuery } from '../../store';
 import { applyKqlFilterQuery as dispatchApplyTimelineFilterQuery } from '../../store/timeline/actions';
@@ -29,7 +29,7 @@ export const useUpdateKql = ({
   timelineId,
 }: UseUpdateKqlProps): RefetchKql => {
   const updateKql: RefetchKql = (dispatch: Dispatch) => {
-    if (kueryFilterQueryDraft != null && !isEqual(kueryFilterQuery, kueryFilterQueryDraft)) {
+    if (kueryFilterQueryDraft != null && !deepEqual(kueryFilterQuery, kueryFilterQueryDraft)) {
       if (storeType === 'timelineType' && timelineId != null) {
         dispatch(
           dispatchApplyTimelineFilterQuery({
diff --git a/x-pack/legacy/plugins/siem/public/utils/route/spy_routes.tsx b/x-pack/legacy/plugins/siem/public/utils/route/spy_routes.tsx
index c88562abef6ae..ddee2359b28ba 100644
--- a/x-pack/legacy/plugins/siem/public/utils/route/spy_routes.tsx
+++ b/x-pack/legacy/plugins/siem/public/utils/route/spy_routes.tsx
@@ -5,7 +5,6 @@
  */
 
 import * as H from 'history';
-import { isEqual } from 'lodash/fp';
 import { memo, useEffect, useState } from 'react';
 import { withRouter } from 'react-router-dom';
 import deepEqual from 'fast-deep-equal';
@@ -35,7 +34,7 @@ export const SpyRouteComponent = memo<SpyRouteProps & { location: H.Location }>(
       }
     }, [search]);
     useEffect(() => {
-      if (pageName && !isEqual(route.pathName, pathname)) {
+      if (pageName && !deepEqual(route.pathName, pathname)) {
         if (isInitializing && detailName == null) {
           dispatch({
             type: 'updateRouteWithOutSearch',
diff --git a/x-pack/package.json b/x-pack/package.json
index f76b0182ea228..b8fe0326903b6 100644
--- a/x-pack/package.json
+++ b/x-pack/package.json
@@ -106,6 +106,7 @@
     "@types/uuid": "^3.4.4",
     "@types/xml-crypto": "^1.4.0",
     "@types/xml2js": "^0.4.5",
+    "@welldone-software/why-did-you-render": "^4.0.0",
     "abab": "^1.0.4",
     "axios": "^0.19.0",
     "babel-jest": "^24.9.0",
diff --git a/x-pack/tsconfig.json b/x-pack/tsconfig.json
index 978271166cc05..723da7cef6a77 100644
--- a/x-pack/tsconfig.json
+++ b/x-pack/tsconfig.json
@@ -36,7 +36,7 @@
         "x-pack/test_utils/*"
       ],
       "plugins/*": ["src/legacy/core_plugins/*/public/"],
-      "fixtures/*": ["src/fixtures/*"]
+      "fixtures/*": ["src/fixtures/*"],
     },
     "types": [
       "node",
diff --git a/yarn.lock b/yarn.lock
index f46e869909e2e..7906f363813b8 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5684,6 +5684,13 @@
     text-table "^0.2.0"
     webpack-log "^1.1.2"
 
+"@welldone-software/why-did-you-render@^4.0.0":
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/@welldone-software/why-did-you-render/-/why-did-you-render-4.0.0.tgz#cc98c996f5a06ea55bd07dc99ba4b4d68af93332"
+  integrity sha512-PjqriZ8Ak9biP2+kOcIrg+NwsFwWVhGV03Hm+ns84YBCArn+hWBKM9rMBEU6e62I1qyrYF2/G9yktNpEmfWfJA==
+  dependencies:
+    lodash "^4"
+
 "@wry/context@^0.4.0":
   version "0.4.1"
   resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.4.1.tgz#b3e23ca036035cbad0bd9711269352dd03a6fe3c"
@@ -19767,7 +19774,7 @@ lodash.uniqby@^4.7.0:
   resolved "https://registry.yarnpkg.com/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz#d99c07a669e9e6d24e1362dfe266c67616af1302"
   integrity sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI=
 
-lodash@4.17.11, lodash@4.17.15, lodash@>4.17.4, lodash@^4.0.0, lodash@^4.0.1, lodash@^4.10.0, lodash@^4.11.1, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.0, lodash@^4.6.1, lodash@~4.17.10, lodash@~4.17.15, lodash@~4.17.5:
+lodash@4.17.11, lodash@4.17.15, lodash@>4.17.4, lodash@^4, lodash@^4.0.0, lodash@^4.0.1, lodash@^4.10.0, lodash@^4.11.1, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.0, lodash@^4.6.1, lodash@~4.17.10, lodash@~4.17.15, lodash@~4.17.5:
   version "4.17.15"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
   integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==