diff --git a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts index 4ed241f3b9b2ec..37d8a4f5eb8aea 100644 --- a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts +++ b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts @@ -152,7 +152,7 @@ export class OptimizerConfig { new Bundle({ type: 'entry', id: 'core', - entry: './public/entry_point', + entry: './public/index', sourceRoot: options.repoRoot, contextDir: Path.resolve(options.repoRoot, 'src/core'), outputDir: Path.resolve(options.repoRoot, 'src/core/target/public'), diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts index 763f1d515804f9..dd003af7dc5e94 100644 --- a/packages/kbn-optimizer/src/worker/webpack.config.ts +++ b/packages/kbn-optimizer/src/worker/webpack.config.ts @@ -38,11 +38,32 @@ const IS_CODE_COVERAGE = !!process.env.CODE_COVERAGE; const ISTANBUL_PRESET_PATH = require.resolve('@kbn/babel-preset/istanbul_preset'); const BABEL_PRESET_PATH = require.resolve('@kbn/babel-preset/webpack_preset'); -const STATIC_BUNDLE_PLUGINS = [ - { id: 'data', dirname: 'data' }, - { id: 'kibanaReact', dirname: 'kibana_react' }, - { id: 'kibanaUtils', dirname: 'kibana_utils' }, - { id: 'esUiShared', dirname: 'es_ui_shared' }, +const SHARED_BUNDLES = [ + { + type: 'entry', + id: 'core', + rootRelativeDir: 'src/core/public', + }, + { + type: 'plugin', + id: 'data', + rootRelativeDir: 'src/plugins/data/public', + }, + { + type: 'plugin', + id: 'kibanaReact', + rootRelativeDir: 'src/plugins/kibana_react/public', + }, + { + type: 'plugin', + id: 'kibanaUtils', + rootRelativeDir: 'src/plugins/kibana_utils/public', + }, + { + type: 'plugin', + id: 'esUiShared', + rootRelativeDir: 'src/plugins/es_ui_shared/public', + }, ]; /** @@ -57,18 +78,8 @@ const STATIC_BUNDLE_PLUGINS = [ * @param request the request for a module from a commonjs require() call or import statement */ function dynamicExternals(bundle: Bundle, context: string, request: string) { - // ignore imports that have loaders defined - if (request.includes('!')) { - return; - } - - // ignore requests that don't include a /{dirname}/public for one of our - // "static" bundles as a cheap way to avoid doing path resolution - // for paths that couldn't possibly resolve to what we're looking for - const reqToStaticBundle = STATIC_BUNDLE_PLUGINS.some((p) => - request.includes(`/${p.dirname}/public`) - ); - if (!reqToStaticBundle) { + // ignore imports that have loaders defined or are not relative seeming + if (request.includes('!') || !request.startsWith('.')) { return; } @@ -76,10 +87,15 @@ function dynamicExternals(bundle: Bundle, context: string, request: string) { const rootRelative = normalizePath( Path.relative(bundle.sourceRoot, Path.resolve(context, request)) ); - for (const { id, dirname } of STATIC_BUNDLE_PLUGINS) { - if (rootRelative === `src/plugins/${dirname}/public`) { - return `__kbnBundles__['plugin/${id}']`; + for (const sharedBundle of SHARED_BUNDLES) { + if ( + rootRelative !== sharedBundle.rootRelativeDir || + `${bundle.type}/${bundle.id}` === `${sharedBundle.type}/${sharedBundle.id}` + ) { + continue; } + + return `__kbnBundles__['${sharedBundle.type}/${sharedBundle.id}']`; } // import doesn't match a root public import @@ -112,13 +128,9 @@ export function getWebpackConfig(bundle: Bundle, worker: WorkerConfig) { info.absoluteResourcePath )}${info.query}`, jsonpFunction: `${bundle.id}_bundle_jsonpfunction`, - ...(bundle.type === 'plugin' - ? { - // When the entry point is loaded, assign it's exported `plugin` - // value to a key on the global `__kbnBundles__` object. - library: ['__kbnBundles__', `plugin/${bundle.id}`], - } - : {}), + // When the entry point is loaded, assign it's default export + // to a key on the global `__kbnBundles__` object. + library: ['__kbnBundles__', `${bundle.type}/${bundle.id}`], }, optimization: { diff --git a/src/core/public/chrome/nav_links/to_nav_link.ts b/src/core/public/chrome/nav_links/to_nav_link.ts index b8f97f9ddc0053..2dedbfd5f36ace 100644 --- a/src/core/public/chrome/nav_links/to_nav_link.ts +++ b/src/core/public/chrome/nav_links/to_nav_link.ts @@ -42,9 +42,7 @@ export function toNavLink( legacy: isLegacyApp(app), baseUrl, ...(isLegacyApp(app) - ? { - href: url && !url.startsWith(app.subUrlBase!) ? url : baseUrl, - } + ? {} : { href: url, url, diff --git a/src/core/public/chrome/ui/header/nav_link.tsx b/src/core/public/chrome/ui/header/nav_link.tsx index c09b15fac9bdb5..969b6728e0263f 100644 --- a/src/core/public/chrome/ui/header/nav_link.tsx +++ b/src/core/public/chrome/ui/header/nav_link.tsx @@ -55,7 +55,12 @@ export function createEuiListItem({ navigateToApp, dataTestSubj, }: Props) { - const { legacy, active, id, title, disabled, euiIconType, icon, tooltip, href } = link; + const { legacy, active, id, title, disabled, euiIconType, icon, tooltip } = link; + let { href } = link; + + if (legacy) { + href = link.url && !active ? link.url : link.baseUrl; + } return { label: tooltip ?? title, diff --git a/src/core/public/index.ts b/src/core/public/index.ts index aa037329b1b6ea..bd275ca1d4565c 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -35,6 +35,8 @@ * @packageDocumentation */ +import './index.scss'; + import { ChromeBadge, ChromeBrand, @@ -363,3 +365,5 @@ export { UiSettingsState, NavType, }; + +export { __kbnBootstrap__ } from './kbn_bootstrap'; diff --git a/src/core/public/entry_point.ts b/src/core/public/kbn_bootstrap.ts similarity index 51% rename from src/core/public/entry_point.ts rename to src/core/public/kbn_bootstrap.ts index 25180c13ccbd4e..caeb95a540de30 100644 --- a/src/core/public/entry_point.ts +++ b/src/core/public/kbn_bootstrap.ts @@ -25,39 +25,41 @@ * src/legacy/ui/ui_bundles/app_entry_template.js */ -import './index.scss'; import { i18n } from '@kbn/i18n'; import { CoreSystem } from './core_system'; -const injectedMetadata = JSON.parse( - document.querySelector('kbn-injected-metadata')!.getAttribute('data')! -); +/** @internal */ +export function __kbnBootstrap__() { + const injectedMetadata = JSON.parse( + document.querySelector('kbn-injected-metadata')!.getAttribute('data')! + ); -/** - * `apmConfig` would be populated with relavant APM RUM agent - * configuration if server is started with `ELASTIC_APM_ACTIVE=true` - */ -if (process.env.IS_KIBANA_DISTRIBUTABLE !== 'true' && injectedMetadata.vars.apmConfig != null) { - // @ts-ignore - // eslint-disable-next-line @typescript-eslint/no-var-requires - const { init } = require('@elastic/apm-rum'); - init(injectedMetadata.vars.apmConfig); -} + /** + * `apmConfig` would be populated with relavant APM RUM agent + * configuration if server is started with `ELASTIC_APM_ACTIVE=true` + */ + if (process.env.IS_KIBANA_DISTRIBUTABLE !== 'true' && injectedMetadata.vars.apmConfig != null) { + // @ts-ignore + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { init } = require('@elastic/apm-rum'); + init(injectedMetadata.vars.apmConfig); + } -i18n - .load(injectedMetadata.i18n.translationsUrl) - .catch((e) => e) - .then(async (i18nError) => { - const coreSystem = new CoreSystem({ - injectedMetadata, - rootDomElement: document.body, - browserSupportsCsp: !(window as any).__kbnCspNotEnforced__, - }); + i18n + .load(injectedMetadata.i18n.translationsUrl) + .catch((e) => e) + .then(async (i18nError) => { + const coreSystem = new CoreSystem({ + injectedMetadata, + rootDomElement: document.body, + browserSupportsCsp: !(window as any).__kbnCspNotEnforced__, + }); - const setup = await coreSystem.setup(); - if (i18nError && setup) { - setup.fatalErrors.add(i18nError); - } + const setup = await coreSystem.setup(); + if (i18nError && setup) { + setup.fatalErrors.add(i18nError); + } - await coreSystem.start(); - }); + await coreSystem.start(); + }); +} diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index bae0f9a2281cf1..74c41d010ca8db 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -25,6 +25,9 @@ import { Type } from '@kbn/config-schema'; import { UnregisterCallback } from 'history'; import { UserProvidedValues as UserProvidedValues_2 } from 'src/core/server/types'; +// @internal (undocumented) +export function __kbnBootstrap__(): void; + // @public export interface App extends AppBase { appRoute?: string; diff --git a/src/dev/code_coverage/ingest_coverage/integration_tests/ingest_coverage.test.js b/src/dev/code_coverage/ingest_coverage/integration_tests/ingest_coverage.test.js deleted file mode 100644 index 03126d130e9849..00000000000000 --- a/src/dev/code_coverage/ingest_coverage/integration_tests/ingest_coverage.test.js +++ /dev/null @@ -1,72 +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 { resolve } from 'path'; -import execa from 'execa'; -import expect from '@kbn/expect'; - -const ROOT_DIR = resolve(__dirname, '../../../../..'); -const MOCKS_DIR = resolve(__dirname, './mocks'); -const env = { - BUILD_ID: 407, - CI_RUN_URL: 'https://kibana-ci.elastic.co/job/elastic+kibana+code-coverage/407/', - STATIC_SITE_URL_BASE: 'https://kibana-coverage.elastic.dev', - TIME_STAMP: '2020-03-02T21:11:47Z', - ES_HOST: 'https://super:changeme@some.fake.host:9243', - NODE_ENV: 'integration_test', - COVERAGE_INGESTION_KIBANA_ROOT: '/var/lib/jenkins/workspace/elastic+kibana+code-coverage/kibana', -}; -const verboseArgs = [ - 'scripts/ingest_coverage.js', - '--verbose', - '--vcsInfoPath', - 'src/dev/code_coverage/ingest_coverage/integration_tests/mocks/VCS_INFO.txt', - '--path', -]; - -// FLAKY: https://github.com/elastic/kibana/issues/67554 -// FLAKY: https://github.com/elastic/kibana/issues/67555 -// FLAKY: https://github.com/elastic/kibana/issues/67556 -describe.skip('Ingesting coverage', () => { - const summaryPath = 'jest-combined/coverage-summary-manual-mix.json'; - const resolved = resolve(MOCKS_DIR, summaryPath); - const siteUrlRegex = /"staticSiteUrl": (".+",)/; - let actualUrl = ''; - - beforeAll(async () => { - const opts = [...verboseArgs, resolved]; - const { stdout } = await execa(process.execPath, opts, { cwd: ROOT_DIR, env }); - actualUrl = siteUrlRegex.exec(stdout)[1]; - }); - - describe(`staticSiteUrl`, () => { - it('should contain the static host', () => { - const staticHost = /https:\/\/kibana-coverage\.elastic\.dev/; - expect(staticHost.test(actualUrl)).ok(); - }); - it('should contain the timestamp', () => { - const timeStamp = /\d{4}-\d{2}-\d{2}T\d{2}.*\d{2}.*\d{2}Z/; - expect(timeStamp.test(actualUrl)).ok(); - }); - it('should contain the folder structure', () => { - const folderStructure = /(?:.*|.*-combined)\//; - expect(folderStructure.test(actualUrl)).ok(); - }); - }); -}); diff --git a/src/dev/typescript/run_type_check_cli.ts b/src/dev/typescript/run_type_check_cli.ts index 1417d304846781..5d4cf14c1cd954 100644 --- a/src/dev/typescript/run_type_check_cli.ts +++ b/src/dev/typescript/run_type_check_cli.ts @@ -88,7 +88,7 @@ export function runTypeCheckCli() { } execInProjects(log, projects, process.execPath, (project) => [ - ...(project.name === 'x-pack' ? ['--max-old-space-size=4096'] : []), + ...(project.name.startsWith('x-pack') ? ['--max-old-space-size=4096'] : []), require.resolve('typescript/bin/tsc'), ...['--project', project.tsConfigPath], ...tscArgs, diff --git a/src/legacy/ui/ui_render/bootstrap/template.js.hbs b/src/legacy/ui/ui_render/bootstrap/template.js.hbs index 1453c974c11809..e8f05b46f70611 100644 --- a/src/legacy/ui/ui_render/bootstrap/template.js.hbs +++ b/src/legacy/ui/ui_render/bootstrap/template.js.hbs @@ -73,12 +73,23 @@ if (window.__kbnStrictCsp__ && window.__kbnCspNotEnforced__) { } load([ - {{#each jsDependencyPaths}} - '{{this}}', - {{/each}} + {{#each jsDependencyPaths}} + '{{this}}', + {{/each}} ], function () { + {{#unless legacyBundlePath}} + if (!__kbnBundles__ || !__kbnBundles__['entry/core'] || typeof __kbnBundles__['entry/core'].__kbnBootstrap__ !== 'function') { + console.error('entry/core bundle did not load correctly'); + failure(); + } else { + __kbnBundles__['entry/core'].__kbnBootstrap__() + } + {{/unless}} + load([ - '{{entryBundlePath}}', + {{#if legacyBundlePath}} + '{{legacyBundlePath}}', + {{/if}} {{#each styleSheetPaths}} '{{this}}', {{/each}} diff --git a/src/legacy/ui/ui_render/ui_render_mixin.js b/src/legacy/ui/ui_render/ui_render_mixin.js index 10847b99285282..b09d4861b343ba 100644 --- a/src/legacy/ui/ui_render/ui_render_mixin.js +++ b/src/legacy/ui/ui_render/ui_render_mixin.js @@ -173,6 +173,7 @@ export function uiRenderMixin(kbnServer, server, config) { `${regularBundlePath}/commons.bundle.js`, ]), + `${regularBundlePath}/core/core.entry.js`, ...kpPluginIds.map( (pluginId) => `${regularBundlePath}/plugin/${pluginId}/${pluginId}.plugin.js` ), @@ -199,9 +200,7 @@ export function uiRenderMixin(kbnServer, server, config) { jsDependencyPaths, styleSheetPaths, publicPathMap, - entryBundlePath: isCore - ? `${regularBundlePath}/core/core.entry.js` - : `${regularBundlePath}/${app.getId()}.bundle.js`, + legacyBundlePath: isCore ? undefined : `${regularBundlePath}/${app.getId()}.bundle.js`, }, }); diff --git a/src/plugins/bfetch/public/batching/create_streaming_batched_function.test.ts b/src/plugins/bfetch/public/batching/create_streaming_batched_function.test.ts index 26c1d9e5033e01..da6c940c48d0ae 100644 --- a/src/plugins/bfetch/public/batching/create_streaming_batched_function.test.ts +++ b/src/plugins/bfetch/public/batching/create_streaming_batched_function.test.ts @@ -303,6 +303,47 @@ describe('createStreamingBatchedFunction()', () => { expect(await promise3).toEqual({ foo: 'bar 2' }); }); + test('resolves falsy results', async () => { + const { fetchStreaming, stream } = setup(); + const fn = createStreamingBatchedFunction({ + url: '/test', + fetchStreaming, + maxItemAge: 5, + flushOnMaxItems: 3, + }); + + const promise1 = fn({ a: '1' }); + const promise2 = fn({ b: '2' }); + const promise3 = fn({ c: '3' }); + await new Promise((r) => setTimeout(r, 6)); + + stream.next( + JSON.stringify({ + id: 0, + result: false, + }) + '\n' + ); + stream.next( + JSON.stringify({ + id: 1, + result: 0, + }) + '\n' + ); + stream.next( + JSON.stringify({ + id: 2, + result: '', + }) + '\n' + ); + + expect(await isPending(promise1)).toBe(false); + expect(await isPending(promise2)).toBe(false); + expect(await isPending(promise3)).toBe(false); + expect(await promise1).toEqual(false); + expect(await promise2).toEqual(0); + expect(await promise3).toEqual(''); + }); + test('rejects promise on error response', async () => { const { fetchStreaming, stream } = setup(); const fn = createStreamingBatchedFunction({ diff --git a/src/plugins/bfetch/public/batching/create_streaming_batched_function.ts b/src/plugins/bfetch/public/batching/create_streaming_batched_function.ts index f80a97137d1ab2..89793fff6b3259 100644 --- a/src/plugins/bfetch/public/batching/create_streaming_batched_function.ts +++ b/src/plugins/bfetch/public/batching/create_streaming_batched_function.ts @@ -106,7 +106,7 @@ export const createStreamingBatchedFunction = ( if (response.error) { responsesReceived++; items[response.id].future.reject(response.error); - } else if (response.result) { + } else if (response.result !== undefined) { responsesReceived++; items[response.id].future.resolve(response.result); } diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index 08740b21f39a44..0de3982039928b 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -228,7 +228,7 @@ export class DashboardPlugin const app: App = { id: DashboardConstants.DASHBOARDS_ID, title: 'Dashboard', - order: -1001, + order: 2500, euiIconType: 'dashboardApp', defaultPath: `#${DashboardConstants.LANDING_PAGE_PATH}`, updater$: this.appStateUpdater, diff --git a/src/plugins/discover/public/plugin.ts b/src/plugins/discover/public/plugin.ts index 5a031872913c04..4323e3d8deda46 100644 --- a/src/plugins/discover/public/plugin.ts +++ b/src/plugins/discover/public/plugin.ts @@ -188,7 +188,7 @@ export class DiscoverPlugin id: 'discover', title: 'Discover', updater$: this.appStateUpdater.asObservable(), - order: -1004, + order: 1000, euiIconType: 'discoverApp', defaultPath: '#/', category: DEFAULT_APP_CATEGORIES.kibana, diff --git a/src/plugins/visualize/public/plugin.ts b/src/plugins/visualize/public/plugin.ts index 8a104dc51feb4b..8a05adc18964a0 100644 --- a/src/plugins/visualize/public/plugin.ts +++ b/src/plugins/visualize/public/plugin.ts @@ -102,7 +102,7 @@ export class VisualizePlugin core.application.register({ id: 'visualize', title: 'Visualize', - order: -1002, + order: 8000, euiIconType: 'visualizeApp', defaultPath: '#/', category: DEFAULT_APP_CATEGORIES.kibana, diff --git a/test/functional/services/dashboard/add_panel.js b/test/functional/services/dashboard/add_panel.ts similarity index 88% rename from test/functional/services/dashboard/add_panel.js rename to test/functional/services/dashboard/add_panel.ts index 62592039821611..1263501aa9c135 100644 --- a/test/functional/services/dashboard/add_panel.js +++ b/test/functional/services/dashboard/add_panel.ts @@ -17,7 +17,9 @@ * under the License. */ -export function DashboardAddPanelProvider({ getService, getPageObjects }) { +import { FtrProviderContext } from '../../ftr_provider_context'; + +export function DashboardAddPanelProvider({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); const retry = getService('retry'); const testSubjects = getService('testSubjects'); @@ -39,7 +41,7 @@ export function DashboardAddPanelProvider({ getService, getPageObjects }) { await PageObjects.common.sleep(500); } - async clickAddNewEmbeddableLink(type) { + async clickAddNewEmbeddableLink(type: string) { await testSubjects.click('createNew'); await testSubjects.click(`createNew-${type}`); await testSubjects.missingOrFail(`createNew-${type}`); @@ -50,7 +52,7 @@ export function DashboardAddPanelProvider({ getService, getPageObjects }) { await testSubjects.click('savedObjectFinderFilterButton'); } - async toggleFilter(type) { + async toggleFilter(type: string) { log.debug(`DashboardAddPanel.addToFilter(${type})`); await this.waitForListLoading(); await this.toggleFilterPopover(); @@ -61,7 +63,7 @@ export function DashboardAddPanelProvider({ getService, getPageObjects }) { async addEveryEmbeddableOnCurrentPage() { log.debug('addEveryEmbeddableOnCurrentPage'); const itemList = await testSubjects.find('savedObjectFinderItemList'); - const embeddableList = []; + const embeddableList: string[] = []; await retry.try(async () => { const embeddableRows = await itemList.findAllByCssSelector('li'); for (let i = 0; i < embeddableRows.length; i++) { @@ -130,7 +132,7 @@ export function DashboardAddPanelProvider({ getService, getPageObjects }) { await flyout.ensureClosed('dashboardAddPanel'); } - async addEveryVisualization(filter) { + async addEveryVisualization(filter: string) { log.debug('DashboardAddPanel.addEveryVisualization'); await this.ensureAddPanelIsShowing(); await this.toggleFilter('visualization'); @@ -138,16 +140,16 @@ export function DashboardAddPanelProvider({ getService, getPageObjects }) { await this.filterEmbeddableNames(filter.replace('-', ' ')); } let morePages = true; - const vizList = []; + const vizList: string[][] = []; while (morePages) { vizList.push(await this.addEveryEmbeddableOnCurrentPage()); morePages = await this.clickPagerNextButton(); } await this.closeAddPanel(); - return vizList.reduce((acc, vizList) => [...acc, ...vizList], []); + return vizList.reduce((acc, list) => [...acc, ...list], []); } - async addEverySavedSearch(filter) { + async addEverySavedSearch(filter: string) { log.debug('DashboardAddPanel.addEverySavedSearch'); await this.ensureAddPanelIsShowing(); await this.toggleFilter('search'); @@ -161,20 +163,20 @@ export function DashboardAddPanelProvider({ getService, getPageObjects }) { morePages = await this.clickPagerNextButton(); } await this.closeAddPanel(); - return searchList.reduce((acc, searchList) => [...acc, ...searchList], []); + return searchList.reduce((acc, list) => [...acc, ...list], []); } - async addSavedSearch(searchName) { + async addSavedSearch(searchName: string) { return this.addEmbeddable(searchName, 'search'); } - async addSavedSearches(searches) { + async addSavedSearches(searches: string[]) { for (const name of searches) { await this.addSavedSearch(name); } } - async addVisualizations(visualizations) { + async addVisualizations(visualizations: string[]) { log.debug('DashboardAddPanel.addVisualizations'); const vizList = []; for (const vizName of visualizations) { @@ -184,11 +186,11 @@ export function DashboardAddPanelProvider({ getService, getPageObjects }) { return vizList; } - async addVisualization(vizName) { + async addVisualization(vizName: string) { return this.addEmbeddable(vizName, 'visualization'); } - async addEmbeddable(embeddableName, embeddableType) { + async addEmbeddable(embeddableName: string, embeddableType: string) { log.debug( `DashboardAddPanel.addEmbeddable, name: ${embeddableName}, type: ${embeddableType}` ); @@ -201,14 +203,14 @@ export function DashboardAddPanelProvider({ getService, getPageObjects }) { return embeddableName; } - async filterEmbeddableNames(name) { + async filterEmbeddableNames(name: string) { // The search input field may be disabled while the table is loading so wait for it await this.waitForListLoading(); await testSubjects.setValue('savedObjectFinderSearchInput', name); await this.waitForListLoading(); } - async panelAddLinkExists(name) { + async panelAddLinkExists(name: string) { log.debug(`DashboardAddPanel.panelAddLinkExists(${name})`); await this.ensureAddPanelIsShowing(); await this.filterEmbeddableNames(`"${name}"`); diff --git a/test/functional/services/dashboard/expectations.js b/test/functional/services/dashboard/expectations.ts similarity index 82% rename from test/functional/services/dashboard/expectations.js rename to test/functional/services/dashboard/expectations.ts index 66073e1043b0d8..77a441a772d84e 100644 --- a/test/functional/services/dashboard/expectations.js +++ b/test/functional/services/dashboard/expectations.ts @@ -18,8 +18,10 @@ */ import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { WebElementWrapper } from '../lib/web_element_wrapper'; -export function DashboardExpectProvider({ getService, getPageObjects }) { +export function DashboardExpectProvider({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); const retry = getService('retry'); const testSubjects = getService('testSubjects'); @@ -29,7 +31,7 @@ export function DashboardExpectProvider({ getService, getPageObjects }) { const findTimeout = 2500; return new (class DashboardExpect { - async panelCount(expectedCount) { + async panelCount(expectedCount: number) { log.debug(`DashboardExpect.panelCount(${expectedCount})`); await retry.try(async () => { const panelCount = await PageObjects.dashboard.getPanelCount(); @@ -37,7 +39,7 @@ export function DashboardExpectProvider({ getService, getPageObjects }) { }); } - async visualizationsArePresent(vizList) { + async visualizationsArePresent(vizList: string[]) { log.debug('Checking all visualisations are present on dashsboard'); let notLoaded = await PageObjects.dashboard.getNotLoadedVisualizations(vizList); // TODO: Determine issue occasionally preventing 'geo map' from loading @@ -45,7 +47,7 @@ export function DashboardExpectProvider({ getService, getPageObjects }) { expect(notLoaded).to.be.empty(); } - async selectedLegendColorCount(color, expectedCount) { + async selectedLegendColorCount(color: string, expectedCount: number) { log.debug(`DashboardExpect.selectedLegendColorCount(${color}, ${expectedCount})`); await retry.try(async () => { const selectedLegendColor = await testSubjects.findAll( @@ -56,7 +58,7 @@ export function DashboardExpectProvider({ getService, getPageObjects }) { }); } - async docTableFieldCount(expectedCount) { + async docTableFieldCount(expectedCount: number) { log.debug(`DashboardExpect.docTableFieldCount(${expectedCount})`); await retry.try(async () => { const docTableCells = await testSubjects.findAll('docTableField', findTimeout); @@ -64,7 +66,7 @@ export function DashboardExpectProvider({ getService, getPageObjects }) { }); } - async fieldSuggestions(expectedFields) { + async fieldSuggestions(expectedFields: string[]) { log.debug(`DashboardExpect.fieldSuggestions(${expectedFields})`); const fields = await filterBar.getFilterEditorFields(); expectedFields.forEach((expectedField) => { @@ -72,7 +74,7 @@ export function DashboardExpectProvider({ getService, getPageObjects }) { }); } - async legendValuesToExist(legendValues) { + async legendValuesToExist(legendValues: string[]) { log.debug(`DashboardExpect.legendValuesToExist(${legendValues})`); await Promise.all( legendValues.map(async (legend) => { @@ -84,11 +86,11 @@ export function DashboardExpectProvider({ getService, getPageObjects }) { ); } - async textWithinElementsExists(texts, getElementsFn) { + async textWithinElementsExists(texts: string[], getElementsFn: Function) { log.debug(`DashboardExpect.textWithinElementsExists(${texts})`); await retry.try(async () => { - const elements = await getElementsFn(); - const elementTexts = []; + const elements: WebElementWrapper[] = await getElementsFn(); + const elementTexts: string[] = []; await Promise.all( elements.map(async (element) => { elementTexts.push(await element.getVisibleText()); @@ -103,23 +105,23 @@ export function DashboardExpectProvider({ getService, getPageObjects }) { }); } - async textWithinTestSubjectsExists(texts, selector) { + async textWithinTestSubjectsExists(texts: string[], selector: string) { log.debug(`DashboardExpect.textWithinTestSubjectsExists(${texts})`); log.debug(`textWithinTestSubjectsExists:(${JSON.stringify(texts)},${selector})`); await this.textWithinElementsExists(texts, async () => await testSubjects.findAll(selector)); } - async textWithinCssElementExists(texts, selector) { + async textWithinCssElementExists(texts: string[], selector: string) { log.debug(`DashboardExpect.textWithinCssElementExists(${texts})`); log.debug(`textWithinCssElementExists:(${JSON.stringify(texts)},${selector})`); await this.textWithinElementsExists(texts, async () => await find.allByCssSelector(selector)); } - async textWithinElementsDoNotExist(texts, getElementsFn) { + async textWithinElementsDoNotExist(texts: string[], getElementsFn: Function) { log.debug(`DashboardExpect.textWithinElementsDoNotExist(${texts})`); await retry.try(async () => { - const elements = await getElementsFn(); - const elementTexts = []; + const elements: WebElementWrapper[] = await getElementsFn(); + const elementTexts: string[] = []; await Promise.all( elements.map(async (element) => { elementTexts.push(await element.getVisibleText()); @@ -133,7 +135,7 @@ export function DashboardExpectProvider({ getService, getPageObjects }) { }); } - async textWithinCssElementDoNotExist(texts, selector) { + async textWithinCssElementDoNotExist(texts: string[], selector: string) { log.debug(`textWithinCssElementExists:(${JSON.stringify(texts)},${selector})`); await this.textWithinElementsDoNotExist( texts, @@ -141,7 +143,7 @@ export function DashboardExpectProvider({ getService, getPageObjects }) { ); } - async timelionLegendCount(expectedCount) { + async timelionLegendCount(expectedCount: number) { log.debug(`DashboardExpect.timelionLegendCount(${expectedCount})`); await retry.try(async () => { const flotLegendLabels = await testSubjects.findAll('flotLegendLabel', findTimeout); @@ -160,7 +162,7 @@ export function DashboardExpectProvider({ getService, getPageObjects }) { expect(tagCloudsHaveContent.indexOf(false)).to.be.greaterThan(-1); } - async tagCloudWithValuesFound(values) { + async tagCloudWithValuesFound(values: string[]) { log.debug(`DashboardExpect.tagCloudWithValuesFound(${values})`); const tagCloudVisualizations = await testSubjects.findAll('tagCloudVisualization'); const matches = await Promise.all( @@ -177,47 +179,47 @@ export function DashboardExpectProvider({ getService, getPageObjects }) { expect(matches.indexOf(true)).to.be.greaterThan(-1); } - async goalAndGuageLabelsExist(labels) { + async goalAndGuageLabelsExist(labels: string[]) { log.debug(`DashboardExpect.goalAndGuageLabelsExist(${labels})`); await this.textWithinCssElementExists(labels, '.chart-label'); } - async metricValuesExist(values) { + async metricValuesExist(values: string[]) { log.debug(`DashboardExpect.metricValuesExist(${values})`); await this.textWithinCssElementExists(values, '.mtrVis__value'); } - async tsvbMetricValuesExist(values) { + async tsvbMetricValuesExist(values: string[]) { log.debug(`DashboardExpect.tsvbMetricValuesExist(${values})`); await this.textWithinTestSubjectsExists(values, 'tsvbMetricValue'); } - async tsvbTopNValuesExist(values) { + async tsvbTopNValuesExist(values: string[]) { log.debug(`DashboardExpect.tsvbTopNValuesExist(${values})`); await this.textWithinTestSubjectsExists(values, 'tsvbTopNValue'); } - async vegaTextsExist(values) { + async vegaTextsExist(values: string[]) { log.debug(`DashboardExpect.vegaTextsExist(${values})`); await this.textWithinCssElementExists(values, '.vgaVis__view text'); } - async vegaTextsDoNotExist(values) { + async vegaTextsDoNotExist(values: string[]) { log.debug(`DashboardExpect.vegaTextsDoNotExist(${values})`); await this.textWithinCssElementDoNotExist(values, '.vgaVis__view text'); } - async tsvbMarkdownWithValuesExists(values) { + async tsvbMarkdownWithValuesExists(values: string[]) { log.debug(`DashboardExpect.tsvbMarkdownWithValuesExists(${values})`); await this.textWithinTestSubjectsExists(values, 'tsvbMarkdown'); } - async markdownWithValuesExists(values) { + async markdownWithValuesExists(values: string[]) { log.debug(`DashboardExpect.markdownWithValuesExists(${values})`); await this.textWithinTestSubjectsExists(values, 'markdownBody'); } - async savedSearchRowCount(expectedCount) { + async savedSearchRowCount(expectedCount: number) { log.debug(`DashboardExpect.savedSearchRowCount(${expectedCount})`); await retry.try(async () => { const savedSearchRows = await testSubjects.findAll( @@ -228,7 +230,7 @@ export function DashboardExpectProvider({ getService, getPageObjects }) { }); } - async dataTableRowCount(expectedCount) { + async dataTableRowCount(expectedCount: number) { log.debug(`DashboardExpect.dataTableRowCount(${expectedCount})`); await retry.try(async () => { const dataTableRows = await find.allByCssSelector( @@ -239,7 +241,7 @@ export function DashboardExpectProvider({ getService, getPageObjects }) { }); } - async seriesElementCount(expectedCount) { + async seriesElementCount(expectedCount: number) { log.debug(`DashboardExpect.seriesElementCount(${expectedCount})`); await retry.try(async () => { const seriesElements = await find.allByCssSelector('.series', findTimeout); @@ -247,7 +249,7 @@ export function DashboardExpectProvider({ getService, getPageObjects }) { }); } - async inputControlItemCount(expectedCount) { + async inputControlItemCount(expectedCount: number) { log.debug(`DashboardExpect.inputControlItemCount(${expectedCount})`); await retry.try(async () => { const inputControlItems = await testSubjects.findAll('inputControlItem'); @@ -255,7 +257,7 @@ export function DashboardExpectProvider({ getService, getPageObjects }) { }); } - async lineChartPointsCount(expectedCount) { + async lineChartPointsCount(expectedCount: number) { log.debug(`DashboardExpect.lineChartPointsCount(${expectedCount})`); await retry.try(async () => { const points = await find.allByCssSelector('.points', findTimeout); @@ -263,7 +265,7 @@ export function DashboardExpectProvider({ getService, getPageObjects }) { }); } - async tsvbTableCellCount(expectedCount) { + async tsvbTableCellCount(expectedCount: number) { log.debug(`DashboardExpect.tsvbTableCellCount(${expectedCount})`); await retry.try(async () => { const tableCells = await testSubjects.findAll('tvbTableVis__value', findTimeout); diff --git a/test/functional/services/dashboard/index.js b/test/functional/services/dashboard/index.ts similarity index 100% rename from test/functional/services/dashboard/index.js rename to test/functional/services/dashboard/index.ts diff --git a/test/functional/services/dashboard/panel_actions.js b/test/functional/services/dashboard/panel_actions.ts similarity index 80% rename from test/functional/services/dashboard/panel_actions.js rename to test/functional/services/dashboard/panel_actions.ts index b155d747f3b939..c9a5dcfba32b11 100644 --- a/test/functional/services/dashboard/panel_actions.js +++ b/test/functional/services/dashboard/panel_actions.ts @@ -17,6 +17,9 @@ * under the License. */ +import { FtrProviderContext } from '../../ftr_provider_context'; +import { WebElementWrapper } from '../lib/web_element_wrapper'; + const REMOVE_PANEL_DATA_TEST_SUBJ = 'embeddablePanelAction-deletePanel'; const EDIT_PANEL_DATA_TEST_SUBJ = 'embeddablePanelAction-editPanel'; const REPLACE_PANEL_DATA_TEST_SUBJ = 'embeddablePanelAction-replacePanel'; @@ -26,13 +29,13 @@ const CUSTOMIZE_PANEL_DATA_TEST_SUBJ = 'embeddablePanelAction-ACTION_CUSTOMIZE_P const OPEN_CONTEXT_MENU_ICON_DATA_TEST_SUBJ = 'embeddablePanelToggleMenuIcon'; const OPEN_INSPECTOR_TEST_SUBJ = 'embeddablePanelAction-openInspector'; -export function DashboardPanelActionsProvider({ getService, getPageObjects }) { +export function DashboardPanelActionsProvider({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); const testSubjects = getService('testSubjects'); const PageObjects = getPageObjects(['header', 'common']); return new (class DashboardPanelActions { - async findContextMenu(parent) { + async findContextMenu(parent?: WebElementWrapper) { return parent ? await testSubjects.findDescendant(OPEN_CONTEXT_MENU_ICON_DATA_TEST_SUBJ, parent) : await testSubjects.find(OPEN_CONTEXT_MENU_ICON_DATA_TEST_SUBJ); @@ -43,7 +46,7 @@ export function DashboardPanelActionsProvider({ getService, getPageObjects }) { return await testSubjects.exists(OPEN_CONTEXT_MENU_ICON_DATA_TEST_SUBJ); } - async toggleContextMenu(parent) { + async toggleContextMenu(parent?: WebElementWrapper) { log.debug('toggleContextMenu'); await (parent ? parent.moveMouseTo() : testSubjects.moveMouseTo('dashboardPanelTitle')); const toggleMenuItem = await this.findContextMenu(parent); @@ -54,7 +57,7 @@ export function DashboardPanelActionsProvider({ getService, getPageObjects }) { await testSubjects.existOrFail('embeddablePanelContextMenuOpen'); } - async openContextMenu(parent) { + async openContextMenu(parent?: WebElementWrapper) { log.debug(`openContextMenu(${parent}`); await this.toggleContextMenu(parent); await this.expectContextMenuToBeOpen(); @@ -77,43 +80,45 @@ export function DashboardPanelActionsProvider({ getService, getPageObjects }) { await testSubjects.click(REMOVE_PANEL_DATA_TEST_SUBJ); } - async removePanelByTitle(title) { + async removePanelByTitle(title: string) { const header = await this.getPanelHeading(title); await this.openContextMenu(header); await testSubjects.click(REMOVE_PANEL_DATA_TEST_SUBJ); } - async customizePanel(parent) { + async customizePanel(parent?: WebElementWrapper) { await this.openContextMenu(parent); await testSubjects.click(CUSTOMIZE_PANEL_DATA_TEST_SUBJ); } - async replacePanelByTitle(title) { + async replacePanelByTitle(title?: string) { log.debug(`replacePanel(${title})`); - let panelOptions = null; if (title) { - panelOptions = await this.getPanelHeading(title); + const panelOptions = await this.getPanelHeading(title); + await this.openContextMenu(panelOptions); + } else { + await this.openContextMenu(); } - await this.openContextMenu(panelOptions); await testSubjects.click(REPLACE_PANEL_DATA_TEST_SUBJ); } - async clonePanelByTitle(title) { + async clonePanelByTitle(title?: string) { log.debug(`clonePanel(${title})`); - let panelOptions = null; if (title) { - panelOptions = await this.getPanelHeading(title); + const panelOptions = await this.getPanelHeading(title); + await this.openContextMenu(panelOptions); + } else { + await this.openContextMenu(); } - await this.openContextMenu(panelOptions); await testSubjects.click(CLONE_PANEL_DATA_TEST_SUBJ); } - async openInspectorByTitle(title) { + async openInspectorByTitle(title: string) { const header = await this.getPanelHeading(title); await this.openInspector(header); } - async openInspector(parent) { + async openInspector(parent: WebElementWrapper) { await this.openContextMenu(parent); await testSubjects.click(OPEN_INSPECTOR_TEST_SUBJ); } @@ -163,7 +168,7 @@ export function DashboardPanelActionsProvider({ getService, getPageObjects }) { await testSubjects.existOrFail(TOGGLE_EXPAND_PANEL_DATA_TEST_SUBJ); } - async getPanelHeading(title) { + async getPanelHeading(title: string) { return await testSubjects.find(`embeddablePanelHeading-${title.replace(/\s/g, '')}`); } @@ -171,13 +176,14 @@ export function DashboardPanelActionsProvider({ getService, getPageObjects }) { await testSubjects.click('customizePanelHideTitle'); } - async toggleHidePanelTitle(originalTitle) { + async toggleHidePanelTitle(originalTitle: string) { log.debug(`hidePanelTitle(${originalTitle})`); - let panelOptions = null; if (originalTitle) { - panelOptions = await this.getPanelHeading(originalTitle); + const panelOptions = await this.getPanelHeading(originalTitle); + await this.customizePanel(panelOptions); + } else { + await this.customizePanel(); } - await this.customizePanel(panelOptions); await this.clickHidePanelTitleToggle(); await testSubjects.click('saveNewTitleButton'); } @@ -188,18 +194,19 @@ export function DashboardPanelActionsProvider({ getService, getPageObjects }) { * @param originalTitle - optional to specify which panel to change the title on. * @return {Promise} */ - async setCustomPanelTitle(customTitle, originalTitle) { + async setCustomPanelTitle(customTitle: string, originalTitle?: string) { log.debug(`setCustomPanelTitle(${customTitle}, ${originalTitle})`); - let panelOptions = null; if (originalTitle) { - panelOptions = await this.getPanelHeading(originalTitle); + const panelOptions = await this.getPanelHeading(originalTitle); + await this.customizePanel(panelOptions); + } else { + await this.customizePanel(); } - await this.customizePanel(panelOptions); await testSubjects.setValue('customEmbeddablePanelTitleInput', customTitle); await testSubjects.click('saveNewTitleButton'); } - async resetCustomPanelTitle(panel) { + async resetCustomPanelTitle(panel: WebElementWrapper) { log.debug('resetCustomPanelTitle'); await this.customizePanel(panel); await testSubjects.click('resetCustomEmbeddablePanelTitle'); diff --git a/test/functional/services/dashboard/replace_panel.js b/test/functional/services/dashboard/replace_panel.ts similarity index 86% rename from test/functional/services/dashboard/replace_panel.js rename to test/functional/services/dashboard/replace_panel.ts index faa4d404442d75..d1cb4e5e697a1c 100644 --- a/test/functional/services/dashboard/replace_panel.js +++ b/test/functional/services/dashboard/replace_panel.ts @@ -17,7 +17,9 @@ * under the License. */ -export function DashboardReplacePanelProvider({ getService }) { +import { FtrProviderContext } from '../../ftr_provider_context'; + +export function DashboardReplacePanelProvider({ getService }: FtrProviderContext) { const log = getService('log'); const testSubjects = getService('testSubjects'); const flyout = getService('flyout'); @@ -28,7 +30,7 @@ export function DashboardReplacePanelProvider({ getService }) { await testSubjects.click('savedObjectFinderFilterButton'); } - async toggleFilter(type) { + async toggleFilter(type: string) { log.debug(`DashboardReplacePanel.replaceToFilter(${type})`); await this.waitForListLoading(); await this.toggleFilterPopover(); @@ -57,21 +59,21 @@ export function DashboardReplacePanelProvider({ getService }) { await flyout.ensureClosed('dashboardReplacePanel'); } - async replaceSavedSearch(searchName) { + async replaceSavedSearch(searchName: string) { return this.replaceEmbeddable(searchName, 'search'); } - async replaceSavedSearches(searches) { + async replaceSavedSearches(searches: string[]) { for (const name of searches) { await this.replaceSavedSearch(name); } } - async replaceVisualization(vizName) { + async replaceVisualization(vizName: string) { return this.replaceEmbeddable(vizName, 'visualization'); } - async replaceEmbeddable(embeddableName, embeddableType) { + async replaceEmbeddable(embeddableName: string, embeddableType: string) { log.debug( `DashboardReplacePanel.replaceEmbeddable, name: ${embeddableName}, type: ${embeddableType}` ); @@ -86,14 +88,14 @@ export function DashboardReplacePanelProvider({ getService }) { return embeddableName; } - async filterEmbeddableNames(name) { + async filterEmbeddableNames(name: string) { // The search input field may be disabled while the table is loading so wait for it await this.waitForListLoading(); await testSubjects.setValue('savedObjectFinderSearchInput', name); await this.waitForListLoading(); } - async panelReplaceLinkExists(name) { + async panelReplaceLinkExists(name: string) { log.debug(`DashboardReplacePanel.panelReplaceLinkExists(${name})`); await this.ensureReplacePanelIsShowing(); await this.filterEmbeddableNames(`"${name}"`); diff --git a/test/functional/services/dashboard/visualizations.js b/test/functional/services/dashboard/visualizations.ts similarity index 88% rename from test/functional/services/dashboard/visualizations.js rename to test/functional/services/dashboard/visualizations.ts index 676e4c384fe36b..10747658d8c9b7 100644 --- a/test/functional/services/dashboard/visualizations.js +++ b/test/functional/services/dashboard/visualizations.ts @@ -17,7 +17,9 @@ * under the License. */ -export function DashboardVisualizationProvider({ getService, getPageObjects }) { +import { FtrProviderContext } from '../../ftr_provider_context'; + +export function DashboardVisualizationProvider({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); const find = getService('find'); const retry = getService('retry'); @@ -34,7 +36,7 @@ export function DashboardVisualizationProvider({ getService, getPageObjects }) { ]); return new (class DashboardVisualizations { - async createAndAddTSVBVisualization(name) { + async createAndAddTSVBVisualization(name: string) { log.debug(`createAndAddTSVBVisualization(${name})`); const inViewMode = await PageObjects.dashboard.getIsInViewMode(); if (inViewMode) { @@ -46,7 +48,15 @@ export function DashboardVisualizationProvider({ getService, getPageObjects }) { await PageObjects.visualize.saveVisualizationExpectSuccess(name); } - async createSavedSearch({ name, query, fields }) { + async createSavedSearch({ + name, + query, + fields, + }: { + name: string; + query?: string; + fields?: string[]; + }) { log.debug(`createSavedSearch(${name})`); await PageObjects.header.clickDiscover(); @@ -68,7 +78,15 @@ export function DashboardVisualizationProvider({ getService, getPageObjects }) { await testSubjects.exists('saveSearchSuccess'); } - async createAndAddSavedSearch({ name, query, fields }) { + async createAndAddSavedSearch({ + name, + query, + fields, + }: { + name: string; + query?: string; + fields?: string[]; + }) { log.debug(`createAndAddSavedSearch(${name})`); await this.createSavedSearch({ name, query, fields }); @@ -106,7 +124,7 @@ export function DashboardVisualizationProvider({ getService, getPageObjects }) { } } - async createAndAddMarkdown({ name, markdown }) { + async createAndAddMarkdown({ name, markdown }: { name: string; markdown: string }) { log.debug(`createAndAddMarkdown(${markdown})`); const inViewMode = await PageObjects.dashboard.getIsInViewMode(); if (inViewMode) { diff --git a/test/functional/services/index.ts b/test/functional/services/index.ts index cbb0c6790dbe94..7891a6b00f7296 100644 --- a/test/functional/services/index.ts +++ b/test/functional/services/index.ts @@ -35,10 +35,8 @@ import { DashboardExpectProvider, DashboardPanelActionsProvider, DashboardVisualizationProvider, - // @ts-ignore not TS yet } from './dashboard'; import { DocTableProvider } from './doc_table'; -import { ElasticChartProvider } from './elastic_chart'; import { EmbeddingProvider } from './embedding'; import { FilterBarProvider } from './filter_bar'; import { FlyoutProvider } from './flyout'; @@ -49,8 +47,7 @@ import { RemoteProvider } from './remote'; import { RenderableProvider } from './renderable'; import { TableProvider } from './table'; import { ToastsProvider } from './toasts'; -// @ts-ignore not TS yet -import { PieChartProvider } from './visualizations'; +import { PieChartProvider, ElasticChartProvider } from './visualizations'; import { ListingTableProvider } from './listing_table'; import { SavedQueryManagementComponentProvider } from './saved_query_management_component'; import { KibanaSupertestProvider } from './supertest'; diff --git a/test/functional/services/elastic_chart.ts b/test/functional/services/visualizations/elastic_chart.ts similarity index 97% rename from test/functional/services/elastic_chart.ts rename to test/functional/services/visualizations/elastic_chart.ts index 1c3071ac01587f..3c454f0a88e248 100644 --- a/test/functional/services/elastic_chart.ts +++ b/test/functional/services/visualizations/elastic_chart.ts @@ -18,7 +18,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrProviderContext } from '../../ftr_provider_context'; export function ElasticChartProvider({ getService }: FtrProviderContext) { const testSubjects = getService('testSubjects'); diff --git a/test/functional/services/visualizations/index.js b/test/functional/services/visualizations/index.ts similarity index 93% rename from test/functional/services/visualizations/index.js rename to test/functional/services/visualizations/index.ts index 1cc9d40abeab6a..1da1691b07c2ab 100644 --- a/test/functional/services/visualizations/index.js +++ b/test/functional/services/visualizations/index.ts @@ -18,3 +18,4 @@ */ export { PieChartProvider } from './pie_chart'; +export { ElasticChartProvider } from './elastic_chart'; diff --git a/test/functional/services/visualizations/pie_chart.js b/test/functional/services/visualizations/pie_chart.ts similarity index 78% rename from test/functional/services/visualizations/pie_chart.js rename to test/functional/services/visualizations/pie_chart.ts index edabc7ce989c0e..66f32d246b31f4 100644 --- a/test/functional/services/visualizations/pie_chart.js +++ b/test/functional/services/visualizations/pie_chart.ts @@ -18,8 +18,9 @@ */ import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; -export function PieChartProvider({ getService }) { +export function PieChartProvider({ getService }: FtrProviderContext) { const log = getService('log'); const retry = getService('retry'); const config = getService('config'); @@ -29,7 +30,7 @@ export function PieChartProvider({ getService }) { const defaultFindTimeout = config.get('timeouts.find'); return new (class PieChart { - async filterOnPieSlice(name) { + async filterOnPieSlice(name?: string) { log.debug(`PieChart.filterOnPieSlice(${name})`); if (name) { await testSubjects.click(`pieSlice-${name.split(' ').join('-')}`); @@ -43,27 +44,27 @@ export function PieChartProvider({ getService }) { } } - async filterByLegendItem(label) { + async filterByLegendItem(label: string) { log.debug(`PieChart.filterByLegendItem(${label})`); await testSubjects.click(`legend-${label}`); await testSubjects.click(`legend-${label}-filterIn`); } - async getPieSlice(name) { + async getPieSlice(name: string) { return await testSubjects.find(`pieSlice-${name.split(' ').join('-')}`); } - async getAllPieSlices(name) { + async getAllPieSlices(name: string) { return await testSubjects.findAll(`pieSlice-${name.split(' ').join('-')}`); } - async getPieSliceStyle(name) { + async getPieSliceStyle(name: string) { log.debug(`VisualizePage.getPieSliceStyle(${name})`); const pieSlice = await this.getPieSlice(name); return await pieSlice.getAttribute('style'); } - async getAllPieSliceStyles(name) { + async getAllPieSliceStyles(name: string) { log.debug(`VisualizePage.getAllPieSliceStyles(${name})`); const pieSlices = await this.getAllPieSlices(name); return await Promise.all( @@ -73,12 +74,10 @@ export function PieChartProvider({ getService }) { async getPieChartData() { const chartTypes = await find.allByCssSelector('path.slice', defaultFindTimeout * 2); - - const getChartTypesPromises = chartTypes.map(async (chart) => await chart.getAttribute('d')); - return await Promise.all(getChartTypesPromises); + return await Promise.all(chartTypes.map(async (chart) => await chart.getAttribute('d'))); } - async expectPieChartTableData(expectedTableData) { + async expectPieChartTableData(expectedTableData: Array<[]>) { await inspector.open(); await inspector.setTablePageSize(50); await inspector.expectTableData(expectedTableData); @@ -86,22 +85,18 @@ export function PieChartProvider({ getService }) { async getPieChartLabels() { const chartTypes = await find.allByCssSelector('path.slice', defaultFindTimeout * 2); - - const getChartTypesPromises = chartTypes.map( - async (chart) => await chart.getAttribute('data-label') + return await Promise.all( + chartTypes.map(async (chart) => await chart.getAttribute('data-label')) ); - return await Promise.all(getChartTypesPromises); } async getPieSliceCount() { log.debug('PieChart.getPieSliceCount'); - return await retry.try(async () => { - const slices = await find.allByCssSelector('svg > g > g.arcs > path.slice', 2500); - return slices.length; - }); + const slices = await find.allByCssSelector('svg > g > g.arcs > path.slice'); + return slices.length; } - async expectPieSliceCount(expectedCount) { + async expectPieSliceCount(expectedCount: number) { log.debug(`PieChart.expectPieSliceCount(${expectedCount})`); await retry.try(async () => { const slicesCount = await this.getPieSliceCount(); @@ -109,7 +104,7 @@ export function PieChartProvider({ getService }) { }); } - async expectPieChartLabels(expectedLabels) { + async expectPieChartLabels(expectedLabels: string[]) { log.debug(`PieChart.expectPieChartLabels(${expectedLabels.join(',')})`); await retry.try(async () => { const pieData = await this.getPieChartLabels(); diff --git a/x-pack/package.json b/x-pack/package.json index dc23602bac86c1..9f69cbc40bf331 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -128,7 +128,7 @@ "cheerio": "0.22.0", "commander": "3.0.2", "copy-webpack-plugin": "^5.0.4", - "cypress": "^4.4.1", + "cypress": "4.5.0", "cypress-multi-reporters": "^1.2.3", "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.2", diff --git a/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts index a6d66d47975c00..5fe9a45a7ceede 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts @@ -194,7 +194,10 @@ const useFetchEntriesEffect = ( } }; - const runFetchMoreEntriesRequest = async (direction: ShouldFetchMoreEntries) => { + const runFetchMoreEntriesRequest = async ( + direction: ShouldFetchMoreEntries, + overrides: Partial = {} + ) => { if (!props.startTimestamp || !props.endTimestamp) { return; } @@ -209,10 +212,10 @@ const useFetchEntriesEffect = ( try { const commonFetchArgs: LogEntriesBaseRequest = { - sourceId: props.sourceId, - startTimestamp: props.startTimestamp, - endTimestamp: props.endTimestamp, - query: props.filterQuery, + sourceId: overrides.sourceId || props.sourceId, + startTimestamp: overrides.startTimestamp || props.startTimestamp, + endTimestamp: overrides.endTimestamp || props.endTimestamp, + query: overrides.filterQuery || props.filterQuery, }; const fetchArgs: LogEntriesRequest = getEntriesBefore @@ -279,10 +282,10 @@ const useFetchEntriesEffect = ( const streamEntriesEffect = () => { (async () => { if (props.isStreaming && !state.isLoadingMore && !state.isReloading) { + const endTimestamp = Date.now(); if (startedStreaming) { await new Promise((res) => setTimeout(res, LIVE_STREAM_INTERVAL)); } else { - const endTimestamp = Date.now(); props.jumpToTargetPosition({ tiebreaker: 0, time: endTimestamp }); setStartedStreaming(true); if (state.hasMoreAfterEnd) { @@ -290,7 +293,9 @@ const useFetchEntriesEffect = ( return; } } - const newEntriesEnd = await runFetchMoreEntriesRequest(ShouldFetchMoreEntries.After); + const newEntriesEnd = await runFetchMoreEntriesRequest(ShouldFetchMoreEntries.After, { + endTimestamp, + }); if (newEntriesEnd) { props.jumpToTargetPosition(newEntriesEnd); } diff --git a/x-pack/plugins/siem/common/endpoint/generate_data.ts b/x-pack/plugins/siem/common/endpoint/generate_data.ts index a683db86dc6a0e..597ad4df64dfeb 100644 --- a/x-pack/plugins/siem/common/endpoint/generate_data.ts +++ b/x-pack/plugins/siem/common/endpoint/generate_data.ts @@ -845,10 +845,6 @@ export class EndpointDocGenerator { }, ], id: this.commonInfo.endpoint.policy.id, - policy: { - id: this.commonInfo.endpoint.policy.id, - version: policyVersion, - }, response: { configurations: { events: { diff --git a/x-pack/plugins/siem/common/endpoint/types.ts b/x-pack/plugins/siem/common/endpoint/types.ts index 6d04f1dfac38f1..45b5cf2526e12d 100644 --- a/x-pack/plugins/siem/common/endpoint/types.ts +++ b/x-pack/plugins/siem/common/endpoint/types.ts @@ -685,10 +685,6 @@ export interface HostPolicyResponse { id: string; status: HostPolicyResponseActionStatus; actions: HostPolicyResponseAppliedAction[]; - policy: { - id: string; - version: string; - }; response: { configurations: { malware: HostPolicyResponseConfigurationStatus; diff --git a/x-pack/plugins/siem/common/endpoint_alerts/types.ts b/x-pack/plugins/siem/common/endpoint_alerts/types.ts index 2df92b43ab52a0..3fbde79414aa09 100644 --- a/x-pack/plugins/siem/common/endpoint_alerts/types.ts +++ b/x-pack/plugins/siem/common/endpoint_alerts/types.ts @@ -15,28 +15,9 @@ import { AlertEvent, KbnConfigSchemaInputTypeOf, AppLocation, + Immutable, } from '../endpoint/types'; -/** - * A deep readonly type that will make all children of a given object readonly recursively - */ -export type Immutable = T extends undefined | null | boolean | string | number - ? T - : unknown extends T - ? unknown - : T extends Array - ? ImmutableArray - : T extends Map - ? ImmutableMap - : T extends Set - ? ImmutableSet - : ImmutableObject; - -type ImmutableArray = ReadonlyArray>; -type ImmutableMap = ReadonlyMap, Immutable>; -type ImmutableSet = ReadonlySet>; -type ImmutableObject = { readonly [K in keyof T]: Immutable }; - /** * Values for the Alert APIs 'order' and 'direction' parameters. */ diff --git a/x-pack/plugins/siem/public/app/types.ts b/x-pack/plugins/siem/public/app/types.ts index 444e0066c3c7b4..4b00dee3b75108 100644 --- a/x-pack/plugins/siem/public/app/types.ts +++ b/x-pack/plugins/siem/public/app/types.ts @@ -4,17 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Reducer, AnyAction, Middleware, Dispatch } from 'redux'; +import { + Reducer, + AnyAction, + Middleware, + Dispatch, + PreloadedState, + StateFromReducersMapObject, + CombinedState, +} from 'redux'; + import { NavTab } from '../common/components/navigation/types'; -import { HostsState } from '../hosts/store'; -import { NetworkState } from '../network/store'; -import { TimelineState } from '../timelines/store/timeline/types'; -import { ImmutableReducer, State } from '../common/store'; +import { State, SubPluginsInitReducer } from '../common/store'; import { Immutable } from '../../common/endpoint/types'; -import { AlertListState } from '../../common/endpoint_alerts/types'; import { AppAction } from '../common/store/actions'; -import { HostState } from '../endpoint_hosts/types'; -import { ManagementState } from '../management/store/types'; export enum SiemPageName { overview = 'overview', @@ -38,7 +41,7 @@ export type SiemNavTabKey = export type SiemNavTab = Record; export interface SecuritySubPluginStore { - initialState: Record; + initialState: Record; reducer: Record>; middleware?: Array>>>; } @@ -54,6 +57,10 @@ type SecuritySubPluginKeyStore = | 'hostList' | 'alertList' | 'management'; + +/** + * Returned by the various 'SecuritySubPlugin' classes from the `start` method. + */ export interface SecuritySubPluginWithStore extends SecuritySubPlugin { store: SecuritySubPluginStore; @@ -61,22 +68,17 @@ export interface SecuritySubPluginWithStore; - hostList: Immutable; - management: ManagementState; - }; - reducer: { - hosts: Reducer; - network: Reducer; - timeline: Reducer; - alertList: ImmutableReducer; - hostList: ImmutableReducer; - management: ImmutableReducer; - }; + initialState: PreloadedState< + CombinedState< + StateFromReducersMapObject< + /** SubPluginsInitReducer, being an interface, will not work in `StateFromReducersMapObject`. + * Picking its keys does the trick. + **/ + Pick + > + > + >; + reducer: SubPluginsInitReducer; middlewares: Array>>>; }; } diff --git a/x-pack/plugins/siem/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx b/x-pack/plugins/siem/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx index c71cd8e60596ce..c33677e41db0e1 100644 --- a/x-pack/plugins/siem/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx +++ b/x-pack/plugins/siem/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx @@ -15,7 +15,7 @@ import { BrowserFields } from '../../containers/source'; import { dragAndDropModel, dragAndDropSelectors } from '../../store'; import { timelineSelectors } from '../../../timelines/store/timeline'; import { IdToDataProvider } from '../../store/drag_and_drop/model'; -import { State } from '../../store/reducer'; +import { State } from '../../store/types'; import { DataProvider } from '../../../timelines/components/timeline/data_providers/data_provider'; import { reArrangeProviders } from '../../../timelines/components/timeline/data_providers/helpers'; import { ACTIVE_TIMELINE_REDUX_ID } from '../top_n'; diff --git a/x-pack/plugins/siem/public/common/components/error_toast_dispatcher/index.test.tsx b/x-pack/plugins/siem/public/common/components/error_toast_dispatcher/index.test.tsx index 50b20099b17d07..39b17f7008e64b 100644 --- a/x-pack/plugins/siem/public/common/components/error_toast_dispatcher/index.test.tsx +++ b/x-pack/plugins/siem/public/common/components/error_toast_dispatcher/index.test.tsx @@ -12,7 +12,7 @@ import { apolloClientObservable, mockGlobalState, SUB_PLUGINS_REDUCER } from '.. import { createStore } from '../../store/store'; import { ErrorToastDispatcher } from '.'; -import { State } from '../../store/reducer'; +import { State } from '../../store/types'; describe('Error Toast Dispatcher', () => { const state: State = mockGlobalState; diff --git a/x-pack/plugins/siem/public/common/mock/endpoint/app_context_render.tsx b/x-pack/plugins/siem/public/common/mock/endpoint/app_context_render.tsx index e62f36c2ec782c..3c0189625ee291 100644 --- a/x-pack/plugins/siem/public/common/mock/endpoint/app_context_render.tsx +++ b/x-pack/plugins/siem/public/common/mock/endpoint/app_context_render.tsx @@ -15,10 +15,10 @@ import { depsStartMock } from './dependencies_start_mock'; import { MiddlewareActionSpyHelper, createSpyMiddleware } from '../../store/test_utils'; import { apolloClientObservable } from '../test_providers'; import { createStore, State, substateMiddlewareFactory } from '../../store'; -import { hostMiddlewareFactory } from '../../../endpoint_hosts/store'; import { alertMiddlewareFactory } from '../../../endpoint_alerts/store/middleware'; import { AppRootProvider } from './app_root_provider'; -import { managementMiddlewareFactory } from '../../../management/store'; +import { managementMiddlewareFactory } from '../../../management/store/middleware'; +import { hostMiddlewareFactory } from '../../../endpoint_hosts/store/middleware'; import { SUB_PLUGINS_REDUCER, mockGlobalState } from '..'; type UiRender = (ui: React.ReactElement, options?: RenderOptions) => RenderResult; @@ -56,8 +56,7 @@ export const createAppRootMockRenderer = (): AppContextTestRender => { const coreStart = coreMock.createStart({ basePath: '/mock' }); const depsStart = depsStartMock(); const middlewareSpy = createSpyMiddleware(); - const state: State = mockGlobalState; - const store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, [ + const store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, apolloClientObservable, [ substateMiddlewareFactory( (globalState) => globalState.hostList, hostMiddlewareFactory(coreStart, depsStart) @@ -76,7 +75,6 @@ export const createAppRootMockRenderer = (): AppContextTestRender => { ); const render: UiRender = (ui, options) => { - // @ts-ignore return reactRender(ui, { wrapper: AppWrapper as React.ComponentType, ...options, diff --git a/x-pack/plugins/siem/public/common/mock/global_state.ts b/x-pack/plugins/siem/public/common/mock/global_state.ts index c96f67a39dbfe8..30dffa8dbf6bfd 100644 --- a/x-pack/plugins/siem/public/common/mock/global_state.ts +++ b/x-pack/plugins/siem/public/common/mock/global_state.ts @@ -27,11 +27,10 @@ import { networkModel } from '../../network/store'; import { TimelineType, TimelineStatus } from '../../../common/types/timeline'; import { initialAlertListState } from '../../endpoint_alerts/store/reducer'; import { initialHostListState } from '../../endpoint_hosts/store/reducer'; -import { getManagementInitialState } from '../../management/store'; - -const alertList = initialAlertListState(); -const hostList = initialHostListState(); -const management = getManagementInitialState(); +import { mockManagementState } from '../../management/store/reducer'; +import { AlertListState } from '../../../common/endpoint_alerts/types'; +import { HostState } from '../../endpoint_hosts/types'; +import { ManagementState } from '../../management/types'; export const mockGlobalState: State = { app: { @@ -233,7 +232,11 @@ export const mockGlobalState: State = { }, }, }, - alertList, - hostList, - management, + /** + * These state's are wrapped in `Immutable`, but for compatibility with the overall app architecture, + * they are cast to mutable versions here. + */ + alertList: initialAlertListState as AlertListState, + hostList: initialHostListState as HostState, + management: mockManagementState as ManagementState, }; diff --git a/x-pack/plugins/siem/public/common/mock/utils.ts b/x-pack/plugins/siem/public/common/mock/utils.ts index 532637acab7679..1ff5cb8e734ec3 100644 --- a/x-pack/plugins/siem/public/common/mock/utils.ts +++ b/x-pack/plugins/siem/public/common/mock/utils.ts @@ -7,9 +7,13 @@ import { hostsReducer } from '../../hosts/store'; import { networkReducer } from '../../network/store'; import { timelineReducer } from '../../timelines/store/timeline/reducer'; -import { hostListReducer } from '../../endpoint_hosts/store'; -import { alertListReducer } from '../../endpoint_alerts/store'; -import { managementReducer } from '../../management/store'; +import { managementReducer } from '../../management/store/reducer'; +import { ManagementPluginReducer } from '../../management'; +import { SubPluginsInitReducer } from '../store'; +import { EndpointAlertsPluginReducer } from '../../endpoint_alerts'; +import { EndpointHostsPluginReducer } from '../../endpoint_hosts'; +import { alertListReducer } from '../../endpoint_alerts/store/reducer'; +import { hostListReducer } from '../../endpoint_hosts/store/reducer'; interface Global extends NodeJS.Global { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -18,11 +22,15 @@ interface Global extends NodeJS.Global { export const globalNode: Global = global; -export const SUB_PLUGINS_REDUCER = { +export const SUB_PLUGINS_REDUCER: SubPluginsInitReducer = { hosts: hostsReducer, network: networkReducer, timeline: timelineReducer, - hostList: hostListReducer, - alertList: alertListReducer, - management: managementReducer, + /** + * These state's are wrapped in `Immutable`, but for compatibility with the overall app architecture, + * they are cast to mutable versions here. + */ + hostList: hostListReducer as EndpointHostsPluginReducer['hostList'], + alertList: alertListReducer as EndpointAlertsPluginReducer['alertList'], + management: managementReducer as ManagementPluginReducer['management'], }; diff --git a/x-pack/plugins/siem/public/common/store/app/selectors.ts b/x-pack/plugins/siem/public/common/store/app/selectors.ts index c37695c2ccbe61..d18cb73dbcfb98 100644 --- a/x-pack/plugins/siem/public/common/store/app/selectors.ts +++ b/x-pack/plugins/siem/public/common/store/app/selectors.ts @@ -7,11 +7,9 @@ import { keys } from 'lodash/fp'; import memoizeOne from 'memoize-one'; import { createSelector } from 'reselect'; - import { Note } from '../../lib/note'; -import { State } from '../reducer'; - import { ErrorModel, NotesById } from './model'; +import { State } from '../types'; const selectNotesById = (state: State): NotesById => state.app.notesById; diff --git a/x-pack/plugins/siem/public/common/store/drag_and_drop/selectors.ts b/x-pack/plugins/siem/public/common/store/drag_and_drop/selectors.ts index 99a8369783cdd1..5d6534f96bc7a9 100644 --- a/x-pack/plugins/siem/public/common/store/drag_and_drop/selectors.ts +++ b/x-pack/plugins/siem/public/common/store/drag_and_drop/selectors.ts @@ -5,10 +5,8 @@ */ import { createSelector } from 'reselect'; - -import { State } from '../reducer'; - import { IdToDataProvider } from './model'; +import { State } from '../types'; const selectDataProviders = (state: State): IdToDataProvider => state.dragAndDrop.dataProviders; diff --git a/x-pack/plugins/siem/public/common/store/index.ts b/x-pack/plugins/siem/public/common/store/index.ts index 96eb5e159909c5..6227931b532682 100644 --- a/x-pack/plugins/siem/public/common/store/index.ts +++ b/x-pack/plugins/siem/public/common/store/index.ts @@ -8,18 +8,46 @@ export * from './model'; export * from './reducer'; export * from './selectors'; +import { Middleware, Dispatch } from 'redux'; import { createStore, getStore } from './store'; -import { SubstateMiddlewareFactory } from './types'; +import { ImmutableMiddleware, State } from './types'; +import { AppAction } from './actions'; +import { Immutable } from '../../../common/endpoint/types'; export { createStore, getStore }; -export const substateMiddlewareFactory: SubstateMiddlewareFactory = (selector, middleware) => { +/** + * Takes a selector and an `ImmutableMiddleware`. The + * middleware's version of `getState` will receive + * the result of the selector instead of the global state. + * + * This allows middleware to have knowledge of only a subsection of state. + * + * `selector` returns an `Immutable` version of the substate. + * `middleware` must be an `ImmutableMiddleware`. + * + * Returns a regular middleware, meant to be used with `applyMiddleware`. + */ +export const substateMiddlewareFactory = ( + selector: (state: State) => Substate | Immutable, + middleware: ImmutableMiddleware +): Middleware<{}, State, Dispatch>> => { return (api) => { const substateAPI = { ...api, - // Return just the substate instead of global state. - getState() { - return selector(api.getState()); + // Return the substate instead of global state. + getState(): Immutable { + /** + * The selector will receive the basic (mutable) version of state. This is because + * the top level state shape doesn't use `Immutable`. We cast the return value as `Immutable` + * so that the middleware won't be able to mutate state. + * + * Immutable enforces nothing structural about a type so casting + * a value as `Immutable` is safe as long as nothing else is going to mutate. + * Since the state came from the return value of a reducer, the reducer will (hopefully) + * not be mutating it. + */ + return selector(api.getState()) as Immutable; }, }; return middleware(substateAPI); diff --git a/x-pack/plugins/siem/public/common/store/inputs/selectors.ts b/x-pack/plugins/siem/public/common/store/inputs/selectors.ts index 95c463776e2889..0eee5ebbfbf772 100644 --- a/x-pack/plugins/siem/public/common/store/inputs/selectors.ts +++ b/x-pack/plugins/siem/public/common/store/inputs/selectors.ts @@ -6,7 +6,7 @@ import { createSelector } from 'reselect'; -import { State } from '../reducer'; +import { State } from '../types'; import { InputsModel, InputsRange, GlobalQuery } from './model'; diff --git a/x-pack/plugins/siem/public/common/store/reducer.ts b/x-pack/plugins/siem/public/common/store/reducer.ts index e06543b8d7181d..ba85fbef860d7a 100644 --- a/x-pack/plugins/siem/public/common/store/reducer.ts +++ b/x-pack/plugins/siem/public/common/store/reducer.ts @@ -4,47 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ -import { combineReducers } from 'redux'; +import { combineReducers, PreloadedState, AnyAction, Reducer } from 'redux'; -import { appReducer, AppState, initialAppState } from './app'; -import { dragAndDropReducer, DragAndDropState, initialDragAndDropState } from './drag_and_drop'; -import { createInitialInputsState, initialInputsState, inputsReducer, InputsState } from './inputs'; +import { appReducer, initialAppState } from './app'; +import { dragAndDropReducer, initialDragAndDropState } from './drag_and_drop'; +import { createInitialInputsState, inputsReducer } from './inputs'; -import { HostsPluginState, HostsPluginReducer } from '../../hosts/store'; -import { NetworkPluginState, NetworkPluginReducer } from '../../network/store'; -import { TimelinePluginState, TimelinePluginReducer } from '../../timelines/store/timeline'; -import { - EndpointAlertsPluginState, - EndpointAlertsPluginReducer, -} from '../../endpoint_alerts/store'; -import { EndpointHostsPluginState, EndpointHostsPluginReducer } from '../../endpoint_hosts/store'; +import { HostsPluginReducer } from '../../hosts/store'; +import { NetworkPluginReducer } from '../../network/store'; +import { TimelinePluginReducer } from '../../timelines/store/timeline'; -import { ManagementPluginReducer, ManagementPluginState } from '../../management/store/types'; - -export interface State - extends HostsPluginState, - NetworkPluginState, - TimelinePluginState, - EndpointAlertsPluginState, - EndpointHostsPluginState, - ManagementPluginState { - app: AppState; - dragAndDrop: DragAndDropState; - inputs: InputsState; -} - -export const initialState: Pick = { - app: initialAppState, - dragAndDrop: initialDragAndDropState, - inputs: initialInputsState, -}; - -type SubPluginsInitState = HostsPluginState & - NetworkPluginState & - TimelinePluginState & - EndpointAlertsPluginState & - EndpointHostsPluginState & - ManagementPluginState; +import { SecuritySubPlugins } from '../../app/types'; +import { ManagementPluginReducer } from '../../management'; +import { EndpointAlertsPluginReducer } from '../../endpoint_alerts'; +import { EndpointHostsPluginReducer } from '../../endpoint_hosts'; +import { State } from './types'; +import { AppAction } from './actions'; export type SubPluginsInitReducer = HostsPluginReducer & NetworkPluginReducer & @@ -53,14 +28,28 @@ export type SubPluginsInitReducer = HostsPluginReducer & EndpointHostsPluginReducer & ManagementPluginReducer; -export const createInitialState = (pluginsInitState: SubPluginsInitState): State => ({ - ...initialState, - ...pluginsInitState, - inputs: createInitialInputsState(), -}); +/** + * Factory for the 'initialState' that is used to preload state into the Security App's redux store. + */ +export const createInitialState = ( + pluginsInitState: SecuritySubPlugins['store']['initialState'] +): PreloadedState => { + const preloadedState: PreloadedState = { + app: initialAppState, + dragAndDrop: initialDragAndDropState, + ...pluginsInitState, + inputs: createInitialInputsState(), + }; + return preloadedState; +}; -export const createReducer = (pluginsReducer: SubPluginsInitReducer) => - combineReducers({ +/** + * Factory for the Security app's redux reducer. + */ +export const createReducer: ( + pluginsReducer: SubPluginsInitReducer +) => Reducer = (pluginsReducer: SubPluginsInitReducer) => + combineReducers({ app: appReducer, dragAndDrop: dragAndDropReducer, inputs: inputsReducer, diff --git a/x-pack/plugins/siem/public/common/store/store.ts b/x-pack/plugins/siem/public/common/store/store.ts index 10ea61828ed361..276dcdcaedb854 100644 --- a/x-pack/plugins/siem/public/common/store/store.ts +++ b/x-pack/plugins/siem/public/common/store/store.ts @@ -12,6 +12,7 @@ import { Store, Middleware, Dispatch, + PreloadedState, } from 'redux'; import { createEpicMiddleware } from 'redux-observable'; @@ -21,11 +22,12 @@ import { telemetryMiddleware } from '../lib/telemetry'; import { appSelectors } from './app'; import { timelineSelectors } from '../../timelines/store/timeline'; import { inputsSelectors } from './inputs'; -import { State, SubPluginsInitReducer, createReducer } from './reducer'; +import { SubPluginsInitReducer, createReducer } from './reducer'; import { createRootEpic } from './epic'; import { AppApolloClient } from '../lib/lib'; import { AppAction } from './actions'; import { Immutable } from '../../../common/endpoint/types'; +import { State } from './types'; type ComposeType = typeof compose; declare global { @@ -33,10 +35,17 @@ declare global { __REDUX_DEVTOOLS_EXTENSION_COMPOSE__: ComposeType; } } +/** + * The Redux store type for the Security app. + */ +export type SecurityAppStore = Store; let store: Store | null = null; -export { SubPluginsInitReducer }; + +/** + * Factory for Security App's redux store. + */ export const createStore = ( - state: State, + state: PreloadedState, pluginsReducer: SubPluginsInitReducer, apolloClient: Observable, additionalMiddleware?: Array>>> diff --git a/x-pack/plugins/siem/public/common/store/test_utils.ts b/x-pack/plugins/siem/public/common/store/test_utils.ts index 511cdcb6f06728..89558c37909f7d 100644 --- a/x-pack/plugins/siem/public/common/store/test_utils.ts +++ b/x-pack/plugins/siem/public/common/store/test_utils.ts @@ -5,9 +5,8 @@ */ import { Dispatch } from 'redux'; -import { State } from './reducer'; +import { State, ImmutableMiddlewareFactory } from './types'; import { AppAction } from './actions'; -import { ImmutableMiddlewareFactory } from './types'; /** * Utilities for testing Redux middleware diff --git a/x-pack/plugins/siem/public/common/store/types.ts b/x-pack/plugins/siem/public/common/store/types.ts index a4bfdeb30b438f..b9942979beb1e8 100644 --- a/x-pack/plugins/siem/public/common/store/types.ts +++ b/x-pack/plugins/siem/public/common/store/types.ts @@ -4,19 +4,40 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - Dispatch, - Action as ReduxAction, - AnyAction as ReduxAnyAction, - Action, - Middleware, -} from 'redux'; +import { Dispatch, Action, Middleware, CombinedState } from 'redux'; import { CoreStart } from '../../../../../../src/core/public'; -import { Immutable } from '../../../common/endpoint_alerts/types'; -import { State } from './reducer'; import { StartPlugins } from '../../types'; import { AppAction } from './actions'; +import { Immutable } from '../../../common/endpoint/types'; +import { AppState } from './app/reducer'; +import { InputsState } from './inputs/reducer'; +import { HostsPluginState } from '../../hosts/store'; +import { DragAndDropState } from './drag_and_drop/reducer'; +import { TimelinePluginState } from '../../timelines/store/timeline'; +import { NetworkPluginState } from '../../network/store'; +import { EndpointAlertsPluginState } from '../../endpoint_alerts'; +import { EndpointHostsPluginState } from '../../endpoint_hosts'; +import { ManagementPluginState } from '../../management'; + +/** + * The redux `State` type for the Security App. + * We use `CombinedState` to wrap our shape because we create our reducer using `combineReducers`. + * `combineReducers` returns a type wrapped in `CombinedState`. + * `CombinedState` is required for redux to know what keys to make optional when preloaded state into a store. + */ +export type State = CombinedState< + HostsPluginState & + NetworkPluginState & + TimelinePluginState & + EndpointAlertsPluginState & + EndpointHostsPluginState & + ManagementPluginState & { + app: AppState; + dragAndDrop: DragAndDropState; + inputs: InputsState; + } +>; export type KueryFilterQueryKind = 'kuery' | 'lucene'; @@ -67,60 +88,73 @@ export type ImmutableMiddlewareFactory = ( * Middleware will be of the `ImmutableMiddleware` variety. Not able to directly * change actions or state. */ -export type ImmutableMultipleMiddlewareFactory = ( +export type SecuritySubPluginMiddlewareFactory = ( coreStart: CoreStart, depsStart: Pick -) => Array>; +) => Array>>>; /** - * Simple type for a redux selector. + * Like `Reducer` from `redux` but it accepts immutable versions of `state` and `action`. + * Use this type for all Reducers in order to help enforce our pattern of immutable state. */ -type Selector = (state: S) => R; +export type ImmutableReducer = ( + state: Immutable | undefined, + action: Immutable +) => S | Immutable; /** - * Takes a selector and an `ImmutableMiddleware`. The - * middleware's version of `getState` will receive - * the result of the selector instead of the global state. - * - * This allows middleware to have knowledge of only a subsection of state. - * - * `selector` returns an `Immutable` version of the substate. - * `middleware` must be an `ImmutableMiddleware`. - * - * Returns a regular middleware, meant to be used with `applyMiddleware`. + * A alternate interface for `redux`'s `combineReducers`. Will work with the same underlying implementation, + * but will enforce that `Immutable` versions of `state` and `action` are received. */ -export type SubstateMiddlewareFactory = ( - selector: Selector>, - middleware: ImmutableMiddleware -) => Middleware<{}, State, Dispatch>>; +export type ImmutableCombineReducers = >( + reducers: M +) => ImmutableReducer< + CombinedState>, + ActionFromImmutableReducersMapObject +>; /** - * Like `Reducer` from `redux` but it accepts immutable versions of `state` and `action`. - * Use this type for all Reducers in order to help enforce our pattern of immutable state. + * Helper type for `ImmutableCombineReducers`. Infers the combined state type from an immutable reducer map. */ -export type ImmutableReducer = ( - state: Immutable | undefined, - action: Immutable -) => State | Immutable; +type StateFromImmutableReducersMapObject = M extends ImmutableReducersMapObject + ? { [P in keyof M]: M[P] extends ImmutableReducer ? S : never } + : never; /** - * A alternate interface for `redux`'s `combineReducers`. Will work with the same underlying implementation, - * but will enforce that `Immutable` versions of `state` and `action` are received. + * Helper type for `ImmutableCombineReducers`. Infers the combined action type from an immutable reducer map. + */ +type ActionFromImmutableReducersMapObject = M extends ImmutableReducersMapObject + ? ActionFromImmutableReducer> + : never; + +/** + * Helper type for `ImmutableCombineReducers`. Infers the combined reducer type from an immutable reducer map. + */ +type ImmutableReducerFromImmutableReducersMapObject = M extends { + [P in keyof M]: infer R; +} + ? R extends ImmutableReducer + ? R + : never + : never; + +/** + * Helper type for `ImmutableCombineReducers`. Infers the action type for an immutable reducer. */ -export type ImmutableCombineReducers = ( - reducers: ImmutableReducersMapObject -) => ImmutableReducer; +type ActionFromImmutableReducer = R extends ImmutableReducer ? A : never; /** + * Helper type for `ImmutableCombineReducers`. * Like `redux`'s `ReducersMapObject` (which is used by `combineReducers`) but enforces that * the `state` and `action` received are `Immutable` versions. */ -type ImmutableReducersMapObject = { +type ImmutableReducersMapObject = { [K in keyof S]: ImmutableReducer; }; /** * A better type for createStructuredSelector. This doesn't support the options object. + * https://github.com/reduxjs/reselect/pull/454 */ export type CreateStructuredSelector = < SelectorMap extends { [key: string]: (...args: never[]) => unknown } diff --git a/x-pack/plugins/siem/public/common/utils/clone_http_fetch_query.ts b/x-pack/plugins/siem/public/common/utils/clone_http_fetch_query.ts index bfa433dc9f9ac1..90b81df8bc21e6 100644 --- a/x-pack/plugins/siem/public/common/utils/clone_http_fetch_query.ts +++ b/x-pack/plugins/siem/public/common/utils/clone_http_fetch_query.ts @@ -4,9 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Immutable } from '../../../common/endpoint_alerts/types'; - import { HttpFetchQuery } from '../../../../../../src/core/public'; +import { Immutable } from '../../../common/endpoint/types'; export function cloneHttpFetchQuery(query: Immutable): HttpFetchQuery { const clone: HttpFetchQuery = {}; diff --git a/x-pack/plugins/siem/public/endpoint_alerts/index.ts b/x-pack/plugins/siem/public/endpoint_alerts/index.ts index 6380edbde6958c..e7e45b95ceb91c 100644 --- a/x-pack/plugins/siem/public/endpoint_alerts/index.ts +++ b/x-pack/plugins/siem/public/endpoint_alerts/index.ts @@ -4,15 +4,32 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Reducer } from 'redux'; import { SecuritySubPluginWithStore } from '../app/types'; -import { getEndpointAlertsRoutes } from './routes'; -import { Immutable } from '../../common/endpoint/types'; -import { initialAlertListState, alertListReducer } from './store/reducer'; +import { endpointAlertsRoutes } from './routes'; +import { alertListReducer } from './store/reducer'; import { AlertListState } from '../../common/endpoint_alerts/types'; import { alertMiddlewareFactory } from './store/middleware'; import { substateMiddlewareFactory } from '../common/store'; import { CoreStart } from '../../../../../src/core/public'; import { StartPlugins } from '../types'; +import { AppAction } from '../common/store/actions'; + +/** + * Internally, our state is sometimes immutable, ignore that in our external + * interface. + */ +export interface EndpointAlertsPluginState { + alertList: AlertListState; +} + +/** + * Internally, we use `ImmutableReducer`, but we present a regular reducer + * externally for compatibility w/ regular redux. + */ +export interface EndpointAlertsPluginReducer { + alertList: Reducer; +} export class EndpointAlerts { public setup() {} @@ -20,20 +37,24 @@ export class EndpointAlerts { public start( core: CoreStart, plugins: StartPlugins - ): SecuritySubPluginWithStore<'alertList', Immutable> { + ): SecuritySubPluginWithStore<'alertList', AlertListState> { const { data, ingestManager } = plugins; const middleware = [ - substateMiddlewareFactory( + substateMiddlewareFactory( (globalState) => globalState.alertList, alertMiddlewareFactory(core, { data, ingestManager }) ), ]; return { - routes: getEndpointAlertsRoutes(), + routes: endpointAlertsRoutes(), store: { - initialState: { alertList: initialAlertListState() }, - reducer: { alertList: alertListReducer }, + initialState: { alertList: undefined }, + /** + * Cast the ImmutableReducer to a regular reducer for compatibility with + * the subplugin architecture (which expects plain redux reducers.) + */ + reducer: { alertList: alertListReducer } as EndpointAlertsPluginReducer, middleware, }, }; diff --git a/x-pack/plugins/siem/public/endpoint_alerts/models/index_pattern.ts b/x-pack/plugins/siem/public/endpoint_alerts/models/index_pattern.ts index 8daaa3fef7a294..3eb347c6cada81 100644 --- a/x-pack/plugins/siem/public/endpoint_alerts/models/index_pattern.ts +++ b/x-pack/plugins/siem/public/endpoint_alerts/models/index_pattern.ts @@ -6,7 +6,7 @@ import { all } from 'deepmerge'; import { IIndexPattern } from 'src/plugins/data/public'; -import { Immutable } from '../../../common/endpoint_alerts/types'; +import { Immutable } from '../../../common/endpoint/types'; /** * Model for the `IIndexPattern` interface exported by the `data` plugin. diff --git a/x-pack/plugins/siem/public/endpoint_alerts/routes.tsx b/x-pack/plugins/siem/public/endpoint_alerts/routes.tsx index 60df7f5d471292..d62ef20c384dcc 100644 --- a/x-pack/plugins/siem/public/endpoint_alerts/routes.tsx +++ b/x-pack/plugins/siem/public/endpoint_alerts/routes.tsx @@ -9,7 +9,7 @@ import { Route } from 'react-router-dom'; import { AlertIndex } from './view'; -export const getEndpointAlertsRoutes = () => [ +export const endpointAlertsRoutes = () => [ , diff --git a/x-pack/plugins/siem/public/endpoint_alerts/store/action.ts b/x-pack/plugins/siem/public/endpoint_alerts/store/action.ts index ae103edaa5a2b4..3330cef1816a75 100644 --- a/x-pack/plugins/siem/public/endpoint_alerts/store/action.ts +++ b/x-pack/plugins/siem/public/endpoint_alerts/store/action.ts @@ -5,8 +5,8 @@ */ import { IIndexPattern } from 'src/plugins/data/public'; -// import { Immutable } from '../../../common/types'; -import { AlertDetails, AlertListData, Immutable } from '../../../common/endpoint_alerts/types'; +import { Immutable } from '../../../common/endpoint/types'; +import { AlertDetails, AlertListData } from '../../../common/endpoint_alerts/types'; interface ServerReturnedAlertsData { readonly type: 'serverReturnedAlertsData'; diff --git a/x-pack/plugins/siem/public/endpoint_alerts/store/alert_details.test.ts b/x-pack/plugins/siem/public/endpoint_alerts/store/alert_details.test.ts index b634e6455d971d..8e20ad089b9c24 100644 --- a/x-pack/plugins/siem/public/endpoint_alerts/store/alert_details.test.ts +++ b/x-pack/plugins/siem/public/endpoint_alerts/store/alert_details.test.ts @@ -8,7 +8,7 @@ import { Store, createStore, applyMiddleware } from 'redux'; import { createBrowserHistory, History } from 'history'; import { coreMock } from '../../../../../../src/core/public/mocks'; -import { AlertListState, Immutable } from '../../../common/endpoint_alerts/types'; +import { AlertListState } from '../../../common/endpoint_alerts/types'; import { depsStartMock, DepsStartMock } from '../../common/mock/endpoint'; import { alertListReducer } from './reducer'; @@ -16,6 +16,7 @@ import { alertListReducer } from './reducer'; import { alertMiddlewareFactory } from './middleware'; import { mockAlertResultList } from './mock_alert_result_list'; +import { Immutable } from '../../../common/endpoint/types'; describe('alert details tests', () => { let store: Store; diff --git a/x-pack/plugins/siem/public/endpoint_alerts/store/alert_list.test.ts b/x-pack/plugins/siem/public/endpoint_alerts/store/alert_list.test.ts index 8f82c2522ea69b..a21e449552960e 100644 --- a/x-pack/plugins/siem/public/endpoint_alerts/store/alert_list.test.ts +++ b/x-pack/plugins/siem/public/endpoint_alerts/store/alert_list.test.ts @@ -7,12 +7,13 @@ import { Store, createStore, applyMiddleware } from 'redux'; import { History, createBrowserHistory } from 'history'; import { alertListReducer } from './reducer'; -import { AlertListState, AlertResultList, Immutable } from '../../../common/endpoint_alerts/types'; +import { AlertListState, AlertResultList } from '../../../common/endpoint_alerts/types'; import { alertMiddlewareFactory } from './middleware'; import { coreMock } from 'src/core/public/mocks'; import { DepsStartMock, depsStartMock } from '../../common/mock/endpoint'; import { isOnAlertPage } from './selectors'; import { mockAlertResultList } from './mock_alert_result_list'; +import { Immutable } from '../../../common/endpoint/types'; describe('alert list tests', () => { let store: Store; diff --git a/x-pack/plugins/siem/public/endpoint_alerts/store/index.ts b/x-pack/plugins/siem/public/endpoint_alerts/store/index.ts deleted file mode 100644 index dd97d60c532b00..00000000000000 --- a/x-pack/plugins/siem/public/endpoint_alerts/store/index.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. - */ - -import { AlertListState, Immutable } from '../../../common/endpoint_alerts/types'; -import { ImmutableReducer } from '../../common/store'; -import { AppAction } from '../../common/store/actions'; - -export { alertListReducer } from './reducer'; -export { AlertAction } from './action'; - -export interface EndpointAlertsPluginState { - alertList: Immutable; -} - -export interface EndpointAlertsPluginReducer { - alertList: ImmutableReducer; -} diff --git a/x-pack/plugins/siem/public/endpoint_alerts/store/middleware.ts b/x-pack/plugins/siem/public/endpoint_alerts/store/middleware.ts index b8e8d36801d48a..dd84b4fcff5bdd 100644 --- a/x-pack/plugins/siem/public/endpoint_alerts/store/middleware.ts +++ b/x-pack/plugins/siem/public/endpoint_alerts/store/middleware.ts @@ -20,9 +20,8 @@ import { uiQueryParams, isAlertPageTabChange, } from './selectors'; -import { Immutable } from '../../../common/endpoint/types'; -export const alertMiddlewareFactory: ImmutableMiddlewareFactory> = ( +export const alertMiddlewareFactory: ImmutableMiddlewareFactory = ( coreStart, depsStart ) => { diff --git a/x-pack/plugins/siem/public/endpoint_alerts/store/reducer.ts b/x-pack/plugins/siem/public/endpoint_alerts/store/reducer.ts index 3e79ad4d1c6144..22fc5025656d72 100644 --- a/x-pack/plugins/siem/public/endpoint_alerts/store/reducer.ts +++ b/x-pack/plugins/siem/public/endpoint_alerts/store/reducer.ts @@ -4,26 +4,25 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Immutable, AlertListState } from '../../../common/endpoint_alerts/types'; +import { Immutable } from '../../../common/endpoint/types'; +import { AlertListState } from '../../../common/endpoint_alerts/types'; import { ImmutableReducer } from '../../common/store'; import { AppAction } from '../../common/store/actions'; -export const initialAlertListState = (): Immutable => { - return { - alerts: [], - alertDetails: undefined, - pageSize: 10, - pageIndex: 0, - total: 0, - location: undefined, - searchBar: { - patterns: [], - }, - }; +export const initialAlertListState: Immutable = { + alerts: [], + alertDetails: undefined, + pageSize: 10, + pageIndex: 0, + total: 0, + location: undefined, + searchBar: { + patterns: [], + }, }; export const alertListReducer: ImmutableReducer = ( - state = initialAlertListState(), + state = initialAlertListState, action ) => { if (action.type === 'serverReturnedAlertsData') { diff --git a/x-pack/plugins/siem/public/endpoint_alerts/store/selectors.ts b/x-pack/plugins/siem/public/endpoint_alerts/store/selectors.ts index bec524f948d762..ab0e4165a25771 100644 --- a/x-pack/plugins/siem/public/endpoint_alerts/store/selectors.ts +++ b/x-pack/plugins/siem/public/endpoint_alerts/store/selectors.ts @@ -12,10 +12,10 @@ import { } from 'reselect'; import { encode, decode } from 'rison-node'; +import { Immutable } from '../../../common/endpoint/types'; import { Query, TimeRange, Filter } from '../../../../../../src/plugins/data/public'; import { - Immutable, AlertingIndexGetQueryInput, AlertListState, AlertingIndexUIQueryParams, diff --git a/x-pack/plugins/siem/public/endpoint_alerts/view/alert_details.test.tsx b/x-pack/plugins/siem/public/endpoint_alerts/view/alert_details.test.tsx index a4fe4811fa6020..de939ad4f54c65 100644 --- a/x-pack/plugins/siem/public/endpoint_alerts/view/alert_details.test.tsx +++ b/x-pack/plugins/siem/public/endpoint_alerts/view/alert_details.test.tsx @@ -11,7 +11,7 @@ import { Store } from 'redux'; import { mockAlertDetailsResult } from '../store/mock_alert_result_list'; import { alertPageTestRender } from './test_helpers/render_alert_page'; import { AppAction } from '../../common/store/actions'; -import { State } from '../../common/store/reducer'; +import { State } from '../../common/store/types'; describe('when the alert details flyout is open', () => { let render: () => reactTestingLibrary.RenderResult; diff --git a/x-pack/plugins/siem/public/endpoint_alerts/view/details/metadata/file_accordion.tsx b/x-pack/plugins/siem/public/endpoint_alerts/view/details/metadata/file_accordion.tsx index 1009bec0cec0e7..e319cbe52a69c9 100644 --- a/x-pack/plugins/siem/public/endpoint_alerts/view/details/metadata/file_accordion.tsx +++ b/x-pack/plugins/siem/public/endpoint_alerts/view/details/metadata/file_accordion.tsx @@ -6,7 +6,8 @@ import React, { memo, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiAccordion, EuiDescriptionList } from '@elastic/eui'; -import { Immutable, AlertData } from '../../../../../common/endpoint_alerts/types'; +import { Immutable } from '../../../../../common/endpoint/types'; +import { AlertData } from '../../../../../common/endpoint_alerts/types'; import { FormattedDate } from '../../formatted_date'; export const FileAccordion = memo(({ alertData }: { alertData: Immutable }) => { diff --git a/x-pack/plugins/siem/public/endpoint_alerts/view/details/metadata/general_accordion.tsx b/x-pack/plugins/siem/public/endpoint_alerts/view/details/metadata/general_accordion.tsx index fc0d38188fd277..76ee46cb5a1075 100644 --- a/x-pack/plugins/siem/public/endpoint_alerts/view/details/metadata/general_accordion.tsx +++ b/x-pack/plugins/siem/public/endpoint_alerts/view/details/metadata/general_accordion.tsx @@ -6,8 +6,9 @@ import React, { memo, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiAccordion, EuiDescriptionList } from '@elastic/eui'; -import { Immutable, AlertData } from '../../../../../common/endpoint_alerts/types'; +import { AlertData } from '../../../../../common/endpoint_alerts/types'; import { FormattedDate } from '../../formatted_date'; +import { Immutable } from '../../../../../common/endpoint/types'; export const GeneralAccordion = memo(({ alertData }: { alertData: Immutable }) => { const columns = useMemo(() => { diff --git a/x-pack/plugins/siem/public/endpoint_alerts/view/details/metadata/hash_accordion.tsx b/x-pack/plugins/siem/public/endpoint_alerts/view/details/metadata/hash_accordion.tsx index ae62bd80b73bc1..3077a98905dff0 100644 --- a/x-pack/plugins/siem/public/endpoint_alerts/view/details/metadata/hash_accordion.tsx +++ b/x-pack/plugins/siem/public/endpoint_alerts/view/details/metadata/hash_accordion.tsx @@ -6,7 +6,8 @@ import React, { memo, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiAccordion, EuiDescriptionList } from '@elastic/eui'; -import { Immutable, AlertData } from '../../../../../common/endpoint_alerts/types'; +import { Immutable } from '../../../../../common/endpoint/types'; +import { AlertData } from '../../../../../common/endpoint_alerts/types'; export const HashAccordion = memo(({ alertData }: { alertData: Immutable }) => { const columns = useMemo(() => { diff --git a/x-pack/plugins/siem/public/endpoint_alerts/view/details/metadata/host_accordion.tsx b/x-pack/plugins/siem/public/endpoint_alerts/view/details/metadata/host_accordion.tsx index 70723efd97b8c5..bea49cbba06ba2 100644 --- a/x-pack/plugins/siem/public/endpoint_alerts/view/details/metadata/host_accordion.tsx +++ b/x-pack/plugins/siem/public/endpoint_alerts/view/details/metadata/host_accordion.tsx @@ -8,7 +8,8 @@ import { i18n } from '@kbn/i18n'; import { EuiAccordion, EuiDescriptionList, EuiHealth } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { Immutable, AlertDetails } from '../../../../../common/endpoint_alerts/types'; +import { Immutable } from '../../../../../common/endpoint/types'; +import { AlertDetails } from '../../../../../common/endpoint_alerts/types'; export const HostAccordion = memo(({ alertData }: { alertData: Immutable }) => { const columns = useMemo(() => { diff --git a/x-pack/plugins/siem/public/endpoint_alerts/view/details/metadata/source_process_accordion.tsx b/x-pack/plugins/siem/public/endpoint_alerts/view/details/metadata/source_process_accordion.tsx index 607327a49de1c1..b2d0e369bc453c 100644 --- a/x-pack/plugins/siem/public/endpoint_alerts/view/details/metadata/source_process_accordion.tsx +++ b/x-pack/plugins/siem/public/endpoint_alerts/view/details/metadata/source_process_accordion.tsx @@ -6,7 +6,8 @@ import React, { memo, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiAccordion, EuiDescriptionList } from '@elastic/eui'; -import { Immutable, AlertData } from '../../../../../common/endpoint_alerts/types'; +import { Immutable } from '../../../../../common/endpoint/types'; +import { AlertData } from '../../../../../common/endpoint_alerts/types'; export const SourceProcessAccordion = memo(({ alertData }: { alertData: Immutable }) => { const columns = useMemo(() => { diff --git a/x-pack/plugins/siem/public/endpoint_alerts/view/details/metadata/source_process_token_accordion.tsx b/x-pack/plugins/siem/public/endpoint_alerts/view/details/metadata/source_process_token_accordion.tsx index 9be494d92a88dc..e559daa0a6350d 100644 --- a/x-pack/plugins/siem/public/endpoint_alerts/view/details/metadata/source_process_token_accordion.tsx +++ b/x-pack/plugins/siem/public/endpoint_alerts/view/details/metadata/source_process_token_accordion.tsx @@ -6,7 +6,8 @@ import React, { memo, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiAccordion, EuiDescriptionList } from '@elastic/eui'; -import { Immutable, AlertData } from '../../../../../common/endpoint_alerts/types'; +import { Immutable } from '../../../../../common/endpoint/types'; +import { AlertData } from '../../../../../common/endpoint_alerts/types'; export const SourceProcessTokenAccordion = memo( ({ alertData }: { alertData: Immutable }) => { diff --git a/x-pack/plugins/siem/public/endpoint_alerts/view/hooks/use_alerts_selector.ts b/x-pack/plugins/siem/public/endpoint_alerts/view/hooks/use_alerts_selector.ts index 726f6a453cb5de..95c347893b99ae 100644 --- a/x-pack/plugins/siem/public/endpoint_alerts/view/hooks/use_alerts_selector.ts +++ b/x-pack/plugins/siem/public/endpoint_alerts/view/hooks/use_alerts_selector.ts @@ -5,8 +5,9 @@ */ import { useSelector } from 'react-redux'; -import { Immutable, AlertListState } from '../../../../common/endpoint_alerts/types'; -import { State } from '../../../common/store/reducer'; +import { Immutable } from '../../../../common/endpoint/types'; +import { AlertListState } from '../../../../common/endpoint_alerts/types'; +import { State } from '../../../common/store/types'; export function useAlertListSelector( selector: ( diff --git a/x-pack/plugins/siem/public/endpoint_alerts/view/index.test.tsx b/x-pack/plugins/siem/public/endpoint_alerts/view/index.test.tsx index 4967d7661a0855..3d056ca5c188f1 100644 --- a/x-pack/plugins/siem/public/endpoint_alerts/view/index.test.tsx +++ b/x-pack/plugins/siem/public/endpoint_alerts/view/index.test.tsx @@ -12,7 +12,7 @@ import { Store } from 'redux'; import { mockAlertResultList } from '../store/mock_alert_result_list'; import { alertPageTestRender } from './test_helpers/render_alert_page'; import { DepsStartMock } from '../../common/mock/endpoint'; -import { State } from '../../common/store/reducer'; +import { State } from '../../common/store/types'; import { AppAction } from '../../common/store/actions'; describe('when on the alerting page', () => { diff --git a/x-pack/plugins/siem/public/endpoint_hosts/index.ts b/x-pack/plugins/siem/public/endpoint_hosts/index.ts index 1c2649ec5cf91b..bd1c5f96f8cdaa 100644 --- a/x-pack/plugins/siem/public/endpoint_hosts/index.ts +++ b/x-pack/plugins/siem/public/endpoint_hosts/index.ts @@ -4,15 +4,32 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Reducer } from 'redux'; import { SecuritySubPluginWithStore } from '../app/types'; -import { getEndpointHostsRoutes } from './routes'; -import { initialHostListState, hostListReducer } from './store/reducer'; -import { Immutable } from '../../common/endpoint/types'; +import { endpointHostsRoutes } from './routes'; +import { hostListReducer } from './store/reducer'; import { HostState } from './types'; import { hostMiddlewareFactory } from './store/middleware'; import { CoreStart } from '../../../../../src/core/public'; import { StartPlugins } from '../types'; import { substateMiddlewareFactory } from '../common/store'; +import { AppAction } from '../common/store/actions'; + +/** + * Internally, our state is sometimes immutable, ignore that in our external + * interface. + */ +export interface EndpointHostsPluginState { + hostList: HostState; +} + +/** + * Internally, we use `ImmutableReducer`, but we present a regular reducer + * externally for compatibility w/ regular redux. + */ +export interface EndpointHostsPluginReducer { + hostList: Reducer; +} export class EndpointHosts { public setup() {} @@ -20,7 +37,7 @@ export class EndpointHosts { public start( core: CoreStart, plugins: StartPlugins - ): SecuritySubPluginWithStore<'hostList', Immutable> { + ): SecuritySubPluginWithStore<'hostList', HostState> { const { data, ingestManager } = plugins; const middleware = [ substateMiddlewareFactory( @@ -29,10 +46,14 @@ export class EndpointHosts { ), ]; return { - routes: getEndpointHostsRoutes(), + routes: endpointHostsRoutes(), store: { - initialState: { hostList: initialHostListState() }, - reducer: { hostList: hostListReducer }, + initialState: { hostList: undefined }, + /** + * Cast the ImmutableReducer to a regular reducer for compatibility with + * the subplugin architecture (which expects plain redux reducers.) + */ + reducer: { hostList: hostListReducer } as EndpointHostsPluginReducer, middleware, }, }; diff --git a/x-pack/plugins/siem/public/endpoint_hosts/routes.tsx b/x-pack/plugins/siem/public/endpoint_hosts/routes.tsx index b7e549dc4e5e8e..4ff9ecfaeab0e3 100644 --- a/x-pack/plugins/siem/public/endpoint_hosts/routes.tsx +++ b/x-pack/plugins/siem/public/endpoint_hosts/routes.tsx @@ -9,7 +9,7 @@ import { Route } from 'react-router-dom'; import { HostList } from './view'; -export const getEndpointHostsRoutes = () => [ +export const endpointHostsRoutes = () => [ , diff --git a/x-pack/plugins/siem/public/endpoint_hosts/store/index.test.ts b/x-pack/plugins/siem/public/endpoint_hosts/store/index.test.ts index 8518c37fe3f5d2..71452993abf07e 100644 --- a/x-pack/plugins/siem/public/endpoint_hosts/store/index.test.ts +++ b/x-pack/plugins/siem/public/endpoint_hosts/store/index.test.ts @@ -5,10 +5,11 @@ */ import { createStore, Dispatch, Store } from 'redux'; -import { HostAction, hostListReducer } from './index'; import { HostState } from '../types'; import { listData } from './selectors'; import { mockHostResultList } from './mock_host_result_list'; +import { HostAction } from './action'; +import { hostListReducer } from './reducer'; describe('HostList store concerns', () => { let store: Store; diff --git a/x-pack/plugins/siem/public/endpoint_hosts/store/index.ts b/x-pack/plugins/siem/public/endpoint_hosts/store/index.ts deleted file mode 100644 index eafea5b9c7404e..00000000000000 --- a/x-pack/plugins/siem/public/endpoint_hosts/store/index.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 { HostState } from '../types'; -import { ImmutableReducer } from '../../common/store'; -import { AppAction } from '../../common/store/actions'; -import { Immutable } from '../../../common/endpoint/types'; - -export { hostListReducer } from './reducer'; -export { HostAction } from './action'; -export { hostMiddlewareFactory } from './middleware'; - -export interface EndpointHostsPluginState { - hostList: Immutable; -} - -export interface EndpointHostsPluginReducer { - hostList: ImmutableReducer; -} diff --git a/x-pack/plugins/siem/public/endpoint_hosts/store/middleware.test.ts b/x-pack/plugins/siem/public/endpoint_hosts/store/middleware.test.ts index 6c9e3dd41907f6..0959a3438aad9a 100644 --- a/x-pack/plugins/siem/public/endpoint_hosts/store/middleware.test.ts +++ b/x-pack/plugins/siem/public/endpoint_hosts/store/middleware.test.ts @@ -7,7 +7,6 @@ import { CoreStart, HttpSetup } from 'kibana/public'; import { applyMiddleware, createStore, Store } from 'redux'; import { coreMock } from '../../../../../../src/core/public/mocks'; import { History, createBrowserHistory } from 'history'; -import { hostListReducer, hostMiddlewareFactory } from './index'; import { DepsStartMock, depsStartMock } from '../../common/mock/endpoint'; @@ -17,6 +16,8 @@ import { AppAction } from '../../common/store/actions'; import { mockHostResultList } from './mock_host_result_list'; import { listData } from './selectors'; import { HostState } from '../types'; +import { hostListReducer } from './reducer'; +import { hostMiddlewareFactory } from './middleware'; describe('host list middleware', () => { let fakeCoreStart: jest.Mocked; diff --git a/x-pack/plugins/siem/public/endpoint_hosts/store/middleware.ts b/x-pack/plugins/siem/public/endpoint_hosts/store/middleware.ts index b9d84756010232..dd9ab19a702eaa 100644 --- a/x-pack/plugins/siem/public/endpoint_hosts/store/middleware.ts +++ b/x-pack/plugins/siem/public/endpoint_hosts/store/middleware.ts @@ -4,14 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { HostResultList, Immutable } from '../../../common/endpoint/types'; +import { HostResultList } from '../../../common/endpoint/types'; import { ImmutableMiddlewareFactory } from '../../common/store'; import { isOnHostPage, hasSelectedHost, uiQueryParams, listData } from './selectors'; import { HostState } from '../types'; -export const hostMiddlewareFactory: ImmutableMiddlewareFactory> = ( - coreStart -) => { +export const hostMiddlewareFactory: ImmutableMiddlewareFactory = (coreStart) => { return ({ getState, dispatch }) => (next) => async (action) => { next(action); const state = getState(); diff --git a/x-pack/plugins/siem/public/endpoint_hosts/store/reducer.ts b/x-pack/plugins/siem/public/endpoint_hosts/store/reducer.ts index 98f4a457a4598e..c0d5e6931db2b7 100644 --- a/x-pack/plugins/siem/public/endpoint_hosts/store/reducer.ts +++ b/x-pack/plugins/siem/public/endpoint_hosts/store/reducer.ts @@ -10,26 +10,24 @@ import { AppAction } from '../../common/store/actions'; import { ImmutableReducer } from '../../common/store'; import { Immutable } from '../../../common/endpoint/types'; -export const initialHostListState = (): HostState => { - return { - hosts: [], - pageSize: 10, - pageIndex: 0, - total: 0, - loading: false, - error: undefined, - details: undefined, - detailsLoading: false, - detailsError: undefined, - policyResponse: undefined, - policyResponseLoading: false, - policyResponseError: undefined, - location: undefined, - }; +export const initialHostListState: Immutable = { + hosts: [], + pageSize: 10, + pageIndex: 0, + total: 0, + loading: false, + error: undefined, + details: undefined, + detailsLoading: false, + detailsError: undefined, + policyResponse: undefined, + policyResponseLoading: false, + policyResponseError: undefined, + location: undefined, }; export const hostListReducer: ImmutableReducer = ( - state = initialHostListState(), + state = initialHostListState, action ) => { if (action.type === 'serverReturnedHostList') { diff --git a/x-pack/plugins/siem/public/endpoint_hosts/view/hooks.ts b/x-pack/plugins/siem/public/endpoint_hosts/view/hooks.ts index 6d552831c2fec4..78fd679f818b64 100644 --- a/x-pack/plugins/siem/public/endpoint_hosts/view/hooks.ts +++ b/x-pack/plugins/siem/public/endpoint_hosts/view/hooks.ts @@ -8,7 +8,7 @@ import { useSelector } from 'react-redux'; import { useMemo } from 'react'; import { HostState } from '../types'; import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; -import { State } from '../../common/store/reducer'; +import { State } from '../../common/store/types'; export function useHostSelector(selector: (state: HostState) => TSelected) { return useSelector(function (state: State) { diff --git a/x-pack/plugins/siem/public/hosts/store/selectors.ts b/x-pack/plugins/siem/public/hosts/store/selectors.ts index aee93441e94091..61fa44a5f41d15 100644 --- a/x-pack/plugins/siem/public/hosts/store/selectors.ts +++ b/x-pack/plugins/siem/public/hosts/store/selectors.ts @@ -7,7 +7,7 @@ import { get } from 'lodash/fp'; import { createSelector } from 'reselect'; -import { State } from '../../common/store/reducer'; +import { State } from '../../common/store/types'; import { GenericHostsModel, HostsType, HostsTableType } from './model'; diff --git a/x-pack/plugins/siem/public/management/index.ts b/x-pack/plugins/siem/public/management/index.ts index 86522df110dfb5..d6a723e5340bfa 100644 --- a/x-pack/plugins/siem/public/management/index.ts +++ b/x-pack/plugins/siem/public/management/index.ts @@ -5,33 +5,53 @@ */ import { CoreStart } from 'kibana/public'; -import { managementReducer, getManagementInitialState, managementMiddlewareFactory } from './store'; -import { getManagementRoutes } from './routes'; +import { Reducer, CombinedState } from 'redux'; +import { managementRoutes } from './routes'; import { StartPlugins } from '../types'; -import { MANAGEMENT_STORE_GLOBAL_NAMESPACE } from './common/constants'; import { SecuritySubPluginWithStore } from '../app/types'; -import { Immutable } from '../../common/endpoint/types'; -import { ManagementStoreGlobalNamespace } from './types'; -import { ManagementState } from './store/types'; +import { managementReducer } from './store/reducer'; +import { AppAction } from '../common/store/actions'; +import { managementMiddlewareFactory } from './store/middleware'; +import { ManagementState } from './types'; export { getManagementUrl } from './common/routing'; +/** + * Internally, our state is sometimes immutable, ignore that in our external + * interface. + */ +export interface ManagementPluginState { + management: ManagementState; +} + +/** + * Internally, we use `ImmutableReducer`, but we present a regular reducer + * externally for compatibility w/ regular redux. + */ +export interface ManagementPluginReducer { + management: Reducer, AppAction>; +} + export class Management { public setup() {} public start( core: CoreStart, plugins: StartPlugins - ): SecuritySubPluginWithStore> { + ): SecuritySubPluginWithStore<'management', ManagementState> { return { - routes: getManagementRoutes(), + routes: managementRoutes(), store: { initialState: { - [MANAGEMENT_STORE_GLOBAL_NAMESPACE]: getManagementInitialState(), + management: undefined, }, + /** + * Cast the ImmutableReducer to a regular reducer for compatibility with + * the subplugin architecture (which expects plain redux reducers.) + */ reducer: { - [MANAGEMENT_STORE_GLOBAL_NAMESPACE]: managementReducer, - }, + management: managementReducer, + } as ManagementPluginReducer, middleware: managementMiddlewareFactory(core, plugins), }, }; diff --git a/x-pack/plugins/siem/public/management/pages/policy/store/policy_details/middleware.ts b/x-pack/plugins/siem/public/management/pages/policy/store/policy_details/middleware.ts index 97cdcac0fcae93..ec0c526482b453 100644 --- a/x-pack/plugins/siem/public/management/pages/policy/store/policy_details/middleware.ts +++ b/x-pack/plugins/siem/public/management/pages/policy/store/policy_details/middleware.ts @@ -16,13 +16,13 @@ import { sendGetFleetAgentStatusForConfig, sendPutDatasource, } from '../policy_list/services/ingest'; -import { NewPolicyData, PolicyData, Immutable } from '../../../../../../common/endpoint/types'; +import { NewPolicyData, PolicyData } from '../../../../../../common/endpoint/types'; import { factory as policyConfigFactory } from '../../../../../../common/endpoint/models/policy_config'; import { ImmutableMiddlewareFactory } from '../../../../../common/store'; -export const policyDetailsMiddlewareFactory: ImmutableMiddlewareFactory> = (coreStart) => { +export const policyDetailsMiddlewareFactory: ImmutableMiddlewareFactory = ( + coreStart +) => { const http = coreStart.http; return ({ getState, dispatch }) => (next) => async (action) => { diff --git a/x-pack/plugins/siem/public/management/pages/policy/store/policy_details/reducer.ts b/x-pack/plugins/siem/public/management/pages/policy/store/policy_details/reducer.ts index 95daad7ae59778..75e7808ea30b1d 100644 --- a/x-pack/plugins/siem/public/management/pages/policy/store/policy_details/reducer.ts +++ b/x-pack/plugins/siem/public/management/pages/policy/store/policy_details/reducer.ts @@ -4,24 +4,25 @@ * you may not use this file except in compliance with the Elastic License. */ import { fullPolicy, isOnPolicyDetailsPage } from './selectors'; -import { PolicyDetailsState } from '../../types'; import { Immutable, PolicyConfig, UIPolicyConfig } from '../../../../../../common/endpoint/types'; import { ImmutableReducer } from '../../../../../common/store'; import { AppAction } from '../../../../../common/store/actions'; +import { PolicyDetailsState } from '../../types'; -export const initialPolicyDetailsState = (): PolicyDetailsState => { - return { - policyItem: undefined, - isLoading: false, - agentStatusSummary: { - error: 0, - events: 0, - offline: 0, - online: 0, - total: 0, - }, - }; -}; +/** + * Return a fresh copy of initial state, since we mutate state in the reducer. + */ +export const initialPolicyDetailsState: () => Immutable = () => ({ + policyItem: undefined, + isLoading: false, + agentStatusSummary: { + error: 0, + events: 0, + offline: 0, + online: 0, + total: 0, + }, +}); export const policyDetailsReducer: ImmutableReducer = ( state = initialPolicyDetailsState(), diff --git a/x-pack/plugins/siem/public/management/pages/policy/store/policy_list/index.test.ts b/x-pack/plugins/siem/public/management/pages/policy/store/policy_list/index.test.ts index c796edff8aabcb..a312134bbcd224 100644 --- a/x-pack/plugins/siem/public/management/pages/policy/store/policy_list/index.test.ts +++ b/x-pack/plugins/siem/public/management/pages/policy/store/policy_list/index.test.ts @@ -10,7 +10,7 @@ import { Store, applyMiddleware, createStore } from 'redux'; import { coreMock } from '../../../../../../../../../src/core/public/mocks'; import { DATASOURCE_SAVED_OBJECT_TYPE } from '../../../../../../../ingest_manager/common'; -import { policyListReducer, initialPolicyListState } from './reducer'; +import { policyListReducer } from './reducer'; import { policyListMiddlewareFactory } from './middleware'; import { isOnPolicyListPage, selectIsLoading, urlSearchParams } from './selectors'; @@ -39,7 +39,7 @@ describe('policy list store concerns', () => { store = createStore( policyListReducer, - initialPolicyListState(), + undefined, applyMiddleware(policyListMiddlewareFactory(fakeCoreStart, depsStart), actionSpyMiddleware) ); }); diff --git a/x-pack/plugins/siem/public/management/pages/policy/store/policy_list/middleware.ts b/x-pack/plugins/siem/public/management/pages/policy/store/policy_list/middleware.ts index 7259c0fd4d21cd..66962c378537f1 100644 --- a/x-pack/plugins/siem/public/management/pages/policy/store/policy_list/middleware.ts +++ b/x-pack/plugins/siem/public/management/pages/policy/store/policy_list/middleware.ts @@ -8,10 +8,9 @@ import { GetPolicyListResponse, PolicyListState } from '../../types'; import { sendGetEndpointSpecificDatasources } from './services/ingest'; import { isOnPolicyListPage, urlSearchParams } from './selectors'; import { ImmutableMiddlewareFactory } from '../../../../../common/store'; -import { Immutable } from '../../../../../../common/endpoint/types'; import { initialPolicyListState } from './reducer'; -export const policyListMiddlewareFactory: ImmutableMiddlewareFactory> = ( +export const policyListMiddlewareFactory: ImmutableMiddlewareFactory = ( coreStart ) => { const http = coreStart.http; @@ -20,7 +19,6 @@ export const policyListMiddlewareFactory: ImmutableMiddlewareFactory { - return { - policyItems: [], - isLoading: false, - apiError: undefined, - pageIndex: 0, - pageSize: 10, - total: 0, - location: undefined, - }; -}; +/** + * Return the initial state. + * In case `state` was mutated, we return a fresh initial state object. + */ +export const initialPolicyListState: () => Immutable = () => ({ + policyItems: [], + isLoading: false, + apiError: undefined, + pageIndex: 0, + pageSize: 10, + total: 0, + location: undefined, +}); export const policyListReducer: ImmutableReducer = ( state = initialPolicyListState(), diff --git a/x-pack/plugins/siem/public/management/routes.tsx b/x-pack/plugins/siem/public/management/routes.tsx index fbcea37c76962a..12727ea97458ed 100644 --- a/x-pack/plugins/siem/public/management/routes.tsx +++ b/x-pack/plugins/siem/public/management/routes.tsx @@ -12,7 +12,7 @@ import { MANAGEMENT_ROUTING_ROOT_PATH } from './common/constants'; /** * Returns the React Router Routes for the management area */ -export const getManagementRoutes = () => [ +export const managementRoutes = () => [ // Mounts the Management interface on `/management` , ]; diff --git a/x-pack/plugins/siem/public/management/store/index.ts b/x-pack/plugins/siem/public/management/store/index.ts deleted file mode 100644 index 50049f98280823..00000000000000 --- a/x-pack/plugins/siem/public/management/store/index.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 { managementReducer, getManagementInitialState } from './reducer'; -export { managementMiddlewareFactory } from './middleware'; diff --git a/x-pack/plugins/siem/public/management/store/middleware.ts b/x-pack/plugins/siem/public/management/store/middleware.ts index f73736e04a5b70..c8eb27e35f9dd9 100644 --- a/x-pack/plugins/siem/public/management/store/middleware.ts +++ b/x-pack/plugins/siem/public/management/store/middleware.ts @@ -4,30 +4,23 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ImmutableMultipleMiddlewareFactory, substateMiddlewareFactory } from '../../common/store'; +import { + substateMiddlewareFactory, + SecuritySubPluginMiddlewareFactory, + State, +} from '../../common/store'; import { policyListMiddlewareFactory } from '../pages/policy/store/policy_list'; import { policyDetailsMiddlewareFactory } from '../pages/policy/store/policy_details'; -import { - MANAGEMENT_STORE_GLOBAL_NAMESPACE, - MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE, - MANAGEMENT_STORE_POLICY_LIST_NAMESPACE, -} from '../common/constants'; -// @ts-ignore -export const managementMiddlewareFactory: ImmutableMultipleMiddlewareFactory = ( +export const managementMiddlewareFactory: SecuritySubPluginMiddlewareFactory = ( coreStart, depsStart ) => { + const listSelector = (state: State) => state.management.policyList; + const detailSelector = (state: State) => state.management.policyDetails; + return [ - substateMiddlewareFactory( - (globalState) => - globalState[MANAGEMENT_STORE_GLOBAL_NAMESPACE][MANAGEMENT_STORE_POLICY_LIST_NAMESPACE], - policyListMiddlewareFactory(coreStart, depsStart) - ), - substateMiddlewareFactory( - (globalState) => - globalState[MANAGEMENT_STORE_GLOBAL_NAMESPACE][MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE], - policyDetailsMiddlewareFactory(coreStart, depsStart) - ), + substateMiddlewareFactory(listSelector, policyListMiddlewareFactory(coreStart, depsStart)), + substateMiddlewareFactory(detailSelector, policyDetailsMiddlewareFactory(coreStart, depsStart)), ]; }; diff --git a/x-pack/plugins/siem/public/management/store/reducer.ts b/x-pack/plugins/siem/public/management/store/reducer.ts index ba7927684ad3d8..64b2ab5c05f96a 100644 --- a/x-pack/plugins/siem/public/management/store/reducer.ts +++ b/x-pack/plugins/siem/public/management/store/reducer.ts @@ -4,42 +4,34 @@ * you may not use this file except in compliance with the Elastic License. */ -import { combineReducers as reduxCombineReducers } from 'redux'; +import { combineReducers } from 'redux'; import { - initialPolicyDetailsState, policyDetailsReducer, + initialPolicyDetailsState, } from '../pages/policy/store/policy_details/reducer'; import { - initialPolicyListState, policyListReducer, + initialPolicyListState, } from '../pages/policy/store/policy_list/reducer'; import { MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE, MANAGEMENT_STORE_POLICY_LIST_NAMESPACE, } from '../common/constants'; import { ImmutableCombineReducers } from '../../common/store'; -import { AppAction } from '../../common/store/actions'; -import { ManagementState } from './types'; +import { Immutable } from '../../../common/endpoint/types'; +import { ManagementState } from '../types'; -// Change the type of `combinerReducers` locally -const combineReducers: ImmutableCombineReducers = reduxCombineReducers; +const immutableCombineReducers: ImmutableCombineReducers = combineReducers; -/** - * Returns the initial state of the store for the SIEM Management section - */ -export const getManagementInitialState = (): ManagementState => { - return { - [MANAGEMENT_STORE_POLICY_LIST_NAMESPACE]: initialPolicyListState(), - [MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE]: initialPolicyDetailsState(), - }; +export const mockManagementState: Immutable = { + policyList: initialPolicyListState(), + policyDetails: initialPolicyDetailsState(), }; /** * Redux store reducer for the SIEM Management section */ -export const managementReducer = combineReducers({ - // @ts-ignore +export const managementReducer = immutableCombineReducers({ [MANAGEMENT_STORE_POLICY_LIST_NAMESPACE]: policyListReducer, - // @ts-ignore [MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE]: policyDetailsReducer, }); diff --git a/x-pack/plugins/siem/public/management/store/types.ts b/x-pack/plugins/siem/public/management/store/types.ts deleted file mode 100644 index 884724982fa8f4..00000000000000 --- a/x-pack/plugins/siem/public/management/store/types.ts +++ /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 { Immutable } from '../../../common/endpoint/types'; -import { PolicyDetailsState, PolicyListState } from '../pages/policy/types'; -import { ImmutableReducer } from '../../common/store'; -import { AppAction } from '../../common/store/actions'; - -/** - * Redux store state for the Management section - */ -export interface ManagementState { - policyDetails: Immutable; - policyList: Immutable; -} - -export interface ManagementPluginState { - management: ManagementState; -} - -export interface ManagementPluginReducer { - management: ImmutableReducer; -} diff --git a/x-pack/plugins/siem/public/management/types.ts b/x-pack/plugins/siem/public/management/types.ts index 5ee16bcd434e3a..eeeafb4cbe1509 100644 --- a/x-pack/plugins/siem/public/management/types.ts +++ b/x-pack/plugins/siem/public/management/types.ts @@ -4,7 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ +import { CombinedState } from 'redux'; import { SiemPageName } from '../app/types'; +import { PolicyListState, PolicyDetailsState } from './pages/policy/types'; /** * The type for the management store global namespace. Used mostly internally to reference @@ -12,6 +14,11 @@ import { SiemPageName } from '../app/types'; */ export type ManagementStoreGlobalNamespace = 'management'; +export type ManagementState = CombinedState<{ + policyList: PolicyListState; + policyDetails: PolicyDetailsState; +}>; + /** * The management list of sub-tabs. Changes to these will impact the Router routes. */ diff --git a/x-pack/plugins/siem/public/network/store/selectors.ts b/x-pack/plugins/siem/public/network/store/selectors.ts index eac373cd63ebd1..cef8b139402eff 100644 --- a/x-pack/plugins/siem/public/network/store/selectors.ts +++ b/x-pack/plugins/siem/public/network/store/selectors.ts @@ -8,7 +8,7 @@ import { createSelector } from 'reselect'; import { get } from 'lodash/fp'; import { FlowTargetSourceDest } from '../../graphql/types'; -import { State } from '../../common/store/reducer'; +import { State } from '../../common/store/types'; import { initialNetworkState } from './reducer'; import { IpDetailsTableType, diff --git a/x-pack/plugins/siem/public/timelines/store/timeline/model.ts b/x-pack/plugins/siem/public/timelines/store/timeline/model.ts index df5e2a99e0248c..bf86d6363861e3 100644 --- a/x-pack/plugins/siem/public/timelines/store/timeline/model.ts +++ b/x-pack/plugins/siem/public/timelines/store/timeline/model.ts @@ -14,7 +14,7 @@ import { TimelineType, TimelineStatus, } from '../../../graphql/types'; -import { KueryFilterQuery, SerializedFilterQuery } from '../../../common/store/model'; +import { KueryFilterQuery, SerializedFilterQuery } from '../../../common/store/types'; export const DEFAULT_PAGE_COUNT = 2; // Eui Pager will not render unless this is a minimum of 2 pages export type KqlMode = 'filter' | 'search'; diff --git a/x-pack/plugins/siem/public/timelines/store/timeline/selectors.ts b/x-pack/plugins/siem/public/timelines/store/timeline/selectors.ts index d4c8f3cf3a2c26..af7ac075468c38 100644 --- a/x-pack/plugins/siem/public/timelines/store/timeline/selectors.ts +++ b/x-pack/plugins/siem/public/timelines/store/timeline/selectors.ts @@ -7,7 +7,7 @@ import { createSelector } from 'reselect'; import { isFromKueryExpressionValid } from '../../../common/lib/keury'; -import { State } from '../../../common/store/reducer'; +import { State } from '../../../common/store/types'; import { TimelineModel } from './model'; import { AutoSavedWarningMsg, TimelineById } from './types'; diff --git a/x-pack/plugins/siem/scripts/endpoint/resolver_generator.ts b/x-pack/plugins/siem/scripts/endpoint/resolver_generator.ts index 77bf200eeb540a..26c6e5ccc28a88 100644 --- a/x-pack/plugins/siem/scripts/endpoint/resolver_generator.ts +++ b/x-pack/plugins/siem/scripts/endpoint/resolver_generator.ts @@ -70,7 +70,7 @@ async function main() { metadataIndex: { alias: 'mi', describe: 'index to store host metadata in', - default: 'metrics-endpoint-default-1', + default: 'metrics-endpoint.metadata-default-1', type: 'string', }, policyIndex: { diff --git a/x-pack/plugins/siem/server/endpoint/alerts/index_pattern.ts b/x-pack/plugins/siem/server/endpoint/alerts/index_pattern.ts index 1cbdf96c5bceec..391aedecdd0995 100644 --- a/x-pack/plugins/siem/server/endpoint/alerts/index_pattern.ts +++ b/x-pack/plugins/siem/server/endpoint/alerts/index_pattern.ts @@ -22,6 +22,7 @@ export interface IndexPatternRetriever { export class IngestIndexPatternRetriever implements IndexPatternRetriever { private static endpointPackageName = 'endpoint'; private static metadataDataset = 'metadata'; + private static policyDataset = 'policy'; private readonly log: Logger; constructor(private readonly service: ESIndexPatternService, loggerFactory: LoggerFactory) { this.log = loggerFactory.get('index-pattern-retriever'); @@ -76,7 +77,7 @@ export class IngestIndexPatternRetriever implements IndexPatternRetriever { } } - getPolicyResponseIndexPattern(ctx: RequestHandlerContext): Promise { - return Promise.resolve('metrics-endpoint.policy-default-1'); + async getPolicyResponseIndexPattern(ctx: RequestHandlerContext): Promise { + return this.getIndexPattern(ctx, IngestIndexPatternRetriever.policyDataset); } } diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts index 0d0cd28738c928..e9c0ca08c88ee5 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts @@ -122,6 +122,7 @@ export const patchRulesBulkRoute = (router: IRouter, ml: SetupPlugins['ml']) => version, anomalyThreshold, machineLearningJobId, + actions, }); if (rule != null && rule.enabled != null && rule.name != null) { const ruleActions = await updateRulesNotifications({ diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.ts index ae23e0efc857d3..2a1ac9862e7d0f 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.ts @@ -118,6 +118,7 @@ export const patchRulesRoute = (router: IRouter, ml: SetupPlugins['ml']) => { version, anomalyThreshold, machineLearningJobId, + actions, }); if (rule != null && rule.enabled != null && rule.name != null) { const ruleActions = await updateRulesNotifications({ diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/patch_rules.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/patch_rules.test.ts index 3c1267c9393454..0dffa665f780b4 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/patch_rules.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/patch_rules.test.ts @@ -91,4 +91,82 @@ describe('patchRules', () => { }) ); }); + + describe('regression tests', () => { + it("updates the rule's actions if provided", async () => { + const existingRule = getResult(); + + const action = { + action_type_id: '.slack', + id: '2933e581-d81c-4fe3-88fe-c57c6b8a5bfd', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} signals', + }, + group: 'default', + }; + + await patchRules({ + alertsClient, + savedObjectsClient, + actions: [action], + rule: existingRule, + }); + + expect(alertsClient.update).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + actions: [ + { + actionTypeId: '.slack', + id: '2933e581-d81c-4fe3-88fe-c57c6b8a5bfd', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} signals', + }, + group: 'default', + }, + ], + }), + }) + ); + }); + + it('does not update actions if none are specified', async () => { + const existingRule = { + ...getResult(), + actions: [ + { + actionTypeId: '.slack', + id: '2933e581-d81c-4fe3-88fe-c57c6b8a5bfd', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} signals', + }, + group: 'default', + }, + ], + }; + + await patchRules({ + alertsClient, + savedObjectsClient, + rule: existingRule, + }); + + expect(alertsClient.update).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + actions: [ + { + actionTypeId: '.slack', + id: '2933e581-d81c-4fe3-88fe-c57c6b8a5bfd', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} signals', + }, + group: 'default', + }, + ], + }), + }) + ); + }); + }); }); diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts index 1e728ae7b8d0bf..950a3e90fb70cc 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts @@ -6,6 +6,7 @@ import { defaults } from 'lodash/fp'; import { PartialAlert } from '../../../../../alerts/server'; +import { transformRuleToAlertAction } from '../../../../common/detection_engine/transform_actions'; import { PatchRuleParams } from './types'; import { addTags } from './add_tags'; import { calculateVersion, calculateName, calculateInterval } from './utils'; @@ -44,6 +45,7 @@ export const patchRules = async ({ exceptions_list, anomalyThreshold, machineLearningJobId, + actions, }: PatchRuleParams): Promise => { if (rule == null) { return null; @@ -121,7 +123,7 @@ export const patchRules = async ({ schedule: { interval: calculateInterval(interval, rule.schedule.interval), }, - actions: rule.actions, + actions: actions?.map(transformRuleToAlertAction) ?? rule.actions, params: nextParams, }, }); diff --git a/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts b/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts index d8dc7fc89d94b6..ab539b38c3e41a 100644 --- a/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts +++ b/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts @@ -36,9 +36,9 @@ export const MonitorType = t.intersection([ check_group: t.string, ip: t.string, name: t.string, - timespan: t.partial({ + timespan: t.type({ gte: t.string, - lte: t.string, + lt: t.string, }), }), ]); @@ -55,13 +55,13 @@ export const PingType = t.intersection([ agent: t.intersection([ t.type({ ephemeral_id: t.string, - hostname: t.string, id: t.string, type: t.string, version: t.string, }), t.partial({ name: t.string, + hostname: t.string, }), ]), container: t.partial({ diff --git a/x-pack/test/api_integration/apis/endpoint/alerts/index.ts b/x-pack/test/api_integration/apis/endpoint/alerts/index.ts index ecdee09ce7edf4..155513aefc6091 100644 --- a/x-pack/test/api_integration/apis/endpoint/alerts/index.ts +++ b/x-pack/test/api_integration/apis/endpoint/alerts/index.ts @@ -70,7 +70,7 @@ export default function ({ getService }: FtrProviderContext) { let nullableEventId = ''; - describe.skip('Endpoint alert API', () => { + describe('Endpoint alert API', () => { describe('when data is in elasticsearch', () => { before(async () => { await esArchiver.load('endpoint/alerts/api_feature'); diff --git a/x-pack/test/api_integration/apis/endpoint/alerts/index_pattern.ts b/x-pack/test/api_integration/apis/endpoint/alerts/index_pattern.ts index df1cbcfe28e7be..ad9f4463c64196 100644 --- a/x-pack/test/api_integration/apis/endpoint/alerts/index_pattern.ts +++ b/x-pack/test/api_integration/apis/endpoint/alerts/index_pattern.ts @@ -9,7 +9,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - describe.skip('Endpoint index pattern API', () => { + describe('Endpoint index pattern API', () => { it('should retrieve the index pattern for events', async () => { const { body } = await supertest.get('/api/endpoint/index_pattern/events').expect(200); expect(body.indexPattern).to.eql('events-endpoint-*'); @@ -17,7 +17,12 @@ export default function ({ getService }: FtrProviderContext) { it('should retrieve the index pattern for metadata', async () => { const { body } = await supertest.get('/api/endpoint/index_pattern/metadata').expect(200); - expect(body.indexPattern).to.eql('metrics-endpoint-*'); + expect(body.indexPattern).to.eql('metrics-endpoint.metadata-*'); + }); + + it('should retrieve the index pattern for policy', async () => { + const { body } = await supertest.get('/api/endpoint/index_pattern/policy').expect(200); + expect(body.indexPattern).to.eql('metrics-endpoint.policy-*'); }); it('should not retrieve the index pattern for an invalid key', async () => { diff --git a/x-pack/test/api_integration/apis/endpoint/metadata.ts b/x-pack/test/api_integration/apis/endpoint/metadata.ts index c01919f60a9227..5c4bb52b8d9e2d 100644 --- a/x-pack/test/api_integration/apis/endpoint/metadata.ts +++ b/x-pack/test/api_integration/apis/endpoint/metadata.ts @@ -14,7 +14,7 @@ const numberOfHostsInFixture = 3; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); - describe.skip('test metadata api', () => { + 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'); diff --git a/x-pack/test/functional/es_archives/endpoint/alerts/host_api_feature/data.json.gz b/x-pack/test/functional/es_archives/endpoint/alerts/host_api_feature/data.json.gz index 3d4f0e11a7cc67..49082ed3bec8b0 100644 Binary files a/x-pack/test/functional/es_archives/endpoint/alerts/host_api_feature/data.json.gz and b/x-pack/test/functional/es_archives/endpoint/alerts/host_api_feature/data.json.gz differ diff --git a/x-pack/test/functional/es_archives/endpoint/alerts/host_api_feature/mappings.json b/x-pack/test/functional/es_archives/endpoint/alerts/host_api_feature/mappings.json index f9d5de0d0a94ce..ffd64ab3bb939b 100644 --- a/x-pack/test/functional/es_archives/endpoint/alerts/host_api_feature/mappings.json +++ b/x-pack/test/functional/es_archives/endpoint/alerts/host_api_feature/mappings.json @@ -2,7 +2,7 @@ "type": "index", "value": { "aliases": {}, - "index": "metrics-endpoint-default-1", + "index": "metrics-endpoint.metadata-default-1", "mappings": { "_meta": { "version": "1.5.0-dev" 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 d3617dc2363759..0f9f86b36dec79 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 @@ -2,7 +2,7 @@ "type": "doc", "value": { "id": "3KVN2G8BYQH1gtPUuYk7", - "index": "metrics-endpoint-default-1", + "index": "metrics-endpoint.metadata-default-1", "source": { "@timestamp": 1579881969541, "agent": { @@ -51,7 +51,7 @@ "type": "doc", "value": { "id": "3aVN2G8BYQH1gtPUuYk7", - "index": "metrics-endpoint-default-1", + "index": "metrics-endpoint.metadata-default-1", "source": { "@timestamp": 1579881969541, "agent": { @@ -99,7 +99,7 @@ "type": "doc", "value": { "id": "3qVN2G8BYQH1gtPUuYk7", - "index": "metrics-endpoint-default-1", + "index": "metrics-endpoint.metadata-default-1", "source": { "@timestamp": 1579881969541, "agent": { @@ -145,7 +145,7 @@ "type": "doc", "value": { "id": "36VN2G8BYQH1gtPUuYk7", - "index": "metrics-endpoint-default-1", + "index": "metrics-endpoint.metadata-default-1", "source": { "@timestamp": 1579878369541, "agent": { @@ -194,7 +194,7 @@ "type": "doc", "value": { "id": "4KVN2G8BYQH1gtPUuYk7", - "index": "metrics-endpoint-default-1", + "index": "metrics-endpoint.metadata-default-1", "source": { "@timestamp": 1579878369541, "agent": { @@ -241,7 +241,7 @@ "type": "doc", "value": { "id": "4aVN2G8BYQH1gtPUuYk7", - "index": "metrics-endpoint-default-1", + "index": "metrics-endpoint.metadata-default-1", "source": { "@timestamp": 1579878369541, "agent": { @@ -288,7 +288,7 @@ "type": "doc", "value": { "id": "4qVN2G8BYQH1gtPUuYk7", - "index": "metrics-endpoint-default-1", + "index": "metrics-endpoint.metadata-default-1", "source": { "@timestamp": 1579874769541, "agent": { @@ -336,7 +336,7 @@ "type": "doc", "value": { "id": "46VN2G8BYQH1gtPUuYk7", - "index": "metrics-endpoint-default-1", + "index": "metrics-endpoint.metadata-default-1", "source": { "@timestamp": 1579874769541, "agent": { @@ -383,7 +383,7 @@ "type": "doc", "value": { "id": "5KVN2G8BYQH1gtPUuYk7", - "index": "metrics-endpoint-default-1", + "index": "metrics-endpoint.metadata-default-1", "source": { "@timestamp": 1579874769541, "agent": { diff --git a/x-pack/test/functional/es_archives/endpoint/metadata/api_feature/mappings.json b/x-pack/test/functional/es_archives/endpoint/metadata/api_feature/mappings.json index f9d5de0d0a94ce..ffd64ab3bb939b 100644 --- a/x-pack/test/functional/es_archives/endpoint/metadata/api_feature/mappings.json +++ b/x-pack/test/functional/es_archives/endpoint/metadata/api_feature/mappings.json @@ -2,7 +2,7 @@ "type": "index", "value": { "aliases": {}, - "index": "metrics-endpoint-default-1", + "index": "metrics-endpoint.metadata-default-1", "mappings": { "_meta": { "version": "1.5.0-dev" diff --git a/yarn.lock b/yarn.lock index 5d47056857bbfa..5506165da2d342 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4865,9 +4865,9 @@ "@types/sinon" "*" "@types/sinon@*": - version "9.0.0" - resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-9.0.0.tgz#5b70a360f55645dd64f205defd2a31b749a59799" - integrity sha512-v2TkYHkts4VXshMkcmot/H+ERZ2SevKa10saGaJPGCJ8vh3lKrC4u663zYEeRZxep+VbG6YRDtQ6gVqw9dYzPA== + version "9.0.4" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-9.0.4.tgz#e934f904606632287a6e7f7ab0ce3f08a0dad4b1" + integrity sha512-sJmb32asJZY6Z2u09bl0G2wglSxDlROlAejCjsnor+LzBMz17gu8IU7vKC/vWDnv9zEq2wqADHVXFjf4eE8Gdw== dependencies: "@types/sinonjs__fake-timers" "*" @@ -10532,10 +10532,10 @@ cypress-multi-reporters@^1.2.3: debug "^4.1.1" lodash "^4.17.11" -cypress@^4.4.1: - version "4.4.1" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-4.4.1.tgz#f5aa1aa5f328f1299bff328103f7cbad89e80f29" - integrity sha512-LcskZ/PXRG9XTlEeeenKqz/KddT1x+7O7dqXsdKWPII01LxLNmNHIvHnlUqApchVbinJ5vir6J255CkELSeL0A== +cypress@4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-4.5.0.tgz#01940d085f6429cec3c87d290daa47bb976a7c7b" + integrity sha512-2A4g5FW5d2fHzq8HKUGAMVTnW6P8nlWYQALiCoGN4bqBLvgwhYM/oG9oKc2CS6LnvgHFiKivKzpm9sfk3uU3zQ== dependencies: "@cypress/listr-verbose-renderer" "0.4.1" "@cypress/request" "2.88.5"