From a1a717862b4dc21891bbdf03b3900aa3e2f00d21 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Thu, 9 Sep 2021 08:39:18 -0400 Subject: [PATCH 1/9] [Fleet] Fix config migration from ingestManager to support both xpack.fleet and xpack.ingestManager config values (#111612) --- x-pack/plugins/fleet/server/index.test.ts | 79 +++++++++++++++++++++++ x-pack/plugins/fleet/server/index.ts | 35 +++++++++- 2 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 x-pack/plugins/fleet/server/index.test.ts diff --git a/x-pack/plugins/fleet/server/index.test.ts b/x-pack/plugins/fleet/server/index.test.ts new file mode 100644 index 0000000000000..924fecc311073 --- /dev/null +++ b/x-pack/plugins/fleet/server/index.test.ts @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { applyDeprecations, configDeprecationFactory } from '@kbn/config'; + +import { config } from '.'; + +const applyConfigDeprecations = (settings: Record = {}) => { + if (!config.deprecations) { + throw new Error('Config is not valid no deprecations'); + } + const deprecations = config.deprecations(configDeprecationFactory); + const deprecationMessages: string[] = []; + const migrated = applyDeprecations( + settings, + deprecations.map((deprecation) => ({ + deprecation, + path: '', + })), + () => ({ message }) => deprecationMessages.push(message) + ); + return { + messages: deprecationMessages, + migrated: migrated.config, + }; +}; + +describe('Config depreciation test', () => { + it('should migrate old xpack.ingestManager.fleet settings to xpack.fleet.agents', () => { + const { migrated } = applyConfigDeprecations({ + xpack: { + ingestManager: { + fleet: { enabled: true, elasticsearch: { host: 'http://testes.fr:9200' } }, + }, + }, + }); + + expect(migrated).toMatchInlineSnapshot(` + Object { + "xpack": Object { + "fleet": Object { + "agents": Object { + "elasticsearch": Object { + "hosts": Array [ + "http://testes.fr:9200", + ], + }, + "enabled": true, + }, + }, + }, + } + `); + }); + + it('should support mixing xpack.ingestManager config and xpack.fleet config', () => { + const { migrated } = applyConfigDeprecations({ + xpack: { + ingestManager: { registryUrl: 'http://registrytest.fr' }, + fleet: { registryProxyUrl: 'http://registryProxy.fr' }, + }, + }); + + expect(migrated).toMatchInlineSnapshot(` + Object { + "xpack": Object { + "fleet": Object { + "registryProxyUrl": "http://registryProxy.fr", + "registryUrl": "http://registrytest.fr", + }, + }, + } + `); + }); +}); diff --git a/x-pack/plugins/fleet/server/index.ts b/x-pack/plugins/fleet/server/index.ts index 8841c897fcb2a..21cdf659f2f5a 100644 --- a/x-pack/plugins/fleet/server/index.ts +++ b/x-pack/plugins/fleet/server/index.ts @@ -38,9 +38,37 @@ export const config: PluginConfigDescriptor = { epm: true, agents: true, }, - deprecations: ({ renameFromRoot, unused }) => [ - renameFromRoot('xpack.ingestManager', 'xpack.fleet'), - renameFromRoot('xpack.fleet.fleet', 'xpack.fleet.agents'), + deprecations: ({ renameFromRoot, unused, unusedFromRoot }) => [ + // Fleet plugin was named ingestManager before + renameFromRoot('xpack.ingestManager.enabled', 'xpack.fleet.enabled'), + renameFromRoot('xpack.ingestManager.registryUrl', 'xpack.fleet.registryUrl'), + renameFromRoot('xpack.ingestManager.registryProxyUrl', 'xpack.fleet.registryProxyUrl'), + renameFromRoot('xpack.ingestManager.fleet', 'xpack.ingestManager.agents'), + renameFromRoot('xpack.ingestManager.agents.enabled', 'xpack.fleet.agents.enabled'), + renameFromRoot('xpack.ingestManager.agents.elasticsearch', 'xpack.fleet.agents.elasticsearch'), + renameFromRoot( + 'xpack.ingestManager.agents.tlsCheckDisabled', + 'xpack.fleet.agents.tlsCheckDisabled' + ), + renameFromRoot( + 'xpack.ingestManager.agents.pollingRequestTimeout', + 'xpack.fleet.agents.pollingRequestTimeout' + ), + renameFromRoot( + 'xpack.ingestManager.agents.maxConcurrentConnections', + 'xpack.fleet.agents.maxConcurrentConnections' + ), + renameFromRoot('xpack.ingestManager.agents.kibana', 'xpack.fleet.agents.kibana'), + renameFromRoot( + 'xpack.ingestManager.agents.agentPolicyRolloutRateLimitIntervalMs', + 'xpack.fleet.agents.agentPolicyRolloutRateLimitIntervalMs' + ), + renameFromRoot( + 'xpack.ingestManager.agents.agentPolicyRolloutRateLimitRequestPerInterval', + 'xpack.fleet.agents.agentPolicyRolloutRateLimitRequestPerInterval' + ), + unusedFromRoot('xpack.ingestManager'), + // Unused settings before Fleet server exists unused('agents.kibana'), unused('agents.maxConcurrentConnections'), unused('agents.agentPolicyRolloutRateLimitIntervalMs'), @@ -48,6 +76,7 @@ export const config: PluginConfigDescriptor = { unused('agents.pollingRequestTimeout'), unused('agents.tlsCheckDisabled'), unused('agents.fleetServerEnabled'), + // Renaming elasticsearch.host => elasticsearch.hosts (fullConfig, fromPath, addDeprecation) => { const oldValue = fullConfig?.xpack?.fleet?.agents?.elasticsearch?.host; if (oldValue) { From 9792c1079ea1b233f9d54e89ea7ada393a72ba26 Mon Sep 17 00:00:00 2001 From: Dmitry Tomashevich <39378793+Dmitriynj@users.noreply.github.com> Date: Thu, 9 Sep 2021 15:48:20 +0300 Subject: [PATCH 2/9] [Discover] Fix pagination when applying filter (#110763) * [Discover] fix pagination when applying filter * [Discover] refactoring to forward ref usage * [Discover] remove console log debug * [Discover] hide pagination on empty result * [Discover] add usePager test Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../doc_table/doc_table_embeddable.tsx | 95 ++++-- .../doc_table/doc_table_infinite.tsx | 147 +++++---- .../doc_table/doc_table_wrapper.tsx | 302 +++++++++--------- .../doc_table/lib/use_pager.test.tsx | 75 +++++ .../components/doc_table/lib/use_pager.ts | 69 +--- 5 files changed, 395 insertions(+), 293 deletions(-) create mode 100644 src/plugins/discover/public/application/apps/main/components/doc_table/lib/use_pager.test.tsx diff --git a/src/plugins/discover/public/application/apps/main/components/doc_table/doc_table_embeddable.tsx b/src/plugins/discover/public/application/apps/main/components/doc_table/doc_table_embeddable.tsx index 04902af692b74..c01f661eb116a 100644 --- a/src/plugins/discover/public/application/apps/main/components/doc_table/doc_table_embeddable.tsx +++ b/src/plugins/discover/public/application/apps/main/components/doc_table/doc_table_embeddable.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { memo, useCallback, useMemo } from 'react'; +import React, { memo, useCallback, useEffect, useMemo, useRef } from 'react'; import './index.scss'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; @@ -24,30 +24,61 @@ export interface DocTableEmbeddableProps extends DocTableProps { const DocTableWrapperMemoized = memo(DocTableWrapper); export const DocTableEmbeddable = (props: DocTableEmbeddableProps) => { - const pager = usePager({ totalItems: props.rows.length }); + const tableWrapperRef = useRef(null); + const { + currentPage, + pageSize, + totalPages, + startIndex, + hasNextPage, + changePage, + changePageSize, + } = usePager({ + totalItems: props.rows.length, + }); + const showPagination = totalPages !== 0; - const pageOfItems = useMemo( - () => props.rows.slice(pager.startIndex, pager.pageSize + pager.startIndex), - [pager.pageSize, pager.startIndex, props.rows] - ); + const scrollTop = useCallback(() => { + if (tableWrapperRef.current) { + tableWrapperRef.current.scrollTo(0, 0); + } + }, []); + + const pageOfItems = useMemo(() => props.rows.slice(startIndex, pageSize + startIndex), [ + pageSize, + startIndex, + props.rows, + ]); - const shouldShowLimitedResultsWarning = () => - !pager.hasNextPage && props.rows.length < props.totalHitCount; + const onPageChange = useCallback( + (page: number) => { + scrollTop(); + changePage(page); + }, + [changePage, scrollTop] + ); - const scrollTop = () => { - const scrollDiv = document.querySelector('.kbnDocTableWrapper') as HTMLElement; - scrollDiv.scrollTo(0, 0); - }; + const onPageSizeChange = useCallback( + (size: number) => { + scrollTop(); + changePageSize(size); + }, + [changePageSize, scrollTop] + ); - const onPageChange = (page: number) => { - scrollTop(); - pager.onPageChange(page); - }; + /** + * Go to the first page if the current is no longer available + */ + useEffect(() => { + if (totalPages < currentPage + 1) { + onPageChange(0); + } + }, [currentPage, totalPages, onPageChange]); - const onPageSizeChange = (size: number) => { - scrollTop(); - pager.onPageSizeChange(size); - }; + const shouldShowLimitedResultsWarning = useMemo( + () => !hasNextPage && props.rows.length < props.totalHitCount, + [hasNextPage, props.rows.length, props.totalHitCount] + ); const sampleSize = useMemo(() => { return getServices().uiSettings.get(SAMPLE_SIZE_SETTING, 500); @@ -77,7 +108,7 @@ export const DocTableEmbeddable = (props: DocTableEmbeddableProps) => { responsive={false} wrap={true} > - {shouldShowLimitedResultsWarning() && ( + {shouldShowLimitedResultsWarning && ( { - + - - - + {showPagination && ( + + + + )} ); }; diff --git a/src/plugins/discover/public/application/apps/main/components/doc_table/doc_table_infinite.tsx b/src/plugins/discover/public/application/apps/main/components/doc_table/doc_table_infinite.tsx index 5f0825d9cbd15..dddfefa906962 100644 --- a/src/plugins/discover/public/application/apps/main/components/doc_table/doc_table_infinite.tsx +++ b/src/plugins/discover/public/application/apps/main/components/doc_table/doc_table_infinite.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { Fragment, memo, useCallback, useEffect, useState } from 'react'; +import React, { Fragment, memo, useCallback, useEffect, useRef, useState } from 'react'; import './index.scss'; import { FormattedMessage } from '@kbn/i18n/react'; import { debounce } from 'lodash'; @@ -17,24 +17,88 @@ import { shouldLoadNextDocPatch } from './lib/should_load_next_doc_patch'; const FOOTER_PADDING = { padding: 0 }; -const DocTableInfiniteContent = (props: DocTableRenderProps) => { - const [limit, setLimit] = useState(props.minimumVisibleRows); +const DocTableWrapperMemoized = memo(DocTableWrapper); - // Reset infinite scroll limit - useEffect(() => { - setLimit(props.minimumVisibleRows); - }, [props.rows, props.minimumVisibleRows]); +interface DocTableInfiniteContentProps extends DocTableRenderProps { + limit: number; + onSetMaxLimit: () => void; + onBackToTop: () => void; +} + +const DocTableInfiniteContent = ({ + rows, + columnLength, + sampleSize, + limit, + onSkipBottomButtonClick, + renderHeader, + renderRows, + onSetMaxLimit, + onBackToTop, +}: DocTableInfiniteContentProps) => { + const onSkipBottomButton = useCallback(() => { + onSetMaxLimit(); + onSkipBottomButtonClick(); + }, [onSetMaxLimit, onSkipBottomButtonClick]); + + return ( + + + + {renderHeader()} + {renderRows(rows.slice(0, limit))} + + + + + +
+ {rows.length === sampleSize ? ( +
+ + + + +
+ ) : ( + + ​ + + )} +
+
+ ); +}; + +export const DocTableInfinite = (props: DocTableProps) => { + const tableWrapperRef = useRef(null); + const [limit, setLimit] = useState(50); /** * depending on which version of Discover is displayed, different elements are scrolling * and have therefore to be considered for calculation of infinite scrolling */ useEffect(() => { - const scrollDiv = document.querySelector('.kbnDocTableWrapper') as HTMLElement; + // After mounting table wrapper should be initialized + const scrollDiv = tableWrapperRef.current as HTMLDivElement; const scrollMobileElem = document.documentElement; const scheduleCheck = debounce(() => { const isMobileView = document.getElementsByClassName('dscSidebar__mobile').length > 0; + const usedScrollDiv = isMobileView ? scrollMobileElem : scrollDiv; if (shouldLoadNextDocPatch(usedScrollDiv)) { setLimit((prevLimit) => prevLimit + 50); @@ -58,63 +122,26 @@ const DocTableInfiniteContent = (props: DocTableRenderProps) => { focusElem.focus(); // Only the desktop one needs to target a specific container - if (!isMobileView) { - const scrollDiv = document.querySelector('.kbnDocTableWrapper') as HTMLElement; - scrollDiv.scrollTo(0, 0); + if (!isMobileView && tableWrapperRef.current) { + tableWrapperRef.current.scrollTo(0, 0); } else if (window) { window.scrollTo(0, 0); } }, []); - return ( - - - - {props.renderHeader()} - {props.renderRows(props.rows.slice(0, limit))} - - - - - -
- {props.rows.length === props.sampleSize ? ( -
- - - - -
- ) : ( - - ​ - - )} -
-
- ); -}; + const setMaxLimit = useCallback(() => setLimit(props.rows.length), [props.rows.length]); -const DocTableWrapperMemoized = memo(DocTableWrapper); -const DocTableInfiniteContentMemoized = memo(DocTableInfiniteContent); - -const renderDocTable = (tableProps: DocTableRenderProps) => ( - -); + const renderDocTable = useCallback( + (tableProps: DocTableRenderProps) => ( + + ), + [limit, onBackToTop, setMaxLimit] + ); -export const DocTableInfinite = (props: DocTableProps) => { - return ; + return ; }; diff --git a/src/plugins/discover/public/application/apps/main/components/doc_table/doc_table_wrapper.tsx b/src/plugins/discover/public/application/apps/main/components/doc_table/doc_table_wrapper.tsx index 08e7dcfb66695..2fac1c828796d 100644 --- a/src/plugins/discover/public/application/apps/main/components/doc_table/doc_table_wrapper.tsx +++ b/src/plugins/discover/public/application/apps/main/components/doc_table/doc_table_wrapper.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { useCallback, useMemo, useState } from 'react'; +import React, { forwardRef, useCallback, useMemo } from 'react'; import { EuiIcon, EuiSpacer, EuiText } from '@elastic/eui'; import type { IndexPattern, IndexPatternField } from 'src/plugins/data/common'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -86,7 +86,6 @@ export interface DocTableProps { export interface DocTableRenderProps { columnLength: number; rows: DocTableRow[]; - minimumVisibleRows: number; sampleSize: number; renderRows: (row: DocTableRow[]) => JSX.Element[]; renderHeader: () => JSX.Element; @@ -100,163 +99,166 @@ export interface DocTableWrapperProps extends DocTableProps { render: (params: DocTableRenderProps) => JSX.Element; } -export const DocTableWrapper = ({ - render, - columns, - rows, - indexPattern, - onSort, - onAddColumn, - onMoveColumn, - onRemoveColumn, - sort, - onFilter, - useNewFieldsApi, - searchDescription, - sharedItemTitle, - dataTestSubj, - isLoading, -}: DocTableWrapperProps) => { - const [minimumVisibleRows, setMinimumVisibleRows] = useState(50); - const [ - defaultSortOrder, - hideTimeColumn, - isShortDots, - sampleSize, - showMultiFields, - filterManager, - addBasePath, - ] = useMemo(() => { - const services = getServices(); - return [ - services.uiSettings.get(SORT_DEFAULT_ORDER_SETTING, 'desc'), - services.uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false), - services.uiSettings.get(FORMATS_UI_SETTINGS.SHORT_DOTS_ENABLE), - services.uiSettings.get(SAMPLE_SIZE_SETTING, 500), - services.uiSettings.get(SHOW_MULTIFIELDS, false), - services.filterManager, - services.addBasePath, - ]; - }, []); +const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); - const onSkipBottomButtonClick = useCallback(async () => { - // delay scrolling to after the rows have been rendered - const bottomMarker = document.getElementById('discoverBottomMarker'); - const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); - // show all the rows - setMinimumVisibleRows(rows.length); - - while (rows.length !== document.getElementsByClassName('kbnDocTable__row').length) { - await wait(50); - } - bottomMarker!.focus(); - await wait(50); - bottomMarker!.blur(); - }, [setMinimumVisibleRows, rows]); - - const fieldsToShow = useMemo( - () => - getFieldsToShow( - indexPattern.fields.map((field: IndexPatternField) => field.name), - indexPattern, - showMultiFields - ), - [indexPattern, showMultiFields] - ); - - const renderHeader = useCallback( - () => ( - - ), - [ +export const DocTableWrapper = forwardRef( + ( + { + render, columns, - defaultSortOrder, - hideTimeColumn, + rows, indexPattern, - isShortDots, + onSort, + onAddColumn, onMoveColumn, onRemoveColumn, - onSort, sort, - ] - ); - - const renderRows = useCallback( - (rowsToRender: DocTableRow[]) => { - return rowsToRender.map((current) => ( - - )); - }, - [ - columns, onFilter, - indexPattern, useNewFieldsApi, + searchDescription, + sharedItemTitle, + dataTestSubj, + isLoading, + }: DocTableWrapperProps, + ref + ) => { + const [ + defaultSortOrder, hideTimeColumn, - onAddColumn, - onRemoveColumn, + isShortDots, + sampleSize, + showMultiFields, filterManager, addBasePath, - fieldsToShow, - ] - ); + ] = useMemo(() => { + const services = getServices(); + return [ + services.uiSettings.get(SORT_DEFAULT_ORDER_SETTING, 'desc'), + services.uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false), + services.uiSettings.get(FORMATS_UI_SETTINGS.SHORT_DOTS_ENABLE), + services.uiSettings.get(SAMPLE_SIZE_SETTING, 500), + services.uiSettings.get(SHOW_MULTIFIELDS, false), + services.filterManager, + services.addBasePath, + ]; + }, []); + + const onSkipBottomButtonClick = useCallback(async () => { + // delay scrolling to after the rows have been rendered + const bottomMarker = document.getElementById('discoverBottomMarker'); + + while (rows.length !== document.getElementsByClassName('kbnDocTable__row').length) { + await wait(50); + } + bottomMarker!.focus(); + await wait(50); + bottomMarker!.blur(); + }, [rows]); + + const fieldsToShow = useMemo( + () => + getFieldsToShow( + indexPattern.fields.map((field: IndexPatternField) => field.name), + indexPattern, + showMultiFields + ), + [indexPattern, showMultiFields] + ); + + const renderHeader = useCallback( + () => ( + + ), + [ + columns, + defaultSortOrder, + hideTimeColumn, + indexPattern, + isShortDots, + onMoveColumn, + onRemoveColumn, + onSort, + sort, + ] + ); + + const renderRows = useCallback( + (rowsToRender: DocTableRow[]) => { + return rowsToRender.map((current) => ( + + )); + }, + [ + columns, + onFilter, + indexPattern, + useNewFieldsApi, + hideTimeColumn, + onAddColumn, + onRemoveColumn, + filterManager, + addBasePath, + fieldsToShow, + ] + ); - return ( -
- {rows.length !== 0 && - render({ - columnLength: columns.length, - rows, - minimumVisibleRows, - sampleSize, - onSkipBottomButtonClick, - renderHeader, - renderRows, - })} - {!rows.length && ( -
- - - - - -
- )} -
- ); -}; + return ( +
} + > + {rows.length !== 0 && + render({ + columnLength: columns.length, + rows, + sampleSize, + onSkipBottomButtonClick, + renderHeader, + renderRows, + })} + {!rows.length && ( +
+ + + + + +
+ )} +
+ ); + } +); diff --git a/src/plugins/discover/public/application/apps/main/components/doc_table/lib/use_pager.test.tsx b/src/plugins/discover/public/application/apps/main/components/doc_table/lib/use_pager.test.tsx new file mode 100644 index 0000000000000..e94600b5d1725 --- /dev/null +++ b/src/plugins/discover/public/application/apps/main/components/doc_table/lib/use_pager.test.tsx @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { act, renderHook } from '@testing-library/react-hooks'; +import { usePager } from './use_pager'; + +describe('usePager', () => { + const defaultProps = { + totalItems: 745, + }; + + test('should initialize the first page', () => { + const { result } = renderHook(() => { + return usePager(defaultProps); + }); + + expect(result.current.currentPage).toEqual(0); + expect(result.current.pageSize).toEqual(50); + expect(result.current.totalPages).toEqual(15); + expect(result.current.startIndex).toEqual(0); + expect(result.current.hasNextPage).toEqual(true); + }); + + test('should change the page', () => { + const { result } = renderHook(() => { + return usePager(defaultProps); + }); + + act(() => { + result.current.changePage(5); + }); + + expect(result.current.currentPage).toEqual(5); + expect(result.current.pageSize).toEqual(50); + expect(result.current.totalPages).toEqual(15); + expect(result.current.startIndex).toEqual(250); + expect(result.current.hasNextPage).toEqual(true); + }); + + test('should go to the last page', () => { + const { result } = renderHook(() => { + return usePager(defaultProps); + }); + + act(() => { + result.current.changePage(15); + }); + + expect(result.current.currentPage).toEqual(15); + expect(result.current.pageSize).toEqual(50); + expect(result.current.totalPages).toEqual(15); + expect(result.current.startIndex).toEqual(750); + expect(result.current.hasNextPage).toEqual(false); + }); + + test('should change page size and stay on the current page', () => { + const { result } = renderHook(() => usePager(defaultProps)); + + act(() => { + result.current.changePage(5); + result.current.changePageSize(100); + }); + + expect(result.current.currentPage).toEqual(5); + expect(result.current.pageSize).toEqual(100); + expect(result.current.totalPages).toEqual(8); + expect(result.current.startIndex).toEqual(500); + expect(result.current.hasNextPage).toEqual(true); + }); +}); diff --git a/src/plugins/discover/public/application/apps/main/components/doc_table/lib/use_pager.ts b/src/plugins/discover/public/application/apps/main/components/doc_table/lib/use_pager.ts index 5522e3c150213..d21941b8360eb 100644 --- a/src/plugins/discover/public/application/apps/main/components/doc_table/lib/use_pager.ts +++ b/src/plugins/discover/public/application/apps/main/components/doc_table/lib/use_pager.ts @@ -6,73 +6,38 @@ * Side Public License, v 1. */ -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useMemo, useState } from 'react'; interface MetaParams { - currentPage: number; - totalItems: number; totalPages: number; startIndex: number; hasNextPage: boolean; - pageSize: number; -} - -interface ProvidedMeta { - updatedPageSize?: number; - updatedCurrentPage?: number; } const INITIAL_PAGE_SIZE = 50; export const usePager = ({ totalItems }: { totalItems: number }) => { - const [meta, setMeta] = useState({ - currentPage: 0, - totalItems, - startIndex: 0, - totalPages: Math.ceil(totalItems / INITIAL_PAGE_SIZE), - hasNextPage: true, - pageSize: INITIAL_PAGE_SIZE, - }); - - const getNewMeta = useCallback( - (newMeta: ProvidedMeta) => { - const actualCurrentPage = newMeta.updatedCurrentPage ?? meta.currentPage; - const actualPageSize = newMeta.updatedPageSize ?? meta.pageSize; - - const newTotalPages = Math.ceil(totalItems / actualPageSize); - const newStartIndex = actualPageSize * actualCurrentPage; - - return { - currentPage: actualCurrentPage, - totalPages: newTotalPages, - startIndex: newStartIndex, - totalItems, - hasNextPage: meta.currentPage + 1 < meta.totalPages, - pageSize: actualPageSize, - }; - }, - [meta.currentPage, meta.pageSize, meta.totalPages, totalItems] - ); + const [pageSize, setPageSize] = useState(INITIAL_PAGE_SIZE); + const [currentPage, setCurrentPage] = useState(0); - const onPageChange = useCallback( - (pageIndex: number) => setMeta(getNewMeta({ updatedCurrentPage: pageIndex })), - [getNewMeta] - ); + const meta: MetaParams = useMemo(() => { + const totalPages = Math.ceil(totalItems / pageSize); + return { + totalPages, + startIndex: pageSize * currentPage, + hasNextPage: currentPage + 1 < totalPages, + }; + }, [currentPage, pageSize, totalItems]); - const onPageSizeChange = useCallback( - (newPageSize: number) => - setMeta(getNewMeta({ updatedPageSize: newPageSize, updatedCurrentPage: 0 })), - [getNewMeta] - ); + const changePage = useCallback((pageIndex: number) => setCurrentPage(pageIndex), []); - /** - * Update meta on totalItems change - */ - useEffect(() => setMeta(getNewMeta({})), [getNewMeta, totalItems]); + const changePageSize = useCallback((newPageSize: number) => setPageSize(newPageSize), []); return { ...meta, - onPageChange, - onPageSizeChange, + currentPage, + pageSize, + changePage, + changePageSize, }; }; From 764256573ae2cdd09b2c68c2231a2f0f01a892b6 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Thu, 9 Sep 2021 15:56:46 +0300 Subject: [PATCH 3/9] [Canvas] `SidebarContent` refactor. (#111672) * Refactored sidebar. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../element_settings/element_settings.tsx | 2 +- .../public/components/sidebar/sidebar.tsx | 1 - .../sidebar/sidebar_content/index.ts | 9 +++ .../sidebar_content.component.tsx} | 71 +++++++++---------- .../sidebar_content/sidebar_content.tsx | 34 +++++++++ 5 files changed, 78 insertions(+), 39 deletions(-) create mode 100644 x-pack/plugins/canvas/public/components/sidebar/sidebar_content/index.ts rename x-pack/plugins/canvas/public/components/sidebar/{sidebar_content.js => sidebar_content/sidebar_content.component.tsx} (58%) create mode 100644 x-pack/plugins/canvas/public/components/sidebar/sidebar_content/sidebar_content.tsx diff --git a/x-pack/plugins/canvas/public/components/sidebar/element_settings/element_settings.tsx b/x-pack/plugins/canvas/public/components/sidebar/element_settings/element_settings.tsx index 9cca858223078..4935647ca6810 100644 --- a/x-pack/plugins/canvas/public/components/sidebar/element_settings/element_settings.tsx +++ b/x-pack/plugins/canvas/public/components/sidebar/element_settings/element_settings.tsx @@ -12,7 +12,7 @@ import { ElementSettings as Component } from './element_settings.component'; import { State, PositionedElement } from '../../../../types'; interface Props { - selectedElementId: string; + selectedElementId: string | null; } const mapStateToProps = (state: State, { selectedElementId }: Props): StateProps => ({ diff --git a/x-pack/plugins/canvas/public/components/sidebar/sidebar.tsx b/x-pack/plugins/canvas/public/components/sidebar/sidebar.tsx index 7976ad1f6d01a..8252455e9ebd8 100644 --- a/x-pack/plugins/canvas/public/components/sidebar/sidebar.tsx +++ b/x-pack/plugins/canvas/public/components/sidebar/sidebar.tsx @@ -6,7 +6,6 @@ */ import React, { FunctionComponent } from 'react'; -// @ts-expect-error unconverted component import { SidebarContent } from './sidebar_content'; interface Props { diff --git a/x-pack/plugins/canvas/public/components/sidebar/sidebar_content/index.ts b/x-pack/plugins/canvas/public/components/sidebar/sidebar_content/index.ts new file mode 100644 index 0000000000000..867f47ea3de65 --- /dev/null +++ b/x-pack/plugins/canvas/public/components/sidebar/sidebar_content/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { SidebarContent } from './sidebar_content'; +export { SidebarContent as SidebarContentComponent } from './sidebar_content.component'; diff --git a/x-pack/plugins/canvas/public/components/sidebar/sidebar_content.js b/x-pack/plugins/canvas/public/components/sidebar/sidebar_content/sidebar_content.component.tsx similarity index 58% rename from x-pack/plugins/canvas/public/components/sidebar/sidebar_content.js rename to x-pack/plugins/canvas/public/components/sidebar/sidebar_content/sidebar_content.component.tsx index 7292a98fa91ae..c469c2fda2776 100644 --- a/x-pack/plugins/canvas/public/components/sidebar/sidebar_content.js +++ b/x-pack/plugins/canvas/public/components/sidebar/sidebar_content/sidebar_content.component.tsx @@ -6,17 +6,19 @@ */ import React, { Fragment } from 'react'; -import { connect } from 'react-redux'; -import { compose, branch, renderComponent } from 'recompose'; import { EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { SidebarHeader } from '../../sidebar_header'; +import { MultiElementSettings } from '../multi_element_settings'; +import { GroupSettings } from '../group_settings'; +import { GlobalConfig } from '../global_config'; +import { ElementSettings } from '../element_settings'; -import { getSelectedToplevelNodes, getSelectedElementId } from '../../state/selectors/workpad'; -import { SidebarHeader } from '../sidebar_header'; -import { MultiElementSettings } from './multi_element_settings'; -import { GroupSettings } from './group_settings'; -import { GlobalConfig } from './global_config'; -import { ElementSettings } from './element_settings'; +interface SidebarContentProps { + commit?: Function; + selectedElementId: string | null; + selectedToplevelNodes: string[]; +} const strings = { getGroupedElementSidebarTitle: () => @@ -43,12 +45,7 @@ const strings = { }), }; -const mapStateToProps = (state) => ({ - selectedToplevelNodes: getSelectedToplevelNodes(state), - selectedElementId: getSelectedElementId(state), -}); - -const MultiElementSidebar = () => ( +const MultiElementSidebar: React.FC = () => ( @@ -56,38 +53,38 @@ const MultiElementSidebar = () => ( ); -const GroupedElementSidebar = () => ( +const GroupedElementSidebar: React.FC = () => ( - + ); -const SingleElementSidebar = ({ selectedElementId }) => ( +const SingleElementSidebar: React.FC<{ selectedElementId: string | null }> = ({ + selectedElementId, +}) => ( - + ); -const branches = [ - // multiple elements are selected - branch( - ({ selectedToplevelNodes }) => selectedToplevelNodes.length > 1, - renderComponent(MultiElementSidebar) - ), - // a single, grouped element is selected - branch( - ({ selectedToplevelNodes }) => - selectedToplevelNodes.length === 1 && selectedToplevelNodes[0].includes('group'), - renderComponent(GroupedElementSidebar) - ), - // a single element is selected - branch( - ({ selectedToplevelNodes }) => selectedToplevelNodes.length === 1, - renderComponent(SingleElementSidebar) - ), -]; +export const SidebarContent: React.FC = ({ + selectedToplevelNodes, + selectedElementId, +}) => { + if (selectedToplevelNodes.length > 1) { + return ; + } + + if (selectedToplevelNodes.length === 1 && selectedToplevelNodes[0].includes('group')) { + return ; + } -export const SidebarContent = compose(connect(mapStateToProps), ...branches)(GlobalConfig); + if (selectedToplevelNodes.length === 1) { + return ; + } + + return ; +}; diff --git a/x-pack/plugins/canvas/public/components/sidebar/sidebar_content/sidebar_content.tsx b/x-pack/plugins/canvas/public/components/sidebar/sidebar_content/sidebar_content.tsx new file mode 100644 index 0000000000000..e53f5d6d515df --- /dev/null +++ b/x-pack/plugins/canvas/public/components/sidebar/sidebar_content/sidebar_content.tsx @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { useSelector } from 'react-redux'; +import { getSelectedToplevelNodes, getSelectedElementId } from '../../../state/selectors/workpad'; +import { State } from '../../../../types'; +import { SidebarContent as Component } from './sidebar_content.component'; + +interface SidebarContentProps { + commit?: Function; +} + +export const SidebarContent: React.FC = ({ commit }) => { + const selectedToplevelNodes = useSelector((state) => + getSelectedToplevelNodes(state) + ); + + const selectedElementId = useSelector((state) => + getSelectedElementId(state) + ); + + return ( + + ); +}; From b001830d585bb8efc2918cd6d2f50fe14a3f2d7a Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Thu, 9 Sep 2021 15:59:44 +0300 Subject: [PATCH 4/9] [Deprecations service] Expose `level` field to config deprecations (#111516) --- packages/kbn-config/src/deprecation/types.ts | 6 ++++ .../deprecations/deprecations_service.test.ts | 30 +++++++++++++++++-- .../deprecations/deprecations_service.ts | 26 +++++++++------- 3 files changed, 49 insertions(+), 13 deletions(-) diff --git a/packages/kbn-config/src/deprecation/types.ts b/packages/kbn-config/src/deprecation/types.ts index 007c3ec54113b..0e1f36121e50e 100644 --- a/packages/kbn-config/src/deprecation/types.ts +++ b/packages/kbn-config/src/deprecation/types.ts @@ -23,6 +23,12 @@ export interface DeprecatedConfigDetails { title?: string; /* The message to be displayed for the deprecation. */ message: string; + /** + * levels: + * - warning: will not break deployment upon upgrade + * - critical: needs to be addressed before upgrade. + */ + level?: 'warning' | 'critical'; /* (optional) set false to prevent the config service from logging the deprecation message. */ silent?: boolean; /* (optional) link to the documentation for more details on the deprecation. */ diff --git a/src/core/server/deprecations/deprecations_service.test.ts b/src/core/server/deprecations/deprecations_service.test.ts index 0e8aaf3de49c9..bc0dbcef4a5b6 100644 --- a/src/core/server/deprecations/deprecations_service.test.ts +++ b/src/core/server/deprecations/deprecations_service.test.ts @@ -66,7 +66,7 @@ describe('DeprecationsService', () => { const deprecationsRegistry = mockDeprecationsRegistry.create(); const getDeprecationsContext = mockDeprecationsRegistry.createGetDeprecationsContext(); - it('registers config deprecations', () => { + it('registers config deprecations', async () => { const deprecationsService = new DeprecationsService(coreContext); coreContext.configService.getHandledDeprecatedConfigs.mockReturnValue([ [ @@ -93,7 +93,7 @@ describe('DeprecationsService', () => { expect(deprecationsFactory.getRegistry).toBeCalledTimes(1); expect(deprecationsFactory.getRegistry).toBeCalledWith('testDomain'); expect(deprecationsRegistry.registerDeprecations).toBeCalledTimes(1); - const configDeprecations = deprecationsRegistry.registerDeprecations.mock.calls[0][0].getDeprecations( + const configDeprecations = await deprecationsRegistry.registerDeprecations.mock.calls[0][0].getDeprecations( getDeprecationsContext ); expect(configDeprecations).toMatchInlineSnapshot(` @@ -115,5 +115,31 @@ describe('DeprecationsService', () => { ] `); }); + + it('accepts `level` field overrides', async () => { + const deprecationsService = new DeprecationsService(coreContext); + coreContext.configService.getHandledDeprecatedConfigs.mockReturnValue([ + [ + 'testDomain', + [ + { + message: 'testMessage', + level: 'warning', + correctiveActions: { + manualSteps: ['step a'], + }, + }, + ], + ], + ]); + + deprecationsFactory.getRegistry.mockReturnValue(deprecationsRegistry); + deprecationsService['registerConfigDeprecationsInfo'](deprecationsFactory); + + const configDeprecations = await deprecationsRegistry.registerDeprecations.mock.calls[0][0].getDeprecations( + getDeprecationsContext + ); + expect(configDeprecations[0].level).toBe('warning'); + }); }); }); diff --git a/src/core/server/deprecations/deprecations_service.ts b/src/core/server/deprecations/deprecations_service.ts index c41567d88a2aa..f6f48d2a88b01 100644 --- a/src/core/server/deprecations/deprecations_service.ts +++ b/src/core/server/deprecations/deprecations_service.ts @@ -186,17 +186,21 @@ export class DeprecationsService deprecationsRegistry.registerDeprecations({ getDeprecations: () => { return deprecationsContexts.map( - ({ title, message, correctiveActions, documentationUrl }) => { - return { - title: title || `${domainId} has a deprecated setting`, - level: 'critical', - deprecationType: 'config', - message, - correctiveActions, - documentationUrl, - requireRestart: true, - }; - } + ({ + title = `${domainId} has a deprecated setting`, + level = 'critical', + message, + correctiveActions, + documentationUrl, + }) => ({ + title, + level, + message, + correctiveActions, + documentationUrl, + deprecationType: 'config', + requireRestart: true, + }) ); }, }); From 29142a6fb8028356ae216a4aee8976b7297baebb Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Thu, 9 Sep 2021 15:01:07 +0200 Subject: [PATCH 5/9] Fix audit log tests (#111706) This ensures that the config object given to the `AuditService` setup method is in fact the `xpack.security.audit.*` config options. Previously the entire `xpack.security.*` config object would be passed in. The reason why this didn't fail the tests before was that both happen to have a similarly named config option called `enabled`, which these tests rely on. --- x-pack/plugins/security/server/audit/audit_service.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security/server/audit/audit_service.test.ts b/x-pack/plugins/security/server/audit/audit_service.test.ts index 7c7bc4f031793..a1848068eac35 100644 --- a/x-pack/plugins/security/server/audit/audit_service.test.ts +++ b/x-pack/plugins/security/server/audit/audit_service.test.ts @@ -29,7 +29,7 @@ import { jest.useFakeTimers(); const createConfig = (settings: Partial) => { - return ConfigSchema.validate(settings); + return ConfigSchema.validate({ audit: settings }).audit; }; const logger = loggingSystemMock.createLogger(); From cac4957ef27b07b1fbb6abfcfb8fa1ab88dfa568 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20Zolt=C3=A1n=20Szab=C3=B3?= Date: Thu, 9 Sep 2021 15:10:12 +0200 Subject: [PATCH 6/9] [APM] Changes link version to master in APM links. (#111685) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- src/core/public/doc_links/doc_links_service.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index 6b03581a7765b..ca174fc12109d 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -35,9 +35,9 @@ export class DocLinksService { kibanaSettings: `${KIBANA_DOCS}apm-settings-in-kibana.html`, supportedServiceMaps: `${KIBANA_DOCS}service-maps.html#service-maps-supported`, customLinks: `${KIBANA_DOCS}custom-links.html`, - droppedTransactionSpans: `${APM_DOCS}get-started/${DOC_LINK_VERSION}/transaction-spans.html#dropped-spans`, - upgrading: `${APM_DOCS}server/${DOC_LINK_VERSION}/upgrading.html`, - metaData: `${APM_DOCS}get-started/${DOC_LINK_VERSION}/metadata.html`, + droppedTransactionSpans: `${APM_DOCS}get-started/master/transaction-spans.html#dropped-spans`, + upgrading: `${APM_DOCS}server/master/upgrading.html`, + metaData: `${APM_DOCS}get-started/master/metadata.html`, }, canvas: { guide: `${KIBANA_DOCS}canvas.html`, From 36cec00c6dedbb4abf56df2bf583c2f668b2926a Mon Sep 17 00:00:00 2001 From: Chris Cowan Date: Thu, 9 Sep 2021 07:20:33 -0600 Subject: [PATCH 7/9] [Metrics UI] Remove rounding from Metric Threshold start time (#111634) * [Metrics UI] Remove rounding from Metric Threshold start time * adding missed files --- .../lib/create_timerange.test.ts | 132 ++++++++++++++++++ .../metric_threshold/lib/create_timerange.ts | 31 ++++ .../metric_threshold/lib/evaluate_alert.ts | 28 +--- 3 files changed, 170 insertions(+), 21 deletions(-) create mode 100644 x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/create_timerange.test.ts create mode 100644 x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/create_timerange.ts diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/create_timerange.test.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/create_timerange.test.ts new file mode 100644 index 0000000000000..5640a1d928436 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/create_timerange.test.ts @@ -0,0 +1,132 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createTimerange } from './create_timerange'; +import { Aggregators } from '../types'; +import moment from 'moment'; + +describe('createTimerange(interval, aggType, timeframe)', () => { + describe('without timeframe', () => { + describe('Basic Metric Aggs', () => { + it('should return a second range for last 1 second', () => { + const subject = createTimerange(1000, Aggregators.COUNT); + expect(subject.end - subject.start).toEqual(1000); + }); + it('should return a minute range for last 1 minute', () => { + const subject = createTimerange(60000, Aggregators.COUNT); + expect(subject.end - subject.start).toEqual(60000); + }); + it('should return 5 minute range for last 5 minutes', () => { + const subject = createTimerange(300000, Aggregators.COUNT); + expect(subject.end - subject.start).toEqual(300000); + }); + it('should return a hour range for last 1 hour', () => { + const subject = createTimerange(3600000, Aggregators.COUNT); + expect(subject.end - subject.start).toEqual(3600000); + }); + it('should return a day range for last 1 day', () => { + const subject = createTimerange(86400000, Aggregators.COUNT); + expect(subject.end - subject.start).toEqual(86400000); + }); + }); + describe('Rate Aggs', () => { + it('should return a 20 second range for last 1 second', () => { + const subject = createTimerange(1000, Aggregators.RATE); + expect(subject.end - subject.start).toEqual(1000 * 5); + }); + it('should return a 5 minute range for last 1 minute', () => { + const subject = createTimerange(60000, Aggregators.RATE); + expect(subject.end - subject.start).toEqual(60000 * 5); + }); + it('should return 25 minute range for last 5 minutes', () => { + const subject = createTimerange(300000, Aggregators.RATE); + expect(subject.end - subject.start).toEqual(300000 * 5); + }); + it('should return 5 hour range for last hour', () => { + const subject = createTimerange(3600000, Aggregators.RATE); + expect(subject.end - subject.start).toEqual(3600000 * 5); + }); + it('should return a 5 day range for last day', () => { + const subject = createTimerange(86400000, Aggregators.RATE); + expect(subject.end - subject.start).toEqual(86400000 * 5); + }); + }); + }); + describe('with full timeframe', () => { + describe('Basic Metric Aggs', () => { + it('should return 5 minute range when given 4 minute timeframe', () => { + const end = moment(); + const timeframe = { + start: end.clone().subtract(4, 'minutes').valueOf(), + end: end.valueOf(), + }; + const subject = createTimerange(300000, Aggregators.COUNT, timeframe); + expect(subject.end - subject.start).toEqual(300000); + }); + it('should return 6 minute range when given 6 minute timeframe', () => { + const end = moment(); + const timeframe = { + start: end.clone().subtract(6, 'minutes').valueOf(), + end: end.valueOf(), + }; + const subject = createTimerange(300000, Aggregators.COUNT, timeframe); + expect(subject.end - subject.start).toEqual(360000); + }); + }); + describe('Rate Aggs', () => { + it('should return 25 minute range when given 4 minute timeframe', () => { + const end = moment(); + const timeframe = { + start: end.clone().subtract(4, 'minutes').valueOf(), + end: end.valueOf(), + }; + const subject = createTimerange(300000, Aggregators.RATE, timeframe); + expect(subject.end - subject.start).toEqual(300000 * 5); + }); + it('should return 25 minute range when given 6 minute timeframe', () => { + const end = moment(); + const timeframe = { + start: end.clone().subtract(6, 'minutes').valueOf(), + end: end.valueOf(), + }; + const subject = createTimerange(300000, Aggregators.RATE, timeframe); + expect(subject.end - subject.start).toEqual(300000 * 5); + }); + }); + }); + describe('with partial timeframe', () => { + describe('Basic Metric Aggs', () => { + it('should return 5 minute range for last 5 minutes', () => { + const end = moment(); + const timeframe = { + end: end.valueOf(), + }; + const subject = createTimerange(300000, Aggregators.AVERAGE, timeframe); + expect(subject).toEqual({ + start: end.clone().subtract(5, 'minutes').valueOf(), + end: end.valueOf(), + }); + }); + }); + describe('Rate Aggs', () => { + it('should return 25 minute range for last 5 minutes', () => { + const end = moment(); + const timeframe = { + end: end.valueOf(), + }; + const subject = createTimerange(300000, Aggregators.RATE, timeframe); + expect(subject).toEqual({ + start: end + .clone() + .subtract(300 * 5, 'seconds') + .valueOf(), + end: end.valueOf(), + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/create_timerange.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/create_timerange.ts new file mode 100644 index 0000000000000..cca63aca14d09 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/create_timerange.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import moment from 'moment'; +import { Aggregators } from '../types'; + +export const createTimerange = ( + interval: number, + aggType: Aggregators, + timeframe?: { end: number; start?: number } +) => { + const to = moment(timeframe ? timeframe.end : Date.now()).valueOf(); + + // Rate aggregations need 5 buckets worth of data + const minimumBuckets = aggType === Aggregators.RATE ? 5 : 1; + + const calculatedFrom = to - interval * minimumBuckets; + + // Use either the timeframe.start when the start is less then calculatedFrom + // OR use the calculatedFrom + const from = + timeframe && timeframe.start && timeframe.start <= calculatedFrom + ? timeframe.start + : calculatedFrom; + + return { start: from, end: to }; +}; diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_alert.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_alert.ts index 9a8f2267e7efe..a099b83fdb423 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_alert.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_alert.ts @@ -13,7 +13,6 @@ import { TOO_MANY_BUCKETS_PREVIEW_EXCEPTION, } from '../../../../../common/alerting/metrics'; import { getIntervalInSeconds } from '../../../../utils/get_interval_in_seconds'; -import { roundTimestamp } from '../../../../utils/round_timestamp'; import { InfraSource } from '../../../../../common/source_configuration/source_configuration'; import { InfraDatabaseSearchResponse } from '../../../adapters/framework/adapter_types'; import { createAfterKeyHandler } from '../../../../utils/create_afterkey_handler'; @@ -22,6 +21,7 @@ import { DOCUMENT_COUNT_I18N } from '../../common/messages'; import { UNGROUPED_FACTORY_KEY } from '../../common/utils'; import { MetricExpressionParams, Comparator, Aggregators } from '../types'; import { getElasticsearchMetricQuery } from './metric_query'; +import { createTimerange } from './create_timerange'; interface AggregationWithoutIntervals { aggregatedValue: { value: number; values?: Array<{ key: number; value: number }> }; @@ -135,23 +135,12 @@ const getMetric: ( const interval = `${timeSize}${timeUnit}`; const intervalAsSeconds = getIntervalInSeconds(interval); const intervalAsMS = intervalAsSeconds * 1000; - - const to = moment(timeframe ? timeframe.end : Date.now()).valueOf(); - - // Rate aggregations need 5 buckets worth of data - const minimumBuckets = aggType === Aggregators.RATE ? 5 : 1; - - const minimumFrom = to - intervalAsMS * minimumBuckets; - - const from = roundTimestamp( - timeframe && timeframe.start && timeframe.start <= minimumFrom ? timeframe.start : minimumFrom, - timeUnit - ); + const calculatedTimerange = createTimerange(intervalAsMS, aggType, timeframe); const searchBody = getElasticsearchMetricQuery( params, timefield, - { start: from, end: to }, + calculatedTimerange, hasGroupBy ? groupBy : undefined, filterQuery ); @@ -160,8 +149,8 @@ const getMetric: ( // Rate aggs always drop partial buckets; guard against this boolean being passed as false shouldDropPartialBuckets || aggType === Aggregators.RATE ? { - from, - to, + from: calculatedTimerange.start, + to: calculatedTimerange.end, bucketSizeInMillis: intervalAsMS, } : null; @@ -191,10 +180,7 @@ const getMetric: ( bucket, aggType, dropPartialBucketsOptions, - { - start: from, - end: to, - }, + calculatedTimerange, bucket.doc_count ), }), @@ -212,7 +198,7 @@ const getMetric: ( (result.aggregations! as unknown) as Aggregation, aggType, dropPartialBucketsOptions, - { start: from, end: to }, + calculatedTimerange, isNumber(result.hits.total) ? result.hits.total : result.hits.total.value ), }; From 334f129bcbffb1502d408f8be1c6c13a60c68635 Mon Sep 17 00:00:00 2001 From: ymao1 Date: Thu, 9 Sep 2021 09:43:42 -0400 Subject: [PATCH 8/9] [Alerting] Fixing flaky tests (#111366) * Unskipping test * Retrying deletes * Unskipping test * Changing fn signature * hmm * Removing unnecessary code * Unskipping test Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../alerting/server/rules_client/rules_client.ts | 8 ++++++++ .../security_and_spaces/tests/alerting/alerts.ts | 16 +--------------- .../security_and_spaces/tests/alerting/delete.ts | 3 +-- .../security_and_spaces/tests/alerting/find.ts | 3 +-- .../security_and_spaces/tests/alerting/get.ts | 3 +-- .../security_and_spaces/tests/alerting/update.ts | 3 +-- 6 files changed, 13 insertions(+), 23 deletions(-) diff --git a/x-pack/plugins/alerting/server/rules_client/rules_client.ts b/x-pack/plugins/alerting/server/rules_client/rules_client.ts index c3e21e02cdb7d..51f27916e015a 100644 --- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts +++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts @@ -672,6 +672,14 @@ export class RulesClient { } public async delete({ id }: { id: string }) { + return await retryIfConflicts( + this.logger, + `rulesClient.delete('${id}')`, + async () => await this.deleteWithOCC({ id }) + ); + } + + private async deleteWithOCC({ id }: { id: string }) { let taskIdToRemove: string | undefined | null; let apiKeyToInvalidate: string | null = null; let attributes: RawAlert; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts index 93535826d14e7..3131649e7c742 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts @@ -39,8 +39,7 @@ export default function alertTests({ getService }: FtrProviderContext) { const esTestIndexTool = new ESTestIndexTool(es, retry); const taskManagerUtils = new TaskManagerUtils(es, retry); - // FLAKY: https://github.com/elastic/kibana/issues/106492 - describe.skip('alerts', () => { + describe('alerts', () => { const authorizationIndex = '.kibana-test-authorization'; const objectRemover = new ObjectRemover(supertest); @@ -502,19 +501,6 @@ instanceStateValue: true }) ); - // Enqueue non ephemerically so we the latter code can query properly - const enqueueResponse = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts_fixture/${createdAction.id}/enqueue_action`) - .set('kbn-xsrf', 'foo') - .send({ - params: { - reference, - index: ES_TEST_INDEX_NAME, - retryAt: retryDate.getTime(), - }, - }); - expect(enqueueResponse.status).to.eql(204); - switch (scenario.id) { case 'no_kibana_privileges at space1': case 'global_read at space1': diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/delete.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/delete.ts index fe67decd7d191..d43fb2e7d835f 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/delete.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/delete.ts @@ -23,8 +23,7 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { const retry = getService('retry'); const supertestWithoutAuth = getService('supertestWithoutAuth'); - // FLAKY https://github.com/elastic/kibana/issues/111001 - describe.skip('delete', () => { + describe('delete', () => { const objectRemover = new ObjectRemover(supertest); after(() => objectRemover.removeAll()); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts index 2126e7383e321..3454ef5c94d9f 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts @@ -17,8 +17,7 @@ export default function createFindTests({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const supertestWithoutAuth = getService('supertestWithoutAuth'); - // FLAKY https://github.com/elastic/kibana/issues/111022 - describe.skip('find', () => { + describe('find', () => { const objectRemover = new ObjectRemover(supertest); afterEach(() => objectRemover.removeAll()); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts index 5925f2bcc812f..0400557209348 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts @@ -21,8 +21,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const supertestWithoutAuth = getService('supertestWithoutAuth'); - // FLAKY https://github.com/elastic/kibana/issues/111496 - describe.skip('get', () => { + describe('get', () => { const objectRemover = new ObjectRemover(supertest); afterEach(() => objectRemover.removeAll()); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts index d0ab2e0189a44..e628f0b3d950e 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts @@ -32,8 +32,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { .then((response: SupertestResponse) => response.body); } - // FLAKY: https://github.com/elastic/kibana/issues/110801 - describe.skip('update', () => { + describe('update', () => { const objectRemover = new ObjectRemover(supertest); after(() => objectRemover.removeAll()); From 72dfb9c7e3e49d22c94c50126aa0e974aa69663c Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Thu, 9 Sep 2021 09:29:27 -0500 Subject: [PATCH 9/9] skip flaky suite, #109564 --- .../functional/apps/graph/feature_controls/graph_security.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts b/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts index cc121dccfb13e..913a5034bacc5 100644 --- a/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts +++ b/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts @@ -16,7 +16,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const appsMenu = getService('appsMenu'); const globalNav = getService('globalNav'); - describe('security', () => { + // FLAKY https://github.com/elastic/kibana/issues/109564 + describe.skip('security', () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/empty_kibana'); // ensure we're logged out so we can login as the appropriate users