From 640a7b9b7f65c284dd82ca4572caa189f483f450 Mon Sep 17 00:00:00 2001 From: Constance Date: Wed, 18 Nov 2020 14:49:14 -0800 Subject: [PATCH 01/16] [Enterprise Search] Rename React Router helpers (#83718) * Rename EUI React Router components - Instead of bogarting the EUI component names, use EuiLinkTo instead of EuiLink Other misc renaming - eui_link.tsx to eui_components.tsx for clearer file name - EuiReactRouterHelper to ReactRouterHelper, to make the distinction between EUI and React Router clearer (in theory you could use this helper for non-EUI components) - other misc type renaming * Update simple instances of previous EUI RR components to Eui*To * Clean up complex/renamed instances of Eui*To (hopefully much more straightforward now) - unfortunately side_nav requires an eslint disable --- .../components/engines/engines_table.test.tsx | 4 +-- .../components/engines/engines_table.tsx | 10 +++---- .../product_card/product_card.test.tsx | 8 +++--- .../components/product_card/product_card.tsx | 6 ++-- .../setup_guide/setup_guide_cta.tsx | 6 ++-- .../shared/error_state/error_state_prompt.tsx | 6 ++-- .../shared/layout/side_nav.test.tsx | 8 +++--- .../applications/shared/layout/side_nav.tsx | 19 +++++-------- .../shared/not_found/not_found.tsx | 12 ++++---- ..._link.test.tsx => eui_components.test.tsx} | 20 ++++++------- .../{eui_link.tsx => eui_components.tsx} | 28 +++++++++---------- .../shared/react_router_helpers/index.ts | 6 +--- .../shared/source_row/source_row.tsx | 10 +++---- .../groups/components/group_manager_modal.tsx | 6 ++-- .../views/groups/components/group_row.tsx | 10 +++---- .../views/groups/groups.test.tsx | 4 +-- .../workplace_search/views/groups/groups.tsx | 10 +++---- 17 files changed, 82 insertions(+), 91 deletions(-) rename x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/{eui_link.test.tsx => eui_components.test.tsx} (78%) rename x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/{eui_link.tsx => eui_components.tsx} (73%) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.test.tsx index e9ac51b6a901cb..c8872fe43a1846 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.test.tsx @@ -10,7 +10,7 @@ import { mockTelemetryActions, mountWithIntl } from '../../../__mocks__/'; import React from 'react'; import { EuiBasicTable, EuiPagination, EuiButtonEmpty } from '@elastic/eui'; -import { EuiLink } from '../../../shared/react_router_helpers'; +import { EuiLinkTo } from '../../../shared/react_router_helpers'; import { EnginesTable } from './engines_table'; @@ -50,7 +50,7 @@ describe('EnginesTable', () => { }); it('contains engine links which send telemetry', () => { - const engineLinks = wrapper.find(EuiLink); + const engineLinks = wrapper.find(EuiLinkTo); engineLinks.forEach((link) => { expect(link.prop('to')).toEqual('/engines/test-engine'); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx index 9591bbda1f7c25..7d69cd2b4d4da0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx @@ -11,7 +11,7 @@ import { FormattedMessage, FormattedDate, FormattedNumber } from '@kbn/i18n/reac import { i18n } from '@kbn/i18n'; import { TelemetryLogic } from '../../../shared/telemetry'; -import { EuiLink } from '../../../shared/react_router_helpers'; +import { EuiLinkTo } from '../../../shared/react_router_helpers'; import { getEngineRoute } from '../../routes'; import { ENGINES_PAGE_SIZE } from '../../../../../common/constants'; @@ -59,9 +59,9 @@ export const EnginesTable: React.FC = ({ defaultMessage: 'Name', }), render: (name: string) => ( - + {name} - + ), width: '30%', truncateText: true, @@ -122,12 +122,12 @@ export const EnginesTable: React.FC = ({ ), dataType: 'string', render: (name: string) => ( - + - + ), align: 'right', width: '100px', diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.test.tsx index a257ccde9f4744..8ba2da11604c2c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.test.tsx @@ -12,7 +12,7 @@ import { useValues } from 'kea'; import { shallow } from 'enzyme'; import { EuiCard } from '@elastic/eui'; -import { EuiButton } from '../../../shared/react_router_helpers'; +import { EuiButtonTo } from '../../../shared/react_router_helpers'; import { APP_SEARCH_PLUGIN, WORKPLACE_SEARCH_PLUGIN } from '../../../../../common/constants'; import { ProductCard } from './'; @@ -29,7 +29,7 @@ describe('ProductCard', () => { expect(card.find('h2').text()).toEqual('Elastic App Search'); expect(card.find('.productCard__image').prop('src')).toEqual('as.jpg'); - const button = card.find(EuiButton); + const button = card.find(EuiButtonTo); expect(button.prop('to')).toEqual('/app/enterprise_search/app_search'); expect(button.prop('children')).toEqual('Launch App Search'); @@ -47,7 +47,7 @@ describe('ProductCard', () => { expect(card.find('h2').text()).toEqual('Elastic Workplace Search'); expect(card.find('.productCard__image').prop('src')).toEqual('ws.jpg'); - const button = card.find(EuiButton); + const button = card.find(EuiButtonTo); expect(button.prop('to')).toEqual('/app/enterprise_search/workplace_search'); expect(button.prop('children')).toEqual('Launch Workplace Search'); @@ -63,7 +63,7 @@ describe('ProductCard', () => { const wrapper = shallow(); const card = wrapper.find(EuiCard).dive().shallow(); - const button = card.find(EuiButton); + const button = card.find(EuiButtonTo); expect(button.prop('children')).toEqual('Setup Workplace Search'); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.tsx index de553acf11f7ba..954743f2d0439b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.tsx @@ -10,7 +10,7 @@ import { snakeCase } from 'lodash'; import { i18n } from '@kbn/i18n'; import { EuiCard, EuiTextColor } from '@elastic/eui'; -import { EuiButton } from '../../../shared/react_router_helpers'; +import { EuiButtonTo } from '../../../shared/react_router_helpers'; import { TelemetryLogic } from '../../../shared/telemetry'; import { KibanaLogic } from '../../../shared/kibana'; @@ -63,7 +63,7 @@ export const ProductCard: React.FC = ({ product, image }) => { paddingSize="l" description={{product.CARD_DESCRIPTION}} footer={ - = ({ product, image }) => { } > {config.host ? LAUNCH_BUTTON_TEXT : SETUP_BUTTON_TEXT} - + } /> ); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/setup_guide_cta.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/setup_guide_cta.tsx index 2a0e2ffc34f3fd..bb5d7ab570a08e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/setup_guide_cta.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/setup_guide_cta.tsx @@ -7,13 +7,13 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFlexItem, EuiTitle, EuiText } from '@elastic/eui'; -import { EuiPanel } from '../../../shared/react_router_helpers'; +import { EuiPanelTo } from '../../../shared/react_router_helpers'; import CtaImage from './assets/getting_started.png'; import './setup_guide_cta.scss'; export const SetupGuideCta: React.FC = () => ( - + @@ -34,5 +34,5 @@ export const SetupGuideCta: React.FC = () => ( - + ); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/error_state/error_state_prompt.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/error_state/error_state_prompt.tsx index b92a5bbf1c64e6..a04357816886c6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/error_state/error_state_prompt.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/error_state/error_state_prompt.tsx @@ -9,7 +9,7 @@ import { useValues } from 'kea'; import { EuiEmptyPrompt, EuiCode } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiButton } from '../react_router_helpers'; +import { EuiButtonTo } from '../react_router_helpers'; import { KibanaLogic } from '../../shared/kibana'; import './error_state_prompt.scss'; @@ -90,12 +90,12 @@ export const ErrorStatePrompt: React.FC = () => { } actions={ - + - + } /> ); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/layout/side_nav.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/layout/side_nav.test.tsx index 9eaa2ba4c4d6f2..a7cc21fa63f426 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/layout/side_nav.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/layout/side_nav.test.tsx @@ -10,8 +10,8 @@ import React from 'react'; import { useLocation } from 'react-router-dom'; import { shallow } from 'enzyme'; -import { EuiLink as EuiLinkExternal } from '@elastic/eui'; -import { EuiLink } from '../react_router_helpers'; +import { EuiLink } from '@elastic/eui'; +import { EuiLinkTo } from '../react_router_helpers'; import { ENTERPRISE_SEARCH_PLUGIN, APP_SEARCH_PLUGIN } from '../../../../common/constants'; import { SideNav, SideNavLink, SideNavItem } from './'; @@ -42,7 +42,7 @@ describe('SideNavLink', () => { const wrapper = shallow(Link); expect(wrapper.type()).toEqual('li'); - expect(wrapper.find(EuiLink)).toHaveLength(1); + expect(wrapper.find(EuiLinkTo)).toHaveLength(1); expect(wrapper.find('.enterpriseSearchNavLinks__item')).toHaveLength(1); }); @@ -52,7 +52,7 @@ describe('SideNavLink', () => { Link ); - const externalLink = wrapper.find(EuiLinkExternal); + const externalLink = wrapper.find(EuiLink); expect(externalLink).toHaveLength(1); expect(externalLink.prop('href')).toEqual('http://website.com'); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/layout/side_nav.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/layout/side_nav.tsx index c75a48d5af41db..8da8f457579615 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/layout/side_nav.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/layout/side_nav.tsx @@ -9,8 +9,8 @@ import { useLocation } from 'react-router-dom'; import classNames from 'classnames'; import { i18n } from '@kbn/i18n'; -import { EuiIcon, EuiTitle, EuiText, EuiLink as EuiLinkExternal } from '@elastic/eui'; // TODO: Remove EuiLinkExternal after full Kibana transition -import { EuiLink } from '../react_router_helpers'; +import { EuiIcon, EuiTitle, EuiText, EuiLink } from '@elastic/eui'; // TODO: Remove EuiLink after full Kibana transition +import { EuiLinkTo } from '../react_router_helpers'; import { ENTERPRISE_SEARCH_PLUGIN } from '../../../../common/constants'; import { stripTrailingSlash } from '../../../../common/strip_slashes'; @@ -96,19 +96,14 @@ export const SideNavLink: React.FC = ({ return (
  • {isExternal ? ( - + // eslint-disable-next-line @elastic/eui/href-or-on-click + {children} - + ) : ( - + {children} - + )} {subNav &&
      {subNav}
    }
  • diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.tsx index 05374cb5f0274a..d0140b87302291 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.tsx @@ -13,7 +13,7 @@ import { EuiTitle, EuiFlexGroup, EuiFlexItem, - EuiButton as EuiButtonExternal, + EuiButton, } from '@elastic/eui'; import { @@ -22,7 +22,7 @@ import { LICENSED_SUPPORT_URL, } from '../../../../common/constants'; -import { EuiButton } from '../react_router_helpers'; +import { EuiButtonTo } from '../react_router_helpers'; import { SetAppSearchChrome, SetWorkplaceSearchChrome } from '../kibana_chrome'; import { SendAppSearchTelemetry, SendWorkplaceSearchTelemetry } from '../telemetry'; import { LicensingLogic } from '../licensing'; @@ -89,18 +89,18 @@ export const NotFound: React.FC = ({ product = {} }) => { actions={ - + {i18n.translate('xpack.enterpriseSearch.notFound.action1', { defaultMessage: 'Back to your dashboard', })} - + - + {i18n.translate('xpack.enterpriseSearch.notFound.action2', { defaultMessage: 'Contact support', })} - + } diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_components.test.tsx similarity index 78% rename from x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.test.tsx rename to x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_components.test.tsx index 3a4585b6d9a718..37784fbf4ffb59 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_components.test.tsx @@ -12,7 +12,7 @@ import { EuiLink, EuiButton, EuiPanel } from '@elastic/eui'; import { mockKibanaValues, mockHistory } from '../../__mocks__'; -import { EuiReactRouterLink, EuiReactRouterButton, EuiReactRouterPanel } from './eui_link'; +import { EuiLinkTo, EuiButtonTo, EuiPanelTo } from './eui_components'; describe('EUI & React Router Component Helpers', () => { beforeEach(() => { @@ -20,26 +20,26 @@ describe('EUI & React Router Component Helpers', () => { }); it('renders an EuiLink', () => { - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.find(EuiLink)).toHaveLength(1); }); it('renders an EuiButton', () => { - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.find(EuiButton)).toHaveLength(1); }); it('renders an EuiPanel', () => { - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper.find(EuiPanel)).toHaveLength(1); expect(wrapper.find(EuiPanel).prop('paddingSize')).toEqual('l'); }); it('passes down all ...rest props', () => { - const wrapper = shallow(); + const wrapper = shallow(); const link = wrapper.find(EuiLink); expect(link.prop('external')).toEqual(true); @@ -47,7 +47,7 @@ describe('EUI & React Router Component Helpers', () => { }); it('renders with the correct href and onClick props', () => { - const wrapper = mount(); + const wrapper = mount(); const link = wrapper.find(EuiLink); expect(link.prop('onClick')).toBeInstanceOf(Function); @@ -56,7 +56,7 @@ describe('EUI & React Router Component Helpers', () => { }); it('renders with the correct non-basenamed href when shouldNotCreateHref is passed', () => { - const wrapper = mount(); + const wrapper = mount(); const link = wrapper.find(EuiLink); expect(link.prop('href')).toEqual('/foo/bar'); @@ -65,7 +65,7 @@ describe('EUI & React Router Component Helpers', () => { describe('onClick', () => { it('prevents default navigation and uses React Router history', () => { - const wrapper = mount(); + const wrapper = mount(); const simulatedEvent = { button: 0, @@ -79,7 +79,7 @@ describe('EUI & React Router Component Helpers', () => { }); it('does not prevent default browser behavior on new tab/window clicks', () => { - const wrapper = mount(); + const wrapper = mount(); const simulatedEvent = { shiftKey: true, @@ -92,7 +92,7 @@ describe('EUI & React Router Component Helpers', () => { it('calls inherited onClick actions in addition to default navigation', () => { const customOnClick = jest.fn(); // Can be anything from telemetry to a state reset - const wrapper = mount(); + const wrapper = mount(); wrapper.find(EuiLink).simulate('click', { shiftKey: true }); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_components.tsx similarity index 73% rename from x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.tsx rename to x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_components.tsx index 61f6b31d3e2e99..56beed8780707f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_components.tsx @@ -20,7 +20,7 @@ import { letBrowserHandleEvent, createHref } from './'; * https://github.com/elastic/eui/blob/master/wiki/react-router.md#react-router-51 */ -interface EuiReactRouterProps { +interface ReactRouterProps { to: string; onClick?(): void; // Used to navigate outside of the React Router plugin basename but still within Kibana, @@ -28,7 +28,7 @@ interface EuiReactRouterProps { shouldNotCreateHref?: boolean; } -export const EuiReactRouterHelper: React.FC = ({ +export const ReactRouterHelper: React.FC = ({ to, onClick, shouldNotCreateHref, @@ -59,38 +59,38 @@ export const EuiReactRouterHelper: React.FC = ({ * Component helpers */ -type EuiReactRouterLinkProps = EuiLinkAnchorProps & EuiReactRouterProps; -export const EuiReactRouterLink: React.FC = ({ +type ReactRouterEuiLinkProps = ReactRouterProps & EuiLinkAnchorProps; +export const EuiLinkTo: React.FC = ({ to, onClick, shouldNotCreateHref, ...rest }) => ( - + - + ); -type EuiReactRouterButtonProps = EuiButtonProps & EuiReactRouterProps; -export const EuiReactRouterButton: React.FC = ({ +type ReactRouterEuiButtonProps = ReactRouterProps & EuiButtonProps; +export const EuiButtonTo: React.FC = ({ to, onClick, shouldNotCreateHref, ...rest }) => ( - + - + ); -type EuiReactRouterPanelProps = EuiPanelProps & EuiReactRouterProps; -export const EuiReactRouterPanel: React.FC = ({ +type ReactRouterEuiPanelProps = ReactRouterProps & EuiPanelProps; +export const EuiPanelTo: React.FC = ({ to, onClick, shouldNotCreateHref, ...rest }) => ( - + - + ); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/index.ts b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/index.ts index 326bfb32e41f4f..05a9450e4a348f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/index.ts @@ -6,8 +6,4 @@ export { letBrowserHandleEvent } from './link_events'; export { createHref, CreateHrefOptions } from './create_href'; -export { - EuiReactRouterLink as EuiLink, - EuiReactRouterButton as EuiButton, - EuiReactRouterPanel as EuiPanel, -} from './eui_link'; +export { EuiLinkTo, EuiButtonTo, EuiPanelTo } from './eui_components'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_row/source_row.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_row/source_row.tsx index 896b8f8f5b4c7e..818d06c55dd12a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_row/source_row.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_row/source_row.tsx @@ -23,7 +23,7 @@ import { EuiToolTip, } from '@elastic/eui'; -import { EuiLink } from '../../../../shared/react_router_helpers'; +import { EuiLinkTo } from '../../../../shared/react_router_helpers'; import { SOURCE_STATUSES as statuses } from '../../../constants'; import { ContentSourceDetails } from '../../../types'; import { ADD_SOURCE_PATH, SOURCE_DETAILS_PATH, getContentSourcePath } from '../../../routes'; @@ -77,9 +77,9 @@ export const SourceRow: React.FC = ({ const imageClass = classNames('source-row__icon', { 'source-row__icon--loading': isIndexing }); const fixLink = ( - + Fix - + ); const remoteTooltip = ( @@ -159,13 +159,13 @@ export const SourceRow: React.FC = ({ {showFix && {fixLink}} {showDetails && ( - Details - + )} diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/group_manager_modal.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/group_manager_modal.tsx index 11c0430a8b429e..c0f8bf57989ca9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/group_manager_modal.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/group_manager_modal.tsx @@ -26,7 +26,7 @@ import { EuiSpacer, } from '@elastic/eui'; -import { EuiButton as EuiLinkButton } from '../../../../shared/react_router_helpers'; +import { EuiButtonTo } from '../../../../shared/react_router_helpers'; import { Group } from '../../../types'; import { ORG_SOURCES_PATH } from '../../../routes'; @@ -96,9 +96,9 @@ export const GroupManagerModal: React.FC = ({ const handleSelectAll = () => selectAll(allSelected ? [] : allItems); const sourcesButton = ( - + {ADD_SOURCE_BUTTON_TEXT} - + ); const emptyState = ( diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/group_row.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/group_row.tsx index 5cebb96d00eb9c..9d33f810edae6c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/group_row.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/components/group_row.tsx @@ -13,7 +13,7 @@ import { i18n } from '@kbn/i18n'; import { EuiTableRow, EuiTableRowCell, EuiIcon } from '@elastic/eui'; import { TruncatedContent } from '../../../../shared/truncate'; -import { EuiLink } from '../../../../shared/react_router_helpers'; +import { EuiLinkTo } from '../../../../shared/react_router_helpers'; import { Group } from '../../../types'; @@ -64,9 +64,9 @@ export const GroupRow: React.FC = ({ - + - +
    {GROUP_UPDATED_TEXT} @@ -93,9 +93,9 @@ export const GroupRow: React.FC = ({ )} - + - + diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.test.tsx index cc50c4d0af5c4a..85175d156f886b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.test.tsx @@ -27,7 +27,7 @@ import { TableFilters } from './components/table_filters'; import { DEFAULT_META } from '../../../shared/constants'; import { EuiFieldSearch, EuiLoadingSpinner } from '@elastic/eui'; -import { EuiButton as EuiLinkButton } from '../../../shared/react_router_helpers'; +import { EuiButtonTo } from '../../../shared/react_router_helpers'; const getSearchResults = jest.fn(); const openNewGroupModal = jest.fn(); @@ -138,7 +138,7 @@ describe('GroupOverview', () => { const action = shallow(); expect(action.find('[data-test-subj="InviteUsersButton"]')).toHaveLength(1); - expect(action.find(EuiLinkButton)).toHaveLength(1); + expect(action.find(EuiButtonTo)).toHaveLength(1); }); it('does not render inviteUsersButton when federated auth', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.tsx index 4064391c098939..97647f149bc9b8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.tsx @@ -10,7 +10,7 @@ import { useActions, useValues } from 'kea'; import { i18n } from '@kbn/i18n'; import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiSpacer } from '@elastic/eui'; -import { EuiButton as EuiLinkButton } from '../../../shared/react_router_helpers'; +import { EuiButtonTo } from '../../../shared/react_router_helpers'; import { AppLogic } from '../../app_logic'; @@ -61,7 +61,7 @@ export const Groups: React.FC = () => { if (newGroup && hasMessages) { messages[0].description = ( - { {i18n.translate('xpack.enterpriseSearch.workplaceSearch.groups.newGroup.action', { defaultMessage: 'Manage Group', })} - + ); } const clearFilters = hasFiltersSet && ; const inviteUsersButton = !isFederatedAuth ? ( - + {i18n.translate('xpack.enterpriseSearch.workplaceSearch.groups.inviteUsers.action', { defaultMessage: 'Invite users', })} - + ) : null; const headerAction = ( From b819287ce3489283d49def9a74dd2f82ae0b68be Mon Sep 17 00:00:00 2001 From: Scotty Bollinger Date: Wed, 18 Nov 2020 16:50:14 -0600 Subject: [PATCH 02/16] [Workplace Search] Update SourceIcon to match latest changes in ent-search (#83714) * Move source icons into subfolder * Copy over new icons * Update SourceIcon to account for full bleed images * Remove unused file * Fix broken icon path --- .../shared/assets/{ => source_icons}/box.svg | 0 .../assets/{ => source_icons}/confluence.svg | 0 .../connection_illustration.svg | 0 .../assets/{ => source_icons}/crawler.svg | 0 .../assets/{ => source_icons}/custom.svg | 0 .../assets/{ => source_icons}/drive.svg | 0 .../assets/{ => source_icons}/dropbox.svg | 0 .../assets/{ => source_icons}/github.svg | 0 .../assets/{ => source_icons}/gmail.svg | 0 .../assets/{ => source_icons}/google.svg | 0 .../{ => source_icons}/google_drive.svg | 0 .../shared/assets/{ => source_icons}/index.ts | 0 .../shared/assets/{ => source_icons}/jira.svg | 0 .../assets/{ => source_icons}/jira_server.svg | 0 .../{ => source_icons}/loading_small.svg | 0 .../assets/{ => source_icons}/office365.svg | 0 .../assets/{ => source_icons}/one_drive.svg | 0 .../assets/{ => source_icons}/outlook.svg | 0 .../assets/{ => source_icons}/people.svg | 0 .../assets/{ => source_icons}/salesforce.svg | 0 .../assets/{ => source_icons}/service_now.svg | 0 .../{ => source_icons}/share_circle.svg | 0 .../assets/{ => source_icons}/share_point.svg | 0 .../assets/{ => source_icons}/slack.svg | 0 .../assets/{ => source_icons}/zendesk.svg | 0 .../assets/sources_full_bleed/confluence.svg | 1 + .../assets/sources_full_bleed/custom.svg | 1 + .../assets/sources_full_bleed/dropbox.svg | 1 + .../assets/sources_full_bleed/github.svg | 1 + .../assets/sources_full_bleed/gmail.svg | 1 + .../sources_full_bleed/google_drive.svg | 1 + .../shared/assets/sources_full_bleed/index.ts | 42 +++++++++++++++++++ .../shared/assets/sources_full_bleed/jira.svg | 1 + .../assets/sources_full_bleed/jira_server.svg | 1 + .../assets/sources_full_bleed/onedrive.svg | 1 + .../assets/sources_full_bleed/salesforce.svg | 1 + .../assets/sources_full_bleed/servicenow.svg | 1 + .../assets/sources_full_bleed/sharepoint.svg | 1 + .../assets/sources_full_bleed/slack.svg | 1 + .../assets/sources_full_bleed/zendesk.svg | 1 + .../shared/source_icon/source_icon.tsx | 17 +++++--- .../views/overview/onboarding_steps.tsx | 2 +- 42 files changed, 69 insertions(+), 6 deletions(-) rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{ => source_icons}/box.svg (100%) rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{ => source_icons}/confluence.svg (100%) rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{ => source_icons}/connection_illustration.svg (100%) rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{ => source_icons}/crawler.svg (100%) rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{ => source_icons}/custom.svg (100%) rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{ => source_icons}/drive.svg (100%) rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{ => source_icons}/dropbox.svg (100%) rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{ => source_icons}/github.svg (100%) rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{ => source_icons}/gmail.svg (100%) rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{ => source_icons}/google.svg (100%) rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{ => source_icons}/google_drive.svg (100%) rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{ => source_icons}/index.ts (100%) rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{ => source_icons}/jira.svg (100%) rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{ => source_icons}/jira_server.svg (100%) rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{ => source_icons}/loading_small.svg (100%) rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{ => source_icons}/office365.svg (100%) rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{ => source_icons}/one_drive.svg (100%) rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{ => source_icons}/outlook.svg (100%) rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{ => source_icons}/people.svg (100%) rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{ => source_icons}/salesforce.svg (100%) rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{ => source_icons}/service_now.svg (100%) rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{ => source_icons}/share_circle.svg (100%) rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{ => source_icons}/share_point.svg (100%) rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{ => source_icons}/slack.svg (100%) rename x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/{ => source_icons}/zendesk.svg (100%) create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/confluence.svg create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/custom.svg create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/dropbox.svg create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/github.svg create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/gmail.svg create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/google_drive.svg create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/index.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/jira.svg create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/jira_server.svg create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/onedrive.svg create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/salesforce.svg create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/servicenow.svg create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/sharepoint.svg create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/slack.svg create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/zendesk.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/box.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/box.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/box.svg rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/box.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/confluence.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/confluence.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/confluence.svg rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/confluence.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/connection_illustration.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/connection_illustration.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/connection_illustration.svg rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/connection_illustration.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/crawler.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/crawler.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/crawler.svg rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/crawler.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/custom.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/custom.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/custom.svg rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/custom.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/drive.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/drive.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/drive.svg rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/drive.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/dropbox.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/dropbox.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/dropbox.svg rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/dropbox.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/github.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/github.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/github.svg rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/github.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/gmail.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/gmail.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/gmail.svg rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/gmail.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/google.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/google.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/google.svg rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/google.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/google_drive.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/google_drive.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/google_drive.svg rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/google_drive.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/index.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/index.ts similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/index.ts rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/index.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/jira.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/jira.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/jira.svg rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/jira.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/jira_server.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/jira_server.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/jira_server.svg rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/jira_server.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/loading_small.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/loading_small.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/loading_small.svg rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/loading_small.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/office365.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/office365.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/office365.svg rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/office365.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/one_drive.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/one_drive.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/one_drive.svg rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/one_drive.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/outlook.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/outlook.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/outlook.svg rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/outlook.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/people.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/people.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/people.svg rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/people.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/salesforce.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/salesforce.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/salesforce.svg rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/salesforce.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/service_now.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/service_now.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/service_now.svg rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/service_now.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/share_circle.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/share_circle.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/share_circle.svg rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/share_circle.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/share_point.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/share_point.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/share_point.svg rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/share_point.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/slack.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/slack.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/slack.svg rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/slack.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/zendesk.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/zendesk.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/zendesk.svg rename to x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/source_icons/zendesk.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/confluence.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/confluence.svg new file mode 100644 index 00000000000000..7aac36a6fe3c5b --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/confluence.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/custom.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/custom.svg new file mode 100644 index 00000000000000..cc07fbbc508776 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/custom.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/dropbox.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/dropbox.svg new file mode 100644 index 00000000000000..01e5a7735de12e --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/dropbox.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/github.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/github.svg new file mode 100644 index 00000000000000..aa9c3e5b45146a --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/gmail.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/gmail.svg new file mode 100644 index 00000000000000..98d418244c22f3 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/gmail.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/google_drive.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/google_drive.svg new file mode 100644 index 00000000000000..6541b3f9e753ff --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/google_drive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/index.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/index.ts new file mode 100644 index 00000000000000..e749fb9482758f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/index.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import confluence from './confluence.svg'; +import custom from './custom.svg'; +import dropbox from './dropbox.svg'; +import github from './github.svg'; +import gmail from './gmail.svg'; +import googleDrive from './google_drive.svg'; +import jira from './jira.svg'; +import jiraServer from './jira_server.svg'; +import oneDrive from './onedrive.svg'; +import salesforce from './salesforce.svg'; +import serviceNow from './servicenow.svg'; +import sharePoint from './sharepoint.svg'; +import slack from './slack.svg'; +import zendesk from './zendesk.svg'; + +export const imagesFull = { + confluence, + confluenceCloud: confluence, + confluenceServer: confluence, + custom, + dropbox, + github, + githubEnterpriseServer: github, + gmail, + googleDrive, + jira, + jiraServer, + jiraCloud: jira, + oneDrive, + salesforce, + salesforceSandbox: salesforce, + serviceNow, + sharePoint, + slack, + zendesk, +} as { [key: string]: string }; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/jira.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/jira.svg new file mode 100644 index 00000000000000..c12e55798d8895 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/jira.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/jira_server.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/jira_server.svg new file mode 100644 index 00000000000000..4dfd0fd9100794 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/jira_server.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/onedrive.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/onedrive.svg new file mode 100644 index 00000000000000..c390dff1e418f7 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/onedrive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/salesforce.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/salesforce.svg new file mode 100644 index 00000000000000..ef6d552949424a --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/salesforce.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/servicenow.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/servicenow.svg new file mode 100644 index 00000000000000..6388ec44d21d79 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/servicenow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/sharepoint.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/sharepoint.svg new file mode 100644 index 00000000000000..aebfd7a8e49c0f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/sharepoint.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/slack.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/slack.svg new file mode 100644 index 00000000000000..8f6fc0c987eaa3 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/slack.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/zendesk.svg b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/zendesk.svg new file mode 100644 index 00000000000000..8afd143dd9a7c9 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/assets/sources_full_bleed/zendesk.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_icon/source_icon.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_icon/source_icon.tsx index 857b6f3aaf9976..dec9e25fe24409 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_icon/source_icon.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_icon/source_icon.tsx @@ -6,17 +6,17 @@ import React from 'react'; -// Prefer importing entire lodash library, e.g. import { get } from "lodash" -// eslint-disable-next-line no-restricted-imports -import _camelCase from 'lodash/camelCase'; +import { camelCase } from 'lodash'; -import { images } from '../assets'; +import { images } from '../assets/source_icons'; +import { imagesFull } from '../assets/sources_full_bleed'; interface SourceIconProps { serviceType: string; name: string; className?: string; wrapped?: boolean; + fullBleed?: boolean; } export const SourceIcon: React.FC = ({ @@ -24,8 +24,15 @@ export const SourceIcon: React.FC = ({ serviceType, className, wrapped, + fullBleed = false, }) => { - const icon = {name}; + const icon = ( + {name} + ); return wrapped ? (
    {icon} diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_steps.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_steps.tsx index 7251461b848a4e..ed5136a6f7a4e2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_steps.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_steps.tsx @@ -21,7 +21,7 @@ import { EuiButtonEmptyProps, EuiLinkProps, } from '@elastic/eui'; -import sharedSourcesIcon from '../../components/shared/assets/share_circle.svg'; +import sharedSourcesIcon from '../../components/shared/assets/source_icons/share_circle.svg'; import { TelemetryLogic } from '../../../shared/telemetry'; import { getWorkplaceSearchUrl } from '../../../shared/enterprise_search_url'; import { ORG_SOURCES_PATH, USERS_PATH, ORG_SETTINGS_PATH } from '../../routes'; From 71f972dc837d6186ad2b8157af8fb178d86e6a96 Mon Sep 17 00:00:00 2001 From: Constance Date: Wed, 18 Nov 2020 15:53:45 -0800 Subject: [PATCH 03/16] [App Search] Engine overview layout stub (#83504) * Set up Overview file * Finish Overview page logic, stub out empty/metric views * Stub in basic empty engine overview - Minus document creation button & API code example * Stub out EngineOverviewMetrics and unavailable empty prompt * Stub out EngineOverMetrics components (stats, charts, logs) * [Refactor] Pull out some document creation i18n strings to constants - They're repeated/reused by the DocumentCreationPopover component * PR feedback: Drop the regex * PR feedback: RecentLogs -> RecentApiLogs * PR feedback: Copy * PR feedback: Copy, sentence-casing --- .../components/analytics/constants.ts | 27 +++++ .../components/api_logs/constants.ts | 12 +++ .../document_creation/constants.tsx | 54 ++++++++++ .../app_search/components/engine/constants.ts | 4 - .../components/engine/engine_nav.tsx | 2 +- .../components/engine/engine_router.test.tsx | 3 +- .../components/engine/engine_router.tsx | 5 +- .../engine_overview/components/index.ts | 10 ++ .../components/recent_api_logs.test.tsx | 32 ++++++ .../components/recent_api_logs.tsx | 50 ++++++++++ .../components/total_charts.test.tsx | 46 +++++++++ .../components/total_charts.tsx | 99 +++++++++++++++++++ .../components/total_stats.test.tsx | 51 ++++++++++ .../components/total_stats.tsx | 37 +++++++ .../components/unavailable_prompt.test.tsx | 18 ++++ .../components/unavailable_prompt.tsx | 30 ++++++ .../components/engine_overview/constants.ts | 27 +++++ .../engine_overview/engine_overview.test.tsx | 80 +++++++++++++++ .../engine_overview/engine_overview.tsx | 44 +++++++++ .../engine_overview_empty.test.tsx | 40 ++++++++ .../engine_overview/engine_overview_empty.tsx | 98 ++++++++++++++++++ .../engine_overview_metrics.test.tsx | 34 +++++++ .../engine_overview_metrics.tsx | 44 +++++++++ .../components/engine_overview/index.ts | 2 + 24 files changed, 841 insertions(+), 8 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/constants.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/api_logs/constants.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/constants.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/index.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/unavailable_prompt.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/unavailable_prompt.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/constants.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_metrics.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_metrics.tsx diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/constants.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/constants.ts new file mode 100644 index 00000000000000..51ae11ad2ab822 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/constants.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const TOTAL_DOCUMENTS = i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.analytics.totalDocuments', + { defaultMessage: 'Total documents' } +); + +export const TOTAL_API_OPERATIONS = i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.analytics.totalApiOperations', + { defaultMessage: 'Total API operations' } +); + +export const TOTAL_QUERIES = i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.analytics.totalQueries', + { defaultMessage: 'Total queries' } +); + +export const TOTAL_CLICKS = i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.analytics.totalClicks', + { defaultMessage: 'Total clicks' } +); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/api_logs/constants.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/api_logs/constants.ts new file mode 100644 index 00000000000000..6fd60b7a34ebc4 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/api_logs/constants.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const RECENT_API_EVENTS = i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.apiLogs.recent', + { defaultMessage: 'Recent API events' } +); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/constants.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/constants.tsx new file mode 100644 index 00000000000000..736ef09fa6cf3f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/constants.tsx @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiCode, EuiLink } from '@elastic/eui'; + +import { DOCS_PREFIX } from '../../routes'; + +export const DOCUMENT_CREATION_DESCRIPTION = ( + .json, + postCode: POST, + documentsApiLink: ( + + documents API + + ), + apiStrong: Indexing by API, + }} + /> +); + +export const DOCUMENT_API_INDEXING_TITLE = i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.documentCreation.api.title', + { defaultMessage: 'Indexing by API' } +); + +export const DOCUMENT_API_INDEXING_DESCRIPTION = ( + + documents API + + ), + clientLibrariesLink: ( + + client libraries + + ), + }} + /> +); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/constants.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/constants.ts index 3c963e415f33b7..9ce524038075b2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/constants.ts @@ -9,10 +9,6 @@ import { i18n } from '@kbn/i18n'; // TODO: It's very likely that we'll move these i18n constants to their respective component // folders once those are migrated over. This is a temporary way of DRYing them out for now. -export const OVERVIEW_TITLE = i18n.translate( - 'xpack.enterpriseSearch.appSearch.engine.overview.title', - { defaultMessage: 'Overview' } -); export const ANALYTICS_TITLE = i18n.translate( 'xpack.enterpriseSearch.appSearch.engine.analytics.title', { defaultMessage: 'Analytics' } diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx index 77aca8a71994da..a7ac6f203b1f72 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx @@ -28,8 +28,8 @@ import { } from '../../routes'; import { getAppSearchUrl } from '../../../shared/enterprise_search_url'; import { ENGINES_TITLE } from '../engines'; +import { OVERVIEW_TITLE } from '../engine_overview'; import { - OVERVIEW_TITLE, ANALYTICS_TITLE, DOCUMENTS_TITLE, SCHEMA_TITLE, diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx index 8f067754c48a08..e8609c169855b1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx @@ -18,6 +18,7 @@ jest.mock('../../../shared/flash_messages', () => ({ import { setQueuedErrorMessage } from '../../../shared/flash_messages'; import { Loading } from '../../../shared/loading'; +import { EngineOverview } from '../engine_overview'; import { EngineRouter } from './'; @@ -71,7 +72,7 @@ describe('EngineRouter', () => { const wrapper = shallow(); expect(wrapper.find(Switch)).toHaveLength(1); - expect(wrapper.find('[data-test-subj="EngineOverviewTODO"]')).toHaveLength(1); + expect(wrapper.find(EngineOverview)).toHaveLength(1); }); it('renders an analytics view', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx index 9833305c438c1c..f586106924f2cb 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx @@ -31,8 +31,8 @@ import { // ENGINE_API_LOGS_PATH, } from '../../routes'; import { ENGINES_TITLE } from '../engines'; +import { OVERVIEW_TITLE } from '../engine_overview'; import { - OVERVIEW_TITLE, ANALYTICS_TITLE, // DOCUMENTS_TITLE, // SCHEMA_TITLE, @@ -46,6 +46,7 @@ import { } from './constants'; import { Loading } from '../../../shared/loading'; +import { EngineOverview } from '../engine_overview'; import { EngineLogic } from './'; @@ -100,7 +101,7 @@ export const EngineRouter: React.FC = () => { )} -
    Overview
    +
    ); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/index.ts new file mode 100644 index 00000000000000..11e7b2a5fba978 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { UnavailablePrompt } from './unavailable_prompt'; +export { TotalStats } from './total_stats'; +export { TotalCharts } from './total_charts'; +export { RecentApiLogs } from './recent_api_logs'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.test.tsx new file mode 100644 index 00000000000000..725c89894b80ba --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.test.tsx @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { setMockValues } from '../../../../__mocks__/kea.mock'; + +import React from 'react'; +import { shallow, ShallowWrapper } from 'enzyme'; + +import { EuiButton } from '../../../../shared/react_router_helpers'; + +import { RecentApiLogs } from './recent_api_logs'; + +describe('RecentApiLogs', () => { + let wrapper: ShallowWrapper; + + beforeAll(() => { + jest.clearAllMocks(); + setMockValues({ + engineName: 'some-engine', + }); + wrapper = shallow(); + }); + + it('renders the recent API logs table', () => { + expect(wrapper.find('h2').text()).toEqual('Recent API events'); + expect(wrapper.find(EuiButton).prop('to')).toEqual('/engines/some-engine/api-logs'); + // TODO: expect(wrapper.find(ApiLogsTable)).toHaveLength(1) + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.tsx new file mode 100644 index 00000000000000..05eb7311168847 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.tsx @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { useValues } from 'kea'; + +import { + EuiPageContent, + EuiPageContentHeader, + EuiPageContentHeaderSection, + EuiPageContentBody, + EuiTitle, +} from '@elastic/eui'; + +import { EuiButton } from '../../../../shared/react_router_helpers'; + +import { ENGINE_API_LOGS_PATH, getEngineRoute } from '../../../routes'; +import { RECENT_API_EVENTS } from '../../api_logs/constants'; +import { VIEW_API_LOGS } from '../constants'; + +import { EngineLogic } from '../../engine'; + +export const RecentApiLogs: React.FC = () => { + const { engineName } = useValues(EngineLogic); + const engineRoute = getEngineRoute(engineName); + + return ( + + + + +

    {RECENT_API_EVENTS}

    +
    +
    + + + {VIEW_API_LOGS} + + +
    + + TODO: API Logs Table + {/* */} + +
    + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.test.tsx new file mode 100644 index 00000000000000..a56b41dfb05038 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.test.tsx @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { setMockValues } from '../../../../__mocks__/kea.mock'; + +import React from 'react'; +import { shallow, ShallowWrapper } from 'enzyme'; + +import { EuiButton } from '../../../../shared/react_router_helpers'; + +import { TotalCharts } from './total_charts'; + +describe('TotalCharts', () => { + let wrapper: ShallowWrapper; + + beforeAll(() => { + jest.clearAllMocks(); + setMockValues({ + engineName: 'some-engine', + startDate: '1970-01-01', + endDate: '1970-01-08', + queriesPerDay: [0, 1, 2, 3, 5, 10, 50], + operationsPerDay: [0, 0, 0, 0, 0, 0, 0], + }); + wrapper = shallow(); + }); + + it('renders the total queries chart', () => { + const chart = wrapper.find('[data-test-subj="TotalQueriesChart"]'); + + expect(chart.find('h2').text()).toEqual('Total queries'); + expect(chart.find(EuiButton).prop('to')).toEqual('/engines/some-engine/analytics'); + // TODO: find chart component + }); + + it('renders the total API operations chart', () => { + const chart = wrapper.find('[data-test-subj="TotalApiOperationsChart"]'); + + expect(chart.find('h2').text()).toEqual('Total API operations'); + expect(chart.find(EuiButton).prop('to')).toEqual('/engines/some-engine/api-logs'); + // TODO: find chart component + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.tsx new file mode 100644 index 00000000000000..e27fe3cdfc799b --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.tsx @@ -0,0 +1,99 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { useValues } from 'kea'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiPageContent, + EuiPageContentHeader, + EuiPageContentHeaderSection, + EuiPageContentBody, + EuiTitle, + EuiText, +} from '@elastic/eui'; + +import { EuiButton } from '../../../../shared/react_router_helpers'; + +import { ENGINE_ANALYTICS_PATH, ENGINE_API_LOGS_PATH, getEngineRoute } from '../../../routes'; +import { TOTAL_QUERIES, TOTAL_API_OPERATIONS } from '../../analytics/constants'; +import { VIEW_ANALYTICS, VIEW_API_LOGS, LAST_7_DAYS } from '../constants'; + +import { EngineLogic } from '../../engine'; +import { EngineOverviewLogic } from '../'; + +export const TotalCharts: React.FC = () => { + const { engineName } = useValues(EngineLogic); + const engineRoute = getEngineRoute(engineName); + + const { + // startDate, + // endDate, + // queriesPerDay, + // operationsPerDay, + } = useValues(EngineOverviewLogic); + + return ( + + + + + + +

    {TOTAL_QUERIES}

    +
    + + {LAST_7_DAYS} + +
    + + + {VIEW_ANALYTICS} + + +
    + + TODO: Analytics chart + {/* */} + +
    +
    + + + + + +

    {TOTAL_API_OPERATIONS}

    +
    + + {LAST_7_DAYS} + +
    + + + {VIEW_API_LOGS} + + +
    + + TODO: API Logs chart + {/* */} + +
    +
    +
    + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.test.tsx new file mode 100644 index 00000000000000..6cb47e8b419f3d --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.test.tsx @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { setMockValues } from '../../../../__mocks__/kea.mock'; + +import React from 'react'; +import { shallow, ShallowWrapper } from 'enzyme'; +import { EuiStat } from '@elastic/eui'; + +import { TotalStats } from './total_stats'; + +describe('TotalStats', () => { + let wrapper: ShallowWrapper; + + beforeAll(() => { + jest.clearAllMocks(); + setMockValues({ + totalQueries: 11, + documentCount: 22, + totalClicks: 33, + }); + wrapper = shallow(); + }); + + it('renders the total queries stat', () => { + expect(wrapper.find('[data-test-subj="TotalQueriesCard"]')).toHaveLength(1); + + const card = wrapper.find(EuiStat).at(0); + expect(card.prop('title')).toEqual(11); + expect(card.prop('description')).toEqual('Total queries'); + }); + + it('renders the total documents stat', () => { + expect(wrapper.find('[data-test-subj="TotalDocumentsCard"]')).toHaveLength(1); + + const card = wrapper.find(EuiStat).at(1); + expect(card.prop('title')).toEqual(22); + expect(card.prop('description')).toEqual('Total documents'); + }); + + it('renders the total clicks stat', () => { + expect(wrapper.find('[data-test-subj="TotalClicksCard"]')).toHaveLength(1); + + const card = wrapper.find(EuiStat).at(2); + expect(card.prop('title')).toEqual(33); + expect(card.prop('description')).toEqual('Total clicks'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.tsx new file mode 100644 index 00000000000000..a27142938f5583 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.tsx @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { useValues } from 'kea'; +import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiStat } from '@elastic/eui'; + +import { TOTAL_QUERIES, TOTAL_DOCUMENTS, TOTAL_CLICKS } from '../../analytics/constants'; + +import { EngineOverviewLogic } from '../'; + +export const TotalStats: React.FC = () => { + const { totalQueries, documentCount, totalClicks } = useValues(EngineOverviewLogic); + + return ( + + + + + + + + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/unavailable_prompt.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/unavailable_prompt.test.tsx new file mode 100644 index 00000000000000..3ddfd14b0eb0cc --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/unavailable_prompt.test.tsx @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { shallow } from 'enzyme'; +import { EuiEmptyPrompt } from '@elastic/eui'; + +import { UnavailablePrompt } from './unavailable_prompt'; + +describe('UnavailablePrompt', () => { + it('renders', () => { + const wrapper = shallow(); + expect(wrapper.find(EuiEmptyPrompt)).toHaveLength(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/unavailable_prompt.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/unavailable_prompt.tsx new file mode 100644 index 00000000000000..e9cc6e2f05bf36 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/unavailable_prompt.tsx @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +import { i18n } from '@kbn/i18n'; +import { EuiEmptyPrompt } from '@elastic/eui'; + +export const UnavailablePrompt: React.FC = () => ( + + {i18n.translate('xpack.enterpriseSearch.appSearch.engine.overview.unavailableTitle', { + defaultMessage: 'Dashboard metrics are currently unavailable', + })} + + } + body={ +

    + {i18n.translate('xpack.enterpriseSearch.appSearch.engine.overview.unavailableBody', { + defaultMessage: 'Please try again in a few minutes.', + })} +

    + } + /> +); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/constants.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/constants.ts new file mode 100644 index 00000000000000..797811ec6cde8b --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/constants.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const OVERVIEW_TITLE = i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.overview.title', + { defaultMessage: 'Overview' } +); + +export const VIEW_ANALYTICS = i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.overview.analyticsLink', + { defaultMessage: 'View analytics' } +); + +export const VIEW_API_LOGS = i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.overview.apiLogsLink', + { defaultMessage: 'View API logs' } +); + +export const LAST_7_DAYS = i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.overview.chartDuration', + { defaultMessage: 'Last 7 days' } +); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx new file mode 100644 index 00000000000000..196fb2ca2bf13f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import '../../../__mocks__/shallow_useeffect.mock'; +import { setMockValues, setMockActions } from '../../../__mocks__/kea.mock'; + +import React from 'react'; +import { shallow } from 'enzyme'; + +import { Loading } from '../../../shared/loading'; +import { EmptyEngineOverview } from './engine_overview_empty'; +import { EngineOverviewMetrics } from './engine_overview_metrics'; +import { EngineOverview } from './'; + +describe('EngineOverview', () => { + const values = { + dataLoading: false, + documentCount: 0, + myRole: {}, + isMetaEngine: false, + }; + const actions = { + pollForOverviewMetrics: jest.fn(), + }; + + beforeEach(() => { + jest.clearAllMocks(); + setMockValues(values); + setMockActions(actions); + }); + + it('renders', () => { + const wrapper = shallow(); + expect(wrapper.find('[data-test-subj="EngineOverview"]')).toHaveLength(1); + }); + + it('initializes data on mount', () => { + shallow(); + expect(actions.pollForOverviewMetrics).toHaveBeenCalledTimes(1); + }); + + it('renders a loading component if async data is still loading', () => { + setMockValues({ ...values, dataLoading: true }); + const wrapper = shallow(); + expect(wrapper.find(Loading)).toHaveLength(1); + }); + + describe('EmptyEngineOverview', () => { + it('renders when the engine has no documents & the user can add documents', () => { + const myRole = { canManageEngineDocuments: true, canViewEngineCredentials: true }; + setMockValues({ ...values, myRole, documentCount: 0 }); + const wrapper = shallow(); + expect(wrapper.find(EmptyEngineOverview)).toHaveLength(1); + }); + }); + + describe('EngineOverviewMetrics', () => { + it('renders when the engine has documents', () => { + setMockValues({ ...values, documentCount: 1 }); + const wrapper = shallow(); + expect(wrapper.find(EngineOverviewMetrics)).toHaveLength(1); + }); + + it('renders when the user does not have the ability to add documents', () => { + const myRole = { canManageEngineDocuments: false, canViewEngineCredentials: false }; + setMockValues({ ...values, myRole }); + const wrapper = shallow(); + expect(wrapper.find(EngineOverviewMetrics)).toHaveLength(1); + }); + + it('always renders for meta engines', () => { + setMockValues({ ...values, isMetaEngine: true }); + const wrapper = shallow(); + expect(wrapper.find(EngineOverviewMetrics)).toHaveLength(1); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx new file mode 100644 index 00000000000000..dd43bc67b3e88a --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useEffect } from 'react'; +import { useActions, useValues } from 'kea'; + +import { AppLogic } from '../../app_logic'; +import { EngineLogic } from '../engine'; +import { Loading } from '../../../shared/loading'; + +import { EngineOverviewLogic } from './'; +import { EmptyEngineOverview } from './engine_overview_empty'; +import { EngineOverviewMetrics } from './engine_overview_metrics'; + +export const EngineOverview: React.FC = () => { + const { + myRole: { canManageEngineDocuments, canViewEngineCredentials }, + } = useValues(AppLogic); + const { isMetaEngine } = useValues(EngineLogic); + + const { pollForOverviewMetrics } = useActions(EngineOverviewLogic); + const { dataLoading, documentCount } = useValues(EngineOverviewLogic); + + useEffect(() => { + pollForOverviewMetrics(); + }, []); + + if (dataLoading) { + return ; + } + + const engineHasDocuments = documentCount > 0; + const canAddDocuments = canManageEngineDocuments && canViewEngineCredentials; + const showEngineOverview = engineHasDocuments || !canAddDocuments || isMetaEngine; + + return ( +
    + {showEngineOverview ? : } +
    + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.test.tsx new file mode 100644 index 00000000000000..8ebe09820a67e2 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.test.tsx @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import '../../../__mocks__/enterprise_search_url.mock'; +import { setMockValues } from '../../../__mocks__/kea.mock'; + +import React from 'react'; +import { shallow, ShallowWrapper } from 'enzyme'; +import { EuiButton } from '@elastic/eui'; + +import { CURRENT_MAJOR_VERSION } from '../../../../../common/version'; + +import { EmptyEngineOverview } from './engine_overview_empty'; + +describe('EmptyEngineOverview', () => { + let wrapper: ShallowWrapper; + + beforeAll(() => { + jest.clearAllMocks(); + setMockValues({ + engineName: 'empty-engine', + }); + wrapper = shallow(); + }); + + it('renders', () => { + expect(wrapper.find('h1').text()).toEqual('Engine setup'); + expect(wrapper.find('h2').text()).toEqual('Setting up the “empty-engine” engine'); + expect(wrapper.find('h3').text()).toEqual('Indexing by API'); + }); + + it('renders correctly versioned documentation URLs', () => { + expect(wrapper.find(EuiButton).prop('href')).toEqual( + `https://www.elastic.co/guide/en/app-search/${CURRENT_MAJOR_VERSION}/index.html` + ); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.tsx new file mode 100644 index 00000000000000..f2bf5a54f810c0 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.tsx @@ -0,0 +1,98 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { useValues } from 'kea'; + +import { i18n } from '@kbn/i18n'; +import { + EuiPageHeader, + EuiPageHeaderSection, + EuiPageContent, + EuiPageContentHeader, + EuiPageContentBody, + EuiTitle, + EuiText, + EuiButton, + EuiSpacer, +} from '@elastic/eui'; + +import { EngineLogic } from '../engine'; + +import { DOCS_PREFIX } from '../../routes'; +import { + DOCUMENT_CREATION_DESCRIPTION, + DOCUMENT_API_INDEXING_TITLE, + DOCUMENT_API_INDEXING_DESCRIPTION, +} from '../document_creation/constants'; +// TODO +// import { DocumentCreationButtons, CodeExample } from '../document_creation' + +export const EmptyEngineOverview: React.FC = () => { + const { engineName } = useValues(EngineLogic); + + return ( + <> + + + +

    + {i18n.translate('xpack.enterpriseSearch.appSearch.engine.overview.empty.heading', { + defaultMessage: 'Engine setup', + })} +

    +
    +
    + + + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.overview.empty.headingAction', + { defaultMessage: 'View documentation' } + )} + + +
    + + + +

    + {i18n.translate('xpack.enterpriseSearch.appSearch.engine.overview.empty.subheading', { + defaultMessage: 'Setting up the “{engineName}” engine', + values: { engineName }, + })} +

    +
    +
    + + +

    {DOCUMENT_CREATION_DESCRIPTION}

    +
    + + {/* TODO: */} +
    + + + +

    {DOCUMENT_API_INDEXING_TITLE}

    +
    +
    + + +

    {DOCUMENT_API_INDEXING_DESCRIPTION}

    +

    + {i18n.translate('xpack.enterpriseSearch.appSearch.engine.overview.empty.apiExample', { + defaultMessage: + 'To see the API in action, you can experiment with the example request below using a command line or a client library.', + })} +

    +
    + + {/* */} +
    +
    + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_metrics.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_metrics.test.tsx new file mode 100644 index 00000000000000..8250446e231b35 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_metrics.test.tsx @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { setMockValues } from '../../../__mocks__/kea.mock'; + +import React from 'react'; +import { shallow } from 'enzyme'; + +import { UnavailablePrompt, TotalStats, TotalCharts, RecentApiLogs } from './components'; +import { EngineOverviewMetrics } from './engine_overview_metrics'; + +describe('EngineOverviewMetrics', () => { + it('renders', () => { + const wrapper = shallow(); + expect(wrapper.find('h1').text()).toEqual('Engine overview'); + }); + + it('renders an unavailable prompt if engine data is still indexing', () => { + setMockValues({ apiLogsUnavailable: true }); + const wrapper = shallow(); + expect(wrapper.find(UnavailablePrompt)).toHaveLength(1); + }); + + it('renders total stats, charts, and recent logs when metrics are available', () => { + setMockValues({ apiLogsUnavailable: false }); + const wrapper = shallow(); + expect(wrapper.find(TotalStats)).toHaveLength(1); + expect(wrapper.find(TotalCharts)).toHaveLength(1); + expect(wrapper.find(RecentApiLogs)).toHaveLength(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_metrics.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_metrics.tsx new file mode 100644 index 00000000000000..9630f6fa2f81dc --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_metrics.tsx @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { useValues } from 'kea'; + +import { i18n } from '@kbn/i18n'; +import { EuiPageHeader, EuiTitle, EuiSpacer } from '@elastic/eui'; + +import { EngineOverviewLogic } from './'; + +import { UnavailablePrompt, TotalStats, TotalCharts, RecentApiLogs } from './components'; + +export const EngineOverviewMetrics: React.FC = () => { + const { apiLogsUnavailable } = useValues(EngineOverviewLogic); + + return ( + <> + + +

    + {i18n.translate('xpack.enterpriseSearch.appSearch.engine.overview.heading', { + defaultMessage: 'Engine overview', + })} +

    +
    +
    + {apiLogsUnavailable ? ( + + ) : ( + <> + + + + + + + )} + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/index.ts index fcd92ba6a338c0..82c5d7dc8e60a4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/index.ts @@ -5,3 +5,5 @@ */ export { EngineOverviewLogic } from './engine_overview_logic'; +export { EngineOverview } from './engine_overview'; +export { OVERVIEW_TITLE } from './constants'; From f2d97a9fe2064eb47c4e871a41cd07e7cbae9258 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 19 Nov 2020 00:08:50 +0000 Subject: [PATCH 04/16] chore(NA): update lmdb store to v0.8.15 (#83726) * chore(NA): upgrade lmdb-store to v0.8.15 * chore(NA): remove unused ts-error statements --- package.json | 2 +- packages/kbn-optimizer/src/node/cache.ts | 4 ---- yarn.lock | 25 ++++++++++-------------- 3 files changed, 11 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index 87e51abe49be3e..23f7a0b4306548 100644 --- a/package.json +++ b/package.json @@ -723,7 +723,7 @@ "less": "npm:@elastic/less@2.7.3-kibana", "license-checker": "^16.0.0", "listr": "^0.14.1", - "lmdb-store": "^0.8.11", + "lmdb-store": "^0.8.15", "load-grunt-config": "^3.0.1", "loader-utils": "^1.2.3", "log-symbols": "^2.2.0", diff --git a/packages/kbn-optimizer/src/node/cache.ts b/packages/kbn-optimizer/src/node/cache.ts index dc96bf47fafcf4..a73dba5b16469e 100644 --- a/packages/kbn-optimizer/src/node/cache.ts +++ b/packages/kbn-optimizer/src/node/cache.ts @@ -49,23 +49,19 @@ export class Cache { this.codes = LmdbStore.open({ name: 'codes', path: CACHE_DIR, - // @ts-expect-error See https://github.com/DoctorEvidence/lmdb-store/pull/18 maxReaders: 500, }); - // @ts-expect-error See https://github.com/DoctorEvidence/lmdb-store/pull/18 this.atimes = this.codes.openDB({ name: 'atimes', encoding: 'string', }); - // @ts-expect-error See https://github.com/DoctorEvidence/lmdb-store/pull/18 this.mtimes = this.codes.openDB({ name: 'mtimes', encoding: 'string', }); - // @ts-expect-error See https://github.com/DoctorEvidence/lmdb-store/pull/18 this.sourceMaps = this.codes.openDB({ name: 'sourceMaps', encoding: 'msgpack', diff --git a/yarn.lock b/yarn.lock index 2a82e7024a895e..9be39ea18e3d13 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18798,24 +18798,24 @@ livereload-js@^2.3.0: resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-2.4.0.tgz#447c31cf1ea9ab52fc20db615c5ddf678f78009c" integrity sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw== -lmdb-store-0.9@0.7.2: - version "0.7.2" - resolved "https://registry.yarnpkg.com/lmdb-store-0.9/-/lmdb-store-0.9-0.7.2.tgz#45b907a46d0a676fee955629bd2f70f06efb26bb" - integrity sha512-/MO8G6p4l7ku1ltCCdE/2ZOtSQBSM0B02vIemMHjoKgjE/fooanJYXIFwtZYM5r/hBDxmO+L3q5ASAXbfHQ6pQ== +lmdb-store-0.9@0.7.3: + version "0.7.3" + resolved "https://registry.yarnpkg.com/lmdb-store-0.9/-/lmdb-store-0.9-0.7.3.tgz#c2cb27dfa916ab966cceed692c67e4236813104a" + integrity sha512-t8iCnN6T3NZPFelPmjYIjCg+nhGbOdc0xcHCG40v01AWRTN49OINSt2k/u+16/2/HrI+b6Ssb8WByXUhbyHz6w== dependencies: fs-extra "^9.0.1" msgpackr "^0.5.3" nan "^2.14.1" node-gyp-build "^4.2.3" - weak-lru-cache "^0.2.0" + weak-lru-cache "^0.3.9" -lmdb-store@^0.8.11: - version "0.8.11" - resolved "https://registry.yarnpkg.com/lmdb-store/-/lmdb-store-0.8.11.tgz#7f7c756a115ceab32c51c0948444bfd5d1716ab3" - integrity sha512-CFgxh2/TL1NXyJ8FQPXY50O/gADxih7Gx0RjKRScGlyxihqSLd/fGzMkbvDdeAOAS8bsnYpLojAMTSeNiwN8dQ== +lmdb-store@^0.8.15: + version "0.8.15" + resolved "https://registry.yarnpkg.com/lmdb-store/-/lmdb-store-0.8.15.tgz#4efb0341c2df505dd6f3a7f26f834f0a142a80a2" + integrity sha512-4Q0WZh2FmcJC6esZRUWMfkCmNiz0WU9cOgrxt97ZMTnVfHyOdZhtrt0oOF5EQPfetxxJf/BorKY28aX92R6G6g== dependencies: fs-extra "^9.0.1" - lmdb-store-0.9 "0.7.2" + lmdb-store-0.9 "0.7.3" msgpackr "^0.5.4" nan "^2.14.1" node-gyp-build "^4.2.3" @@ -29141,11 +29141,6 @@ wcwidth@^1.0.1: dependencies: defaults "^1.0.3" -weak-lru-cache@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/weak-lru-cache/-/weak-lru-cache-0.2.0.tgz#447379ccff6dfda1b7a9566c9ef168260be859d1" - integrity sha512-M1l5CzKvM7maa7tCbtL0NW6sOnp8gqup853+9Aq7GL0XNWKNnFOkeE3v3Z5X2IeMzedPwQyPbi4RlFvD6rxs7A== - weak-lru-cache@^0.3.9: version "0.3.9" resolved "https://registry.yarnpkg.com/weak-lru-cache/-/weak-lru-cache-0.3.9.tgz#9e56920d4115e8542625d8ef8cc278cbd97f7624" From a04cb37f2b510de522e9284ff71cfeab00f7c06e Mon Sep 17 00:00:00 2001 From: Chris Cowan Date: Wed, 18 Nov 2020 18:14:22 -0700 Subject: [PATCH 05/16] [Metrics UI] Optimizations for Snapshot and Inventory Metadata (#83596) * [Metrics UI] Add time range to inventory metadata request * Adding optimizations for snapshot request * Adding sorting to dataset request * Only query inventory metadata for AWS * moving check inside getCloudMetadata * removing unused deps --- .../common/http_api/inventory_meta_api.ts | 1 + .../inventory_view/components/layout.tsx | 2 +- .../components/toolbars/toolbar.tsx | 5 ++-- .../hooks/use_inventory_meta.ts | 7 +++++- .../inventory_view/hooks/use_snaphot.ts | 2 +- .../server/routes/inventory_metadata/index.ts | 6 +++-- .../lib/get_cloud_metadata.ts | 24 +++++++++++++++++-- .../lib/find_interval_for_metrics.ts | 3 ++- .../lib/get_dataset_for_field.ts | 21 ++++++++++++++-- .../lib/create_timerange_with_interval.ts | 5 +++- .../server/routes/snapshot/lib/get_nodes.ts | 13 +++++----- 11 files changed, 69 insertions(+), 20 deletions(-) diff --git a/x-pack/plugins/infra/common/http_api/inventory_meta_api.ts b/x-pack/plugins/infra/common/http_api/inventory_meta_api.ts index 77de515c9cc46d..43f3b2037e3812 100644 --- a/x-pack/plugins/infra/common/http_api/inventory_meta_api.ts +++ b/x-pack/plugins/infra/common/http_api/inventory_meta_api.ts @@ -21,6 +21,7 @@ export const InventoryMetaResponseRT = rt.type({ export const InventoryMetaRequestRT = rt.type({ sourceId: rt.string, nodeType: ItemTypeRT, + currentTime: rt.number, }); export type InventoryMetaRequest = rt.TypeOf; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx index 92aa015113b2a4..2e5ddab77d374d 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx @@ -124,7 +124,7 @@ export const Layout = () => { <> - + diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/toolbars/toolbar.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/toolbars/toolbar.tsx index e9ffc56d8c47f1..7bcb1270c30a52 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/toolbars/toolbar.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/toolbars/toolbar.tsx @@ -54,11 +54,12 @@ const wrapToolbarItems = ( interface Props { nodeType: InventoryItemType; + currentTime: number; } -export const Toolbar = ({ nodeType }: Props) => { +export const Toolbar = ({ nodeType, currentTime }: Props) => { const { sourceId } = useSourceContext(); - const { accounts, regions } = useInventoryMeta(sourceId, nodeType); + const { accounts, regions } = useInventoryMeta(sourceId, nodeType, currentTime); const ToolbarItems = findToolbar(nodeType); return wrapToolbarItems(ToolbarItems, accounts, regions); }; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_inventory_meta.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_inventory_meta.ts index b038491690a137..01811eb21a1106 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_inventory_meta.ts +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_inventory_meta.ts @@ -15,7 +15,11 @@ import { } from '../../../../../common/http_api/inventory_meta_api'; import { InventoryItemType } from '../../../../../common/inventory_models/types'; -export function useInventoryMeta(sourceId: string, nodeType: InventoryItemType) { +export function useInventoryMeta( + sourceId: string, + nodeType: InventoryItemType, + currentTime: number +) { const decodeResponse = (response: any) => { return pipe( InventoryMetaResponseRT.decode(response), @@ -29,6 +33,7 @@ export function useInventoryMeta(sourceId: string, nodeType: InventoryItemType) JSON.stringify({ sourceId, nodeType, + currentTime, }), decodeResponse ); diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_snaphot.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_snaphot.ts index 702213516c1232..eec46b04862876 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_snaphot.ts +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_snaphot.ts @@ -44,7 +44,7 @@ export function useSnapshot( interval: '1m', to: currentTime, from: currentTime - 1200 * 1000, - lookbackSize: 20, + lookbackSize: 5, }; const { error, loading, response, makeRequest } = useHTTPRequest( diff --git a/x-pack/plugins/infra/server/routes/inventory_metadata/index.ts b/x-pack/plugins/infra/server/routes/inventory_metadata/index.ts index 8b5271cb960c1b..c784aa0f7d20b0 100644 --- a/x-pack/plugins/infra/server/routes/inventory_metadata/index.ts +++ b/x-pack/plugins/infra/server/routes/inventory_metadata/index.ts @@ -33,7 +33,7 @@ export const initInventoryMetaRoute = (libs: InfraBackendLibs) => { }, async (requestContext, request, response) => { try { - const { sourceId, nodeType } = pipe( + const { sourceId, nodeType, currentTime } = pipe( InventoryMetaRequestRT.decode(request.body), fold(throwErrors(Boom.badRequest), identity) ); @@ -42,11 +42,13 @@ export const initInventoryMetaRoute = (libs: InfraBackendLibs) => { requestContext.core.savedObjects.client, sourceId ); + const awsMetadata = await getCloudMetadata( framework, requestContext, configuration, - nodeType + nodeType, + currentTime ); return response.ok({ diff --git a/x-pack/plugins/infra/server/routes/inventory_metadata/lib/get_cloud_metadata.ts b/x-pack/plugins/infra/server/routes/inventory_metadata/lib/get_cloud_metadata.ts index b4288dae0c2215..af9e9c5f57c5b7 100644 --- a/x-pack/plugins/infra/server/routes/inventory_metadata/lib/get_cloud_metadata.ts +++ b/x-pack/plugins/infra/server/routes/inventory_metadata/lib/get_cloud_metadata.ts @@ -25,9 +25,18 @@ export const getCloudMetadata = async ( framework: KibanaFramework, req: RequestHandlerContext, sourceConfiguration: InfraSourceConfiguration, - nodeType: InventoryItemType + nodeType: InventoryItemType, + currentTime: number ): Promise => { const model = findInventoryModel(nodeType); + // Only run this for AWS modules, eventually we might have more. + if (model.requiredModule !== 'aws') { + return { + accounts: [], + projects: [], + regions: [], + }; + } const metricQuery = { allowNoIndices: true, @@ -36,7 +45,18 @@ export const getCloudMetadata = async ( body: { query: { bool: { - must: [{ match: { 'event.module': model.requiredModule } }], + must: [ + { + range: { + [sourceConfiguration.fields.timestamp]: { + gte: currentTime - 86400000, // 24 hours ago + lte: currentTime, + format: 'epoch_millis', + }, + }, + }, + { match: { 'event.module': model.requiredModule } }, + ], }, }, size: 0, diff --git a/x-pack/plugins/infra/server/routes/metrics_explorer/lib/find_interval_for_metrics.ts b/x-pack/plugins/infra/server/routes/metrics_explorer/lib/find_interval_for_metrics.ts index 8ab0f4a44c85d3..b3d960e30404f5 100644 --- a/x-pack/plugins/infra/server/routes/metrics_explorer/lib/find_interval_for_metrics.ts +++ b/x-pack/plugins/infra/server/routes/metrics_explorer/lib/find_interval_for_metrics.ts @@ -34,7 +34,8 @@ export const findIntervalForMetrics = async ( const modules = await Promise.all( fields.map( - async (field) => await getDatasetForField(client, field as string, options.indexPattern) + async (field) => + await getDatasetForField(client, field as string, options.indexPattern, options.timerange) ) ); diff --git a/x-pack/plugins/infra/server/routes/metrics_explorer/lib/get_dataset_for_field.ts b/x-pack/plugins/infra/server/routes/metrics_explorer/lib/get_dataset_for_field.ts index 85bb5b106c87c7..15e6f7ba86d01a 100644 --- a/x-pack/plugins/infra/server/routes/metrics_explorer/lib/get_dataset_for_field.ts +++ b/x-pack/plugins/infra/server/routes/metrics_explorer/lib/get_dataset_for_field.ts @@ -17,7 +17,8 @@ interface EventDatasetHit { export const getDatasetForField = async ( client: ESSearchClient, field: string, - indexPattern: string + indexPattern: string, + timerange: { field: string; to: number; from: number } ) => { const params = { allowNoIndices: true, @@ -25,9 +26,25 @@ export const getDatasetForField = async ( terminateAfter: 1, index: indexPattern, body: { - query: { exists: { field } }, + query: { + bool: { + filter: [ + { exists: { field } }, + { + range: { + [timerange.field]: { + gte: timerange.from, + lte: timerange.to, + format: 'epoch_millis', + }, + }, + }, + ], + }, + }, size: 1, _source: ['event.dataset'], + sort: [{ [timerange.field]: { order: 'desc' } }], }, }; diff --git a/x-pack/plugins/infra/server/routes/snapshot/lib/create_timerange_with_interval.ts b/x-pack/plugins/infra/server/routes/snapshot/lib/create_timerange_with_interval.ts index 827e0901c1c01f..833b5349f4b56b 100644 --- a/x-pack/plugins/infra/server/routes/snapshot/lib/create_timerange_with_interval.ts +++ b/x-pack/plugins/infra/server/routes/snapshot/lib/create_timerange_with_interval.ts @@ -75,7 +75,10 @@ const aggregationsToModules = async ( const fields = await Promise.all( uniqueFields.map( async (field) => - await getDatasetForField(client, field as string, options.sourceConfiguration.metricAlias) + await getDatasetForField(client, field as string, options.sourceConfiguration.metricAlias, { + ...options.timerange, + field: options.sourceConfiguration.fields.timestamp, + }) ) ); return fields.filter((f) => f) as string[]; diff --git a/x-pack/plugins/infra/server/routes/snapshot/lib/get_nodes.ts b/x-pack/plugins/infra/server/routes/snapshot/lib/get_nodes.ts index 9332d5aee1f52b..7a2985188dccf8 100644 --- a/x-pack/plugins/infra/server/routes/snapshot/lib/get_nodes.ts +++ b/x-pack/plugins/infra/server/routes/snapshot/lib/get_nodes.ts @@ -23,12 +23,11 @@ export const getNodes = async ( snapshotRequest ); const metricsApiResponse = await queryAllData(client, metricsApiRequest); - return copyMissingMetrics( - transformMetricsApiResponseToSnapshotResponse( - metricsApiRequest, - snapshotRequest, - source, - metricsApiResponse - ) + const snapshotResponse = transformMetricsApiResponseToSnapshotResponse( + metricsApiRequest, + snapshotRequest, + source, + metricsApiResponse ); + return copyMissingMetrics(snapshotResponse); }; From 5375ea41356a1e47f7f8267b6b78b48af908b67f Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Wed, 18 Nov 2020 20:19:13 -0500 Subject: [PATCH 06/16] Adding documentation for global action configuration options (#83557) * Adding documentation for global action configuration options * Update docs/user/alerting/defining-alerts.asciidoc Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * incorporating PR feedback Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> --- docs/user/alerting/defining-alerts.asciidoc | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/user/alerting/defining-alerts.asciidoc b/docs/user/alerting/defining-alerts.asciidoc index 89a487ca8fb328..05d022d039b23a 100644 --- a/docs/user/alerting/defining-alerts.asciidoc +++ b/docs/user/alerting/defining-alerts.asciidoc @@ -74,6 +74,21 @@ image::images/alert-flyout-add-action.png[You can add multiple actions on an ale Actions are not required on alerts. In some cases you may want to run an alert without actions first to understand its behavior, and configure actions later. ============================================== +[float] +=== Global actions configuration +Some actions configuration options apply to all actions. +If you are using an *on-prem* Elastic Stack deployment, you can set these in the kibana.yml file. +If you are using a cloud deployment, you can set these via the console. + +Here's a list of the available global configuration options and an explanation of what each one does: + +* `xpack.actions.allowedHosts`: Specifies an array of host names which actions such as email, Slack, PagerDuty, and webhook can connect to. An element of * indicates any host can be connected to. An empty array indicates no hosts can be connected to. Default: [ {asterisk} ] +* `xpack.actions.enabledActionTypes`: Specifies to an array of action types that are enabled. An {asterisk} indicates all action types registered are enabled. The action types that {kib} provides are: .server-log, .slack, .email, .index, .pagerduty, .webhook. Default: [ {asterisk} ] +* `xpack.actions.proxyUrl`: Specifies the proxy URL to use, if using a proxy for actions. +* `xpack.actions.proxyHeader`: Specifies HTTP headers for proxy, if using a proxy for actions. +* `xpack.actions.proxyRejectUnauthorizedCertificates`: Set to `false` to bypass certificate validation for proxy, if using a proxy for actions. +* `xpack.actions.rejectUnauthorized`: Set to `false` to bypass certificate validation for actions. + [float] === Managing alerts From 92acf4586ee2a0ccffab4f6c8b9fa8a5e8f693b4 Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 18 Nov 2020 18:35:36 -0700 Subject: [PATCH 07/16] Revert "[App Search] Engine overview layout stub (#83504)" This reverts commit 71f972dc837d6186ad2b8157af8fb178d86e6a96. --- .../components/analytics/constants.ts | 27 ----- .../components/api_logs/constants.ts | 12 --- .../document_creation/constants.tsx | 54 ---------- .../app_search/components/engine/constants.ts | 4 + .../components/engine/engine_nav.tsx | 2 +- .../components/engine/engine_router.test.tsx | 3 +- .../components/engine/engine_router.tsx | 5 +- .../engine_overview/components/index.ts | 10 -- .../components/recent_api_logs.test.tsx | 32 ------ .../components/recent_api_logs.tsx | 50 ---------- .../components/total_charts.test.tsx | 46 --------- .../components/total_charts.tsx | 99 ------------------- .../components/total_stats.test.tsx | 51 ---------- .../components/total_stats.tsx | 37 ------- .../components/unavailable_prompt.test.tsx | 18 ---- .../components/unavailable_prompt.tsx | 30 ------ .../components/engine_overview/constants.ts | 27 ----- .../engine_overview/engine_overview.test.tsx | 80 --------------- .../engine_overview/engine_overview.tsx | 44 --------- .../engine_overview_empty.test.tsx | 40 -------- .../engine_overview/engine_overview_empty.tsx | 98 ------------------ .../engine_overview_metrics.test.tsx | 34 ------- .../engine_overview_metrics.tsx | 44 --------- .../components/engine_overview/index.ts | 2 - 24 files changed, 8 insertions(+), 841 deletions(-) delete mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/constants.ts delete mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/api_logs/constants.ts delete mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/constants.tsx delete mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/index.ts delete mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.test.tsx delete mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.tsx delete mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.test.tsx delete mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.tsx delete mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.test.tsx delete mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.tsx delete mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/unavailable_prompt.test.tsx delete mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/unavailable_prompt.tsx delete mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/constants.ts delete mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx delete mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx delete mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.test.tsx delete mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.tsx delete mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_metrics.test.tsx delete mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_metrics.tsx diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/constants.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/constants.ts deleted file mode 100644 index 51ae11ad2ab822..00000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/constants.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { i18n } from '@kbn/i18n'; - -export const TOTAL_DOCUMENTS = i18n.translate( - 'xpack.enterpriseSearch.appSearch.engine.analytics.totalDocuments', - { defaultMessage: 'Total documents' } -); - -export const TOTAL_API_OPERATIONS = i18n.translate( - 'xpack.enterpriseSearch.appSearch.engine.analytics.totalApiOperations', - { defaultMessage: 'Total API operations' } -); - -export const TOTAL_QUERIES = i18n.translate( - 'xpack.enterpriseSearch.appSearch.engine.analytics.totalQueries', - { defaultMessage: 'Total queries' } -); - -export const TOTAL_CLICKS = i18n.translate( - 'xpack.enterpriseSearch.appSearch.engine.analytics.totalClicks', - { defaultMessage: 'Total clicks' } -); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/api_logs/constants.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/api_logs/constants.ts deleted file mode 100644 index 6fd60b7a34ebc4..00000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/api_logs/constants.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { i18n } from '@kbn/i18n'; - -export const RECENT_API_EVENTS = i18n.translate( - 'xpack.enterpriseSearch.appSearch.engine.apiLogs.recent', - { defaultMessage: 'Recent API events' } -); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/constants.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/constants.tsx deleted file mode 100644 index 736ef09fa6cf3f..00000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/constants.tsx +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; - -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiCode, EuiLink } from '@elastic/eui'; - -import { DOCS_PREFIX } from '../../routes'; - -export const DOCUMENT_CREATION_DESCRIPTION = ( - .json, - postCode: POST, - documentsApiLink: ( - - documents API - - ), - apiStrong: Indexing by API, - }} - /> -); - -export const DOCUMENT_API_INDEXING_TITLE = i18n.translate( - 'xpack.enterpriseSearch.appSearch.engine.documentCreation.api.title', - { defaultMessage: 'Indexing by API' } -); - -export const DOCUMENT_API_INDEXING_DESCRIPTION = ( - - documents API - - ), - clientLibrariesLink: ( - - client libraries - - ), - }} - /> -); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/constants.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/constants.ts index 9ce524038075b2..3c963e415f33b7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/constants.ts @@ -9,6 +9,10 @@ import { i18n } from '@kbn/i18n'; // TODO: It's very likely that we'll move these i18n constants to their respective component // folders once those are migrated over. This is a temporary way of DRYing them out for now. +export const OVERVIEW_TITLE = i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.overview.title', + { defaultMessage: 'Overview' } +); export const ANALYTICS_TITLE = i18n.translate( 'xpack.enterpriseSearch.appSearch.engine.analytics.title', { defaultMessage: 'Analytics' } diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx index a7ac6f203b1f72..77aca8a71994da 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx @@ -28,8 +28,8 @@ import { } from '../../routes'; import { getAppSearchUrl } from '../../../shared/enterprise_search_url'; import { ENGINES_TITLE } from '../engines'; -import { OVERVIEW_TITLE } from '../engine_overview'; import { + OVERVIEW_TITLE, ANALYTICS_TITLE, DOCUMENTS_TITLE, SCHEMA_TITLE, diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx index e8609c169855b1..8f067754c48a08 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx @@ -18,7 +18,6 @@ jest.mock('../../../shared/flash_messages', () => ({ import { setQueuedErrorMessage } from '../../../shared/flash_messages'; import { Loading } from '../../../shared/loading'; -import { EngineOverview } from '../engine_overview'; import { EngineRouter } from './'; @@ -72,7 +71,7 @@ describe('EngineRouter', () => { const wrapper = shallow(); expect(wrapper.find(Switch)).toHaveLength(1); - expect(wrapper.find(EngineOverview)).toHaveLength(1); + expect(wrapper.find('[data-test-subj="EngineOverviewTODO"]')).toHaveLength(1); }); it('renders an analytics view', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx index f586106924f2cb..9833305c438c1c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx @@ -31,8 +31,8 @@ import { // ENGINE_API_LOGS_PATH, } from '../../routes'; import { ENGINES_TITLE } from '../engines'; -import { OVERVIEW_TITLE } from '../engine_overview'; import { + OVERVIEW_TITLE, ANALYTICS_TITLE, // DOCUMENTS_TITLE, // SCHEMA_TITLE, @@ -46,7 +46,6 @@ import { } from './constants'; import { Loading } from '../../../shared/loading'; -import { EngineOverview } from '../engine_overview'; import { EngineLogic } from './'; @@ -101,7 +100,7 @@ export const EngineRouter: React.FC = () => { )} - +
    Overview
    ); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/index.ts deleted file mode 100644 index 11e7b2a5fba978..00000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/index.ts +++ /dev/null @@ -1,10 +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 { UnavailablePrompt } from './unavailable_prompt'; -export { TotalStats } from './total_stats'; -export { TotalCharts } from './total_charts'; -export { RecentApiLogs } from './recent_api_logs'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.test.tsx deleted file mode 100644 index 725c89894b80ba..00000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.test.tsx +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { setMockValues } from '../../../../__mocks__/kea.mock'; - -import React from 'react'; -import { shallow, ShallowWrapper } from 'enzyme'; - -import { EuiButton } from '../../../../shared/react_router_helpers'; - -import { RecentApiLogs } from './recent_api_logs'; - -describe('RecentApiLogs', () => { - let wrapper: ShallowWrapper; - - beforeAll(() => { - jest.clearAllMocks(); - setMockValues({ - engineName: 'some-engine', - }); - wrapper = shallow(); - }); - - it('renders the recent API logs table', () => { - expect(wrapper.find('h2').text()).toEqual('Recent API events'); - expect(wrapper.find(EuiButton).prop('to')).toEqual('/engines/some-engine/api-logs'); - // TODO: expect(wrapper.find(ApiLogsTable)).toHaveLength(1) - }); -}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.tsx deleted file mode 100644 index 05eb7311168847..00000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.tsx +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { useValues } from 'kea'; - -import { - EuiPageContent, - EuiPageContentHeader, - EuiPageContentHeaderSection, - EuiPageContentBody, - EuiTitle, -} from '@elastic/eui'; - -import { EuiButton } from '../../../../shared/react_router_helpers'; - -import { ENGINE_API_LOGS_PATH, getEngineRoute } from '../../../routes'; -import { RECENT_API_EVENTS } from '../../api_logs/constants'; -import { VIEW_API_LOGS } from '../constants'; - -import { EngineLogic } from '../../engine'; - -export const RecentApiLogs: React.FC = () => { - const { engineName } = useValues(EngineLogic); - const engineRoute = getEngineRoute(engineName); - - return ( - - - - -

    {RECENT_API_EVENTS}

    -
    -
    - - - {VIEW_API_LOGS} - - -
    - - TODO: API Logs Table - {/* */} - -
    - ); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.test.tsx deleted file mode 100644 index a56b41dfb05038..00000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.test.tsx +++ /dev/null @@ -1,46 +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 { setMockValues } from '../../../../__mocks__/kea.mock'; - -import React from 'react'; -import { shallow, ShallowWrapper } from 'enzyme'; - -import { EuiButton } from '../../../../shared/react_router_helpers'; - -import { TotalCharts } from './total_charts'; - -describe('TotalCharts', () => { - let wrapper: ShallowWrapper; - - beforeAll(() => { - jest.clearAllMocks(); - setMockValues({ - engineName: 'some-engine', - startDate: '1970-01-01', - endDate: '1970-01-08', - queriesPerDay: [0, 1, 2, 3, 5, 10, 50], - operationsPerDay: [0, 0, 0, 0, 0, 0, 0], - }); - wrapper = shallow(); - }); - - it('renders the total queries chart', () => { - const chart = wrapper.find('[data-test-subj="TotalQueriesChart"]'); - - expect(chart.find('h2').text()).toEqual('Total queries'); - expect(chart.find(EuiButton).prop('to')).toEqual('/engines/some-engine/analytics'); - // TODO: find chart component - }); - - it('renders the total API operations chart', () => { - const chart = wrapper.find('[data-test-subj="TotalApiOperationsChart"]'); - - expect(chart.find('h2').text()).toEqual('Total API operations'); - expect(chart.find(EuiButton).prop('to')).toEqual('/engines/some-engine/api-logs'); - // TODO: find chart component - }); -}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.tsx deleted file mode 100644 index e27fe3cdfc799b..00000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.tsx +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { useValues } from 'kea'; - -import { - EuiFlexGroup, - EuiFlexItem, - EuiPageContent, - EuiPageContentHeader, - EuiPageContentHeaderSection, - EuiPageContentBody, - EuiTitle, - EuiText, -} from '@elastic/eui'; - -import { EuiButton } from '../../../../shared/react_router_helpers'; - -import { ENGINE_ANALYTICS_PATH, ENGINE_API_LOGS_PATH, getEngineRoute } from '../../../routes'; -import { TOTAL_QUERIES, TOTAL_API_OPERATIONS } from '../../analytics/constants'; -import { VIEW_ANALYTICS, VIEW_API_LOGS, LAST_7_DAYS } from '../constants'; - -import { EngineLogic } from '../../engine'; -import { EngineOverviewLogic } from '../'; - -export const TotalCharts: React.FC = () => { - const { engineName } = useValues(EngineLogic); - const engineRoute = getEngineRoute(engineName); - - const { - // startDate, - // endDate, - // queriesPerDay, - // operationsPerDay, - } = useValues(EngineOverviewLogic); - - return ( - - - - - - -

    {TOTAL_QUERIES}

    -
    - - {LAST_7_DAYS} - -
    - - - {VIEW_ANALYTICS} - - -
    - - TODO: Analytics chart - {/* */} - -
    -
    - - - - - -

    {TOTAL_API_OPERATIONS}

    -
    - - {LAST_7_DAYS} - -
    - - - {VIEW_API_LOGS} - - -
    - - TODO: API Logs chart - {/* */} - -
    -
    -
    - ); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.test.tsx deleted file mode 100644 index 6cb47e8b419f3d..00000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.test.tsx +++ /dev/null @@ -1,51 +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 { setMockValues } from '../../../../__mocks__/kea.mock'; - -import React from 'react'; -import { shallow, ShallowWrapper } from 'enzyme'; -import { EuiStat } from '@elastic/eui'; - -import { TotalStats } from './total_stats'; - -describe('TotalStats', () => { - let wrapper: ShallowWrapper; - - beforeAll(() => { - jest.clearAllMocks(); - setMockValues({ - totalQueries: 11, - documentCount: 22, - totalClicks: 33, - }); - wrapper = shallow(); - }); - - it('renders the total queries stat', () => { - expect(wrapper.find('[data-test-subj="TotalQueriesCard"]')).toHaveLength(1); - - const card = wrapper.find(EuiStat).at(0); - expect(card.prop('title')).toEqual(11); - expect(card.prop('description')).toEqual('Total queries'); - }); - - it('renders the total documents stat', () => { - expect(wrapper.find('[data-test-subj="TotalDocumentsCard"]')).toHaveLength(1); - - const card = wrapper.find(EuiStat).at(1); - expect(card.prop('title')).toEqual(22); - expect(card.prop('description')).toEqual('Total documents'); - }); - - it('renders the total clicks stat', () => { - expect(wrapper.find('[data-test-subj="TotalClicksCard"]')).toHaveLength(1); - - const card = wrapper.find(EuiStat).at(2); - expect(card.prop('title')).toEqual(33); - expect(card.prop('description')).toEqual('Total clicks'); - }); -}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.tsx deleted file mode 100644 index a27142938f5583..00000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_stats.tsx +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { useValues } from 'kea'; -import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiStat } from '@elastic/eui'; - -import { TOTAL_QUERIES, TOTAL_DOCUMENTS, TOTAL_CLICKS } from '../../analytics/constants'; - -import { EngineOverviewLogic } from '../'; - -export const TotalStats: React.FC = () => { - const { totalQueries, documentCount, totalClicks } = useValues(EngineOverviewLogic); - - return ( - - - - - - - - - - - - - - - - - - ); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/unavailable_prompt.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/unavailable_prompt.test.tsx deleted file mode 100644 index 3ddfd14b0eb0cc..00000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/unavailable_prompt.test.tsx +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { shallow } from 'enzyme'; -import { EuiEmptyPrompt } from '@elastic/eui'; - -import { UnavailablePrompt } from './unavailable_prompt'; - -describe('UnavailablePrompt', () => { - it('renders', () => { - const wrapper = shallow(); - expect(wrapper.find(EuiEmptyPrompt)).toHaveLength(1); - }); -}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/unavailable_prompt.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/unavailable_prompt.tsx deleted file mode 100644 index e9cc6e2f05bf36..00000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/unavailable_prompt.tsx +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; - -import { i18n } from '@kbn/i18n'; -import { EuiEmptyPrompt } from '@elastic/eui'; - -export const UnavailablePrompt: React.FC = () => ( - - {i18n.translate('xpack.enterpriseSearch.appSearch.engine.overview.unavailableTitle', { - defaultMessage: 'Dashboard metrics are currently unavailable', - })} - - } - body={ -

    - {i18n.translate('xpack.enterpriseSearch.appSearch.engine.overview.unavailableBody', { - defaultMessage: 'Please try again in a few minutes.', - })} -

    - } - /> -); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/constants.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/constants.ts deleted file mode 100644 index 797811ec6cde8b..00000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/constants.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { i18n } from '@kbn/i18n'; - -export const OVERVIEW_TITLE = i18n.translate( - 'xpack.enterpriseSearch.appSearch.engine.overview.title', - { defaultMessage: 'Overview' } -); - -export const VIEW_ANALYTICS = i18n.translate( - 'xpack.enterpriseSearch.appSearch.engine.overview.analyticsLink', - { defaultMessage: 'View analytics' } -); - -export const VIEW_API_LOGS = i18n.translate( - 'xpack.enterpriseSearch.appSearch.engine.overview.apiLogsLink', - { defaultMessage: 'View API logs' } -); - -export const LAST_7_DAYS = i18n.translate( - 'xpack.enterpriseSearch.appSearch.engine.overview.chartDuration', - { defaultMessage: 'Last 7 days' } -); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx deleted file mode 100644 index 196fb2ca2bf13f..00000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx +++ /dev/null @@ -1,80 +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 '../../../__mocks__/shallow_useeffect.mock'; -import { setMockValues, setMockActions } from '../../../__mocks__/kea.mock'; - -import React from 'react'; -import { shallow } from 'enzyme'; - -import { Loading } from '../../../shared/loading'; -import { EmptyEngineOverview } from './engine_overview_empty'; -import { EngineOverviewMetrics } from './engine_overview_metrics'; -import { EngineOverview } from './'; - -describe('EngineOverview', () => { - const values = { - dataLoading: false, - documentCount: 0, - myRole: {}, - isMetaEngine: false, - }; - const actions = { - pollForOverviewMetrics: jest.fn(), - }; - - beforeEach(() => { - jest.clearAllMocks(); - setMockValues(values); - setMockActions(actions); - }); - - it('renders', () => { - const wrapper = shallow(); - expect(wrapper.find('[data-test-subj="EngineOverview"]')).toHaveLength(1); - }); - - it('initializes data on mount', () => { - shallow(); - expect(actions.pollForOverviewMetrics).toHaveBeenCalledTimes(1); - }); - - it('renders a loading component if async data is still loading', () => { - setMockValues({ ...values, dataLoading: true }); - const wrapper = shallow(); - expect(wrapper.find(Loading)).toHaveLength(1); - }); - - describe('EmptyEngineOverview', () => { - it('renders when the engine has no documents & the user can add documents', () => { - const myRole = { canManageEngineDocuments: true, canViewEngineCredentials: true }; - setMockValues({ ...values, myRole, documentCount: 0 }); - const wrapper = shallow(); - expect(wrapper.find(EmptyEngineOverview)).toHaveLength(1); - }); - }); - - describe('EngineOverviewMetrics', () => { - it('renders when the engine has documents', () => { - setMockValues({ ...values, documentCount: 1 }); - const wrapper = shallow(); - expect(wrapper.find(EngineOverviewMetrics)).toHaveLength(1); - }); - - it('renders when the user does not have the ability to add documents', () => { - const myRole = { canManageEngineDocuments: false, canViewEngineCredentials: false }; - setMockValues({ ...values, myRole }); - const wrapper = shallow(); - expect(wrapper.find(EngineOverviewMetrics)).toHaveLength(1); - }); - - it('always renders for meta engines', () => { - setMockValues({ ...values, isMetaEngine: true }); - const wrapper = shallow(); - expect(wrapper.find(EngineOverviewMetrics)).toHaveLength(1); - }); - }); -}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx deleted file mode 100644 index dd43bc67b3e88a..00000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { useEffect } from 'react'; -import { useActions, useValues } from 'kea'; - -import { AppLogic } from '../../app_logic'; -import { EngineLogic } from '../engine'; -import { Loading } from '../../../shared/loading'; - -import { EngineOverviewLogic } from './'; -import { EmptyEngineOverview } from './engine_overview_empty'; -import { EngineOverviewMetrics } from './engine_overview_metrics'; - -export const EngineOverview: React.FC = () => { - const { - myRole: { canManageEngineDocuments, canViewEngineCredentials }, - } = useValues(AppLogic); - const { isMetaEngine } = useValues(EngineLogic); - - const { pollForOverviewMetrics } = useActions(EngineOverviewLogic); - const { dataLoading, documentCount } = useValues(EngineOverviewLogic); - - useEffect(() => { - pollForOverviewMetrics(); - }, []); - - if (dataLoading) { - return ; - } - - const engineHasDocuments = documentCount > 0; - const canAddDocuments = canManageEngineDocuments && canViewEngineCredentials; - const showEngineOverview = engineHasDocuments || !canAddDocuments || isMetaEngine; - - return ( -
    - {showEngineOverview ? : } -
    - ); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.test.tsx deleted file mode 100644 index 8ebe09820a67e2..00000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.test.tsx +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import '../../../__mocks__/enterprise_search_url.mock'; -import { setMockValues } from '../../../__mocks__/kea.mock'; - -import React from 'react'; -import { shallow, ShallowWrapper } from 'enzyme'; -import { EuiButton } from '@elastic/eui'; - -import { CURRENT_MAJOR_VERSION } from '../../../../../common/version'; - -import { EmptyEngineOverview } from './engine_overview_empty'; - -describe('EmptyEngineOverview', () => { - let wrapper: ShallowWrapper; - - beforeAll(() => { - jest.clearAllMocks(); - setMockValues({ - engineName: 'empty-engine', - }); - wrapper = shallow(); - }); - - it('renders', () => { - expect(wrapper.find('h1').text()).toEqual('Engine setup'); - expect(wrapper.find('h2').text()).toEqual('Setting up the “empty-engine” engine'); - expect(wrapper.find('h3').text()).toEqual('Indexing by API'); - }); - - it('renders correctly versioned documentation URLs', () => { - expect(wrapper.find(EuiButton).prop('href')).toEqual( - `https://www.elastic.co/guide/en/app-search/${CURRENT_MAJOR_VERSION}/index.html` - ); - }); -}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.tsx deleted file mode 100644 index f2bf5a54f810c0..00000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_empty.tsx +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { useValues } from 'kea'; - -import { i18n } from '@kbn/i18n'; -import { - EuiPageHeader, - EuiPageHeaderSection, - EuiPageContent, - EuiPageContentHeader, - EuiPageContentBody, - EuiTitle, - EuiText, - EuiButton, - EuiSpacer, -} from '@elastic/eui'; - -import { EngineLogic } from '../engine'; - -import { DOCS_PREFIX } from '../../routes'; -import { - DOCUMENT_CREATION_DESCRIPTION, - DOCUMENT_API_INDEXING_TITLE, - DOCUMENT_API_INDEXING_DESCRIPTION, -} from '../document_creation/constants'; -// TODO -// import { DocumentCreationButtons, CodeExample } from '../document_creation' - -export const EmptyEngineOverview: React.FC = () => { - const { engineName } = useValues(EngineLogic); - - return ( - <> - - - -

    - {i18n.translate('xpack.enterpriseSearch.appSearch.engine.overview.empty.heading', { - defaultMessage: 'Engine setup', - })} -

    -
    -
    - - - {i18n.translate( - 'xpack.enterpriseSearch.appSearch.engine.overview.empty.headingAction', - { defaultMessage: 'View documentation' } - )} - - -
    - - - -

    - {i18n.translate('xpack.enterpriseSearch.appSearch.engine.overview.empty.subheading', { - defaultMessage: 'Setting up the “{engineName}” engine', - values: { engineName }, - })} -

    -
    -
    - - -

    {DOCUMENT_CREATION_DESCRIPTION}

    -
    - - {/* TODO: */} -
    - - - -

    {DOCUMENT_API_INDEXING_TITLE}

    -
    -
    - - -

    {DOCUMENT_API_INDEXING_DESCRIPTION}

    -

    - {i18n.translate('xpack.enterpriseSearch.appSearch.engine.overview.empty.apiExample', { - defaultMessage: - 'To see the API in action, you can experiment with the example request below using a command line or a client library.', - })} -

    -
    - - {/* */} -
    -
    - - ); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_metrics.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_metrics.test.tsx deleted file mode 100644 index 8250446e231b35..00000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_metrics.test.tsx +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { setMockValues } from '../../../__mocks__/kea.mock'; - -import React from 'react'; -import { shallow } from 'enzyme'; - -import { UnavailablePrompt, TotalStats, TotalCharts, RecentApiLogs } from './components'; -import { EngineOverviewMetrics } from './engine_overview_metrics'; - -describe('EngineOverviewMetrics', () => { - it('renders', () => { - const wrapper = shallow(); - expect(wrapper.find('h1').text()).toEqual('Engine overview'); - }); - - it('renders an unavailable prompt if engine data is still indexing', () => { - setMockValues({ apiLogsUnavailable: true }); - const wrapper = shallow(); - expect(wrapper.find(UnavailablePrompt)).toHaveLength(1); - }); - - it('renders total stats, charts, and recent logs when metrics are available', () => { - setMockValues({ apiLogsUnavailable: false }); - const wrapper = shallow(); - expect(wrapper.find(TotalStats)).toHaveLength(1); - expect(wrapper.find(TotalCharts)).toHaveLength(1); - expect(wrapper.find(RecentApiLogs)).toHaveLength(1); - }); -}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_metrics.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_metrics.tsx deleted file mode 100644 index 9630f6fa2f81dc..00000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview_metrics.tsx +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { useValues } from 'kea'; - -import { i18n } from '@kbn/i18n'; -import { EuiPageHeader, EuiTitle, EuiSpacer } from '@elastic/eui'; - -import { EngineOverviewLogic } from './'; - -import { UnavailablePrompt, TotalStats, TotalCharts, RecentApiLogs } from './components'; - -export const EngineOverviewMetrics: React.FC = () => { - const { apiLogsUnavailable } = useValues(EngineOverviewLogic); - - return ( - <> - - -

    - {i18n.translate('xpack.enterpriseSearch.appSearch.engine.overview.heading', { - defaultMessage: 'Engine overview', - })} -

    -
    -
    - {apiLogsUnavailable ? ( - - ) : ( - <> - - - - - - - )} - - ); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/index.ts index 82c5d7dc8e60a4..fcd92ba6a338c0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/index.ts @@ -5,5 +5,3 @@ */ export { EngineOverviewLogic } from './engine_overview_logic'; -export { EngineOverview } from './engine_overview'; -export { OVERVIEW_TITLE } from './constants'; From d97ddcd4dae437bf2b81251926b89a024d69f6d5 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Wed, 18 Nov 2020 18:42:37 -0700 Subject: [PATCH 08/16] [maps] convert VectorStyleEditor to TS (#83582) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../style_property_descriptor_types.ts | 24 +- .../blended_vector_layer.ts | 11 +- .../create_choropleth_layer_descriptor.ts | 2 +- .../create_region_map_layer_descriptor.ts | 4 +- .../create_tile_map_layer_descriptor.ts | 10 +- .../maps/public/classes/layers/layer.tsx | 18 +- .../observability/create_layer_descriptor.ts | 6 +- .../security/create_layer_descriptors.ts | 8 +- .../clusters_layer_wizard.tsx | 4 +- .../classes/styles/heatmap/heatmap_style.tsx | 6 +- .../maps/public/classes/styles/style.ts | 11 +- .../public/classes/styles/tile/tile_style.ts | 2 +- .../color/vector_style_color_editor.tsx | 6 +- ...ditor.js => vector_style_label_editor.tsx} | 9 +- ...editor.js => vector_style_size_editor.tsx} | 9 +- .../vector/components/style_prop_editor.tsx | 10 +- ...editor.js => vector_style_icon_editor.tsx} | 9 +- ...tyle_editor.js => vector_style_editor.tsx} | 252 ++++++++++++------ .../styles/vector/style_fields_helper.ts | 2 +- .../classes/styles/vector/vector_style.tsx | 43 ++- .../vector/vector_style_defaults.test.ts | 8 +- .../styles/vector/vector_style_defaults.ts | 36 +-- .../style_settings/style_settings.js | 4 +- 23 files changed, 297 insertions(+), 197 deletions(-) rename x-pack/plugins/maps/public/classes/styles/vector/components/label/{vector_style_label_editor.js => vector_style_label_editor.tsx} (62%) rename x-pack/plugins/maps/public/classes/styles/vector/components/size/{vector_style_size_editor.js => vector_style_size_editor.tsx} (62%) rename x-pack/plugins/maps/public/classes/styles/vector/components/symbol/{vector_style_icon_editor.js => vector_style_icon_editor.tsx} (62%) rename x-pack/plugins/maps/public/classes/styles/vector/components/{vector_style_editor.js => vector_style_editor.tsx} (64%) diff --git a/x-pack/plugins/maps/common/descriptor_types/style_property_descriptor_types.ts b/x-pack/plugins/maps/common/descriptor_types/style_property_descriptor_types.ts index 5aba9b06a6ccf6..d52afebcaa2549 100644 --- a/x-pack/plugins/maps/common/descriptor_types/style_property_descriptor_types.ts +++ b/x-pack/plugins/maps/common/descriptor_types/style_property_descriptor_types.ts @@ -174,18 +174,18 @@ export type SizeStylePropertyDescriptor = }; export type VectorStylePropertiesDescriptor = { - [VECTOR_STYLES.SYMBOLIZE_AS]?: SymbolizeAsStylePropertyDescriptor; - [VECTOR_STYLES.FILL_COLOR]?: ColorStylePropertyDescriptor; - [VECTOR_STYLES.LINE_COLOR]?: ColorStylePropertyDescriptor; - [VECTOR_STYLES.LINE_WIDTH]?: SizeStylePropertyDescriptor; - [VECTOR_STYLES.ICON]?: IconStylePropertyDescriptor; - [VECTOR_STYLES.ICON_SIZE]?: SizeStylePropertyDescriptor; - [VECTOR_STYLES.ICON_ORIENTATION]?: OrientationStylePropertyDescriptor; - [VECTOR_STYLES.LABEL_TEXT]?: LabelStylePropertyDescriptor; - [VECTOR_STYLES.LABEL_COLOR]?: ColorStylePropertyDescriptor; - [VECTOR_STYLES.LABEL_SIZE]?: SizeStylePropertyDescriptor; - [VECTOR_STYLES.LABEL_BORDER_COLOR]?: ColorStylePropertyDescriptor; - [VECTOR_STYLES.LABEL_BORDER_SIZE]?: LabelBorderSizeStylePropertyDescriptor; + [VECTOR_STYLES.SYMBOLIZE_AS]: SymbolizeAsStylePropertyDescriptor; + [VECTOR_STYLES.FILL_COLOR]: ColorStylePropertyDescriptor; + [VECTOR_STYLES.LINE_COLOR]: ColorStylePropertyDescriptor; + [VECTOR_STYLES.LINE_WIDTH]: SizeStylePropertyDescriptor; + [VECTOR_STYLES.ICON]: IconStylePropertyDescriptor; + [VECTOR_STYLES.ICON_SIZE]: SizeStylePropertyDescriptor; + [VECTOR_STYLES.ICON_ORIENTATION]: OrientationStylePropertyDescriptor; + [VECTOR_STYLES.LABEL_TEXT]: LabelStylePropertyDescriptor; + [VECTOR_STYLES.LABEL_COLOR]: ColorStylePropertyDescriptor; + [VECTOR_STYLES.LABEL_SIZE]: SizeStylePropertyDescriptor; + [VECTOR_STYLES.LABEL_BORDER_COLOR]: ColorStylePropertyDescriptor; + [VECTOR_STYLES.LABEL_BORDER_SIZE]: LabelBorderSizeStylePropertyDescriptor; }; export type StyleDescriptor = { diff --git a/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts b/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts index 2ab8a70f2e4dfc..85391ea82cbf2a 100644 --- a/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts +++ b/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts @@ -36,6 +36,7 @@ import { LayerDescriptor, VectorLayerDescriptor, VectorSourceRequestMeta, + VectorStylePropertiesDescriptor, } from '../../../../common/descriptor_types'; import { IVectorSource } from '../../sources/vector_source'; import { LICENSED_FEATURES } from '../../../licensed_features'; @@ -79,13 +80,15 @@ function getClusterStyleDescriptor( clusterSource: ESGeoGridSource ): VectorStyleDescriptor { const defaultDynamicProperties = getDefaultDynamicProperties(); - const clusterStyleDescriptor: VectorStyleDescriptor = { + const clusterStyleDescriptor: Omit & { + properties: Partial; + } = { type: LAYER_STYLE_TYPE.VECTOR, properties: { [VECTOR_STYLES.LABEL_TEXT]: { type: STYLE_TYPE.DYNAMIC, options: { - ...defaultDynamicProperties[VECTOR_STYLES.LABEL_TEXT]!.options, + ...defaultDynamicProperties[VECTOR_STYLES.LABEL_TEXT].options, field: { name: COUNT_PROP_NAME, origin: FIELD_ORIGIN.SOURCE, @@ -95,7 +98,7 @@ function getClusterStyleDescriptor( [VECTOR_STYLES.ICON_SIZE]: { type: STYLE_TYPE.DYNAMIC, options: { - ...(defaultDynamicProperties[VECTOR_STYLES.ICON_SIZE]!.options as SizeDynamicOptions), + ...(defaultDynamicProperties[VECTOR_STYLES.ICON_SIZE].options as SizeDynamicOptions), field: { name: COUNT_PROP_NAME, origin: FIELD_ORIGIN.SOURCE, @@ -157,7 +160,7 @@ function getClusterStyleDescriptor( } }); - return clusterStyleDescriptor; + return clusterStyleDescriptor as VectorStyleDescriptor; } export interface BlendedVectorLayerArguments { diff --git a/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/create_choropleth_layer_descriptor.ts b/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/create_choropleth_layer_descriptor.ts index cdfe60946f5f90..fa82b9dc3b5423 100644 --- a/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/create_choropleth_layer_descriptor.ts +++ b/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/create_choropleth_layer_descriptor.ts @@ -71,7 +71,7 @@ function createChoroplethLayerDescriptor({ [VECTOR_STYLES.FILL_COLOR]: { type: STYLE_TYPE.DYNAMIC, options: { - ...(defaultDynamicProperties[VECTOR_STYLES.FILL_COLOR]!.options as ColorDynamicOptions), + ...(defaultDynamicProperties[VECTOR_STYLES.FILL_COLOR].options as ColorDynamicOptions), field: { name: joinKey, origin: FIELD_ORIGIN.JOIN, diff --git a/x-pack/plugins/maps/public/classes/layers/create_region_map_layer_descriptor.ts b/x-pack/plugins/maps/public/classes/layers/create_region_map_layer_descriptor.ts index 6f9bb686459b5a..5fa2524b1b790a 100644 --- a/x-pack/plugins/maps/public/classes/layers/create_region_map_layer_descriptor.ts +++ b/x-pack/plugins/maps/public/classes/layers/create_region_map_layer_descriptor.ts @@ -100,7 +100,7 @@ export function createRegionMapLayerDescriptor({ [VECTOR_STYLES.FILL_COLOR]: { type: STYLE_TYPE.DYNAMIC, options: { - ...(defaultDynamicProperties[VECTOR_STYLES.FILL_COLOR]!.options as ColorDynamicOptions), + ...(defaultDynamicProperties[VECTOR_STYLES.FILL_COLOR].options as ColorDynamicOptions), field: { name: joinKey, origin: FIELD_ORIGIN.JOIN, @@ -108,7 +108,7 @@ export function createRegionMapLayerDescriptor({ color: colorPallette ? colorPallette.value : 'Yellow to Red', type: COLOR_MAP_TYPE.ORDINAL, fieldMetaOptions: { - ...(defaultDynamicProperties[VECTOR_STYLES.FILL_COLOR]!.options as ColorDynamicOptions) + ...(defaultDynamicProperties[VECTOR_STYLES.FILL_COLOR].options as ColorDynamicOptions) .fieldMetaOptions, isEnabled: false, }, diff --git a/x-pack/plugins/maps/public/classes/layers/create_tile_map_layer_descriptor.ts b/x-pack/plugins/maps/public/classes/layers/create_tile_map_layer_descriptor.ts index 5b89373f2db481..05616f6916f627 100644 --- a/x-pack/plugins/maps/public/classes/layers/create_tile_map_layer_descriptor.ts +++ b/x-pack/plugins/maps/public/classes/layers/create_tile_map_layer_descriptor.ts @@ -113,16 +113,16 @@ export function createTileMapLayerDescriptor({ const colorPallette = NUMERICAL_COLOR_PALETTES.find((pallette) => { return pallette.value.toLowerCase() === colorSchema.toLowerCase(); }); - const styleProperties: VectorStylePropertiesDescriptor = { + const styleProperties: Partial = { [VECTOR_STYLES.FILL_COLOR]: { type: STYLE_TYPE.DYNAMIC, options: { - ...(defaultDynamicProperties[VECTOR_STYLES.FILL_COLOR]!.options as ColorDynamicOptions), + ...(defaultDynamicProperties[VECTOR_STYLES.FILL_COLOR].options as ColorDynamicOptions), field: metricStyleField, color: colorPallette ? colorPallette.value : 'Yellow to Red', type: COLOR_MAP_TYPE.ORDINAL, fieldMetaOptions: { - ...(defaultDynamicProperties[VECTOR_STYLES.FILL_COLOR]!.options as ColorDynamicOptions) + ...(defaultDynamicProperties[VECTOR_STYLES.FILL_COLOR].options as ColorDynamicOptions) .fieldMetaOptions, isEnabled: false, }, @@ -139,11 +139,11 @@ export function createTileMapLayerDescriptor({ styleProperties[VECTOR_STYLES.ICON_SIZE] = { type: STYLE_TYPE.DYNAMIC, options: { - ...(defaultDynamicProperties[VECTOR_STYLES.ICON_SIZE]!.options as SizeDynamicOptions), + ...(defaultDynamicProperties[VECTOR_STYLES.ICON_SIZE].options as SizeDynamicOptions), maxSize: 18, field: metricStyleField, fieldMetaOptions: { - ...(defaultDynamicProperties[VECTOR_STYLES.ICON_SIZE]!.options as SizeDynamicOptions) + ...(defaultDynamicProperties[VECTOR_STYLES.ICON_SIZE].options as SizeDynamicOptions) .fieldMetaOptions, isEnabled: false, }, diff --git a/x-pack/plugins/maps/public/classes/layers/layer.tsx b/x-pack/plugins/maps/public/classes/layers/layer.tsx index b982e6452e8cb1..060ff4d46fa2ac 100644 --- a/x-pack/plugins/maps/public/classes/layers/layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/layer.tsx @@ -76,11 +76,9 @@ export interface ILayer { getType(): string | undefined; isVisible(): boolean; cloneDescriptor(): Promise; - renderStyleEditor({ - onStyleDescriptorChange, - }: { - onStyleDescriptorChange: (styleDescriptor: StyleDescriptor) => void; - }): ReactElement | null; + renderStyleEditor( + onStyleDescriptorChange: (styleDescriptor: StyleDescriptor) => void + ): ReactElement | null; getInFlightRequestTokens(): symbol[]; getPrevRequestToken(dataId: string): symbol | undefined; destroy: () => void; @@ -437,16 +435,14 @@ export class AbstractLayer implements ILayer { return null; } - renderStyleEditor({ - onStyleDescriptorChange, - }: { - onStyleDescriptorChange: (styleDescriptor: StyleDescriptor) => void; - }): ReactElement | null { + renderStyleEditor( + onStyleDescriptorChange: (styleDescriptor: StyleDescriptor) => void + ): ReactElement | null { const style = this.getStyleForEditing(); if (!style) { return null; } - return style.renderEditor({ layer: this, onStyleDescriptorChange }); + return style.renderEditor(onStyleDescriptorChange); } getIndexPatternIds(): string[] { diff --git a/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.ts b/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.ts index dea551866f4a95..7e8a216685bbd1 100644 --- a/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.ts +++ b/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.ts @@ -50,7 +50,7 @@ function createDynamicFillColorDescriptor( return { type: STYLE_TYPE.DYNAMIC, options: { - ...(defaultDynamicProperties[VECTOR_STYLES.FILL_COLOR]!.options as ColorDynamicOptions), + ...(defaultDynamicProperties[VECTOR_STYLES.FILL_COLOR].options as ColorDynamicOptions), field, color: layer === OBSERVABILITY_LAYER_TYPE.APM_RUM_PERFORMANCE ? 'Green to Red' : 'Yellow to Red', @@ -226,12 +226,12 @@ export function createLayerDescriptor({ origin: FIELD_ORIGIN.SOURCE, }; - const styleProperties: VectorStylePropertiesDescriptor = { + const styleProperties: Partial = { [VECTOR_STYLES.FILL_COLOR]: createDynamicFillColorDescriptor(layer, metricStyleField), [VECTOR_STYLES.ICON_SIZE]: { type: STYLE_TYPE.DYNAMIC, options: { - ...(defaultDynamicProperties[VECTOR_STYLES.ICON_SIZE]!.options as SizeDynamicOptions), + ...(defaultDynamicProperties[VECTOR_STYLES.ICON_SIZE].options as SizeDynamicOptions), field: metricStyleField, }, }, diff --git a/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.ts b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.ts index 909cd93b3df7a1..b52ce02acb5f08 100644 --- a/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.ts +++ b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.ts @@ -68,7 +68,7 @@ function createSourceLayerDescriptor(indexPatternId: string, indexPatternTitle: ], }); - const styleProperties: VectorStylePropertiesDescriptor = { + const styleProperties: Partial = { [VECTOR_STYLES.FILL_COLOR]: { type: STYLE_TYPE.STATIC, options: { color: euiVisColorPalette[1] }, @@ -121,7 +121,7 @@ function createDestinationLayerDescriptor(indexPatternId: string, indexPatternTi ], }); - const styleProperties: VectorStylePropertiesDescriptor = { + const styleProperties: Partial = { [VECTOR_STYLES.FILL_COLOR]: { type: STYLE_TYPE.STATIC, options: { color: euiVisColorPalette[2] }, @@ -168,7 +168,7 @@ function createLineLayerDescriptor(indexPatternId: string, indexPatternTitle: st ], }); - const styleProperties: VectorStylePropertiesDescriptor = { + const styleProperties: Partial = { [VECTOR_STYLES.LINE_COLOR]: { type: STYLE_TYPE.STATIC, options: { color: euiVisColorPalette[1] }, @@ -176,7 +176,7 @@ function createLineLayerDescriptor(indexPatternId: string, indexPatternTitle: st [VECTOR_STYLES.LINE_WIDTH]: { type: STYLE_TYPE.DYNAMIC, options: { - ...(defaultDynamicProperties[VECTOR_STYLES.LINE_WIDTH]!.options as SizeDynamicOptions), + ...(defaultDynamicProperties[VECTOR_STYLES.LINE_WIDTH].options as SizeDynamicOptions), field: { name: COUNT_PROP_NAME, origin: FIELD_ORIGIN.SOURCE, diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/clusters_layer_wizard.tsx b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/clusters_layer_wizard.tsx index 0f596c47fc9b68..1fd6a9c9ecc8e5 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/clusters_layer_wizard.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/clusters_layer_wizard.tsx @@ -77,7 +77,7 @@ export const clustersLayerWizardConfig: LayerWizard = { [VECTOR_STYLES.ICON_SIZE]: { type: STYLE_TYPE.DYNAMIC, options: { - ...(defaultDynamicProperties[VECTOR_STYLES.ICON_SIZE]!.options as SizeDynamicOptions), + ...(defaultDynamicProperties[VECTOR_STYLES.ICON_SIZE].options as SizeDynamicOptions), field: { name: COUNT_PROP_NAME, origin: FIELD_ORIGIN.SOURCE, @@ -87,7 +87,7 @@ export const clustersLayerWizardConfig: LayerWizard = { [VECTOR_STYLES.LABEL_TEXT]: { type: STYLE_TYPE.DYNAMIC, options: { - ...defaultDynamicProperties[VECTOR_STYLES.LABEL_TEXT]!.options, + ...defaultDynamicProperties[VECTOR_STYLES.LABEL_TEXT].options, field: { name: COUNT_PROP_NAME, origin: FIELD_ORIGIN.SOURCE, diff --git a/x-pack/plugins/maps/public/classes/styles/heatmap/heatmap_style.tsx b/x-pack/plugins/maps/public/classes/styles/heatmap/heatmap_style.tsx index c75698805225f1..599f3b2dfbb02a 100644 --- a/x-pack/plugins/maps/public/classes/styles/heatmap/heatmap_style.tsx +++ b/x-pack/plugins/maps/public/classes/styles/heatmap/heatmap_style.tsx @@ -41,11 +41,7 @@ export class HeatmapStyle implements IStyle { return LAYER_STYLE_TYPE.HEATMAP; } - renderEditor({ - onStyleDescriptorChange, - }: { - onStyleDescriptorChange: (styleDescriptor: StyleDescriptor) => void; - }) { + renderEditor(onStyleDescriptorChange: (styleDescriptor: StyleDescriptor) => void) { const onHeatmapColorChange = ({ colorRampName }: { colorRampName: string }) => { const styleDescriptor = HeatmapStyle.createDescriptor(colorRampName); onStyleDescriptorChange(styleDescriptor); diff --git a/x-pack/plugins/maps/public/classes/styles/style.ts b/x-pack/plugins/maps/public/classes/styles/style.ts index abaa6184b0ca44..de14ab990fa234 100644 --- a/x-pack/plugins/maps/public/classes/styles/style.ts +++ b/x-pack/plugins/maps/public/classes/styles/style.ts @@ -6,15 +6,10 @@ import { ReactElement } from 'react'; import { StyleDescriptor } from '../../../common/descriptor_types'; -import { ILayer } from '../layers/layer'; export interface IStyle { getType(): string; - renderEditor({ - layer, - onStyleDescriptorChange, - }: { - layer: ILayer; - onStyleDescriptorChange: (styleDescriptor: StyleDescriptor) => void; - }): ReactElement | null; + renderEditor( + onStyleDescriptorChange: (styleDescriptor: StyleDescriptor) => void + ): ReactElement | null; } diff --git a/x-pack/plugins/maps/public/classes/styles/tile/tile_style.ts b/x-pack/plugins/maps/public/classes/styles/tile/tile_style.ts index cac3913d3149d3..dad26d4172e0a0 100644 --- a/x-pack/plugins/maps/public/classes/styles/tile/tile_style.ts +++ b/x-pack/plugins/maps/public/classes/styles/tile/tile_style.ts @@ -21,7 +21,7 @@ export class TileStyle implements IStyle { return LAYER_STYLE_TYPE.TILE; } - renderEditor(/* { layer, onStyleDescriptorChange } */) { + renderEditor(/* onStyleDescriptorChange */) { return null; } } diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/color/vector_style_color_editor.tsx b/x-pack/plugins/maps/public/classes/styles/vector/components/color/vector_style_color_editor.tsx index 4527f56c04d2ee..d45c33bbc3f574 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/color/vector_style_color_editor.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/color/vector_style_color_editor.tsx @@ -14,7 +14,11 @@ import { DynamicColorForm } from './dynamic_color_form'; import { StaticColorForm } from './static_color_form'; import { ColorDynamicOptions, ColorStaticOptions } from '../../../../../../common/descriptor_types'; -export function VectorStyleColorEditor(props: Props) { +type ColorEditorProps = Omit, 'children'> & { + swatches: string[]; +}; + +export function VectorStyleColorEditor(props: ColorEditorProps) { const colorForm = props.styleProperty.isDynamic() ? ( ) : ( diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/label/vector_style_label_editor.js b/x-pack/plugins/maps/public/classes/styles/vector/components/label/vector_style_label_editor.tsx similarity index 62% rename from x-pack/plugins/maps/public/classes/styles/vector/components/label/vector_style_label_editor.js rename to x-pack/plugins/maps/public/classes/styles/vector/components/label/vector_style_label_editor.tsx index aaa21ea315f36c..586d4fc0576ad4 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/label/vector_style_label_editor.js +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/label/vector_style_label_editor.tsx @@ -6,11 +6,16 @@ import React from 'react'; -import { StylePropEditor } from '../style_prop_editor'; +import { Props, StylePropEditor } from '../style_prop_editor'; +// @ts-expect-error import { DynamicLabelForm } from './dynamic_label_form'; +// @ts-expect-error import { StaticLabelForm } from './static_label_form'; +import { LabelDynamicOptions, LabelStaticOptions } from '../../../../../../common/descriptor_types'; -export function VectorStyleLabelEditor(props) { +type LabelEditorProps = Omit, 'children'>; + +export function VectorStyleLabelEditor(props: LabelEditorProps) { const labelForm = props.styleProperty.isDynamic() ? ( ) : ( diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/size/vector_style_size_editor.js b/x-pack/plugins/maps/public/classes/styles/vector/components/size/vector_style_size_editor.tsx similarity index 62% rename from x-pack/plugins/maps/public/classes/styles/vector/components/size/vector_style_size_editor.js rename to x-pack/plugins/maps/public/classes/styles/vector/components/size/vector_style_size_editor.tsx index e344f72bd429ad..c492f24661e713 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/size/vector_style_size_editor.js +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/size/vector_style_size_editor.tsx @@ -6,11 +6,16 @@ import React from 'react'; -import { StylePropEditor } from '../style_prop_editor'; +import { Props, StylePropEditor } from '../style_prop_editor'; +// @ts-expect-error import { DynamicSizeForm } from './dynamic_size_form'; +// @ts-expect-error import { StaticSizeForm } from './static_size_form'; +import { SizeDynamicOptions, SizeStaticOptions } from '../../../../../../common/descriptor_types'; -export function VectorStyleSizeEditor(props) { +type SizeEditorProps = Omit, 'children'>; + +export function VectorStyleSizeEditor(props: SizeEditorProps) { const sizeForm = props.styleProperty.isDynamic() ? ( ) : ( diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/style_prop_editor.tsx b/x-pack/plugins/maps/public/classes/styles/vector/components/style_prop_editor.tsx index 43b088074a30ef..f3363a9443cfde 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/style_prop_editor.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/style_prop_editor.tsx @@ -25,12 +25,12 @@ export interface Props { customStaticOptionLabel?: string; defaultStaticStyleOptions: StaticOptions; defaultDynamicStyleOptions: DynamicOptions; - disabled: boolean; + disabled?: boolean; disabledBy?: VECTOR_STYLES; fields: StyleField[]; onDynamicStyleChange: (propertyName: VECTOR_STYLES, options: DynamicOptions) => void; onStaticStyleChange: (propertyName: VECTOR_STYLES, options: StaticOptions) => void; - styleProperty: IStyleProperty; + styleProperty: IStyleProperty; } export class StylePropEditor extends Component< @@ -42,7 +42,7 @@ export class StylePropEditor extends Component< _onTypeToggle = () => { if (this.props.styleProperty.isDynamic()) { // preserve current dynmaic style - this._prevDynamicStyleOptions = this.props.styleProperty.getOptions(); + this._prevDynamicStyleOptions = this.props.styleProperty.getOptions() as DynamicOptions; // toggle to static style this.props.onStaticStyleChange( this.props.styleProperty.getStyleName(), @@ -50,7 +50,7 @@ export class StylePropEditor extends Component< ); } else { // preserve current static style - this._prevStaticStyleOptions = this.props.styleProperty.getOptions(); + this._prevStaticStyleOptions = this.props.styleProperty.getOptions() as StaticOptions; // toggle to dynamic style this.props.onDynamicStyleChange( this.props.styleProperty.getStyleName(), @@ -61,7 +61,7 @@ export class StylePropEditor extends Component< _onFieldMetaOptionsChange = (fieldMetaOptions: FieldMetaOptions) => { const options = { - ...this.props.styleProperty.getOptions(), + ...(this.props.styleProperty.getOptions() as DynamicOptions), fieldMetaOptions, }; this.props.onDynamicStyleChange(this.props.styleProperty.getStyleName(), options); diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/vector_style_icon_editor.js b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/vector_style_icon_editor.tsx similarity index 62% rename from x-pack/plugins/maps/public/classes/styles/vector/components/symbol/vector_style_icon_editor.js rename to x-pack/plugins/maps/public/classes/styles/vector/components/symbol/vector_style_icon_editor.tsx index 2a983a32f0d825..bd6cda0b57f8da 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/vector_style_icon_editor.js +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/vector_style_icon_editor.tsx @@ -6,11 +6,16 @@ import React from 'react'; -import { StylePropEditor } from '../style_prop_editor'; +import { Props, StylePropEditor } from '../style_prop_editor'; +// @ts-expect-error import { DynamicIconForm } from './dynamic_icon_form'; +// @ts-expect-error import { StaticIconForm } from './static_icon_form'; +import { IconDynamicOptions, IconStaticOptions } from '../../../../../../common/descriptor_types'; -export function VectorStyleIconEditor(props) { +type IconEditorProps = Omit, 'children'>; + +export function VectorStyleIconEditor(props: IconEditorProps) { const iconForm = props.styleProperty.isDynamic() ? ( ) : ( diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/vector_style_editor.js b/x-pack/plugins/maps/public/classes/styles/vector/components/vector_style_editor.tsx similarity index 64% rename from x-pack/plugins/maps/public/classes/styles/vector/components/vector_style_editor.js rename to x-pack/plugins/maps/public/classes/styles/vector/components/vector_style_editor.tsx index d577912efb830c..95e32f0e9969b0 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/vector_style_editor.js +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/vector_style_editor.tsx @@ -7,34 +7,95 @@ import _ from 'lodash'; import React, { Component, Fragment } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiSpacer, EuiButtonGroup, EuiFormRow, EuiSwitch, EuiSwitchEvent } from '@elastic/eui'; import { VectorStyleColorEditor } from './color/vector_style_color_editor'; import { VectorStyleSizeEditor } from './size/vector_style_size_editor'; +// @ts-expect-error import { VectorStyleSymbolizeAsEditor } from './symbol/vector_style_symbolize_as_editor'; import { VectorStyleIconEditor } from './symbol/vector_style_icon_editor'; import { VectorStyleLabelEditor } from './label/vector_style_label_editor'; +// @ts-expect-error import { VectorStyleLabelBorderSizeEditor } from './label/vector_style_label_border_size_editor'; +// @ts-expect-error import { OrientationEditor } from './orientation/orientation_editor'; import { getDefaultDynamicProperties, getDefaultStaticProperties } from '../vector_style_defaults'; import { DEFAULT_FILL_COLORS, DEFAULT_LINE_COLORS } from '../../color_palettes'; -import { i18n } from '@kbn/i18n'; -import { EuiSpacer, EuiButtonGroup, EuiFormRow, EuiSwitch } from '@elastic/eui'; import { LABEL_BORDER_SIZES, VECTOR_STYLES, STYLE_TYPE, VECTOR_SHAPE_TYPE, } from '../../../../../common/constants'; -import { createStyleFieldsHelper } from '../style_fields_helper'; - -export class VectorStyleEditor extends Component { - state = { - styleFields: [], - defaultDynamicProperties: getDefaultDynamicProperties(), - defaultStaticProperties: getDefaultStaticProperties(), - supportedFeatures: undefined, - selectedFeature: null, - }; +import { createStyleFieldsHelper, StyleField, StyleFieldsHelper } from '../style_fields_helper'; +import { + ColorDynamicOptions, + ColorStaticOptions, + DynamicStylePropertyOptions, + IconDynamicOptions, + IconStaticOptions, + LabelDynamicOptions, + LabelStaticOptions, + SizeDynamicOptions, + SizeStaticOptions, + StaticStylePropertyOptions, + StylePropertyOptions, + VectorStylePropertiesDescriptor, +} from '../../../../../common/descriptor_types'; +import { IStyleProperty } from '../properties/style_property'; +import { SymbolizeAsProperty } from '../properties/symbolize_as_property'; +import { LabelBorderSizeProperty } from '../properties/label_border_size_property'; +import { StaticTextProperty } from '../properties/static_text_property'; +import { StaticSizeProperty } from '../properties/static_size_property'; +import { IVectorLayer } from '../../../layers/vector_layer/vector_layer'; + +export interface StyleProperties { + [key: string]: IStyleProperty; +} + +interface Props { + layer: IVectorLayer; + isPointsOnly: boolean; + isLinesOnly: boolean; + onIsTimeAwareChange: (isTimeAware: boolean) => void; + handlePropertyChange: (propertyName: VECTOR_STYLES, stylePropertyDescriptor: unknown) => void; + hasBorder: boolean; + styleProperties: StyleProperties; + isTimeAware: boolean; + showIsTimeAware: boolean; +} + +interface State { + styleFields: StyleField[]; + defaultDynamicProperties: VectorStylePropertiesDescriptor; + defaultStaticProperties: VectorStylePropertiesDescriptor; + supportedFeatures: VECTOR_SHAPE_TYPE[]; + selectedFeature: VECTOR_SHAPE_TYPE; + styleFieldsHelper?: StyleFieldsHelper; +} + +export class VectorStyleEditor extends Component { + private _isMounted: boolean = false; + + constructor(props: Props) { + super(props); + + let selectedFeature = VECTOR_SHAPE_TYPE.POLYGON; + if (props.isPointsOnly) { + selectedFeature = VECTOR_SHAPE_TYPE.POINT; + } else if (props.isLinesOnly) { + selectedFeature = VECTOR_SHAPE_TYPE.LINE; + } + + this.state = { + styleFields: [], + defaultDynamicProperties: getDefaultDynamicProperties(), + defaultStaticProperties: getDefaultStaticProperties(), + supportedFeatures: [], + selectedFeature, + }; + } componentWillUnmount() { this._isMounted = false; @@ -68,36 +129,20 @@ export class VectorStyleEditor extends Component { async _loadSupportedFeatures() { const supportedFeatures = await this.props.layer.getSource().getSupportedShapeTypes(); - if (!this._isMounted) { - return; - } - - if (!_.isEqual(supportedFeatures, this.state.supportedFeatures)) { + if (this._isMounted && !_.isEqual(supportedFeatures, this.state.supportedFeatures)) { this.setState({ supportedFeatures }); } - - if (this.state.selectedFeature === null) { - let selectedFeature = VECTOR_SHAPE_TYPE.POLYGON; - if (this.props.isPointsOnly) { - selectedFeature = VECTOR_SHAPE_TYPE.POINT; - } else if (this.props.isLinesOnly) { - selectedFeature = VECTOR_SHAPE_TYPE.LINE; - } - this.setState({ - selectedFeature: selectedFeature, - }); - } } - _handleSelectedFeatureChange = (selectedFeature) => { - this.setState({ selectedFeature }); + _handleSelectedFeatureChange = (selectedFeature: string) => { + this.setState({ selectedFeature: selectedFeature as VECTOR_SHAPE_TYPE }); }; - _onIsTimeAwareChange = (event) => { + _onIsTimeAwareChange = (event: EuiSwitchEvent) => { this.props.onIsTimeAwareChange(event.target.checked); }; - _onStaticStyleChange = (propertyName, options) => { + _onStaticStyleChange = (propertyName: VECTOR_STYLES, options: StaticStylePropertyOptions) => { const styleDescriptor = { type: STYLE_TYPE.STATIC, options, @@ -105,7 +150,7 @@ export class VectorStyleEditor extends Component { this.props.handlePropertyChange(propertyName, styleDescriptor); }; - _onDynamicStyleChange = (propertyName, options) => { + _onDynamicStyleChange = (propertyName: VECTOR_STYLES, options: DynamicStylePropertyOptions) => { const styleDescriptor = { type: STYLE_TYPE.DYNAMIC, options, @@ -115,18 +160,21 @@ export class VectorStyleEditor extends Component { _hasMarkerOrIcon() { const iconSize = this.props.styleProperties[VECTOR_STYLES.ICON_SIZE]; - return iconSize.isDynamic() || iconSize.getOptions().size > 0; + return iconSize.isDynamic() || (iconSize as StaticSizeProperty).getOptions().size > 0; } _hasLabel() { const label = this.props.styleProperties[VECTOR_STYLES.LABEL_TEXT]; return label.isDynamic() ? label.isComplete() - : label.getOptions().value != null && label.getOptions().value.length; + : (label as StaticTextProperty).getOptions().value != null && + (label as StaticTextProperty).getOptions().value.length; } _hasLabelBorder() { - const labelBorderSize = this.props.styleProperties[VECTOR_STYLES.LABEL_BORDER_SIZE]; + const labelBorderSize = this.props.styleProperties[ + VECTOR_STYLES.LABEL_BORDER_SIZE + ] as LabelBorderSizeProperty; return labelBorderSize.getOptions().size !== LABEL_BORDER_SIZES.NONE; } @@ -138,13 +186,18 @@ export class VectorStyleEditor extends Component { swatches={DEFAULT_FILL_COLORS} onStaticStyleChange={this._onStaticStyleChange} onDynamicStyleChange={this._onDynamicStyleChange} - styleProperty={this.props.styleProperties[VECTOR_STYLES.FILL_COLOR]} - fields={this.state.styleFieldsHelper.getFieldsForStyle(VECTOR_STYLES.FILL_COLOR)} + styleProperty={ + this.props.styleProperties[VECTOR_STYLES.FILL_COLOR] as IStyleProperty< + ColorDynamicOptions | ColorStaticOptions + > + } + fields={this.state.styleFieldsHelper!.getFieldsForStyle(VECTOR_STYLES.FILL_COLOR)} defaultStaticStyleOptions={ - this.state.defaultStaticProperties[VECTOR_STYLES.FILL_COLOR].options + this.state.defaultStaticProperties[VECTOR_STYLES.FILL_COLOR].options as ColorStaticOptions } defaultDynamicStyleOptions={ - this.state.defaultDynamicProperties[VECTOR_STYLES.FILL_COLOR].options + this.state.defaultDynamicProperties[VECTOR_STYLES.FILL_COLOR] + .options as ColorDynamicOptions } /> ); @@ -159,13 +212,18 @@ export class VectorStyleEditor extends Component { swatches={DEFAULT_LINE_COLORS} onStaticStyleChange={this._onStaticStyleChange} onDynamicStyleChange={this._onDynamicStyleChange} - styleProperty={this.props.styleProperties[VECTOR_STYLES.LINE_COLOR]} - fields={this.state.styleFieldsHelper.getFieldsForStyle(VECTOR_STYLES.LINE_COLOR)} + styleProperty={ + this.props.styleProperties[VECTOR_STYLES.LINE_COLOR] as IStyleProperty< + ColorDynamicOptions | ColorStaticOptions + > + } + fields={this.state.styleFieldsHelper!.getFieldsForStyle(VECTOR_STYLES.LINE_COLOR)} defaultStaticStyleOptions={ - this.state.defaultStaticProperties[VECTOR_STYLES.LINE_COLOR].options + this.state.defaultStaticProperties[VECTOR_STYLES.LINE_COLOR].options as ColorStaticOptions } defaultDynamicStyleOptions={ - this.state.defaultDynamicProperties[VECTOR_STYLES.LINE_COLOR].options + this.state.defaultDynamicProperties[VECTOR_STYLES.LINE_COLOR] + .options as ColorDynamicOptions } /> ); @@ -178,13 +236,18 @@ export class VectorStyleEditor extends Component { disabledBy={VECTOR_STYLES.ICON_SIZE} onStaticStyleChange={this._onStaticStyleChange} onDynamicStyleChange={this._onDynamicStyleChange} - styleProperty={this.props.styleProperties[VECTOR_STYLES.LINE_WIDTH]} - fields={this.state.styleFieldsHelper.getFieldsForStyle(VECTOR_STYLES.LINE_WIDTH)} + styleProperty={ + this.props.styleProperties[VECTOR_STYLES.LINE_WIDTH] as IStyleProperty< + SizeDynamicOptions | SizeStaticOptions + > + } + fields={this.state.styleFieldsHelper!.getFieldsForStyle(VECTOR_STYLES.LINE_WIDTH)} defaultStaticStyleOptions={ - this.state.defaultStaticProperties[VECTOR_STYLES.LINE_WIDTH].options + this.state.defaultStaticProperties[VECTOR_STYLES.LINE_WIDTH].options as SizeStaticOptions } defaultDynamicStyleOptions={ - this.state.defaultDynamicProperties[VECTOR_STYLES.LINE_WIDTH].options + this.state.defaultDynamicProperties[VECTOR_STYLES.LINE_WIDTH] + .options as SizeDynamicOptions } /> ); @@ -198,13 +261,19 @@ export class VectorStyleEditor extends Component { + } + fields={this.state.styleFieldsHelper!.getFieldsForStyle(VECTOR_STYLES.LABEL_TEXT)} defaultStaticStyleOptions={ - this.state.defaultStaticProperties[VECTOR_STYLES.LABEL_TEXT].options + this.state.defaultStaticProperties[VECTOR_STYLES.LABEL_TEXT] + .options as LabelStaticOptions } defaultDynamicStyleOptions={ - this.state.defaultDynamicProperties[VECTOR_STYLES.LABEL_TEXT].options + this.state.defaultDynamicProperties[VECTOR_STYLES.LABEL_TEXT] + .options as LabelDynamicOptions } /> @@ -215,13 +284,19 @@ export class VectorStyleEditor extends Component { swatches={DEFAULT_LINE_COLORS} onStaticStyleChange={this._onStaticStyleChange} onDynamicStyleChange={this._onDynamicStyleChange} - styleProperty={this.props.styleProperties[VECTOR_STYLES.LABEL_COLOR]} - fields={this.state.styleFieldsHelper.getFieldsForStyle(VECTOR_STYLES.LABEL_COLOR)} + styleProperty={ + this.props.styleProperties[VECTOR_STYLES.LABEL_COLOR] as IStyleProperty< + ColorDynamicOptions | ColorStaticOptions + > + } + fields={this.state.styleFieldsHelper!.getFieldsForStyle(VECTOR_STYLES.LABEL_COLOR)} defaultStaticStyleOptions={ - this.state.defaultStaticProperties[VECTOR_STYLES.LABEL_COLOR].options + this.state.defaultStaticProperties[VECTOR_STYLES.LABEL_COLOR] + .options as ColorStaticOptions } defaultDynamicStyleOptions={ - this.state.defaultDynamicProperties[VECTOR_STYLES.LABEL_COLOR].options + this.state.defaultDynamicProperties[VECTOR_STYLES.LABEL_COLOR] + .options as ColorDynamicOptions } /> @@ -231,13 +306,19 @@ export class VectorStyleEditor extends Component { disabledBy={VECTOR_STYLES.LABEL_TEXT} onStaticStyleChange={this._onStaticStyleChange} onDynamicStyleChange={this._onDynamicStyleChange} - styleProperty={this.props.styleProperties[VECTOR_STYLES.LABEL_SIZE]} - fields={this.state.styleFieldsHelper.getFieldsForStyle(VECTOR_STYLES.LABEL_SIZE)} + styleProperty={ + this.props.styleProperties[VECTOR_STYLES.LABEL_SIZE] as IStyleProperty< + SizeDynamicOptions | SizeStaticOptions + > + } + fields={this.state.styleFieldsHelper!.getFieldsForStyle(VECTOR_STYLES.LABEL_SIZE)} defaultStaticStyleOptions={ - this.state.defaultStaticProperties[VECTOR_STYLES.LABEL_SIZE].options + this.state.defaultStaticProperties[VECTOR_STYLES.LABEL_SIZE] + .options as SizeStaticOptions } defaultDynamicStyleOptions={ - this.state.defaultDynamicProperties[VECTOR_STYLES.LABEL_SIZE].options + this.state.defaultDynamicProperties[VECTOR_STYLES.LABEL_SIZE] + .options as SizeDynamicOptions } /> @@ -248,13 +329,19 @@ export class VectorStyleEditor extends Component { swatches={DEFAULT_LINE_COLORS} onStaticStyleChange={this._onStaticStyleChange} onDynamicStyleChange={this._onDynamicStyleChange} - styleProperty={this.props.styleProperties[VECTOR_STYLES.LABEL_BORDER_COLOR]} - fields={this.state.styleFieldsHelper.getFieldsForStyle(VECTOR_STYLES.LABEL_BORDER_COLOR)} + styleProperty={ + this.props.styleProperties[VECTOR_STYLES.LABEL_BORDER_COLOR] as IStyleProperty< + ColorDynamicOptions | ColorStaticOptions + > + } + fields={this.state.styleFieldsHelper!.getFieldsForStyle(VECTOR_STYLES.LABEL_BORDER_COLOR)} defaultStaticStyleOptions={ - this.state.defaultStaticProperties[VECTOR_STYLES.LABEL_BORDER_COLOR].options + this.state.defaultStaticProperties[VECTOR_STYLES.LABEL_BORDER_COLOR] + .options as ColorStaticOptions } defaultDynamicStyleOptions={ - this.state.defaultDynamicProperties[VECTOR_STYLES.LABEL_BORDER_COLOR].options + this.state.defaultDynamicProperties[VECTOR_STYLES.LABEL_BORDER_COLOR] + .options as ColorDynamicOptions } /> @@ -274,7 +361,11 @@ export class VectorStyleEditor extends Component { const hasMarkerOrIcon = this._hasMarkerOrIcon(); let iconOrientationEditor; let iconEditor; - if (this.props.styleProperties[VECTOR_STYLES.SYMBOLIZE_AS].isSymbolizedAsIcon()) { + if ( + (this.props.styleProperties[ + VECTOR_STYLES.SYMBOLIZE_AS + ] as SymbolizeAsProperty).isSymbolizedAsIcon() + ) { iconOrientationEditor = ( + } + fields={this.state.styleFieldsHelper!.getFieldsForStyle(VECTOR_STYLES.ICON)} defaultStaticStyleOptions={ - this.state.defaultStaticProperties[VECTOR_STYLES.ICON].options + this.state.defaultStaticProperties[VECTOR_STYLES.ICON].options as IconStaticOptions } defaultDynamicStyleOptions={ - this.state.defaultDynamicProperties[VECTOR_STYLES.ICON].options + this.state.defaultDynamicProperties[VECTOR_STYLES.ICON].options as IconDynamicOptions } /> @@ -341,13 +436,18 @@ export class VectorStyleEditor extends Component { + } + fields={this.state.styleFieldsHelper!.getFieldsForStyle(VECTOR_STYLES.ICON_SIZE)} defaultStaticStyleOptions={ - this.state.defaultStaticProperties[VECTOR_STYLES.ICON_SIZE].options + this.state.defaultStaticProperties[VECTOR_STYLES.ICON_SIZE].options as SizeStaticOptions } defaultDynamicStyleOptions={ - this.state.defaultDynamicProperties[VECTOR_STYLES.ICON_SIZE].options + this.state.defaultDynamicProperties[VECTOR_STYLES.ICON_SIZE] + .options as SizeDynamicOptions } /> @@ -385,7 +485,7 @@ export class VectorStyleEditor extends Component { _renderProperties() { const { supportedFeatures, selectedFeature, styleFieldsHelper } = this.state; - if (!supportedFeatures || !styleFieldsHelper) { + if (supportedFeatures.length === 0 || !styleFieldsHelper) { return null; } diff --git a/x-pack/plugins/maps/public/classes/styles/vector/style_fields_helper.ts b/x-pack/plugins/maps/public/classes/styles/vector/style_fields_helper.ts index 8613f9e1e946ff..fbe643a401484e 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/style_fields_helper.ts +++ b/x-pack/plugins/maps/public/classes/styles/vector/style_fields_helper.ts @@ -34,7 +34,7 @@ export async function createStyleFieldsHelper(fields: IField[]): Promise = {}, + isTimeAware = true + ) { return { type: LAYER_STYLE_TYPE.VECTOR, - properties: { ...getDefaultProperties(), ...properties }, + properties: { ...getDefaultStaticProperties(), ...properties }, isTimeAware, }; } static createDefaultStyleProperties(mapColors: string[]) { - return getDefaultProperties(mapColors); + return getDefaultStaticProperties(mapColors); } constructor( @@ -192,7 +188,7 @@ export class VectorStyle implements IVectorStyle { this._styleMeta = new StyleMeta(this._descriptor.__styleMeta); this._symbolizeAsStyleProperty = new SymbolizeAsProperty( - this._descriptor.properties[VECTOR_STYLES.SYMBOLIZE_AS]!.options, + this._descriptor.properties[VECTOR_STYLES.SYMBOLIZE_AS].options, VECTOR_STYLES.SYMBOLIZE_AS ); this._lineColorStyleProperty = this._makeColorProperty( @@ -237,7 +233,7 @@ export class VectorStyle implements IVectorStyle { VECTOR_STYLES.LABEL_BORDER_COLOR ); this._labelBorderSizeStyleProperty = new LabelBorderSizeProperty( - this._descriptor.properties[VECTOR_STYLES.LABEL_BORDER_SIZE]!.options, + this._descriptor.properties[VECTOR_STYLES.LABEL_BORDER_SIZE].options, VECTOR_STYLES.LABEL_BORDER_SIZE, this._labelSizeStyleProperty ); @@ -270,16 +266,10 @@ export class VectorStyle implements IVectorStyle { : (this._lineWidthStyleProperty as StaticSizeProperty).getOptions().size !== 0; } - renderEditor({ - layer, - onStyleDescriptorChange, - }: { - layer: ILayer; - onStyleDescriptorChange: (styleDescriptor: StyleDescriptor) => void; - }) { + renderEditor(onStyleDescriptorChange: (styleDescriptor: StyleDescriptor) => void) { const rawProperties = this.getRawProperties(); - const handlePropertyChange = (propertyName: VECTOR_STYLES, settings: any) => { - rawProperties[propertyName] = settings; // override single property, but preserve the rest + const handlePropertyChange = (propertyName: VECTOR_STYLES, stylePropertyDescriptor: any) => { + rawProperties[propertyName] = stylePropertyDescriptor; // override single property, but preserve the rest const vectorStyleDescriptor = VectorStyle.createDescriptor(rawProperties, this.isTimeAware()); onStyleDescriptorChange(vectorStyleDescriptor); }; @@ -293,9 +283,8 @@ export class VectorStyle implements IVectorStyle { return dynamicStyleProp.isFieldMetaEnabled(); }); - const styleProperties: VectorStylePropertiesDescriptor = {}; + const styleProperties: StyleProperties = {}; this.getAllStyleProperties().forEach((styleProperty) => { - // @ts-expect-error styleProperties[styleProperty.getStyleName()] = styleProperty; }); @@ -303,7 +292,7 @@ export class VectorStyle implements IVectorStyle { { test('Should use first color in DEFAULT_*_COLORS when no colors are used on the map', () => { const styleProperties = getDefaultStaticProperties([]); - expect(styleProperties[VECTOR_STYLES.FILL_COLOR]!.options.color).toBe('#54B399'); - expect(styleProperties[VECTOR_STYLES.LINE_COLOR]!.options.color).toBe('#41937c'); + expect(styleProperties[VECTOR_STYLES.FILL_COLOR].options.color).toBe('#54B399'); + expect(styleProperties[VECTOR_STYLES.LINE_COLOR].options.color).toBe('#41937c'); }); test('Should next color in DEFAULT_*_COLORS when colors are used on the map', () => { const styleProperties = getDefaultStaticProperties(['#54B399']); - expect(styleProperties[VECTOR_STYLES.FILL_COLOR]!.options.color).toBe('#6092C0'); - expect(styleProperties[VECTOR_STYLES.LINE_COLOR]!.options.color).toBe('#4379aa'); + expect(styleProperties[VECTOR_STYLES.FILL_COLOR].options.color).toBe('#6092C0'); + expect(styleProperties[VECTOR_STYLES.LINE_COLOR].options.color).toBe('#4379aa'); }); }); diff --git a/x-pack/plugins/maps/public/classes/styles/vector/vector_style_defaults.ts b/x-pack/plugins/maps/public/classes/styles/vector/vector_style_defaults.ts index 50321510c2ba82..fc152b9e5a9742 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/vector_style_defaults.ts +++ b/x-pack/plugins/maps/public/classes/styles/vector/vector_style_defaults.ts @@ -37,22 +37,6 @@ export const POLYGON_STYLES = [ VECTOR_STYLES.LINE_WIDTH, ]; -export function getDefaultProperties(mapColors: string[] = []): VectorStylePropertiesDescriptor { - return { - ...getDefaultStaticProperties(mapColors), - [VECTOR_STYLES.SYMBOLIZE_AS]: { - options: { - value: SYMBOLIZE_AS_TYPES.CIRCLE, - }, - }, - [VECTOR_STYLES.LABEL_BORDER_SIZE]: { - options: { - size: LABEL_BORDER_SIZES.SMALL, - }, - }, - }; -} - export function getDefaultStaticProperties( mapColors: string[] = [] ): VectorStylePropertiesDescriptor { @@ -129,6 +113,16 @@ export function getDefaultStaticProperties( color: isDarkMode ? '#000000' : '#FFFFFF', }, }, + [VECTOR_STYLES.SYMBOLIZE_AS]: { + options: { + value: SYMBOLIZE_AS_TYPES.CIRCLE, + }, + }, + [VECTOR_STYLES.LABEL_BORDER_SIZE]: { + options: { + size: LABEL_BORDER_SIZES.SMALL, + }, + }, }; } @@ -244,5 +238,15 @@ export function getDefaultDynamicProperties(): VectorStylePropertiesDescriptor { }, }, }, + [VECTOR_STYLES.SYMBOLIZE_AS]: { + options: { + value: SYMBOLIZE_AS_TYPES.CIRCLE, + }, + }, + [VECTOR_STYLES.LABEL_BORDER_SIZE]: { + options: { + size: LABEL_BORDER_SIZES.SMALL, + }, + }, }; } diff --git a/x-pack/plugins/maps/public/connected_components/layer_panel/style_settings/style_settings.js b/x-pack/plugins/maps/public/connected_components/layer_panel/style_settings/style_settings.js index 69cf51fb29c0db..e460d7728a3190 100644 --- a/x-pack/plugins/maps/public/connected_components/layer_panel/style_settings/style_settings.js +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/style_settings/style_settings.js @@ -11,9 +11,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiTitle, EuiPanel, EuiSpacer } from '@elast import { FormattedMessage } from '@kbn/i18n/react'; export function StyleSettings({ layer, updateStyleDescriptor }) { - const settingsEditor = layer.renderStyleEditor({ - onStyleDescriptorChange: updateStyleDescriptor, - }); + const settingsEditor = layer.renderStyleEditor(updateStyleDescriptor); if (!settingsEditor) { return null; From 9b30de41b6cf682b97e5a0518a18bb142fdb96bd Mon Sep 17 00:00:00 2001 From: Lukas Olson Date: Wed, 18 Nov 2020 22:04:26 -0700 Subject: [PATCH 09/16] [data.search] Server-side background session service (#81099) * [Search] Add request context and asScoped pattern * Update docs * Unify interface for getting search client * [WIP] [data.search] Server-side background session service * Update examples/search_examples/server/my_strategy.ts Co-authored-by: Anton Dosov * Review feedback * Fix checks * Add tapFirst and additional props for session * Fix CI * Fix security search * Fix test * Fix test for reals * Add restore method * Add code to search examples * Add restore and search using restored ID * Fix handling of preference and order of params * Trim & cleanup * Fix types * Review feedback * Add tests and remove handling of username * Update docs * Move utils to server * Review feedback * More review feedback * Regenerate docs * Review feedback * Doc changes Co-authored-by: Anton Dosov Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- ...ns-data-public.isearchoptions.isrestore.md | 13 + ...ins-data-public.isearchoptions.isstored.md | 13 + ...ugin-plugins-data-public.isearchoptions.md | 2 + ...gins-data-public.isessionservice.delete.md | 13 + ...lugins-data-public.isessionservice.find.md | 13 + ...plugins-data-public.isessionservice.get.md | 13 + ...s-data-public.isessionservice.isrestore.md | 13 + ...ns-data-public.isessionservice.isstored.md | 13 + ...gin-plugins-data-public.isessionservice.md | 9 +- ...ins-data-public.isessionservice.restore.md | 2 +- ...lugins-data-public.isessionservice.save.md | 13 + ...gins-data-public.isessionservice.update.md | 13 + ...ns-data-server.isearchoptions.isrestore.md | 13 + ...ins-data-server.isearchoptions.isstored.md | 13 + ...ugin-plugins-data-server.isearchoptions.md | 2 + examples/search_examples/kibana.json | 2 +- .../data/common/search/session/index.ts | 1 + .../data/common/search/session/mocks.ts | 7 + .../data/common/search/session/status.ts | 26 ++ .../data/common/search/session/types.ts | 62 ++++- src/plugins/data/common/search/types.ts | 11 + src/plugins/data/common/utils/index.ts | 1 + .../data/common/utils/tap_first.test.ts | 30 +++ src/plugins/data/common/utils/tap_first.ts | 31 +++ src/plugins/data/public/public.api.md | 25 +- .../data/public/search/search_interceptor.ts | 19 +- .../data/public/search/session_service.ts | 69 +++++- .../saved_objects/background_session.ts | 56 +++++ .../data/server/saved_objects/index.ts | 1 + src/plugins/data/server/search/mocks.ts | 21 ++ .../data/server/search/routes/search.ts | 14 +- .../data/server/search/routes/session.test.ts | 119 +++++++++ .../data/server/search/routes/session.ts | 201 +++++++++++++++ .../data/server/search/search_service.ts | 44 +++- .../data/server/search/session/index.ts | 20 ++ .../search/session/session_service.test.ts | 233 ++++++++++++++++++ .../server/search/session/session_service.ts | 204 +++++++++++++++ .../data/server/search/session/utils.test.ts | 37 +++ .../data/server/search/session/utils.ts | 30 +++ src/plugins/data/server/server.api.md | 2 + src/plugins/embeddable/public/public.api.md | 4 +- .../public/search/search_interceptor.ts | 8 +- .../server/search/es_search_strategy.ts | 1 + 43 files changed, 1407 insertions(+), 30 deletions(-) create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.isrestore.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.isstored.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.delete.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.find.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.get.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.isrestore.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.isstored.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.save.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.update.md create mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.isrestore.md create mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.isstored.md create mode 100644 src/plugins/data/common/search/session/status.ts create mode 100644 src/plugins/data/common/utils/tap_first.test.ts create mode 100644 src/plugins/data/common/utils/tap_first.ts create mode 100644 src/plugins/data/server/saved_objects/background_session.ts create mode 100644 src/plugins/data/server/search/routes/session.test.ts create mode 100644 src/plugins/data/server/search/routes/session.ts create mode 100644 src/plugins/data/server/search/session/index.ts create mode 100644 src/plugins/data/server/search/session/session_service.test.ts create mode 100644 src/plugins/data/server/search/session/session_service.ts create mode 100644 src/plugins/data/server/search/session/utils.test.ts create mode 100644 src/plugins/data/server/search/session/utils.ts diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.isrestore.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.isrestore.md new file mode 100644 index 00000000000000..672d77719962fb --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.isrestore.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchOptions](./kibana-plugin-plugins-data-public.isearchoptions.md) > [isRestore](./kibana-plugin-plugins-data-public.isearchoptions.isrestore.md) + +## ISearchOptions.isRestore property + +Whether the session is restored (i.e. search requests should re-use the stored search IDs, rather than starting from scratch) + +Signature: + +```typescript +isRestore?: boolean; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.isstored.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.isstored.md new file mode 100644 index 00000000000000..0d2c173f351c82 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.isstored.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchOptions](./kibana-plugin-plugins-data-public.isearchoptions.md) > [isStored](./kibana-plugin-plugins-data-public.isearchoptions.isstored.md) + +## ISearchOptions.isStored property + +Whether the session is already saved (i.e. sent to background) + +Signature: + +```typescript +isStored?: boolean; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.md index 76d09141734472..5acd837495dac4 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.md @@ -15,6 +15,8 @@ export interface ISearchOptions | Property | Type | Description | | --- | --- | --- | | [abortSignal](./kibana-plugin-plugins-data-public.isearchoptions.abortsignal.md) | AbortSignal | An AbortSignal that allows the caller of search to abort a search request. | +| [isRestore](./kibana-plugin-plugins-data-public.isearchoptions.isrestore.md) | boolean | Whether the session is restored (i.e. search requests should re-use the stored search IDs, rather than starting from scratch) | +| [isStored](./kibana-plugin-plugins-data-public.isearchoptions.isstored.md) | boolean | Whether the session is already saved (i.e. sent to background) | | [sessionId](./kibana-plugin-plugins-data-public.isearchoptions.sessionid.md) | string | A session ID, grouping multiple search requests into a single session. | | [strategy](./kibana-plugin-plugins-data-public.isearchoptions.strategy.md) | string | Use this option to force using a specific server side search strategy. Leave empty to use the default strategy. | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.delete.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.delete.md new file mode 100644 index 00000000000000..eabb966160c4d8 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.delete.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISessionService](./kibana-plugin-plugins-data-public.isessionservice.md) > [delete](./kibana-plugin-plugins-data-public.isessionservice.delete.md) + +## ISessionService.delete property + +Deletes a session + +Signature: + +```typescript +delete: (sessionId: string) => Promise; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.find.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.find.md new file mode 100644 index 00000000000000..58e2fea0e6fe93 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.find.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISessionService](./kibana-plugin-plugins-data-public.isessionservice.md) > [find](./kibana-plugin-plugins-data-public.isessionservice.find.md) + +## ISessionService.find property + +Gets a list of saved sessions + +Signature: + +```typescript +find: (options: SearchSessionFindOptions) => Promise>; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.get.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.get.md new file mode 100644 index 00000000000000..a2dff2f18253bc --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.get.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISessionService](./kibana-plugin-plugins-data-public.isessionservice.md) > [get](./kibana-plugin-plugins-data-public.isessionservice.get.md) + +## ISessionService.get property + +Gets a saved session + +Signature: + +```typescript +get: (sessionId: string) => Promise>; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.isrestore.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.isrestore.md new file mode 100644 index 00000000000000..8d8cd1f31bb957 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.isrestore.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISessionService](./kibana-plugin-plugins-data-public.isessionservice.md) > [isRestore](./kibana-plugin-plugins-data-public.isessionservice.isrestore.md) + +## ISessionService.isRestore property + +Whether the active session is restored (i.e. reusing previous search IDs) + +Signature: + +```typescript +isRestore: () => boolean; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.isstored.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.isstored.md new file mode 100644 index 00000000000000..db737880bb84e9 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.isstored.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISessionService](./kibana-plugin-plugins-data-public.isessionservice.md) > [isStored](./kibana-plugin-plugins-data-public.isessionservice.isstored.md) + +## ISessionService.isStored property + +Whether the active session is already saved (i.e. sent to background) + +Signature: + +```typescript +isStored: () => boolean; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.md index 174f9dbe66bf4c..02c0a821e552dc 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.md @@ -15,8 +15,15 @@ export interface ISessionService | Property | Type | Description | | --- | --- | --- | | [clear](./kibana-plugin-plugins-data-public.isessionservice.clear.md) | () => void | Clears the active session. | +| [delete](./kibana-plugin-plugins-data-public.isessionservice.delete.md) | (sessionId: string) => Promise<void> | Deletes a session | +| [find](./kibana-plugin-plugins-data-public.isessionservice.find.md) | (options: SearchSessionFindOptions) => Promise<SavedObjectsFindResponse<BackgroundSessionSavedObjectAttributes>> | Gets a list of saved sessions | +| [get](./kibana-plugin-plugins-data-public.isessionservice.get.md) | (sessionId: string) => Promise<SavedObject<BackgroundSessionSavedObjectAttributes>> | Gets a saved session | | [getSession$](./kibana-plugin-plugins-data-public.isessionservice.getsession_.md) | () => Observable<string | undefined> | Returns the observable that emits an update every time the session ID changes | | [getSessionId](./kibana-plugin-plugins-data-public.isessionservice.getsessionid.md) | () => string | undefined | Returns the active session ID | -| [restore](./kibana-plugin-plugins-data-public.isessionservice.restore.md) | (sessionId: string) => void | Restores existing session | +| [isRestore](./kibana-plugin-plugins-data-public.isessionservice.isrestore.md) | () => boolean | Whether the active session is restored (i.e. reusing previous search IDs) | +| [isStored](./kibana-plugin-plugins-data-public.isessionservice.isstored.md) | () => boolean | Whether the active session is already saved (i.e. sent to background) | +| [restore](./kibana-plugin-plugins-data-public.isessionservice.restore.md) | (sessionId: string) => Promise<SavedObject<BackgroundSessionSavedObjectAttributes>> | Restores existing session | +| [save](./kibana-plugin-plugins-data-public.isessionservice.save.md) | (name: string, url: string) => Promise<SavedObject<BackgroundSessionSavedObjectAttributes>> | Saves a session | | [start](./kibana-plugin-plugins-data-public.isessionservice.start.md) | () => string | Starts a new session | +| [update](./kibana-plugin-plugins-data-public.isessionservice.update.md) | (sessionId: string, attributes: Partial<BackgroundSessionSavedObjectAttributes>) => Promise<any> | Updates a session | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.restore.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.restore.md index 857e85bbd30eb5..96106a6ef7e2d1 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.restore.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.restore.md @@ -9,5 +9,5 @@ Restores existing session Signature: ```typescript -restore: (sessionId: string) => void; +restore: (sessionId: string) => Promise>; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.save.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.save.md new file mode 100644 index 00000000000000..4ac4a966144676 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.save.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISessionService](./kibana-plugin-plugins-data-public.isessionservice.md) > [save](./kibana-plugin-plugins-data-public.isessionservice.save.md) + +## ISessionService.save property + +Saves a session + +Signature: + +```typescript +save: (name: string, url: string) => Promise>; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.update.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.update.md new file mode 100644 index 00000000000000..5e2ff53d58ab77 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isessionservice.update.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISessionService](./kibana-plugin-plugins-data-public.isessionservice.md) > [update](./kibana-plugin-plugins-data-public.isessionservice.update.md) + +## ISessionService.update property + +Updates a session + +Signature: + +```typescript +update: (sessionId: string, attributes: Partial) => Promise; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.isrestore.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.isrestore.md new file mode 100644 index 00000000000000..ae518e5a052fc9 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.isrestore.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ISearchOptions](./kibana-plugin-plugins-data-server.isearchoptions.md) > [isRestore](./kibana-plugin-plugins-data-server.isearchoptions.isrestore.md) + +## ISearchOptions.isRestore property + +Whether the session is restored (i.e. search requests should re-use the stored search IDs, rather than starting from scratch) + +Signature: + +```typescript +isRestore?: boolean; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.isstored.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.isstored.md new file mode 100644 index 00000000000000..aceee7fd6df68d --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.isstored.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ISearchOptions](./kibana-plugin-plugins-data-server.isearchoptions.md) > [isStored](./kibana-plugin-plugins-data-server.isearchoptions.isstored.md) + +## ISearchOptions.isStored property + +Whether the session is already saved (i.e. sent to background) + +Signature: + +```typescript +isStored?: boolean; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.md index af96e1413ba0cb..85847e1c61d25b 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.md @@ -15,6 +15,8 @@ export interface ISearchOptions | Property | Type | Description | | --- | --- | --- | | [abortSignal](./kibana-plugin-plugins-data-server.isearchoptions.abortsignal.md) | AbortSignal | An AbortSignal that allows the caller of search to abort a search request. | +| [isRestore](./kibana-plugin-plugins-data-server.isearchoptions.isrestore.md) | boolean | Whether the session is restored (i.e. search requests should re-use the stored search IDs, rather than starting from scratch) | +| [isStored](./kibana-plugin-plugins-data-server.isearchoptions.isstored.md) | boolean | Whether the session is already saved (i.e. sent to background) | | [sessionId](./kibana-plugin-plugins-data-server.isearchoptions.sessionid.md) | string | A session ID, grouping multiple search requests into a single session. | | [strategy](./kibana-plugin-plugins-data-server.isearchoptions.strategy.md) | string | Use this option to force using a specific server side search strategy. Leave empty to use the default strategy. | diff --git a/examples/search_examples/kibana.json b/examples/search_examples/kibana.json index 9577ec353a4c93..07bb6a0b750e30 100644 --- a/examples/search_examples/kibana.json +++ b/examples/search_examples/kibana.json @@ -4,7 +4,7 @@ "kibanaVersion": "kibana", "server": true, "ui": true, - "requiredPlugins": ["navigation", "data", "developerExamples"], + "requiredPlugins": ["navigation", "data", "developerExamples", "kibanaUtils"], "optionalPlugins": [], "requiredBundles": [] } diff --git a/src/plugins/data/common/search/session/index.ts b/src/plugins/data/common/search/session/index.ts index d8f7b5091eb8f6..0feb43f8f1d4b9 100644 --- a/src/plugins/data/common/search/session/index.ts +++ b/src/plugins/data/common/search/session/index.ts @@ -17,4 +17,5 @@ * under the License. */ +export * from './status'; export * from './types'; diff --git a/src/plugins/data/common/search/session/mocks.ts b/src/plugins/data/common/search/session/mocks.ts index 370faaa640c56d..4604e15e4e93b8 100644 --- a/src/plugins/data/common/search/session/mocks.ts +++ b/src/plugins/data/common/search/session/mocks.ts @@ -27,5 +27,12 @@ export function getSessionServiceMock(): jest.Mocked { restore: jest.fn(), getSessionId: jest.fn(), getSession$: jest.fn(() => new BehaviorSubject(undefined).asObservable()), + isStored: jest.fn(), + isRestore: jest.fn(), + save: jest.fn(), + get: jest.fn(), + find: jest.fn(), + update: jest.fn(), + delete: jest.fn(), }; } diff --git a/src/plugins/data/common/search/session/status.ts b/src/plugins/data/common/search/session/status.ts new file mode 100644 index 00000000000000..1f6b6eb3084bb5 --- /dev/null +++ b/src/plugins/data/common/search/session/status.ts @@ -0,0 +1,26 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export enum BackgroundSessionStatus { + IN_PROGRESS = 'in_progress', + ERROR = 'error', + COMPLETE = 'complete', + CANCELLED = 'cancelled', + EXPIRED = 'expired', +} diff --git a/src/plugins/data/common/search/session/types.ts b/src/plugins/data/common/search/session/types.ts index 6660b8395547ff..d1ab22057695a7 100644 --- a/src/plugins/data/common/search/session/types.ts +++ b/src/plugins/data/common/search/session/types.ts @@ -18,6 +18,7 @@ */ import { Observable } from 'rxjs'; +import type { SavedObject, SavedObjectsFindResponse } from 'kibana/server'; export interface ISessionService { /** @@ -30,6 +31,17 @@ export interface ISessionService { * @returns `Observable` */ getSession$: () => Observable; + + /** + * Whether the active session is already saved (i.e. sent to background) + */ + isStored: () => boolean; + + /** + * Whether the active session is restored (i.e. reusing previous search IDs) + */ + isRestore: () => boolean; + /** * Starts a new session */ @@ -38,10 +50,58 @@ export interface ISessionService { /** * Restores existing session */ - restore: (sessionId: string) => void; + restore: (sessionId: string) => Promise>; /** * Clears the active session. */ clear: () => void; + + /** + * Saves a session + */ + save: (name: string, url: string) => Promise>; + + /** + * Gets a saved session + */ + get: (sessionId: string) => Promise>; + + /** + * Gets a list of saved sessions + */ + find: ( + options: SearchSessionFindOptions + ) => Promise>; + + /** + * Updates a session + */ + update: ( + sessionId: string, + attributes: Partial + ) => Promise; + + /** + * Deletes a session + */ + delete: (sessionId: string) => Promise; +} + +export interface BackgroundSessionSavedObjectAttributes { + name: string; + created: string; + expires: string; + status: string; + initialState: Record; + restoreState: Record; + idMapping: Record; +} + +export interface SearchSessionFindOptions { + page?: number; + perPage?: number; + sortField?: string; + sortOrder?: string; + filter?: string; } diff --git a/src/plugins/data/common/search/types.ts b/src/plugins/data/common/search/types.ts index 7451edf5e2fa35..695ee34d3b468e 100644 --- a/src/plugins/data/common/search/types.ts +++ b/src/plugins/data/common/search/types.ts @@ -92,4 +92,15 @@ export interface ISearchOptions { * A session ID, grouping multiple search requests into a single session. */ sessionId?: string; + + /** + * Whether the session is already saved (i.e. sent to background) + */ + isStored?: boolean; + + /** + * Whether the session is restored (i.e. search requests should re-use the stored search IDs, + * rather than starting from scratch) + */ + isRestore?: boolean; } diff --git a/src/plugins/data/common/utils/index.ts b/src/plugins/data/common/utils/index.ts index 8b8686c51b9c19..4b602cb963a8fd 100644 --- a/src/plugins/data/common/utils/index.ts +++ b/src/plugins/data/common/utils/index.ts @@ -19,3 +19,4 @@ /** @internal */ export { shortenDottedString } from './shorten_dotted_string'; +export { tapFirst } from './tap_first'; diff --git a/src/plugins/data/common/utils/tap_first.test.ts b/src/plugins/data/common/utils/tap_first.test.ts new file mode 100644 index 00000000000000..033ae59f8c715e --- /dev/null +++ b/src/plugins/data/common/utils/tap_first.test.ts @@ -0,0 +1,30 @@ +/* + * 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 { of } from 'rxjs'; +import { tapFirst } from './tap_first'; + +describe('tapFirst', () => { + it('should tap the first and only the first', () => { + const fn = jest.fn(); + of(1, 2, 3).pipe(tapFirst(fn)).subscribe(); + expect(fn).toBeCalledTimes(1); + expect(fn).lastCalledWith(1); + }); +}); diff --git a/src/plugins/data/common/utils/tap_first.ts b/src/plugins/data/common/utils/tap_first.ts new file mode 100644 index 00000000000000..2c783a3ef87f06 --- /dev/null +++ b/src/plugins/data/common/utils/tap_first.ts @@ -0,0 +1,31 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { pipe } from 'rxjs'; +import { tap } from 'rxjs/operators'; + +export function tapFirst(next: (x: T) => void) { + let isFirst = true; + return pipe( + tap((x: T) => { + if (isFirst) next(x); + isFirst = false; + }) + ); +} diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 165e11517311c9..6c4609e5506c23 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -70,10 +70,12 @@ import { RequestAdapter } from 'src/plugins/inspector/common'; import { RequestStatistics as RequestStatistics_2 } from 'src/plugins/inspector/common'; import { Required } from '@kbn/utility-types'; import * as Rx from 'rxjs'; -import { SavedObject } from 'src/core/server'; -import { SavedObject as SavedObject_2 } from 'src/core/public'; +import { SavedObject } from 'kibana/server'; +import { SavedObject as SavedObject_2 } from 'src/core/server'; +import { SavedObject as SavedObject_3 } from 'src/core/public'; import { SavedObjectReference } from 'src/core/types'; import { SavedObjectsClientContract } from 'src/core/public'; +import { SavedObjectsFindResponse } from 'kibana/server'; import { Search } from '@elastic/elasticsearch/api/requestParams'; import { SearchResponse } from 'elasticsearch'; import { SerializedFieldFormat as SerializedFieldFormat_2 } from 'src/plugins/expressions/common'; @@ -1389,7 +1391,7 @@ export class IndexPatternsService { // Warning: (ae-forgotten-export) The symbol "IndexPatternSavedObjectAttrs" needs to be exported by the entry point index.d.ts // // (undocumented) - getCache: () => Promise[] | null | undefined>; + getCache: () => Promise[] | null | undefined>; getDefault: () => Promise; getFieldsForIndexPattern: (indexPattern: IndexPattern | IndexPatternSpec, options?: GetFieldsOptions | undefined) => Promise; // Warning: (ae-forgotten-export) The symbol "GetFieldsOptions" needs to be exported by the entry point index.d.ts @@ -1401,7 +1403,7 @@ export class IndexPatternsService { }>>; getTitles: (refresh?: boolean) => Promise; refreshFields: (indexPattern: IndexPattern) => Promise; - savedObjectToSpec: (savedObject: SavedObject) => IndexPatternSpec; + savedObjectToSpec: (savedObject: SavedObject_2) => IndexPatternSpec; setDefault: (id: string, force?: boolean) => Promise; updateSavedObject(indexPattern: IndexPattern, saveAttempts?: number, ignoreErrors?: boolean): Promise; } @@ -1446,6 +1448,8 @@ export type ISearchGeneric = | undefined // @public (undocumented) export interface ISessionService { clear: () => void; + delete: (sessionId: string) => Promise; + // Warning: (ae-forgotten-export) The symbol "SearchSessionFindOptions" needs to be exported by the entry point index.d.ts + find: (options: SearchSessionFindOptions) => Promise>; + get: (sessionId: string) => Promise>; getSession$: () => Observable; getSessionId: () => string | undefined; - restore: (sessionId: string) => void; + isRestore: () => boolean; + isStored: () => boolean; + // Warning: (ae-forgotten-export) The symbol "BackgroundSessionSavedObjectAttributes" needs to be exported by the entry point index.d.ts + restore: (sessionId: string) => Promise>; + save: (name: string, url: string) => Promise>; start: () => string; + update: (sessionId: string, attributes: Partial) => Promise; } // Warning: (ae-missing-release-tag) "isFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -2069,7 +2082,7 @@ export class SearchInterceptor { // @internal protected pendingCount$: BehaviorSubject; // @internal (undocumented) - protected runSearch(request: IKibanaSearchRequest, signal: AbortSignal, strategy?: string): Promise; + protected runSearch(request: IKibanaSearchRequest, options?: ISearchOptions): Promise; search(request: IKibanaSearchRequest, options?: ISearchOptions): Observable; // @internal (undocumented) protected setupAbortSignal({ abortSignal, timeout, }: { diff --git a/src/plugins/data/public/search/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor.ts index 78e65802bcf99b..3fadb723b27cd9 100644 --- a/src/plugins/data/public/search/search_interceptor.ts +++ b/src/plugins/data/public/search/search_interceptor.ts @@ -126,18 +126,25 @@ export class SearchInterceptor { */ protected runSearch( request: IKibanaSearchRequest, - signal: AbortSignal, - strategy?: string + options?: ISearchOptions ): Promise { const { id, ...searchRequest } = request; - const path = trimEnd(`/internal/search/${strategy || ES_SEARCH_STRATEGY}/${id || ''}`, '/'); - const body = JSON.stringify(searchRequest); + const path = trimEnd( + `/internal/search/${options?.strategy ?? ES_SEARCH_STRATEGY}/${id ?? ''}`, + '/' + ); + const body = JSON.stringify({ + sessionId: options?.sessionId, + isStored: options?.isStored, + isRestore: options?.isRestore, + ...searchRequest, + }); return this.deps.http.fetch({ method: 'POST', path, body, - signal, + signal: options?.abortSignal, }); } @@ -235,7 +242,7 @@ export class SearchInterceptor { abortSignal: options?.abortSignal, }); this.pendingCount$.next(this.pendingCount$.getValue() + 1); - return from(this.runSearch(request, combinedSignal, options?.strategy)).pipe( + return from(this.runSearch(request, { ...options, abortSignal: combinedSignal })).pipe( catchError((e: Error) => { return throwError(this.handleSearchError(e, request, timeoutSignal, options)); }), diff --git a/src/plugins/data/public/search/session_service.ts b/src/plugins/data/public/search/session_service.ts index a172738812937b..0141cff258a9f2 100644 --- a/src/plugins/data/public/search/session_service.ts +++ b/src/plugins/data/public/search/session_service.ts @@ -19,9 +19,13 @@ import uuid from 'uuid'; import { BehaviorSubject, Subscription } from 'rxjs'; -import { PluginInitializerContext, StartServicesAccessor } from 'kibana/public'; +import { HttpStart, PluginInitializerContext, StartServicesAccessor } from 'kibana/public'; import { ConfigSchema } from '../../config'; -import { ISessionService } from '../../common/search'; +import { + ISessionService, + BackgroundSessionSavedObjectAttributes, + SearchSessionFindOptions, +} from '../../common'; export class SessionService implements ISessionService { private session$ = new BehaviorSubject(undefined); @@ -30,6 +34,18 @@ export class SessionService implements ISessionService { } private appChangeSubscription$?: Subscription; private curApp?: string; + private http!: HttpStart; + + /** + * Has the session already been stored (i.e. "sent to background")? + */ + private _isStored: boolean = false; + + /** + * Is this session a restored session (have these requests already been made, and we're just + * looking to re-use the previous search IDs)? + */ + private _isRestore: boolean = false; constructor( initializerContext: PluginInitializerContext, @@ -39,6 +55,8 @@ export class SessionService implements ISessionService { Make sure that apps don't leave sessions open. */ getStartServices().then(([coreStart]) => { + this.http = coreStart.http; + this.appChangeSubscription$ = coreStart.application.currentAppId$.subscribe((appName) => { if (this.sessionId) { const message = `Application '${this.curApp}' had an open session while navigating`; @@ -69,16 +87,63 @@ export class SessionService implements ISessionService { return this.session$.asObservable(); } + public isStored() { + return this._isStored; + } + + public isRestore() { + return this._isRestore; + } + public start() { + this._isStored = false; + this._isRestore = false; this.session$.next(uuid.v4()); return this.sessionId!; } public restore(sessionId: string) { + this._isStored = true; + this._isRestore = true; this.session$.next(sessionId); + return this.http.get(`/internal/session/${encodeURIComponent(sessionId)}`); } public clear() { + this._isStored = false; + this._isRestore = false; this.session$.next(undefined); } + + public async save(name: string, url: string) { + const response = await this.http.post(`/internal/session`, { + body: JSON.stringify({ + name, + url, + sessionId: this.sessionId, + }), + }); + this._isStored = true; + return response; + } + + public get(sessionId: string) { + return this.http.get(`/internal/session/${encodeURIComponent(sessionId)}`); + } + + public find(options: SearchSessionFindOptions) { + return this.http.post(`/internal/session`, { + body: JSON.stringify(options), + }); + } + + public update(sessionId: string, attributes: Partial) { + return this.http.put(`/internal/session/${encodeURIComponent(sessionId)}`, { + body: JSON.stringify(attributes), + }); + } + + public delete(sessionId: string) { + return this.http.delete(`/internal/session/${encodeURIComponent(sessionId)}`); + } } diff --git a/src/plugins/data/server/saved_objects/background_session.ts b/src/plugins/data/server/saved_objects/background_session.ts new file mode 100644 index 00000000000000..74b03c4d867e40 --- /dev/null +++ b/src/plugins/data/server/saved_objects/background_session.ts @@ -0,0 +1,56 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SavedObjectsType } from 'kibana/server'; + +export const BACKGROUND_SESSION_TYPE = 'background-session'; + +export const backgroundSessionMapping: SavedObjectsType = { + name: BACKGROUND_SESSION_TYPE, + namespaceType: 'single', + hidden: true, + mappings: { + properties: { + name: { + type: 'keyword', + }, + created: { + type: 'date', + }, + expires: { + type: 'date', + }, + status: { + type: 'keyword', + }, + initialState: { + type: 'object', + enabled: false, + }, + restoreState: { + type: 'object', + enabled: false, + }, + idMapping: { + type: 'object', + enabled: false, + }, + }, + }, +}; diff --git a/src/plugins/data/server/saved_objects/index.ts b/src/plugins/data/server/saved_objects/index.ts index 077f9380823d08..7cd4d319e64171 100644 --- a/src/plugins/data/server/saved_objects/index.ts +++ b/src/plugins/data/server/saved_objects/index.ts @@ -20,3 +20,4 @@ export { querySavedObjectType } from './query'; export { indexPatternSavedObjectType } from './index_patterns'; export { kqlTelemetry } from './kql_telemetry'; export { searchTelemetry } from './search_telemetry'; +export { BACKGROUND_SESSION_TYPE, backgroundSessionMapping } from './background_session'; diff --git a/src/plugins/data/server/search/mocks.ts b/src/plugins/data/server/search/mocks.ts index 4914726c85ef8b..290e94ee7cf991 100644 --- a/src/plugins/data/server/search/mocks.ts +++ b/src/plugins/data/server/search/mocks.ts @@ -17,6 +17,8 @@ * under the License. */ +import type { RequestHandlerContext } from 'src/core/server'; +import { coreMock } from '../../../../core/server/mocks'; import { ISearchSetup, ISearchStart } from './types'; import { searchAggsSetupMock, searchAggsStartMock } from './aggs/mocks'; import { searchSourceMock } from './search_source/mocks'; @@ -40,3 +42,22 @@ export function createSearchStartMock(): jest.Mocked { searchSource: searchSourceMock.createStartContract(), }; } + +export function createSearchRequestHandlerContext(): jest.Mocked { + return { + core: coreMock.createRequestHandlerContext(), + search: { + search: jest.fn(), + cancel: jest.fn(), + session: { + save: jest.fn(), + get: jest.fn(), + find: jest.fn(), + update: jest.fn(), + delete: jest.fn(), + trackId: jest.fn(), + getId: jest.fn(), + }, + }, + }; +} diff --git a/src/plugins/data/server/search/routes/search.ts b/src/plugins/data/server/search/routes/search.ts index a4161fe47b388f..ed519164c8e43a 100644 --- a/src/plugins/data/server/search/routes/search.ts +++ b/src/plugins/data/server/search/routes/search.ts @@ -35,11 +35,18 @@ export function registerSearchRoute(router: IRouter): void { query: schema.object({}, { unknowns: 'allow' }), - body: schema.object({}, { unknowns: 'allow' }), + body: schema.object( + { + sessionId: schema.maybe(schema.string()), + isStored: schema.maybe(schema.boolean()), + isRestore: schema.maybe(schema.boolean()), + }, + { unknowns: 'allow' } + ), }, }, async (context, request, res) => { - const searchRequest = request.body; + const { sessionId, isStored, isRestore, ...searchRequest } = request.body; const { strategy, id } = request.params; const abortSignal = getRequestAbortedSignal(request.events.aborted$); @@ -50,6 +57,9 @@ export function registerSearchRoute(router: IRouter): void { { abortSignal, strategy, + sessionId, + isStored, + isRestore, } ) .pipe(first()) diff --git a/src/plugins/data/server/search/routes/session.test.ts b/src/plugins/data/server/search/routes/session.test.ts new file mode 100644 index 00000000000000..f697f6d5a5c2bd --- /dev/null +++ b/src/plugins/data/server/search/routes/session.test.ts @@ -0,0 +1,119 @@ +/* + * 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 type { MockedKeys } from '@kbn/utility-types/jest'; +import type { CoreSetup, RequestHandlerContext } from 'kibana/server'; +import type { DataPluginStart } from '../../plugin'; +import { coreMock, httpServerMock } from '../../../../../../src/core/server/mocks'; +import { createSearchRequestHandlerContext } from '../mocks'; +import { registerSessionRoutes } from './session'; + +describe('registerSessionRoutes', () => { + let mockCoreSetup: MockedKeys>; + let mockContext: jest.Mocked; + + beforeEach(() => { + mockCoreSetup = coreMock.createSetup(); + mockContext = createSearchRequestHandlerContext(); + registerSessionRoutes(mockCoreSetup.http.createRouter()); + }); + + it('save calls session.save with sessionId and attributes', async () => { + const sessionId = 'd7170a35-7e2c-48d6-8dec-9a056721b489'; + const name = 'my saved background search session'; + const body = { sessionId, name }; + + const mockRequest = httpServerMock.createKibanaRequest({ body }); + const mockResponse = httpServerMock.createResponseFactory(); + + const mockRouter = mockCoreSetup.http.createRouter.mock.results[0].value; + const [[, saveHandler]] = mockRouter.post.mock.calls; + + saveHandler(mockContext, mockRequest, mockResponse); + + expect(mockContext.search!.session.save).toHaveBeenCalledWith(sessionId, { name }); + }); + + it('get calls session.get with sessionId', async () => { + const id = 'd7170a35-7e2c-48d6-8dec-9a056721b489'; + const params = { id }; + + const mockRequest = httpServerMock.createKibanaRequest({ params }); + const mockResponse = httpServerMock.createResponseFactory(); + + const mockRouter = mockCoreSetup.http.createRouter.mock.results[0].value; + const [[, getHandler]] = mockRouter.get.mock.calls; + + getHandler(mockContext, mockRequest, mockResponse); + + expect(mockContext.search!.session.get).toHaveBeenCalledWith(id); + }); + + it('find calls session.find with options', async () => { + const page = 1; + const perPage = 5; + const sortField = 'my_field'; + const sortOrder = 'desc'; + const filter = 'foo: bar'; + const body = { page, perPage, sortField, sortOrder, filter }; + + const mockRequest = httpServerMock.createKibanaRequest({ body }); + const mockResponse = httpServerMock.createResponseFactory(); + + const mockRouter = mockCoreSetup.http.createRouter.mock.results[0].value; + const [, [, findHandler]] = mockRouter.post.mock.calls; + + findHandler(mockContext, mockRequest, mockResponse); + + expect(mockContext.search!.session.find).toHaveBeenCalledWith(body); + }); + + it('update calls session.update with id and attributes', async () => { + const id = 'd7170a35-7e2c-48d6-8dec-9a056721b489'; + const name = 'my saved background search session'; + const expires = new Date().toISOString(); + const params = { id }; + const body = { name, expires }; + + const mockRequest = httpServerMock.createKibanaRequest({ params, body }); + const mockResponse = httpServerMock.createResponseFactory(); + + const mockRouter = mockCoreSetup.http.createRouter.mock.results[0].value; + const [[, updateHandler]] = mockRouter.put.mock.calls; + + updateHandler(mockContext, mockRequest, mockResponse); + + expect(mockContext.search!.session.update).toHaveBeenCalledWith(id, body); + }); + + it('delete calls session.delete with id', async () => { + const id = 'd7170a35-7e2c-48d6-8dec-9a056721b489'; + const params = { id }; + + const mockRequest = httpServerMock.createKibanaRequest({ params }); + const mockResponse = httpServerMock.createResponseFactory(); + + const mockRouter = mockCoreSetup.http.createRouter.mock.results[0].value; + const [[, deleteHandler]] = mockRouter.delete.mock.calls; + + deleteHandler(mockContext, mockRequest, mockResponse); + + expect(mockContext.search!.session.delete).toHaveBeenCalledWith(id); + }); +}); diff --git a/src/plugins/data/server/search/routes/session.ts b/src/plugins/data/server/search/routes/session.ts new file mode 100644 index 00000000000000..93f07ecfb92ff6 --- /dev/null +++ b/src/plugins/data/server/search/routes/session.ts @@ -0,0 +1,201 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { schema } from '@kbn/config-schema'; +import { IRouter } from 'src/core/server'; + +export function registerSessionRoutes(router: IRouter): void { + router.post( + { + path: '/internal/session', + validate: { + body: schema.object({ + sessionId: schema.string(), + name: schema.string(), + expires: schema.maybe(schema.string()), + initialState: schema.maybe(schema.object({}, { unknowns: 'allow' })), + restoreState: schema.maybe(schema.object({}, { unknowns: 'allow' })), + }), + }, + }, + async (context, request, res) => { + const { sessionId, name, expires, initialState, restoreState } = request.body; + + try { + const response = await context.search!.session.save(sessionId, { + name, + expires, + initialState, + restoreState, + }); + + return res.ok({ + body: response, + }); + } catch (err) { + return res.customError({ + statusCode: err.statusCode || 500, + body: { + message: err.message, + attributes: { + error: err.body?.error || err.message, + }, + }, + }); + } + } + ); + + router.get( + { + path: '/internal/session/{id}', + validate: { + params: schema.object({ + id: schema.string(), + }), + }, + }, + async (context, request, res) => { + const { id } = request.params; + try { + const response = await context.search!.session.get(id); + + return res.ok({ + body: response, + }); + } catch (err) { + return res.customError({ + statusCode: err.statusCode || 500, + body: { + message: err.message, + attributes: { + error: err.body?.error || err.message, + }, + }, + }); + } + } + ); + + router.post( + { + path: '/internal/session/_find', + validate: { + body: schema.object({ + page: schema.maybe(schema.number()), + perPage: schema.maybe(schema.number()), + sortField: schema.maybe(schema.string()), + sortOrder: schema.maybe(schema.string()), + filter: schema.maybe(schema.string()), + }), + }, + }, + async (context, request, res) => { + const { page, perPage, sortField, sortOrder, filter } = request.body; + try { + const response = await context.search!.session.find({ + page, + perPage, + sortField, + sortOrder, + filter, + }); + + return res.ok({ + body: response, + }); + } catch (err) { + return res.customError({ + statusCode: err.statusCode || 500, + body: { + message: err.message, + attributes: { + error: err.body?.error || err.message, + }, + }, + }); + } + } + ); + + router.delete( + { + path: '/internal/session/{id}', + validate: { + params: schema.object({ + id: schema.string(), + }), + }, + }, + async (context, request, res) => { + const { id } = request.params; + try { + await context.search!.session.delete(id); + + return res.ok(); + } catch (err) { + return res.customError({ + statusCode: err.statusCode || 500, + body: { + message: err.message, + attributes: { + error: err.body?.error || err.message, + }, + }, + }); + } + } + ); + + router.put( + { + path: '/internal/session/{id}', + validate: { + params: schema.object({ + id: schema.string(), + }), + body: schema.object({ + name: schema.maybe(schema.string()), + expires: schema.maybe(schema.string()), + }), + }, + }, + async (context, request, res) => { + const { id } = request.params; + const { name, expires } = request.body; + try { + const response = await context.search!.session.update(id, { name, expires }); + + return res.ok({ + body: response, + }); + } catch (err) { + return res.customError({ + statusCode: err.statusCode || 500, + body: { + message: err.message, + attributes: { + error: err.body?.error || err.message, + }, + }, + }); + } + } + ); +} diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index d8aa588719e3e2..b44980164d0976 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -17,7 +17,7 @@ * under the License. */ -import { BehaviorSubject, Observable } from 'rxjs'; +import { BehaviorSubject, from, Observable } from 'rxjs'; import { pick } from 'lodash'; import { CoreSetup, @@ -29,7 +29,7 @@ import { SharedGlobalConfig, StartServicesAccessor, } from 'src/core/server'; -import { first } from 'rxjs/operators'; +import { first, switchMap } from 'rxjs/operators'; import { ExpressionsServerSetup } from 'src/plugins/expressions/server'; import { ISearchSetup, @@ -49,7 +49,7 @@ import { DataPluginStart } from '../plugin'; import { UsageCollectionSetup } from '../../../usage_collection/server'; import { registerUsageCollector } from './collectors/register'; import { usageProvider } from './collectors/usage'; -import { searchTelemetry } from '../saved_objects'; +import { BACKGROUND_SESSION_TYPE, searchTelemetry } from '../saved_objects'; import { IEsSearchRequest, IEsSearchResponse, @@ -70,10 +70,14 @@ import { } from '../../common/search/aggs/buckets/shard_delay'; import { aggShardDelay } from '../../common/search/aggs/buckets/shard_delay_fn'; import { ConfigSchema } from '../../config'; +import { BackgroundSessionService, ISearchSessionClient } from './session'; +import { registerSessionRoutes } from './routes/session'; +import { backgroundSessionMapping } from '../saved_objects'; +import { tapFirst } from '../../common/utils'; declare module 'src/core/server' { interface RequestHandlerContext { - search?: ISearchClient; + search?: ISearchClient & { session: ISearchSessionClient }; } } @@ -102,6 +106,7 @@ export class SearchService implements Plugin { private readonly searchSourceService = new SearchSourceService(); private defaultSearchStrategyName: string = ES_SEARCH_STRATEGY; private searchStrategies: StrategyMap = {}; + private sessionService: BackgroundSessionService = new BackgroundSessionService(); constructor( private initializerContext: PluginInitializerContext, @@ -121,12 +126,17 @@ export class SearchService implements Plugin { }; registerSearchRoute(router); registerMsearchRoute(router, routeDependencies); + registerSessionRoutes(router); core.http.registerRouteHandlerContext('search', async (context, request) => { const [coreStart] = await core.getStartServices(); - return this.asScopedProvider(coreStart)(request); + const search = this.asScopedProvider(coreStart)(request); + const session = this.sessionService.asScopedProvider(coreStart)(request); + return { ...search, session }; }); + core.savedObjects.registerType(backgroundSessionMapping); + this.registerSearchStrategy( ES_SEARCH_STRATEGY, esSearchStrategyProvider( @@ -223,6 +233,7 @@ export class SearchService implements Plugin { public stop() { this.aggsService.stop(); + this.sessionService.stop(); } private registerSearchStrategy = < @@ -248,7 +259,24 @@ export class SearchService implements Plugin { options.strategy ); - return strategy.search(searchRequest, options, deps); + // If this is a restored background search session, look up the ID using the provided sessionId + const getSearchRequest = async () => + !options.isRestore || searchRequest.id + ? searchRequest + : { + ...searchRequest, + id: await this.sessionService.getId(searchRequest, options, deps), + }; + + return from(getSearchRequest()).pipe( + switchMap((request) => strategy.search(request, options, deps)), + tapFirst((response) => { + if (searchRequest.id || !options.sessionId || !response.id || options.isRestore) return; + this.sessionService.trackId(searchRequest, response.id, options, { + savedObjectsClient: deps.savedObjectsClient, + }); + }) + ); }; private cancel = (id: string, options: ISearchOptions, deps: SearchStrategyDependencies) => { @@ -273,7 +301,9 @@ export class SearchService implements Plugin { private asScopedProvider = ({ elasticsearch, savedObjects, uiSettings }: CoreStart) => { return (request: KibanaRequest): ISearchClient => { - const savedObjectsClient = savedObjects.getScopedClient(request); + const savedObjectsClient = savedObjects.getScopedClient(request, { + includedHiddenTypes: [BACKGROUND_SESSION_TYPE], + }); const deps = { savedObjectsClient, esClient: elasticsearch.client.asScoped(request), diff --git a/src/plugins/data/server/search/session/index.ts b/src/plugins/data/server/search/session/index.ts new file mode 100644 index 00000000000000..11b5b16a02b561 --- /dev/null +++ b/src/plugins/data/server/search/session/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { BackgroundSessionService, ISearchSessionClient } from './session_service'; diff --git a/src/plugins/data/server/search/session/session_service.test.ts b/src/plugins/data/server/search/session/session_service.test.ts new file mode 100644 index 00000000000000..1ceebae967d4c6 --- /dev/null +++ b/src/plugins/data/server/search/session/session_service.test.ts @@ -0,0 +1,233 @@ +/* + * 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 type { SavedObject, SavedObjectsClientContract } from 'kibana/server'; +import { savedObjectsClientMock } from '../../../../../core/server/mocks'; +import { BackgroundSessionStatus } from '../../../common'; +import { BACKGROUND_SESSION_TYPE } from '../../saved_objects'; +import { BackgroundSessionService } from './session_service'; +import { createRequestHash } from './utils'; + +describe('BackgroundSessionService', () => { + let savedObjectsClient: jest.Mocked; + let service: BackgroundSessionService; + + const mockSavedObject: SavedObject = { + id: 'd7170a35-7e2c-48d6-8dec-9a056721b489', + type: BACKGROUND_SESSION_TYPE, + attributes: { + name: 'my_name', + idMapping: {}, + }, + references: [], + }; + + beforeEach(() => { + savedObjectsClient = savedObjectsClientMock.create(); + service = new BackgroundSessionService(); + }); + + it('save throws if `name` is not provided', () => { + const sessionId = 'd7170a35-7e2c-48d6-8dec-9a056721b489'; + + expect(() => service.save(sessionId, {}, { savedObjectsClient })).rejects.toMatchInlineSnapshot( + `[Error: Name is required]` + ); + }); + + it('get calls saved objects client', async () => { + savedObjectsClient.get.mockResolvedValue(mockSavedObject); + + const sessionId = 'd7170a35-7e2c-48d6-8dec-9a056721b489'; + const response = await service.get(sessionId, { savedObjectsClient }); + + expect(response).toBe(mockSavedObject); + expect(savedObjectsClient.get).toHaveBeenCalledWith(BACKGROUND_SESSION_TYPE, sessionId); + }); + + it('find calls saved objects client', async () => { + const mockFindSavedObject = { + ...mockSavedObject, + score: 1, + }; + const mockResponse = { + saved_objects: [mockFindSavedObject], + total: 1, + per_page: 1, + page: 0, + }; + savedObjectsClient.find.mockResolvedValue(mockResponse); + + const options = { page: 0, perPage: 5 }; + const response = await service.find(options, { savedObjectsClient }); + + expect(response).toBe(mockResponse); + expect(savedObjectsClient.find).toHaveBeenCalledWith({ + ...options, + type: BACKGROUND_SESSION_TYPE, + }); + }); + + it('update calls saved objects client', async () => { + const mockUpdateSavedObject = { + ...mockSavedObject, + attributes: {}, + }; + savedObjectsClient.update.mockResolvedValue(mockUpdateSavedObject); + + const sessionId = 'd7170a35-7e2c-48d6-8dec-9a056721b489'; + const attributes = { name: 'new_name' }; + const response = await service.update(sessionId, attributes, { savedObjectsClient }); + + expect(response).toBe(mockUpdateSavedObject); + expect(savedObjectsClient.update).toHaveBeenCalledWith( + BACKGROUND_SESSION_TYPE, + sessionId, + attributes + ); + }); + + it('delete calls saved objects client', async () => { + savedObjectsClient.delete.mockResolvedValue({}); + + const sessionId = 'd7170a35-7e2c-48d6-8dec-9a056721b489'; + const response = await service.delete(sessionId, { savedObjectsClient }); + + expect(response).toEqual({}); + expect(savedObjectsClient.delete).toHaveBeenCalledWith(BACKGROUND_SESSION_TYPE, sessionId); + }); + + describe('trackId', () => { + it('stores hash in memory when `isStored` is `false` for when `save` is called', async () => { + const searchRequest = { params: {} }; + const requestHash = createRequestHash(searchRequest.params); + const searchId = 'FnpFYlBpeXdCUTMyZXhCLTc1TWFKX0EbdDFDTzJzTE1Sck9PVTBIcW1iU05CZzo4MDA0'; + const sessionId = 'd7170a35-7e2c-48d6-8dec-9a056721b489'; + const isStored = false; + const name = 'my saved background search session'; + const created = new Date().toISOString(); + const expires = new Date().toISOString(); + + await service.trackId( + searchRequest, + searchId, + { sessionId, isStored }, + { savedObjectsClient } + ); + + expect(savedObjectsClient.update).not.toHaveBeenCalled(); + + await service.save(sessionId, { name, created, expires }, { savedObjectsClient }); + + expect(savedObjectsClient.create).toHaveBeenCalledWith( + BACKGROUND_SESSION_TYPE, + { + name, + created, + expires, + initialState: {}, + restoreState: {}, + status: BackgroundSessionStatus.IN_PROGRESS, + idMapping: { [requestHash]: searchId }, + }, + { id: sessionId } + ); + }); + + it('updates saved object when `isStored` is `true`', async () => { + const searchRequest = { params: {} }; + const requestHash = createRequestHash(searchRequest.params); + const searchId = 'FnpFYlBpeXdCUTMyZXhCLTc1TWFKX0EbdDFDTzJzTE1Sck9PVTBIcW1iU05CZzo4MDA0'; + const sessionId = 'd7170a35-7e2c-48d6-8dec-9a056721b489'; + const isStored = true; + + await service.trackId( + searchRequest, + searchId, + { sessionId, isStored }, + { savedObjectsClient } + ); + + expect(savedObjectsClient.update).toHaveBeenCalledWith(BACKGROUND_SESSION_TYPE, sessionId, { + idMapping: { [requestHash]: searchId }, + }); + }); + }); + + describe('getId', () => { + it('throws if `sessionId` is not provided', () => { + const searchRequest = { params: {} }; + + expect(() => + service.getId(searchRequest, {}, { savedObjectsClient }) + ).rejects.toMatchInlineSnapshot(`[Error: Session ID is required]`); + }); + + it('throws if there is not a saved object', () => { + const searchRequest = { params: {} }; + const sessionId = 'd7170a35-7e2c-48d6-8dec-9a056721b489'; + + expect(() => + service.getId(searchRequest, { sessionId, isStored: false }, { savedObjectsClient }) + ).rejects.toMatchInlineSnapshot( + `[Error: Cannot get search ID from a session that is not stored]` + ); + }); + + it('throws if not restoring a saved session', () => { + const searchRequest = { params: {} }; + const sessionId = 'd7170a35-7e2c-48d6-8dec-9a056721b489'; + + expect(() => + service.getId( + searchRequest, + { sessionId, isStored: true, isRestore: false }, + { savedObjectsClient } + ) + ).rejects.toMatchInlineSnapshot( + `[Error: Get search ID is only supported when restoring a session]` + ); + }); + + it('returns the search ID from the saved object ID mapping', async () => { + const searchRequest = { params: {} }; + const requestHash = createRequestHash(searchRequest.params); + const searchId = 'FnpFYlBpeXdCUTMyZXhCLTc1TWFKX0EbdDFDTzJzTE1Sck9PVTBIcW1iU05CZzo4MDA0'; + const sessionId = 'd7170a35-7e2c-48d6-8dec-9a056721b489'; + const mockSession = { + id: 'd7170a35-7e2c-48d6-8dec-9a056721b489', + type: BACKGROUND_SESSION_TYPE, + attributes: { + name: 'my_name', + idMapping: { [requestHash]: searchId }, + }, + references: [], + }; + savedObjectsClient.get.mockResolvedValue(mockSession); + + const id = await service.getId( + searchRequest, + { sessionId, isStored: true, isRestore: true }, + { savedObjectsClient } + ); + + expect(id).toBe(searchId); + }); + }); +}); diff --git a/src/plugins/data/server/search/session/session_service.ts b/src/plugins/data/server/search/session/session_service.ts new file mode 100644 index 00000000000000..eca5f428b8555f --- /dev/null +++ b/src/plugins/data/server/search/session/session_service.ts @@ -0,0 +1,204 @@ +/* + * 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 { CoreStart, KibanaRequest, SavedObjectsClientContract } from 'kibana/server'; +import { + BackgroundSessionSavedObjectAttributes, + IKibanaSearchRequest, + ISearchOptions, + SearchSessionFindOptions, + BackgroundSessionStatus, +} from '../../../common'; +import { BACKGROUND_SESSION_TYPE } from '../../saved_objects'; +import { createRequestHash } from './utils'; + +const DEFAULT_EXPIRATION = 7 * 24 * 60 * 60 * 1000; + +export interface BackgroundSessionDependencies { + savedObjectsClient: SavedObjectsClientContract; +} + +export type ISearchSessionClient = ReturnType< + ReturnType +>; + +export class BackgroundSessionService { + /** + * Map of sessionId to { [requestHash]: searchId } + * @private + */ + private sessionSearchMap = new Map>(); + + constructor() {} + + public setup = () => {}; + + public start = (core: CoreStart) => { + return { + asScoped: this.asScopedProvider(core), + }; + }; + + public stop = () => { + this.sessionSearchMap.clear(); + }; + + // TODO: Generate the `userId` from the realm type/realm name/username + public save = async ( + sessionId: string, + { + name, + created = new Date().toISOString(), + expires = new Date(Date.now() + DEFAULT_EXPIRATION).toISOString(), + status = BackgroundSessionStatus.IN_PROGRESS, + initialState = {}, + restoreState = {}, + }: Partial, + { savedObjectsClient }: BackgroundSessionDependencies + ) => { + if (!name) throw new Error('Name is required'); + + // Get the mapping of request hash/search ID for this session + const searchMap = this.sessionSearchMap.get(sessionId) ?? new Map(); + const idMapping = Object.fromEntries(searchMap.entries()); + const attributes = { name, created, expires, status, initialState, restoreState, idMapping }; + const session = await savedObjectsClient.create( + BACKGROUND_SESSION_TYPE, + attributes, + { id: sessionId } + ); + + // Clear out the entries for this session ID so they don't get saved next time + this.sessionSearchMap.delete(sessionId); + + return session; + }; + + // TODO: Throw an error if this session doesn't belong to this user + public get = (sessionId: string, { savedObjectsClient }: BackgroundSessionDependencies) => { + return savedObjectsClient.get( + BACKGROUND_SESSION_TYPE, + sessionId + ); + }; + + // TODO: Throw an error if this session doesn't belong to this user + public find = ( + options: SearchSessionFindOptions, + { savedObjectsClient }: BackgroundSessionDependencies + ) => { + return savedObjectsClient.find({ + ...options, + type: BACKGROUND_SESSION_TYPE, + }); + }; + + // TODO: Throw an error if this session doesn't belong to this user + public update = ( + sessionId: string, + attributes: Partial, + { savedObjectsClient }: BackgroundSessionDependencies + ) => { + return savedObjectsClient.update( + BACKGROUND_SESSION_TYPE, + sessionId, + attributes + ); + }; + + // TODO: Throw an error if this session doesn't belong to this user + public delete = (sessionId: string, { savedObjectsClient }: BackgroundSessionDependencies) => { + return savedObjectsClient.delete(BACKGROUND_SESSION_TYPE, sessionId); + }; + + /** + * Tracks the given search request/search ID in the saved session (if it exists). Otherwise, just + * store it in memory until a saved session exists. + * @internal + */ + public trackId = async ( + searchRequest: IKibanaSearchRequest, + searchId: string, + { sessionId, isStored }: ISearchOptions, + deps: BackgroundSessionDependencies + ) => { + if (!sessionId || !searchId) return; + const requestHash = createRequestHash(searchRequest.params); + + // If there is already a saved object for this session, update it to include this request/ID. + // Otherwise, just update the in-memory mapping for this session for when the session is saved. + if (isStored) { + const attributes = { idMapping: { [requestHash]: searchId } }; + await this.update(sessionId, attributes, deps); + } else { + const map = this.sessionSearchMap.get(sessionId) ?? new Map(); + map.set(requestHash, searchId); + this.sessionSearchMap.set(sessionId, map); + } + }; + + /** + * Look up an existing search ID that matches the given request in the given session so that the + * request can continue rather than restart. + * @internal + */ + public getId = async ( + searchRequest: IKibanaSearchRequest, + { sessionId, isStored, isRestore }: ISearchOptions, + deps: BackgroundSessionDependencies + ) => { + if (!sessionId) { + throw new Error('Session ID is required'); + } else if (!isStored) { + throw new Error('Cannot get search ID from a session that is not stored'); + } else if (!isRestore) { + throw new Error('Get search ID is only supported when restoring a session'); + } + + const session = await this.get(sessionId, deps); + const requestHash = createRequestHash(searchRequest.params); + if (!session.attributes.idMapping.hasOwnProperty(requestHash)) { + throw new Error('No search ID in this session matching the given search request'); + } + + return session.attributes.idMapping[requestHash]; + }; + + public asScopedProvider = ({ savedObjects }: CoreStart) => { + return (request: KibanaRequest) => { + const savedObjectsClient = savedObjects.getScopedClient(request, { + includedHiddenTypes: [BACKGROUND_SESSION_TYPE], + }); + const deps = { savedObjectsClient }; + return { + save: (sessionId: string, attributes: Partial) => + this.save(sessionId, attributes, deps), + get: (sessionId: string) => this.get(sessionId, deps), + find: (options: SearchSessionFindOptions) => this.find(options, deps), + update: (sessionId: string, attributes: Partial) => + this.update(sessionId, attributes, deps), + delete: (sessionId: string) => this.delete(sessionId, deps), + trackId: (searchRequest: IKibanaSearchRequest, searchId: string, options: ISearchOptions) => + this.trackId(searchRequest, searchId, options, deps), + getId: (searchRequest: IKibanaSearchRequest, options: ISearchOptions) => + this.getId(searchRequest, options, deps), + }; + }; + }; +} diff --git a/src/plugins/data/server/search/session/utils.test.ts b/src/plugins/data/server/search/session/utils.test.ts new file mode 100644 index 00000000000000..d190f892a7f848 --- /dev/null +++ b/src/plugins/data/server/search/session/utils.test.ts @@ -0,0 +1,37 @@ +/* + * 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 { createRequestHash } from './utils'; + +describe('data/search/session utils', () => { + describe('createRequestHash', () => { + it('ignores `preference`', () => { + const request = { + foo: 'bar', + }; + + const withPreference = { + ...request, + preference: 1234, + }; + + expect(createRequestHash(request)).toEqual(createRequestHash(withPreference)); + }); + }); +}); diff --git a/src/plugins/data/server/search/session/utils.ts b/src/plugins/data/server/search/session/utils.ts new file mode 100644 index 00000000000000..c3332f80b6e3fa --- /dev/null +++ b/src/plugins/data/server/search/session/utils.ts @@ -0,0 +1,30 @@ +/* + * 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 { createHash } from 'crypto'; + +/** + * Generate the hash for this request so that, in the future, this hash can be used to look up + * existing search IDs for this request. Ignores the `preference` parameter since it generally won't + * match from one request to another identical request. + */ +export function createRequestHash(keys: Record) { + const { preference, ...params } = keys; + return createHash(`sha256`).update(JSON.stringify(params)).digest('hex'); +} diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index ce66610edf880b..8d1699c4ad5ed9 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -753,6 +753,8 @@ export class IndexPatternsService implements Plugin_3( - () => this.runSearch(request, combinedSignal, strategy), - (requestId) => this.runSearch({ ...request, id: requestId }, combinedSignal, strategy), + () => this.runSearch(request, { ...options, strategy, abortSignal: combinedSignal }), + (requestId) => + this.runSearch( + { ...request, id: requestId }, + { ...options, strategy, abortSignal: combinedSignal } + ), (r) => !r.isRunning, (response) => response.id, id, diff --git a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts index 53bcac02cb01d6..2070610ceb20e4 100644 --- a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts +++ b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts @@ -57,6 +57,7 @@ export const enhancedEsSearchStrategyProvider = ( utils.toSnakeCase({ ...(await getDefaultSearchParams(uiSettingsClient)), batchedReduceSize: 64, + keepOnCompletion: !!options.sessionId, // Always return an ID, even if the request completes quickly ...asyncOptions, ...request.params, }) From f2ad337fefc434cdd420e098c4d94a3a944f38b3 Mon Sep 17 00:00:00 2001 From: Robert Oskamp Date: Thu, 19 Nov 2020 07:05:10 +0100 Subject: [PATCH 10/16] Increase bulk request timeout during esArchiver load (#83657) This PR fixes some timeouts during esArchive load by increasing the request timeout. --- .../kbn-es-archiver/src/lib/docs/index_doc_records_stream.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kbn-es-archiver/src/lib/docs/index_doc_records_stream.ts b/packages/kbn-es-archiver/src/lib/docs/index_doc_records_stream.ts index 28790176af73d4..46c46ad5d1b686 100644 --- a/packages/kbn-es-archiver/src/lib/docs/index_doc_records_stream.ts +++ b/packages/kbn-es-archiver/src/lib/docs/index_doc_records_stream.ts @@ -44,7 +44,7 @@ export function createIndexDocRecordsStream( ); }); - const resp = await client.bulk({ body }); + const resp = await client.bulk({ requestTimeout: 2 * 60 * 1000, body }); if (resp.errors) { throw new Error(`Failed to index all documents: ${JSON.stringify(resp, null, 2)}`); } From 8d9e383980ddb9ff19338d6d748ed9dc38562e06 Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Wed, 18 Nov 2020 22:29:11 -0800 Subject: [PATCH 11/16] Skip failing cypress test Signed-off-by: Tyler Smalley --- .../cypress/integration/alerts_detection_rules_custom.spec.ts | 3 ++- .../cypress/integration/alerts_detection_rules_export.spec.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts index fb1f2920aaceb1..d14e09d9384a2f 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts @@ -114,7 +114,8 @@ const expectedEditedtags = editedRule.tags.join(''); const expectedEditedIndexPatterns = editedRule.index && editedRule.index.length ? editedRule.index : indexPatterns; -describe('Custom detection rules creation', () => { +// SKIP: https://github.com/elastic/kibana/issues/83769 +describe.skip('Custom detection rules creation', () => { before(() => { esArchiverLoad('timeline'); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_export.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_export.spec.ts index eb8448233c6241..6f995045dfc6ae 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_export.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_export.spec.ts @@ -17,7 +17,8 @@ import { DETECTIONS_URL } from '../urls/navigation'; const EXPECTED_EXPORTED_RULE_FILE_PATH = 'cypress/test_files/expected_rules_export.ndjson'; -describe('Export rules', () => { +// SKIP: https://github.com/elastic/kibana/issues/83769 +describe.skip('Export rules', () => { before(() => { esArchiverLoad('export_rule'); cy.server(); From 3a8ea2993f6c7bfdcbf52617ffc0601f49d69879 Mon Sep 17 00:00:00 2001 From: Dario Gieselaar Date: Thu, 19 Nov 2020 08:38:51 +0100 Subject: [PATCH 12/16] Make expectSnapshot available in all functional test runs (#82932) Co-authored-by: spalger --- .../src/functional_test_runner/cli.ts | 16 ++- .../fake_mocha_types.d.ts | 7 + .../lib/config/schema.ts | 2 +- .../lib/mocha/load_test_files.js | 13 +- .../lib/mocha/setup_mocha.js | 1 + .../snapshots/decorate_snapshot_ui.test.ts | 133 ++++++++++++++++++ .../lib/snapshots/decorate_snapshot_ui.ts | 99 +++++++------ .../run_tests/__snapshots__/args.test.js.snap | 23 +++ .../run_tests/__snapshots__/cli.test.js.snap | 2 + .../functional_tests/cli/run_tests/args.js | 6 + .../cli/run_tests/args.test.js | 5 + .../cli/run_tests/cli.test.js | 16 +++ .../__snapshots__/cli.test.js.snap | 9 +- .../cli/start_servers/cli.test.js | 9 ++ .../src/functional_tests/lib/run_ftr.js | 3 +- packages/kbn-test/tsconfig.json | 3 + .../kbn-test/types/ftr_globals/mocha.d.ts | 18 +-- .../kbn-test/types/ftr_globals/snapshots.d.ts | 25 ++++ test/tsconfig.json | 2 +- .../api_integration/apis/uptime/rest/index.ts | 3 - .../uptime/rest/monitor_states_real_data.ts | 1 - .../basic/tests/correlations/ranges.ts | 1 - .../tests/correlations/slow_durations.ts | 1 - .../apm_api_integration/basic/tests/index.ts | 3 - .../tests/metrics_charts/metrics_charts.ts | 1 - .../observability_overview.ts | 1 - .../basic/tests/service_maps/service_maps.ts | 1 - .../tests/service_overview/error_groups.ts | 1 - .../basic/tests/services/top_services.ts | 1 - .../basic/tests/services/transaction_types.ts | 1 - .../tests/settings/agent_configuration.ts | 1 - .../settings/anomaly_detection/read_user.ts | 1 - .../settings/anomaly_detection/write_user.ts | 1 - .../basic/tests/settings/custom_link.ts | 1 - .../basic/tests/traces/top_traces.ts | 1 - .../tests/transaction_groups/breakdown.ts | 1 - .../tests/transaction_groups/distribution.ts | 1 - .../tests/transaction_groups/error_rate.ts | 1 - .../top_transaction_groups.ts | 1 - .../transaction_groups/transaction_charts.ts | 1 - .../trial/tests/csm/csm_services.ts | 1 - .../trial/tests/csm/has_rum_data.ts | 1 - .../trial/tests/csm/js_errors.ts | 1 - .../trial/tests/csm/long_task_metrics.ts | 1 - .../trial/tests/csm/page_views.ts | 1 - .../trial/tests/csm/url_search.ts | 1 - .../trial/tests/csm/web_core_vitals.ts | 1 - .../apm_api_integration/trial/tests/index.ts | 3 - .../trial/tests/service_maps/service_maps.ts | 1 - .../trial/tests/services/top_services.ts | 1 - .../services/transaction_groups_charts.ts | 1 - x-pack/test/mocha_decorations.d.ts | 17 --- x-pack/test/tsconfig.json | 2 +- 53 files changed, 329 insertions(+), 120 deletions(-) create mode 100644 packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.test.ts rename x-pack/test/apm_api_integration/common/match_snapshot.ts => packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.ts (66%) rename test/mocha_decorations.d.ts => packages/kbn-test/types/ftr_globals/mocha.d.ts (73%) create mode 100644 packages/kbn-test/types/ftr_globals/snapshots.d.ts delete mode 100644 x-pack/test/mocha_decorations.d.ts diff --git a/packages/kbn-test/src/functional_test_runner/cli.ts b/packages/kbn-test/src/functional_test_runner/cli.ts index d744be94673116..8f53d6f7cf58b3 100644 --- a/packages/kbn-test/src/functional_test_runner/cli.ts +++ b/packages/kbn-test/src/functional_test_runner/cli.ts @@ -60,7 +60,8 @@ export function runFtrCli() { include: toArray(flags['include-tag'] as string | string[]), exclude: toArray(flags['exclude-tag'] as string | string[]), }, - updateBaselines: flags.updateBaselines, + updateBaselines: flags.updateBaselines || flags.u, + updateSnapshots: flags.updateSnapshots || flags.u, } ); @@ -126,7 +127,16 @@ export function runFtrCli() { 'exclude-tag', 'kibana-install-dir', ], - boolean: ['bail', 'invert', 'test-stats', 'updateBaselines', 'throttle', 'headless'], + boolean: [ + 'bail', + 'invert', + 'test-stats', + 'updateBaselines', + 'updateSnapshots', + 'u', + 'throttle', + 'headless', + ], default: { config: 'test/functional/config.js', }, @@ -141,6 +151,8 @@ export function runFtrCli() { --exclude-tag=tag a tag to be excluded, pass multiple times for multiple tags --test-stats print the number of tests (included and excluded) to STDERR --updateBaselines replace baseline screenshots with whatever is generated from the test + --updateSnapshots replace inline and file snapshots with whatever is generated from the test + -u replace both baseline screenshots and snapshots --kibana-install-dir directory where the Kibana install being tested resides --throttle enable network throttling in Chrome browser --headless run browser in headless mode diff --git a/packages/kbn-test/src/functional_test_runner/fake_mocha_types.d.ts b/packages/kbn-test/src/functional_test_runner/fake_mocha_types.d.ts index 12390a95a4961a..35b4b85e4d22a2 100644 --- a/packages/kbn-test/src/functional_test_runner/fake_mocha_types.d.ts +++ b/packages/kbn-test/src/functional_test_runner/fake_mocha_types.d.ts @@ -28,10 +28,17 @@ import EventEmitter from 'events'; export interface Suite { suites: Suite[]; tests: Test[]; + title: string; + file?: string; + parent?: Suite; } export interface Test { fullTitle(): string; + title: string; + file?: string; + parent?: Suite; + isPassed: () => boolean; } export interface Runner extends EventEmitter { diff --git a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts index 6ed114d62e2446..6f1519c079bee6 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts @@ -138,7 +138,7 @@ export const schema = Joi.object() .default(), updateBaselines: Joi.boolean().default(false), - + updateSnapshots: Joi.boolean().default(false), browser: Joi.object() .keys({ type: Joi.string().valid('chrome', 'firefox', 'msedge').default('chrome'), diff --git a/packages/kbn-test/src/functional_test_runner/lib/mocha/load_test_files.js b/packages/kbn-test/src/functional_test_runner/lib/mocha/load_test_files.js index 5c23be6361866d..0f5f3df6bd4135 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/mocha/load_test_files.js +++ b/packages/kbn-test/src/functional_test_runner/lib/mocha/load_test_files.js @@ -21,6 +21,7 @@ import { isAbsolute } from 'path'; import { loadTracer } from '../load_tracer'; import { decorateMochaUi } from './decorate_mocha_ui'; +import { decorateSnapshotUi } from '../snapshots/decorate_snapshot_ui'; /** * Load an array of test files into a mocha instance @@ -31,7 +32,17 @@ import { decorateMochaUi } from './decorate_mocha_ui'; * @param {String} path * @return {undefined} - mutates mocha, no return value */ -export const loadTestFiles = ({ mocha, log, lifecycle, providers, paths, updateBaselines }) => { +export const loadTestFiles = ({ + mocha, + log, + lifecycle, + providers, + paths, + updateBaselines, + updateSnapshots, +}) => { + decorateSnapshotUi(lifecycle, updateSnapshots); + const innerLoadTestFile = (path) => { if (typeof path !== 'string' || !isAbsolute(path)) { throw new TypeError('loadTestFile() only accepts absolute paths'); diff --git a/packages/kbn-test/src/functional_test_runner/lib/mocha/setup_mocha.js b/packages/kbn-test/src/functional_test_runner/lib/mocha/setup_mocha.js index 39eb69a1519189..66b93ec001ac9b 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/mocha/setup_mocha.js +++ b/packages/kbn-test/src/functional_test_runner/lib/mocha/setup_mocha.js @@ -53,6 +53,7 @@ export async function setupMocha(lifecycle, log, config, providers) { providers, paths: config.get('testFiles'), updateBaselines: config.get('updateBaselines'), + updateSnapshots: config.get('updateSnapshots'), }); // Each suite has a tag that is the path relative to the root of the repo diff --git a/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.test.ts b/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.test.ts new file mode 100644 index 00000000000000..abfbd8acea7835 --- /dev/null +++ b/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.test.ts @@ -0,0 +1,133 @@ +/* + * 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 { Test } from '../../fake_mocha_types'; +import { Lifecycle } from '../lifecycle'; +import { decorateSnapshotUi, expectSnapshot } from './decorate_snapshot_ui'; +import path from 'path'; +import fs from 'fs'; + +describe('decorateSnapshotUi', () => { + describe('when running a test', () => { + let lifecycle: Lifecycle; + beforeEach(() => { + lifecycle = new Lifecycle(); + decorateSnapshotUi(lifecycle, false); + }); + + it('passes when the snapshot matches the actual value', async () => { + const test: Test = { + title: 'Test', + file: 'foo.ts', + parent: { + file: 'foo.ts', + tests: [], + suites: [], + }, + } as any; + + await lifecycle.beforeEachTest.trigger(test); + + expect(() => { + expectSnapshot('foo').toMatchInline(`"foo"`); + }).not.toThrow(); + }); + + it('throws when the snapshot does not match the actual value', async () => { + const test: Test = { + title: 'Test', + file: 'foo.ts', + parent: { + file: 'foo.ts', + tests: [], + suites: [], + }, + } as any; + + await lifecycle.beforeEachTest.trigger(test); + + expect(() => { + expectSnapshot('foo').toMatchInline(`"bar"`); + }).toThrow(); + }); + + it('writes a snapshot to an external file if it does not exist', async () => { + const test: Test = { + title: 'Test', + file: __filename, + isPassed: () => true, + } as any; + + // @ts-expect-error + test.parent = { + file: __filename, + tests: [test], + suites: [], + }; + + await lifecycle.beforeEachTest.trigger(test); + + const snapshotFile = path.resolve( + __dirname, + '__snapshots__', + 'decorate_snapshot_ui.test.snap' + ); + + expect(fs.existsSync(snapshotFile)).toBe(false); + + expect(() => { + expectSnapshot('foo').toMatch(); + }).not.toThrow(); + + await lifecycle.afterTestSuite.trigger(test.parent); + + expect(fs.existsSync(snapshotFile)).toBe(true); + + fs.unlinkSync(snapshotFile); + + fs.rmdirSync(path.resolve(__dirname, '__snapshots__')); + }); + }); + + describe('when updating snapshots', () => { + let lifecycle: Lifecycle; + beforeEach(() => { + lifecycle = new Lifecycle(); + decorateSnapshotUi(lifecycle, true); + }); + + it("doesn't throw if the value does not match", async () => { + const test: Test = { + title: 'Test', + file: 'foo.ts', + parent: { + file: 'foo.ts', + tests: [], + suites: [], + }, + } as any; + + await lifecycle.beforeEachTest.trigger(test); + + expect(() => { + expectSnapshot('bar').toMatchInline(`"foo"`); + }).not.toThrow(); + }); + }); +}); diff --git a/x-pack/test/apm_api_integration/common/match_snapshot.ts b/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.ts similarity index 66% rename from x-pack/test/apm_api_integration/common/match_snapshot.ts rename to packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.ts index 567a1ced360f85..45550b55e73c7a 100644 --- a/x-pack/test/apm_api_integration/common/match_snapshot.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.ts @@ -1,7 +1,20 @@ /* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. + * 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 { @@ -14,8 +27,9 @@ import path from 'path'; import expect from '@kbn/expect'; import prettier from 'prettier'; import babelTraverse from '@babel/traverse'; -import { Suite, Test } from 'mocha'; -import { flatten } from 'lodash'; +import { flatten, once } from 'lodash'; +import { Lifecycle } from '../lifecycle'; +import { Test, Suite } from '../../fake_mocha_types'; type ISnapshotState = InstanceType; @@ -59,12 +73,38 @@ function getSnapshotMeta(currentTest: Test) { }; } -export function registerMochaHooksForSnapshots() { +const modifyStackTracePrepareOnce = once(() => { + const originalPrepareStackTrace = Error.prepareStackTrace; + + // jest-snapshot uses a stack trace to determine which file/line/column + // an inline snapshot should be written to. We filter out match_snapshot + // from the stack trace to prevent it from wanting to write to this file. + + Error.prepareStackTrace = (error, structuredStackTrace) => { + let filteredStrackTrace: NodeJS.CallSite[] = structuredStackTrace; + if (registered) { + filteredStrackTrace = filteredStrackTrace.filter((callSite) => { + // check for both compiled and uncompiled files + return !callSite.getFileName()?.match(/decorate_snapshot_ui\.(js|ts)/); + }); + } + + if (originalPrepareStackTrace) { + return originalPrepareStackTrace(error, filteredStrackTrace); + } + }; +}); + +export function decorateSnapshotUi(lifecycle: Lifecycle, updateSnapshots: boolean) { let snapshotStatesByFilePath: Record< string, { snapshotState: ISnapshotState; testsInFile: Test[] } > = {}; + registered = true; + + modifyStackTracePrepareOnce(); + addSerializer({ serialize: (num: number) => { return String(parseFloat(num.toPrecision(15))); @@ -74,15 +114,14 @@ export function registerMochaHooksForSnapshots() { }, }); - registered = true; - - beforeEach(function () { - const currentTest = this.currentTest!; + // @ts-expect-error + global.expectSnapshot = expectSnapshot; + lifecycle.beforeEachTest.add((currentTest: Test) => { const { file, snapshotTitle } = getSnapshotMeta(currentTest); if (!snapshotStatesByFilePath[file]) { - snapshotStatesByFilePath[file] = getSnapshotState(file, currentTest); + snapshotStatesByFilePath[file] = getSnapshotState(file, currentTest, updateSnapshots); } testContext = { @@ -95,17 +134,14 @@ export function registerMochaHooksForSnapshots() { }; }); - afterEach(function () { - testContext = null; - }); - - after(function () { - // save snapshot after tests complete + lifecycle.afterTestSuite.add(function (testSuite) { + // save snapshot & check unused after top-level test suite completes + if (testSuite.parent?.parent) { + return; + } const unused: string[] = []; - const isUpdatingSnapshots = process.env.UPDATE_SNAPSHOTS; - Object.keys(snapshotStatesByFilePath).forEach((file) => { const { snapshotState, testsInFile } = snapshotStatesByFilePath[file]; @@ -118,7 +154,7 @@ export function registerMochaHooksForSnapshots() { } }); - if (!isUpdatingSnapshots) { + if (!updateSnapshots) { unused.push(...snapshotState.getUncheckedKeys()); } else { snapshotState.removeUncheckedKeys(); @@ -131,36 +167,19 @@ export function registerMochaHooksForSnapshots() { throw new Error( `${unused.length} obsolete snapshot(s) found:\n${unused.join( '\n\t' - )}.\n\nRun tests again with \`UPDATE_SNAPSHOTS=1\` to remove them.` + )}.\n\nRun tests again with \`--updateSnapshots\` to remove them.` ); } snapshotStatesByFilePath = {}; - - registered = false; }); } -const originalPrepareStackTrace = Error.prepareStackTrace; - -// jest-snapshot uses a stack trace to determine which file/line/column -// an inline snapshot should be written to. We filter out match_snapshot -// from the stack trace to prevent it from wanting to write to this file. - -Error.prepareStackTrace = (error, structuredStackTrace) => { - const filteredStrackTrace = structuredStackTrace.filter((callSite) => { - return !callSite.getFileName()?.endsWith('match_snapshot.ts'); - }); - if (originalPrepareStackTrace) { - return originalPrepareStackTrace(error, filteredStrackTrace); - } -}; - function recursivelyGetTestsFromSuite(suite: Suite): Test[] { return suite.tests.concat(flatten(suite.suites.map((s) => recursivelyGetTestsFromSuite(s)))); } -function getSnapshotState(file: string, test: Test) { +function getSnapshotState(file: string, test: Test, updateSnapshots: boolean) { const dirname = path.dirname(file); const filename = path.basename(file); @@ -177,7 +196,7 @@ function getSnapshotState(file: string, test: Test) { const snapshotState = new SnapshotState( path.join(dirname + `/__snapshots__/` + filename.replace(path.extname(filename), '.snap')), { - updateSnapshot: process.env.UPDATE_SNAPSHOTS ? 'all' : 'new', + updateSnapshot: updateSnapshots ? 'all' : 'new', getPrettier: () => prettier, getBabelTraverse: () => babelTraverse, } diff --git a/packages/kbn-test/src/functional_tests/cli/run_tests/__snapshots__/args.test.js.snap b/packages/kbn-test/src/functional_tests/cli/run_tests/__snapshots__/args.test.js.snap index 434c374d5d23da..ad2f82de87b82d 100644 --- a/packages/kbn-test/src/functional_tests/cli/run_tests/__snapshots__/args.test.js.snap +++ b/packages/kbn-test/src/functional_tests/cli/run_tests/__snapshots__/args.test.js.snap @@ -16,6 +16,8 @@ Options: --bail Stop the test run at the first failure. --grep Pattern to select which tests to run. --updateBaselines Replace baseline screenshots with whatever is generated from the test. + --updateSnapshots Replace inline and file snapshots with whatever is generated from the test. + --u Replace both baseline screenshots and snapshots --include Files that must included to be run, can be included multiple times. --exclude Files that must NOT be included to be run, can be included multiple times. --include-tag Tags that suites must include to be run, can be included multiple times. @@ -48,6 +50,27 @@ Object { } `; +exports[`process options for run tests CLI accepts boolean value for updateSnapshots 1`] = ` +Object { + "assertNoneExcluded": false, + "configs": Array [ + /foo, + ], + "createLogger": [Function], + "esFrom": "snapshot", + "extraKbnOpts": undefined, + "suiteFiles": Object { + "exclude": Array [], + "include": Array [], + }, + "suiteTags": Object { + "exclude": Array [], + "include": Array [], + }, + "updateSnapshots": true, +} +`; + exports[`process options for run tests CLI accepts debug option 1`] = ` Object { "assertNoneExcluded": false, diff --git a/packages/kbn-test/src/functional_tests/cli/run_tests/__snapshots__/cli.test.js.snap b/packages/kbn-test/src/functional_tests/cli/run_tests/__snapshots__/cli.test.js.snap index 6ede71a6c39402..02d11b0033d572 100644 --- a/packages/kbn-test/src/functional_tests/cli/run_tests/__snapshots__/cli.test.js.snap +++ b/packages/kbn-test/src/functional_tests/cli/run_tests/__snapshots__/cli.test.js.snap @@ -16,6 +16,8 @@ Options: --bail Stop the test run at the first failure. --grep Pattern to select which tests to run. --updateBaselines Replace baseline screenshots with whatever is generated from the test. + --updateSnapshots Replace inline and file snapshots with whatever is generated from the test. + --u Replace both baseline screenshots and snapshots --include Files that must included to be run, can be included multiple times. --exclude Files that must NOT be included to be run, can be included multiple times. --include-tag Tags that suites must include to be run, can be included multiple times. diff --git a/packages/kbn-test/src/functional_tests/cli/run_tests/args.js b/packages/kbn-test/src/functional_tests/cli/run_tests/args.js index 94d510915d8e59..5af0fe7d7b61cc 100644 --- a/packages/kbn-test/src/functional_tests/cli/run_tests/args.js +++ b/packages/kbn-test/src/functional_tests/cli/run_tests/args.js @@ -46,6 +46,12 @@ const options = { updateBaselines: { desc: 'Replace baseline screenshots with whatever is generated from the test.', }, + updateSnapshots: { + desc: 'Replace inline and file snapshots with whatever is generated from the test.', + }, + u: { + desc: 'Replace both baseline screenshots and snapshots', + }, include: { arg: '', desc: 'Files that must included to be run, can be included multiple times.', diff --git a/packages/kbn-test/src/functional_tests/cli/run_tests/args.test.js b/packages/kbn-test/src/functional_tests/cli/run_tests/args.test.js index 35e4cef5b3a664..34a2d19c22a3fd 100644 --- a/packages/kbn-test/src/functional_tests/cli/run_tests/args.test.js +++ b/packages/kbn-test/src/functional_tests/cli/run_tests/args.test.js @@ -76,6 +76,11 @@ describe('process options for run tests CLI', () => { expect(options).toMatchSnapshot(); }); + it('accepts boolean value for updateSnapshots', () => { + const options = processOptions({ updateSnapshots: true }, ['foo']); + expect(options).toMatchSnapshot(); + }); + it('accepts source value for esFrom', () => { const options = processOptions({ esFrom: 'source' }, ['foo']); expect(options).toMatchSnapshot(); diff --git a/packages/kbn-test/src/functional_tests/cli/run_tests/cli.test.js b/packages/kbn-test/src/functional_tests/cli/run_tests/cli.test.js index 9f9a8f59fde9ad..438e3585fc068d 100644 --- a/packages/kbn-test/src/functional_tests/cli/run_tests/cli.test.js +++ b/packages/kbn-test/src/functional_tests/cli/run_tests/cli.test.js @@ -125,6 +125,22 @@ describe('run tests CLI', () => { expect(exitMock).not.toHaveBeenCalledWith(); }); + it('accepts boolean value for updateSnapshots', async () => { + global.process.argv.push('--updateSnapshots'); + + await runTestsCli(['foo']); + + expect(exitMock).not.toHaveBeenCalledWith(); + }); + + it('accepts boolean value for -u', async () => { + global.process.argv.push('-u'); + + await runTestsCli(['foo']); + + expect(exitMock).not.toHaveBeenCalledWith(); + }); + it('accepts source value for esFrom', async () => { global.process.argv.push('--esFrom', 'source'); diff --git a/packages/kbn-test/src/functional_tests/cli/start_servers/__snapshots__/cli.test.js.snap b/packages/kbn-test/src/functional_tests/cli/start_servers/__snapshots__/cli.test.js.snap index b54bf5dc84dd11..ba085b08682169 100644 --- a/packages/kbn-test/src/functional_tests/cli/start_servers/__snapshots__/cli.test.js.snap +++ b/packages/kbn-test/src/functional_tests/cli/start_servers/__snapshots__/cli.test.js.snap @@ -7,6 +7,13 @@ exports[`start servers CLI options accepts boolean value for updateBaselines 1`] " `; +exports[`start servers CLI options accepts boolean value for updateSnapshots 1`] = ` +" +functional_tests_server: invalid option [updateSnapshots] + ...stack trace... +" +`; + exports[`start servers CLI options rejects bail 1`] = ` " functional_tests_server: invalid option [bail] @@ -40,4 +47,4 @@ exports[`start servers CLI options rejects invalid options even if valid options functional_tests_server: invalid option [grep] ...stack trace... " -`; \ No newline at end of file +`; diff --git a/packages/kbn-test/src/functional_tests/cli/start_servers/cli.test.js b/packages/kbn-test/src/functional_tests/cli/start_servers/cli.test.js index 3ceecb28066286..d63a8df2491a82 100644 --- a/packages/kbn-test/src/functional_tests/cli/start_servers/cli.test.js +++ b/packages/kbn-test/src/functional_tests/cli/start_servers/cli.test.js @@ -126,6 +126,15 @@ describe('start servers CLI', () => { checkMockConsoleLogSnapshot(logMock); }); + it('accepts boolean value for updateSnapshots', async () => { + global.process.argv.push('--updateSnapshots'); + + await startServersCli('foo'); + + expect(exitMock).toHaveBeenCalledWith(1); + checkMockConsoleLogSnapshot(logMock); + }); + it('accepts source value for esFrom', async () => { global.process.argv.push('--esFrom', 'source'); diff --git a/packages/kbn-test/src/functional_tests/lib/run_ftr.js b/packages/kbn-test/src/functional_tests/lib/run_ftr.js index 14883ac977c431..d9389c8cbc1542 100644 --- a/packages/kbn-test/src/functional_tests/lib/run_ftr.js +++ b/packages/kbn-test/src/functional_tests/lib/run_ftr.js @@ -22,7 +22,7 @@ import { CliError } from './run_cli'; async function createFtr({ configPath, - options: { installDir, log, bail, grep, updateBaselines, suiteFiles, suiteTags }, + options: { installDir, log, bail, grep, updateBaselines, suiteFiles, suiteTags, updateSnapshots }, }) { const config = await readConfigFile(log, configPath); @@ -37,6 +37,7 @@ async function createFtr({ installDir, }, updateBaselines, + updateSnapshots, suiteFiles: { include: [...suiteFiles.include, ...config.get('suiteFiles.include')], exclude: [...suiteFiles.exclude, ...config.get('suiteFiles.exclude')], diff --git a/packages/kbn-test/tsconfig.json b/packages/kbn-test/tsconfig.json index 3219e6cf3d6eee..6d94389f82caaf 100644 --- a/packages/kbn-test/tsconfig.json +++ b/packages/kbn-test/tsconfig.json @@ -5,6 +5,9 @@ "src/**/*", "index.d.ts" ], + "exclude": [ + "types/ftr_globals/**/*" + ], "compilerOptions": { "declaration": true, "emitDeclarationOnly": true, diff --git a/test/mocha_decorations.d.ts b/packages/kbn-test/types/ftr_globals/mocha.d.ts similarity index 73% rename from test/mocha_decorations.d.ts rename to packages/kbn-test/types/ftr_globals/mocha.d.ts index 5ad289eb4f1a33..d143b742b6dd87 100644 --- a/test/mocha_decorations.d.ts +++ b/packages/kbn-test/types/ftr_globals/mocha.d.ts @@ -19,27 +19,11 @@ import { Suite } from 'mocha'; -type Tags = - | 'ciGroup1' - | 'ciGroup2' - | 'ciGroup3' - | 'ciGroup4' - | 'ciGroup5' - | 'ciGroup6' - | 'ciGroup7' - | 'ciGroup8' - | 'ciGroup9' - | 'ciGroup10' - | 'ciGroup11' - | 'ciGroup12'; - -// We need to use the namespace here to match the Mocha definition declare module 'mocha' { interface Suite { /** * Assign tags to the test suite to determine in which CI job it should be run. */ - tags(tags: T | T[]): void; - tags(tags: T | T[]): void; + tags(tags: string[] | string): void; } } diff --git a/packages/kbn-test/types/ftr_globals/snapshots.d.ts b/packages/kbn-test/types/ftr_globals/snapshots.d.ts new file mode 100644 index 00000000000000..ab247a72991eb7 --- /dev/null +++ b/packages/kbn-test/types/ftr_globals/snapshots.d.ts @@ -0,0 +1,25 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +declare const expectSnapshot: ( + received: any +) => { + toMatch: () => void; + toMatchInline: (_actual?: any) => void; +}; diff --git a/test/tsconfig.json b/test/tsconfig.json index 390e0b88c3d5c6..df26441b0806f3 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -4,7 +4,7 @@ "incremental": false, "types": ["node", "mocha", "flot"] }, - "include": ["**/*", "../typings/elastic__node_crypto.d.ts", "typings/**/*"], + "include": ["**/*", "../typings/elastic__node_crypto.d.ts", "typings/**/*", "../packages/kbn-test/types/ftr_globals/**/*"], "exclude": ["plugin_functional/plugins/**/*", "interpreter_functional/plugins/**/*"], "references": [ { "path": "../src/core/tsconfig.json" }, diff --git a/x-pack/test/api_integration/apis/uptime/rest/index.ts b/x-pack/test/api_integration/apis/uptime/rest/index.ts index 6f410add0fa4d4..f59b79a6b7bfcf 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/index.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/index.ts @@ -9,15 +9,12 @@ import { settingsObjectId, settingsObjectType, } from '../../../../../plugins/uptime/server/lib/saved_objects'; -import { registerMochaHooksForSnapshots } from '../../../../apm_api_integration/common/match_snapshot'; export default function ({ getService, loadTestFile }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const server = getService('kibanaServer'); describe('uptime REST endpoints', () => { - registerMochaHooksForSnapshots(); - beforeEach('clear settings', async () => { try { await server.savedObjects.delete({ diff --git a/x-pack/test/api_integration/apis/uptime/rest/monitor_states_real_data.ts b/x-pack/test/api_integration/apis/uptime/rest/monitor_states_real_data.ts index 08a339ed59326a..bdc18ac831d27b 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/monitor_states_real_data.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/monitor_states_real_data.ts @@ -9,7 +9,6 @@ import { isRight } from 'fp-ts/lib/Either'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { MonitorSummariesResultType } from '../../../../../plugins/uptime/common/runtime_types'; import { API_URLS } from '../../../../../plugins/uptime/common/constants'; -import { expectSnapshot } from '../../../../apm_api_integration/common/match_snapshot'; interface ExpectedMonitorStatesPage { response: any; diff --git a/x-pack/test/apm_api_integration/basic/tests/correlations/ranges.ts b/x-pack/test/apm_api_integration/basic/tests/correlations/ranges.ts index 0a730217e53f57..751ee8753c449e 100644 --- a/x-pack/test/apm_api_integration/basic/tests/correlations/ranges.ts +++ b/x-pack/test/apm_api_integration/basic/tests/correlations/ranges.ts @@ -9,7 +9,6 @@ import { format } from 'url'; import { PromiseReturnType } from '../../../../../plugins/observability/typings/common'; import archives_metadata from '../../../common/archives_metadata'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; -import { expectSnapshot } from '../../../common/match_snapshot'; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); diff --git a/x-pack/test/apm_api_integration/basic/tests/correlations/slow_durations.ts b/x-pack/test/apm_api_integration/basic/tests/correlations/slow_durations.ts index 0cfdf3ec474d5a..3cf1c2cecb42b5 100644 --- a/x-pack/test/apm_api_integration/basic/tests/correlations/slow_durations.ts +++ b/x-pack/test/apm_api_integration/basic/tests/correlations/slow_durations.ts @@ -9,7 +9,6 @@ import { format } from 'url'; import { PromiseReturnType } from '../../../../../plugins/observability/typings/common'; import archives_metadata from '../../../common/archives_metadata'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; -import { expectSnapshot } from '../../../common/match_snapshot'; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); diff --git a/x-pack/test/apm_api_integration/basic/tests/index.ts b/x-pack/test/apm_api_integration/basic/tests/index.ts index 39dd721c7067e9..0381e5f51bb9b4 100644 --- a/x-pack/test/apm_api_integration/basic/tests/index.ts +++ b/x-pack/test/apm_api_integration/basic/tests/index.ts @@ -4,12 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registerMochaHooksForSnapshots } from '../../common/match_snapshot'; export default function apmApiIntegrationTests({ loadTestFile }: FtrProviderContext) { describe('APM specs (basic)', function () { - registerMochaHooksForSnapshots(); - this.tags('ciGroup1'); loadTestFile(require.resolve('./feature_controls')); diff --git a/x-pack/test/apm_api_integration/basic/tests/metrics_charts/metrics_charts.ts b/x-pack/test/apm_api_integration/basic/tests/metrics_charts/metrics_charts.ts index cae562b3f5dc5a..d52aa2727d651e 100644 --- a/x-pack/test/apm_api_integration/basic/tests/metrics_charts/metrics_charts.ts +++ b/x-pack/test/apm_api_integration/basic/tests/metrics_charts/metrics_charts.ts @@ -8,7 +8,6 @@ import { first } from 'lodash'; import { MetricsChartsByAgentAPIResponse } from '../../../../../plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent'; import { GenericMetricsChart } from '../../../../../plugins/apm/server/lib/metrics/transform_metrics_chart'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; -import { expectSnapshot } from '../../../common/match_snapshot'; interface ChartResponse { body: MetricsChartsByAgentAPIResponse; diff --git a/x-pack/test/apm_api_integration/basic/tests/observability_overview/observability_overview.ts b/x-pack/test/apm_api_integration/basic/tests/observability_overview/observability_overview.ts index 01fa09630e85a7..cdeab9ecbdc49d 100644 --- a/x-pack/test/apm_api_integration/basic/tests/observability_overview/observability_overview.ts +++ b/x-pack/test/apm_api_integration/basic/tests/observability_overview/observability_overview.ts @@ -5,7 +5,6 @@ */ import expect from '@kbn/expect'; import archives_metadata from '../../../common/archives_metadata'; -import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function ApiTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/apm_api_integration/basic/tests/service_maps/service_maps.ts b/x-pack/test/apm_api_integration/basic/tests/service_maps/service_maps.ts index d729680154c1da..3820a766510539 100644 --- a/x-pack/test/apm_api_integration/basic/tests/service_maps/service_maps.ts +++ b/x-pack/test/apm_api_integration/basic/tests/service_maps/service_maps.ts @@ -5,7 +5,6 @@ */ import expect from '@kbn/expect'; -import { expectSnapshot } from '../../../common/match_snapshot'; import archives_metadata from '../../../common/archives_metadata'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; diff --git a/x-pack/test/apm_api_integration/basic/tests/service_overview/error_groups.ts b/x-pack/test/apm_api_integration/basic/tests/service_overview/error_groups.ts index b699a30d40418c..088b7cb8bb5687 100644 --- a/x-pack/test/apm_api_integration/basic/tests/service_overview/error_groups.ts +++ b/x-pack/test/apm_api_integration/basic/tests/service_overview/error_groups.ts @@ -7,7 +7,6 @@ import expect from '@kbn/expect'; import qs from 'querystring'; import { pick, uniqBy } from 'lodash'; -import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import archives from '../../../common/archives_metadata'; diff --git a/x-pack/test/apm_api_integration/basic/tests/services/top_services.ts b/x-pack/test/apm_api_integration/basic/tests/services/top_services.ts index cd2bdb7fde19e8..4d70c4e9494330 100644 --- a/x-pack/test/apm_api_integration/basic/tests/services/top_services.ts +++ b/x-pack/test/apm_api_integration/basic/tests/services/top_services.ts @@ -7,7 +7,6 @@ import expect from '@kbn/expect'; import { isEmpty, pick } from 'lodash'; import { PromiseReturnType } from '../../../../../plugins/observability/typings/common'; -import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; import archives_metadata from '../../../common/archives_metadata'; diff --git a/x-pack/test/apm_api_integration/basic/tests/services/transaction_types.ts b/x-pack/test/apm_api_integration/basic/tests/services/transaction_types.ts index 1221ce0198d82a..40b6db6997f8a1 100644 --- a/x-pack/test/apm_api_integration/basic/tests/services/transaction_types.ts +++ b/x-pack/test/apm_api_integration/basic/tests/services/transaction_types.ts @@ -6,7 +6,6 @@ import expect from '@kbn/expect'; import archives_metadata from '../../../common/archives_metadata'; -import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; export default function ApiTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/apm_api_integration/basic/tests/settings/agent_configuration.ts b/x-pack/test/apm_api_integration/basic/tests/settings/agent_configuration.ts index 32a06b8fb880e4..bde9364efc685a 100644 --- a/x-pack/test/apm_api_integration/basic/tests/settings/agent_configuration.ts +++ b/x-pack/test/apm_api_integration/basic/tests/settings/agent_configuration.ts @@ -6,7 +6,6 @@ import expect from '@kbn/expect'; import { omit, orderBy } from 'lodash'; -import { expectSnapshot } from '../../../common/match_snapshot'; import { AgentConfigurationIntake } from '../../../../../plugins/apm/common/agent_configuration/configuration_types'; import { AgentConfigSearchParams } from '../../../../../plugins/apm/server/routes/settings/agent_configuration'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; diff --git a/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/read_user.ts b/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/read_user.ts index 2c8f13ce79f765..a9e6eae8bed884 100644 --- a/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/read_user.ts +++ b/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/read_user.ts @@ -5,7 +5,6 @@ */ import expect from '@kbn/expect'; -import { expectSnapshot } from '../../../../common/match_snapshot'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; export default function apiTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/write_user.ts b/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/write_user.ts index d1dbd15f4dced0..4fa3e46430e91e 100644 --- a/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/write_user.ts +++ b/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/write_user.ts @@ -5,7 +5,6 @@ */ import expect from '@kbn/expect'; -import { expectSnapshot } from '../../../../common/match_snapshot'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; export default function apiTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/apm_api_integration/basic/tests/settings/custom_link.ts b/x-pack/test/apm_api_integration/basic/tests/settings/custom_link.ts index 60b4020e73dceb..8ac5566fc2c495 100644 --- a/x-pack/test/apm_api_integration/basic/tests/settings/custom_link.ts +++ b/x-pack/test/apm_api_integration/basic/tests/settings/custom_link.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ import expect from '@kbn/expect'; -import { expectSnapshot } from '../../../common/match_snapshot'; import { CustomLink } from '../../../../../plugins/apm/common/custom_link/custom_link_types'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; diff --git a/x-pack/test/apm_api_integration/basic/tests/traces/top_traces.ts b/x-pack/test/apm_api_integration/basic/tests/traces/top_traces.ts index 6a3a1ddd0f6aeb..4dbd6cc4cd6f7e 100644 --- a/x-pack/test/apm_api_integration/basic/tests/traces/top_traces.ts +++ b/x-pack/test/apm_api_integration/basic/tests/traces/top_traces.ts @@ -6,7 +6,6 @@ import expect from '@kbn/expect'; import { sortBy } from 'lodash'; import archives_metadata from '../../../common/archives_metadata'; -import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; export default function ApiTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/breakdown.ts b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/breakdown.ts index f2e58718870bf4..24f542c222d6e8 100644 --- a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/breakdown.ts +++ b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/breakdown.ts @@ -5,7 +5,6 @@ */ import expect from '@kbn/expect'; import archives_metadata from '../../../common/archives_metadata'; -import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function ApiTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/distribution.ts b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/distribution.ts index e0b03e1a91f402..a93aff5c8cf32c 100644 --- a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/distribution.ts +++ b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/distribution.ts @@ -7,7 +7,6 @@ import expect from '@kbn/expect'; import qs from 'querystring'; import { isEmpty } from 'lodash'; import archives_metadata from '../../../common/archives_metadata'; -import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function ApiTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/error_rate.ts b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/error_rate.ts index 86309c91b0bc21..da3d07a0e83a3b 100644 --- a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/error_rate.ts +++ b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/error_rate.ts @@ -6,7 +6,6 @@ import expect from '@kbn/expect'; import { first, last } from 'lodash'; import archives_metadata from '../../../common/archives_metadata'; -import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function ApiTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/top_transaction_groups.ts b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/top_transaction_groups.ts index 2e802957a95e38..d4fdfe6d0fc762 100644 --- a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/top_transaction_groups.ts +++ b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/top_transaction_groups.ts @@ -6,7 +6,6 @@ import expect from '@kbn/expect'; import { sortBy } from 'lodash'; import archives_metadata from '../../../common/archives_metadata'; -import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; function sortTransactionGroups(items: any[]) { diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/transaction_charts.ts b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/transaction_charts.ts index c3b969d7656642..5ebbdfa16d9a8a 100644 --- a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/transaction_charts.ts +++ b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/transaction_charts.ts @@ -6,7 +6,6 @@ import expect from '@kbn/expect'; import archives_metadata from '../../../common/archives_metadata'; import { PromiseReturnType } from '../../../../../plugins/observability/typings/common'; -import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; export default function ApiTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/csm_services.ts b/x-pack/test/apm_api_integration/trial/tests/csm/csm_services.ts index 6235e7abd37ec5..05c6439508ecea 100644 --- a/x-pack/test/apm_api_integration/trial/tests/csm/csm_services.ts +++ b/x-pack/test/apm_api_integration/trial/tests/csm/csm_services.ts @@ -5,7 +5,6 @@ */ import expect from '@kbn/expect'; -import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function rumServicesApiTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/has_rum_data.ts b/x-pack/test/apm_api_integration/trial/tests/csm/has_rum_data.ts index 12fdb5ba9704e2..f2033e03f5821e 100644 --- a/x-pack/test/apm_api_integration/trial/tests/csm/has_rum_data.ts +++ b/x-pack/test/apm_api_integration/trial/tests/csm/has_rum_data.ts @@ -5,7 +5,6 @@ */ import expect from '@kbn/expect'; -import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function rumHasDataApiTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/js_errors.ts b/x-pack/test/apm_api_integration/trial/tests/csm/js_errors.ts index 0edffe7999a654..6fc8cb4c1d4e16 100644 --- a/x-pack/test/apm_api_integration/trial/tests/csm/js_errors.ts +++ b/x-pack/test/apm_api_integration/trial/tests/csm/js_errors.ts @@ -5,7 +5,6 @@ */ import expect from '@kbn/expect'; -import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function rumJsErrorsApiTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/long_task_metrics.ts b/x-pack/test/apm_api_integration/trial/tests/csm/long_task_metrics.ts index 518c4ef8a81a73..6db5de24baa991 100644 --- a/x-pack/test/apm_api_integration/trial/tests/csm/long_task_metrics.ts +++ b/x-pack/test/apm_api_integration/trial/tests/csm/long_task_metrics.ts @@ -5,7 +5,6 @@ */ import expect from '@kbn/expect'; -import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function rumServicesApiTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/page_views.ts b/x-pack/test/apm_api_integration/trial/tests/csm/page_views.ts index ca5670d41d8ee0..5d910862843d55 100644 --- a/x-pack/test/apm_api_integration/trial/tests/csm/page_views.ts +++ b/x-pack/test/apm_api_integration/trial/tests/csm/page_views.ts @@ -5,7 +5,6 @@ */ import expect from '@kbn/expect'; -import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function rumServicesApiTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/url_search.ts b/x-pack/test/apm_api_integration/trial/tests/csm/url_search.ts index c887fa3e776481..961c783434639c 100644 --- a/x-pack/test/apm_api_integration/trial/tests/csm/url_search.ts +++ b/x-pack/test/apm_api_integration/trial/tests/csm/url_search.ts @@ -5,7 +5,6 @@ */ import expect from '@kbn/expect'; -import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function rumServicesApiTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/web_core_vitals.ts b/x-pack/test/apm_api_integration/trial/tests/csm/web_core_vitals.ts index 5dbe266deeb811..7e970493eb611d 100644 --- a/x-pack/test/apm_api_integration/trial/tests/csm/web_core_vitals.ts +++ b/x-pack/test/apm_api_integration/trial/tests/csm/web_core_vitals.ts @@ -5,7 +5,6 @@ */ import expect from '@kbn/expect'; -import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function rumServicesApiTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/apm_api_integration/trial/tests/index.ts b/x-pack/test/apm_api_integration/trial/tests/index.ts index a67dd1bcbd7a8b..97ab662313c7c7 100644 --- a/x-pack/test/apm_api_integration/trial/tests/index.ts +++ b/x-pack/test/apm_api_integration/trial/tests/index.ts @@ -5,14 +5,11 @@ */ import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; -import { registerMochaHooksForSnapshots } from '../../common/match_snapshot'; export default function observabilityApiIntegrationTests({ loadTestFile }: FtrProviderContext) { describe('APM specs (trial)', function () { this.tags('ciGroup1'); - registerMochaHooksForSnapshots(); - describe('Services', function () { loadTestFile(require.resolve('./services/annotations')); loadTestFile(require.resolve('./services/top_services.ts')); diff --git a/x-pack/test/apm_api_integration/trial/tests/service_maps/service_maps.ts b/x-pack/test/apm_api_integration/trial/tests/service_maps/service_maps.ts index 9c01833f78e5d9..b1e29b220dd5c0 100644 --- a/x-pack/test/apm_api_integration/trial/tests/service_maps/service_maps.ts +++ b/x-pack/test/apm_api_integration/trial/tests/service_maps/service_maps.ts @@ -9,7 +9,6 @@ import expect from '@kbn/expect'; import { isEmpty, uniq } from 'lodash'; import archives_metadata from '../../../common/archives_metadata'; import { PromiseReturnType } from '../../../../../plugins/observability/typings/common'; -import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function serviceMapsApiTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/apm_api_integration/trial/tests/services/top_services.ts b/x-pack/test/apm_api_integration/trial/tests/services/top_services.ts index bb611013351d77..90cad966ba102b 100644 --- a/x-pack/test/apm_api_integration/trial/tests/services/top_services.ts +++ b/x-pack/test/apm_api_integration/trial/tests/services/top_services.ts @@ -5,7 +5,6 @@ */ import expect from '@kbn/expect'; -import { expectSnapshot } from '../../../common/match_snapshot'; import { PromiseReturnType } from '../../../../../plugins/observability/typings/common'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; import archives_metadata from '../../../common/archives_metadata'; diff --git a/x-pack/test/apm_api_integration/trial/tests/services/transaction_groups_charts.ts b/x-pack/test/apm_api_integration/trial/tests/services/transaction_groups_charts.ts index 47e465596e0d72..99e90b8433c84b 100644 --- a/x-pack/test/apm_api_integration/trial/tests/services/transaction_groups_charts.ts +++ b/x-pack/test/apm_api_integration/trial/tests/services/transaction_groups_charts.ts @@ -5,7 +5,6 @@ */ import expect from '@kbn/expect'; -import { expectSnapshot } from '../../../common/match_snapshot'; import { PromiseReturnType } from '../../../../../plugins/observability/typings/common'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; import archives_metadata from '../../../common/archives_metadata'; diff --git a/x-pack/test/mocha_decorations.d.ts b/x-pack/test/mocha_decorations.d.ts deleted file mode 100644 index 44f43a22de1f95..00000000000000 --- a/x-pack/test/mocha_decorations.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { Suite } from 'mocha'; - -// We need to use the namespace here to match the Mocha definition -declare module 'mocha' { - interface Suite { - /** - * Assign tags to the test suite to determine in which CI job it should be run. - */ - tags(tags: string[] | string): void; - } -} diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json index e041292ebf3c9d..3ac7026d16a173 100644 --- a/x-pack/test/tsconfig.json +++ b/x-pack/test/tsconfig.json @@ -5,7 +5,7 @@ "incremental": false, "types": ["mocha", "node", "flot"] }, - "include": ["**/*", "../typings/**/*"], + "include": ["**/*", "../typings/**/*", "../../packages/kbn-test/types/ftr_globals/**/*"], "exclude": ["../typings/jest.d.ts"], "references": [ { "path": "../../src/core/tsconfig.json" }, From 893b2961c0da9b267ca07aa9cb7f793fcca92acc Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 19 Nov 2020 10:24:38 +0200 Subject: [PATCH 13/16] [Security Solution][Detections] Fix adding an action to detection rules (#83722) --- .../rules/rule_actions_field/index.test.tsx | 9 ++++- .../rules/rule_actions_field/index.tsx | 10 ++++-- .../rules/step_rule_actions/index.test.tsx | 12 ++++++- .../rules/step_rule_actions/index.tsx | 4 +-- .../pages/detection_engine/rules/helpers.tsx | 35 +++++++++++-------- .../triggers_actions_ui/public/index.ts | 1 + 6 files changed, 49 insertions(+), 22 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.test.tsx index cce6c72ca4cc50..65e25b788b3d93 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.test.tsx @@ -31,13 +31,20 @@ describe('RuleActionsField', () => { }, }, }); + + const messageVariables = { + context: [], + state: [], + params: [], + }; + const Component = () => { const field = useFormFieldMock(); const { form } = useForm(); return (
    - + ); }; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx index 4ff1b4e4f20f35..0211788509db3b 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx @@ -12,17 +12,21 @@ import ReactMarkdown from 'react-markdown'; import styled from 'styled-components'; import { NOTIFICATION_SUPPORTED_ACTION_TYPES_IDS } from '../../../../../common/constants'; -import { SelectField, useFormContext } from '../../../../shared_imports'; +import { FieldHook, useFormContext } from '../../../../shared_imports'; import { ActionForm, ActionType, loadActionTypes, + ActionVariables, } from '../../../../../../triggers_actions_ui/public'; import { AlertAction } from '../../../../../../alerts/common'; import { useKibana } from '../../../../common/lib/kibana'; import { FORM_ERRORS_TITLE } from './translations'; -type ThrottleSelectField = typeof SelectField; +interface Props { + field: FieldHook; + messageVariables: ActionVariables; +} const DEFAULT_ACTION_GROUP_ID = 'default'; const DEFAULT_ACTION_MESSAGE = @@ -34,7 +38,7 @@ const FieldErrorsContainer = styled.div` } `; -export const RuleActionsField: ThrottleSelectField = ({ field, messageVariables }) => { +export const RuleActionsField: React.FC = ({ field, messageVariables }) => { const [fieldErrors, setFieldErrors] = useState(null); const [supportedActionTypes, setSupportedActionTypes] = useState(); const form = useFormContext(); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.test.tsx index a1d7e69b7a60f7..565998806033c5 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.test.tsx @@ -30,10 +30,20 @@ jest.mock('../../../../common/lib/kibana', () => ({ }), })); +const actionMessageParams = { + context: [], + state: [], + params: [], +}; + describe('StepRuleActions', () => { it('renders correctly', () => { const wrapper = shallow( - + ); expect(wrapper.find('[data-test-subj="stepRuleActions"]')).toHaveLength(1); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx index dd1d92e7e72a3a..daf4e3c01bbb3e 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx @@ -16,7 +16,7 @@ import { import { findIndex } from 'lodash/fp'; import React, { FC, memo, useCallback, useEffect, useMemo } from 'react'; -import { ActionVariable } from '../../../../../../triggers_actions_ui/public'; +import { ActionVariables } from '../../../../../../triggers_actions_ui/public'; import { RuleStep, RuleStepProps, @@ -38,7 +38,7 @@ import { APP_ID } from '../../../../../common/constants'; interface StepRuleActionsProps extends RuleStepProps { defaultValues?: ActionsStepRule | null; - actionMessageParams: ActionVariable[]; + actionMessageParams: ActionVariables; } const stepActionsDefaultValue: ActionsStepRule = { diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx index ffcf25d2537985..513982f099c618 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx @@ -11,7 +11,7 @@ import { useLocation } from 'react-router-dom'; import styled from 'styled-components'; import { EuiFlexItem } from '@elastic/eui'; -import { ActionVariable } from '../../../../../../triggers_actions_ui/public'; +import { ActionVariables } from '../../../../../../triggers_actions_ui/public'; import { RuleAlertAction } from '../../../../../common/detection_engine/types'; import { assertUnreachable } from '../../../../../common/utility_types'; import { transformRuleToAlertAction } from '../../../../../common/detection_engine/transform_actions'; @@ -366,21 +366,26 @@ export const getActionMessageRuleParams = (ruleType: Type): string[] => { return ruleParamsKeys; }; -export const getActionMessageParams = memoizeOne((ruleType: Type | undefined): ActionVariable[] => { - if (!ruleType) { - return []; +export const getActionMessageParams = memoizeOne( + (ruleType: Type | undefined): ActionVariables => { + if (!ruleType) { + return { state: [], params: [] }; + } + const actionMessageRuleParams = getActionMessageRuleParams(ruleType); + // Prefixes are being added automatically by the ActionTypeForm + return { + state: [{ name: 'signals_count', description: 'state.signals_count' }], + params: [], + context: [ + { name: 'results_link', description: 'context.results_link' }, + ...actionMessageRuleParams.map((param) => { + const extendedParam = `rule.${param}`; + return { name: extendedParam, description: `context.${extendedParam}` }; + }), + ], + }; } - const actionMessageRuleParams = getActionMessageRuleParams(ruleType); - - return [ - { name: 'state.signals_count', description: 'state.signals_count' }, - { name: '{context.results_link}', description: 'context.results_link' }, - ...actionMessageRuleParams.map((param) => { - const extendedParam = `context.rule.${param}`; - return { name: extendedParam, description: extendedParam }; - }), - ]; -}); +); // typed as null not undefined as the initial state for this value is null. export const userHasNoPermissions = (canUserCRUD: boolean | null): boolean => diff --git a/x-pack/plugins/triggers_actions_ui/public/index.ts b/x-pack/plugins/triggers_actions_ui/public/index.ts index 3187451d2600eb..c479359ff7e6ea 100644 --- a/x-pack/plugins/triggers_actions_ui/public/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/index.ts @@ -21,6 +21,7 @@ export { AlertTypeParamsExpressionProps, ValidationResult, ActionVariable, + ActionVariables, ActionConnector, IErrorObject, } from './types'; From 6a2c415a98cf56a6cfed38690297d378a63c07b1 Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Thu, 19 Nov 2020 06:08:56 -0500 Subject: [PATCH 14/16] Revert "[Alerting] Add `alert.updatedAt` field to represent date of last user edit (#83578)" This reverts commit acc3e2f443e3c60dfc923aa1b3b179f34cf69804. --- .../server/alerts_client/alerts_client.ts | 39 +++++++++-------- .../server/alerts_client/tests/create.test.ts | 7 --- .../alerts_client/tests/disable.test.ts | 6 +-- .../server/alerts_client/tests/enable.test.ts | 6 +-- .../server/alerts_client/tests/find.test.ts | 1 - .../server/alerts_client/tests/get.test.ts | 1 - .../tests/get_alert_instance_summary.test.ts | 1 - .../alerts_client/tests/mute_all.test.ts | 5 +-- .../alerts_client/tests/mute_instance.test.ts | 5 +-- .../alerts_client/tests/unmute_all.test.ts | 5 +-- .../tests/unmute_instance.test.ts | 5 +-- .../server/alerts_client/tests/update.test.ts | 7 +-- .../tests/update_api_key.test.ts | 6 +-- .../alerts/server/saved_objects/index.ts | 2 - .../alerts/server/saved_objects/mappings.json | 3 -- .../server/saved_objects/migrations.test.ts | 43 +------------------ .../alerts/server/saved_objects/migrations.ts | 20 --------- .../partially_update_alert.test.ts | 1 - x-pack/plugins/alerts/server/types.ts | 1 - .../spaces_only/tests/alerting/create.ts | 1 - .../tests/alerting/execution_status.ts | 22 ---------- .../spaces_only/tests/alerting/migrations.ts | 9 ---- 22 files changed, 30 insertions(+), 166 deletions(-) diff --git a/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts b/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts index c08ff9449d1513..e97b37f16faf0e 100644 --- a/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts +++ b/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts @@ -228,17 +228,14 @@ export class AlertsClient { this.validateActions(alertType, data.actions); - const createTime = Date.now(); const { references, actions } = await this.denormalizeActions(data.actions); - const rawAlert: RawAlert = { ...data, ...this.apiKeyAsAlertAttributes(createdAPIKey, username), actions, createdBy: username, updatedBy: username, - createdAt: new Date(createTime).toISOString(), - updatedAt: new Date(createTime).toISOString(), + createdAt: new Date().toISOString(), params: validatedAlertTypeParams as RawAlert['params'], muteAll: false, mutedInstanceIds: [], @@ -292,7 +289,12 @@ export class AlertsClient { }); createdAlert.attributes.scheduledTaskId = scheduledTask.id; } - return this.getAlertFromRaw(createdAlert.id, createdAlert.attributes, references); + return this.getAlertFromRaw( + createdAlert.id, + createdAlert.attributes, + createdAlert.updated_at, + references + ); } public async get({ id }: { id: string }): Promise { @@ -302,7 +304,7 @@ export class AlertsClient { result.attributes.consumer, ReadOperations.Get ); - return this.getAlertFromRaw(result.id, result.attributes, result.references); + return this.getAlertFromRaw(result.id, result.attributes, result.updated_at, result.references); } public async getAlertState({ id }: { id: string }): Promise { @@ -391,11 +393,13 @@ export class AlertsClient { type: 'alert', }); - const authorizedData = data.map(({ id, attributes, references }) => { + // eslint-disable-next-line @typescript-eslint/naming-convention + const authorizedData = data.map(({ id, attributes, updated_at, references }) => { ensureAlertTypeIsAuthorized(attributes.alertTypeId, attributes.consumer); return this.getAlertFromRaw( id, fields ? (pick(attributes, fields) as RawAlert) : attributes, + updated_at, references ); }); @@ -581,7 +585,6 @@ export class AlertsClient { params: validatedAlertTypeParams as RawAlert['params'], actions, updatedBy: username, - updatedAt: new Date().toISOString(), }); try { updatedObject = await this.unsecuredSavedObjectsClient.create( @@ -604,7 +607,12 @@ export class AlertsClient { throw e; } - return this.getPartialAlertFromRaw(id, updatedObject.attributes, updatedObject.references); + return this.getPartialAlertFromRaw( + id, + updatedObject.attributes, + updatedObject.updated_at, + updatedObject.references + ); } private apiKeyAsAlertAttributes( @@ -669,7 +677,6 @@ export class AlertsClient { await this.createAPIKey(this.generateAPIKeyName(attributes.alertTypeId, attributes.name)), username ), - updatedAt: new Date().toISOString(), updatedBy: username, }); try { @@ -744,7 +751,6 @@ export class AlertsClient { username ), updatedBy: username, - updatedAt: new Date().toISOString(), }); try { await this.unsecuredSavedObjectsClient.update('alert', id, updateAttributes, { version }); @@ -823,7 +829,6 @@ export class AlertsClient { apiKey: null, apiKeyOwner: null, updatedBy: await this.getUserName(), - updatedAt: new Date().toISOString(), }), { version } ); @@ -870,7 +875,6 @@ export class AlertsClient { muteAll: true, mutedInstanceIds: [], updatedBy: await this.getUserName(), - updatedAt: new Date().toISOString(), }); const updateOptions = { version }; @@ -909,7 +913,6 @@ export class AlertsClient { muteAll: false, mutedInstanceIds: [], updatedBy: await this.getUserName(), - updatedAt: new Date().toISOString(), }); const updateOptions = { version }; @@ -954,7 +957,6 @@ export class AlertsClient { this.updateMeta({ mutedInstanceIds, updatedBy: await this.getUserName(), - updatedAt: new Date().toISOString(), }), { version } ); @@ -997,7 +999,6 @@ export class AlertsClient { alertId, this.updateMeta({ updatedBy: await this.getUserName(), - updatedAt: new Date().toISOString(), mutedInstanceIds: mutedInstanceIds.filter((id: string) => id !== alertInstanceId), }), { version } @@ -1049,17 +1050,19 @@ export class AlertsClient { private getAlertFromRaw( id: string, rawAlert: RawAlert, + updatedAt: SavedObject['updated_at'], references: SavedObjectReference[] | undefined ): Alert { // In order to support the partial update API of Saved Objects we have to support // partial updates of an Alert, but when we receive an actual RawAlert, it is safe // to cast the result to an Alert - return this.getPartialAlertFromRaw(id, rawAlert, references) as Alert; + return this.getPartialAlertFromRaw(id, rawAlert, updatedAt, references) as Alert; } private getPartialAlertFromRaw( id: string, - { createdAt, updatedAt, meta, scheduledTaskId, ...rawAlert }: Partial, + { createdAt, meta, scheduledTaskId, ...rawAlert }: Partial, + updatedAt: SavedObject['updated_at'] = createdAt, references: SavedObjectReference[] | undefined ): PartialAlert { // Not the prettiest code here, but if we want to use most of the diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/create.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/create.test.ts index 6d259029ac4806..ee407b1a6d50c4 100644 --- a/x-pack/plugins/alerts/server/alerts_client/tests/create.test.ts +++ b/x-pack/plugins/alerts/server/alerts_client/tests/create.test.ts @@ -196,7 +196,6 @@ describe('create()', () => { createdAt: '2019-02-12T21:01:22.479Z', createdBy: 'elastic', updatedBy: 'elastic', - updatedAt: '2019-02-12T21:01:22.479Z', muteAll: false, mutedInstanceIds: [], actions: [ @@ -331,7 +330,6 @@ describe('create()', () => { "foo", ], "throttle": null, - "updatedAt": "2019-02-12T21:01:22.479Z", "updatedBy": "elastic", } `); @@ -420,7 +418,6 @@ describe('create()', () => { bar: true, }, createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), actions: [ { group: 'default', @@ -558,7 +555,6 @@ describe('create()', () => { bar: true, }, createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), actions: [ { group: 'default', @@ -635,7 +631,6 @@ describe('create()', () => { bar: true, }, createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), actions: [ { group: 'default', @@ -976,7 +971,6 @@ describe('create()', () => { createdBy: 'elastic', createdAt: '2019-02-12T21:01:22.479Z', updatedBy: 'elastic', - updatedAt: '2019-02-12T21:01:22.479Z', enabled: true, meta: { versionApiKeyLastmodified: 'v7.10.0', @@ -1098,7 +1092,6 @@ describe('create()', () => { createdBy: 'elastic', createdAt: '2019-02-12T21:01:22.479Z', updatedBy: 'elastic', - updatedAt: '2019-02-12T21:01:22.479Z', enabled: false, meta: { versionApiKeyLastmodified: 'v7.10.0', diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/disable.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/disable.test.ts index 8c9ab9494a50af..11ce0027f82d83 100644 --- a/x-pack/plugins/alerts/server/alerts_client/tests/disable.test.ts +++ b/x-pack/plugins/alerts/server/alerts_client/tests/disable.test.ts @@ -12,7 +12,7 @@ import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/s import { actionsAuthorizationMock } from '../../../../actions/server/mocks'; import { AlertsAuthorization } from '../../authorization/alerts_authorization'; import { ActionsAuthorization } from '../../../../actions/server'; -import { getBeforeSetup, setGlobalDate } from './lib'; +import { getBeforeSetup } from './lib'; import { InvalidatePendingApiKey } from '../../types'; const taskManager = taskManagerMock.createStart(); @@ -45,8 +45,6 @@ beforeEach(() => { getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry); }); -setGlobalDate(); - describe('disable()', () => { let alertsClient: AlertsClient; const existingAlert = { @@ -138,7 +136,6 @@ describe('disable()', () => { scheduledTaskId: null, apiKey: null, apiKeyOwner: null, - updatedAt: '2019-02-12T21:01:22.479Z', updatedBy: 'elastic', actions: [ { @@ -193,7 +190,6 @@ describe('disable()', () => { scheduledTaskId: null, apiKey: null, apiKeyOwner: null, - updatedAt: '2019-02-12T21:01:22.479Z', updatedBy: 'elastic', actions: [ { diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/enable.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/enable.test.ts index feec1d1b9334a1..16e83c42d89309 100644 --- a/x-pack/plugins/alerts/server/alerts_client/tests/enable.test.ts +++ b/x-pack/plugins/alerts/server/alerts_client/tests/enable.test.ts @@ -13,7 +13,7 @@ import { actionsAuthorizationMock } from '../../../../actions/server/mocks'; import { AlertsAuthorization } from '../../authorization/alerts_authorization'; import { ActionsAuthorization } from '../../../../actions/server'; import { TaskStatus } from '../../../../task_manager/server'; -import { getBeforeSetup, setGlobalDate } from './lib'; +import { getBeforeSetup } from './lib'; import { InvalidatePendingApiKey } from '../../types'; const taskManager = taskManagerMock.createStart(); @@ -46,8 +46,6 @@ beforeEach(() => { getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry); }); -setGlobalDate(); - describe('enable()', () => { let alertsClient: AlertsClient; const existingAlert = { @@ -188,7 +186,6 @@ describe('enable()', () => { meta: { versionApiKeyLastmodified: kibanaVersion, }, - updatedAt: '2019-02-12T21:01:22.479Z', updatedBy: 'elastic', apiKey: null, apiKeyOwner: null, @@ -295,7 +292,6 @@ describe('enable()', () => { apiKey: Buffer.from('123:abc').toString('base64'), apiKeyOwner: 'elastic', updatedBy: 'elastic', - updatedAt: '2019-02-12T21:01:22.479Z', actions: [ { group: 'default', diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/find.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/find.test.ts index 3d7473a7469869..1b3a776bd23e0d 100644 --- a/x-pack/plugins/alerts/server/alerts_client/tests/find.test.ts +++ b/x-pack/plugins/alerts/server/alerts_client/tests/find.test.ts @@ -79,7 +79,6 @@ describe('find()', () => { bar: true, }, createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), actions: [ { group: 'default', diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/get.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/get.test.ts index 3f0c783f424d13..5c0d80f159b310 100644 --- a/x-pack/plugins/alerts/server/alerts_client/tests/get.test.ts +++ b/x-pack/plugins/alerts/server/alerts_client/tests/get.test.ts @@ -59,7 +59,6 @@ describe('get()', () => { bar: true, }, createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), actions: [ { group: 'default', diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/get_alert_instance_summary.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/get_alert_instance_summary.test.ts index 9bd61c0fe66d2d..269b2eb2ab7a7f 100644 --- a/x-pack/plugins/alerts/server/alerts_client/tests/get_alert_instance_summary.test.ts +++ b/x-pack/plugins/alerts/server/alerts_client/tests/get_alert_instance_summary.test.ts @@ -76,7 +76,6 @@ const BaseAlertInstanceSummarySavedObject: SavedObject = { createdBy: null, updatedBy: null, createdAt: mockedDateString, - updatedAt: mockedDateString, apiKey: null, apiKeyOwner: null, throttle: null, diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/mute_all.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/mute_all.test.ts index 14ebca21355879..868fa3d8c6aa27 100644 --- a/x-pack/plugins/alerts/server/alerts_client/tests/mute_all.test.ts +++ b/x-pack/plugins/alerts/server/alerts_client/tests/mute_all.test.ts @@ -12,7 +12,7 @@ import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/s import { actionsAuthorizationMock } from '../../../../actions/server/mocks'; import { AlertsAuthorization } from '../../authorization/alerts_authorization'; import { ActionsAuthorization } from '../../../../actions/server'; -import { getBeforeSetup, setGlobalDate } from './lib'; +import { getBeforeSetup } from './lib'; const taskManager = taskManagerMock.createStart(); const alertTypeRegistry = alertTypeRegistryMock.create(); @@ -43,8 +43,6 @@ beforeEach(() => { getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry); }); -setGlobalDate(); - describe('muteAll()', () => { test('mutes an alert', async () => { const alertsClient = new AlertsClient(alertsClientParams); @@ -76,7 +74,6 @@ describe('muteAll()', () => { { muteAll: true, mutedInstanceIds: [], - updatedAt: '2019-02-12T21:01:22.479Z', updatedBy: 'elastic', }, { diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/mute_instance.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/mute_instance.test.ts index c2188f128cb4d5..05ca741f480cab 100644 --- a/x-pack/plugins/alerts/server/alerts_client/tests/mute_instance.test.ts +++ b/x-pack/plugins/alerts/server/alerts_client/tests/mute_instance.test.ts @@ -12,7 +12,7 @@ import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/s import { actionsAuthorizationMock } from '../../../../actions/server/mocks'; import { AlertsAuthorization } from '../../authorization/alerts_authorization'; import { ActionsAuthorization } from '../../../../actions/server'; -import { getBeforeSetup, setGlobalDate } from './lib'; +import { getBeforeSetup } from './lib'; const taskManager = taskManagerMock.createStart(); const alertTypeRegistry = alertTypeRegistryMock.create(); @@ -44,8 +44,6 @@ beforeEach(() => { getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry); }); -setGlobalDate(); - describe('muteInstance()', () => { test('mutes an alert instance', async () => { const alertsClient = new AlertsClient(alertsClientParams); @@ -70,7 +68,6 @@ describe('muteInstance()', () => { '1', { mutedInstanceIds: ['2'], - updatedAt: '2019-02-12T21:01:22.479Z', updatedBy: 'elastic', }, { diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/unmute_all.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/unmute_all.test.ts index d92304ab873be7..5ef1af9b6f0eee 100644 --- a/x-pack/plugins/alerts/server/alerts_client/tests/unmute_all.test.ts +++ b/x-pack/plugins/alerts/server/alerts_client/tests/unmute_all.test.ts @@ -12,7 +12,7 @@ import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/s import { actionsAuthorizationMock } from '../../../../actions/server/mocks'; import { AlertsAuthorization } from '../../authorization/alerts_authorization'; import { ActionsAuthorization } from '../../../../actions/server'; -import { getBeforeSetup, setGlobalDate } from './lib'; +import { getBeforeSetup } from './lib'; const taskManager = taskManagerMock.createStart(); const alertTypeRegistry = alertTypeRegistryMock.create(); @@ -44,8 +44,6 @@ beforeEach(() => { getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry); }); -setGlobalDate(); - describe('unmuteAll()', () => { test('unmutes an alert', async () => { const alertsClient = new AlertsClient(alertsClientParams); @@ -77,7 +75,6 @@ describe('unmuteAll()', () => { { muteAll: false, mutedInstanceIds: [], - updatedAt: '2019-02-12T21:01:22.479Z', updatedBy: 'elastic', }, { diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/unmute_instance.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/unmute_instance.test.ts index 3486df98f2f057..88692239ac2fe4 100644 --- a/x-pack/plugins/alerts/server/alerts_client/tests/unmute_instance.test.ts +++ b/x-pack/plugins/alerts/server/alerts_client/tests/unmute_instance.test.ts @@ -12,7 +12,7 @@ import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/s import { actionsAuthorizationMock } from '../../../../actions/server/mocks'; import { AlertsAuthorization } from '../../authorization/alerts_authorization'; import { ActionsAuthorization } from '../../../../actions/server'; -import { getBeforeSetup, setGlobalDate } from './lib'; +import { getBeforeSetup } from './lib'; const taskManager = taskManagerMock.createStart(); const alertTypeRegistry = alertTypeRegistryMock.create(); @@ -44,8 +44,6 @@ beforeEach(() => { getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry); }); -setGlobalDate(); - describe('unmuteInstance()', () => { test('unmutes an alert instance', async () => { const alertsClient = new AlertsClient(alertsClientParams); @@ -71,7 +69,6 @@ describe('unmuteInstance()', () => { { mutedInstanceIds: [], updatedBy: 'elastic', - updatedAt: '2019-02-12T21:01:22.479Z', }, { version: '123' } ); diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/update.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/update.test.ts index d0bb2607f7a47a..ad58e36ade7225 100644 --- a/x-pack/plugins/alerts/server/alerts_client/tests/update.test.ts +++ b/x-pack/plugins/alerts/server/alerts_client/tests/update.test.ts @@ -140,8 +140,8 @@ describe('update()', () => { ], scheduledTaskId: 'task-123', createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), }, + updated_at: new Date().toISOString(), references: [ { name: 'action_0', @@ -300,7 +300,6 @@ describe('update()', () => { "foo", ], "throttle": null, - "updatedAt": "2019-02-12T21:01:22.479Z", "updatedBy": "elastic", } `); @@ -363,7 +362,6 @@ describe('update()', () => { bar: true, }, createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), actions: [ { group: 'default', @@ -486,7 +484,6 @@ describe('update()', () => { "foo", ], "throttle": "5m", - "updatedAt": "2019-02-12T21:01:22.479Z", "updatedBy": "elastic", } `); @@ -537,7 +534,6 @@ describe('update()', () => { bar: true, }, createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), actions: [ { group: 'default', @@ -652,7 +648,6 @@ describe('update()', () => { "foo", ], "throttle": "5m", - "updatedAt": "2019-02-12T21:01:22.479Z", "updatedBy": "elastic", } `); diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/update_api_key.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/update_api_key.test.ts index ca5f44078f5138..af178a1fac5f5d 100644 --- a/x-pack/plugins/alerts/server/alerts_client/tests/update_api_key.test.ts +++ b/x-pack/plugins/alerts/server/alerts_client/tests/update_api_key.test.ts @@ -12,7 +12,7 @@ import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/s import { actionsAuthorizationMock } from '../../../../actions/server/mocks'; import { AlertsAuthorization } from '../../authorization/alerts_authorization'; import { ActionsAuthorization } from '../../../../actions/server'; -import { getBeforeSetup, setGlobalDate } from './lib'; +import { getBeforeSetup } from './lib'; import { InvalidatePendingApiKey } from '../../types'; const taskManager = taskManagerMock.createStart(); @@ -44,8 +44,6 @@ beforeEach(() => { getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry); }); -setGlobalDate(); - describe('updateApiKey()', () => { let alertsClient: AlertsClient; const existingAlert = { @@ -115,7 +113,6 @@ describe('updateApiKey()', () => { apiKey: Buffer.from('234:abc').toString('base64'), apiKeyOwner: 'elastic', updatedBy: 'elastic', - updatedAt: '2019-02-12T21:01:22.479Z', actions: [ { group: 'default', @@ -165,7 +162,6 @@ describe('updateApiKey()', () => { enabled: true, apiKey: Buffer.from('234:abc').toString('base64'), apiKeyOwner: 'elastic', - updatedAt: '2019-02-12T21:01:22.479Z', updatedBy: 'elastic', actions: [ { diff --git a/x-pack/plugins/alerts/server/saved_objects/index.ts b/x-pack/plugins/alerts/server/saved_objects/index.ts index dfe122f56bc485..da30273e93c6b3 100644 --- a/x-pack/plugins/alerts/server/saved_objects/index.ts +++ b/x-pack/plugins/alerts/server/saved_objects/index.ts @@ -16,7 +16,6 @@ export const AlertAttributesExcludedFromAAD = [ 'muteAll', 'mutedInstanceIds', 'updatedBy', - 'updatedAt', 'executionStatus', ]; @@ -29,7 +28,6 @@ export type AlertAttributesExcludedFromAADType = | 'muteAll' | 'mutedInstanceIds' | 'updatedBy' - | 'updatedAt' | 'executionStatus'; export function setupSavedObjects( diff --git a/x-pack/plugins/alerts/server/saved_objects/mappings.json b/x-pack/plugins/alerts/server/saved_objects/mappings.json index f40a7d9075eed2..a6c92080f18be5 100644 --- a/x-pack/plugins/alerts/server/saved_objects/mappings.json +++ b/x-pack/plugins/alerts/server/saved_objects/mappings.json @@ -62,9 +62,6 @@ "createdAt": { "type": "date" }, - "updatedAt": { - "type": "date" - }, "apiKey": { "type": "binary" }, diff --git a/x-pack/plugins/alerts/server/saved_objects/migrations.test.ts b/x-pack/plugins/alerts/server/saved_objects/migrations.test.ts index a4cbc18e13b473..8c9d10769b18a1 100644 --- a/x-pack/plugins/alerts/server/saved_objects/migrations.test.ts +++ b/x-pack/plugins/alerts/server/saved_objects/migrations.test.ts @@ -261,48 +261,8 @@ describe('7.10.0 migrates with failure', () => { }); }); -describe('7.11.0', () => { - beforeEach(() => { - jest.resetAllMocks(); - encryptedSavedObjectsSetup.createMigration.mockImplementation( - (shouldMigrateWhenPredicate, migration) => migration - ); - }); - - test('add updatedAt field to alert - set to SavedObject updated_at attribute', () => { - const migration711 = getMigrations(encryptedSavedObjectsSetup)['7.11.0']; - const alert = getMockData({}, true); - expect(migration711(alert, { log })).toEqual({ - ...alert, - attributes: { - ...alert.attributes, - updatedAt: alert.updated_at, - }, - }); - }); - - test('add updatedAt field to alert - set to createdAt when SavedObject updated_at is not defined', () => { - const migration711 = getMigrations(encryptedSavedObjectsSetup)['7.11.0']; - const alert = getMockData({}); - expect(migration711(alert, { log })).toEqual({ - ...alert, - attributes: { - ...alert.attributes, - updatedAt: alert.attributes.createdAt, - }, - }); - }); -}); - -function getUpdatedAt(): string { - const updatedAt = new Date(); - updatedAt.setHours(updatedAt.getHours() + 2); - return updatedAt.toISOString(); -} - function getMockData( - overwrites: Record = {}, - withSavedObjectUpdatedAt: boolean = false + overwrites: Record = {} ): SavedObjectUnsanitizedDoc> { return { attributes: { @@ -335,7 +295,6 @@ function getMockData( ], ...overwrites, }, - updated_at: withSavedObjectUpdatedAt ? getUpdatedAt() : undefined, id: uuid.v4(), type: 'alert', }; diff --git a/x-pack/plugins/alerts/server/saved_objects/migrations.ts b/x-pack/plugins/alerts/server/saved_objects/migrations.ts index d8ebced03c5a62..0b2c86b84f67b3 100644 --- a/x-pack/plugins/alerts/server/saved_objects/migrations.ts +++ b/x-pack/plugins/alerts/server/saved_objects/migrations.ts @@ -37,15 +37,8 @@ export function getMigrations( ) ); - const migrationAlertUpdatedAtDate = encryptedSavedObjects.createMigration( - // migrate all documents in 7.11 in order to add the "updatedAt" field - (doc): doc is SavedObjectUnsanitizedDoc => true, - pipeMigrations(setAlertUpdatedAtDate) - ); - return { '7.10.0': executeMigrationWithErrorHandling(migrationWhenRBACWasIntroduced, '7.10.0'), - '7.11.0': executeMigrationWithErrorHandling(migrationAlertUpdatedAtDate, '7.11.0'), }; } @@ -66,19 +59,6 @@ function executeMigrationWithErrorHandling( }; } -const setAlertUpdatedAtDate = ( - doc: SavedObjectUnsanitizedDoc -): SavedObjectUnsanitizedDoc => { - const updatedAt = doc.updated_at || doc.attributes.createdAt; - return { - ...doc, - attributes: { - ...doc.attributes, - updatedAt, - }, - }; -}; - const consumersToChange: Map = new Map( Object.entries({ alerting: 'alerts', diff --git a/x-pack/plugins/alerts/server/saved_objects/partially_update_alert.test.ts b/x-pack/plugins/alerts/server/saved_objects/partially_update_alert.test.ts index 8041ec551bb0df..50815c797e399b 100644 --- a/x-pack/plugins/alerts/server/saved_objects/partially_update_alert.test.ts +++ b/x-pack/plugins/alerts/server/saved_objects/partially_update_alert.test.ts @@ -95,7 +95,6 @@ const DefaultAttributes = { muteAll: true, mutedInstanceIds: ['muted-instance-id-1', 'muted-instance-id-2'], updatedBy: 'someone', - updatedAt: '2019-02-12T21:01:22.479Z', }; const InvalidAttributes = { ...DefaultAttributes, foo: 'bar' }; diff --git a/x-pack/plugins/alerts/server/types.ts b/x-pack/plugins/alerts/server/types.ts index 4ccf251540a154..dde16281566580 100644 --- a/x-pack/plugins/alerts/server/types.ts +++ b/x-pack/plugins/alerts/server/types.ts @@ -148,7 +148,6 @@ export interface RawAlert extends SavedObjectAttributes { createdBy: string | null; updatedBy: string | null; createdAt: string; - updatedAt: string; apiKey: string | null; apiKeyOwner: string | null; throttle: string | null; diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts index cf7fc9edd9529d..41f6b66c30aafa 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts @@ -91,7 +91,6 @@ export default function createAlertTests({ getService }: FtrProviderContext) { }); expect(Date.parse(response.body.createdAt)).to.be.greaterThan(0); expect(Date.parse(response.body.updatedAt)).to.be.greaterThan(0); - expect(Date.parse(response.body.updatedAt)).to.eql(Date.parse(response.body.createdAt)); expect(typeof response.body.scheduledTaskId).to.be('string'); const { _source: taskRecord } = await getScheduledTask(response.body.scheduledTaskId); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/execution_status.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/execution_status.ts index 642173a7c2c6cb..5ebce8edf6fb74 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/execution_status.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/execution_status.ts @@ -63,7 +63,6 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon ); expect(response.status).to.eql(200); const alertId = response.body.id; - const alertUpdatedAt = response.body.updatedAt; dates.push(response.body.executionStatus.lastExecutionDate); objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts'); @@ -71,7 +70,6 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon dates.push(executionStatus.lastExecutionDate); dates.push(Date.now()); ensureDatetimesAreOrdered(dates); - ensureAlertUpdatedAtHasNotChanged(alertId, alertUpdatedAt); // Ensure AAD isn't broken await checkAAD({ @@ -99,7 +97,6 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon ); expect(response.status).to.eql(200); const alertId = response.body.id; - const alertUpdatedAt = response.body.updatedAt; dates.push(response.body.executionStatus.lastExecutionDate); objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts'); @@ -107,7 +104,6 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon dates.push(executionStatus.lastExecutionDate); dates.push(Date.now()); ensureDatetimesAreOrdered(dates); - ensureAlertUpdatedAtHasNotChanged(alertId, alertUpdatedAt); // Ensure AAD isn't broken await checkAAD({ @@ -132,7 +128,6 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon ); expect(response.status).to.eql(200); const alertId = response.body.id; - const alertUpdatedAt = response.body.updatedAt; dates.push(response.body.executionStatus.lastExecutionDate); objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts'); @@ -140,7 +135,6 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon dates.push(executionStatus.lastExecutionDate); dates.push(Date.now()); ensureDatetimesAreOrdered(dates); - ensureAlertUpdatedAtHasNotChanged(alertId, alertUpdatedAt); // Ensure AAD isn't broken await checkAAD({ @@ -168,14 +162,12 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon ); expect(response.status).to.eql(200); const alertId = response.body.id; - const alertUpdatedAt = response.body.updatedAt; objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts'); const executionStatus = await waitForStatus(alertId, new Set(['error'])); expect(executionStatus.error).to.be.ok(); expect(executionStatus.error.reason).to.be('execute'); expect(executionStatus.error.message).to.be('this alert is intended to fail'); - ensureAlertUpdatedAtHasNotChanged(alertId, alertUpdatedAt); }); it('should eventually have error reason "unknown" when appropriate', async () => { @@ -191,7 +183,6 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon ); expect(response.status).to.eql(200); const alertId = response.body.id; - const alertUpdatedAt = response.body.updatedAt; objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts'); let executionStatus = await waitForStatus(alertId, new Set(['ok'])); @@ -210,7 +201,6 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon executionStatus = await waitForStatus(alertId, new Set(['error'])); expect(executionStatus.error).to.be.ok(); expect(executionStatus.error.reason).to.be('unknown'); - ensureAlertUpdatedAtHasNotChanged(alertId, alertUpdatedAt); const message = 'params invalid: [param1]: expected value of type [string] but got [number]'; expect(executionStatus.error.message).to.be(message); @@ -316,18 +306,6 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon await delay(WaitForStatusIncrement); return await waitForStatus(id, statuses, waitMillis - WaitForStatusIncrement); } - - async function ensureAlertUpdatedAtHasNotChanged(alertId: string, originalUpdatedAt: string) { - const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${alertId}` - ); - const { updatedAt, executionStatus } = response.body; - expect(Date.parse(updatedAt)).to.be.greaterThan(0); - expect(Date.parse(updatedAt)).to.eql(Date.parse(originalUpdatedAt)); - expect(Date.parse(executionStatus.lastExecutionDate)).to.be.greaterThan( - Date.parse(originalUpdatedAt) - ); - } } function expectErrorExecutionStatus(executionStatus: Record, startDate: number) { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/migrations.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/migrations.ts index bd6afacf206d9d..17070a14069ce5 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/migrations.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/migrations.ts @@ -82,14 +82,5 @@ export default function createGetTests({ getService }: FtrProviderContext) { }, ]); }); - - it('7.11.0 migrates alerts to contain `updatedAt` field', async () => { - const response = await supertest.get( - `${getUrlPrefix(``)}/api/alerts/alert/74f3e6d7-b7bb-477d-ac28-92ee22728e6e` - ); - - expect(response.status).to.eql(200); - expect(response.body.updatedAt).to.eql('2020-06-17T15:35:39.839Z'); - }); }); } From 514b50e4c2d7a3be79d77e73838ff57b6cf1304a Mon Sep 17 00:00:00 2001 From: John Schulz Date: Thu, 19 Nov 2020 07:19:41 -0500 Subject: [PATCH 15/16] Forward any registry cache-control header for files (#83680) closes #83631 ### Problem Assets are served with a `cache-control` header that prevents any caching ### Root cause Likely from this code https://github.com/elastic/kibana/blob/2a365ff6329544465227e61141ded6fba8bb2c80/src/core/server/http/http_tools.ts#L40-L43 Also based on these tests, it seems this is default/expected behavior https://github.com/elastic/kibana/blob/b3eefb97da8e712789b5c5d2eeae65c886ed8f64/src/core/server/http/integration_tests/router.test.ts#L510-L520 ### Proposed solution Set the header via the response handler as shown in this test: https://github.com/elastic/kibana/blob/b3eefb97da8e712789b5c5d2eeae65c886ed8f64/src/core/server/http/integration_tests/router.test.ts#L522-L536 ### This PR If this registry response contains a `cache-control` header, that value is included in the EPM response as well In `master`, which points to `epr-snapshot` Screen Shot 2020-11-18 at 12 33 47 PM which matches https://epr-snapshot.elastic.co/package/apache/0.2.6/img/logo_apache.svg or using `epr.elastic.co`, Screen Shot 2020-11-18 at 12 31 56 PM which matches https://epr.elastic.co/package/apache/0.2.6/img/logo_apache.svg --- .../fleet/server/routes/epm/handlers.ts | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/fleet/server/routes/epm/handlers.ts b/x-pack/plugins/fleet/server/routes/epm/handlers.ts index 1d221b8b1eeadd..ce03d0eeb38269 100644 --- a/x-pack/plugins/fleet/server/routes/epm/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/epm/handlers.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { TypeOf } from '@kbn/config-schema'; -import { RequestHandler, CustomHttpResponseOptions } from 'src/core/server'; +import { RequestHandler, ResponseHeaders, KnownHeaders } from 'src/core/server'; import { GetInfoResponse, InstallPackageResponse, @@ -103,15 +103,21 @@ export const getFileHandler: RequestHandler = { + + const headersToProxy: KnownHeaders[] = ['content-type', 'cache-control']; + const proxiedHeaders = headersToProxy.reduce((headers, knownHeader) => { + const value = registryResponse.headers.get(knownHeader); + if (value !== null) { + headers[knownHeader] = value; + } + return headers; + }, {} as ResponseHeaders); + + return response.custom({ body: registryResponse.body, statusCode: registryResponse.status, - }; - if (contentType !== null) { - customResponseObj.headers = { 'Content-Type': contentType }; - } - return response.custom(customResponseObj); + headers: proxiedHeaders, + }); } catch (error) { return defaultIngestErrorHandler({ error, response }); } From ffdc507668740ec7af69c9f45e3dc7793507092f Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Thu, 19 Nov 2020 12:50:26 +0000 Subject: [PATCH 16/16] fixed pagination in connectors list (#83638) Ensures we specify the page on the EuiTable so that pagination is retain after rerenders. --- .../actions_connectors_list.test.tsx | 88 +++++++++++++------ .../components/actions_connectors_list.tsx | 12 ++- 2 files changed, 72 insertions(+), 28 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx index 71e1c60a92aedf..226b9de8b677f2 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx @@ -16,6 +16,8 @@ import { chartPluginMock } from '../../../../../../../../src/plugins/charts/publ import { dataPluginMock } from '../../../../../../../../src/plugins/data/public/mocks'; import { alertingPluginMock } from '../../../../../../alerts/public/mocks'; import { featuresPluginMock } from '../../../../../../features/public/mocks'; +import { ActionConnector } from '../../../../types'; +import { times } from 'lodash'; jest.mock('../../../lib/action_connector_api', () => ({ loadAllActions: jest.fn(), @@ -109,36 +111,38 @@ describe('actions_connectors_list component empty', () => { describe('actions_connectors_list component with items', () => { let wrapper: ReactWrapper; - async function setup() { + async function setup(actionConnectors?: ActionConnector[]) { const { loadAllActions, loadActionTypes } = jest.requireMock( '../../../lib/action_connector_api' ); - loadAllActions.mockResolvedValueOnce([ - { - id: '1', - actionTypeId: 'test', - description: 'My test', - isPreconfigured: false, - referencedByCount: 1, - config: {}, - }, - { - id: '2', - actionTypeId: 'test2', - description: 'My test 2', - referencedByCount: 1, - isPreconfigured: false, - config: {}, - }, - { - id: '3', - actionTypeId: 'test2', - description: 'My preconfigured test 2', - referencedByCount: 1, - isPreconfigured: true, - config: {}, - }, - ]); + loadAllActions.mockResolvedValueOnce( + actionConnectors ?? [ + { + id: '1', + actionTypeId: 'test', + description: 'My test', + isPreconfigured: false, + referencedByCount: 1, + config: {}, + }, + { + id: '2', + actionTypeId: 'test2', + description: 'My test 2', + referencedByCount: 1, + isPreconfigured: false, + config: {}, + }, + { + id: '3', + actionTypeId: 'test2', + description: 'My preconfigured test 2', + referencedByCount: 1, + isPreconfigured: true, + config: {}, + }, + ] + ); loadActionTypes.mockResolvedValueOnce([ { id: 'test', @@ -217,6 +221,36 @@ describe('actions_connectors_list component with items', () => { expect(wrapper.find('[data-test-subj="preConfiguredTitleMessage"]')).toHaveLength(2); }); + it('supports pagination', async () => { + await setup( + times(15, (index) => ({ + id: `connector${index}`, + actionTypeId: 'test', + name: `My test ${index}`, + secrets: {}, + description: `My test ${index}`, + isPreconfigured: false, + referencedByCount: 1, + config: {}, + })) + ); + expect(wrapper.find('[data-test-subj="actionsTable"]').first().prop('pagination')) + .toMatchInlineSnapshot(` + Object { + "initialPageIndex": 0, + "pageIndex": 0, + } + `); + wrapper.find('[data-test-subj="pagination-button-1"]').first().simulate('click'); + expect(wrapper.find('[data-test-subj="actionsTable"]').first().prop('pagination')) + .toMatchInlineSnapshot(` + Object { + "initialPageIndex": 0, + "pageIndex": 1, + } + `); + }); + test('if select item for edit should render ConnectorEditFlyout', async () => { await setup(); await wrapper.find('[data-test-subj="edit1"]').first().simulate('click'); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx index ff5585cf04dbe2..c5d0a6aae54fc6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx @@ -18,6 +18,7 @@ import { EuiToolTip, EuiButtonIcon, EuiEmptyPrompt, + Criteria, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { omit } from 'lodash'; @@ -54,6 +55,7 @@ export const ActionsConnectorsList: React.FunctionComponent = () => { const [actionTypesIndex, setActionTypesIndex] = useState(undefined); const [actions, setActions] = useState([]); + const [pageIndex, setPageIndex] = useState(0); const [selectedItems, setSelectedItems] = useState([]); const [isLoadingActionTypes, setIsLoadingActionTypes] = useState(false); const [isLoadingActions, setIsLoadingActions] = useState(false); @@ -233,7 +235,15 @@ export const ActionsConnectorsList: React.FunctionComponent = () => { : '', })} data-test-subj="actionsTable" - pagination={true} + pagination={{ + initialPageIndex: 0, + pageIndex, + }} + onTableChange={({ page }: Criteria) => { + if (page) { + setPageIndex(page.index); + } + }} selection={ canDelete ? {