From 586682a0c480a37e04aa717aceb2dcfa39835495 Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Thu, 14 Oct 2021 15:49:35 +0200 Subject: [PATCH 001/100] [Discover] Rename default column in the advanced settings (#114100) * [Discover] Rename default column in the advanced settings * Fix eslint * Rename default column to an empty string * Fix typo * Fix default column filtering * Update comment * Make an empty array a default columns * Improve functional test * Wording change Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../apps/main/utils/get_state_defaults.ts | 11 +++++++-- .../application/helpers/state_helpers.ts | 7 +++++- src/plugins/discover/server/ui_settings.ts | 5 ++-- .../apps/discover/_discover_fields_api.ts | 24 ++++++++++++++++++- 4 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/plugins/discover/public/application/apps/main/utils/get_state_defaults.ts b/src/plugins/discover/public/application/apps/main/utils/get_state_defaults.ts index 11ebf0ecf9af4..f2f6e4a002aaf 100644 --- a/src/plugins/discover/public/application/apps/main/utils/get_state_defaults.ts +++ b/src/plugins/discover/public/application/apps/main/utils/get_state_defaults.ts @@ -6,9 +6,13 @@ * Side Public License, v 1. */ -import { cloneDeep } from 'lodash'; +import { cloneDeep, isEqual } from 'lodash'; import { IUiSettingsClient } from 'kibana/public'; -import { DEFAULT_COLUMNS_SETTING, SORT_DEFAULT_ORDER_SETTING } from '../../../../../common'; +import { + DEFAULT_COLUMNS_SETTING, + SEARCH_FIELDS_FROM_SOURCE, + SORT_DEFAULT_ORDER_SETTING, +} from '../../../../../common'; import { SavedSearch } from '../../../../saved_searches'; import { DataPublicPluginStart } from '../../../../../../data/public'; @@ -19,6 +23,9 @@ function getDefaultColumns(savedSearch: SavedSearch, config: IUiSettingsClient) if (savedSearch.columns && savedSearch.columns.length > 0) { return [...savedSearch.columns]; } + if (config.get(SEARCH_FIELDS_FROM_SOURCE) && isEqual(config.get(DEFAULT_COLUMNS_SETTING), [])) { + return ['_source']; + } return [...config.get(DEFAULT_COLUMNS_SETTING)]; } diff --git a/src/plugins/discover/public/application/helpers/state_helpers.ts b/src/plugins/discover/public/application/helpers/state_helpers.ts index fd17ec9516ab5..bb64f823d61a6 100644 --- a/src/plugins/discover/public/application/helpers/state_helpers.ts +++ b/src/plugins/discover/public/application/helpers/state_helpers.ts @@ -24,6 +24,7 @@ export function handleSourceColumnState( } const useNewFieldsApi = !uiSettings.get(SEARCH_FIELDS_FROM_SOURCE); const defaultColumns = uiSettings.get(DEFAULT_COLUMNS_SETTING); + if (useNewFieldsApi) { // if fields API is used, filter out the source column let cleanedColumns = state.columns.filter((column) => column !== '_source'); @@ -39,9 +40,13 @@ export function handleSourceColumnState( } else if (state.columns.length === 0) { // if _source fetching is used and there are no column, switch back to default columns // this can happen if the fields API was previously used + const columns = defaultColumns; + if (columns.length === 0) { + columns.push('_source'); + } return { ...state, - columns: [...defaultColumns], + columns: [...columns], }; } diff --git a/src/plugins/discover/server/ui_settings.ts b/src/plugins/discover/server/ui_settings.ts index aa1b44da12bfc..82221fb8e8593 100644 --- a/src/plugins/discover/server/ui_settings.ts +++ b/src/plugins/discover/server/ui_settings.ts @@ -33,9 +33,10 @@ export const getUiSettings: () => Record = () => ({ name: i18n.translate('discover.advancedSettings.defaultColumnsTitle', { defaultMessage: 'Default columns', }), - value: ['_source'], + value: [], description: i18n.translate('discover.advancedSettings.defaultColumnsText', { - defaultMessage: 'Columns displayed by default in the Discovery tab', + defaultMessage: + 'Columns displayed by default in the Discover app. If empty, a summary of the document will be displayed.', }), category: ['discover'], schema: schema.arrayOf(schema.string()), diff --git a/test/functional/apps/discover/_discover_fields_api.ts b/test/functional/apps/discover/_discover_fields_api.ts index 42e2a94b36462..700c865031cd6 100644 --- a/test/functional/apps/discover/_discover_fields_api.ts +++ b/test/functional/apps/discover/_discover_fields_api.ts @@ -15,7 +15,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); - const PageObjects = getPageObjects(['common', 'discover', 'header', 'timePicker']); + const PageObjects = getPageObjects(['common', 'discover', 'header', 'timePicker', 'settings']); const defaultSettings = { defaultIndex: 'logstash-*', 'discover:searchFieldsFromSource': false, @@ -67,5 +67,27 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.discover.clickDocViewerTab(1); await PageObjects.discover.expectSourceViewerToExist(); }); + + it('switches to _source column when fields API is no longer used', async function () { + await PageObjects.settings.navigateTo(); + await PageObjects.settings.clickKibanaSettings(); + await PageObjects.settings.toggleAdvancedSettingCheckbox('discover:searchFieldsFromSource'); + + await PageObjects.common.navigateToApp('discover'); + await PageObjects.timePicker.setDefaultAbsoluteRange(); + + expect(await PageObjects.discover.getDocHeader()).to.have.string('_source'); + }); + + it('switches to Document column when fields API is used', async function () { + await PageObjects.settings.navigateTo(); + await PageObjects.settings.clickKibanaSettings(); + await PageObjects.settings.toggleAdvancedSettingCheckbox('discover:searchFieldsFromSource'); + + await PageObjects.common.navigateToApp('discover'); + await PageObjects.timePicker.setDefaultAbsoluteRange(); + + expect(await PageObjects.discover.getDocHeader()).to.have.string('Document'); + }); }); } From cdce98c8a363520e99f59f3f10c59a32586480a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ester=20Mart=C3=AD=20Vilaseca?= Date: Thu, 14 Oct 2021 15:57:27 +0200 Subject: [PATCH 002/100] [Stack monitoring] Fix clusters functional tests when react is enabled (#114982) * Fix test subjects for overview page * fix pathname matching --- .../public/application/pages/cluster/overview_page.tsx | 2 +- x-pack/plugins/monitoring/public/application/route_init.tsx | 2 +- x-pack/plugins/monitoring/public/directives/main/index.html | 2 +- x-pack/test/functional/services/monitoring/cluster_overview.js | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/monitoring/public/application/pages/cluster/overview_page.tsx b/x-pack/plugins/monitoring/public/application/pages/cluster/overview_page.tsx index b78df27cd12c4..04074762c8d22 100644 --- a/x-pack/plugins/monitoring/public/application/pages/cluster/overview_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/cluster/overview_page.tsx @@ -50,7 +50,7 @@ export const ClusterOverview: React.FC<{}> = () => { { id: 'clusterName', label: clusters[0].cluster_name, - testSubj: 'clusterName', + testSubj: 'overviewTabsclusterName', route: '/overview', }, ]; diff --git a/x-pack/plugins/monitoring/public/application/route_init.tsx b/x-pack/plugins/monitoring/public/application/route_init.tsx index c620229eb059a..52780aa280707 100644 --- a/x-pack/plugins/monitoring/public/application/route_init.tsx +++ b/x-pack/plugins/monitoring/public/application/route_init.tsx @@ -57,7 +57,7 @@ export const RouteInit: React.FC = ({ // check if we need to redirect because of attempt at unsupported multi-cluster monitoring const clusterSupported = cluster.isSupported || clusters.length === 1; - if (location.pathname !== 'home' && !clusterSupported) { + if (location.pathname !== '/home' && !clusterSupported) { return ; } } diff --git a/x-pack/plugins/monitoring/public/directives/main/index.html b/x-pack/plugins/monitoring/public/directives/main/index.html index c989c71d8c1d4..fd14120e1db2f 100644 --- a/x-pack/plugins/monitoring/public/directives/main/index.html +++ b/x-pack/plugins/monitoring/public/directives/main/index.html @@ -299,7 +299,7 @@

{{pageTitle || monitoringMain.instance}}

= ({ hideSubtitle = false, }) => (
- + - + -

+

{title} {tooltip && ( <> @@ -81,7 +81,7 @@ const HeaderSectionComponent: React.FC = ({ )} -

+
{!hideSubtitle && ( diff --git a/x-pack/plugins/security_solution/public/common/components/hover_actions/actions/show_top_n.tsx b/x-pack/plugins/security_solution/public/common/components/hover_actions/actions/show_top_n.tsx index e1546c5220e22..738103f02dcdf 100644 --- a/x-pack/plugins/security_solution/public/common/components/hover_actions/actions/show_top_n.tsx +++ b/x-pack/plugins/security_solution/public/common/components/hover_actions/actions/show_top_n.tsx @@ -30,33 +30,49 @@ const SHOW_TOP = (fieldName: string) => }); interface Props { + className?: string; /** When `Component` is used with `EuiDataGrid`; the grid keeps a reference to `Component` for show / hide functionality. * When `Component` is used with `EuiContextMenu`, we pass EuiContextMenuItem to render the right style. */ Component?: typeof EuiButtonEmpty | typeof EuiButtonIcon | typeof EuiContextMenuItem; enablePopOver?: boolean; field: string; + flush?: 'left' | 'right' | 'both'; globalFilters?: Filter[]; + iconSide?: 'left' | 'right'; + iconType?: string; + isExpandable?: boolean; onClick: () => void; onFilterAdded?: () => void; ownFocus: boolean; + paddingSize?: 's' | 'm' | 'l' | 'none'; showTooltip?: boolean; showTopN: boolean; + showLegend?: boolean; timelineId?: string | null; + title?: string; value?: string[] | string | null; } export const ShowTopNButton: React.FC = React.memo( ({ + className, Component, enablePopOver, field, + flush, + iconSide, + iconType, + isExpandable, onClick, onFilterAdded, ownFocus, + paddingSize, + showLegend, showTooltip = true, showTopN, timelineId, + title, value, globalFilters, }) => { @@ -70,31 +86,36 @@ export const ShowTopNButton: React.FC = React.memo( ? SourcererScopeName.detections : SourcererScopeName.default; const { browserFields, indexPattern } = useSourcererScope(activeScope); - + const icon = iconType ?? 'visBarVertical'; + const side = iconSide ?? 'left'; + const buttonTitle = title ?? SHOW_TOP(field); const basicButton = useMemo( () => Component ? ( - {SHOW_TOP(field)} + {buttonTitle} ) : ( ), - [Component, field, onClick] + [Component, buttonTitle, className, flush, icon, onClick, side] ); const button = useMemo( @@ -107,7 +128,7 @@ export const ShowTopNButton: React.FC = React.memo( field, value, })} - content={SHOW_TOP(field)} + content={buttonTitle} shortcut={SHOW_TOP_N_KEYBOARD_SHORTCUT} showShortcut={ownFocus} /> @@ -118,7 +139,7 @@ export const ShowTopNButton: React.FC = React.memo( ) : ( basicButton ), - [basicButton, field, ownFocus, showTooltip, showTopN, value] + [basicButton, buttonTitle, field, ownFocus, showTooltip, showTopN, value] ); const topNPannel = useMemo( @@ -128,15 +149,37 @@ export const ShowTopNButton: React.FC = React.memo( field={field} indexPattern={indexPattern} onFilterAdded={onFilterAdded} + paddingSize={paddingSize} + showLegend={showLegend} timelineId={timelineId ?? undefined} toggleTopN={onClick} value={value} globalFilters={globalFilters} /> ), - [browserFields, field, indexPattern, onClick, onFilterAdded, timelineId, value, globalFilters] + [ + browserFields, + field, + indexPattern, + onFilterAdded, + paddingSize, + showLegend, + timelineId, + onClick, + value, + globalFilters, + ] ); + if (isExpandable) { + return ( + <> + {basicButton} + {showTopN && topNPannel} + + ); + } + return showTopN ? ( enablePopOver ? ( | PropsForAnchor> = + ({ children, ...props }) => {children}; + +export const LinkAnchor: React.FC = ({ children, ...props }) => ( + {children} +); + +export const Comma = styled('span')` + margin-right: 5px; + margin-left: 5px; + &::after { + content: ' ,'; + } +`; + +Comma.displayName = 'Comma'; + +const GenericLinkButtonComponent: React.FC<{ + children?: React.ReactNode; + /** `Component` is only used with `EuiDataGrid`; the grid keeps a reference to `Component` for show / hide functionality */ + Component?: typeof EuiButtonEmpty | typeof EuiButtonIcon; + dataTestSubj?: string; + href: string; + onClick?: (e: SyntheticEvent) => void; + title?: string; + iconType?: string; +}> = ({ children, Component, dataTestSubj, href, onClick, title, iconType = 'expand' }) => { + return Component ? ( + + {title ?? children} + + ) : ( + + {title ?? children} + + ); +}; + +export const GenericLinkButton = React.memo(GenericLinkButtonComponent); + +export const PortContainer = styled.div` + & svg { + position: relative; + top: -1px; + } +`; diff --git a/x-pack/plugins/security_solution/public/common/components/links/index.tsx b/x-pack/plugins/security_solution/public/common/components/links/index.tsx index 7db6b0204b649..c74791b8b3aa7 100644 --- a/x-pack/plugins/security_solution/public/common/components/links/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/links/index.tsx @@ -6,19 +6,15 @@ */ import { - EuiButton, - EuiButtonProps, - EuiLink, - EuiLinkProps, - EuiToolTip, + EuiButtonEmpty, + EuiButtonIcon, EuiFlexGroup, EuiFlexItem, - PropsForAnchor, - PropsForButton, + EuiLink, + EuiToolTip, } from '@elastic/eui'; import React, { useMemo, useCallback, SyntheticEvent } from 'react'; import { isNil } from 'lodash/fp'; -import styled from 'styled-components'; import { IP_REPUTATION_LINKS_SETTING, APP_ID } from '../../../../common/constants'; import { @@ -43,22 +39,11 @@ import { isUrlInvalid } from '../../utils/validators'; import * as i18n from './translations'; import { SecurityPageName } from '../../../app/types'; import { getUebaDetailsUrl } from '../link_to/redirect_to_ueba'; +import { LinkButton, LinkAnchor, GenericLinkButton, PortContainer, Comma } from './helpers'; -export const DEFAULT_NUMBER_OF_LINK = 5; - -export const LinkButton: React.FC | PropsForAnchor> = - ({ children, ...props }) => {children}; +export { LinkButton, LinkAnchor } from './helpers'; -export const LinkAnchor: React.FC = ({ children, ...props }) => ( - {children} -); - -export const PortContainer = styled.div` - & svg { - position: relative; - top: -1px; - } -`; +export const DEFAULT_NUMBER_OF_LINK = 5; // Internal Links const UebaDetailsLinkComponent: React.FC<{ @@ -102,10 +87,13 @@ export const UebaDetailsLink = React.memo(UebaDetailsLinkComponent); const HostDetailsLinkComponent: React.FC<{ children?: React.ReactNode; + /** `Component` is only used with `EuiDataGrid`; the grid keeps a reference to `Component` for show / hide functionality */ + Component?: typeof EuiButtonEmpty | typeof EuiButtonIcon; hostName: string; isButton?: boolean; onClick?: (e: SyntheticEvent) => void; -}> = ({ children, hostName, isButton, onClick }) => { + title?: string; +}> = ({ children, Component, hostName, isButton, onClick, title }) => { const { formatUrl, search } = useFormatUrl(SecurityPageName.hosts); const { navigateToApp } = useKibana().services.application; const goToHostDetails = useCallback( @@ -118,19 +106,25 @@ const HostDetailsLinkComponent: React.FC<{ }, [hostName, navigateToApp, search] ); - + const href = useMemo( + () => formatUrl(getHostDetailsUrl(encodeURIComponent(hostName))), + [formatUrl, hostName] + ); return isButton ? ( - - {children ? children : hostName} - + {children} + ) : ( {children ? children : hostName} @@ -176,11 +170,14 @@ ExternalLink.displayName = 'ExternalLink'; const NetworkDetailsLinkComponent: React.FC<{ children?: React.ReactNode; + /** `Component` is only used with `EuiDataGrid`; the grid keeps a reference to `Component` for show / hide functionality */ + Component?: typeof EuiButtonEmpty | typeof EuiButtonIcon; ip: string; flowTarget?: FlowTarget | FlowTargetSourceDest; isButton?: boolean; onClick?: (e: SyntheticEvent) => void | undefined; -}> = ({ children, ip, flowTarget = FlowTarget.source, isButton, onClick }) => { + title?: string; +}> = ({ Component, children, ip, flowTarget = FlowTarget.source, isButton, onClick, title }) => { const { formatUrl, search } = useFormatUrl(SecurityPageName.network); const { navigateToApp } = useKibana().services.application; const goToNetworkDetails = useCallback( @@ -193,19 +190,25 @@ const NetworkDetailsLinkComponent: React.FC<{ }, [flowTarget, ip, navigateToApp, search] ); + const href = useMemo( + () => formatUrl(getNetworkDetailsUrl(encodeURIComponent(encodeIpv6(ip)))), + [formatUrl, ip] + ); return isButton ? ( - - {children ? children : ip} - + {children} + ) : ( {children ? children : ip} @@ -272,63 +275,84 @@ CreateCaseLink.displayName = 'CreateCaseLink'; // External Links export const GoogleLink = React.memo<{ children?: React.ReactNode; link: string }>( - ({ children, link }) => ( - - {children ? children : link} - - ) + ({ children, link }) => { + const url = useMemo( + () => `https://www.google.com/search?q=${encodeURIComponent(link)}`, + [link] + ); + return {children ? children : link}; + } ); GoogleLink.displayName = 'GoogleLink'; export const PortOrServiceNameLink = React.memo<{ children?: React.ReactNode; + /** `Component` is only used with `EuiDataGrid`; the grid keeps a reference to `Component` for show / hide functionality */ + Component?: typeof EuiButtonEmpty | typeof EuiButtonIcon; portOrServiceName: number | string; -}>(({ children, portOrServiceName }) => ( - - void | undefined; + title?: string; +}>(({ Component, title, children, portOrServiceName }) => { + const href = useMemo( + () => + `https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=${encodeURIComponent( String(portOrServiceName) - )}`} - target="_blank" + )}`, + [portOrServiceName] + ); + return Component ? ( + - {children ? children : portOrServiceName} - - -)); + {title ?? children ?? portOrServiceName} + + ) : ( + + + {children ? children : portOrServiceName} + + + ); +}); PortOrServiceNameLink.displayName = 'PortOrServiceNameLink'; export const Ja3FingerprintLink = React.memo<{ children?: React.ReactNode; ja3Fingerprint: string; -}>(({ children, ja3Fingerprint }) => ( - - {children ? children : ja3Fingerprint} - -)); +}>(({ children, ja3Fingerprint }) => { + const href = useMemo( + () => `https://sslbl.abuse.ch/ja3-fingerprints/${encodeURIComponent(ja3Fingerprint)}`, + [ja3Fingerprint] + ); + return ( + + {children ? children : ja3Fingerprint} + + ); +}); Ja3FingerprintLink.displayName = 'Ja3FingerprintLink'; export const CertificateFingerprintLink = React.memo<{ children?: React.ReactNode; certificateFingerprint: string; -}>(({ children, certificateFingerprint }) => ( - - {children ? children : certificateFingerprint} - -)); +}>(({ children, certificateFingerprint }) => { + const href = useMemo( + () => + `https://sslbl.abuse.ch/ssl-certificates/sha1/${encodeURIComponent(certificateFingerprint)}`, + [certificateFingerprint] + ); + return ( + + {children ? children : certificateFingerprint} + + ); +}); CertificateFingerprintLink.displayName = 'CertificateFingerprintLink'; @@ -354,16 +378,6 @@ const isReputationLink = ( (rowItem as ReputationLinkSetting).url_template !== undefined && (rowItem as ReputationLinkSetting).name !== undefined; -export const Comma = styled('span')` - margin-right: 5px; - margin-left: 5px; - &::after { - content: ' ,'; - } -`; - -Comma.displayName = 'Comma'; - const defaultNameMapping: Record = { [DefaultReputationLink['virustotal.com']]: i18n.VIEW_VIRUS_TOTAL, [DefaultReputationLink['talosIntelligence.com']]: i18n.VIEW_TALOS_INTELLIGENCE, @@ -463,11 +477,13 @@ ReputationLinkComponent.displayName = 'ReputationLinkComponent'; export const ReputationLink = React.memo(ReputationLinkComponent); export const WhoIsLink = React.memo<{ children?: React.ReactNode; domain: string }>( - ({ children, domain }) => ( - - {children ? children : domain} - - ) + ({ children, domain }) => { + const url = useMemo( + () => `https://www.iana.org/whois?q=${encodeURIComponent(domain)}`, + [domain] + ); + return {children ? children : domain}; + } ); WhoIsLink.displayName = 'WhoIsLink'; diff --git a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx index f25e8311ff8fe..7eac477741a5c 100644 --- a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx @@ -79,6 +79,7 @@ export const MatrixHistogramComponent: React.FC = legendPosition, mapping, onError, + paddingSize = 'm', panelHeight = DEFAULT_PANEL_HEIGHT, setAbsoluteRangeDatePickerTarget = 'global', setQuery, @@ -200,7 +201,11 @@ export const MatrixHistogramComponent: React.FC = return ( <> - + {loading && !isInitialLoading && (