From 9e090849207e7ae46353a957247fd9994cd36606 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Wed, 3 Apr 2024 11:39:45 +0200 Subject: [PATCH 01/28] refactor(unified-doc-viewer): switch to dynamic utility --- .../unified_doc_viewer/public/plugin.tsx | 55 ++++++++----------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/src/plugins/unified_doc_viewer/public/plugin.tsx b/src/plugins/unified_doc_viewer/public/plugin.tsx index 4b3793872b4548..6cbbd84ca7b815 100644 --- a/src/plugins/unified_doc_viewer/public/plugin.tsx +++ b/src/plugins/unified_doc_viewer/public/plugin.tsx @@ -16,14 +16,23 @@ import { createGetterSetter, Storage } from '@kbn/kibana-utils-plugin/public'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { CoreStart } from '@kbn/core/public'; +import { dynamic } from '@kbn/shared-ux-utility'; import type { UnifiedDocViewerServices } from './types'; export const [getUnifiedDocViewerServices, setUnifiedDocViewerServices] = createGetterSetter('UnifiedDocViewerServices'); -const DocViewerLegacyTable = React.lazy(() => import('./components/doc_viewer_table/legacy')); -const DocViewerTable = React.lazy(() => import('./components/doc_viewer_table')); -const SourceViewer = React.lazy(() => import('./components/doc_viewer_source')); +const fallback = ( + + + +); + +const LazyDocViewerLegacyTable = dynamic(() => import('./components/doc_viewer_table/legacy'), { + fallback, +}); +const LazyDocViewerTable = dynamic(() => import('./components/doc_viewer_table'), { fallback }); +const LazySourceViewer = dynamic(() => import('./components/doc_viewer_source'), { fallback }); export interface UnifiedDocViewerSetup { registry: DocViewsRegistry; @@ -52,19 +61,11 @@ export class UnifiedDocViewerPublicPlugin order: 10, component: (props) => { const { uiSettings } = getUnifiedDocViewerServices(); - const DocView = uiSettings.get(DOC_TABLE_LEGACY) ? DocViewerLegacyTable : DocViewerTable; + const LazyDocView = uiSettings.get(DOC_TABLE_LEGACY) + ? LazyDocViewerLegacyTable + : LazyDocViewerTable; - return ( - - - - } - > - - - ); + return ; }, }); @@ -76,22 +77,14 @@ export class UnifiedDocViewerPublicPlugin order: 20, component: ({ hit, dataView, textBasedHits }) => { return ( - - - - } - > - {}} - /> - + {}} + /> ); }, }); From 47a094ca8bce0e04435b739717ea40e25ee0f160 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Thu, 4 Apr 2024 15:23:22 +0200 Subject: [PATCH 02/28] wip(unified-doc-viewer): push down logs overview doc view --- .../doc_viewer_logs_overview/index.ts | 0 .../logs_overview.tsx | 36 +++ .../logs_overview_header.tsx | 121 ++++++++++ .../logs_overview_highlights.tsx | 221 ++++++++++++++++++ .../sub_components/highlight_container.tsx | 41 ++++ .../sub_components/highlight_field.tsx | 95 ++++++++ .../highlight_field_description.tsx | 34 +++ .../sub_components/highlight_section.tsx | 90 +++++++ .../sub_components/hover_popover_action.tsx | 87 +++++++ .../sub_components/timestamp.tsx | 25 ++ .../public/hooks/use_field_actions.tsx | 116 +++++++++ 11 files changed, 866 insertions(+) create mode 100644 src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/index.ts create mode 100644 src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx create mode 100644 src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx create mode 100644 src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_highlights.tsx create mode 100644 src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_container.tsx create mode 100644 src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_field.tsx create mode 100644 src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_field_description.tsx create mode 100644 src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_section.tsx create mode 100644 src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/hover_popover_action.tsx create mode 100644 src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/timestamp.tsx create mode 100644 src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/index.ts b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/index.ts new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx new file mode 100644 index 00000000000000..8ec57c0ce30b03 --- /dev/null +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useMemo } from 'react'; +import { DocViewRenderProps } from '@kbn/unified-doc-viewer/types'; +import { useDocDetail } from '../../hooks/use_doc_detail'; +import { DiscoverActionsProvider } from '../../hooks/use_discover_action'; +import { LogsOverviewHeader } from './logs_overview_header'; +import { LogsOverviewHighlights } from './logs_overview_highlights'; + +export function LogsOverview({ + dataView, + hit, + filter, + onAddColumn, + onRemoveColumn, +}: DocViewRenderProps) { + const parsedDoc = useDocDetail(hit, { dataView }); + + const actions = useMemo( + () => ({ filter, onAddColumn, onRemoveColumn }), + [filter, onAddColumn, onRemoveColumn] + ); + + return ( + + + + + ); +} diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx new file mode 100644 index 00000000000000..66f978dace010a --- /dev/null +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx @@ -0,0 +1,121 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { + EuiCodeBlock, + EuiFlexGroup, + EuiFlexItem, + EuiText, + EuiAccordion, + useGeneratedHtmlId, + EuiTitle, +} from '@elastic/eui'; +import { FlyoutDoc } from '../../../common/document'; +import { getMessageWithFallbacks } from '../../hooks/use_doc_detail'; +import { LogLevel } from '../common/log_level'; +import { Timestamp } from './sub_components/timestamp'; +import * as constants from '../../../common/constants'; +import { flyoutContentLabel } from '../common/translations'; +import { HoverActionPopover } from './sub_components/hover_popover_action'; + +export function LogsOverviewHeader({ doc }: { doc: FlyoutDoc }) { + const hasTimestamp = Boolean(doc[constants.TIMESTAMP_FIELD]); + const hasLogLevel = Boolean(doc[constants.LOG_LEVEL_FIELD]); + const hasBadges = hasTimestamp || hasLogLevel; + const { field, value } = getMessageWithFallbacks(doc); + const hasMessageField = field && value; + const hasFlyoutHeader = hasMessageField || hasBadges; + + const accordionId = useGeneratedHtmlId({ + prefix: flyoutContentLabel, + }); + + const accordionTitle = ( + +

{flyoutContentLabel}

+
+ ); + + const logLevelAndTimestamp = ( + + {hasBadges && ( + + {doc[constants.LOG_LEVEL_FIELD] && ( + + + + + + )} + {hasTimestamp && ( + + + + )} + + )} + + ); + + const contentField = hasMessageField && ( + + + + + + + {field} + + + {logLevelAndTimestamp} + + + + + + {value} + + + + + + ); + + return hasFlyoutHeader ? ( + + + {hasMessageField ? contentField : logLevelAndTimestamp} + + + ) : null; +} diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_highlights.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_highlights.tsx new file mode 100644 index 00000000000000..ea668fc4cedc50 --- /dev/null +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_highlights.tsx @@ -0,0 +1,221 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { CloudProvider, CloudProviderIcon } from '@kbn/custom-icons'; +import { useMeasure } from 'react-use/lib'; +import { first } from 'lodash'; +import { FlyoutDoc, LogDocument } from '../../../common/document'; +import * as constants from '../../../common/constants'; +import { HighlightField } from './sub_components/highlight_field'; +import { + cloudAccordionTitle, + flyoutCloudAvailabilityZoneLabel, + flyoutCloudInstanceIdLabel, + flyoutCloudProjectIdLabel, + flyoutCloudProviderLabel, + flyoutCloudRegionLabel, + flyoutDatasetLabel, + flyoutHostNameLabel, + flyoutLogPathFileLabel, + flyoutNamespaceLabel, + flyoutOrchestratorClusterNameLabel, + flyoutOrchestratorResourceIdLabel, + flyoutServiceLabel, + flyoutShipperLabel, + flyoutTraceLabel, + otherAccordionTitle, + serviceInfraAccordionTitle, +} from '../common/translations'; +import { HighlightSection } from './sub_components/highlight_section'; +import { HighlightContainer } from './sub_components/highlight_container'; +import { useFlyoutColumnWidth } from '../../hooks/use_flyouot_column_width'; + +export function LogsOverviewHighlights({ + formattedDoc, + flattenedDoc, +}: { + formattedDoc: FlyoutDoc; + flattenedDoc: LogDocument['flattened']; +}) { + const [ref, dimensions] = useMeasure(); + const { columns, fieldWidth } = useFlyoutColumnWidth(dimensions.width); + return ( + + {/* Service & Infrastructure highlight */} + + {formattedDoc[constants.SERVICE_NAME_FIELD] && ( + + )} + {formattedDoc[constants.HOST_NAME_FIELD] && ( + + )} + {formattedDoc[constants.TRACE_ID_FIELD] && ( + + )} + {formattedDoc[constants.ORCHESTRATOR_CLUSTER_NAME_FIELD] && ( + + )} + {formattedDoc[constants.ORCHESTRATOR_RESOURCE_ID_FIELD] && ( + + )} + + {/* Cloud highlight */} + + {formattedDoc[constants.CLOUD_PROVIDER_FIELD] && ( + + } + label={flyoutCloudProviderLabel} + value={flattenedDoc[constants.CLOUD_PROVIDER_FIELD]} + width={fieldWidth} + /> + )} + {formattedDoc[constants.CLOUD_REGION_FIELD] && ( + + )} + {formattedDoc[constants.CLOUD_AVAILABILITY_ZONE_FIELD] && ( + + )} + {formattedDoc[constants.CLOUD_PROJECT_ID_FIELD] && ( + + )} + {formattedDoc[constants.CLOUD_INSTANCE_ID_FIELD] && ( + + )} + + {/* Other highlights */} + + {formattedDoc[constants.LOG_FILE_PATH_FIELD] && ( + + )} + {formattedDoc[constants.DATASTREAM_DATASET_FIELD] && ( + + )} + {formattedDoc[constants.DATASTREAM_NAMESPACE_FIELD] && ( + + )} + {formattedDoc[constants.AGENT_NAME_FIELD] && ( + + )} + + + ); +} diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_container.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_container.tsx new file mode 100644 index 00000000000000..57b38e4bdab469 --- /dev/null +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_container.tsx @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { EuiHorizontalRule } from '@elastic/eui'; + +interface HighlightContainerProps { + children: React.ReactNode; +} + +const hasNonUndefinedSubChild = (children: React.ReactNode[]): boolean => { + return children.some((child) => { + if (React.isValidElement(child)) { + const subChildren = React.Children.toArray(child.props.children); + return subChildren.some((subChild) => subChild !== undefined && subChild !== null); + } + return false; + }); +}; + +export const HighlightContainer = React.forwardRef( + ({ children }, ref) => { + const validChildren = React.Children.toArray(children).filter(Boolean); + const hasChildren = validChildren.length > 0; + const shouldRender = hasChildren && hasNonUndefinedSubChild(validChildren); + + const flexChildren = validChildren.map((child, idx) =>
{child}
); + + return shouldRender ? ( +
+ + {flexChildren} +
+ ) : null; + } +); diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_field.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_field.tsx new file mode 100644 index 00000000000000..1ddccf79cdb700 --- /dev/null +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_field.tsx @@ -0,0 +1,95 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + EuiBadge, + EuiFlexGroup, + EuiFlexItem, + EuiText, + EuiTextTruncate, + EuiTitle, + useEuiTheme, +} from '@elastic/eui'; +import { css } from '@emotion/react'; +import React, { ReactNode } from 'react'; +import { dynamic } from '@kbn/shared-ux-utility'; +import { HoverActionPopover } from './hover_popover_action'; + +const HighlightFieldDescription = dynamic(() => import('./highlight_field_description')); + +interface HighlightFieldProps { + useBadge?: boolean; + field: string; + formattedValue: string; + icon?: ReactNode; + label: string; + value?: string; + width: number; +} + +export function HighlightField({ + useBadge = false, + field, + formattedValue, + icon, + label, + value, + width, + ...props +}: HighlightFieldProps) { + const { euiTheme } = useEuiTheme(); + + return formattedValue && value ? ( + + + + + + {label} + + + + + + + + + + + {icon && {icon}} + + + {(truncatedText: string) => + useBadge ? ( + {truncatedText} + ) : ( + + ) + } + + + + + + + ) : null; +} diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_field_description.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_field_description.tsx new file mode 100644 index 00000000000000..6566cd511c3d05 --- /dev/null +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_field_description.tsx @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EuiFlexGroup, EuiFlexItem, EuiIconTip } from '@elastic/eui'; +import { EcsFlat } from '@elastic/ecs'; +import { FieldIcon } from '@kbn/react-field'; +import React from 'react'; + +export function HighlightFieldDescription({ fieldName }: { fieldName: string }) { + const { short, type } = EcsFlat[fieldName as keyof typeof EcsFlat] ?? {}; + + if (!short) return null; + + const title = ( + + {type && ( + + + + )} + {fieldName} + + ); + + return ; +} + +// eslint-disable-next-line import/no-default-export +export default HighlightFieldDescription; diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_section.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_section.tsx new file mode 100644 index 00000000000000..193f9e6ec64e9c --- /dev/null +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_section.tsx @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useState } from 'react'; +import { + EuiAccordion, + EuiFlexGrid, + EuiHorizontalRule, + EuiTitle, + EuiFlexItem, + useGeneratedHtmlId, + EuiButtonEmpty, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +interface HighlightSectionProps { + title: string; + children: React.ReactNode; + columns: 1 | 2 | 3; +} + +const CHILDREN_PER_SECTION: 3 | 6 | 9 = 6; + +export function HighlightSection({ title, children, columns, ...props }: HighlightSectionProps) { + const validChildren = React.Children.toArray(children).filter(Boolean); + const childLength = validChildren.length; + const shouldRenderSection = childLength > 0; + const limitedChildren = validChildren.slice(0, CHILDREN_PER_SECTION - 1); + const [showMore, setShowMore] = useState(childLength > CHILDREN_PER_SECTION); + + const accordionId = useGeneratedHtmlId({ + prefix: title, + }); + + const showMoreButtonLabel = i18n.translate( + 'unifiedDocViewer.docView.logsOverview.section.showMore', + { + defaultMessage: '+ {count} more', + values: { count: childLength - limitedChildren.length }, + } + ); + + const showMoreButton = ( + { + setShowMore(false); + }} + > + {showMoreButtonLabel} + + ); + + limitedChildren.push(showMoreButton); + + const accordionTitle = ( + +

{title}

+
+ ); + + const flexChildren = (showMore ? limitedChildren : validChildren).map((child, idx) => ( + {child} + )); + + return shouldRenderSection ? ( + <> + + + {flexChildren} + + + + + ) : null; +} diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/hover_popover_action.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/hover_popover_action.tsx new file mode 100644 index 00000000000000..1f9855c28a86b6 --- /dev/null +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/hover_popover_action.tsx @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useRef, useState } from 'react'; +import { + EuiFlexGroup, + EuiPopover, + EuiButtonIcon, + EuiPopoverTitle, + EuiToolTip, + PopoverAnchorPosition, + type EuiPopoverProps, +} from '@elastic/eui'; +import { useHoverActions } from '../../../hooks/use_hover_actions'; + +interface HoverPopoverActionProps { + children: React.ReactChild; + field: string; + value: string; + title?: string; + anchorPosition?: PopoverAnchorPosition; + display?: EuiPopoverProps['display']; +} + +export const HoverActionPopover = ({ + children, + title, + field, + value, + anchorPosition = 'upCenter', + display = 'inline-block', +}: HoverPopoverActionProps) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const leaveTimer = useRef(null); + const hoverActions = useHoverActions({ field, value }); + + // The timeout hack is required because we are using a Popover which ideally should be used with a mouseclick, + // but we are using it as a Tooltip. Which means we now need to manually handle the open and close + // state using the mouse hover events. This cause the popover to close even before the user could + // navigate actions inside it. Hence, to prevent this, we need this hack + const onMouseEnter = () => { + if (leaveTimer.current) { + clearTimeout(leaveTimer.current); + } + setIsPopoverOpen(true); + }; + + const onMouseLeave = () => { + leaveTimer.current = setTimeout(() => setIsPopoverOpen(false), 100); + }; + + return ( +
+ + {title && ( + + {title} + + )} + + {hoverActions.map((action) => ( + + action.onClick()} + /> + + ))} + + +
+ ); +}; diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/timestamp.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/timestamp.tsx new file mode 100644 index 00000000000000..fb1d4a1f11342c --- /dev/null +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/timestamp.tsx @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { EuiBadge } from '@elastic/eui'; +import { FlyoutDoc } from '../../../../common/document'; + +interface TimestampProps { + timestamp: FlyoutDoc['@timestamp']; +} + +export function Timestamp({ timestamp }: TimestampProps) { + if (!timestamp) return null; + + return ( + + {timestamp} + + ); +} diff --git a/src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx b/src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx new file mode 100644 index 00000000000000..abb42856ca7284 --- /dev/null +++ b/src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx @@ -0,0 +1,116 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { useMemo, useState } from 'react'; +import { copyToClipboard, IconType } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { + copyToClipboardLabel, + filterForFieldPresentLabel, + toggleColumnLabel, +} from '../components/common/translations'; +import { useDiscoverActionsContext } from './use_discover_action'; + +interface HoverActionProps { + field: string; + value: string; +} + +export interface HoverActionType { + id: string; + tooltipContent: string; + iconType: IconType; + onClick: () => void; + display: boolean; +} + +export const useFieldActions = ({ field, value }: HoverActionProps): HoverActionType[] => { + const filterForText = actionFilterForText(value); + const filterOutText = actionFilterOutText(value); + const actions = useDiscoverActionsContext(); + const [columnAdded, setColumnAdded] = useState(false); + + return useMemo( + () => [ + { + id: 'addToFilterAction', + tooltipContent: filterForText, + iconType: 'plusInCircle', + onClick: () => actions?.addFilter && actions.addFilter(field, value, '+'), + display: true, + }, + { + id: 'removeFromFilterAction', + tooltipContent: filterOutText, + iconType: 'minusInCircle', + onClick: () => actions?.addFilter && actions.addFilter(field, value, '-'), + display: true, + }, + { + id: 'filterForFieldPresentAction', + tooltipContent: filterForFieldPresentLabel, + iconType: 'filter', + onClick: () => actions?.addFilter && actions.addFilter('_exists_', field, '+'), + display: true, + }, + { + id: 'toggleColumnAction', + tooltipContent: toggleColumnLabel, + iconType: 'listAdd', + onClick: () => { + if (actions) { + if (columnAdded) { + actions?.removeColumn?.(field); + } else { + actions?.addColumn?.(field); + } + setColumnAdded(!columnAdded); + } + }, + display: true, + }, + { + id: 'copyToClipboardAction', + tooltipContent: copyToClipboardLabel, + iconType: 'copyClipboard', + onClick: () => copyToClipboard(value as string), + display: true, + }, + ], + [filterForText, filterOutText, actions, field, value, columnAdded] + ); +}; + +const actionFilterForText = (text: string) => + i18n.translate('unifiedDocViewer.fieldActions.filterFor', { + defaultMessage: 'Filter for this {value}', + values: { + value: text, + }, + }); + +const actionFilterOutText = (text: string) => + i18n.translate('unifiedDocViewer.fieldActions.filterOut', { + defaultMessage: 'Filter out this {value}', + values: { + value: text, + }, + }); + +const filterForFieldPresentLabel = i18n.translate( + 'unifiedDocViewer.fieldActions.filterForFieldPresent', + { defaultMessage: 'Filter for field present' } +); + +const toggleColumnLabel = i18n.translate('unifiedDocViewer.fieldActions.toggleColumn', { + defaultMessage: 'Toggle column in table', +}); + +const copyToClipboardLabel = i18n.translate('unifiedDocViewer.fieldActions.copyToClipboard', { + defaultMessage: 'Copy to clipboard', +}); From 511b86b85bb60edfb359799d4f3dd316ee9c16c0 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Fri, 5 Apr 2024 10:39:27 +0200 Subject: [PATCH 03/28] wip(unified-doc-viewer): support enable/disable doc views --- .../src/components/doc_viewer/doc_viewer.tsx | 34 ++++++++++--------- .../src/services/doc_views_registry.ts | 22 ++++++++++++ .../src/services/types.ts | 1 + .../public/hooks/use_field_actions.tsx | 5 --- .../unified_doc_viewer/public/plugin.tsx | 17 ++++++++++ .../customizations/logs_explorer_profile.tsx | 21 +----------- 6 files changed, 59 insertions(+), 41 deletions(-) diff --git a/packages/kbn-unified-doc-viewer/src/components/doc_viewer/doc_viewer.tsx b/packages/kbn-unified-doc-viewer/src/components/doc_viewer/doc_viewer.tsx index fab4673eb8ed5b..74fbfcf0a61330 100644 --- a/packages/kbn-unified-doc-viewer/src/components/doc_viewer/doc_viewer.tsx +++ b/packages/kbn-unified-doc-viewer/src/components/doc_viewer/doc_viewer.tsx @@ -22,22 +22,24 @@ export interface DocViewerProps extends DocViewRenderProps { * a `render` function. */ export function DocViewer({ docViews, ...renderProps }: DocViewerProps) { - const tabs = docViews.map(({ id, title, render, component }: DocView) => { - return { - id: `kbn_doc_viewer_tab_${id}`, - name: title, - content: ( - - ), - ['data-test-subj']: `docViewerTab-${id}`, - }; - }); + const tabs = docViews + .filter(({ enabled }) => enabled) // Filter out disabled doc views + .map(({ id, title, render, component }: DocView) => { + return { + id: `kbn_doc_viewer_tab_${id}`, + name: title, + content: ( + + ), + ['data-test-subj']: `docViewerTab-${id}`, + }; + }); if (!tabs.length) { // There's a minimum of 2 tabs active in Discover. diff --git a/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts b/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts index 9cada7ed497a39..cfca97fceb18ca 100644 --- a/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts +++ b/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts @@ -50,6 +50,28 @@ export class DocViewsRegistry { this.docViews.delete(id); } + enableById(id: string) { + const docView = this.docViews.get(id); + if (docView) { + docView.enabled = true; + } else { + throw new Error( + `DocViewsRegistry#enableById: there is no DocView registered with id "${id}".` + ); + } + } + + disableById(id: string) { + const docView = this.docViews.get(id); + if (docView) { + docView.enabled = false; + } else { + throw new Error( + `DocViewsRegistry#disableById: there is no DocView registered with id "${id}".` + ); + } + } + clone() { return new DocViewsRegistry(this); } diff --git a/packages/kbn-unified-doc-viewer/src/services/types.ts b/packages/kbn-unified-doc-viewer/src/services/types.ts index bcf64c817e8873..8037979f61d1e9 100644 --- a/packages/kbn-unified-doc-viewer/src/services/types.ts +++ b/packages/kbn-unified-doc-viewer/src/services/types.ts @@ -60,6 +60,7 @@ export interface BaseDocViewInput { id: string; order: number; title: string; + enabled: boolean; } export interface RenderDocViewInput extends BaseDocViewInput { diff --git a/src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx b/src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx index abb42856ca7284..ff765a7bdd90b9 100644 --- a/src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx +++ b/src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx @@ -9,11 +9,6 @@ import { useMemo, useState } from 'react'; import { copyToClipboard, IconType } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { - copyToClipboardLabel, - filterForFieldPresentLabel, - toggleColumnLabel, -} from '../components/common/translations'; import { useDiscoverActionsContext } from './use_discover_action'; interface HoverActionProps { diff --git a/src/plugins/unified_doc_viewer/public/plugin.tsx b/src/plugins/unified_doc_viewer/public/plugin.tsx index 6cbbd84ca7b815..c9812faeaba815 100644 --- a/src/plugins/unified_doc_viewer/public/plugin.tsx +++ b/src/plugins/unified_doc_viewer/public/plugin.tsx @@ -28,6 +28,9 @@ const fallback = ( ); +const LazyDocViewerLogsOverview = dynamic(() => import('./components/doc_viewer_logs_overview'), { + fallback, +}); const LazyDocViewerLegacyTable = dynamic(() => import('./components/doc_viewer_table/legacy'), { fallback, }); @@ -53,12 +56,25 @@ export class UnifiedDocViewerPublicPlugin private docViewsRegistry = new DocViewsRegistry(); public setup(core: CoreSetup) { + this.docViewsRegistry.add({ + id: 'doc_view_logs_overview', + title: i18n.translate('unifiedDocViewer.docViews.logsOverview.title', { + defaultMessage: 'Overview', + }), + order: 0, + enabled: false, // Disabled doc view by default, can be programmatically enabled using the DocViewsRegistry.prototype.enableById method. + component: (props) => { + return ; + }, + }); + this.docViewsRegistry.add({ id: 'doc_view_table', title: i18n.translate('unifiedDocViewer.docViews.table.tableTitle', { defaultMessage: 'Table', }), order: 10, + enabled: true, component: (props) => { const { uiSettings } = getUnifiedDocViewerServices(); const LazyDocView = uiSettings.get(DOC_TABLE_LEGACY) @@ -75,6 +91,7 @@ export class UnifiedDocViewerPublicPlugin defaultMessage: 'JSON', }), order: 20, + enabled: true, component: ({ hit, dataView, textBasedHits }) => { return ( { - registry.add({ - id: 'doc_view_log_overview', - title: i18n.translate('xpack.logsExplorer.flyoutDetail.docViews.overview', { - defaultMessage: 'Overview', - }), - order: 0, - component: (props) => { - const KibanaContextProviderForPlugin = useKibanaContextForPluginProvider(core, plugins); - - return ( - - - - - - ); - }, - }); + registry.enableById('doc_view_logs_overview'); return registry; }, From 16473ec9bfef6a7d2292572e952f93106783bc62 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Fri, 5 Apr 2024 12:50:55 +0200 Subject: [PATCH 04/28] wip(unified-doc-viewer): move ui actions --- .../doc_viewer_logs_overview/index.ts | 12 +++ .../logs_overview.tsx | 19 ++-- .../sub_components/hover_popover_action.tsx | 17 ++-- .../public/hooks/use_field_actions.tsx | 90 +++++++++++-------- 4 files changed, 85 insertions(+), 53 deletions(-) diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/index.ts b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/index.ts index e69de29bb2d1d6..69f01c944ad253 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/index.ts +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/index.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { LogsOverview } from './logs_overview'; + +// Required for usage in React.lazy +// eslint-disable-next-line import/no-default-export +export default LogsOverview; diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx index 8ec57c0ce30b03..02fb7b00d85163 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx @@ -6,14 +6,15 @@ * Side Public License, v 1. */ -import React, { useMemo } from 'react'; +import React from 'react'; import { DocViewRenderProps } from '@kbn/unified-doc-viewer/types'; import { useDocDetail } from '../../hooks/use_doc_detail'; -import { DiscoverActionsProvider } from '../../hooks/use_discover_action'; import { LogsOverviewHeader } from './logs_overview_header'; import { LogsOverviewHighlights } from './logs_overview_highlights'; +import { FieldActionsProvider } from '../../hooks/use_field_actions'; export function LogsOverview({ + columns, dataView, hit, filter, @@ -22,15 +23,15 @@ export function LogsOverview({ }: DocViewRenderProps) { const parsedDoc = useDocDetail(hit, { dataView }); - const actions = useMemo( - () => ({ filter, onAddColumn, onRemoveColumn }), - [filter, onAddColumn, onRemoveColumn] - ); - return ( - + - + ); } diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/hover_popover_action.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/hover_popover_action.tsx index 1f9855c28a86b6..0c5e3b8d4239a4 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/hover_popover_action.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/hover_popover_action.tsx @@ -1,8 +1,9 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ import React, { useRef, useState } from 'react'; @@ -15,7 +16,7 @@ import { PopoverAnchorPosition, type EuiPopoverProps, } from '@elastic/eui'; -import { useHoverActions } from '../../../hooks/use_hover_actions'; +import { useUIFieldActions } from '../../../hooks/use_field_actions'; interface HoverPopoverActionProps { children: React.ReactChild; @@ -36,7 +37,7 @@ export const HoverActionPopover = ({ }: HoverPopoverActionProps) => { const [isPopoverOpen, setIsPopoverOpen] = useState(false); const leaveTimer = useRef(null); - const hoverActions = useHoverActions({ field, value }); + const uiFieldActions = useUIFieldActions({ field, value }); // The timeout hack is required because we are using a Popover which ideally should be used with a mouseclick, // but we are using it as a Tooltip. Which means we now need to manually handle the open and close @@ -69,14 +70,14 @@ export const HoverActionPopover = ({ )} - {hoverActions.map((action) => ( - + {uiFieldActions.map((action) => ( + action.onClick()} + aria-label={action.label} + onClick={action.onClick} /> ))} diff --git a/src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx b/src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx index ff765a7bdd90b9..d900cfd6e996ad 100644 --- a/src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx +++ b/src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx @@ -6,78 +6,96 @@ * Side Public License, v 1. */ -import { useMemo, useState } from 'react'; +import { useMemo } from 'react'; +import createContainer from 'constate'; import { copyToClipboard, IconType } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { useDiscoverActionsContext } from './use_discover_action'; +import { DocViewRenderProps } from '@kbn/unified-doc-viewer/types'; -interface HoverActionProps { +interface WithFieldParam { field: string; +} + +interface WithValueParam { value: string; } -export interface HoverActionType { +interface TFieldActionParams extends WithFieldParam, WithValueParam {} + +export interface TFieldAction { id: string; - tooltipContent: string; iconType: IconType; + label: string; onClick: () => void; - display: boolean; } -export const useFieldActions = ({ field, value }: HoverActionProps): HoverActionType[] => { - const filterForText = actionFilterForText(value); - const filterOutText = actionFilterOutText(value); - const actions = useDiscoverActionsContext(); - const [columnAdded, setColumnAdded] = useState(false); +type UseFieldActionsDeps = Pick< + DocViewRenderProps, + 'columns' | 'filter' | 'onAddColumn' | 'onRemoveColumn' +>; + +const useFieldActions = ({ columns, filter, onAddColumn, onRemoveColumn }: UseFieldActionsDeps) => { + return useMemo( + () => ({ + addColumn: onAddColumn, + addFilterExist: ({ field }: WithFieldParam) => filter && filter('_exists_', field, '+'), + addFilterIn: ({ field, value }: TFieldActionParams) => filter && filter(field, value, '+'), + addFilterOut: ({ field, value }: TFieldActionParams) => filter && filter(field, value, '-'), + copyToClipboard, + removeColumn: onRemoveColumn, + toggleFieldColumn: ({ field }: WithFieldParam) => { + if (!columns) return; + const isFieldInTable = columns.includes(field); + if (isFieldInTable && onRemoveColumn) { + onRemoveColumn(field); + } else if (!isFieldInTable && onAddColumn) { + onAddColumn(field); + } + }, + }), + [columns, filter, onAddColumn, onRemoveColumn] + ); +}; + +export const [FieldActionsProvider, useFieldActionsContext] = createContainer(useFieldActions); + +export const useUIFieldActions = ({ field, value }: TFieldActionParams): TFieldAction[] => { + const actions = useFieldActionsContext(); return useMemo( () => [ { id: 'addToFilterAction', - tooltipContent: filterForText, iconType: 'plusInCircle', - onClick: () => actions?.addFilter && actions.addFilter(field, value, '+'), - display: true, + label: actionFilterForText(value), + onClick: () => actions.addFilterIn({ field, value }), }, { id: 'removeFromFilterAction', - tooltipContent: filterOutText, iconType: 'minusInCircle', - onClick: () => actions?.addFilter && actions.addFilter(field, value, '-'), - display: true, + label: actionFilterOutText(value), + onClick: () => actions.addFilterOut({ field, value }), }, { id: 'filterForFieldPresentAction', - tooltipContent: filterForFieldPresentLabel, iconType: 'filter', - onClick: () => actions?.addFilter && actions.addFilter('_exists_', field, '+'), - display: true, + label: filterForFieldPresentLabel, + onClick: () => actions.addFilterExist({ field }), }, { id: 'toggleColumnAction', - tooltipContent: toggleColumnLabel, iconType: 'listAdd', - onClick: () => { - if (actions) { - if (columnAdded) { - actions?.removeColumn?.(field); - } else { - actions?.addColumn?.(field); - } - setColumnAdded(!columnAdded); - } - }, - display: true, + label: toggleColumnLabel, + onClick: () => actions.toggleFieldColumn({ field }), }, { id: 'copyToClipboardAction', - tooltipContent: copyToClipboardLabel, iconType: 'copyClipboard', - onClick: () => copyToClipboard(value as string), - display: true, + label: copyToClipboardLabel, + onClick: () => actions.copyToClipboard(value), }, ], - [filterForText, filterOutText, actions, field, value, columnAdded] + [actions, field, value] ); }; From 3714e80e800faf4616da7e8a25e6bd8d0330239e Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Fri, 5 Apr 2024 12:56:56 +0200 Subject: [PATCH 05/28] wip(unified-doc-viewer): minor naming changes --- .../sub_components/timestamp.tsx | 3 +-- .../public/hooks/use_field_actions.tsx | 22 ++++++++----------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/timestamp.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/timestamp.tsx index fb1d4a1f11342c..a68ba966bde977 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/timestamp.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/timestamp.tsx @@ -8,10 +8,9 @@ import React from 'react'; import { EuiBadge } from '@elastic/eui'; -import { FlyoutDoc } from '../../../../common/document'; interface TimestampProps { - timestamp: FlyoutDoc['@timestamp']; + timestamp?: string; } export function Timestamp({ timestamp }: TimestampProps) { diff --git a/src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx b/src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx index d900cfd6e996ad..6b2d6d3f8a64d0 100644 --- a/src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx +++ b/src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx @@ -65,25 +65,25 @@ export const useUIFieldActions = ({ field, value }: TFieldActionParams): TFieldA return useMemo( () => [ { - id: 'addToFilterAction', + id: 'addFilterInAction', iconType: 'plusInCircle', label: actionFilterForText(value), onClick: () => actions.addFilterIn({ field, value }), }, { - id: 'removeFromFilterAction', + id: 'addFilterOutremoveFromFilterAction', iconType: 'minusInCircle', label: actionFilterOutText(value), onClick: () => actions.addFilterOut({ field, value }), }, { - id: 'filterForFieldPresentAction', + id: 'addFilterExistAction', iconType: 'filter', label: filterForFieldPresentLabel, onClick: () => actions.addFilterExist({ field }), }, { - id: 'toggleColumnAction', + id: 'toggleFieldColumnAction', iconType: 'listAdd', label: toggleColumnLabel, onClick: () => actions.toggleFieldColumn({ field }), @@ -99,20 +99,16 @@ export const useUIFieldActions = ({ field, value }: TFieldActionParams): TFieldA ); }; -const actionFilterForText = (text: string) => - i18n.translate('unifiedDocViewer.fieldActions.filterFor', { +const actionFilterForText = (value: string) => + i18n.translate('unifiedDocViewer.fieldActions.filterIn', { defaultMessage: 'Filter for this {value}', - values: { - value: text, - }, + values: { value }, }); -const actionFilterOutText = (text: string) => +const actionFilterOutText = (value: string) => i18n.translate('unifiedDocViewer.fieldActions.filterOut', { defaultMessage: 'Filter out this {value}', - values: { - value: text, - }, + values: { value }, }); const filterForFieldPresentLabel = i18n.translate( From 91062be8e77138eac121eb6ccb23693031850a4c Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Fri, 5 Apr 2024 15:43:22 +0200 Subject: [PATCH 06/28] wip(unified-doc-viewer): move constants into discover-utils --- packages/kbn-discover-utils/index.ts | 1 + .../kbn-discover-utils/src/field_constants.ts | 42 +++ packages/kbn-discover-utils/src/index.ts | 1 + .../logs_overview.tsx | 2 +- .../logs_overview_highlights.tsx | 251 ++++++++++++------ 5 files changed, 218 insertions(+), 79 deletions(-) create mode 100644 packages/kbn-discover-utils/src/field_constants.ts diff --git a/packages/kbn-discover-utils/index.ts b/packages/kbn-discover-utils/index.ts index 5eb26504826111..902de66e0b2776 100644 --- a/packages/kbn-discover-utils/index.ts +++ b/packages/kbn-discover-utils/index.ts @@ -31,6 +31,7 @@ export { IgnoredReason, buildDataTableRecord, buildDataTableRecordList, + fieldConstants, formatFieldValue, formatHit, getDocId, diff --git a/packages/kbn-discover-utils/src/field_constants.ts b/packages/kbn-discover-utils/src/field_constants.ts new file mode 100644 index 00000000000000..5cc8be86f06d31 --- /dev/null +++ b/packages/kbn-discover-utils/src/field_constants.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +// Fields constants +export const TIMESTAMP_FIELD = '@timestamp'; +export const HOST_NAME_FIELD = 'host.name'; +export const LOG_LEVEL_FIELD = 'log.level'; +export const MESSAGE_FIELD = 'message'; +export const ERROR_MESSAGE_FIELD = 'error.message'; +export const EVENT_ORIGINAL_FIELD = 'event.original'; +export const TRACE_ID_FIELD = 'trace.id'; + +export const LOG_FILE_PATH_FIELD = 'log.file.path'; +export const DATASTREAM_NAMESPACE_FIELD = 'data_stream.namespace'; +export const DATASTREAM_DATASET_FIELD = 'data_stream.dataset'; + +// Resource Fields +export const AGENT_NAME_FIELD = 'agent.name'; +export const CLOUD_PROVIDER_FIELD = 'cloud.provider'; +export const CLOUD_REGION_FIELD = 'cloud.region'; +export const CLOUD_AVAILABILITY_ZONE_FIELD = 'cloud.availability_zone'; +export const CLOUD_PROJECT_ID_FIELD = 'cloud.project.id'; +export const CLOUD_INSTANCE_ID_FIELD = 'cloud.instance.id'; +export const SERVICE_NAME_FIELD = 'service.name'; +export const ORCHESTRATOR_CLUSTER_NAME_FIELD = 'orchestrator.cluster.name'; +export const ORCHESTRATOR_RESOURCE_ID_FIELD = 'orchestrator.resource.id'; +export const ORCHESTRATOR_NAMESPACE_FIELD = 'orchestrator.namespace'; +export const CONTAINER_NAME_FIELD = 'container.name'; +export const CONTAINER_ID_FIELD = 'container.id'; + +// Degraded Docs +export const DEGRADED_DOCS_FIELD = 'ignored_field_values'; + +// Error Stacktrace +export const ERROR_STACK_TRACE = 'error.stack_trace'; +export const ERROR_EXCEPTION_STACKTRACE = 'error.exception.stacktrace'; +export const ERROR_LOG_STACKTRACE = 'error.log.stacktrace'; diff --git a/packages/kbn-discover-utils/src/index.ts b/packages/kbn-discover-utils/src/index.ts index abc14f31911fe5..b1dc1901117462 100644 --- a/packages/kbn-discover-utils/src/index.ts +++ b/packages/kbn-discover-utils/src/index.ts @@ -7,5 +7,6 @@ */ export * from './constants'; +export * as fieldConstants from './field_constants'; export * from './hooks'; export * from './utils'; diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx index 02fb7b00d85163..a38c95fdce129b 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx @@ -31,7 +31,7 @@ export function LogsOverview({ onRemoveColumn={onRemoveColumn} > - + ); } diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_highlights.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_highlights.tsx index ea668fc4cedc50..4fc4cec0e8d876 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_highlights.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_highlights.tsx @@ -10,31 +10,31 @@ import React from 'react'; import { CloudProvider, CloudProviderIcon } from '@kbn/custom-icons'; import { useMeasure } from 'react-use/lib'; import { first } from 'lodash'; +import { i18n } from '@kbn/i18n'; +import { useEuiTheme } from '@elastic/eui'; +import { fieldConstants } from '@kbn/discover-utils'; import { FlyoutDoc, LogDocument } from '../../../common/document'; -import * as constants from '../../../common/constants'; import { HighlightField } from './sub_components/highlight_field'; -import { - cloudAccordionTitle, - flyoutCloudAvailabilityZoneLabel, - flyoutCloudInstanceIdLabel, - flyoutCloudProjectIdLabel, - flyoutCloudProviderLabel, - flyoutCloudRegionLabel, - flyoutDatasetLabel, - flyoutHostNameLabel, - flyoutLogPathFileLabel, - flyoutNamespaceLabel, - flyoutOrchestratorClusterNameLabel, - flyoutOrchestratorResourceIdLabel, - flyoutServiceLabel, - flyoutShipperLabel, - flyoutTraceLabel, - otherAccordionTitle, - serviceInfraAccordionTitle, -} from '../common/translations'; import { HighlightSection } from './sub_components/highlight_section'; import { HighlightContainer } from './sub_components/highlight_container'; -import { useFlyoutColumnWidth } from '../../hooks/use_flyouot_column_width'; + +interface FlyoutColumnWidth { + columns: 1 | 2 | 3; + fieldWidth: number; +} + +export const useFlyoutColumnWidth = (width: number): FlyoutColumnWidth => { + const { euiTheme } = useEuiTheme(); + + const numberOfColumns = width > euiTheme.breakpoint.m ? 3 : width > euiTheme.breakpoint.s ? 2 : 1; + const WIDTH_FACTOR = 1.25; + const fieldWidth = width / (numberOfColumns * WIDTH_FACTOR); + + return { + columns: numberOfColumns, + fieldWidth, + }; +}; export function LogsOverviewHighlights({ formattedDoc, @@ -53,53 +53,53 @@ export function LogsOverviewHighlights({ columns={columns} data-test-subj="logsExplorerFlyoutHighlightSectionServiceInfra" > - {formattedDoc[constants.SERVICE_NAME_FIELD] && ( + {formattedDoc[fieldConstants.SERVICE_NAME_FIELD] && ( )} - {formattedDoc[constants.HOST_NAME_FIELD] && ( + {formattedDoc[fieldConstants.HOST_NAME_FIELD] && ( )} - {formattedDoc[constants.TRACE_ID_FIELD] && ( + {formattedDoc[fieldConstants.TRACE_ID_FIELD] && ( )} - {formattedDoc[constants.ORCHESTRATOR_CLUSTER_NAME_FIELD] && ( + {formattedDoc[fieldConstants.ORCHESTRATOR_CLUSTER_NAME_FIELD] && ( )} - {formattedDoc[constants.ORCHESTRATOR_RESOURCE_ID_FIELD] && ( + {formattedDoc[fieldConstants.ORCHESTRATOR_RESOURCE_ID_FIELD] && ( )} @@ -110,60 +110,60 @@ export function LogsOverviewHighlights({ columns={columns} data-test-subj="logsExplorerFlyoutHighlightSectionCloud" > - {formattedDoc[constants.CLOUD_PROVIDER_FIELD] && ( + {formattedDoc[fieldConstants.CLOUD_PROVIDER_FIELD] && ( } label={flyoutCloudProviderLabel} - value={flattenedDoc[constants.CLOUD_PROVIDER_FIELD]} + value={flattenedDoc[fieldConstants.CLOUD_PROVIDER_FIELD]} width={fieldWidth} /> )} - {formattedDoc[constants.CLOUD_REGION_FIELD] && ( + {formattedDoc[fieldConstants.CLOUD_REGION_FIELD] && ( )} - {formattedDoc[constants.CLOUD_AVAILABILITY_ZONE_FIELD] && ( + {formattedDoc[fieldConstants.CLOUD_AVAILABILITY_ZONE_FIELD] && ( )} - {formattedDoc[constants.CLOUD_PROJECT_ID_FIELD] && ( + {formattedDoc[fieldConstants.CLOUD_PROJECT_ID_FIELD] && ( )} - {formattedDoc[constants.CLOUD_INSTANCE_ID_FIELD] && ( + {formattedDoc[fieldConstants.CLOUD_INSTANCE_ID_FIELD] && ( )} @@ -174,44 +174,44 @@ export function LogsOverviewHighlights({ columns={columns} data-test-subj="logsExplorerFlyoutHighlightSectionOther" > - {formattedDoc[constants.LOG_FILE_PATH_FIELD] && ( + {formattedDoc[fieldConstants.LOG_FILE_PATH_FIELD] && ( )} - {formattedDoc[constants.DATASTREAM_DATASET_FIELD] && ( + {formattedDoc[fieldConstants.DATASTREAM_DATASET_FIELD] && ( )} - {formattedDoc[constants.DATASTREAM_NAMESPACE_FIELD] && ( + {formattedDoc[fieldConstants.DATASTREAM_NAMESPACE_FIELD] && ( )} - {formattedDoc[constants.AGENT_NAME_FIELD] && ( + {formattedDoc[fieldConstants.AGENT_NAME_FIELD] && ( )} @@ -219,3 +219,98 @@ export function LogsOverviewHighlights({ ); } + +const flyoutServiceLabel = i18n.translate('xpack.logsExplorer.flyoutDetail.label.service', { + defaultMessage: 'Service', +}); + +const flyoutTraceLabel = i18n.translate('xpack.logsExplorer.flyoutDetail.label.trace', { + defaultMessage: 'Trace', +}); + +const flyoutHostNameLabel = i18n.translate('xpack.logsExplorer.flyoutDetail.label.hostName', { + defaultMessage: 'Host name', +}); + +const serviceInfraAccordionTitle = i18n.translate( + 'xpack.logsExplorer.flyoutDetail.accordion.title.serviceInfra', + { + defaultMessage: 'Service & Infrastructure', + } +); + +const cloudAccordionTitle = i18n.translate( + 'xpack.logsExplorer.flyoutDetail.accordion.title.cloud', + { + defaultMessage: 'Cloud', + } +); + +const otherAccordionTitle = i18n.translate( + 'xpack.logsExplorer.flyoutDetail.accordion.title.other', + { + defaultMessage: 'Other', + } +); + +const flyoutOrchestratorClusterNameLabel = i18n.translate( + 'xpack.logsExplorer.flyoutDetail.label.orchestratorClusterName', + { + defaultMessage: 'Orchestrator cluster Name', + } +); + +const flyoutOrchestratorResourceIdLabel = i18n.translate( + 'xpack.logsExplorer.flyoutDetail.label.orchestratorResourceId', + { + defaultMessage: 'Orchestrator resource ID', + } +); + +const flyoutCloudProviderLabel = i18n.translate( + 'xpack.logsExplorer.flyoutDetail.label.cloudProvider', + { + defaultMessage: 'Cloud provider', + } +); + +const flyoutCloudRegionLabel = i18n.translate('xpack.logsExplorer.flyoutDetail.label.cloudRegion', { + defaultMessage: 'Cloud region', +}); + +const flyoutCloudAvailabilityZoneLabel = i18n.translate( + 'xpack.logsExplorer.flyoutDetail.label.cloudAvailabilityZone', + { + defaultMessage: 'Cloud availability zone', + } +); + +const flyoutCloudProjectIdLabel = i18n.translate( + 'xpack.logsExplorer.flyoutDetail.label.cloudProjectId', + { + defaultMessage: 'Cloud project ID', + } +); + +const flyoutCloudInstanceIdLabel = i18n.translate( + 'xpack.logsExplorer.flyoutDetail.label.cloudInstanceId', + { + defaultMessage: 'Cloud instance ID', + } +); + +const flyoutLogPathFileLabel = i18n.translate('xpack.logsExplorer.flyoutDetail.label.logPathFile', { + defaultMessage: 'Log path file', +}); + +const flyoutNamespaceLabel = i18n.translate('xpack.logsExplorer.flyoutDetail.label.namespace', { + defaultMessage: 'Namespace', +}); + +const flyoutDatasetLabel = i18n.translate('xpack.logsExplorer.flyoutDetail.label.dataset', { + defaultMessage: 'Dataset', +}); + +const flyoutShipperLabel = i18n.translate('xpack.logsExplorer.flyoutDetail.label.shipper', { + defaultMessage: 'Shipper', +}); From b97e8be4bc6eb047581565cf8e53599ae8f58b40 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Mon, 8 Apr 2024 12:25:08 +0200 Subject: [PATCH 07/28] wip(unified-doc-viewer): move all required components --- packages/kbn-discover-utils/index.ts | 4 + packages/kbn-discover-utils/src/types.ts | 38 ++++++ .../src/utils/get_document_overview.ts | 89 +++++++++++++ .../utils/get_message_field_with_fallbacks.ts | 27 ++++ .../kbn-discover-utils/src/utils/index.ts | 2 + .../logs_overview.tsx | 8 +- .../logs_overview_header.tsx | 44 ++++--- .../logs_overview_highlights.tsx | 121 +++++++++--------- .../sub_components/highlight_field.tsx | 2 +- .../sub_components/hover_popover_action.tsx | 4 +- .../sub_components/log_level.tsx | 46 +++++++ .../public/hooks/use_field_actions.tsx | 8 +- 12 files changed, 302 insertions(+), 91 deletions(-) create mode 100644 packages/kbn-discover-utils/src/utils/get_document_overview.ts create mode 100644 packages/kbn-discover-utils/src/utils/get_message_field_with_fallbacks.ts create mode 100644 src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/log_level.tsx diff --git a/packages/kbn-discover-utils/index.ts b/packages/kbn-discover-utils/index.ts index 902de66e0b2776..5724f196dbfebd 100644 --- a/packages/kbn-discover-utils/index.ts +++ b/packages/kbn-discover-utils/index.ts @@ -35,8 +35,12 @@ export { formatFieldValue, formatHit, getDocId, + getDocumentOverview, getIgnoredReason, + getMessageFieldWithFallbacks, getShouldShowFieldHandler, isNestedFieldParent, usePager, } from './src'; + +export * from './src/types'; diff --git a/packages/kbn-discover-utils/src/types.ts b/packages/kbn-discover-utils/src/types.ts index 1fcc7c6a07c7f3..d2603be89f9787 100644 --- a/packages/kbn-discover-utils/src/types.ts +++ b/packages/kbn-discover-utils/src/types.ts @@ -46,3 +46,41 @@ type FormattedHitPair = readonly [ * Pairs array for each field in the hit */ export type FormattedHit = FormattedHitPair[]; + +export interface DocumentOverview extends ResourceFields, StackTraceFields, CloudFields { + '@timestamp': string; + 'log.level'?: string; + message?: string; + 'error.message'?: string; + 'event.original'?: string; + 'trace.id'?: string; + 'log.file.path'?: string; + 'data_stream.namespace': string; + 'data_stream.dataset': string; +} + +export interface ResourceFields { + 'host.name'?: string; + 'service.name'?: string; + 'agent.name'?: string; + 'orchestrator.cluster.name'?: string; + 'orchestrator.cluster.id'?: string; + 'orchestrator.resource.id'?: string; + 'orchestrator.namespace'?: string; + 'container.name'?: string; + 'container.id'?: string; +} + +export interface StackTraceFields { + 'error.stack_trace'?: string; + 'error.exception.stacktrace'?: string; + 'error.log.stacktrace'?: string; +} + +export interface CloudFields { + 'cloud.provider'?: string; + 'cloud.region'?: string; + 'cloud.availability_zone'?: string; + 'cloud.project.id'?: string; + 'cloud.instance.id'?: string; +} diff --git a/packages/kbn-discover-utils/src/utils/get_document_overview.ts b/packages/kbn-discover-utils/src/utils/get_document_overview.ts new file mode 100644 index 00000000000000..a894f58e222583 --- /dev/null +++ b/packages/kbn-discover-utils/src/utils/get_document_overview.ts @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; +import type { DataView } from '@kbn/data-views-plugin/public'; +import { DataTableRecord, DocumentOverview, fieldConstants, formatFieldValue } from '../..'; + +export function getDocumentOverview( + doc: DataTableRecord, + { dataView, fieldFormats }: { dataView: DataView; fieldFormats: FieldFormatsStart } +): DocumentOverview { + const formatField = (field: T) => { + return ( + field in doc.flattened + ? formatFieldValue( + doc.flattened[field], + doc.raw, + fieldFormats, + dataView, + dataView.fields.getByName(field) + ) + : undefined + ) as DocumentOverview[T]; + }; + + const levelArray = doc.flattened[fieldConstants.LOG_LEVEL_FIELD]; + const level = + Array.isArray(levelArray) && levelArray.length ? levelArray[0].toLowerCase() : undefined; + const messageArray = doc.flattened[fieldConstants.MESSAGE_FIELD]; + const message = Array.isArray(messageArray) && messageArray.length ? messageArray[0] : undefined; + const errorMessageArray = doc.flattened[fieldConstants.ERROR_MESSAGE_FIELD]; + const errorMessage = + Array.isArray(errorMessageArray) && errorMessageArray.length ? errorMessageArray[0] : undefined; + const eventOriginalArray = doc.flattened[fieldConstants.EVENT_ORIGINAL_FIELD]; + const eventOriginal = + Array.isArray(eventOriginalArray) && eventOriginalArray.length + ? eventOriginalArray[0] + : undefined; + const timestamp = formatField(fieldConstants.TIMESTAMP_FIELD); + + // Service + const serviceName = formatField(fieldConstants.SERVICE_NAME_FIELD); + const traceId = formatField(fieldConstants.TRACE_ID_FIELD); + + // Infrastructure + const hostname = formatField(fieldConstants.HOST_NAME_FIELD); + const orchestratorClusterName = formatField(fieldConstants.ORCHESTRATOR_CLUSTER_NAME_FIELD); + const orchestratorResourceId = formatField(fieldConstants.ORCHESTRATOR_RESOURCE_ID_FIELD); + + // Cloud + const cloudProvider = formatField(fieldConstants.CLOUD_PROVIDER_FIELD); + const cloudRegion = formatField(fieldConstants.CLOUD_REGION_FIELD); + const cloudAz = formatField(fieldConstants.CLOUD_AVAILABILITY_ZONE_FIELD); + const cloudProjectId = formatField(fieldConstants.CLOUD_PROJECT_ID_FIELD); + const cloudInstanceId = formatField(fieldConstants.CLOUD_INSTANCE_ID_FIELD); + + // Other + const logFilePath = formatField(fieldConstants.LOG_FILE_PATH_FIELD); + const namespace = formatField(fieldConstants.DATASTREAM_NAMESPACE_FIELD); + const dataset = formatField(fieldConstants.DATASTREAM_DATASET_FIELD); + const agentName = formatField(fieldConstants.AGENT_NAME_FIELD); + + return { + [fieldConstants.LOG_LEVEL_FIELD]: level, + [fieldConstants.TIMESTAMP_FIELD]: timestamp, + [fieldConstants.MESSAGE_FIELD]: message, + [fieldConstants.ERROR_MESSAGE_FIELD]: errorMessage, + [fieldConstants.EVENT_ORIGINAL_FIELD]: eventOriginal, + [fieldConstants.SERVICE_NAME_FIELD]: serviceName, + [fieldConstants.TRACE_ID_FIELD]: traceId, + [fieldConstants.HOST_NAME_FIELD]: hostname, + [fieldConstants.ORCHESTRATOR_CLUSTER_NAME_FIELD]: orchestratorClusterName, + [fieldConstants.ORCHESTRATOR_RESOURCE_ID_FIELD]: orchestratorResourceId, + [fieldConstants.CLOUD_PROVIDER_FIELD]: cloudProvider, + [fieldConstants.CLOUD_REGION_FIELD]: cloudRegion, + [fieldConstants.CLOUD_AVAILABILITY_ZONE_FIELD]: cloudAz, + [fieldConstants.CLOUD_PROJECT_ID_FIELD]: cloudProjectId, + [fieldConstants.CLOUD_INSTANCE_ID_FIELD]: cloudInstanceId, + [fieldConstants.LOG_FILE_PATH_FIELD]: logFilePath, + [fieldConstants.DATASTREAM_NAMESPACE_FIELD]: namespace, + [fieldConstants.DATASTREAM_DATASET_FIELD]: dataset, + [fieldConstants.AGENT_NAME_FIELD]: agentName, + }; +} diff --git a/packages/kbn-discover-utils/src/utils/get_message_field_with_fallbacks.ts b/packages/kbn-discover-utils/src/utils/get_message_field_with_fallbacks.ts new file mode 100644 index 00000000000000..a4755e6e3fdd80 --- /dev/null +++ b/packages/kbn-discover-utils/src/utils/get_message_field_with_fallbacks.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { fieldConstants } from '..'; +import { DocumentOverview } from '../types'; + +export const getMessageFieldWithFallbacks = (doc: DocumentOverview) => { + const rankingOrder = [ + fieldConstants.MESSAGE_FIELD, + fieldConstants.ERROR_MESSAGE_FIELD, + fieldConstants.EVENT_ORIGINAL_FIELD, + ] as const; + + for (const rank of rankingOrder) { + if (doc[rank] !== undefined && doc[rank] !== null) { + return { field: rank, value: doc[rank] }; + } + } + + // If none of the ranks (fallbacks) are present + return { field: undefined }; +}; diff --git a/packages/kbn-discover-utils/src/utils/index.ts b/packages/kbn-discover-utils/src/utils/index.ts index 4828fcf82a4474..f7af629b7527e2 100644 --- a/packages/kbn-discover-utils/src/utils/index.ts +++ b/packages/kbn-discover-utils/src/utils/index.ts @@ -10,6 +10,8 @@ export * from './build_data_record'; export * from './format_hit'; export * from './format_value'; export * from './get_doc_id'; +export * from './get_document_overview'; export * from './get_ignored_reason'; +export * from './get_message_field_with_fallbacks'; export * from './get_should_show_field_handler'; export * from './nested_fields'; diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx index a38c95fdce129b..078cf2807a552f 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx @@ -8,10 +8,12 @@ import React from 'react'; import { DocViewRenderProps } from '@kbn/unified-doc-viewer/types'; -import { useDocDetail } from '../../hooks/use_doc_detail'; +import { getDocumentOverview } from '@kbn/discover-utils'; +import { EuiSpacer } from '@elastic/eui'; import { LogsOverviewHeader } from './logs_overview_header'; import { LogsOverviewHighlights } from './logs_overview_highlights'; import { FieldActionsProvider } from '../../hooks/use_field_actions'; +import { getUnifiedDocViewerServices } from '../../plugin'; export function LogsOverview({ columns, @@ -21,7 +23,8 @@ export function LogsOverview({ onAddColumn, onRemoveColumn, }: DocViewRenderProps) { - const parsedDoc = useDocDetail(hit, { dataView }); + const { fieldFormats } = getUnifiedDocViewerServices(); + const parsedDoc = getDocumentOverview(hit, { dataView, fieldFormats }); return ( + diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx index 66f978dace010a..5956b346096ffc 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx @@ -16,29 +16,35 @@ import { useGeneratedHtmlId, EuiTitle, } from '@elastic/eui'; -import { FlyoutDoc } from '../../../common/document'; -import { getMessageWithFallbacks } from '../../hooks/use_doc_detail'; -import { LogLevel } from '../common/log_level'; +import { + DocumentOverview, + fieldConstants, + getMessageFieldWithFallbacks, +} from '@kbn/discover-utils'; +import { i18n } from '@kbn/i18n'; import { Timestamp } from './sub_components/timestamp'; -import * as constants from '../../../common/constants'; -import { flyoutContentLabel } from '../common/translations'; import { HoverActionPopover } from './sub_components/hover_popover_action'; +import { LogLevel } from './sub_components/log_level'; + +export const contentLabel = i18n.translate('unifiedDocViewer.docView.logsOverview.label.content', { + defaultMessage: 'Content breakdown', +}); -export function LogsOverviewHeader({ doc }: { doc: FlyoutDoc }) { - const hasTimestamp = Boolean(doc[constants.TIMESTAMP_FIELD]); - const hasLogLevel = Boolean(doc[constants.LOG_LEVEL_FIELD]); +export function LogsOverviewHeader({ doc }: { doc: DocumentOverview }) { + const hasTimestamp = Boolean(doc[fieldConstants.TIMESTAMP_FIELD]); + const hasLogLevel = Boolean(doc[fieldConstants.LOG_LEVEL_FIELD]); const hasBadges = hasTimestamp || hasLogLevel; - const { field, value } = getMessageWithFallbacks(doc); + const { field, value } = getMessageFieldWithFallbacks(doc); const hasMessageField = field && value; const hasFlyoutHeader = hasMessageField || hasBadges; const accordionId = useGeneratedHtmlId({ - prefix: flyoutContentLabel, + prefix: contentLabel, }); const accordionTitle = ( -

{flyoutContentLabel}

+

{contentLabel}

); @@ -46,23 +52,19 @@ export function LogsOverviewHeader({ doc }: { doc: FlyoutDoc }) { {hasBadges && ( - {doc[constants.LOG_LEVEL_FIELD] && ( + {doc[fieldConstants.LOG_LEVEL_FIELD] && ( - + )} {hasTimestamp && ( - + )} @@ -111,7 +113,7 @@ export function LogsOverviewHeader({ doc }: { doc: FlyoutDoc }) { buttonContent={accordionTitle} paddingSize="m" initialIsOpen={true} - data-test-subj={`logsExplorerFlyoutHeaderSection${flyoutContentLabel}`} + data-test-subj={`logsExplorerFlyoutHeaderSection${contentLabel}`} > {hasMessageField ? contentField : logLevelAndTimestamp} diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_highlights.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_highlights.tsx index 4fc4cec0e8d876..f3544c7824567d 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_highlights.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_highlights.tsx @@ -12,18 +12,17 @@ import { useMeasure } from 'react-use/lib'; import { first } from 'lodash'; import { i18n } from '@kbn/i18n'; import { useEuiTheme } from '@elastic/eui'; -import { fieldConstants } from '@kbn/discover-utils'; -import { FlyoutDoc, LogDocument } from '../../../common/document'; +import { DataTableRecord, DocumentOverview, fieldConstants } from '@kbn/discover-utils'; import { HighlightField } from './sub_components/highlight_field'; import { HighlightSection } from './sub_components/highlight_section'; import { HighlightContainer } from './sub_components/highlight_container'; -interface FlyoutColumnWidth { +interface ColumnWidth { columns: 1 | 2 | 3; fieldWidth: number; } -export const useFlyoutColumnWidth = (width: number): FlyoutColumnWidth => { +export const useColumnWidth = (width: number): ColumnWidth => { const { euiTheme } = useEuiTheme(); const numberOfColumns = width > euiTheme.breakpoint.m ? 3 : width > euiTheme.breakpoint.s ? 2 : 1; @@ -40,65 +39,65 @@ export function LogsOverviewHighlights({ formattedDoc, flattenedDoc, }: { - formattedDoc: FlyoutDoc; - flattenedDoc: LogDocument['flattened']; + formattedDoc: DocumentOverview; + flattenedDoc: DataTableRecord['flattened']; }) { const [ref, dimensions] = useMeasure(); - const { columns, fieldWidth } = useFlyoutColumnWidth(dimensions.width); + const { columns, fieldWidth } = useColumnWidth(dimensions.width); return ( {/* Service & Infrastructure highlight */} {formattedDoc[fieldConstants.SERVICE_NAME_FIELD] && ( )} {formattedDoc[fieldConstants.HOST_NAME_FIELD] && ( )} {formattedDoc[fieldConstants.TRACE_ID_FIELD] && ( )} {formattedDoc[fieldConstants.ORCHESTRATOR_CLUSTER_NAME_FIELD] && ( )} {formattedDoc[fieldConstants.ORCHESTRATOR_RESOURCE_ID_FIELD] && ( @@ -108,11 +107,11 @@ export function LogsOverviewHighlights({ {formattedDoc[fieldConstants.CLOUD_PROVIDER_FIELD] && ( } - label={flyoutCloudProviderLabel} + label={CloudProviderLabel} value={flattenedDoc[fieldConstants.CLOUD_PROVIDER_FIELD]} width={fieldWidth} /> )} {formattedDoc[fieldConstants.CLOUD_REGION_FIELD] && ( )} {formattedDoc[fieldConstants.CLOUD_AVAILABILITY_ZONE_FIELD] && ( )} {formattedDoc[fieldConstants.CLOUD_PROJECT_ID_FIELD] && ( )} {formattedDoc[fieldConstants.CLOUD_INSTANCE_ID_FIELD] && ( @@ -172,34 +171,34 @@ export function LogsOverviewHighlights({ {formattedDoc[fieldConstants.LOG_FILE_PATH_FIELD] && ( )} {formattedDoc[fieldConstants.DATASTREAM_DATASET_FIELD] && ( )} {formattedDoc[fieldConstants.DATASTREAM_NAMESPACE_FIELD] && ( @@ -220,97 +219,97 @@ export function LogsOverviewHighlights({ ); } -const flyoutServiceLabel = i18n.translate('xpack.logsExplorer.flyoutDetail.label.service', { +const ServiceLabel = i18n.translate('unifiedDocViewer.docView.logsOverview.label.service', { defaultMessage: 'Service', }); -const flyoutTraceLabel = i18n.translate('xpack.logsExplorer.flyoutDetail.label.trace', { +const TraceLabel = i18n.translate('unifiedDocViewer.docView.logsOverview.label.trace', { defaultMessage: 'Trace', }); -const flyoutHostNameLabel = i18n.translate('xpack.logsExplorer.flyoutDetail.label.hostName', { +const HostNameLabel = i18n.translate('unifiedDocViewer.docView.logsOverview.label.hostName', { defaultMessage: 'Host name', }); const serviceInfraAccordionTitle = i18n.translate( - 'xpack.logsExplorer.flyoutDetail.accordion.title.serviceInfra', + 'unifiedDocViewer.docView.logsOverview.accordion.title.serviceInfra', { defaultMessage: 'Service & Infrastructure', } ); const cloudAccordionTitle = i18n.translate( - 'xpack.logsExplorer.flyoutDetail.accordion.title.cloud', + 'unifiedDocViewer.docView.logsOverview.accordion.title.cloud', { defaultMessage: 'Cloud', } ); const otherAccordionTitle = i18n.translate( - 'xpack.logsExplorer.flyoutDetail.accordion.title.other', + 'unifiedDocViewer.docView.logsOverview.accordion.title.other', { defaultMessage: 'Other', } ); -const flyoutOrchestratorClusterNameLabel = i18n.translate( - 'xpack.logsExplorer.flyoutDetail.label.orchestratorClusterName', +const OrchestratorClusterNameLabel = i18n.translate( + 'unifiedDocViewer.docView.logsOverview.label.orchestratorClusterName', { defaultMessage: 'Orchestrator cluster Name', } ); -const flyoutOrchestratorResourceIdLabel = i18n.translate( - 'xpack.logsExplorer.flyoutDetail.label.orchestratorResourceId', +const OrchestratorResourceIdLabel = i18n.translate( + 'unifiedDocViewer.docView.logsOverview.label.orchestratorResourceId', { defaultMessage: 'Orchestrator resource ID', } ); -const flyoutCloudProviderLabel = i18n.translate( - 'xpack.logsExplorer.flyoutDetail.label.cloudProvider', +const CloudProviderLabel = i18n.translate( + 'unifiedDocViewer.docView.logsOverview.label.cloudProvider', { defaultMessage: 'Cloud provider', } ); -const flyoutCloudRegionLabel = i18n.translate('xpack.logsExplorer.flyoutDetail.label.cloudRegion', { +const CloudRegionLabel = i18n.translate('unifiedDocViewer.docView.logsOverview.label.cloudRegion', { defaultMessage: 'Cloud region', }); -const flyoutCloudAvailabilityZoneLabel = i18n.translate( - 'xpack.logsExplorer.flyoutDetail.label.cloudAvailabilityZone', +const CloudAvailabilityZoneLabel = i18n.translate( + 'unifiedDocViewer.docView.logsOverview.label.cloudAvailabilityZone', { defaultMessage: 'Cloud availability zone', } ); -const flyoutCloudProjectIdLabel = i18n.translate( - 'xpack.logsExplorer.flyoutDetail.label.cloudProjectId', +const CloudProjectIdLabel = i18n.translate( + 'unifiedDocViewer.docView.logsOverview.label.cloudProjectId', { defaultMessage: 'Cloud project ID', } ); -const flyoutCloudInstanceIdLabel = i18n.translate( - 'xpack.logsExplorer.flyoutDetail.label.cloudInstanceId', +const CloudInstanceIdLabel = i18n.translate( + 'unifiedDocViewer.docView.logsOverview.label.cloudInstanceId', { defaultMessage: 'Cloud instance ID', } ); -const flyoutLogPathFileLabel = i18n.translate('xpack.logsExplorer.flyoutDetail.label.logPathFile', { +const LogPathFileLabel = i18n.translate('unifiedDocViewer.docView.logsOverview.label.logPathFile', { defaultMessage: 'Log path file', }); -const flyoutNamespaceLabel = i18n.translate('xpack.logsExplorer.flyoutDetail.label.namespace', { +const NamespaceLabel = i18n.translate('unifiedDocViewer.docView.logsOverview.label.namespace', { defaultMessage: 'Namespace', }); -const flyoutDatasetLabel = i18n.translate('xpack.logsExplorer.flyoutDetail.label.dataset', { +const DatasetLabel = i18n.translate('unifiedDocViewer.docView.logsOverview.label.dataset', { defaultMessage: 'Dataset', }); -const flyoutShipperLabel = i18n.translate('xpack.logsExplorer.flyoutDetail.label.shipper', { +const ShipperLabel = i18n.translate('unifiedDocViewer.docView.logsOverview.label.shipper', { defaultMessage: 'Shipper', }); diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_field.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_field.tsx index 1ddccf79cdb700..fa9043d166d45b 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_field.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_field.tsx @@ -28,7 +28,7 @@ interface HighlightFieldProps { formattedValue: string; icon?: ReactNode; label: string; - value?: string; + value?: unknown; width: number; } diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/hover_popover_action.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/hover_popover_action.tsx index 0c5e3b8d4239a4..a726a37149754f 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/hover_popover_action.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/hover_popover_action.tsx @@ -21,8 +21,8 @@ import { useUIFieldActions } from '../../../hooks/use_field_actions'; interface HoverPopoverActionProps { children: React.ReactChild; field: string; - value: string; - title?: string; + value: unknown; + title?: unknown; anchorPosition?: PopoverAnchorPosition; display?: EuiPopoverProps['display']; } diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/log_level.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/log_level.tsx new file mode 100644 index 00000000000000..349752f99fa3d7 --- /dev/null +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/log_level.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { EuiBadge, useEuiTheme } from '@elastic/eui'; +import { css } from '@emotion/react'; +import { DocumentOverview } from '@kbn/discover-utils'; + +const LEVEL_DICT = { + error: 'danger', + warn: 'warning', + info: 'primary', + debug: 'accent', +} as const; + +type Level = keyof typeof LEVEL_DICT; + +interface LogLevelProps { + level: DocumentOverview['log.level']; +} + +export function LogLevel({ level }: LogLevelProps) { + const { euiTheme } = useEuiTheme(); + + if (!level) return null; + const colorName = LEVEL_DICT[level as Level]; + const computedColor = colorName ? euiTheme.colors[colorName] : null; + + return ( + + {level} + + ); +} diff --git a/src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx b/src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx index 6b2d6d3f8a64d0..5e494bd2f29c77 100644 --- a/src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx +++ b/src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx @@ -17,7 +17,7 @@ interface WithFieldParam { } interface WithValueParam { - value: string; + value: unknown; } interface TFieldActionParams extends WithFieldParam, WithValueParam {} @@ -67,13 +67,13 @@ export const useUIFieldActions = ({ field, value }: TFieldActionParams): TFieldA { id: 'addFilterInAction', iconType: 'plusInCircle', - label: actionFilterForText(value), + label: actionFilterForText(value as string), onClick: () => actions.addFilterIn({ field, value }), }, { id: 'addFilterOutremoveFromFilterAction', iconType: 'minusInCircle', - label: actionFilterOutText(value), + label: actionFilterOutText(value as string), onClick: () => actions.addFilterOut({ field, value }), }, { @@ -92,7 +92,7 @@ export const useUIFieldActions = ({ field, value }: TFieldActionParams): TFieldA id: 'copyToClipboardAction', iconType: 'copyClipboard', label: copyToClipboardLabel, - onClick: () => actions.copyToClipboard(value), + onClick: () => actions.copyToClipboard(value as string), }, ], [actions, field, value] From 6e9438e2a451b8ce835324e44db59df7715d91c3 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Mon, 8 Apr 2024 12:44:09 +0200 Subject: [PATCH 08/28] wip(unified-doc-viewer): disable e2e tests for logs explorer flyout --- .../unified_doc_viewer/public/hooks/use_field_actions.tsx | 6 ++++++ .../functional/apps/observability_logs_explorer/index.ts | 8 ++++---- .../observability/observability_logs_explorer/index.ts | 8 ++++---- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx b/src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx index 5e494bd2f29c77..2a08b07f60d8bb 100644 --- a/src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx +++ b/src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx @@ -34,6 +34,9 @@ type UseFieldActionsDeps = Pick< 'columns' | 'filter' | 'onAddColumn' | 'onRemoveColumn' >; +/** + * Higher level hook that wraps the logic for the requires actions on a field. + */ const useFieldActions = ({ columns, filter, onAddColumn, onRemoveColumn }: UseFieldActionsDeps) => { return useMemo( () => ({ @@ -59,6 +62,9 @@ const useFieldActions = ({ columns, filter, onAddColumn, onRemoveColumn }: UseFi export const [FieldActionsProvider, useFieldActionsContext] = createContainer(useFieldActions); +/** + * This is a preset of the UI elements and related actions that can be used to build an action bar anywhere in a DocView + */ export const useUIFieldActions = ({ field, value }: TFieldActionParams): TFieldAction[] => { const actions = useFieldActionsContext(); diff --git a/x-pack/test/functional/apps/observability_logs_explorer/index.ts b/x-pack/test/functional/apps/observability_logs_explorer/index.ts index 640f552317f219..29dfc72df0ea7e 100644 --- a/x-pack/test/functional/apps/observability_logs_explorer/index.ts +++ b/x-pack/test/functional/apps/observability_logs_explorer/index.ts @@ -11,13 +11,13 @@ export default function ({ loadTestFile }: FtrProviderContext) { describe('Observability Logs Explorer', function () { loadTestFile(require.resolve('./app')); loadTestFile(require.resolve('./columns_selection')); + loadTestFile(require.resolve('./custom_control_columns')); loadTestFile(require.resolve('./data_source_selection_state')); loadTestFile(require.resolve('./data_source_selector')); + loadTestFile(require.resolve('./field_list')); loadTestFile(require.resolve('./filter_controls')); - loadTestFile(require.resolve('./flyout')); + // loadTestFile(require.resolve('./flyout_highlights')); + // loadTestFile(require.resolve('./flyout')); loadTestFile(require.resolve('./header_menu')); - loadTestFile(require.resolve('./flyout_highlights')); - loadTestFile(require.resolve('./custom_control_columns')); - loadTestFile(require.resolve('./field_list')); }); } diff --git a/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/index.ts b/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/index.ts index e445761dd6c19b..4036df7cf97bec 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/index.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/index.ts @@ -11,13 +11,13 @@ export default function ({ loadTestFile }: FtrProviderContext) { describe('Observability Logs Explorer', function () { loadTestFile(require.resolve('./app')); loadTestFile(require.resolve('./columns_selection')); + loadTestFile(require.resolve('./custom_control_columns')); loadTestFile(require.resolve('./data_source_selection_state')); loadTestFile(require.resolve('./data_source_selector')); + loadTestFile(require.resolve('./field_list')); loadTestFile(require.resolve('./filter_controls')); - loadTestFile(require.resolve('./flyout')); + // loadTestFile(require.resolve('./flyout_highlights')); + // loadTestFile(require.resolve('./flyout')); loadTestFile(require.resolve('./header_menu')); - loadTestFile(require.resolve('./flyout_highlights')); - loadTestFile(require.resolve('./custom_control_columns')); - loadTestFile(require.resolve('./field_list')); }); } From bbee3417de9f5959f94fad2fc3ca9163a8d4abb8 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Mon, 8 Apr 2024 13:04:41 +0200 Subject: [PATCH 09/28] wip(unified-doc-viewer): remove flyout code from logs explorer --- .../flyout_detail/flyout_detail.tsx | 24 -- .../flyout_detail/flyout_header.tsx | 120 ---------- .../flyout_detail/flyout_highlights.tsx | 219 ------------------ .../public/components/flyout_detail/index.ts | 8 - .../sub_components/highlight_container.tsx | 40 ---- .../sub_components/highlight_field.tsx | 94 -------- .../highlight_field_description.tsx | 33 --- .../sub_components/highlight_section.tsx | 84 ------- .../sub_components/hover_popover_action.tsx | 87 ------- .../sub_components/timestamp.tsx | 24 -- 10 files changed, 733 deletions(-) delete mode 100644 x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/flyout_detail.tsx delete mode 100644 x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/flyout_header.tsx delete mode 100644 x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/flyout_highlights.tsx delete mode 100644 x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/index.ts delete mode 100644 x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/sub_components/highlight_container.tsx delete mode 100644 x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx delete mode 100644 x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/sub_components/highlight_field_description.tsx delete mode 100644 x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/sub_components/highlight_section.tsx delete mode 100644 x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/sub_components/hover_popover_action.tsx delete mode 100644 x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/sub_components/timestamp.tsx diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/flyout_detail.tsx b/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/flyout_detail.tsx deleted file mode 100644 index 6beda5e75b0226..00000000000000 --- a/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/flyout_detail.tsx +++ /dev/null @@ -1,24 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { LogsExplorerFlyoutContentProps } from '../../customizations/types'; -import { useDocDetail } from '../../hooks/use_doc_detail'; -import { FlyoutHeader } from './flyout_header'; -import { FlyoutHighlights } from './flyout_highlights'; -import { DiscoverActionsProvider } from '../../hooks/use_discover_action'; - -export function FlyoutDetail({ dataView, doc, actions }: LogsExplorerFlyoutContentProps) { - const parsedDoc = useDocDetail(doc, { dataView }); - - return ( - - - - - ); -} diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/flyout_header.tsx b/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/flyout_header.tsx deleted file mode 100644 index b26f90145b6a52..00000000000000 --- a/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/flyout_header.tsx +++ /dev/null @@ -1,120 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { - EuiCodeBlock, - EuiFlexGroup, - EuiFlexItem, - EuiText, - EuiAccordion, - useGeneratedHtmlId, - EuiTitle, -} from '@elastic/eui'; -import { FlyoutDoc } from '../../../common/document'; -import { getMessageWithFallbacks } from '../../hooks/use_doc_detail'; -import { LogLevel } from '../common/log_level'; -import { Timestamp } from './sub_components/timestamp'; -import * as constants from '../../../common/constants'; -import { flyoutContentLabel } from '../common/translations'; -import { HoverActionPopover } from './sub_components/hover_popover_action'; - -export function FlyoutHeader({ doc }: { doc: FlyoutDoc }) { - const hasTimestamp = Boolean(doc[constants.TIMESTAMP_FIELD]); - const hasLogLevel = Boolean(doc[constants.LOG_LEVEL_FIELD]); - const hasBadges = hasTimestamp || hasLogLevel; - const { field, value } = getMessageWithFallbacks(doc); - const hasMessageField = field && value; - const hasFlyoutHeader = hasMessageField || hasBadges; - - const accordionId = useGeneratedHtmlId({ - prefix: flyoutContentLabel, - }); - - const accordionTitle = ( - -

{flyoutContentLabel}

-
- ); - - const logLevelAndTimestamp = ( - - {hasBadges && ( - - {doc[constants.LOG_LEVEL_FIELD] && ( - - - - - - )} - {hasTimestamp && ( - - - - )} - - )} - - ); - - const contentField = hasMessageField && ( - - - - - - - {field} - - - {logLevelAndTimestamp} - - - - - - {value} - - - - - - ); - - return hasFlyoutHeader ? ( - - - {hasMessageField ? contentField : logLevelAndTimestamp} - - - ) : null; -} diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/flyout_highlights.tsx b/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/flyout_highlights.tsx deleted file mode 100644 index 5e8af29566372f..00000000000000 --- a/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/flyout_highlights.tsx +++ /dev/null @@ -1,219 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import React from 'react'; -import { CloudProvider, CloudProviderIcon } from '@kbn/custom-icons'; -import { useMeasure } from 'react-use/lib'; -import { first } from 'lodash'; -import { FlyoutDoc, LogDocument } from '../../../common/document'; -import * as constants from '../../../common/constants'; -import { HighlightField } from './sub_components/highlight_field'; -import { - cloudAccordionTitle, - flyoutCloudAvailabilityZoneLabel, - flyoutCloudInstanceIdLabel, - flyoutCloudProjectIdLabel, - flyoutCloudProviderLabel, - flyoutCloudRegionLabel, - flyoutDatasetLabel, - flyoutHostNameLabel, - flyoutLogPathFileLabel, - flyoutNamespaceLabel, - flyoutOrchestratorClusterNameLabel, - flyoutOrchestratorResourceIdLabel, - flyoutServiceLabel, - flyoutShipperLabel, - flyoutTraceLabel, - otherAccordionTitle, - serviceInfraAccordionTitle, -} from '../common/translations'; -import { HighlightSection } from './sub_components/highlight_section'; -import { HighlightContainer } from './sub_components/highlight_container'; -import { useFlyoutColumnWidth } from '../../hooks/use_flyouot_column_width'; - -export function FlyoutHighlights({ - formattedDoc, - flattenedDoc, -}: { - formattedDoc: FlyoutDoc; - flattenedDoc: LogDocument['flattened']; -}) { - const [ref, dimensions] = useMeasure(); - const { columns, fieldWidth } = useFlyoutColumnWidth(dimensions.width); - return ( - - {/* Service & Infrastructure highlight */} - - {formattedDoc[constants.SERVICE_NAME_FIELD] && ( - - )} - {formattedDoc[constants.HOST_NAME_FIELD] && ( - - )} - {formattedDoc[constants.TRACE_ID_FIELD] && ( - - )} - {formattedDoc[constants.ORCHESTRATOR_CLUSTER_NAME_FIELD] && ( - - )} - {formattedDoc[constants.ORCHESTRATOR_RESOURCE_ID_FIELD] && ( - - )} - - {/* Cloud highlight */} - - {formattedDoc[constants.CLOUD_PROVIDER_FIELD] && ( - - } - label={flyoutCloudProviderLabel} - value={flattenedDoc[constants.CLOUD_PROVIDER_FIELD]} - width={fieldWidth} - /> - )} - {formattedDoc[constants.CLOUD_REGION_FIELD] && ( - - )} - {formattedDoc[constants.CLOUD_AVAILABILITY_ZONE_FIELD] && ( - - )} - {formattedDoc[constants.CLOUD_PROJECT_ID_FIELD] && ( - - )} - {formattedDoc[constants.CLOUD_INSTANCE_ID_FIELD] && ( - - )} - - {/* Other highlights */} - - {formattedDoc[constants.LOG_FILE_PATH_FIELD] && ( - - )} - {formattedDoc[constants.DATASTREAM_DATASET_FIELD] && ( - - )} - {formattedDoc[constants.DATASTREAM_NAMESPACE_FIELD] && ( - - )} - {formattedDoc[constants.AGENT_NAME_FIELD] && ( - - )} - - - ); -} diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/index.ts b/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/index.ts deleted file mode 100644 index 78f7d3ac35c505..00000000000000 --- a/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export * from './flyout_detail'; diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/sub_components/highlight_container.tsx b/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/sub_components/highlight_container.tsx deleted file mode 100644 index 4cef6f7b0850c6..00000000000000 --- a/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/sub_components/highlight_container.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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { EuiHorizontalRule } from '@elastic/eui'; - -interface HighlightContainerProps { - children: React.ReactNode; -} - -const hasNonUndefinedSubChild = (children: React.ReactNode[]): boolean => { - return children.some((child) => { - if (React.isValidElement(child)) { - const subChildren = React.Children.toArray(child.props.children); - return subChildren.some((subChild) => subChild !== undefined && subChild !== null); - } - return false; - }); -}; - -export const HighlightContainer = React.forwardRef( - ({ children }, ref) => { - const validChildren = React.Children.toArray(children).filter(Boolean); - const hasChildren = validChildren.length > 0; - const shouldRender = hasChildren && hasNonUndefinedSubChild(validChildren); - - const flexChildren = validChildren.map((child, idx) =>
{child}
); - - return shouldRender ? ( -
- - {flexChildren} -
- ) : null; - } -); diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx b/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx deleted file mode 100644 index 338643b3f302f8..00000000000000 --- a/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx +++ /dev/null @@ -1,94 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - EuiBadge, - EuiFlexGroup, - EuiFlexItem, - EuiText, - EuiTextTruncate, - EuiTitle, - useEuiTheme, -} from '@elastic/eui'; -import { css } from '@emotion/react'; -import React, { ReactNode } from 'react'; -import { dynamic } from '@kbn/shared-ux-utility'; -import { HoverActionPopover } from './hover_popover_action'; - -const HighlightFieldDescription = dynamic(() => import('./highlight_field_description')); - -interface HighlightFieldProps { - useBadge?: boolean; - field: string; - formattedValue: string; - icon?: ReactNode; - label: string; - value?: string; - width: number; -} - -export function HighlightField({ - useBadge = false, - field, - formattedValue, - icon, - label, - value, - width, - ...props -}: HighlightFieldProps) { - const { euiTheme } = useEuiTheme(); - - return formattedValue && value ? ( - - - - - - {label} - - - - - - - - - - - {icon && {icon}} - - - {(truncatedText: string) => - useBadge ? ( - {truncatedText} - ) : ( - - ) - } - - - - - - - ) : null; -} diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/sub_components/highlight_field_description.tsx b/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/sub_components/highlight_field_description.tsx deleted file mode 100644 index 0ecf46a527d86c..00000000000000 --- a/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/sub_components/highlight_field_description.tsx +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiFlexGroup, EuiFlexItem, EuiIconTip } from '@elastic/eui'; -import { EcsFlat } from '@elastic/ecs'; -import { FieldIcon } from '@kbn/react-field'; -import React from 'react'; - -export function HighlightFieldDescription({ fieldName }: { fieldName: string }) { - const { short, type } = EcsFlat[fieldName as keyof typeof EcsFlat] ?? {}; - - if (!short) return null; - - const title = ( - - {type && ( - - - - )} - {fieldName} - - ); - - return ; -} - -// eslint-disable-next-line import/no-default-export -export default HighlightFieldDescription; diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/sub_components/highlight_section.tsx b/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/sub_components/highlight_section.tsx deleted file mode 100644 index 80260a6cff11c5..00000000000000 --- a/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/sub_components/highlight_section.tsx +++ /dev/null @@ -1,84 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useState } from 'react'; -import { - EuiAccordion, - EuiFlexGrid, - EuiHorizontalRule, - EuiTitle, - EuiFlexItem, - useGeneratedHtmlId, - EuiButtonEmpty, -} from '@elastic/eui'; -import { flyoutAccordionShowMoreText } from '../../common/translations'; - -interface HighlightSectionProps { - title: string; - children: React.ReactNode; - columns: 1 | 2 | 3; -} - -const CHILDREN_PER_SECTION: 3 | 6 | 9 = 6; - -export function HighlightSection({ title, children, columns, ...props }: HighlightSectionProps) { - const validChildren = React.Children.toArray(children).filter(Boolean); - const childLength = validChildren.length; - const shouldRenderSection = childLength > 0; - const limitedChildren = validChildren.slice(0, CHILDREN_PER_SECTION - 1); - const [showMore, setShowMore] = useState(childLength > CHILDREN_PER_SECTION); - - const accordionId = useGeneratedHtmlId({ - prefix: title, - }); - - const hiddenCount = childLength - limitedChildren.length; - - const showMoreButtonLabel = flyoutAccordionShowMoreText(hiddenCount); - const showMoreButton = ( - { - setShowMore(false); - }} - > - {showMoreButtonLabel} - - ); - - limitedChildren.push(showMoreButton); - - const accordionTitle = ( - -

{title}

-
- ); - - const flexChildren = (showMore ? limitedChildren : validChildren).map((child, idx) => ( - {child} - )); - - return shouldRenderSection ? ( - <> - - - {flexChildren} - - - - - ) : null; -} diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/sub_components/hover_popover_action.tsx b/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/sub_components/hover_popover_action.tsx deleted file mode 100644 index 1f9855c28a86b6..00000000000000 --- a/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/sub_components/hover_popover_action.tsx +++ /dev/null @@ -1,87 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useRef, useState } from 'react'; -import { - EuiFlexGroup, - EuiPopover, - EuiButtonIcon, - EuiPopoverTitle, - EuiToolTip, - PopoverAnchorPosition, - type EuiPopoverProps, -} from '@elastic/eui'; -import { useHoverActions } from '../../../hooks/use_hover_actions'; - -interface HoverPopoverActionProps { - children: React.ReactChild; - field: string; - value: string; - title?: string; - anchorPosition?: PopoverAnchorPosition; - display?: EuiPopoverProps['display']; -} - -export const HoverActionPopover = ({ - children, - title, - field, - value, - anchorPosition = 'upCenter', - display = 'inline-block', -}: HoverPopoverActionProps) => { - const [isPopoverOpen, setIsPopoverOpen] = useState(false); - const leaveTimer = useRef(null); - const hoverActions = useHoverActions({ field, value }); - - // The timeout hack is required because we are using a Popover which ideally should be used with a mouseclick, - // but we are using it as a Tooltip. Which means we now need to manually handle the open and close - // state using the mouse hover events. This cause the popover to close even before the user could - // navigate actions inside it. Hence, to prevent this, we need this hack - const onMouseEnter = () => { - if (leaveTimer.current) { - clearTimeout(leaveTimer.current); - } - setIsPopoverOpen(true); - }; - - const onMouseLeave = () => { - leaveTimer.current = setTimeout(() => setIsPopoverOpen(false), 100); - }; - - return ( -
- - {title && ( - - {title} - - )} - - {hoverActions.map((action) => ( - - action.onClick()} - /> - - ))} - - -
- ); -}; diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/sub_components/timestamp.tsx b/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/sub_components/timestamp.tsx deleted file mode 100644 index da9e656a9f01d9..00000000000000 --- a/x-pack/plugins/observability_solution/logs_explorer/public/components/flyout_detail/sub_components/timestamp.tsx +++ /dev/null @@ -1,24 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { EuiBadge } from '@elastic/eui'; -import { FlyoutDoc } from '../../../../common/document'; - -interface TimestampProps { - timestamp: FlyoutDoc['@timestamp']; -} - -export function Timestamp({ timestamp }: TimestampProps) { - if (!timestamp) return null; - - return ( - - {timestamp} - - ); -} From 319ca9a770daa540e8830d16957c1e503f663dd2 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Mon, 8 Apr 2024 13:05:09 +0200 Subject: [PATCH 10/28] wip(unified-doc-viewer): update doc view registry code --- .../src/services/doc_views_registry.test.tsx | 19 +++++++++++++++++++ .../src/services/doc_views_registry.ts | 14 ++++++++++++-- .../src/services/types.ts | 2 +- src/plugins/unified_doc_viewer/tsconfig.json | 3 ++- 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.test.tsx b/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.test.tsx index e8f631814d2875..dbc0c93e8ba448 100644 --- a/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.test.tsx +++ b/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.test.tsx @@ -74,6 +74,25 @@ describe('DocViewerRegistry', () => { }); }); + describe('#enableById & #disableById', () => { + test('should enable/disable a doc view given the passed id', () => { + const registry = new DocViewsRegistry([fnDocView, componentDocView]); + + const docViews = registry.getAll(); + + expect(docViews[0]).toHaveProperty('enabled', true); + expect(docViews[1]).toHaveProperty('enabled', true); + + registry.disableById('function-doc-view'); + + expect(registry.getAll()[0]).toHaveProperty('enabled', false); + + registry.enableById('function-doc-view'); + + expect(registry.getAll()[0]).toHaveProperty('enabled', true); + }); + }); + describe('#clone', () => { test('should return a new DocViewRegistry instance starting from the current one', () => { const registry = new DocViewsRegistry([fnDocView, componentDocView]); diff --git a/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts b/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts index cfca97fceb18ca..b580c040a70958 100644 --- a/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts +++ b/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts @@ -15,6 +15,10 @@ export enum ElasticRequestState { NotFoundDataView, } +const defaultDocViewConfig = { + enabled: true, +}; + export class DocViewsRegistry { private docViews: Map; @@ -22,7 +26,9 @@ export class DocViewsRegistry { if (initialValue instanceof DocViewsRegistry) { this.docViews = new Map(initialValue.docViews); } else if (Array.isArray(initialValue)) { - this.docViews = new Map(initialValue.map((docView) => [docView.id, docView])); + this.docViews = new Map( + initialValue.map((docView) => [docView.id, this.createDocView(docView)]) + ); } else { this.docViews = new Map(); } @@ -41,7 +47,7 @@ export class DocViewsRegistry { ); } - this.docViews.set(docView.id, docView); + this.docViews.set(docView.id, this.createDocView(docView)); // Sort the doc views at insertion time to perform this operation once and not on every retrieval. this.sortDocViews(); } @@ -83,4 +89,8 @@ export class DocViewsRegistry { this.docViews = new Map(sortedEntries); } + + private createDocView(docView: DocView) { + return { ...defaultDocViewConfig, ...docView }; + } } diff --git a/packages/kbn-unified-doc-viewer/src/services/types.ts b/packages/kbn-unified-doc-viewer/src/services/types.ts index 8037979f61d1e9..115583c738bb09 100644 --- a/packages/kbn-unified-doc-viewer/src/services/types.ts +++ b/packages/kbn-unified-doc-viewer/src/services/types.ts @@ -60,7 +60,7 @@ export interface BaseDocViewInput { id: string; order: number; title: string; - enabled: boolean; + enabled?: boolean; } export interface RenderDocViewInput extends BaseDocViewInput { diff --git a/src/plugins/unified_doc_viewer/tsconfig.json b/src/plugins/unified_doc_viewer/tsconfig.json index 07139b46059b88..821f71bc3fd217 100644 --- a/src/plugins/unified_doc_viewer/tsconfig.json +++ b/src/plugins/unified_doc_viewer/tsconfig.json @@ -25,7 +25,8 @@ "@kbn/core-ui-settings-browser-mocks", "@kbn/field-utils", "@kbn/code-editor", - "@kbn/code-editor-mock" + "@kbn/code-editor-mock", + "@kbn/custom-icons" ], "exclude": [ "target/**/*", From 142239f485c665fcd648cc1586e67be883bf9ce0 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Mon, 8 Apr 2024 14:43:33 +0200 Subject: [PATCH 11/28] refactor(logs-explorer): disable legacy code --- .../public/customizations/custom_flyout_content.tsx | 4 ++-- .../public/customizations/logs_explorer_profile.tsx | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/customizations/custom_flyout_content.tsx b/x-pack/plugins/observability_solution/logs_explorer/public/customizations/custom_flyout_content.tsx index 5f496fb8d189b0..9b60c5eabc5ae6 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/public/customizations/custom_flyout_content.tsx +++ b/x-pack/plugins/observability_solution/logs_explorer/public/customizations/custom_flyout_content.tsx @@ -8,7 +8,6 @@ import React, { useMemo } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { DocViewRenderProps } from '@kbn/unified-doc-viewer/types'; -import { FlyoutDetail } from '../components/flyout_detail/flyout_detail'; import { LogsExplorerFlyoutContentProps } from './types'; import { useLogsExplorerControllerContext } from '../controller'; import { LogDocument } from '../../common/document'; @@ -55,7 +54,8 @@ const CustomFlyoutContent = ({ const renderContent = ({ actions, dataView, doc }: LogsExplorerFlyoutContentProps) => ( - + {/* TOREMOVE */} + {/* */} ); diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/customizations/logs_explorer_profile.tsx b/x-pack/plugins/observability_solution/logs_explorer/public/customizations/logs_explorer_profile.tsx index 73b7c70a69607c..df0d51db88bd6f 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/public/customizations/logs_explorer_profile.tsx +++ b/x-pack/plugins/observability_solution/logs_explorer/public/customizations/logs_explorer_profile.tsx @@ -12,7 +12,6 @@ import { i18n } from '@kbn/i18n'; import { waitFor } from 'xstate/lib/waitFor'; import { dynamic } from '@kbn/shared-ux-utility'; import type { LogsExplorerController } from '../controller'; -import { LogsExplorerControllerProvider } from '../controller/provider'; import type { LogsExplorerStartDeps } from '../types'; import { useKibanaContextForPluginProvider } from '../utils/use_kibana'; import { createCustomSearchBar } from './custom_search_bar'; @@ -23,7 +22,6 @@ import { createCustomUnifiedHistogram } from './custom_unified_histogram'; const LazyCustomDataSourceFilters = dynamic(() => import('./custom_data_source_filters')); const LazyCustomDataSourceSelector = dynamic(() => import('./custom_data_source_selector')); -const LazyCustomFlyoutContent = dynamic(() => import('./custom_flyout_content')); export interface CreateLogsExplorerProfileCustomizationsDeps { core: CoreStart; From f08fe4492ae637aee325c1bec053441107548799 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 8 Apr 2024 12:50:42 +0000 Subject: [PATCH 12/28] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- src/plugins/unified_doc_viewer/tsconfig.json | 3 ++- .../plugins/observability_solution/logs_explorer/tsconfig.json | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/unified_doc_viewer/tsconfig.json b/src/plugins/unified_doc_viewer/tsconfig.json index 821f71bc3fd217..6fbcf0ebbac847 100644 --- a/src/plugins/unified_doc_viewer/tsconfig.json +++ b/src/plugins/unified_doc_viewer/tsconfig.json @@ -26,7 +26,8 @@ "@kbn/field-utils", "@kbn/code-editor", "@kbn/code-editor-mock", - "@kbn/custom-icons" + "@kbn/custom-icons", + "@kbn/react-field" ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/observability_solution/logs_explorer/tsconfig.json b/x-pack/plugins/observability_solution/logs_explorer/tsconfig.json index e8c54c344ac51d..7c4224eadac192 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/tsconfig.json +++ b/x-pack/plugins/observability_solution/logs_explorer/tsconfig.json @@ -37,7 +37,6 @@ "@kbn/kibana-utils-plugin", "@kbn/management-settings-ids", "@kbn/navigation-plugin", - "@kbn/react-field", "@kbn/router-utils", "@kbn/share-plugin", "@kbn/shared-ux-utility", From ec4ccc763b75725ab56b1735df125e782a9be7d9 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Mon, 8 Apr 2024 16:15:33 +0200 Subject: [PATCH 13/28] refactor(unified-doc-views): update clone for doc views --- .../kbn-unified-doc-viewer/src/services/doc_views_registry.ts | 2 +- src/plugins/unified_doc_viewer/public/plugin.tsx | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts b/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts index b580c040a70958..e2c01c7d530c33 100644 --- a/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts +++ b/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts @@ -79,7 +79,7 @@ export class DocViewsRegistry { } clone() { - return new DocViewsRegistry(this); + return new DocViewsRegistry(this.getAll().map(this.createDocView)); } private sortDocViews() { diff --git a/src/plugins/unified_doc_viewer/public/plugin.tsx b/src/plugins/unified_doc_viewer/public/plugin.tsx index c9812faeaba815..9e8570ba349caf 100644 --- a/src/plugins/unified_doc_viewer/public/plugin.tsx +++ b/src/plugins/unified_doc_viewer/public/plugin.tsx @@ -74,7 +74,6 @@ export class UnifiedDocViewerPublicPlugin defaultMessage: 'Table', }), order: 10, - enabled: true, component: (props) => { const { uiSettings } = getUnifiedDocViewerServices(); const LazyDocView = uiSettings.get(DOC_TABLE_LEGACY) @@ -91,7 +90,6 @@ export class UnifiedDocViewerPublicPlugin defaultMessage: 'JSON', }), order: 20, - enabled: true, component: ({ hit, dataView, textBasedHits }) => { return ( Date: Tue, 9 Apr 2024 09:13:24 +0200 Subject: [PATCH 14/28] refactor(unified-doc-views): i18n lint --- x-pack/plugins/translations/translations/fr-FR.json | 1 - x-pack/plugins/translations/translations/ja-JP.json | 1 - x-pack/plugins/translations/translations/zh-CN.json | 1 - 3 files changed, 3 deletions(-) diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index d1ba9e9ae24b11..9ea76f84877fcc 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -24173,7 +24173,6 @@ "xpack.logsExplorer.flyoutDetail.accordion.title.cloud": "Cloud", "xpack.logsExplorer.flyoutDetail.accordion.title.other": "Autre", "xpack.logsExplorer.flyoutDetail.accordion.title.serviceInfra": "Service et Infrastructure", - "xpack.logsExplorer.flyoutDetail.docViews.overview": "Aperçu", "xpack.logsExplorer.flyoutDetail.label.cloudAvailabilityZone": "Zone de disponibilité du cloud", "xpack.logsExplorer.flyoutDetail.label.cloudInstanceId": "ID d'instance du cloud", "xpack.logsExplorer.flyoutDetail.label.cloudProjectId": "ID de projet du cloud", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 6368ef001c711c..e910268c8c0236 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -24148,7 +24148,6 @@ "xpack.logsExplorer.flyoutDetail.accordion.title.cloud": "クラウド", "xpack.logsExplorer.flyoutDetail.accordion.title.other": "その他", "xpack.logsExplorer.flyoutDetail.accordion.title.serviceInfra": "サービスとインフラストラクチャー", - "xpack.logsExplorer.flyoutDetail.docViews.overview": "概要", "xpack.logsExplorer.flyoutDetail.label.cloudAvailabilityZone": "クラウドアベイラビリティゾーン", "xpack.logsExplorer.flyoutDetail.label.cloudInstanceId": "クラウドインスタンスID", "xpack.logsExplorer.flyoutDetail.label.cloudProjectId": "クラウドプロジェクトID", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 595ef62714a472..48ba6203bc52dd 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -24181,7 +24181,6 @@ "xpack.logsExplorer.flyoutDetail.accordion.title.cloud": "云", "xpack.logsExplorer.flyoutDetail.accordion.title.other": "其他", "xpack.logsExplorer.flyoutDetail.accordion.title.serviceInfra": "服务和基础设施", - "xpack.logsExplorer.flyoutDetail.docViews.overview": "概览", "xpack.logsExplorer.flyoutDetail.label.cloudAvailabilityZone": "云可用区", "xpack.logsExplorer.flyoutDetail.label.cloudInstanceId": "云实例 ID", "xpack.logsExplorer.flyoutDetail.label.cloudProjectId": "云项目 ID", From 55f2714ab0e642e943afd8d8734f443a3b4f1686 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Tue, 9 Apr 2024 09:24:22 +0200 Subject: [PATCH 15/28] refactor(logs-explorer): remove moced code --- .../components/virtual_columns/content.tsx | 16 +-- .../public/hooks/use_discover_action.ts | 17 --- .../public/hooks/use_doc_detail.ts | 105 ------------------ .../public/hooks/use_flyouot_column_width.tsx | 26 ----- .../public/hooks/use_hover_actions.tsx | 87 --------------- 5 files changed, 9 insertions(+), 242 deletions(-) delete mode 100644 x-pack/plugins/observability_solution/logs_explorer/public/hooks/use_discover_action.ts delete mode 100644 x-pack/plugins/observability_solution/logs_explorer/public/hooks/use_doc_detail.ts delete mode 100644 x-pack/plugins/observability_solution/logs_explorer/public/hooks/use_flyouot_column_width.tsx delete mode 100644 x-pack/plugins/observability_solution/logs_explorer/public/hooks/use_hover_actions.tsx diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns/content.tsx b/x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns/content.tsx index 073d18068d650c..a9783e1fcec639 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns/content.tsx +++ b/x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns/content.tsx @@ -10,12 +10,14 @@ import { css } from '@emotion/css'; import { EuiButtonIcon, EuiText } from '@elastic/eui'; import { euiThemeVars } from '@kbn/ui-theme'; import type { DataGridCellValueElementProps } from '@kbn/unified-data-table'; -import { getShouldShowFieldHandler } from '@kbn/discover-utils'; +import { + getDocumentOverview, + getMessageFieldWithFallbacks, + getShouldShowFieldHandler, +} from '@kbn/discover-utils'; import { i18n } from '@kbn/i18n'; import type { DataTableRecord } from '@kbn/discover-utils/src/types'; import { dynamic } from '@kbn/shared-ux-utility'; -import { useDocDetail, getMessageWithFallbacks } from '../../hooks/use_doc_detail'; -import { LogDocument } from '../../../common/document'; import { LogLevel } from '../common/log_level'; import * as constants from '../../../common/constants'; @@ -85,8 +87,8 @@ export const Content = ({ columnId, closePopover, }: DataGridCellValueElementProps) => { - const parsedDoc = useDocDetail(row as LogDocument, { dataView }); - const { field, value } = getMessageWithFallbacks(parsedDoc); + const documentOverview = getDocumentOverview(row, { dataView, fieldFormats }); + const { field, value } = getMessageFieldWithFallbacks(documentOverview); const renderLogMessage = field && value; const shouldShowFieldHandler = useMemo(() => { @@ -106,8 +108,8 @@ export const Content = ({ return ( - {parsedDoc[constants.LOG_LEVEL_FIELD] && ( - + {documentOverview[constants.LOG_LEVEL_FIELD] && ( + )} {renderLogMessage ? ( diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/hooks/use_discover_action.ts b/x-pack/plugins/observability_solution/logs_explorer/public/hooks/use_discover_action.ts deleted file mode 100644 index 68db3d6b932d63..00000000000000 --- a/x-pack/plugins/observability_solution/logs_explorer/public/hooks/use_discover_action.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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import createContainer from 'constate'; -import type { LogsExplorerFlyoutContentProps } from '../customizations/types'; - -interface UseFlyoutActionsDeps { - value: LogsExplorerFlyoutContentProps['actions']; -} - -const useDiscoverActions = ({ value }: UseFlyoutActionsDeps) => value; - -export const [DiscoverActionsProvider, useDiscoverActionsContext] = - createContainer(useDiscoverActions); diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/hooks/use_doc_detail.ts b/x-pack/plugins/observability_solution/logs_explorer/public/hooks/use_doc_detail.ts deleted file mode 100644 index 6b59b0ac5e4f5b..00000000000000 --- a/x-pack/plugins/observability_solution/logs_explorer/public/hooks/use_doc_detail.ts +++ /dev/null @@ -1,105 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { formatFieldValue } from '@kbn/discover-utils'; -import * as constants from '../../common/constants'; -import { useKibanaContextForPlugin } from '../utils/use_kibana'; -import { LogsExplorerFlyoutContentProps } from '../customizations/types'; -import { FlyoutDoc, LogDocument } from '../../common/document'; - -export function useDocDetail( - doc: LogDocument, - { dataView }: Pick -): FlyoutDoc { - const { services } = useKibanaContextForPlugin(); - - const formatField = (field: F) => { - return ( - doc.flattened[field] && - formatFieldValue( - doc.flattened[field], - doc.raw, - services.fieldFormats, - dataView, - dataView.fields.getByName(field) - ) - ); - }; - - // Flyout Headers - const levelArray = doc.flattened[constants.LOG_LEVEL_FIELD]; - const level = levelArray && levelArray.length ? levelArray[0]?.toLowerCase() : undefined; - const messageArray = doc.flattened[constants.MESSAGE_FIELD]; - const message = messageArray && messageArray.length ? messageArray[0] : undefined; - const errorMessageArray = doc.flattened[constants.ERROR_MESSAGE_FIELD]; - const errorMessage = - errorMessageArray && errorMessageArray.length ? errorMessageArray[0] : undefined; - const eventOriginalArray = doc.flattened[constants.EVENT_ORIGINAL_FIELD]; - const eventOriginal = - eventOriginalArray && eventOriginalArray.length ? eventOriginalArray[0] : undefined; - const timestamp = formatField(constants.TIMESTAMP_FIELD); - - // Service Highlights - const serviceName = formatField(constants.SERVICE_NAME_FIELD); - const traceId = formatField(constants.TRACE_ID_FIELD); - - // Infrastructure Highlights - const hostname = formatField(constants.HOST_NAME_FIELD); - const orchestratorClusterName = formatField(constants.ORCHESTRATOR_CLUSTER_NAME_FIELD); - const orchestratorResourceId = formatField(constants.ORCHESTRATOR_RESOURCE_ID_FIELD); - - // Cloud Highlights - const cloudProvider = formatField(constants.CLOUD_PROVIDER_FIELD); - const cloudRegion = formatField(constants.CLOUD_REGION_FIELD); - const cloudAz = formatField(constants.CLOUD_AVAILABILITY_ZONE_FIELD); - const cloudProjectId = formatField(constants.CLOUD_PROJECT_ID_FIELD); - const cloudInstanceId = formatField(constants.CLOUD_INSTANCE_ID_FIELD); - - // Other Highlights - const logFilePath = formatField(constants.LOG_FILE_PATH_FIELD); - const namespace = formatField(constants.DATASTREAM_NAMESPACE_FIELD); - const dataset = formatField(constants.DATASTREAM_DATASET_FIELD); - const agentName = formatField(constants.AGENT_NAME_FIELD); - - return { - [constants.LOG_LEVEL_FIELD]: level, - [constants.TIMESTAMP_FIELD]: timestamp, - [constants.MESSAGE_FIELD]: message, - [constants.ERROR_MESSAGE_FIELD]: errorMessage, - [constants.EVENT_ORIGINAL_FIELD]: eventOriginal, - [constants.SERVICE_NAME_FIELD]: serviceName, - [constants.TRACE_ID_FIELD]: traceId, - [constants.HOST_NAME_FIELD]: hostname, - [constants.ORCHESTRATOR_CLUSTER_NAME_FIELD]: orchestratorClusterName, - [constants.ORCHESTRATOR_RESOURCE_ID_FIELD]: orchestratorResourceId, - [constants.CLOUD_PROVIDER_FIELD]: cloudProvider, - [constants.CLOUD_REGION_FIELD]: cloudRegion, - [constants.CLOUD_AVAILABILITY_ZONE_FIELD]: cloudAz, - [constants.CLOUD_PROJECT_ID_FIELD]: cloudProjectId, - [constants.CLOUD_INSTANCE_ID_FIELD]: cloudInstanceId, - [constants.LOG_FILE_PATH_FIELD]: logFilePath, - [constants.DATASTREAM_NAMESPACE_FIELD]: namespace, - [constants.DATASTREAM_DATASET_FIELD]: dataset, - [constants.AGENT_NAME_FIELD]: agentName, - }; -} - -export const getMessageWithFallbacks = (doc: FlyoutDoc) => { - const rankingOrder = [ - constants.MESSAGE_FIELD, - constants.ERROR_MESSAGE_FIELD, - constants.EVENT_ORIGINAL_FIELD, - ] as const; - - for (const rank of rankingOrder) { - if (doc[rank] !== undefined && doc[rank] !== null) { - return { field: rank, value: doc[rank] }; - } - } - - // If none of the ranks (fallbacks) are present - return { field: undefined }; -}; diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/hooks/use_flyouot_column_width.tsx b/x-pack/plugins/observability_solution/logs_explorer/public/hooks/use_flyouot_column_width.tsx deleted file mode 100644 index 1ea4250de49bfd..00000000000000 --- a/x-pack/plugins/observability_solution/logs_explorer/public/hooks/use_flyouot_column_width.tsx +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { useEuiTheme } from '@elastic/eui'; - -interface FlyoutColumnWidth { - columns: 1 | 2 | 3; - fieldWidth: number; -} - -export const useFlyoutColumnWidth = (width: number): FlyoutColumnWidth => { - const { euiTheme } = useEuiTheme(); - - const numberOfColumns = width > euiTheme.breakpoint.m ? 3 : width > euiTheme.breakpoint.s ? 2 : 1; - const WIDTH_FACTOR = 1.25; - const fieldWidth = width / (numberOfColumns * WIDTH_FACTOR); - - return { - columns: numberOfColumns, - fieldWidth, - }; -}; diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/hooks/use_hover_actions.tsx b/x-pack/plugins/observability_solution/logs_explorer/public/hooks/use_hover_actions.tsx deleted file mode 100644 index d8459215dc3669..00000000000000 --- a/x-pack/plugins/observability_solution/logs_explorer/public/hooks/use_hover_actions.tsx +++ /dev/null @@ -1,87 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { useMemo, useState } from 'react'; -import { copyToClipboard, IconType } from '@elastic/eui'; -import { - flyoutHoverActionCopyToClipboardText, - flyoutHoverActionFilterForFieldPresentText, - actionFilterForText, - actionFilterOutText, - flyoutHoverActionToggleColumnText, -} from '../components/common/translations'; -import { useDiscoverActionsContext } from './use_discover_action'; - -interface HoverActionProps { - field: string; - value: string; -} - -export interface HoverActionType { - id: string; - tooltipContent: string; - iconType: IconType; - onClick: () => void; - display: boolean; -} - -export const useHoverActions = ({ field, value }: HoverActionProps): HoverActionType[] => { - const filterForText = actionFilterForText(value); - const filterOutText = actionFilterOutText(value); - const actions = useDiscoverActionsContext(); - const [columnAdded, setColumnAdded] = useState(false); - - return useMemo( - () => [ - { - id: 'addToFilterAction', - tooltipContent: filterForText, - iconType: 'plusInCircle', - onClick: () => actions?.addFilter && actions.addFilter(field, value, '+'), - display: true, - }, - { - id: 'removeFromFilterAction', - tooltipContent: filterOutText, - iconType: 'minusInCircle', - onClick: () => actions?.addFilter && actions.addFilter(field, value, '-'), - display: true, - }, - { - id: 'filterForFieldPresentAction', - tooltipContent: flyoutHoverActionFilterForFieldPresentText, - iconType: 'filter', - onClick: () => actions?.addFilter && actions.addFilter('_exists_', field, '+'), - display: true, - }, - { - id: 'toggleColumnAction', - tooltipContent: flyoutHoverActionToggleColumnText, - iconType: 'listAdd', - onClick: () => { - if (actions) { - if (columnAdded) { - actions?.removeColumn?.(field); - } else { - actions?.addColumn?.(field); - } - setColumnAdded(!columnAdded); - } - }, - display: true, - }, - { - id: 'copyToClipboardAction', - tooltipContent: flyoutHoverActionCopyToClipboardText, - iconType: 'copyClipboard', - onClick: () => copyToClipboard(value as string), - display: true, - }, - ], - [filterForText, filterOutText, actions, field, value, columnAdded] - ); -}; From 54367cd8b383a1ea01d7514e78cdaf82328b8845 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Tue, 9 Apr 2024 09:37:29 +0200 Subject: [PATCH 16/28] refactor(unified-doc-views): update data test subj --- .../doc_viewer_logs_overview/logs_overview_header.tsx | 2 +- .../sub_components/highlight_section.tsx | 2 +- .../doc_viewer_logs_overview/sub_components/timestamp.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx index 5956b346096ffc..3cc6bd3201c735 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx @@ -113,7 +113,7 @@ export function LogsOverviewHeader({ doc }: { doc: DocumentOverview }) { buttonContent={accordionTitle} paddingSize="m" initialIsOpen={true} - data-test-subj={`logsExplorerFlyoutHeaderSection${contentLabel}`} + data-test-subj="unifiedDocViewLogsOverviewHeader" > {hasMessageField ? contentField : logLevelAndTimestamp} diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_section.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_section.tsx index 193f9e6ec64e9c..fe60e46344e5cb 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_section.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_section.tsx @@ -47,7 +47,7 @@ export function HighlightSection({ title, children, columns, ...props }: Highlig const showMoreButton = ( + {timestamp} ); From eae46a9cb9bba60c686ea5f4728c3e7916ec68b4 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Tue, 9 Apr 2024 09:45:01 +0200 Subject: [PATCH 17/28] refactor(logs-explorer): add doc comment --- .../public/customizations/logs_explorer_profile.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/customizations/logs_explorer_profile.tsx b/x-pack/plugins/observability_solution/logs_explorer/public/customizations/logs_explorer_profile.tsx index df0d51db88bd6f..4b49e3f579f153 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/public/customizations/logs_explorer_profile.tsx +++ b/x-pack/plugins/observability_solution/logs_explorer/public/customizations/logs_explorer_profile.tsx @@ -116,7 +116,12 @@ export const createLogsExplorerProfileCustomizations = }); /** - * Hide flyout actions to prevent rendering hard-coded actions. + * Flyout customization. + * The latest changes moved the implementation of the flyout overview tab into the unified_doc_viewer presets. + * To keep control over the overview tab and enable it only on the Logs Explorer, + * the docViewsRegistry is updated to allow enable/disable of any doc view. + * In a close future, when the contextual awareness for Discover will be in place, + * this configuration will be moved into a flavored logs experience directly defined in Discover. */ customizations.set({ id: 'flyout', From 18a59f2f2002a74f83abeece0afd416ed685f85c Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Fri, 12 Apr 2024 12:11:21 +0200 Subject: [PATCH 18/28] refactor(unified-doc-viewer): assign logs overview doc view ownership --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 70640a4502c6fa..97283e12123f7f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1120,6 +1120,7 @@ x-pack/plugins/observability_solution/infra/server/lib/alerting @elastic/obs-ux- /x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer @elastic/obs-ux-logs-team /x-pack/test/functional/apps/dataset_quality @elastic/obs-ux-logs-team /x-pack/test_serverless/functional/test_suites/observability/dataset_quality @elastic/obs-ux-logs-team +/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview @elastic/obs-ux-logs-team # Observability onboarding tour /x-pack/plugins/observability_solution/observability_shared/public/components/tour @elastic/platform-onboarding From 37e5542c753f38482743116b9b4412707f6f0e5b Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Fri, 12 Apr 2024 16:27:04 +0200 Subject: [PATCH 19/28] refactor(unified-doc-viewer): port tests from e2e --- .../src/utils/get_document_overview.ts | 11 +- .../logs_overview.test.tsx | 162 ++++++++++++++++++ .../logs_overview_header.tsx | 12 +- .../logs_overview_highlights.tsx | 56 +++--- .../sub_components/hover_popover_action.tsx | 5 +- 5 files changed, 211 insertions(+), 35 deletions(-) create mode 100644 src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.test.tsx diff --git a/packages/kbn-discover-utils/src/utils/get_document_overview.ts b/packages/kbn-discover-utils/src/utils/get_document_overview.ts index a894f58e222583..d7530dac231507 100644 --- a/packages/kbn-discover-utils/src/utils/get_document_overview.ts +++ b/packages/kbn-discover-utils/src/utils/get_document_overview.ts @@ -30,17 +30,20 @@ export function getDocumentOverview( const levelArray = doc.flattened[fieldConstants.LOG_LEVEL_FIELD]; const level = - Array.isArray(levelArray) && levelArray.length ? levelArray[0].toLowerCase() : undefined; + Array.isArray(levelArray) && levelArray.length ? levelArray[0].toLowerCase() : levelArray; const messageArray = doc.flattened[fieldConstants.MESSAGE_FIELD]; - const message = Array.isArray(messageArray) && messageArray.length ? messageArray[0] : undefined; + const message = + Array.isArray(messageArray) && messageArray.length ? messageArray[0] : messageArray; const errorMessageArray = doc.flattened[fieldConstants.ERROR_MESSAGE_FIELD]; const errorMessage = - Array.isArray(errorMessageArray) && errorMessageArray.length ? errorMessageArray[0] : undefined; + Array.isArray(errorMessageArray) && errorMessageArray.length + ? errorMessageArray[0] + : errorMessageArray; const eventOriginalArray = doc.flattened[fieldConstants.EVENT_ORIGINAL_FIELD]; const eventOriginal = Array.isArray(eventOriginalArray) && eventOriginalArray.length ? eventOriginalArray[0] - : undefined; + : eventOriginalArray; const timestamp = formatField(fieldConstants.TIMESTAMP_FIELD); // Service diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.test.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.test.tsx new file mode 100644 index 00000000000000..7b9379654cec12 --- /dev/null +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.test.tsx @@ -0,0 +1,162 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { LogsOverview } from './logs_overview'; +import { DataView } from '@kbn/data-views-plugin/common'; +import { DocViewRenderProps } from '@kbn/unified-doc-viewer/types'; +import { buildDataTableRecord } from '@kbn/discover-utils'; +import { setUnifiedDocViewerServices } from '../../plugin'; +import { mockUnifiedDocViewerServices } from '../../__mocks__'; + +const DATASET_NAME = 'logs.overview'; +const NAMESPACE = 'default'; +const DATA_STREAM_NAME = `logs-${DATASET_NAME}-${NAMESPACE}`; +const NOW = Date.now(); + +const dataView = { + fields: { + getAll: () => + [ + '_index', + 'message', + 'log.level', + 'service.name', + 'host.name', + 'trace.id', + 'orchestrator.cluster.id', + 'orchestrator.cluster.name', + 'orchestrator.resource.id', + 'cloud.provider', + 'cloud.region', + 'cloud.availability_zone', + 'cloud.project.id', + 'cloud.instance.id', + 'agent.name', + ].map((name) => ({ + name, + type: 'string', + scripted: false, + filterable: true, + })), + }, + metaFields: ['_index', '_score'], + getFormatterForField: jest.fn(() => ({ convert: (value: unknown) => value })), +} as unknown as DataView; + +dataView.fields.getByName = (name: string) => { + return dataView.fields.getAll().find((field) => field.name === name); +}; + +const fullHit = buildDataTableRecord( + { + _index: DATA_STREAM_NAME, + _id: DATA_STREAM_NAME, + _score: 1, + _source: { + '@timestamp': NOW + 1000, + message: 'full document', + log: { level: 'info', file: { path: '/logs.overview.log' } }, + data_stream: { + type: 'logs', + dataset: DATASET_NAME, + namespace: NAMESPACE, + }, + 'service.name': DATASET_NAME, + 'host.name': 'gke-edge-oblt-pool', + 'trace.id': 'abcdef', + orchestrator: { + cluster: { + id: 'my-cluster-id', + name: 'my-cluster-name', + }, + resource: { + id: 'orchestratorResourceId', + }, + }, + cloud: { + provider: ['gcp'], + region: 'us-central-1', + availability_zone: 'us-central-1a', + project: { + id: 'elastic-project', + }, + instance: { + id: 'BgfderflkjTheUiGuy', + }, + }, + 'agent.name': 'node', + }, + }, + dataView +); + +setUnifiedDocViewerServices(mockUnifiedDocViewerServices); + +const renderLogsOverview = (props: Partial = {}) => { + const { rerender: baseRerender, ...tools } = render( + + ); + + const rerender = (rerenderProps: Partial) => + baseRerender(); + + return { rerender, ...tools }; +}; + +describe('LogsOverview', () => { + beforeEach(() => renderLogsOverview()); + + describe('Header section', () => { + it('should display a timestamp badge', async () => { + expect(screen.queryByTestId('unifiedDocViewLogsOverviewTimestamp')).toBeInTheDocument(); + }); + + it('should display a log level badge when available', async () => { + expect(screen.queryByTestId('unifiedDocViewLogsOverviewLogLevel')).toBeInTheDocument(); + }); + + it('should display a message code block when available', async () => { + expect(screen.queryByTestId('unifiedDocViewLogsOverviewMessage')).toBeInTheDocument(); + }); + }); + + describe('Highlights section', () => { + it('should load the service container with all fields', async () => { + expect( + screen.queryByTestId('unifiedDocViewLogsOverviewHighlightSectionServiceInfra') + ).toBeInTheDocument(); + expect(screen.queryByTestId('unifiedDocViewLogsOverviewService')).toBeInTheDocument(); + expect(screen.queryByTestId('unifiedDocViewLogsOverviewTrace')).toBeInTheDocument(); + expect(screen.queryByTestId('unifiedDocViewLogsOverviewHostName')).toBeInTheDocument(); + expect(screen.queryByTestId('unifiedDocViewLogsOverviewClusterName')).toBeInTheDocument(); + expect(screen.queryByTestId('unifiedDocViewLogsOverviewResourceId')).toBeInTheDocument(); + }); + + it('should load the cloud container with all fields', async () => { + expect( + screen.queryByTestId('unifiedDocViewLogsOverviewHighlightSectionCloud') + ).toBeInTheDocument(); + expect(screen.queryByTestId('unifiedDocViewLogsOverviewCloudProvider')).toBeInTheDocument(); + expect(screen.queryByTestId('unifiedDocViewLogsOverviewCloudRegion')).toBeInTheDocument(); + expect(screen.queryByTestId('unifiedDocViewLogsOverviewCloudAz')).toBeInTheDocument(); + expect(screen.queryByTestId('unifiedDocViewLogsOverviewCloudProjectId')).toBeInTheDocument(); + expect(screen.queryByTestId('unifiedDocViewLogsOverviewCloudInstanceId')).toBeInTheDocument(); + }); + + it('should load the other container with all fields', async () => { + expect( + screen.queryByTestId('unifiedDocViewLogsOverviewHighlightSectionOther') + ).toBeInTheDocument(); + expect(screen.queryByTestId('unifiedDocViewLogsOverviewLogPathFile')).toBeInTheDocument(); + expect(screen.queryByTestId('unifiedDocViewLogsOverviewNamespace')).toBeInTheDocument(); + expect(screen.queryByTestId('unifiedDocViewLogsOverviewDataset')).toBeInTheDocument(); + expect(screen.queryByTestId('unifiedDocViewLogsOverviewLogShipper')).toBeInTheDocument(); + }); + }); +}); diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx index 3cc6bd3201c735..9793990bdfcb98 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx @@ -74,7 +74,11 @@ export function LogsOverviewHeader({ doc }: { doc: DocumentOverview }) { const contentField = hasMessageField && ( - + @@ -115,7 +119,11 @@ export function LogsOverviewHeader({ doc }: { doc: DocumentOverview }) { initialIsOpen={true} data-test-subj="unifiedDocViewLogsOverviewHeader" > - + {hasMessageField ? contentField : logLevelAndTimestamp} diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_highlights.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_highlights.tsx index f3544c7824567d..9253beb0785e92 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_highlights.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_highlights.tsx @@ -57,7 +57,7 @@ export function LogsOverviewHighlights({ data-test-subj="unifiedDocViewLogsOverviewService" field={fieldConstants.SERVICE_NAME_FIELD} formattedValue={formattedDoc[fieldConstants.SERVICE_NAME_FIELD]} - label={ServiceLabel} + label={serviceLabel} value={flattenedDoc[fieldConstants.SERVICE_NAME_FIELD]} width={fieldWidth} /> @@ -67,7 +67,7 @@ export function LogsOverviewHighlights({ data-test-subj="unifiedDocViewLogsOverviewHostName" field={fieldConstants.HOST_NAME_FIELD} formattedValue={formattedDoc[fieldConstants.HOST_NAME_FIELD]} - label={HostNameLabel} + label={hostNameLabel} value={flattenedDoc[fieldConstants.HOST_NAME_FIELD]} width={fieldWidth} /> @@ -77,7 +77,7 @@ export function LogsOverviewHighlights({ data-test-subj="unifiedDocViewLogsOverviewTrace" field={fieldConstants.TRACE_ID_FIELD} formattedValue={formattedDoc[fieldConstants.TRACE_ID_FIELD]} - label={TraceLabel} + label={traceLabel} value={flattenedDoc[fieldConstants.TRACE_ID_FIELD]} width={fieldWidth} /> @@ -87,7 +87,7 @@ export function LogsOverviewHighlights({ data-test-subj="unifiedDocViewLogsOverviewClusterName" field={fieldConstants.ORCHESTRATOR_CLUSTER_NAME_FIELD} formattedValue={formattedDoc[fieldConstants.ORCHESTRATOR_CLUSTER_NAME_FIELD]} - label={OrchestratorClusterNameLabel} + label={orchestratorClusterNameLabel} value={flattenedDoc[fieldConstants.ORCHESTRATOR_CLUSTER_NAME_FIELD]} width={fieldWidth} /> @@ -97,7 +97,7 @@ export function LogsOverviewHighlights({ data-test-subj="unifiedDocViewLogsOverviewResourceId" field={fieldConstants.ORCHESTRATOR_RESOURCE_ID_FIELD} formattedValue={formattedDoc[fieldConstants.ORCHESTRATOR_RESOURCE_ID_FIELD]} - label={OrchestratorResourceIdLabel} + label={orchestratorResourceIdLabel} value={flattenedDoc[fieldConstants.ORCHESTRATOR_RESOURCE_ID_FIELD]} width={fieldWidth} /> @@ -121,7 +121,7 @@ export function LogsOverviewHighlights({ )} /> } - label={CloudProviderLabel} + label={cloudProviderLabel} value={flattenedDoc[fieldConstants.CLOUD_PROVIDER_FIELD]} width={fieldWidth} /> @@ -131,7 +131,7 @@ export function LogsOverviewHighlights({ data-test-subj="unifiedDocViewLogsOverviewCloudRegion" field={fieldConstants.CLOUD_REGION_FIELD} formattedValue={formattedDoc[fieldConstants.CLOUD_REGION_FIELD]} - label={CloudRegionLabel} + label={cloudRegionLabel} value={flattenedDoc[fieldConstants.CLOUD_REGION_FIELD]} width={fieldWidth} /> @@ -141,7 +141,7 @@ export function LogsOverviewHighlights({ data-test-subj="unifiedDocViewLogsOverviewCloudAz" field={fieldConstants.CLOUD_AVAILABILITY_ZONE_FIELD} formattedValue={formattedDoc[fieldConstants.CLOUD_AVAILABILITY_ZONE_FIELD]} - label={CloudAvailabilityZoneLabel} + label={cloudAvailabilityZoneLabel} value={flattenedDoc[fieldConstants.CLOUD_AVAILABILITY_ZONE_FIELD]} width={fieldWidth} /> @@ -151,7 +151,7 @@ export function LogsOverviewHighlights({ data-test-subj="unifiedDocViewLogsOverviewCloudProjectId" field={fieldConstants.CLOUD_PROJECT_ID_FIELD} formattedValue={formattedDoc[fieldConstants.CLOUD_PROJECT_ID_FIELD]} - label={CloudProjectIdLabel} + label={cloudProjectIdLabel} value={flattenedDoc[fieldConstants.CLOUD_PROJECT_ID_FIELD]} width={fieldWidth} /> @@ -161,7 +161,7 @@ export function LogsOverviewHighlights({ data-test-subj="unifiedDocViewLogsOverviewCloudInstanceId" field={fieldConstants.CLOUD_INSTANCE_ID_FIELD} formattedValue={formattedDoc[fieldConstants.CLOUD_INSTANCE_ID_FIELD]} - label={CloudInstanceIdLabel} + label={cloudInstanceIdLabel} value={flattenedDoc[fieldConstants.CLOUD_INSTANCE_ID_FIELD]} width={fieldWidth} /> @@ -178,7 +178,7 @@ export function LogsOverviewHighlights({ data-test-subj="unifiedDocViewLogsOverviewLogPathFile" field={fieldConstants.LOG_FILE_PATH_FIELD} formattedValue={formattedDoc[fieldConstants.LOG_FILE_PATH_FIELD]} - label={LogPathFileLabel} + label={logPathFileLabel} value={flattenedDoc[fieldConstants.LOG_FILE_PATH_FIELD]} width={fieldWidth} /> @@ -188,7 +188,7 @@ export function LogsOverviewHighlights({ data-test-subj="unifiedDocViewLogsOverviewDataset" field={fieldConstants.DATASTREAM_DATASET_FIELD} formattedValue={formattedDoc[fieldConstants.DATASTREAM_DATASET_FIELD]} - label={DatasetLabel} + label={datasetLabel} value={flattenedDoc[fieldConstants.DATASTREAM_DATASET_FIELD]} width={fieldWidth} /> @@ -198,7 +198,7 @@ export function LogsOverviewHighlights({ data-test-subj="unifiedDocViewLogsOverviewNamespace" field={fieldConstants.DATASTREAM_NAMESPACE_FIELD} formattedValue={formattedDoc[fieldConstants.DATASTREAM_NAMESPACE_FIELD]} - label={NamespaceLabel} + label={namespaceLabel} value={flattenedDoc[fieldConstants.DATASTREAM_NAMESPACE_FIELD]} width={fieldWidth} useBadge @@ -209,7 +209,7 @@ export function LogsOverviewHighlights({ data-test-subj="unifiedDocViewLogsOverviewLogShipper" field={fieldConstants.AGENT_NAME_FIELD} formattedValue={formattedDoc[fieldConstants.AGENT_NAME_FIELD]} - label={ShipperLabel} + label={shipperLabel} value={flattenedDoc[fieldConstants.AGENT_NAME_FIELD]} width={fieldWidth} /> @@ -219,15 +219,15 @@ export function LogsOverviewHighlights({ ); } -const ServiceLabel = i18n.translate('unifiedDocViewer.docView.logsOverview.label.service', { +const serviceLabel = i18n.translate('unifiedDocViewer.docView.logsOverview.label.service', { defaultMessage: 'Service', }); -const TraceLabel = i18n.translate('unifiedDocViewer.docView.logsOverview.label.trace', { +const traceLabel = i18n.translate('unifiedDocViewer.docView.logsOverview.label.trace', { defaultMessage: 'Trace', }); -const HostNameLabel = i18n.translate('unifiedDocViewer.docView.logsOverview.label.hostName', { +const hostNameLabel = i18n.translate('unifiedDocViewer.docView.logsOverview.label.hostName', { defaultMessage: 'Host name', }); @@ -252,64 +252,64 @@ const otherAccordionTitle = i18n.translate( } ); -const OrchestratorClusterNameLabel = i18n.translate( +const orchestratorClusterNameLabel = i18n.translate( 'unifiedDocViewer.docView.logsOverview.label.orchestratorClusterName', { defaultMessage: 'Orchestrator cluster Name', } ); -const OrchestratorResourceIdLabel = i18n.translate( +const orchestratorResourceIdLabel = i18n.translate( 'unifiedDocViewer.docView.logsOverview.label.orchestratorResourceId', { defaultMessage: 'Orchestrator resource ID', } ); -const CloudProviderLabel = i18n.translate( +const cloudProviderLabel = i18n.translate( 'unifiedDocViewer.docView.logsOverview.label.cloudProvider', { defaultMessage: 'Cloud provider', } ); -const CloudRegionLabel = i18n.translate('unifiedDocViewer.docView.logsOverview.label.cloudRegion', { +const cloudRegionLabel = i18n.translate('unifiedDocViewer.docView.logsOverview.label.cloudRegion', { defaultMessage: 'Cloud region', }); -const CloudAvailabilityZoneLabel = i18n.translate( +const cloudAvailabilityZoneLabel = i18n.translate( 'unifiedDocViewer.docView.logsOverview.label.cloudAvailabilityZone', { defaultMessage: 'Cloud availability zone', } ); -const CloudProjectIdLabel = i18n.translate( +const cloudProjectIdLabel = i18n.translate( 'unifiedDocViewer.docView.logsOverview.label.cloudProjectId', { defaultMessage: 'Cloud project ID', } ); -const CloudInstanceIdLabel = i18n.translate( +const cloudInstanceIdLabel = i18n.translate( 'unifiedDocViewer.docView.logsOverview.label.cloudInstanceId', { defaultMessage: 'Cloud instance ID', } ); -const LogPathFileLabel = i18n.translate('unifiedDocViewer.docView.logsOverview.label.logPathFile', { +const logPathFileLabel = i18n.translate('unifiedDocViewer.docView.logsOverview.label.logPathFile', { defaultMessage: 'Log path file', }); -const NamespaceLabel = i18n.translate('unifiedDocViewer.docView.logsOverview.label.namespace', { +const namespaceLabel = i18n.translate('unifiedDocViewer.docView.logsOverview.label.namespace', { defaultMessage: 'Namespace', }); -const DatasetLabel = i18n.translate('unifiedDocViewer.docView.logsOverview.label.dataset', { +const datasetLabel = i18n.translate('unifiedDocViewer.docView.logsOverview.label.dataset', { defaultMessage: 'Dataset', }); -const ShipperLabel = i18n.translate('unifiedDocViewer.docView.logsOverview.label.shipper', { +const shipperLabel = i18n.translate('unifiedDocViewer.docView.logsOverview.label.shipper', { defaultMessage: 'Shipper', }); diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/hover_popover_action.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/hover_popover_action.tsx index a726a37149754f..bdf1c45278e5b6 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/hover_popover_action.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/hover_popover_action.tsx @@ -60,6 +60,7 @@ export const HoverActionPopover = ({ button={children} isOpen={isPopoverOpen} anchorPosition={anchorPosition} + closePopover={closePopoverPlaceholder} panelPaddingSize="s" panelStyle={{ minWidth: '24px' }} display={display} @@ -73,7 +74,7 @@ export const HoverActionPopover = ({ {uiFieldActions.map((action) => ( ); }; + +const closePopoverPlaceholder = () => {}; From 540766e63a0e07ce12d4b5c6f162e479122b28f2 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Fri, 12 Apr 2024 16:28:51 +0200 Subject: [PATCH 20/28] refactor(unified-doc-viewer): remove legacy e2e flyout tests --- .../observability_logs_explorer/flyout.ts | 96 ------- .../flyout_highlights.ts | 250 ----------------- .../apps/observability_logs_explorer/index.ts | 2 - .../observability_logs_explorer.ts | 5 - .../observability_logs_explorer/flyout.ts | 98 ------- .../flyout_highlights.ts | 256 ------------------ .../observability_logs_explorer/index.ts | 2 - 7 files changed, 709 deletions(-) delete mode 100644 x-pack/test/functional/apps/observability_logs_explorer/flyout.ts delete mode 100644 x-pack/test/functional/apps/observability_logs_explorer/flyout_highlights.ts delete mode 100644 x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/flyout.ts delete mode 100644 x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/flyout_highlights.ts diff --git a/x-pack/test/functional/apps/observability_logs_explorer/flyout.ts b/x-pack/test/functional/apps/observability_logs_explorer/flyout.ts deleted file mode 100644 index c3ef409546b318..00000000000000 --- a/x-pack/test/functional/apps/observability_logs_explorer/flyout.ts +++ /dev/null @@ -1,96 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { FtrProviderContext } from '../../ftr_provider_context'; - -const DATASET_NAME = 'flyout'; -const NAMESPACE = 'default'; -const DATA_STREAM_NAME = `logs-${DATASET_NAME}-${NAMESPACE}`; -const NOW = Date.now(); - -const sharedDoc = { - logFilepath: '/flyout.log', - serviceName: DATASET_NAME, - datasetName: DATASET_NAME, - namespace: NAMESPACE, -}; - -const docs = [ - { - ...sharedDoc, - time: NOW + 1000, - message: 'full document', - logLevel: 'info', - }, - { - ...sharedDoc, - time: NOW, - }, -]; - -export default function ({ getService, getPageObjects }: FtrProviderContext) { - const dataGrid = getService('dataGrid'); - const testSubjects = getService('testSubjects'); - const PageObjects = getPageObjects(['observabilityLogsExplorer']); - - describe('Flyout content customization', () => { - let cleanupDataStreamSetup: () => Promise; - - before('initialize tests', async () => { - cleanupDataStreamSetup = await PageObjects.observabilityLogsExplorer.setupDataStream( - DATASET_NAME, - NAMESPACE - ); - await PageObjects.observabilityLogsExplorer.ingestLogEntries(DATA_STREAM_NAME, docs); - }); - - beforeEach(async () => { - await PageObjects.observabilityLogsExplorer.navigateTo({ - pageState: { - time: { - from: new Date(NOW - 60_000).toISOString(), - to: new Date(NOW + 60_000).toISOString(), - mode: 'absolute', - }, - }, - }); - }); - - after('clean up DataStream', async () => { - if (cleanupDataStreamSetup) { - await cleanupDataStreamSetup(); - } - }); - - it('should mount the flyout customization content', async () => { - await dataGrid.clickRowToggle({ columnIndex: 4 }); - await testSubjects.existOrFail('logsExplorerFlyoutDetail'); - }); - - it('should display a timestamp badge', async () => { - await dataGrid.clickRowToggle({ columnIndex: 4 }); - await testSubjects.existOrFail('logsExplorerFlyoutLogTimestamp'); - }); - - it('should display a log level badge when available', async () => { - await dataGrid.clickRowToggle({ columnIndex: 4 }); - await testSubjects.existOrFail('logsExplorerFlyoutLogLevel'); - await dataGrid.closeFlyout(); - - await dataGrid.clickRowToggle({ rowIndex: 1, columnIndex: 4 }); - await testSubjects.missingOrFail('logsExplorerFlyoutLogLevel'); - }); - - it('should display a message code block when available', async () => { - await dataGrid.clickRowToggle({ columnIndex: 4 }); - await testSubjects.existOrFail('logsExplorerFlyoutLogMessage'); - await dataGrid.closeFlyout(); - - await dataGrid.clickRowToggle({ rowIndex: 1, columnIndex: 4 }); - await testSubjects.missingOrFail('logsExplorerFlyoutLogMessage'); - }); - }); -} diff --git a/x-pack/test/functional/apps/observability_logs_explorer/flyout_highlights.ts b/x-pack/test/functional/apps/observability_logs_explorer/flyout_highlights.ts deleted file mode 100644 index 238d456b5ec546..00000000000000 --- a/x-pack/test/functional/apps/observability_logs_explorer/flyout_highlights.ts +++ /dev/null @@ -1,250 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { FtrProviderContext } from '../../ftr_provider_context'; - -const DATASET_NAME = 'flyout'; -const NAMESPACE = 'default'; -const DATA_STREAM_NAME = `logs-${DATASET_NAME}-${NAMESPACE}`; -const NOW = Date.now(); - -const sharedDoc = { - time: NOW + 1000, - logFilepath: '/flyout.log', - serviceName: 'frontend-node', - datasetName: DATASET_NAME, - namespace: NAMESPACE, - message: 'full document', - logLevel: 'info', - traceId: 'abcdef', - hostName: 'gke-edge-oblt-pool', - orchestratorClusterId: 'my-cluster-id', - orchestratorClusterName: 'my-cluster-id', - orchestratorResourceId: 'orchestratorResourceId', - cloudProvider: 'gcp', - cloudRegion: 'us-central-1', - cloudAz: 'us-central-1a', - cloudProjectId: 'elastic-project', - cloudInstanceId: 'BgfderflkjTheUiGuy', - agentName: 'node', -}; - -export default function ({ getService, getPageObjects }: FtrProviderContext) { - const dataGrid = getService('dataGrid'); - const testSubjects = getService('testSubjects'); - const PageObjects = getPageObjects(['observabilityLogsExplorer']); - - describe('Flyout highlight customization', () => { - let cleanupDataStreamSetup: () => Promise; - - describe('Service & Infrastructure container', () => { - const { - serviceName, - traceId, - hostName, - orchestratorClusterName, - orchestratorResourceId, - ...rest - } = sharedDoc; - const docWithoutServiceName = { - ...rest, - traceId, - hostName, - orchestratorClusterName, - orchestratorResourceId, - time: NOW - 1000, - }; - const docWithoutServiceInfraContainer = { ...rest, time: NOW - 4000 }; - - const docs = [sharedDoc, docWithoutServiceName, docWithoutServiceInfraContainer]; - before('setup DataStream', async () => { - cleanupDataStreamSetup = await PageObjects.observabilityLogsExplorer.setupDataStream( - DATASET_NAME, - NAMESPACE - ); - await PageObjects.observabilityLogsExplorer.ingestLogEntries(DATA_STREAM_NAME, docs); - }); - - after('clean up DataStream', async () => { - if (cleanupDataStreamSetup) { - await cleanupDataStreamSetup(); - } - }); - - beforeEach(async () => { - await PageObjects.observabilityLogsExplorer.navigateTo({ - pageState: { - time: { - from: new Date(NOW - 60_000).toISOString(), - to: new Date(NOW + 60_000).toISOString(), - mode: 'absolute', - }, - }, - }); - }); - - it('should load the service container with all fields', async () => { - await dataGrid.clickRowToggle({ columnIndex: 4 }); - await testSubjects.existOrFail('logsExplorerFlyoutHighlightSectionServiceInfra'); - await testSubjects.existOrFail('logsExplorerFlyoutService'); - await testSubjects.existOrFail('logsExplorerFlyoutTrace'); - await testSubjects.existOrFail('logsExplorerFlyoutHostName'); - await testSubjects.existOrFail('logsExplorerFlyoutClusterName'); - await testSubjects.existOrFail('logsExplorerFlyoutResourceId'); - await dataGrid.closeFlyout(); - }); - - it('should load the service container even when 1 field is missing', async () => { - await dataGrid.clickRowToggle({ rowIndex: 1, columnIndex: 4 }); - await testSubjects.existOrFail('logsExplorerFlyoutHighlightSectionServiceInfra'); - await testSubjects.missingOrFail('logsExplorerFlyoutService'); - await testSubjects.existOrFail('logsExplorerFlyoutTrace'); - await testSubjects.existOrFail('logsExplorerFlyoutHostName'); - await testSubjects.existOrFail('logsExplorerFlyoutClusterName'); - await testSubjects.existOrFail('logsExplorerFlyoutResourceId'); - await dataGrid.closeFlyout(); - }); - - it('should not load the service container if all fields are missing', async () => { - await dataGrid.clickRowToggle({ rowIndex: 2, columnIndex: 4 }); - await testSubjects.missingOrFail('logsExplorerFlyoutHighlightSectionServiceInfra'); - await dataGrid.closeFlyout(); - }); - }); - - describe('Cloud container', () => { - const { cloudProvider, cloudInstanceId, cloudProjectId, cloudRegion, cloudAz, ...rest } = - sharedDoc; - const docWithoutCloudProviderAndInstanceId = { - ...rest, - cloudProjectId, - cloudRegion, - cloudAz, - time: NOW - 1000, - }; - const docWithoutCloudContainer = { ...rest, time: NOW - 2000 }; - - const docs = [sharedDoc, docWithoutCloudProviderAndInstanceId, docWithoutCloudContainer]; - before('setup DataStream', async () => { - cleanupDataStreamSetup = await PageObjects.observabilityLogsExplorer.setupDataStream( - DATASET_NAME, - NAMESPACE - ); - await PageObjects.observabilityLogsExplorer.ingestLogEntries(DATA_STREAM_NAME, docs); - }); - - after('clean up DataStream', async () => { - if (cleanupDataStreamSetup) { - await cleanupDataStreamSetup(); - } - }); - - beforeEach(async () => { - await PageObjects.observabilityLogsExplorer.navigateTo({ - pageState: { - time: { - from: new Date(NOW - 60_000).toISOString(), - to: new Date(NOW + 60_000).toISOString(), - mode: 'absolute', - }, - }, - }); - }); - - it('should load the cloud container with all fields', async () => { - await dataGrid.clickRowToggle({ columnIndex: 4 }); - await testSubjects.existOrFail('logsExplorerFlyoutHighlightSectionCloud'); - await testSubjects.existOrFail('logsExplorerFlyoutCloudProvider'); - await testSubjects.existOrFail('logsExplorerFlyoutCloudRegion'); - await testSubjects.existOrFail('logsExplorerFlyoutCloudAz'); - await testSubjects.existOrFail('logsExplorerFlyoutCloudProjectId'); - await testSubjects.existOrFail('logsExplorerFlyoutCloudInstanceId'); - await dataGrid.closeFlyout(); - }); - - it('should load the cloud container even when some fields are missing', async () => { - await dataGrid.clickRowToggle({ rowIndex: 1, columnIndex: 4 }); - await testSubjects.existOrFail('logsExplorerFlyoutHighlightSectionCloud'); - - await testSubjects.missingOrFail('logsExplorerFlyoutCloudProvider'); - await testSubjects.missingOrFail('logsExplorerFlyoutCloudInstanceId'); - - await testSubjects.existOrFail('logsExplorerFlyoutCloudRegion'); - await testSubjects.existOrFail('logsExplorerFlyoutCloudAz'); - await testSubjects.existOrFail('logsExplorerFlyoutCloudProjectId'); - await dataGrid.closeFlyout(); - }); - - it('should not load the cloud container if all fields are missing', async () => { - await dataGrid.clickRowToggle({ rowIndex: 2, columnIndex: 4 }); - await testSubjects.missingOrFail('logsExplorerFlyoutHighlightSectionCloud'); - await testSubjects.missingOrFail('logsExplorerFlyoutCloudProvider'); - await testSubjects.missingOrFail('logsExplorerFlyoutCloudRegion'); - await testSubjects.missingOrFail('logsExplorerFlyoutCloudAz'); - await testSubjects.missingOrFail('logsExplorerFlyoutCloudProjectId'); - await testSubjects.missingOrFail('logsExplorerFlyoutCloudInstanceId'); - await dataGrid.closeFlyout(); - }); - }); - - describe('Other container', () => { - const { logFilepath, agentName, ...rest } = sharedDoc; - const docWithoutLogPathAndAgentName = { - ...rest, - time: NOW - 1000, - }; - - const docs = [sharedDoc, docWithoutLogPathAndAgentName]; - before('setup DataStream', async () => { - cleanupDataStreamSetup = await PageObjects.observabilityLogsExplorer.setupDataStream( - DATASET_NAME, - NAMESPACE - ); - await PageObjects.observabilityLogsExplorer.ingestLogEntries(DATA_STREAM_NAME, docs); - }); - - after('clean up DataStream', async () => { - if (cleanupDataStreamSetup) { - await cleanupDataStreamSetup(); - } - }); - - beforeEach(async () => { - await PageObjects.observabilityLogsExplorer.navigateTo({ - pageState: { - time: { - from: new Date(NOW - 60_000).toISOString(), - to: new Date(NOW + 60_000).toISOString(), - mode: 'absolute', - }, - }, - }); - }); - - it('should load the other container with all fields', async () => { - await dataGrid.clickRowToggle({ columnIndex: 4 }); - await testSubjects.existOrFail('logsExplorerFlyoutHighlightSectionOther'); - await testSubjects.existOrFail('logsExplorerFlyoutLogPathFile'); - await testSubjects.existOrFail('logsExplorerFlyoutNamespace'); - await testSubjects.existOrFail('logsExplorerFlyoutDataset'); - await testSubjects.existOrFail('logsExplorerFlyoutLogShipper'); - await dataGrid.closeFlyout(); - }); - - it('should load the other container even when some fields are missing', async () => { - await dataGrid.clickRowToggle({ rowIndex: 1, columnIndex: 4 }); - await testSubjects.existOrFail('logsExplorerFlyoutHighlightSectionOther'); - - await testSubjects.missingOrFail('logsExplorerFlyoutLogPathFile'); - await testSubjects.missingOrFail('logsExplorerFlyoutLogShipper'); - - await testSubjects.existOrFail('logsExplorerFlyoutNamespace'); - await testSubjects.existOrFail('logsExplorerFlyoutDataset'); - await dataGrid.closeFlyout(); - }); - }); - }); -} diff --git a/x-pack/test/functional/apps/observability_logs_explorer/index.ts b/x-pack/test/functional/apps/observability_logs_explorer/index.ts index 29dfc72df0ea7e..5e29637d79a320 100644 --- a/x-pack/test/functional/apps/observability_logs_explorer/index.ts +++ b/x-pack/test/functional/apps/observability_logs_explorer/index.ts @@ -16,8 +16,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./data_source_selector')); loadTestFile(require.resolve('./field_list')); loadTestFile(require.resolve('./filter_controls')); - // loadTestFile(require.resolve('./flyout_highlights')); - // loadTestFile(require.resolve('./flyout')); loadTestFile(require.resolve('./header_menu')); }); } diff --git a/x-pack/test/functional/page_objects/observability_logs_explorer.ts b/x-pack/test/functional/page_objects/observability_logs_explorer.ts index d78f38735cd7a1..81217122a36a99 100644 --- a/x-pack/test/functional/page_objects/observability_logs_explorer.ts +++ b/x-pack/test/functional/page_objects/observability_logs_explorer.ts @@ -335,11 +335,6 @@ export function ObservabilityLogsExplorerPageObject({ return testSubjects.find('unmanagedDatasets'); }, - async getFlyoutDetail(rowIndex: number = 0) { - await dataGrid.clickRowToggle({ rowIndex }); - return testSubjects.find('logsExplorerFlyoutDetail'); - }, - async getIntegrations() { const menu = await this.getIntegrationsContextMenu(); diff --git a/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/flyout.ts b/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/flyout.ts deleted file mode 100644 index e952294c2cda12..00000000000000 --- a/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/flyout.ts +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { FtrProviderContext } from '../../../ftr_provider_context'; - -const DATASET_NAME = 'flyout'; -const NAMESPACE = 'default'; -const DATA_STREAM_NAME = `logs-${DATASET_NAME}-${NAMESPACE}`; -const NOW = Date.now(); - -const sharedDoc = { - logFilepath: '/flyout.log', - serviceName: DATASET_NAME, - datasetName: DATASET_NAME, - namespace: NAMESPACE, -}; - -const docs = [ - { - ...sharedDoc, - time: NOW + 1000, - message: 'full document', - logLevel: 'info', - }, - { - ...sharedDoc, - time: NOW, - }, -]; - -export default function ({ getService, getPageObjects }: FtrProviderContext) { - const dataGrid = getService('dataGrid'); - const testSubjects = getService('testSubjects'); - const PageObjects = getPageObjects(['observabilityLogsExplorer', 'svlCommonPage']); - - describe('Flyout content customization', () => { - let cleanupDataStreamSetup: () => Promise; - - before('initialize tests', async () => { - cleanupDataStreamSetup = await PageObjects.observabilityLogsExplorer.setupDataStream( - DATASET_NAME, - NAMESPACE - ); - await PageObjects.observabilityLogsExplorer.ingestLogEntries(DATA_STREAM_NAME, docs); - await PageObjects.svlCommonPage.login(); - }); - - beforeEach(async () => { - await PageObjects.observabilityLogsExplorer.navigateTo({ - pageState: { - time: { - from: new Date(NOW - 60_000).toISOString(), - to: new Date(NOW + 60_000).toISOString(), - mode: 'absolute', - }, - }, - }); - }); - - after('clean up archives', async () => { - await PageObjects.svlCommonPage.forceLogout(); - if (cleanupDataStreamSetup) { - cleanupDataStreamSetup(); - } - }); - - it('should mount the flyout customization content', async () => { - await dataGrid.clickRowToggle({ columnIndex: 4 }); - await testSubjects.existOrFail('logsExplorerFlyoutDetail'); - }); - - it('should display a timestamp badge', async () => { - await dataGrid.clickRowToggle({ columnIndex: 4 }); - await testSubjects.existOrFail('logsExplorerFlyoutLogTimestamp'); - }); - - it('should display a log level badge when available', async () => { - await dataGrid.clickRowToggle({ columnIndex: 4 }); - await testSubjects.existOrFail('logsExplorerFlyoutLogLevel'); - await dataGrid.closeFlyout(); - - await dataGrid.clickRowToggle({ rowIndex: 1, columnIndex: 4 }); - await testSubjects.missingOrFail('logsExplorerFlyoutLogLevel'); - }); - - it('should display a message code block when available', async () => { - await dataGrid.clickRowToggle({ columnIndex: 4 }); - await testSubjects.existOrFail('logsExplorerFlyoutLogMessage'); - await dataGrid.closeFlyout(); - - await dataGrid.clickRowToggle({ rowIndex: 1, columnIndex: 4 }); - await testSubjects.missingOrFail('logsExplorerFlyoutLogMessage'); - }); - }); -} diff --git a/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/flyout_highlights.ts b/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/flyout_highlights.ts deleted file mode 100644 index b143d59e96cadd..00000000000000 --- a/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/flyout_highlights.ts +++ /dev/null @@ -1,256 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { FtrProviderContext } from '../../../ftr_provider_context'; - -const DATASET_NAME = 'flyout'; -const NAMESPACE = 'default'; -const DATA_STREAM_NAME = `logs-${DATASET_NAME}-${NAMESPACE}`; -const NOW = Date.now(); - -const sharedDoc = { - time: NOW + 1000, - logFilepath: '/flyout.log', - serviceName: 'frontend-node', - datasetName: DATASET_NAME, - namespace: NAMESPACE, - message: 'full document', - logLevel: 'info', - traceId: 'abcdef', - hostName: 'gke-edge-oblt-pool', - orchestratorClusterId: 'my-cluster-id', - orchestratorClusterName: 'my-cluster-id', - orchestratorResourceId: 'orchestratorResourceId', - cloudProvider: 'gcp', - cloudRegion: 'us-central-1', - cloudAz: 'us-central-1a', - cloudProjectId: 'elastic-project', - cloudInstanceId: 'BgfderflkjTheUiGuy', - agentName: 'node', -}; - -export default function ({ getService, getPageObjects }: FtrProviderContext) { - const dataGrid = getService('dataGrid'); - const testSubjects = getService('testSubjects'); - const PageObjects = getPageObjects(['observabilityLogsExplorer', 'svlCommonPage']); - - describe('Flyout highlight customization', () => { - let cleanupDataStreamSetup: () => Promise; - - describe('Service & Infrastructure container', () => { - const { - serviceName, - traceId, - hostName, - orchestratorClusterName, - orchestratorResourceId, - ...rest - } = sharedDoc; - const docWithoutServiceName = { - ...rest, - traceId, - hostName, - orchestratorClusterName, - orchestratorResourceId, - time: NOW - 1000, - }; - const docWithoutServiceInfraContainer = { ...rest, time: NOW - 4000 }; - - const docs = [sharedDoc, docWithoutServiceName, docWithoutServiceInfraContainer]; - before('setup DataStream', async () => { - cleanupDataStreamSetup = await PageObjects.observabilityLogsExplorer.setupDataStream( - DATASET_NAME, - NAMESPACE - ); - await PageObjects.observabilityLogsExplorer.ingestLogEntries(DATA_STREAM_NAME, docs); - await PageObjects.svlCommonPage.login(); - }); - - after('clean up DataStream', async () => { - await PageObjects.svlCommonPage.forceLogout(); - if (cleanupDataStreamSetup) { - await cleanupDataStreamSetup(); - } - }); - - beforeEach(async () => { - await PageObjects.observabilityLogsExplorer.navigateTo({ - pageState: { - time: { - from: new Date(NOW - 60_000).toISOString(), - to: new Date(NOW + 60_000).toISOString(), - mode: 'absolute', - }, - }, - }); - }); - - it('should load the service container with all fields', async () => { - await dataGrid.clickRowToggle({ columnIndex: 4 }); - await testSubjects.existOrFail('logsExplorerFlyoutHighlightSectionServiceInfra'); - await testSubjects.existOrFail('logsExplorerFlyoutService'); - await testSubjects.existOrFail('logsExplorerFlyoutTrace'); - await testSubjects.existOrFail('logsExplorerFlyoutHostName'); - await testSubjects.existOrFail('logsExplorerFlyoutClusterName'); - await testSubjects.existOrFail('logsExplorerFlyoutResourceId'); - await dataGrid.closeFlyout(); - }); - - it('should load the service container even when 1 field is missing', async () => { - await dataGrid.clickRowToggle({ rowIndex: 1, columnIndex: 4 }); - await testSubjects.existOrFail('logsExplorerFlyoutHighlightSectionServiceInfra'); - await testSubjects.missingOrFail('logsExplorerFlyoutService'); - await testSubjects.existOrFail('logsExplorerFlyoutTrace'); - await testSubjects.existOrFail('logsExplorerFlyoutHostName'); - await testSubjects.existOrFail('logsExplorerFlyoutClusterName'); - await testSubjects.existOrFail('logsExplorerFlyoutResourceId'); - await dataGrid.closeFlyout(); - }); - - it('should not load the service container if all fields are missing', async () => { - await dataGrid.clickRowToggle({ rowIndex: 2, columnIndex: 4 }); - await testSubjects.missingOrFail('logsExplorerFlyoutHighlightSectionServiceInfra'); - await dataGrid.closeFlyout(); - }); - }); - - describe('Cloud container', () => { - const { cloudProvider, cloudInstanceId, cloudProjectId, cloudRegion, cloudAz, ...rest } = - sharedDoc; - const docWithoutCloudProviderAndInstanceId = { - ...rest, - cloudProjectId, - cloudRegion, - cloudAz, - time: NOW - 1000, - }; - const docWithoutCloudContainer = { ...rest, time: NOW - 2000 }; - - const docs = [sharedDoc, docWithoutCloudProviderAndInstanceId, docWithoutCloudContainer]; - before('setup DataStream', async () => { - cleanupDataStreamSetup = await PageObjects.observabilityLogsExplorer.setupDataStream( - DATASET_NAME, - NAMESPACE - ); - await PageObjects.observabilityLogsExplorer.ingestLogEntries(DATA_STREAM_NAME, docs); - await PageObjects.svlCommonPage.login(); - }); - - after('clean up DataStream', async () => { - await PageObjects.svlCommonPage.forceLogout(); - if (cleanupDataStreamSetup) { - await cleanupDataStreamSetup(); - } - }); - - beforeEach(async () => { - await PageObjects.observabilityLogsExplorer.navigateTo({ - pageState: { - time: { - from: new Date(NOW - 60_000).toISOString(), - to: new Date(NOW + 60_000).toISOString(), - mode: 'absolute', - }, - }, - }); - }); - - it('should load the cloud container with all fields', async () => { - await dataGrid.clickRowToggle({ columnIndex: 4 }); - await testSubjects.existOrFail('logsExplorerFlyoutHighlightSectionCloud'); - await testSubjects.existOrFail('logsExplorerFlyoutCloudProvider'); - await testSubjects.existOrFail('logsExplorerFlyoutCloudRegion'); - await testSubjects.existOrFail('logsExplorerFlyoutCloudAz'); - await testSubjects.existOrFail('logsExplorerFlyoutCloudProjectId'); - await testSubjects.existOrFail('logsExplorerFlyoutCloudInstanceId'); - await dataGrid.closeFlyout(); - }); - - it('should load the cloud container even when some fields are missing', async () => { - await dataGrid.clickRowToggle({ rowIndex: 1, columnIndex: 4 }); - await testSubjects.existOrFail('logsExplorerFlyoutHighlightSectionCloud'); - - await testSubjects.missingOrFail('logsExplorerFlyoutCloudProvider'); - await testSubjects.missingOrFail('logsExplorerFlyoutCloudInstanceId'); - - await testSubjects.existOrFail('logsExplorerFlyoutCloudRegion'); - await testSubjects.existOrFail('logsExplorerFlyoutCloudAz'); - await testSubjects.existOrFail('logsExplorerFlyoutCloudProjectId'); - await dataGrid.closeFlyout(); - }); - - it('should not load the cloud container if all fields are missing', async () => { - await dataGrid.clickRowToggle({ rowIndex: 2, columnIndex: 4 }); - await testSubjects.missingOrFail('logsExplorerFlyoutHighlightSectionCloud'); - await testSubjects.missingOrFail('logsExplorerFlyoutCloudProvider'); - await testSubjects.missingOrFail('logsExplorerFlyoutCloudRegion'); - await testSubjects.missingOrFail('logsExplorerFlyoutCloudAz'); - await testSubjects.missingOrFail('logsExplorerFlyoutCloudProjectId'); - await testSubjects.missingOrFail('logsExplorerFlyoutCloudInstanceId'); - await dataGrid.closeFlyout(); - }); - }); - - describe('Other container', () => { - const { logFilepath, agentName, ...rest } = sharedDoc; - const docWithoutLogPathAndAgentName = { - ...rest, - time: NOW - 1000, - }; - - const docs = [sharedDoc, docWithoutLogPathAndAgentName]; - before('setup DataStream', async () => { - cleanupDataStreamSetup = await PageObjects.observabilityLogsExplorer.setupDataStream( - DATASET_NAME, - NAMESPACE - ); - await PageObjects.observabilityLogsExplorer.ingestLogEntries(DATA_STREAM_NAME, docs); - await PageObjects.svlCommonPage.login(); - }); - - after('clean up DataStream', async () => { - await PageObjects.svlCommonPage.forceLogout(); - if (cleanupDataStreamSetup) { - await cleanupDataStreamSetup(); - } - }); - - beforeEach(async () => { - await PageObjects.observabilityLogsExplorer.navigateTo({ - pageState: { - time: { - from: new Date(NOW - 60_000).toISOString(), - to: new Date(NOW + 60_000).toISOString(), - mode: 'absolute', - }, - }, - }); - }); - - it('should load the other container with all fields', async () => { - await dataGrid.clickRowToggle({ columnIndex: 4 }); - await testSubjects.existOrFail('logsExplorerFlyoutHighlightSectionOther'); - await testSubjects.existOrFail('logsExplorerFlyoutLogPathFile'); - await testSubjects.existOrFail('logsExplorerFlyoutNamespace'); - await testSubjects.existOrFail('logsExplorerFlyoutDataset'); - await testSubjects.existOrFail('logsExplorerFlyoutLogShipper'); - await dataGrid.closeFlyout(); - }); - - it('should load the other container even when some fields are missing', async () => { - await dataGrid.clickRowToggle({ rowIndex: 1, columnIndex: 4 }); - await testSubjects.existOrFail('logsExplorerFlyoutHighlightSectionOther'); - - await testSubjects.missingOrFail('logsExplorerFlyoutLogPathFile'); - await testSubjects.missingOrFail('logsExplorerFlyoutLogShipper'); - - await testSubjects.existOrFail('logsExplorerFlyoutNamespace'); - await testSubjects.existOrFail('logsExplorerFlyoutDataset'); - await dataGrid.closeFlyout(); - }); - }); - }); -} diff --git a/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/index.ts b/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/index.ts index 4036df7cf97bec..bb651514795745 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/index.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/index.ts @@ -16,8 +16,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./data_source_selector')); loadTestFile(require.resolve('./field_list')); loadTestFile(require.resolve('./filter_controls')); - // loadTestFile(require.resolve('./flyout_highlights')); - // loadTestFile(require.resolve('./flyout')); loadTestFile(require.resolve('./header_menu')); }); } From cb9a96b979854ce2192f7f7cf6719db5cc7498cf Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Mon, 22 Apr 2024 13:04:18 +0200 Subject: [PATCH 21/28] refactor: change request --- .../src/services/doc_views_registry.test.tsx | 18 +++++ .../public/hooks/use_field_actions.tsx | 20 ++--- .../logs_explorer/common/constants.ts | 42 ++--------- .../observability_logs_explorer/flyout.ts | 73 ++++++++++++++++++ .../apps/observability_logs_explorer/index.ts | 1 + .../observability_logs_explorer/flyout.ts | 75 +++++++++++++++++++ .../observability_logs_explorer/index.ts | 1 + 7 files changed, 181 insertions(+), 49 deletions(-) create mode 100644 x-pack/test/functional/apps/observability_logs_explorer/flyout.ts create mode 100644 x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/flyout.ts diff --git a/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.test.tsx b/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.test.tsx index dbc0c93e8ba448..9154866120b017 100644 --- a/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.test.tsx +++ b/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.test.tsx @@ -103,6 +103,24 @@ describe('DocViewerRegistry', () => { expect(docViews[0]).toHaveProperty('id', 'function-doc-view'); expect(docViews[1]).toHaveProperty('id', 'component-doc-view'); expect(registry).not.toBe(clonedRegistry); + + // Test against shared references between clones + expect(clonedRegistry).not.toBe(registry); + + // Mutating a cloned registry should not affect the original registry + registry.disableById('function-doc-view'); + expect(registry.getAll()[0]).toHaveProperty('enabled', false); + expect(clonedRegistry.getAll()[0]).toHaveProperty('enabled', true); + + clonedRegistry.add({ + id: 'additional-doc-view', + order: 20, + title: 'Render function', + render: jest.fn(), + }); + + expect(registry.getAll().length).toBe(2); + expect(clonedRegistry.getAll().length).toBe(3); }); }); }); diff --git a/src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx b/src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx index 2a08b07f60d8bb..e703386818f895 100644 --- a/src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx +++ b/src/plugins/unified_doc_viewer/public/hooks/use_field_actions.tsx @@ -73,13 +73,13 @@ export const useUIFieldActions = ({ field, value }: TFieldActionParams): TFieldA { id: 'addFilterInAction', iconType: 'plusInCircle', - label: actionFilterForText(value as string), + label: filterForValueLabel, onClick: () => actions.addFilterIn({ field, value }), }, { id: 'addFilterOutremoveFromFilterAction', iconType: 'minusInCircle', - label: actionFilterOutText(value as string), + label: filterOutValueLabel, onClick: () => actions.addFilterOut({ field, value }), }, { @@ -105,17 +105,13 @@ export const useUIFieldActions = ({ field, value }: TFieldActionParams): TFieldA ); }; -const actionFilterForText = (value: string) => - i18n.translate('unifiedDocViewer.fieldActions.filterIn', { - defaultMessage: 'Filter for this {value}', - values: { value }, - }); +const filterForValueLabel = i18n.translate('unifiedDocViewer.fieldActions.filterForValue', { + defaultMessage: 'Filter for value', +}); -const actionFilterOutText = (value: string) => - i18n.translate('unifiedDocViewer.fieldActions.filterOut', { - defaultMessage: 'Filter out this {value}', - values: { value }, - }); +const filterOutValueLabel = i18n.translate('unifiedDocViewer.fieldActions.filterOutValue', { + defaultMessage: 'Filter out value', +}); const filterForFieldPresentLabel = i18n.translate( 'unifiedDocViewer.fieldActions.filterForFieldPresent', diff --git a/x-pack/plugins/observability_solution/logs_explorer/common/constants.ts b/x-pack/plugins/observability_solution/logs_explorer/common/constants.ts index aec0ad3b3adea9..ae8eff46fe7e2b 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/common/constants.ts +++ b/x-pack/plugins/observability_solution/logs_explorer/common/constants.ts @@ -5,44 +5,12 @@ * 2.0. */ +import { fieldConstants } from '@kbn/discover-utils'; import { SmartFieldGridColumnOptions } from './display_options'; -export const LOGS_EXPLORER_PROFILE_ID = 'logs-explorer'; - -// Fields constants -export const TIMESTAMP_FIELD = '@timestamp'; -export const HOST_NAME_FIELD = 'host.name'; -export const LOG_LEVEL_FIELD = 'log.level'; -export const MESSAGE_FIELD = 'message'; -export const ERROR_MESSAGE_FIELD = 'error.message'; -export const EVENT_ORIGINAL_FIELD = 'event.original'; -export const TRACE_ID_FIELD = 'trace.id'; - -export const LOG_FILE_PATH_FIELD = 'log.file.path'; -export const DATASTREAM_NAMESPACE_FIELD = 'data_stream.namespace'; -export const DATASTREAM_DATASET_FIELD = 'data_stream.dataset'; +export * from '@kbn/discover-utils/src/field_constants'; -// Resource Fields -export const AGENT_NAME_FIELD = 'agent.name'; -export const CLOUD_PROVIDER_FIELD = 'cloud.provider'; -export const CLOUD_REGION_FIELD = 'cloud.region'; -export const CLOUD_AVAILABILITY_ZONE_FIELD = 'cloud.availability_zone'; -export const CLOUD_PROJECT_ID_FIELD = 'cloud.project.id'; -export const CLOUD_INSTANCE_ID_FIELD = 'cloud.instance.id'; -export const SERVICE_NAME_FIELD = 'service.name'; -export const ORCHESTRATOR_CLUSTER_NAME_FIELD = 'orchestrator.cluster.name'; -export const ORCHESTRATOR_RESOURCE_ID_FIELD = 'orchestrator.resource.id'; -export const ORCHESTRATOR_NAMESPACE_FIELD = 'orchestrator.namespace'; -export const CONTAINER_NAME_FIELD = 'container.name'; -export const CONTAINER_ID_FIELD = 'container.id'; - -// Degraded Docs -export const DEGRADED_DOCS_FIELD = 'ignored_field_values'; - -// Error Stacktrace -export const ERROR_STACK_TRACE = 'error.stack_trace'; -export const ERROR_EXCEPTION_STACKTRACE = 'error.exception.stacktrace'; -export const ERROR_LOG_STACKTRACE = 'error.log.stacktrace'; +export const LOGS_EXPLORER_PROFILE_ID = 'logs-explorer'; // Virtual column fields export const CONTENT_FIELD = 'content'; @@ -56,14 +24,14 @@ export const ACTIONS_COLUMN_WIDTH = 80; export const RESOURCE_FIELD_CONFIGURATION: SmartFieldGridColumnOptions = { type: 'smart-field', smartField: RESOURCE_FIELD, - fallbackFields: [HOST_NAME_FIELD, SERVICE_NAME_FIELD], + fallbackFields: [fieldConstants.HOST_NAME_FIELD, fieldConstants.SERVICE_NAME_FIELD], width: DATA_GRID_COLUMN_WIDTH_MEDIUM, }; export const CONTENT_FIELD_CONFIGURATION: SmartFieldGridColumnOptions = { type: 'smart-field', smartField: CONTENT_FIELD, - fallbackFields: [MESSAGE_FIELD], + fallbackFields: [fieldConstants.MESSAGE_FIELD], }; export const SMART_FALLBACK_FIELDS = { diff --git a/x-pack/test/functional/apps/observability_logs_explorer/flyout.ts b/x-pack/test/functional/apps/observability_logs_explorer/flyout.ts new file mode 100644 index 00000000000000..22732d9933bbb3 --- /dev/null +++ b/x-pack/test/functional/apps/observability_logs_explorer/flyout.ts @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { FtrProviderContext } from '../../ftr_provider_context'; + +const DATASET_NAME = 'flyout'; +const NAMESPACE = 'default'; +const DATA_STREAM_NAME = `logs-${DATASET_NAME}-${NAMESPACE}`; +const NOW = Date.now(); + +const sharedDoc = { + logFilepath: '/flyout.log', + serviceName: DATASET_NAME, + datasetName: DATASET_NAME, + namespace: NAMESPACE, +}; + +const docs = [ + { + ...sharedDoc, + time: NOW + 1000, + message: 'full document', + logLevel: 'info', + }, + { + ...sharedDoc, + time: NOW, + }, +]; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const dataGrid = getService('dataGrid'); + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['observabilityLogsExplorer']); + + describe('Flyout content customization', () => { + let cleanupDataStreamSetup: () => Promise; + + before('initialize tests', async () => { + cleanupDataStreamSetup = await PageObjects.observabilityLogsExplorer.setupDataStream( + DATASET_NAME, + NAMESPACE + ); + await PageObjects.observabilityLogsExplorer.ingestLogEntries(DATA_STREAM_NAME, docs); + }); + + beforeEach(async () => { + await PageObjects.observabilityLogsExplorer.navigateTo({ + pageState: { + time: { + from: new Date(NOW - 60_000).toISOString(), + to: new Date(NOW + 60_000).toISOString(), + mode: 'absolute', + }, + }, + }); + }); + + after('clean up DataStream', async () => { + if (cleanupDataStreamSetup) { + await cleanupDataStreamSetup(); + } + }); + + it('should display the logs overview tab', async () => { + await dataGrid.clickRowToggle({ columnIndex: 4 }); + await testSubjects.existOrFail('docViewerTab-doc_view_logs_overview'); + }); + }); +} diff --git a/x-pack/test/functional/apps/observability_logs_explorer/index.ts b/x-pack/test/functional/apps/observability_logs_explorer/index.ts index 5e29637d79a320..6683fb8d7d26d9 100644 --- a/x-pack/test/functional/apps/observability_logs_explorer/index.ts +++ b/x-pack/test/functional/apps/observability_logs_explorer/index.ts @@ -16,6 +16,7 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./data_source_selector')); loadTestFile(require.resolve('./field_list')); loadTestFile(require.resolve('./filter_controls')); + loadTestFile(require.resolve('./flyout')); loadTestFile(require.resolve('./header_menu')); }); } diff --git a/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/flyout.ts b/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/flyout.ts new file mode 100644 index 00000000000000..8758ca590a5158 --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/flyout.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { FtrProviderContext } from '../../../ftr_provider_context'; + +const DATASET_NAME = 'flyout'; +const NAMESPACE = 'default'; +const DATA_STREAM_NAME = `logs-${DATASET_NAME}-${NAMESPACE}`; +const NOW = Date.now(); + +const sharedDoc = { + logFilepath: '/flyout.log', + serviceName: DATASET_NAME, + datasetName: DATASET_NAME, + namespace: NAMESPACE, +}; + +const docs = [ + { + ...sharedDoc, + time: NOW + 1000, + message: 'full document', + logLevel: 'info', + }, + { + ...sharedDoc, + time: NOW, + }, +]; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const dataGrid = getService('dataGrid'); + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['observabilityLogsExplorer', 'svlCommonPage']); + + describe('Flyout content customization', () => { + let cleanupDataStreamSetup: () => Promise; + + before('initialize tests', async () => { + cleanupDataStreamSetup = await PageObjects.observabilityLogsExplorer.setupDataStream( + DATASET_NAME, + NAMESPACE + ); + await PageObjects.observabilityLogsExplorer.ingestLogEntries(DATA_STREAM_NAME, docs); + await PageObjects.svlCommonPage.login(); + }); + + beforeEach(async () => { + await PageObjects.observabilityLogsExplorer.navigateTo({ + pageState: { + time: { + from: new Date(NOW - 60_000).toISOString(), + to: new Date(NOW + 60_000).toISOString(), + mode: 'absolute', + }, + }, + }); + }); + + after('clean up archives', async () => { + await PageObjects.svlCommonPage.forceLogout(); + if (cleanupDataStreamSetup) { + cleanupDataStreamSetup(); + } + }); + + it('should display the logs overview tab', async () => { + await dataGrid.clickRowToggle({ columnIndex: 4 }); + await testSubjects.existOrFail('docViewerTab-doc_view_logs_overview'); + }); + }); +} diff --git a/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/index.ts b/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/index.ts index bb651514795745..1f49779be0f27e 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/index.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/index.ts @@ -16,6 +16,7 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./data_source_selector')); loadTestFile(require.resolve('./field_list')); loadTestFile(require.resolve('./filter_controls')); + loadTestFile(require.resolve('./flyout')); loadTestFile(require.resolve('./header_menu')); }); } From 0b6ff80a5e2872780639af9ae01fac61f782aeb6 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Mon, 22 Apr 2024 14:21:32 +0200 Subject: [PATCH 22/28] test(infra): update page object --- .../test/functional/page_objects/observability_logs_explorer.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/test/functional/page_objects/observability_logs_explorer.ts b/x-pack/test/functional/page_objects/observability_logs_explorer.ts index 81217122a36a99..832d4caf0e83e3 100644 --- a/x-pack/test/functional/page_objects/observability_logs_explorer.ts +++ b/x-pack/test/functional/page_objects/observability_logs_explorer.ts @@ -119,7 +119,6 @@ export function ObservabilityLogsExplorerPageObject({ getService, }: FtrProviderContext) { const PageObjects = getPageObjects(['common']); - const dataGrid = getService('dataGrid'); const es = getService('es'); const log = getService('log'); const queryBar = getService('queryBar'); From 4ea0c2fcdb044314ceb9ced6ef291dbbdbd4c0a3 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Tue, 23 Apr 2024 14:08:37 +0200 Subject: [PATCH 23/28] refactor(unified-doc-viewer): simplify logs overview logic --- .../logs_overview.tsx | 3 +- .../logs_overview_header.tsx | 95 +++++---------- .../logs_overview_highlights.tsx | 108 ++++-------------- .../sub_components/highlight_container.tsx | 41 ------- .../sub_components/highlight_field.tsx | 99 ++++++---------- .../highlight_field_description.tsx | 10 +- .../sub_components/highlight_section.tsx | 44 +++---- .../sub_components/hover_popover_action.tsx | 4 +- 8 files changed, 122 insertions(+), 282 deletions(-) delete mode 100644 src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_container.tsx diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx index 078cf2807a552f..313bae2fab2165 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { DocViewRenderProps } from '@kbn/unified-doc-viewer/types'; import { getDocumentOverview } from '@kbn/discover-utils'; -import { EuiSpacer } from '@elastic/eui'; +import { EuiHorizontalRule, EuiSpacer } from '@elastic/eui'; import { LogsOverviewHeader } from './logs_overview_header'; import { LogsOverviewHighlights } from './logs_overview_highlights'; import { FieldActionsProvider } from '../../hooks/use_field_actions'; @@ -35,6 +35,7 @@ export function LogsOverview({ > + ); diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx index 9793990bdfcb98..55eea1c0f9af06 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx @@ -31,10 +31,10 @@ export const contentLabel = i18n.translate('unifiedDocViewer.docView.logsOvervie }); export function LogsOverviewHeader({ doc }: { doc: DocumentOverview }) { - const hasTimestamp = Boolean(doc[fieldConstants.TIMESTAMP_FIELD]); const hasLogLevel = Boolean(doc[fieldConstants.LOG_LEVEL_FIELD]); - const hasBadges = hasTimestamp || hasLogLevel; + const hasTimestamp = Boolean(doc[fieldConstants.TIMESTAMP_FIELD]); const { field, value } = getMessageFieldWithFallbacks(doc); + const hasBadges = hasTimestamp || hasLogLevel; const hasMessageField = field && value; const hasFlyoutHeader = hasMessageField || hasBadges; @@ -48,67 +48,38 @@ export function LogsOverviewHeader({ doc }: { doc: DocumentOverview }) { ); - const logLevelAndTimestamp = ( - - {hasBadges && ( - - {doc[fieldConstants.LOG_LEVEL_FIELD] && ( - - - - - - )} - {hasTimestamp && ( - - - - )} - + const logLevelAndTimestamp = hasBadges && ( + + {doc[fieldConstants.LOG_LEVEL_FIELD] && ( + + + )} - + {hasTimestamp && } + ); const contentField = hasMessageField && ( - - - - - - - {field} - - - {logLevelAndTimestamp} - - - - - - {value} - - - + + + + {field} + + {logLevelAndTimestamp} - + + + {value} + + + ); return hasFlyoutHeader ? ( @@ -119,13 +90,7 @@ export function LogsOverviewHeader({ doc }: { doc: DocumentOverview }) { initialIsOpen={true} data-test-subj="unifiedDocViewLogsOverviewHeader" > - - {hasMessageField ? contentField : logLevelAndTimestamp} - + {hasMessageField ? contentField : logLevelAndTimestamp} ) : null; } diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_highlights.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_highlights.tsx index 9253beb0785e92..a358b0a86992ff 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_highlights.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_highlights.tsx @@ -8,32 +8,11 @@ import React from 'react'; import { CloudProvider, CloudProviderIcon } from '@kbn/custom-icons'; -import { useMeasure } from 'react-use/lib'; import { first } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { useEuiTheme } from '@elastic/eui'; import { DataTableRecord, DocumentOverview, fieldConstants } from '@kbn/discover-utils'; import { HighlightField } from './sub_components/highlight_field'; import { HighlightSection } from './sub_components/highlight_section'; -import { HighlightContainer } from './sub_components/highlight_container'; - -interface ColumnWidth { - columns: 1 | 2 | 3; - fieldWidth: number; -} - -export const useColumnWidth = (width: number): ColumnWidth => { - const { euiTheme } = useEuiTheme(); - - const numberOfColumns = width > euiTheme.breakpoint.m ? 3 : width > euiTheme.breakpoint.s ? 2 : 1; - const WIDTH_FACTOR = 1.25; - const fieldWidth = width / (numberOfColumns * WIDTH_FACTOR); - - return { - columns: numberOfColumns, - fieldWidth, - }; -}; export function LogsOverviewHighlights({ formattedDoc, @@ -42,78 +21,64 @@ export function LogsOverviewHighlights({ formattedDoc: DocumentOverview; flattenedDoc: DataTableRecord['flattened']; }) { - const [ref, dimensions] = useMeasure(); - const { columns, fieldWidth } = useColumnWidth(dimensions.width); + const getHighlightProps = (field: keyof DocumentOverview) => ({ + field, + formattedValue: formattedDoc[field], + value: flattenedDoc[field], + }); + return ( - + <> {/* Service & Infrastructure highlight */} {formattedDoc[fieldConstants.SERVICE_NAME_FIELD] && ( )} {formattedDoc[fieldConstants.HOST_NAME_FIELD] && ( )} {formattedDoc[fieldConstants.TRACE_ID_FIELD] && ( )} {formattedDoc[fieldConstants.ORCHESTRATOR_CLUSTER_NAME_FIELD] && ( )} {formattedDoc[fieldConstants.ORCHESTRATOR_RESOURCE_ID_FIELD] && ( )} {/* Cloud highlight */} {formattedDoc[fieldConstants.CLOUD_PROVIDER_FIELD] && ( } - label={cloudProviderLabel} - value={flattenedDoc[fieldConstants.CLOUD_PROVIDER_FIELD]} - width={fieldWidth} + {...getHighlightProps(fieldConstants.CLOUD_PROVIDER_FIELD)} /> )} {formattedDoc[fieldConstants.CLOUD_REGION_FIELD] && ( )} {formattedDoc[fieldConstants.CLOUD_AVAILABILITY_ZONE_FIELD] && ( )} {formattedDoc[fieldConstants.CLOUD_PROJECT_ID_FIELD] && ( )} {formattedDoc[fieldConstants.CLOUD_INSTANCE_ID_FIELD] && ( )} {/* Other highlights */} {formattedDoc[fieldConstants.LOG_FILE_PATH_FIELD] && ( )} {formattedDoc[fieldConstants.DATASTREAM_DATASET_FIELD] && ( )} {formattedDoc[fieldConstants.DATASTREAM_NAMESPACE_FIELD] && ( )} {formattedDoc[fieldConstants.AGENT_NAME_FIELD] && ( )} - + ); } diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_container.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_container.tsx deleted file mode 100644 index 57b38e4bdab469..00000000000000 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_container.tsx +++ /dev/null @@ -1,41 +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 - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React from 'react'; -import { EuiHorizontalRule } from '@elastic/eui'; - -interface HighlightContainerProps { - children: React.ReactNode; -} - -const hasNonUndefinedSubChild = (children: React.ReactNode[]): boolean => { - return children.some((child) => { - if (React.isValidElement(child)) { - const subChildren = React.Children.toArray(child.props.children); - return subChildren.some((subChild) => subChild !== undefined && subChild !== null); - } - return false; - }); -}; - -export const HighlightContainer = React.forwardRef( - ({ children }, ref) => { - const validChildren = React.Children.toArray(children).filter(Boolean); - const hasChildren = validChildren.length > 0; - const shouldRender = hasChildren && hasNonUndefinedSubChild(validChildren); - - const flexChildren = validChildren.map((child, idx) =>
{child}
); - - return shouldRender ? ( -
- - {flexChildren} -
- ) : null; - } -); diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_field.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_field.tsx index fa9043d166d45b..176f8cf2b2a775 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_field.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_field.tsx @@ -6,90 +6,67 @@ * Side Public License, v 1. */ -import { - EuiBadge, - EuiFlexGroup, - EuiFlexItem, - EuiText, - EuiTextTruncate, - EuiTitle, - useEuiTheme, -} from '@elastic/eui'; +import { EuiBadge, EuiFlexGroup, EuiText, EuiTitle } from '@elastic/eui'; import { css } from '@emotion/react'; import React, { ReactNode } from 'react'; import { dynamic } from '@kbn/shared-ux-utility'; +import { euiThemeVars } from '@kbn/ui-theme'; import { HoverActionPopover } from './hover_popover_action'; const HighlightFieldDescription = dynamic(() => import('./highlight_field_description')); interface HighlightFieldProps { - useBadge?: boolean; field: string; - formattedValue: string; + formattedValue?: string; icon?: ReactNode; label: string; + useBadge?: boolean; value?: unknown; - width: number; } export function HighlightField({ - useBadge = false, field, formattedValue, icon, label, + useBadge = false, value, - width, ...props }: HighlightFieldProps) { - const { euiTheme } = useEuiTheme(); - return formattedValue && value ? ( - - - - - - {label} - - - - - +
+ + + {label} + + + + + + {icon} + {useBadge ? ( + + {formattedValue} + + ) : ( + + )} - - - - - {icon && {icon}} - - - {(truncatedText: string) => - useBadge ? ( - {truncatedText} - ) : ( - - ) - } - - - - - - + +
) : null; } + +const fieldNameStyle = css` + color: ${euiThemeVars.euiColorDarkShade}; +`; diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_field_description.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_field_description.tsx index 6566cd511c3d05..bfe2ef236da972 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_field_description.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_field_description.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { EuiFlexGroup, EuiFlexItem, EuiIconTip } from '@elastic/eui'; +import { EuiFlexGroup, EuiIconTip } from '@elastic/eui'; import { EcsFlat } from '@elastic/ecs'; import { FieldIcon } from '@kbn/react-field'; import React from 'react'; @@ -18,12 +18,8 @@ export function HighlightFieldDescription({ fieldName }: { fieldName: string }) const title = ( - {type && ( - - - - )} - {fieldName} + {type && } + {fieldName} ); diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_section.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_section.tsx index fe60e46344e5cb..3f4a233d54015d 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_section.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_section.tsx @@ -6,32 +6,34 @@ * Side Public License, v 1. */ -import React, { useState } from 'react'; +import React, { PropsWithChildren, useReducer } from 'react'; import { EuiAccordion, EuiFlexGrid, EuiHorizontalRule, EuiTitle, - EuiFlexItem, useGeneratedHtmlId, EuiButtonEmpty, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { css } from '@emotion/react'; interface HighlightSectionProps { title: string; - children: React.ReactNode; - columns: 1 | 2 | 3; + visibleItems?: number; } -const CHILDREN_PER_SECTION: 3 | 6 | 9 = 6; - -export function HighlightSection({ title, children, columns, ...props }: HighlightSectionProps) { +export function HighlightSection({ + children, + title, + visibleItems = 6, + ...props +}: PropsWithChildren) { const validChildren = React.Children.toArray(children).filter(Boolean); - const childLength = validChildren.length; - const shouldRenderSection = childLength > 0; - const limitedChildren = validChildren.slice(0, CHILDREN_PER_SECTION - 1); - const [showMore, setShowMore] = useState(childLength > CHILDREN_PER_SECTION); + const childrenLength = validChildren.length; + const shouldRenderSection = childrenLength > 0; + const limitedChildren = validChildren.slice(0, visibleItems - 1); + const [isListExpanded, expandList] = useReducer(() => true, childrenLength <= visibleItems); const accordionId = useGeneratedHtmlId({ prefix: title, @@ -41,19 +43,18 @@ export function HighlightSection({ title, children, columns, ...props }: Highlig 'unifiedDocViewer.docView.logsOverview.section.showMore', { defaultMessage: '+ {count} more', - values: { count: childLength - limitedChildren.length }, + values: { count: childrenLength - limitedChildren.length }, } ); const showMoreButton = ( { - setShowMore(false); - }} + onClick={expandList} > {showMoreButtonLabel} @@ -67,9 +68,7 @@ export function HighlightSection({ title, children, columns, ...props }: Highlig ); - const flexChildren = (showMore ? limitedChildren : validChildren).map((child, idx) => ( - {child} - )); + const displayedItems = isListExpanded ? validChildren : limitedChildren; return shouldRenderSection ? ( <> @@ -80,11 +79,16 @@ export function HighlightSection({ title, children, columns, ...props }: Highlig initialIsOpen={true} {...props} > - - {flexChildren} + + {displayedItems} ) : null; } + +// Applying this custom css rule remove the need for custom runtime js to compute a responsive column layout +const gridStyle = css` + grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); +`; diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/hover_popover_action.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/hover_popover_action.tsx index bdf1c45278e5b6..7df7aa7950e85d 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/hover_popover_action.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/hover_popover_action.tsx @@ -55,7 +55,7 @@ export const HoverActionPopover = ({ }; return ( -
+ -
+
); }; From 88ab78d35c2f7802a6b4091d8e5e50354c1ba991 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 23 Apr 2024 12:14:30 +0000 Subject: [PATCH 24/28] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- src/plugins/unified_doc_viewer/tsconfig.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/unified_doc_viewer/tsconfig.json b/src/plugins/unified_doc_viewer/tsconfig.json index 6fbcf0ebbac847..1e641f1bce8914 100644 --- a/src/plugins/unified_doc_viewer/tsconfig.json +++ b/src/plugins/unified_doc_viewer/tsconfig.json @@ -27,7 +27,8 @@ "@kbn/code-editor", "@kbn/code-editor-mock", "@kbn/custom-icons", - "@kbn/react-field" + "@kbn/react-field", + "@kbn/ui-theme" ], "exclude": [ "target/**/*", From 528673928c9e7483a9b40cad635f541aac01bb60 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Wed, 24 Apr 2024 08:46:56 +0200 Subject: [PATCH 25/28] Update packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts Co-authored-by: Davis McPhee --- .../kbn-unified-doc-viewer/src/services/doc_views_registry.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts b/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts index e2c01c7d530c33..8babc9c5aa9451 100644 --- a/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts +++ b/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts @@ -79,7 +79,7 @@ export class DocViewsRegistry { } clone() { - return new DocViewsRegistry(this.getAll().map(this.createDocView)); + return new DocViewsRegistry(this.getAll()); } private sortDocViews() { From d2647a69466637ca50c126fd45aa894db7111a7e Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Wed, 24 Apr 2024 08:53:10 +0200 Subject: [PATCH 26/28] refactor(unified-doc-viewer): update naming --- packages/kbn-discover-utils/src/types.ts | 11 +++++++---- .../src/utils/get_document_overview.ts | 8 ++++---- .../src/utils/get_message_field_with_fallbacks.ts | 4 ++-- .../doc_viewer_logs_overview/logs_overview_header.tsx | 4 ++-- .../logs_overview_highlights.tsx | 6 +++--- .../sub_components/log_level.tsx | 4 ++-- 6 files changed, 20 insertions(+), 17 deletions(-) diff --git a/packages/kbn-discover-utils/src/types.ts b/packages/kbn-discover-utils/src/types.ts index d2603be89f9787..05d9a79b0bbbd4 100644 --- a/packages/kbn-discover-utils/src/types.ts +++ b/packages/kbn-discover-utils/src/types.ts @@ -47,7 +47,10 @@ type FormattedHitPair = readonly [ */ export type FormattedHit = FormattedHitPair[]; -export interface DocumentOverview extends ResourceFields, StackTraceFields, CloudFields { +export interface LogDocumentOverview + extends LogResourceFields, + LogStackTraceFields, + LogCloudFields { '@timestamp': string; 'log.level'?: string; message?: string; @@ -59,7 +62,7 @@ export interface DocumentOverview extends ResourceFields, StackTraceFields, Clou 'data_stream.dataset': string; } -export interface ResourceFields { +export interface LogResourceFields { 'host.name'?: string; 'service.name'?: string; 'agent.name'?: string; @@ -71,13 +74,13 @@ export interface ResourceFields { 'container.id'?: string; } -export interface StackTraceFields { +export interface LogStackTraceFields { 'error.stack_trace'?: string; 'error.exception.stacktrace'?: string; 'error.log.stacktrace'?: string; } -export interface CloudFields { +export interface LogCloudFields { 'cloud.provider'?: string; 'cloud.region'?: string; 'cloud.availability_zone'?: string; diff --git a/packages/kbn-discover-utils/src/utils/get_document_overview.ts b/packages/kbn-discover-utils/src/utils/get_document_overview.ts index d7530dac231507..045254ea8d6fcf 100644 --- a/packages/kbn-discover-utils/src/utils/get_document_overview.ts +++ b/packages/kbn-discover-utils/src/utils/get_document_overview.ts @@ -8,13 +8,13 @@ import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/public'; -import { DataTableRecord, DocumentOverview, fieldConstants, formatFieldValue } from '../..'; +import { DataTableRecord, LogDocumentOverview, fieldConstants, formatFieldValue } from '../..'; export function getDocumentOverview( doc: DataTableRecord, { dataView, fieldFormats }: { dataView: DataView; fieldFormats: FieldFormatsStart } -): DocumentOverview { - const formatField = (field: T) => { +): LogDocumentOverview { + const formatField = (field: T) => { return ( field in doc.flattened ? formatFieldValue( @@ -25,7 +25,7 @@ export function getDocumentOverview( dataView.fields.getByName(field) ) : undefined - ) as DocumentOverview[T]; + ) as LogDocumentOverview[T]; }; const levelArray = doc.flattened[fieldConstants.LOG_LEVEL_FIELD]; diff --git a/packages/kbn-discover-utils/src/utils/get_message_field_with_fallbacks.ts b/packages/kbn-discover-utils/src/utils/get_message_field_with_fallbacks.ts index a4755e6e3fdd80..09fda703faee42 100644 --- a/packages/kbn-discover-utils/src/utils/get_message_field_with_fallbacks.ts +++ b/packages/kbn-discover-utils/src/utils/get_message_field_with_fallbacks.ts @@ -7,9 +7,9 @@ */ import { fieldConstants } from '..'; -import { DocumentOverview } from '../types'; +import { LogDocumentOverview } from '../types'; -export const getMessageFieldWithFallbacks = (doc: DocumentOverview) => { +export const getMessageFieldWithFallbacks = (doc: LogDocumentOverview) => { const rankingOrder = [ fieldConstants.MESSAGE_FIELD, fieldConstants.ERROR_MESSAGE_FIELD, diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx index 55eea1c0f9af06..2f050abbbbce63 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx @@ -17,7 +17,7 @@ import { EuiTitle, } from '@elastic/eui'; import { - DocumentOverview, + LogDocumentOverview, fieldConstants, getMessageFieldWithFallbacks, } from '@kbn/discover-utils'; @@ -30,7 +30,7 @@ export const contentLabel = i18n.translate('unifiedDocViewer.docView.logsOvervie defaultMessage: 'Content breakdown', }); -export function LogsOverviewHeader({ doc }: { doc: DocumentOverview }) { +export function LogsOverviewHeader({ doc }: { doc: LogDocumentOverview }) { const hasLogLevel = Boolean(doc[fieldConstants.LOG_LEVEL_FIELD]); const hasTimestamp = Boolean(doc[fieldConstants.TIMESTAMP_FIELD]); const { field, value } = getMessageFieldWithFallbacks(doc); diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_highlights.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_highlights.tsx index a358b0a86992ff..a7d998b0f1ebaa 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_highlights.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_highlights.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { CloudProvider, CloudProviderIcon } from '@kbn/custom-icons'; import { first } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { DataTableRecord, DocumentOverview, fieldConstants } from '@kbn/discover-utils'; +import { DataTableRecord, LogDocumentOverview, fieldConstants } from '@kbn/discover-utils'; import { HighlightField } from './sub_components/highlight_field'; import { HighlightSection } from './sub_components/highlight_section'; @@ -18,10 +18,10 @@ export function LogsOverviewHighlights({ formattedDoc, flattenedDoc, }: { - formattedDoc: DocumentOverview; + formattedDoc: LogDocumentOverview; flattenedDoc: DataTableRecord['flattened']; }) { - const getHighlightProps = (field: keyof DocumentOverview) => ({ + const getHighlightProps = (field: keyof LogDocumentOverview) => ({ field, formattedValue: formattedDoc[field], value: flattenedDoc[field], diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/log_level.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/log_level.tsx index 349752f99fa3d7..5fcfaa871ddf2c 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/log_level.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/log_level.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { EuiBadge, useEuiTheme } from '@elastic/eui'; import { css } from '@emotion/react'; -import { DocumentOverview } from '@kbn/discover-utils'; +import { LogDocumentOverview } from '@kbn/discover-utils'; const LEVEL_DICT = { error: 'danger', @@ -21,7 +21,7 @@ const LEVEL_DICT = { type Level = keyof typeof LEVEL_DICT; interface LogLevelProps { - level: DocumentOverview['log.level']; + level: LogDocumentOverview['log.level']; } export function LogLevel({ level }: LogLevelProps) { From 88b678714daf5b8c29294d497bee74f3568d0872 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Wed, 24 Apr 2024 08:55:07 +0200 Subject: [PATCH 27/28] refactor(unified-doc-viewer): update naming getLogDocumentOverview --- packages/kbn-discover-utils/index.ts | 2 +- ...{get_document_overview.ts => get_log_document_overview.ts} | 2 +- packages/kbn-discover-utils/src/utils/index.ts | 2 +- .../components/doc_viewer_logs_overview/logs_overview.tsx | 4 ++-- .../public/components/virtual_columns/content.tsx | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) rename packages/kbn-discover-utils/src/utils/{get_document_overview.ts => get_log_document_overview.ts} (99%) diff --git a/packages/kbn-discover-utils/index.ts b/packages/kbn-discover-utils/index.ts index 0330bfb06b7775..0434a05a9ce922 100644 --- a/packages/kbn-discover-utils/index.ts +++ b/packages/kbn-discover-utils/index.ts @@ -35,7 +35,7 @@ export { formatFieldValue, formatHit, getDocId, - getDocumentOverview, + getLogDocumentOverview, getIgnoredReason, getMessageFieldWithFallbacks, getShouldShowFieldHandler, diff --git a/packages/kbn-discover-utils/src/utils/get_document_overview.ts b/packages/kbn-discover-utils/src/utils/get_log_document_overview.ts similarity index 99% rename from packages/kbn-discover-utils/src/utils/get_document_overview.ts rename to packages/kbn-discover-utils/src/utils/get_log_document_overview.ts index 045254ea8d6fcf..92fb5e6c0834d8 100644 --- a/packages/kbn-discover-utils/src/utils/get_document_overview.ts +++ b/packages/kbn-discover-utils/src/utils/get_log_document_overview.ts @@ -10,7 +10,7 @@ import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/public'; import { DataTableRecord, LogDocumentOverview, fieldConstants, formatFieldValue } from '../..'; -export function getDocumentOverview( +export function getLogDocumentOverview( doc: DataTableRecord, { dataView, fieldFormats }: { dataView: DataView; fieldFormats: FieldFormatsStart } ): LogDocumentOverview { diff --git a/packages/kbn-discover-utils/src/utils/index.ts b/packages/kbn-discover-utils/src/utils/index.ts index 43c990dd6df737..399d4e8ba298e7 100644 --- a/packages/kbn-discover-utils/src/utils/index.ts +++ b/packages/kbn-discover-utils/src/utils/index.ts @@ -10,8 +10,8 @@ export * from './build_data_record'; export * from './format_hit'; export * from './format_value'; export * from './get_doc_id'; -export * from './get_document_overview'; export * from './get_ignored_reason'; +export * from './get_log_document_overview'; export * from './get_message_field_with_fallbacks'; export * from './get_should_show_field_handler'; export * from './nested_fields'; diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx index 313bae2fab2165..564ce902f7238d 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { DocViewRenderProps } from '@kbn/unified-doc-viewer/types'; -import { getDocumentOverview } from '@kbn/discover-utils'; +import { getLogDocumentOverview } from '@kbn/discover-utils'; import { EuiHorizontalRule, EuiSpacer } from '@elastic/eui'; import { LogsOverviewHeader } from './logs_overview_header'; import { LogsOverviewHighlights } from './logs_overview_highlights'; @@ -24,7 +24,7 @@ export function LogsOverview({ onRemoveColumn, }: DocViewRenderProps) { const { fieldFormats } = getUnifiedDocViewerServices(); - const parsedDoc = getDocumentOverview(hit, { dataView, fieldFormats }); + const parsedDoc = getLogDocumentOverview(hit, { dataView, fieldFormats }); return ( { - const documentOverview = getDocumentOverview(row, { dataView, fieldFormats }); + const documentOverview = getLogDocumentOverview(row, { dataView, fieldFormats }); const { field, value } = getMessageFieldWithFallbacks(documentOverview); const renderLogMessage = field && value; From 72f7460b2bf757a7a9840c99b65115777604ece0 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Thu, 25 Apr 2024 15:23:12 +0200 Subject: [PATCH 28/28] refactor(unified-doc-viewer): update grid layout to auto-fill for better alignment --- .../doc_viewer_logs_overview/logs_overview_header.tsx | 7 ++++++- .../sub_components/highlight_section.tsx | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx index 2f050abbbbce63..4bbc2993dedb04 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/logs_overview_header.tsx @@ -68,7 +68,12 @@ export function LogsOverviewHeader({ doc }: { doc: LogDocumentOverview }) { gutterSize="s" data-test-subj="unifiedDocViewLogsOverviewMessage" > - + {field} diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_section.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_section.tsx index 3f4a233d54015d..f0e6b1cd5943f0 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_section.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/highlight_section.tsx @@ -90,5 +90,5 @@ export function HighlightSection({ // Applying this custom css rule remove the need for custom runtime js to compute a responsive column layout const gridStyle = css` - grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); + grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); `;