From 36a73ce52e2ca14c2d46c3a6504ebaa53fed9f98 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Mon, 30 Sep 2024 11:08:03 +0200 Subject: [PATCH] [One Discover] Add summary column for logs contextual experience (#192567) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 📓 Summary Closes https://github.com/elastic/logs-dev/issues/165 This work introduces a Summary column as a replacement for the Document one for the Discover logs contextual experience. > We also decided to port this change as a replacement for the **resource** and **content** virtual columns in Logs Explorer to have a better alignment between the 2 apps. ## 🎥 Demo https://github.com/user-attachments/assets/3dfca483-768e-471c-8735-d5fc8bbd5d00 ## 💡 Reviewer hints I left notes through the changes to help answer some questions. The notable changes on this PR are: - Rename `Document` column to `Summary`. - Refactor `resource` and `content` virtual columns into a single `Summary` column, which replaces the default Summary content for the logs' contextual experience. - Provide support for the plugin services to the context awareness profiles. - Disable virtual columns (clean up will go in a follow-up work) in Logs Explorer and rely on the Summary column as the default selection. --------- Co-authored-by: Marco Antonio Ghiani Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Davis McPhee --- .../degraded_docs_control.tsx | 2 +- .../data_types/logs/logs_context_service.ts | 10 +- .../logs/utils/get_field_from_doc.ts | 17 -- .../src/data_types/logs/utils/index.ts | 1 - .../src/utils/get_field_value.ts | 5 +- .../src/utils/get_log_document_overview.ts | 18 +- .../src/utils/get_stack_trace_fields.ts | 8 +- packages/kbn-unified-data-table/index.ts | 6 +- .../src/components/data_table_columns.tsx | 2 +- .../src/hooks/use_data_grid_density.ts | 18 +- .../src/hooks/use_row_height.ts | 77 ++++++-- .../src/utils/columns.ts | 4 +- .../utils/get_render_cell_popover.test.tsx | 17 +- .../src/utils/get_render_cell_popover.tsx | 18 +- .../context/context_app_content.tsx | 23 ++- .../layout/discover_documents.test.tsx | 3 +- .../components/layout/discover_documents.tsx | 29 ++- .../main/hooks/grid_customisations/index.ts | 13 +- .../main/hooks/grid_customisations/logs.tsx | 27 +-- .../use_virtual_column_services.tsx | 24 --- src/plugins/discover/public/build_services.ts | 3 + .../data_types/logs/cell_actions_popover.tsx | 181 ++++++++++++++++++ .../data_types/logs/copy_button.tsx | 30 --- .../data_types/logs/filter_in_button.tsx | 42 ---- .../data_types/logs/filter_out_button.tsx | 42 ---- .../components/data_types/logs/log_level.tsx | 38 ---- .../data_types/logs/log_level_badge_cell.tsx | 2 +- .../data_types/logs/popover_chip.tsx | 173 ----------------- ...sx => service_name_badge_with_actions.tsx} | 44 +++-- .../logs/summary_column/content.tsx | 104 ++++++++++ .../data_types/logs/summary_column/index.tsx | 39 ++++ .../logs/summary_column/resource.tsx | 40 ++++ .../summary_column/summary_column.test.tsx | 176 +++++++++++++++++ .../logs/summary_column/summary_column.tsx | 169 ++++++++++++++++ .../data_types/logs/summary_column/utils.tsx | 126 ++++++++++++ .../data_types/logs/translations.tsx | 4 + .../virtual_columns/logs/cell_renderer.tsx | 45 ----- .../virtual_columns/logs/column.tsx | 32 ---- .../content_column_tooltip.tsx | 47 ----- .../logs/column_tooltips/field_with_token.tsx | 45 ----- .../logs/column_tooltips/hover_popover.tsx | 60 ------ .../resource_column_tooltip.tsx | 50 ----- .../logs/column_tooltips/tooltip_button.tsx | 80 -------- .../virtual_columns/logs/content.tsx | 165 ---------------- .../virtual_columns/logs/resource.tsx | 71 ------- .../virtual_columns/logs/utils/resource.ts | 35 ---- .../context_awareness/__mocks__/index.tsx | 11 +- .../composable_profile.test.ts | 27 ++- .../hooks/use_profile_accessor.test.ts | 18 +- .../accessors/get_cell_renderers.tsx | 9 +- .../logs_data_source_profile/profile.test.ts | 10 +- .../logs_data_source_profile/profile.ts | 2 +- .../example_data_source_profile/profile.tsx | 6 +- .../example/example_root_pofile/profile.tsx | 6 +- .../profile_provider_services.ts | 12 +- .../register_profile_providers.test.ts | 29 +-- .../register_profile_providers.ts | 10 +- .../security_root_profile/profile.tsx | 4 +- .../public/context_awareness/types.ts | 33 +++- .../components/saved_search_grid.tsx | 34 +++- src/plugins/discover/public/plugin.tsx | 65 +++---- src/plugins/discover/public/types.ts | 4 +- .../utils/get_available_resource_fields.ts | 23 +++ src/plugins/discover/tsconfig.json | 1 + .../logs_overview_header.tsx | 11 +- .../extensions/_get_cell_renderers.ts | 63 ++++++ .../extensions/_get_default_app_state.ts | 8 +- .../apps/discover/esql/_esql_columns.ts | 8 +- .../discover/group2_data_grid1/_data_grid.ts | 4 +- .../_data_grid_copy_to_clipboard.ts | 4 +- .../_data_grid_field_data.ts | 2 +- .../apps/discover/group3/_drag_drop.ts | 4 +- .../discover/group4/_discover_fields_api.ts | 10 +- .../group5/_field_data_with_fields_api.ts | 4 +- .../apps/discover/group5/_source_filters.ts | 2 +- .../apps/discover/group6/_sidebar.ts | 4 +- .../discover/group6/_time_field_column.ts | 24 +-- .../logs_explorer/common/constants.ts | 11 +- .../customizations/logs_explorer_profile.tsx | 8 +- .../hooks/use_virtual_column_services.tsx | 21 -- .../translations/translations/fr-FR.json | 1 - .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - .../columns_selection.ts | 28 +-- .../observability_logs_explorer/field_list.ts | 2 +- .../header_menu.ts | 7 +- .../extensions/_get_cell_renderers.ts | 63 ++++++ .../extensions/_get_default_app_state.ts | 8 +- .../common/discover/group6/_sidebar.ts | 4 +- .../columns_selection.ts | 28 +-- .../observability_logs_explorer/field_list.ts | 2 +- .../header_menu.ts | 7 +- 92 files changed, 1482 insertions(+), 1327 deletions(-) delete mode 100644 packages/kbn-discover-utils/src/data_types/logs/utils/get_field_from_doc.ts delete mode 100644 src/plugins/discover/public/application/main/hooks/grid_customisations/use_virtual_column_services.tsx create mode 100644 src/plugins/discover/public/components/data_types/logs/cell_actions_popover.tsx delete mode 100644 src/plugins/discover/public/components/data_types/logs/copy_button.tsx delete mode 100644 src/plugins/discover/public/components/data_types/logs/filter_in_button.tsx delete mode 100644 src/plugins/discover/public/components/data_types/logs/filter_out_button.tsx delete mode 100644 src/plugins/discover/public/components/data_types/logs/log_level.tsx delete mode 100644 src/plugins/discover/public/components/data_types/logs/popover_chip.tsx rename src/plugins/discover/public/components/data_types/logs/{service_name_chip_with_popover.tsx => service_name_badge_with_actions.tsx} (56%) create mode 100644 src/plugins/discover/public/components/data_types/logs/summary_column/content.tsx create mode 100644 src/plugins/discover/public/components/data_types/logs/summary_column/index.tsx create mode 100644 src/plugins/discover/public/components/data_types/logs/summary_column/resource.tsx create mode 100644 src/plugins/discover/public/components/data_types/logs/summary_column/summary_column.test.tsx create mode 100644 src/plugins/discover/public/components/data_types/logs/summary_column/summary_column.tsx create mode 100644 src/plugins/discover/public/components/data_types/logs/summary_column/utils.tsx delete mode 100644 src/plugins/discover/public/components/discover_grid/virtual_columns/logs/cell_renderer.tsx delete mode 100644 src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column.tsx delete mode 100644 src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/content_column_tooltip.tsx delete mode 100644 src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/field_with_token.tsx delete mode 100644 src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/hover_popover.tsx delete mode 100644 src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/resource_column_tooltip.tsx delete mode 100644 src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/tooltip_button.tsx delete mode 100644 src/plugins/discover/public/components/discover_grid/virtual_columns/logs/content.tsx delete mode 100644 src/plugins/discover/public/components/discover_grid/virtual_columns/logs/resource.tsx delete mode 100644 src/plugins/discover/public/components/discover_grid/virtual_columns/logs/utils/resource.ts create mode 100644 src/plugins/discover/public/utils/get_available_resource_fields.ts delete mode 100644 x-pack/plugins/observability_solution/logs_explorer/public/hooks/use_virtual_column_services.tsx diff --git a/packages/kbn-discover-utils/src/components/custom_control_columns/degraded_docs_control.tsx b/packages/kbn-discover-utils/src/components/custom_control_columns/degraded_docs_control.tsx index be4dbe30272148..f3ea72fcc8780d 100644 --- a/packages/kbn-discover-utils/src/components/custom_control_columns/degraded_docs_control.tsx +++ b/packages/kbn-discover-utils/src/components/custom_control_columns/degraded_docs_control.tsx @@ -43,7 +43,7 @@ const degradedDocButtonLabelWhenPresent = i18n.translate( 'discover.customControl.degradedDocPresent', { defaultMessage: - "This document couldn't be parsed correctly. Not all fields are properly populated", + "This document couldn't be parsed correctly. Not all fields are properly populated.", } ); diff --git a/packages/kbn-discover-utils/src/data_types/logs/logs_context_service.ts b/packages/kbn-discover-utils/src/data_types/logs/logs_context_service.ts index 7af3a723e7b140..5f05def6d9c948 100644 --- a/packages/kbn-discover-utils/src/data_types/logs/logs_context_service.ts +++ b/packages/kbn-discover-utils/src/data_types/logs/logs_context_service.ts @@ -15,7 +15,7 @@ export interface LogsContextService { } export interface LogsContextServiceDeps { - logsDataAccessPlugin?: LogsDataAccessPluginStart; + logsDataAccess?: LogsDataAccessPluginStart; } export const DEFAULT_ALLOWED_LOGS_BASE_PATTERNS = [ @@ -31,13 +31,11 @@ export const DEFAULT_ALLOWED_LOGS_BASE_PATTERNS_REGEXP = createRegExpPatternFrom DEFAULT_ALLOWED_LOGS_BASE_PATTERNS ); -export const createLogsContextService = async ({ - logsDataAccessPlugin, -}: LogsContextServiceDeps) => { +export const createLogsContextService = async ({ logsDataAccess }: LogsContextServiceDeps) => { let logSources: string[] | undefined; - if (logsDataAccessPlugin) { - const logSourcesService = logsDataAccessPlugin.services.logSourcesService; + if (logsDataAccess) { + const logSourcesService = logsDataAccess.services.logSourcesService; logSources = (await logSourcesService.getLogSources()) .map((logSource) => logSource.indexPattern) .join(',') // TODO: Will be replaced by helper in: https://github.com/elastic/kibana/pull/192003 diff --git a/packages/kbn-discover-utils/src/data_types/logs/utils/get_field_from_doc.ts b/packages/kbn-discover-utils/src/data_types/logs/utils/get_field_from_doc.ts deleted file mode 100644 index 32e6d9a52b47b6..00000000000000 --- a/packages/kbn-discover-utils/src/data_types/logs/utils/get_field_from_doc.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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { LogDocument } from '../types'; - -type Field = keyof LogDocument['flattened']; - -export const getFieldFromDoc = (doc: LogDocument, field: T) => { - const fieldValueArray = doc.flattened[field]; - return fieldValueArray && fieldValueArray.length ? fieldValueArray[0] : undefined; -}; diff --git a/packages/kbn-discover-utils/src/data_types/logs/utils/index.ts b/packages/kbn-discover-utils/src/data_types/logs/utils/index.ts index a435502d9ecb92..0b266fa5b4935f 100644 --- a/packages/kbn-discover-utils/src/data_types/logs/utils/index.ts +++ b/packages/kbn-discover-utils/src/data_types/logs/utils/index.ts @@ -7,6 +7,5 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -export * from './get_field_from_doc'; export * from './get_log_level_color'; export * from './get_log_level_coalesed_value'; diff --git a/packages/kbn-discover-utils/src/utils/get_field_value.ts b/packages/kbn-discover-utils/src/utils/get_field_value.ts index 2bd94ce5dcbc84..dab2cdfdfb0200 100644 --- a/packages/kbn-discover-utils/src/utils/get_field_value.ts +++ b/packages/kbn-discover-utils/src/utils/get_field_value.ts @@ -9,7 +9,10 @@ import { DataTableRecord } from '../types'; -export const getFieldValue = (record: DataTableRecord, field: string) => { +export const getFieldValue = ( + record: TRecord, + field: TField & keyof TRecord['flattened'] +): TRecord['flattened'][TField] => { const value = record.flattened[field]; return Array.isArray(value) ? value[0] : value; }; diff --git a/packages/kbn-discover-utils/src/utils/get_log_document_overview.ts b/packages/kbn-discover-utils/src/utils/get_log_document_overview.ts index fafcf5c9faf38c..33c8fdba685265 100644 --- a/packages/kbn-discover-utils/src/utils/get_log_document_overview.ts +++ b/packages/kbn-discover-utils/src/utils/get_log_document_overview.ts @@ -17,7 +17,7 @@ export function getLogDocumentOverview( ): LogDocumentOverview { const formatField = (field: T) => { return ( - field in doc.flattened + doc.flattened[field] !== undefined && doc.flattened[field] !== null ? formatFieldValue( doc.flattened[field], doc.raw, @@ -32,19 +32,9 @@ export function getLogDocumentOverview( const levelArray = doc.flattened[fieldConstants.LOG_LEVEL_FIELD]; const level = 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] : messageArray; - const errorMessageArray = doc.flattened[fieldConstants.ERROR_MESSAGE_FIELD]; - const errorMessage = - 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] - : eventOriginalArray; + const message = formatField(fieldConstants.MESSAGE_FIELD); + const errorMessage = formatField(fieldConstants.ERROR_MESSAGE_FIELD); + const eventOriginal = formatField(fieldConstants.EVENT_ORIGINAL_FIELD); const timestamp = formatField(fieldConstants.TIMESTAMP_FIELD); // Service diff --git a/packages/kbn-discover-utils/src/utils/get_stack_trace_fields.ts b/packages/kbn-discover-utils/src/utils/get_stack_trace_fields.ts index 15a2b345be6152..b2d61c007ea3f2 100644 --- a/packages/kbn-discover-utils/src/utils/get_stack_trace_fields.ts +++ b/packages/kbn-discover-utils/src/utils/get_stack_trace_fields.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { getFieldFromDoc, LogDocument, StackTraceFields } from '..'; +import { getFieldValue, LogDocument, StackTraceFields } from '..'; import { ERROR_EXCEPTION_STACKTRACE, ERROR_LOG_STACKTRACE, @@ -15,9 +15,9 @@ import { } from '../field_constants'; export const getStacktraceFields = (doc: LogDocument): StackTraceFields => { - const errorStackTrace = getFieldFromDoc(doc, ERROR_STACK_TRACE); - const errorExceptionStackTrace = getFieldFromDoc(doc, ERROR_EXCEPTION_STACKTRACE); - const errorLogStackTrace = getFieldFromDoc(doc, ERROR_LOG_STACKTRACE); + const errorStackTrace = getFieldValue(doc, ERROR_STACK_TRACE); + const errorExceptionStackTrace = getFieldValue(doc, ERROR_EXCEPTION_STACKTRACE); + const errorLogStackTrace = getFieldValue(doc, ERROR_LOG_STACKTRACE); return { [ERROR_STACK_TRACE]: errorStackTrace, diff --git a/packages/kbn-unified-data-table/index.ts b/packages/kbn-unified-data-table/index.ts index ddcf6536112577..8f7e711bebcad5 100644 --- a/packages/kbn-unified-data-table/index.ts +++ b/packages/kbn-unified-data-table/index.ts @@ -13,11 +13,12 @@ export { RowHeightSettings, type RowHeightSettingsProps, } from './src/components/row_height_settings'; -export { getDisplayedColumns } from './src/utils/columns'; +export { getDisplayedColumns, SOURCE_COLUMN } from './src/utils/columns'; export { getTextBasedColumnsMeta } from './src/utils/get_columns_meta'; export { ROWS_HEIGHT_OPTIONS, DataGridDensity } from './src/constants'; export { JSONCodeEditorCommonMemoized } from './src/components/json_code_editor/json_code_editor_common'; +export { SourceDocument } from './src/components/source_document'; export * from './src/types'; export * as columnActions from './src/components/actions/columns'; @@ -37,3 +38,6 @@ export { getRenderCustomToolbarWithElements, renderCustomToolbar, } from './src/components/custom_toolbar/render_custom_toolbar'; + +export { getDataGridDensity } from './src/hooks/use_data_grid_density'; +export { getRowHeight } from './src/hooks/use_row_height'; diff --git a/packages/kbn-unified-data-table/src/components/data_table_columns.tsx b/packages/kbn-unified-data-table/src/components/data_table_columns.tsx index 876db4b0e71498..985a5db9f2178d 100644 --- a/packages/kbn-unified-data-table/src/components/data_table_columns.tsx +++ b/packages/kbn-unified-data-table/src/components/data_table_columns.tsx @@ -46,7 +46,7 @@ export const getColumnDisplayName = ( if (columnName === '_source') { return i18n.translate('unifiedDataTable.grid.documentHeader', { - defaultMessage: 'Document', + defaultMessage: 'Summary', }); } diff --git a/packages/kbn-unified-data-table/src/hooks/use_data_grid_density.ts b/packages/kbn-unified-data-table/src/hooks/use_data_grid_density.ts index 3a408637651edb..ff2fb71ca1c35e 100644 --- a/packages/kbn-unified-data-table/src/hooks/use_data_grid_density.ts +++ b/packages/kbn-unified-data-table/src/hooks/use_data_grid_density.ts @@ -30,8 +30,6 @@ interface UseDataGridDensityProps { onUpdateDataGridDensity?: (density: DataGridDensity) => void; } -export const DATA_GRID_DENSITY_STORAGE_KEY = 'dataGridDensity'; - export function getDensityFromStyle(style: EuiDataGridStyle) { return style.cellPadding === DATA_GRID_STYLE_COMPACT.cellPadding && style.fontSize === DATA_GRID_STYLE_COMPACT.fontSize @@ -42,24 +40,30 @@ export function getDensityFromStyle(style: EuiDataGridStyle) { : DataGridDensity.EXPANDED; } +const DATA_GRID_DENSITY_STORAGE_KEY = 'dataGridDensity'; +const getStorageKey = (consumer: string) => `${consumer}:${DATA_GRID_DENSITY_STORAGE_KEY}`; + +export const getDataGridDensity = (storage: Storage, consumer: string): DataGridDensity => { + return storage.get(getStorageKey(consumer)) ?? DataGridDensity.COMPACT; +}; + export const useDataGridDensity = ({ storage, consumer, dataGridDensityState, onUpdateDataGridDensity, }: UseDataGridDensityProps) => { - const storageKey = `${consumer}:${DATA_GRID_DENSITY_STORAGE_KEY}`; const dataGridDensity = useMemo(() => { - return dataGridDensityState ?? storage.get(storageKey) ?? DataGridDensity.COMPACT; - }, [dataGridDensityState, storage, storageKey]); + return dataGridDensityState ?? getDataGridDensity(storage, consumer); + }, [consumer, dataGridDensityState, storage]); const onChangeDataGridDensity = useCallback( (gridStyle: EuiDataGridStyle) => { const newDensity = getDensityFromStyle(gridStyle); - storage.set(storageKey, newDensity); + storage.set(getStorageKey(consumer), newDensity); onUpdateDataGridDensity?.(newDensity); }, - [storageKey, storage, onUpdateDataGridDensity] + [storage, consumer, onUpdateDataGridDensity] ); return { diff --git a/packages/kbn-unified-data-table/src/hooks/use_row_height.ts b/packages/kbn-unified-data-table/src/hooks/use_row_height.ts index 3b91f18c4d731d..f4dada8c6bb60f 100644 --- a/packages/kbn-unified-data-table/src/hooks/use_row_height.ts +++ b/packages/kbn-unified-data-table/src/hooks/use_row_height.ts @@ -27,6 +27,59 @@ interface UseRowHeightProps { onUpdateRowHeight?: (rowHeight: number) => void; } +interface ResolveRowHeightParams { + storage: Storage; + consumer: string; + key: string; + configRowHeight: number; + rowHeightState?: number; +} + +const resolveRowHeight = ({ + storage, + consumer, + key, + configRowHeight, + rowHeightState, +}: ResolveRowHeightParams): number => { + const rowHeightFromLS = getStoredRowHeight(storage, consumer, key); + + const configHasNotChanged = ( + localStorageRecord: DataGridOptionsRecord | null + ): localStorageRecord is DataGridOptionsRecord => + localStorageRecord !== null && configRowHeight === localStorageRecord.previousConfigRowHeight; + + let currentRowLines: number; + if (isValidRowHeight(rowHeightState)) { + currentRowLines = rowHeightState; + } else if (configHasNotChanged(rowHeightFromLS)) { + currentRowLines = rowHeightFromLS.previousRowHeight; + } else { + currentRowLines = configRowHeight; + } + + return currentRowLines; +}; + +export const ROW_HEIGHT_STORAGE_KEY = 'dataGridRowHeight'; + +export const getRowHeight = ({ + storage, + consumer, + rowHeightState, + configRowHeight, +}: Pick & { + configRowHeight?: number; +}) => { + return resolveRowHeight({ + storage, + consumer, + key: ROW_HEIGHT_STORAGE_KEY, + configRowHeight: configRowHeight ?? ROWS_HEIGHT_OPTIONS.default, + rowHeightState, + }); +}; + export const useRowHeight = ({ storage, consumer, @@ -36,23 +89,13 @@ export const useRowHeight = ({ onUpdateRowHeight, }: UseRowHeightProps) => { const rowHeightLines = useMemo(() => { - const rowHeightFromLS = getStoredRowHeight(storage, consumer, key); - - const configHasNotChanged = ( - localStorageRecord: DataGridOptionsRecord | null - ): localStorageRecord is DataGridOptionsRecord => - localStorageRecord !== null && configRowHeight === localStorageRecord.previousConfigRowHeight; - - let currentRowLines: number; - if (isValidRowHeight(rowHeightState)) { - currentRowLines = rowHeightState; - } else if (configHasNotChanged(rowHeightFromLS)) { - currentRowLines = rowHeightFromLS.previousRowHeight; - } else { - currentRowLines = configRowHeight; - } - - return currentRowLines; + return resolveRowHeight({ + storage, + consumer, + key, + configRowHeight, + rowHeightState, + }); }, [configRowHeight, consumer, key, rowHeightState, storage]); const rowHeight = useMemo(() => { diff --git a/packages/kbn-unified-data-table/src/utils/columns.ts b/packages/kbn-unified-data-table/src/utils/columns.ts index a171e320e5c3b3..80f3ba643108f1 100644 --- a/packages/kbn-unified-data-table/src/utils/columns.ts +++ b/packages/kbn-unified-data-table/src/utils/columns.ts @@ -9,10 +9,12 @@ import type { DataView } from '@kbn/data-views-plugin/public'; +export const SOURCE_COLUMN = '_source'; + // We store this outside the function as a constant, so we're not creating a new array every time // the function is returning this. A changing array might cause the data grid to think it got // new columns, and thus performing worse than using the same array over multiple renders. -const SOURCE_ONLY = ['_source']; +const SOURCE_ONLY = [SOURCE_COLUMN]; /** * Function to provide fallback when diff --git a/packages/kbn-unified-data-table/src/utils/get_render_cell_popover.test.tsx b/packages/kbn-unified-data-table/src/utils/get_render_cell_popover.test.tsx index 1d692cc5926069..a1bed774759c0a 100644 --- a/packages/kbn-unified-data-table/src/utils/get_render_cell_popover.test.tsx +++ b/packages/kbn-unified-data-table/src/utils/get_render_cell_popover.test.tsx @@ -27,10 +27,10 @@ const defaultProps: EuiDataGridCellPopoverElementProps = { cellContentsElement: (
{'cellContentsElement'}
) as unknown as HTMLDivElement, }; -const renderTestComponent = () => { +const renderTestComponent = (overrideProps = {}) => { const Renderer = getCustomCellPopoverRenderer(); - render(); + render(); }; describe('getCustomCellPopoverRenderer', () => { @@ -45,4 +45,17 @@ describe('getCustomCellPopoverRenderer', () => { panelClassName: 'unifiedDataTable__cellPopover', }); }); + + it('should render a DefaultCellPopover with a wider panel for allowed columns', () => { + renderTestComponent({ columnId: '_source' }); + + expect(setCellPopoverPropsMocks).toHaveBeenCalledWith({ + panelClassName: 'unifiedDataTable__cellPopover', + panelProps: { + css: { + maxInlineSize: 'min(75vw, 600px) !important', + }, + }, + }); + }); }); diff --git a/packages/kbn-unified-data-table/src/utils/get_render_cell_popover.tsx b/packages/kbn-unified-data-table/src/utils/get_render_cell_popover.tsx index 5bcdfda20cfb63..c685f0c007d409 100644 --- a/packages/kbn-unified-data-table/src/utils/get_render_cell_popover.tsx +++ b/packages/kbn-unified-data-table/src/utils/get_render_cell_popover.tsx @@ -9,6 +9,9 @@ import { EuiDataGridCellPopoverElementProps } from '@elastic/eui'; import React, { memo, useEffect } from 'react'; +import { SOURCE_COLUMN } from './columns'; + +const FIELDS_WITH_WIDE_POPOVER = [SOURCE_COLUMN]; /* * @@ -23,13 +26,20 @@ export const getCustomCellPopoverRenderer = () => { const RenderCustomCellPopoverMemoized = memo(function RenderCustomCellPopoverMemoized( props: EuiDataGridCellPopoverElementProps ) { - const { setCellPopoverProps, DefaultCellPopover } = props; + const { columnId, setCellPopoverProps, DefaultCellPopover } = props; useEffect(() => { - setCellPopoverProps({ + const popoverProps: Parameters[0] = { panelClassName: 'unifiedDataTable__cellPopover', - }); - }, [setCellPopoverProps]); + }; + + const shouldRenderWidePopover = FIELDS_WITH_WIDE_POPOVER.includes(columnId); + if (shouldRenderWidePopover) { + popoverProps.panelProps = { css: { maxInlineSize: 'min(75vw, 600px) !important' } }; + } + + setCellPopoverProps(popoverProps); + }, [columnId, setCellPopoverProps]); return ; }); diff --git a/src/plugins/discover/public/application/context/context_app_content.tsx b/src/plugins/discover/public/application/context/context_app_content.tsx index b822286ae72a9d..b6b4be221377aa 100644 --- a/src/plugins/discover/public/application/context/context_app_content.tsx +++ b/src/plugins/discover/public/application/context/context_app_content.tsx @@ -28,7 +28,12 @@ import { ROW_HEIGHT_OPTION, SHOW_MULTIFIELDS, } from '@kbn/discover-utils'; -import { DataLoadingState, UnifiedDataTableProps } from '@kbn/unified-data-table'; +import { + DataLoadingState, + UnifiedDataTableProps, + getDataGridDensity, + getRowHeight, +} from '@kbn/unified-data-table'; import { DocViewFilterFn } from '@kbn/unified-doc-viewer/types'; import { useQuerySubscriber } from '@kbn/unified-field-list'; import useObservable from 'react-use/lib/useObservable'; @@ -170,11 +175,21 @@ export function ContextAppContent({ [grid, setAppState] ); + const configRowHeight = services.uiSettings.get(ROW_HEIGHT_OPTION); const getCellRenderersAccessor = useProfileAccessor('getCellRenderers'); const cellRenderers = useMemo(() => { const getCellRenderers = getCellRenderersAccessor(() => ({})); - return getCellRenderers(); - }, [getCellRenderersAccessor]); + return getCellRenderers({ + actions: { addFilter }, + dataView, + density: getDataGridDensity(services.storage, 'discover'), + rowHeight: getRowHeight({ + storage: services.storage, + consumer: 'discover', + configRowHeight, + }), + }); + }, [addFilter, configRowHeight, dataView, getCellRenderersAccessor, services.storage]); const dataSource = useMemo(() => createDataSource({ dataView, query: undefined }), [dataView]); const { filters } = useQuerySubscriber({ data: services.data }); @@ -249,7 +264,7 @@ export function ContextAppContent({ setExpandedDoc={setExpandedDoc} onFilter={addFilter} onSetColumns={onSetColumns} - configRowHeight={services.uiSettings.get(ROW_HEIGHT_OPTION)} + configRowHeight={configRowHeight} showMultiFields={services.uiSettings.get(SHOW_MULTIFIELDS)} maxDocFieldsDisplayed={services.uiSettings.get(MAX_DOC_FIELDS_DISPLAYED)} renderDocumentView={renderDocumentView} diff --git a/src/plugins/discover/public/application/main/components/layout/discover_documents.test.tsx b/src/plugins/discover/public/application/main/components/layout/discover_documents.test.tsx index 564e4fc23ea904..effa9f99d6c4b2 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_documents.test.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_documents.test.tsx @@ -144,8 +144,7 @@ describe('Discover documents layout', () => { const discoverGridComponent = component.find(DiscoverGrid); expect(discoverGridComponent.exists()).toBeTruthy(); expect(Object.keys(discoverGridComponent.prop('externalCustomRenderers')!)).toEqual([ - 'content', - 'resource', + '_source', 'rootProfile', ]); }); diff --git a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx index 4d8fd0fbccb956..2fe2a4f5a8f93f 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx @@ -29,9 +29,11 @@ import { type DataTableColumnsMeta, getTextBasedColumnsMeta, getRenderCustomToolbarWithElements, - type DataGridDensity, + DataGridDensity, UnifiedDataTableProps, UseColumnsProps, + getDataGridDensity, + getRowHeight, } from '@kbn/unified-data-table'; import { DOC_HIDE_TIME_COLUMN_SETTING, @@ -72,6 +74,7 @@ import { useContextualGridCustomisations } from '../../hooks/grid_customisations import { useIsEsqlMode } from '../../hooks/use_is_esql_mode'; import { useAdditionalFieldGroups } from '../../hooks/sidebar/use_additional_field_groups'; import { + CellRenderersExtensionParams, DISCOVER_CELL_ACTIONS_TRIGGER, useAdditionalCellActions, useProfileAccessor, @@ -306,16 +309,32 @@ function DiscoverDocumentsComponent({ [dataView, onAddColumn, onAddFilter, onRemoveColumn, query, savedSearch.id, setExpandedDoc] ); + const configRowHeight = uiSettings.get(ROW_HEIGHT_OPTION); + const cellRendererParams: CellRenderersExtensionParams = useMemo( + () => ({ + actions: { addFilter: onAddFilter }, + dataView, + density: density ?? getDataGridDensity(services.storage, 'discover'), + rowHeight: getRowHeight({ + storage: services.storage, + consumer: 'discover', + rowHeightState: rowHeight, + configRowHeight, + }), + }), + [onAddFilter, dataView, density, services.storage, rowHeight, configRowHeight] + ); + const { rowAdditionalLeadingControls } = useDiscoverCustomization('data_table') || {}; const { customCellRenderer, customGridColumnsConfiguration } = - useContextualGridCustomisations() || {}; + useContextualGridCustomisations(cellRendererParams) || {}; const additionalFieldGroups = useAdditionalFieldGroups(); const getCellRenderersAccessor = useProfileAccessor('getCellRenderers'); const cellRenderers = useMemo(() => { const getCellRenderers = getCellRenderersAccessor(() => customCellRenderer ?? {}); - return getCellRenderers(); - }, [customCellRenderer, getCellRenderersAccessor]); + return getCellRenderers(cellRendererParams); + }, [cellRendererParams, customCellRenderer, getCellRenderersAccessor]); const documents = useObservable(stateContainer.dataState.data$.documents$); @@ -458,7 +477,7 @@ function DiscoverDocumentsComponent({ sampleSizeState={getAllowedSampleSize(sampleSizeState, services.uiSettings)} onUpdateSampleSize={!isEsqlMode ? onUpdateSampleSize : undefined} onFieldEdited={onFieldEdited} - configRowHeight={uiSettings.get(ROW_HEIGHT_OPTION)} + configRowHeight={configRowHeight} showMultiFields={uiSettings.get(SHOW_MULTIFIELDS)} maxDocFieldsDisplayed={uiSettings.get(MAX_DOC_FIELDS_DISPLAYED)} renderDocumentView={renderDocumentView} diff --git a/src/plugins/discover/public/application/main/hooks/grid_customisations/index.ts b/src/plugins/discover/public/application/main/hooks/grid_customisations/index.ts index 88b7d27633bbef..996c64d34bd12a 100644 --- a/src/plugins/discover/public/application/main/hooks/grid_customisations/index.ts +++ b/src/plugins/discover/public/application/main/hooks/grid_customisations/index.ts @@ -8,22 +8,23 @@ */ import { useMemo } from 'react'; -import { useDiscoverServices } from '../../../../hooks/use_discover_services'; import { useDiscoverCustomization } from '../../../../customizations'; -import { getLogsVirtualColumnsConfiguration } from './logs'; +import { DataGridColumnsDeps, getDataGridColumnsConfiguration } from './logs'; export * from './logs'; -export const useContextualGridCustomisations = () => { - const { data } = useDiscoverServices(); +type ContextualGridCustomizationParams = DataGridColumnsDeps; + +export const useContextualGridCustomisations = (params: ContextualGridCustomizationParams) => { // TODO / NOTE: This will eventually rely on Discover's context resolution to determine which fields // are returned based on the data type. const isLogsContext = useDiscoverCustomization('data_table')?.logsEnabled; const virtualColumnsConfiguration = useMemo(() => { if (!isLogsContext) return null; - if (isLogsContext) return getLogsVirtualColumnsConfiguration(data); - }, [data, isLogsContext]); + + return getDataGridColumnsConfiguration(params); + }, [isLogsContext, params]); return virtualColumnsConfiguration; }; diff --git a/src/plugins/discover/public/application/main/hooks/grid_customisations/logs.tsx b/src/plugins/discover/public/application/main/hooks/grid_customisations/logs.tsx index b14605663d50bb..409c1e03fa7376 100644 --- a/src/plugins/discover/public/application/main/hooks/grid_customisations/logs.tsx +++ b/src/plugins/discover/public/application/main/hooks/grid_customisations/logs.tsx @@ -7,26 +7,27 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import { CONTENT_FIELD, RESOURCE_FIELD } from '../../../../../common/data_types/logs/constants'; -import { renderCell } from '../../../../components/discover_grid/virtual_columns/logs/cell_renderer'; -import { renderColumn } from '../../../../components/discover_grid/virtual_columns/logs/column'; +import { SOURCE_COLUMN } from '@kbn/unified-data-table'; +import { + SummaryColumnGetterDeps, + getSummaryColumn, +} from '../../../../components/data_types/logs/summary_column'; -export const getLogsVirtualColumnsConfiguration = (data: DataPublicPluginStart) => { +export type DataGridColumnsDeps = CustomCellRendererDeps; + +export const getDataGridColumnsConfiguration = (params: DataGridColumnsDeps) => { return { - customCellRenderer: createCustomCellRenderer({ data }), + customCellRenderer: createCustomCellRenderer(params), customGridColumnsConfiguration: createCustomGridColumnsConfiguration(), }; }; -export const createCustomCellRenderer = ({ data }: { data: DataPublicPluginStart }) => { +type CustomCellRendererDeps = SummaryColumnGetterDeps; + +export const createCustomCellRenderer = (params: CustomCellRendererDeps) => { return { - [CONTENT_FIELD]: renderCell(CONTENT_FIELD, { data }), - [RESOURCE_FIELD]: renderCell(RESOURCE_FIELD, { data }), + [SOURCE_COLUMN]: getSummaryColumn(params), }; }; -export const createCustomGridColumnsConfiguration = () => ({ - [CONTENT_FIELD]: renderColumn(CONTENT_FIELD), - [RESOURCE_FIELD]: renderColumn(RESOURCE_FIELD), -}); +export const createCustomGridColumnsConfiguration = () => ({}); diff --git a/src/plugins/discover/public/application/main/hooks/grid_customisations/use_virtual_column_services.tsx b/src/plugins/discover/public/application/main/hooks/grid_customisations/use_virtual_column_services.tsx deleted file mode 100644 index df8cb411b6e161..00000000000000 --- a/src/plugins/discover/public/application/main/hooks/grid_customisations/use_virtual_column_services.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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import createContainer from 'constate'; -import type { DataView } from '@kbn/data-views-plugin/common'; -import { DataPublicPluginStart } from '@kbn/data-plugin/public'; - -export interface UseVirtualColumnServices { - services: { - data: DataPublicPluginStart; - dataView: DataView; - }; -} - -const useVirtualColumns = ({ services }: UseVirtualColumnServices) => services; - -export const [VirtualColumnServiceProvider, useVirtualColumnServiceContext] = - createContainer(useVirtualColumns); diff --git a/src/plugins/discover/public/build_services.ts b/src/plugins/discover/public/build_services.ts index f4ee14fcf04f77..3d12e16a076941 100644 --- a/src/plugins/discover/public/build_services.ts +++ b/src/plugins/discover/public/build_services.ts @@ -58,6 +58,7 @@ import type { NoDataPagePluginStart } from '@kbn/no-data-page-plugin/public'; import type { AiopsPluginStart } from '@kbn/aiops-plugin/public'; import type { DataVisualizerPluginStart } from '@kbn/data-visualizer-plugin/public'; import type { FieldsMetadataPublicStart } from '@kbn/fields-metadata-plugin/public'; +import { LogsDataAccessPluginStart } from '@kbn/logs-data-access-plugin/public'; import type { DiscoverStartPlugins } from './types'; import type { DiscoverContextAppLocator } from './application/context/services/locator'; import type { DiscoverSingleDocLocator } from './application/doc/locator'; @@ -133,6 +134,7 @@ export interface DiscoverServices { profilesManager: ProfilesManager; ebtContextManager: DiscoverEBTContextManager; fieldsMetadata?: FieldsMetadataPublicStart; + logsDataAccess?: LogsDataAccessPluginStart; } export const buildServices = memoize( @@ -223,6 +225,7 @@ export const buildServices = memoize( profilesManager, ebtContextManager, fieldsMetadata: plugins.fieldsMetadata, + logsDataAccess: plugins.logsDataAccess, }; } ); diff --git a/src/plugins/discover/public/components/data_types/logs/cell_actions_popover.tsx b/src/plugins/discover/public/components/data_types/logs/cell_actions_popover.tsx new file mode 100644 index 00000000000000..7b9d68e8f3dd75 --- /dev/null +++ b/src/plugins/discover/public/components/data_types/logs/cell_actions_popover.tsx @@ -0,0 +1,181 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React, { ReactElement } from 'react'; +import { + EuiBadge, + type EuiBadgeProps, + EuiFlexGroup, + EuiFlexItem, + EuiPopover, + EuiPopoverFooter, + EuiText, + EuiButtonIcon, + EuiTextTruncate, + EuiButtonEmpty, + EuiCopy, +} from '@elastic/eui'; +import { css } from '@emotion/react'; +import { useBoolean } from '@kbn/react-hooks'; +import { euiThemeVars } from '@kbn/ui-theme'; +import { DocViewFilterFn } from '@kbn/unified-doc-viewer/types'; +import { + actionFilterForText, + actionFilterOutText, + closeCellActionPopoverText, + copyValueAriaText, + copyValueText, + filterForText, + filterOutText, + openCellActionPopoverAriaText, +} from './translations'; + +const codeFontCSS = css` + font-family: ${euiThemeVars.euiCodeFontFamily}; +`; + +interface CellActionsPopoverProps { + onFilter?: DocViewFilterFn; + /* ECS mapping for the key */ + property: string; + /* Value for the mapping, which will be displayed */ + value: string; + /* Optional callback to render the value */ + renderValue?: (value: string) => React.ReactNode; + /* Props to forward to the trigger Badge */ + renderPopoverTrigger: (props: { + popoverTriggerProps: { + onClick: () => void; + onClickAriaLabel: string; + 'data-test-subj': string; + }; + }) => ReactElement; +} + +export function CellActionsPopover({ + onFilter, + property, + value, + renderValue, + renderPopoverTrigger, +}: CellActionsPopoverProps) { + const [isPopoverOpen, { toggle: togglePopover, off: closePopover }] = useBoolean(false); + + const makeFilterHandlerByOperator = (operator: '+' | '-') => () => { + if (onFilter) { + onFilter(property, value, operator); + } + }; + + const popoverTriggerProps = { + onClick: togglePopover, + onClickAriaLabel: openCellActionPopoverAriaText, + 'data-test-subj': `dataTableCellActionsPopover_${property}`, + }; + + return ( + + + + + {property}{' '} + {typeof renderValue === 'function' ? renderValue(value) : value} + + + + + + + + + + {filterForText} + + + {filterOutText} + + + + + + {(copy) => ( + + {copyValueText} + + )} + + + + ); +} + +export interface FieldBadgeWithActionsProps + extends Pick { + icon?: EuiBadgeProps['iconType']; +} + +export function FieldBadgeWithActions({ + icon, + onFilter, + property, + renderValue, + value, +}: FieldBadgeWithActionsProps) { + return ( + ( + + + + )} + /> + ); +} diff --git a/src/plugins/discover/public/components/data_types/logs/copy_button.tsx b/src/plugins/discover/public/components/data_types/logs/copy_button.tsx deleted file mode 100644 index a609187ffde9bf..00000000000000 --- a/src/plugins/discover/public/components/data_types/logs/copy_button.tsx +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { EuiButtonEmpty, EuiFlexItem, copyToClipboard } from '@elastic/eui'; -import React from 'react'; -import { copyValueAriaText, copyValueText } from './translations'; - -export const CopyButton = ({ property, value }: { property: string; value: string }) => { - const ariaCopyValueText = copyValueAriaText(property); - - return ( - - copyToClipboard(value)} - data-test-subj={`dataTableCellAction_copyToClipboardAction_${property}`} - > - {copyValueText} - - - ); -}; diff --git a/src/plugins/discover/public/components/data_types/logs/filter_in_button.tsx b/src/plugins/discover/public/components/data_types/logs/filter_in_button.tsx deleted file mode 100644 index 58822a4c84e4d7..00000000000000 --- a/src/plugins/discover/public/components/data_types/logs/filter_in_button.tsx +++ /dev/null @@ -1,42 +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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { EuiButtonEmpty, EuiFlexItem } from '@elastic/eui'; -import React from 'react'; -import { generateFilters } from '@kbn/data-plugin/public'; -import { useVirtualColumnServiceContext } from '../../../application/main/hooks/grid_customisations/use_virtual_column_services'; -import { actionFilterForText, filterForText } from './translations'; - -export const FilterInButton = ({ property, value }: { property: string; value: string }) => { - const ariaFilterForText = actionFilterForText(value); - const serviceContext = useVirtualColumnServiceContext(); - const filterManager = serviceContext?.data.query.filterManager; - const dataView = serviceContext.dataView; - - const onFilterForAction = () => { - if (filterManager != null) { - const filter = generateFilters(filterManager, property, [value], '+', dataView); - filterManager.addFilters(filter); - } - }; - - return ( - - - {filterForText} - - - ); -}; diff --git a/src/plugins/discover/public/components/data_types/logs/filter_out_button.tsx b/src/plugins/discover/public/components/data_types/logs/filter_out_button.tsx deleted file mode 100644 index a08b54e2436cd0..00000000000000 --- a/src/plugins/discover/public/components/data_types/logs/filter_out_button.tsx +++ /dev/null @@ -1,42 +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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { EuiButtonEmpty, EuiFlexItem } from '@elastic/eui'; -import React from 'react'; -import { generateFilters } from '@kbn/data-plugin/public'; -import { useVirtualColumnServiceContext } from '../../../application/main/hooks/grid_customisations/use_virtual_column_services'; -import { actionFilterOutText, filterOutText } from './translations'; - -export const FilterOutButton = ({ property, value }: { property: string; value: string }) => { - const ariaFilterOutText = actionFilterOutText(value); - const serviceContext = useVirtualColumnServiceContext(); - const filterManager = serviceContext?.data.query.filterManager; - const dataView = serviceContext.dataView; - - const onFilterOutAction = () => { - if (filterManager != null) { - const filter = generateFilters(filterManager, property, [value], '-', dataView); - filterManager.addFilters(filter); - } - }; - - return ( - - - {filterOutText} - - - ); -}; diff --git a/src/plugins/discover/public/components/data_types/logs/log_level.tsx b/src/plugins/discover/public/components/data_types/logs/log_level.tsx deleted file mode 100644 index 096c3bf51734ea..00000000000000 --- a/src/plugins/discover/public/components/data_types/logs/log_level.tsx +++ /dev/null @@ -1,38 +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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import React from 'react'; -import { LogFlyoutDoc, LogLevelBadge } from '@kbn/discover-utils/src'; -import * as constants from '../../../../common/data_types/logs/constants'; -import { ChipPopover } from './popover_chip'; - -interface LogLevelProps { - level: LogFlyoutDoc['log.level']; -} - -export function LogLevel({ level }: LogLevelProps) { - if (!level) return null; - - return ( - ( - - )} - /> - ); -} diff --git a/src/plugins/discover/public/components/data_types/logs/log_level_badge_cell.tsx b/src/plugins/discover/public/components/data_types/logs/log_level_badge_cell.tsx index 883452c7cd12c4..bff3bdddee0261 100644 --- a/src/plugins/discover/public/components/data_types/logs/log_level_badge_cell.tsx +++ b/src/plugins/discover/public/components/data_types/logs/log_level_badge_cell.tsx @@ -8,9 +8,9 @@ */ import type { CSSObject } from '@emotion/react'; +import React from 'react'; import { LogLevelBadge } from '@kbn/discover-utils'; import type { DataGridCellValueElementProps } from '@kbn/unified-data-table'; -import React from 'react'; const dataTestSubj = 'logLevelBadgeCell'; const badgeCss: CSSObject = { marginTop: '-4px' }; diff --git a/src/plugins/discover/public/components/data_types/logs/popover_chip.tsx b/src/plugins/discover/public/components/data_types/logs/popover_chip.tsx deleted file mode 100644 index e84fcca52d6271..00000000000000 --- a/src/plugins/discover/public/components/data_types/logs/popover_chip.tsx +++ /dev/null @@ -1,173 +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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import React, { ReactElement, useCallback, useState } from 'react'; -import { - EuiBadge, - type EuiBadgeProps, - EuiFlexGroup, - EuiFlexItem, - EuiPopover, - useEuiFontSize, - EuiPopoverFooter, - EuiText, - EuiButtonIcon, -} from '@elastic/eui'; -import { css, SerializedStyles } from '@emotion/react'; -import { dynamic } from '@kbn/shared-ux-utility'; -import { closeCellActionPopoverText, openCellActionPopoverAriaText } from './translations'; -import { FilterInButton } from './filter_in_button'; -import { FilterOutButton } from './filter_out_button'; -import { CopyButton } from './copy_button'; - -const DataTablePopoverCellValue = dynamic( - () => import('@kbn/unified-data-table/src/components/data_table_cell_value') -); - -type ChipWithPopoverChildrenType = (props: { - content: string; -}) => React.ReactNode | React.ReactNode; - -export interface ChipWithPopoverProps { - /** - * ECS mapping for the key - */ - property: string; - /** - * Value for the mapping, which will be displayed - */ - text: string; - dataTestSubj?: string; - leftSideIcon?: React.ReactNode; - rightSideIcon?: EuiBadgeProps['iconType']; - children?: ChipWithPopoverChildrenType; -} - -export function ChipWithPopover({ - property, - text, - dataTestSubj = `dataTablePopoverChip_${property}`, - leftSideIcon, - rightSideIcon, - children, -}: ChipWithPopoverProps) { - return ( - ( - - - {leftSideIcon && {leftSideIcon}} - {text} - - - )} - > - {children} - - ); -} - -interface ChipPopoverProps { - /** - * ECS mapping for the key - */ - property: string; - /** - * Value for the mapping, which will be displayed - */ - text: string; - renderChip: (props: { - handleChipClick: () => void; - handleChipClickAriaLabel: string; - chipCss: SerializedStyles; - }) => ReactElement; - children?: ChipWithPopoverChildrenType; -} - -export function ChipPopover({ property, text, renderChip, children }: ChipPopoverProps) { - const xsFontSize = useEuiFontSize('xs').fontSize; - const [isPopoverOpen, setIsPopoverOpen] = useState(false); - - const handleChipClick = useCallback(() => { - setIsPopoverOpen(!isPopoverOpen); - }, [isPopoverOpen]); - - const closePopover = () => setIsPopoverOpen(false); - - return ( - - - -
- - - {property}{' '} - {typeof children === 'function' ? children({ content: text }) : text} - - -
-
- - - -
- - - - - - - - - - - -
- ); -} diff --git a/src/plugins/discover/public/components/data_types/logs/service_name_chip_with_popover.tsx b/src/plugins/discover/public/components/data_types/logs/service_name_badge_with_actions.tsx similarity index 56% rename from src/plugins/discover/public/components/data_types/logs/service_name_chip_with_popover.tsx rename to src/plugins/discover/public/components/data_types/logs/service_name_badge_with_actions.tsx index ecb456017c7a9e..581c889b8e98ee 100644 --- a/src/plugins/discover/public/components/data_types/logs/service_name_chip_with_popover.tsx +++ b/src/plugins/discover/public/components/data_types/logs/service_name_badge_with_actions.tsx @@ -11,38 +11,50 @@ import React from 'react'; import { getRouterLinkProps } from '@kbn/router-utils'; import { EuiLink } from '@elastic/eui'; import { OBSERVABILITY_ENTITY_CENTRIC_EXPERIENCE } from '@kbn/management-settings-ids'; -import { type ChipWithPopoverProps, ChipWithPopover } from './popover_chip'; +import { SharePublicStart } from '@kbn/share-plugin/public/plugin'; import { useDiscoverServices } from '../../../hooks/use_discover_services'; +import { FieldBadgeWithActions, FieldBadgeWithActionsProps } from './cell_actions_popover'; const SERVICE_ENTITY_LOCATOR = 'SERVICE_ENTITY_LOCATOR'; -export function ServiceNameChipWithPopover(props: ChipWithPopoverProps) { +export function ServiceNameBadgeWithActions(props: FieldBadgeWithActionsProps) { const { share, core } = useDiscoverServices(); const canViewApm = core.application.capabilities.apm?.show || false; const isEntityCentricExperienceSettingEnabled = canViewApm ? core.uiSettings.get(OBSERVABILITY_ENTITY_CENTRIC_EXPERIENCE) : false; - const urlService = share?.url; - const apmLinkToServiceEntityLocator = urlService?.locators.get<{ serviceName: string }>( + const derivedPropsForEntityExperience = isEntityCentricExperienceSettingEnabled + ? getDerivedPropsForEntityExperience({ serviceName: props.value, share }) + : {}; + + return ; +} + +const getDerivedPropsForEntityExperience = ({ + serviceName, + share, +}: { + serviceName: string; + share?: SharePublicStart; +}): Pick => { + const apmLinkToServiceEntityLocator = share?.url?.locators.get<{ serviceName: string }>( SERVICE_ENTITY_LOCATOR ); - const href = apmLinkToServiceEntityLocator?.getRedirectUrl({ - serviceName: props.text, - }); + const href = apmLinkToServiceEntityLocator?.getRedirectUrl({ serviceName }); const routeLinkProps = href ? getRouterLinkProps({ href, - onClick: () => apmLinkToServiceEntityLocator?.navigate({ serviceName: props.text }), + onClick: () => apmLinkToServiceEntityLocator?.navigate({ serviceName }), }) : undefined; - return ( - - {canViewApm && isEntityCentricExperienceSettingEnabled && routeLinkProps - ? ({ content }) => {content} - : undefined} - - ); -} + if (routeLinkProps) { + return { + renderValue: (value) => {value}, + }; + } + + return {}; +}; diff --git a/src/plugins/discover/public/components/data_types/logs/summary_column/content.tsx b/src/plugins/discover/public/components/data_types/logs/summary_column/content.tsx new file mode 100644 index 00000000000000..0da98cbf7145e1 --- /dev/null +++ b/src/plugins/discover/public/components/data_types/logs/summary_column/content.tsx @@ -0,0 +1,104 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React, { useMemo } from 'react'; +import { SourceDocument, type DataGridCellValueElementProps } from '@kbn/unified-data-table'; +import { + ShouldShowFieldInTableHandler, + getLogDocumentOverview, + getMessageFieldWithFallbacks, +} from '@kbn/discover-utils'; +import * as constants from '../../../../../common/data_types/logs/constants'; +import { formatJsonDocumentForContent } from './utils'; + +interface ContentProps extends DataGridCellValueElementProps { + isCompressed: boolean; + isSingleLine?: boolean; + shouldShowFieldHandler: ShouldShowFieldInTableHandler; +} + +const LogMessage = ({ + field, + value, + className, +}: { + field: string; + value: string; + className: string; +}) => { + const shouldRenderFieldName = field !== constants.MESSAGE_FIELD; + + if (shouldRenderFieldName) { + return ( +
+ {field}{' '} + +
+ ); + } + + return ( +

+ ); +}; + +export const Content = ({ + columnId, + dataView, + fieldFormats, + isCompressed, + isSingleLine = false, + row, + shouldShowFieldHandler, +}: ContentProps) => { + const documentOverview = getLogDocumentOverview(row, { dataView, fieldFormats }); + const { field, value } = getMessageFieldWithFallbacks(documentOverview); + const shouldRenderContent = !!field && !!value; + + return shouldRenderContent ? ( + + ) : ( + + ); +}; + +type FormattedSourceDocumentProps = Pick< + ContentProps, + 'columnId' | 'dataView' | 'fieldFormats' | 'isCompressed' | 'row' | 'shouldShowFieldHandler' +>; + +const FormattedSourceDocument = ({ row, ...props }: FormattedSourceDocumentProps) => { + const formattedRow = useMemo(() => formatJsonDocumentForContent(row), [row]); + + return ( + + ); +}; diff --git a/src/plugins/discover/public/components/data_types/logs/summary_column/index.tsx b/src/plugins/discover/public/components/data_types/logs/summary_column/index.tsx new file mode 100644 index 00000000000000..20fe4380199f33 --- /dev/null +++ b/src/plugins/discover/public/components/data_types/logs/summary_column/index.tsx @@ -0,0 +1,39 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React from 'react'; +import { dynamic } from '@kbn/shared-ux-utility'; +import { getShouldShowFieldHandler } from '@kbn/discover-utils'; +import { DataView } from '@kbn/data-views-plugin/common'; +import { CellRenderersExtensionParams } from '../../../../context_awareness'; +import type { SummaryColumnProps } from './summary_column'; + +const SummaryColumn = dynamic(() => import('./summary_column')); + +export type SummaryColumnGetterDeps = CellRenderersExtensionParams; + +export const getSummaryColumn = (params: SummaryColumnGetterDeps) => { + const { actions, dataView, density, rowHeight } = params; + const shouldShowFieldHandler = createGetShouldShowFieldHandler(dataView); + + return (props: SummaryColumnProps) => ( + + ); +}; + +const createGetShouldShowFieldHandler = (dataView: DataView) => { + const dataViewFields = dataView.fields.getAll().map((fld) => fld.name); + return getShouldShowFieldHandler(dataViewFields, dataView, true); +}; diff --git a/src/plugins/discover/public/components/data_types/logs/summary_column/resource.tsx b/src/plugins/discover/public/components/data_types/logs/summary_column/resource.tsx new file mode 100644 index 00000000000000..a7955fadde6225 --- /dev/null +++ b/src/plugins/discover/public/components/data_types/logs/summary_column/resource.tsx @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React from 'react'; +import { EuiBadge, EuiFlexGroup } from '@elastic/eui'; +import { DocViewFilterFn } from '@kbn/unified-doc-viewer/types'; +import { ResourceFieldDescriptor } from './utils'; + +const MAX_LIMITED_FIELDS_VISIBLE = 3; + +interface ResourceProps { + fields: ResourceFieldDescriptor[]; + /* When true, the column will render a predefined number of resources and indicates with a badge how many more we have */ + limited?: boolean; + onFilter?: DocViewFilterFn; +} + +export const Resource = ({ fields, limited = false, onFilter, ...props }: ResourceProps) => { + const displayedFields = limited ? fields.slice(0, MAX_LIMITED_FIELDS_VISIBLE) : fields; + const extraFieldsCount = limited ? fields.length - MAX_LIMITED_FIELDS_VISIBLE : 0; + + return ( + + {displayedFields.map(({ name, value, ResourceBadge, Icon }) => ( + + ))} + {extraFieldsCount > 0 && ( +

+ +{extraFieldsCount} +
+ )} + + ); +}; diff --git a/src/plugins/discover/public/components/data_types/logs/summary_column/summary_column.test.tsx b/src/plugins/discover/public/components/data_types/logs/summary_column/summary_column.test.tsx new file mode 100644 index 00000000000000..b8eeea613c9c65 --- /dev/null +++ b/src/plugins/discover/public/components/data_types/logs/summary_column/summary_column.test.tsx @@ -0,0 +1,176 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React from 'react'; +import { buildDataTableRecord, DataTableRecord } from '@kbn/discover-utils'; +import { dataViewMock } from '@kbn/discover-utils/src/__mocks__'; +import { fieldFormatsMock } from '@kbn/field-formats-plugin/common/mocks'; +import { render, screen } from '@testing-library/react'; +import SummaryColumn, { SummaryColumnFactoryDeps, SummaryColumnProps } from './summary_column'; +import { DataGridDensity, ROWS_HEIGHT_OPTIONS } from '@kbn/unified-data-table'; +import * as constants from '../../../../../common/data_types/logs/constants'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { discoverServiceMock } from '../../../../__mocks__/services'; + +const renderSummary = ( + record: DataTableRecord, + opts: Partial = {} +) => { + render( + + {}} + closePopover={() => {}} + density={DataGridDensity.COMPACT} + rowHeight={ROWS_HEIGHT_OPTIONS.single} + onFilter={jest.fn()} + shouldShowFieldHandler={() => true} + {...opts} + /> + + ); +}; + +const getBaseRecord = (overrides: Record = {}) => + buildDataTableRecord( + { + fields: { + '@timestamp': 1726218404776, + 'agent.name': 'nodejs', + 'cloud.availability_zone': 'area-51a', + 'cloud.instance.id': '3275100000000076', + 'cloud.project.id': '3275100000000075', + 'cloud.provider': 'azure', + 'cloud.region': 'area-51', + 'container.name': 'synth-service-2-3275100000000073', + 'error.log.stacktrace': 'Error message in error.log.stacktrace', + 'event.original': 'Error with certificate: "ca_trusted_fingerprint"', + 'host.name': 'synth-host', + 'orchestrator.cluster.id': '3275100000000002', + 'orchestrator.cluster.name': 'synth-cluster-3', + 'orchestrator.namespace': 'kube', + 'orchestrator.resource.id': '3275100000000074', + 'service.name': 'synth-service-2', + 'trace.id': '3275100000000072', + message: 'Yet another debug log', + ...overrides, + }, + }, + dataViewMock + ); + +describe('SummaryColumn', () => { + describe('when rendering resource badges', () => { + it('should render a maximum of 3 resource badges in row compact mode', () => { + const record = getBaseRecord(); + renderSummary(record); + expect( + screen.queryByTestId(`dataTableCellActionsPopover_${constants.SERVICE_NAME_FIELD}`) + ).toBeInTheDocument(); + expect( + screen.queryByTestId(`dataTableCellActionsPopover_${constants.CONTAINER_NAME_FIELD}`) + ).toBeInTheDocument(); + expect( + screen.queryByTestId(`dataTableCellActionsPopover_${constants.HOST_NAME_FIELD}`) + ).toBeInTheDocument(); + expect( + screen.queryByTestId( + `dataTableCellActionsPopover_${constants.ORCHESTRATOR_NAMESPACE_FIELD}` + ) + ).not.toBeInTheDocument(); + expect( + screen.queryByTestId(`dataTableCellActionsPopover_${constants.CLOUD_INSTANCE_ID_FIELD}`) + ).not.toBeInTheDocument(); + }); + + it('should render a badge indicating the count of additional resources', () => { + const record = getBaseRecord(); + renderSummary(record); + expect(screen.queryByText('+2')).toBeInTheDocument(); + expect(screen.queryByText('+3')).not.toBeInTheDocument(); + }); + + it('should render all the available resources in row autofit/custom mode', () => { + const record = getBaseRecord(); + renderSummary(record, { + density: DataGridDensity.COMPACT, + rowHeight: ROWS_HEIGHT_OPTIONS.auto, + }); + expect( + screen.queryByTestId(`dataTableCellActionsPopover_${constants.SERVICE_NAME_FIELD}`) + ).toBeInTheDocument(); + expect( + screen.queryByTestId(`dataTableCellActionsPopover_${constants.CONTAINER_NAME_FIELD}`) + ).toBeInTheDocument(); + expect( + screen.queryByTestId(`dataTableCellActionsPopover_${constants.HOST_NAME_FIELD}`) + ).toBeInTheDocument(); + expect( + screen.queryByTestId( + `dataTableCellActionsPopover_${constants.ORCHESTRATOR_NAMESPACE_FIELD}` + ) + ).toBeInTheDocument(); + expect( + screen.queryByTestId(`dataTableCellActionsPopover_${constants.CLOUD_INSTANCE_ID_FIELD}`) + ).toBeInTheDocument(); + expect(screen.queryByText('+2')).not.toBeInTheDocument(); + }); + + it('should display a popover with details and actions upon a badge click', () => { + const record = getBaseRecord(); + renderSummary(record); + // Open badge popover + screen.getByTestId(`dataTableCellActionsPopover_${constants.SERVICE_NAME_FIELD}`).click(); + + expect(screen.getByTestId('dataTableCellActionPopoverTitle')).toHaveTextContent( + 'service.name synth-service-2' + ); + expect( + screen.getByTestId(`dataTableCellAction_addToFilterAction_${constants.SERVICE_NAME_FIELD}`) + ).toBeInTheDocument(); + expect( + screen.getByTestId( + `dataTableCellAction_removeFromFilterAction_${constants.SERVICE_NAME_FIELD}` + ) + ).toBeInTheDocument(); + expect( + screen.getByTestId( + `dataTableCellAction_copyToClipboardAction_${constants.SERVICE_NAME_FIELD}` + ) + ).toBeInTheDocument(); + }); + }); + + describe('when rendering the main content', () => { + it('should display the message field as first choice', () => { + const record = getBaseRecord(); + renderSummary(record); + expect(screen.queryByTestId('discoverDataTableMessageValue')).toHaveTextContent( + record.flattened.message as string + ); + }); + + it(`should fallback to ${constants.ERROR_MESSAGE_FIELD} and ${constants.EVENT_ORIGINAL_FIELD} fields if message does not exist`, () => { + const recordWithoutMessage = getBaseRecord({ message: undefined }); + renderSummary(recordWithoutMessage); + expect(screen.queryByTestId('discoverDataTableMessageValue')).toHaveTextContent( + recordWithoutMessage.flattened[constants.EVENT_ORIGINAL_FIELD] as string + ); + }); + }); +}); diff --git a/src/plugins/discover/public/components/data_types/logs/summary_column/summary_column.tsx b/src/plugins/discover/public/components/data_types/logs/summary_column/summary_column.tsx new file mode 100644 index 00000000000000..ac7e20b944a4a1 --- /dev/null +++ b/src/plugins/discover/public/components/data_types/logs/summary_column/summary_column.tsx @@ -0,0 +1,169 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { + ROWS_HEIGHT_OPTIONS, + type DataGridCellValueElementProps, + DataGridDensity, +} from '@kbn/unified-data-table'; +import React from 'react'; +import { EuiButtonIcon, EuiCodeBlock, EuiFlexGroup, EuiText, EuiTitle } from '@elastic/eui'; +import { + ShouldShowFieldInTableHandler, + getLogDocumentOverview, + getMessageFieldWithFallbacks, +} from '@kbn/discover-utils'; +import { JsonCodeEditor } from '@kbn/unified-doc-viewer-plugin/public'; +import { DocViewFilterFn } from '@kbn/unified-doc-viewer/types'; +import { Resource } from './resource'; +import { Content } from './content'; +import { + closeCellActionPopoverText, + contentLabel, + jsonLabel, + resourceLabel, +} from '../translations'; +import { createResourceFields, formatJsonDocumentForContent } from './utils'; + +export interface SummaryColumnFactoryDeps { + density: DataGridDensity | undefined; + rowHeight: number | undefined; + shouldShowFieldHandler: ShouldShowFieldInTableHandler; + onFilter?: DocViewFilterFn; +} + +export type SummaryColumnProps = DataGridCellValueElementProps; + +const SummaryColumn = (props: SummaryColumnProps & SummaryColumnFactoryDeps) => { + const { isDetails } = props; + + if (isDetails) { + return ; + } + + return ; +}; + +// eslint-disable-next-line import/no-default-export +export default SummaryColumn; + +const SummaryCell = ({ + density: maybeNullishDensity, + rowHeight: maybeNullishRowHeight, + ...props +}: SummaryColumnProps & SummaryColumnFactoryDeps) => { + const { onFilter, row } = props; + + const density = maybeNullishDensity ?? DataGridDensity.COMPACT; + const isCompressed = density === DataGridDensity.COMPACT; + + const rowHeight = maybeNullishRowHeight ?? ROWS_HEIGHT_OPTIONS.single; + const isSingleLine = rowHeight === ROWS_HEIGHT_OPTIONS.single || rowHeight === 1; + + const resourceFields = createResourceFields(row); + const shouldRenderResource = resourceFields.length > 0; + + return isSingleLine ? ( + + {shouldRenderResource && ( + + )} + + + ) : ( + <> + {shouldRenderResource && ( + + )} + + + ); +}; + +const SummaryCellPopover = (props: SummaryColumnProps & SummaryColumnFactoryDeps) => { + const { row, dataView, fieldFormats, onFilter, closePopover } = props; + + const resourceFields = createResourceFields(row); + const shouldRenderResource = resourceFields.length > 0; + + const documentOverview = getLogDocumentOverview(row, { dataView, fieldFormats }); + const { field, value } = getMessageFieldWithFallbacks(documentOverview); + const shouldRenderContent = Boolean(field && value); + + const shouldRenderSource = !shouldRenderContent; + + return ( + + + {shouldRenderResource && ( + + + {resourceLabel} + + + + )} + + + {contentLabel} + + {shouldRenderContent && ( + + + {field} + + + {value} + + + )} + {shouldRenderSource && ( + + + {jsonLabel} + + + + )} + + + ); +}; + +const singleLineResourceCss = { + flexGrow: 0, + lineHeight: 'normal', + marginTop: -1, +}; + +const multiLineResourceCss = { display: 'inline-flex' }; diff --git a/src/plugins/discover/public/components/data_types/logs/summary_column/utils.tsx b/src/plugins/discover/public/components/data_types/logs/summary_column/utils.tsx new file mode 100644 index 00000000000000..470ec8a0f86fa4 --- /dev/null +++ b/src/plugins/discover/public/components/data_types/logs/summary_column/utils.tsx @@ -0,0 +1,126 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { getFieldValue, LogDocument, ResourceFields } from '@kbn/discover-utils/src'; +import { DataTableRecord } from '@kbn/discover-utils'; +import { dynamic } from '@kbn/shared-ux-utility'; +import React from 'react'; +import { css } from '@emotion/react'; +import { AgentName } from '@kbn/elastic-agent-utils'; +import { euiThemeVars } from '@kbn/ui-theme'; +import { getAvailableResourceFields } from '../../../../utils/get_available_resource_fields'; +import * as constants from '../../../../../common/data_types/logs/constants'; +import { ServiceNameBadgeWithActions } from '../service_name_badge_with_actions'; +import { FieldBadgeWithActions, FieldBadgeWithActionsProps } from '../cell_actions_popover'; + +/** + * getUnformattedResourceFields definitions + */ +export const getUnformattedResourceFields = (doc: LogDocument): ResourceFields => { + const serviceName = getFieldValue(doc, constants.SERVICE_NAME_FIELD); + const hostName = getFieldValue(doc, constants.HOST_NAME_FIELD); + const agentName = getFieldValue(doc, constants.AGENT_NAME_FIELD); + const orchestratorClusterName = getFieldValue(doc, constants.ORCHESTRATOR_CLUSTER_NAME_FIELD); + const orchestratorResourceId = getFieldValue(doc, constants.ORCHESTRATOR_RESOURCE_ID_FIELD); + const orchestratorNamespace = getFieldValue(doc, constants.ORCHESTRATOR_NAMESPACE_FIELD); + const containerName = getFieldValue(doc, constants.CONTAINER_NAME_FIELD); + const containerId = getFieldValue(doc, constants.CONTAINER_ID_FIELD); + const cloudInstanceId = getFieldValue(doc, constants.CLOUD_INSTANCE_ID_FIELD); + + return { + [constants.SERVICE_NAME_FIELD]: serviceName, + [constants.HOST_NAME_FIELD]: hostName, + [constants.AGENT_NAME_FIELD]: agentName, + [constants.ORCHESTRATOR_CLUSTER_NAME_FIELD]: orchestratorClusterName, + [constants.ORCHESTRATOR_RESOURCE_ID_FIELD]: orchestratorResourceId, + [constants.ORCHESTRATOR_NAMESPACE_FIELD]: orchestratorNamespace, + [constants.CONTAINER_NAME_FIELD]: containerName, + [constants.CONTAINER_ID_FIELD]: containerId, + [constants.CLOUD_INSTANCE_ID_FIELD]: cloudInstanceId, + }; +}; + +/** + * createResourceFields definitions + */ +const AgentIcon = dynamic(() => import('@kbn/custom-icons/src/components/agent_icon')); + +const resourceCustomComponentsMap: Partial< + Record> +> = { + [constants.SERVICE_NAME_FIELD]: ServiceNameBadgeWithActions, +}; + +export interface ResourceFieldDescriptor { + ResourceBadge: React.ComponentType; + Icon?: () => JSX.Element; + name: keyof ResourceFields; + value: string; +} + +export const createResourceFields = (row: DataTableRecord): ResourceFieldDescriptor[] => { + const resourceDoc = getUnformattedResourceFields(row as LogDocument); + + const availableResourceFields = getAvailableResourceFields(resourceDoc); + + const resourceFields = availableResourceFields.map((name) => ({ + name, + value: resourceDoc[name] as string, + ResourceBadge: resourceCustomComponentsMap[name] ?? FieldBadgeWithActions, + ...(name === constants.SERVICE_NAME_FIELD && { + Icon: () => ( + + ), + }), + })); + + return resourceFields; +}; + +/** + * formatJsonDocumentForContent definitions + */ +export const formatJsonDocumentForContent = (row: DataTableRecord) => { + const flattenedResult: DataTableRecord['flattened'] = {}; + const rawFieldResult: DataTableRecord['raw']['fields'] = {}; + const { raw, flattened } = row; + const { fields } = raw; + + // We need 2 loops here for flattened and raw.fields. Flattened contains all fields, + // whereas raw.fields only contains certain fields excluding _ignored + for (const fieldName in flattened) { + if (isFieldAllowed(fieldName) && flattened[fieldName]) { + flattenedResult[fieldName] = flattened[fieldName]; + } + } + + for (const fieldName in fields) { + if (isFieldAllowed(fieldName) && fields[fieldName]) { + rawFieldResult[fieldName] = fields[fieldName]; + } + } + + return { + ...row, + flattened: flattenedResult, + raw: { + ...raw, + fields: rawFieldResult, + }, + }; +}; + +const isFieldAllowed = (field: string) => + !constants.FILTER_OUT_FIELDS_PREFIXES_FOR_CONTENT.some((prefix) => field.startsWith(prefix)); diff --git a/src/plugins/discover/public/components/data_types/logs/translations.tsx b/src/plugins/discover/public/components/data_types/logs/translations.tsx index a09675c66bcd79..bbc39022bd5031 100644 --- a/src/plugins/discover/public/components/data_types/logs/translations.tsx +++ b/src/plugins/discover/public/components/data_types/logs/translations.tsx @@ -16,6 +16,10 @@ export const flyoutContentLabel = i18n.translate('discover.logs.flyoutDetail.lab defaultMessage: 'Content breakdown', }); +export const jsonLabel = i18n.translate('discover.logs.dataTable.header.popover.json', { + defaultMessage: 'JSON', +}); + export const contentLabel = i18n.translate('discover.logs.dataTable.header.popover.content', { defaultMessage: 'Content', }); diff --git a/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/cell_renderer.tsx b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/cell_renderer.tsx deleted file mode 100644 index 87d07d073dde3a..00000000000000 --- a/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/cell_renderer.tsx +++ /dev/null @@ -1,45 +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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import React from 'react'; -import type { DataGridCellValueElementProps } from '@kbn/unified-data-table'; -import { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import { VirtualColumnServiceProvider } from '../../../../application/main/hooks/grid_customisations/use_virtual_column_services'; -import { CONTENT_FIELD, RESOURCE_FIELD } from '../../../../../common/data_types/logs/constants'; -import { Content } from './content'; -import { Resource } from './resource'; - -export const renderCell = - (type: string, { data }: { data: DataPublicPluginStart }) => - (props: DataGridCellValueElementProps) => { - const { dataView } = props; - const virtualColumnServices = { - data, - dataView, - }; - - let renderedCell = null; - - switch (type) { - case CONTENT_FIELD: - renderedCell = ; - break; - case RESOURCE_FIELD: - renderedCell = ; - break; - default: - break; - } - - return ( - - {renderedCell} - - ); - }; diff --git a/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column.tsx b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column.tsx deleted file mode 100644 index 61b77df7e0be17..00000000000000 --- a/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column.tsx +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import React from 'react'; -import { CustomGridColumnProps } from '@kbn/unified-data-table'; -import { CONTENT_FIELD, RESOURCE_FIELD } from '../../../../../common/data_types/logs/constants'; -import { ContentColumnTooltip } from './column_tooltips/content_column_tooltip'; -import { ResourceColumnTooltip } from './column_tooltips/resource_column_tooltip'; - -export const renderColumn = - (field: string) => - ({ column, headerRowHeight }: CustomGridColumnProps) => { - switch (field) { - case CONTENT_FIELD: - column.display = ; - break; - case RESOURCE_FIELD: - column.display = ( - - ); - break; - default: - break; - } - return column; - }; diff --git a/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/content_column_tooltip.tsx b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/content_column_tooltip.tsx deleted file mode 100644 index db2cbd8ed6fc43..00000000000000 --- a/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/content_column_tooltip.tsx +++ /dev/null @@ -1,47 +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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { EuiText, useEuiTheme } from '@elastic/eui'; -import React from 'react'; -import { CustomGridColumnProps } from '@kbn/unified-data-table'; -import { css } from '@emotion/react'; -import { - contentHeaderTooltipParagraph1, - contentHeaderTooltipParagraph2, - contentLabel, -} from '../../../../data_types/logs/translations'; -import * as constants from '../../../../../../common/data_types/logs/constants'; -import { TooltipButton } from './tooltip_button'; -import { FieldWithToken } from './field_with_token'; - -export const ContentColumnTooltip = ({ column, headerRowHeight }: CustomGridColumnProps) => { - const { euiTheme } = useEuiTheme(); - const spacingCSS = css` - margin-bottom: ${euiTheme.size.s}; - `; - - return ( - -
- -

{contentHeaderTooltipParagraph1}

-
- -

{contentHeaderTooltipParagraph2}

-
- - -
-
- ); -}; diff --git a/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/field_with_token.tsx b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/field_with_token.tsx deleted file mode 100644 index db334734670e64..00000000000000 --- a/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/field_with_token.tsx +++ /dev/null @@ -1,45 +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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { EuiFlexGroup, EuiFlexItem, EuiText, EuiToken } from '@elastic/eui'; -import React from 'react'; -import { css } from '@emotion/react'; -import { euiThemeVars } from '@kbn/ui-theme'; - -const spacingXsCss = css` - margin-bottom: ${euiThemeVars.euiSizeXS}; -`; - -export const FieldWithToken = ({ - field, - iconType = 'tokenKeyword', -}: { - field: string; - iconType?: string; -}) => { - return ( -
- - - - - - - {field} - - - -
- ); -}; diff --git a/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/hover_popover.tsx b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/hover_popover.tsx deleted file mode 100644 index aab0e704dfc419..00000000000000 --- a/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/hover_popover.tsx +++ /dev/null @@ -1,60 +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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import React, { useEffect, useRef, useState } from 'react'; -import { EuiPopover, EuiPopoverTitle } from '@elastic/eui'; - -export const HoverPopover = ({ - children, - button, - title, -}: { - children: React.ReactChild; - button: React.ReactElement; - title: string; -}) => { - const [isPopoverOpen, setIsPopoverOpen] = useState(false); - const leaveTimer = useRef(null); - - const clearTimer = () => { - if (leaveTimer.current) { - clearTimeout(leaveTimer.current); - } - }; - - const onMouseEnter = () => { - clearTimer(); - setIsPopoverOpen(true); - }; - - const onMouseLeave = () => { - leaveTimer.current = setTimeout(() => setIsPopoverOpen(false), 100); - }; - - useEffect(() => { - return () => { - clearTimer(); - }; - }, []); - - return ( -
- - {title} - {children} - -
- ); -}; diff --git a/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/resource_column_tooltip.tsx b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/resource_column_tooltip.tsx deleted file mode 100644 index bbe5992724e526..00000000000000 --- a/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/resource_column_tooltip.tsx +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import React from 'react'; -import { css } from '@emotion/react'; -import { EuiText } from '@elastic/eui'; -import type { CustomGridColumnProps } from '@kbn/unified-data-table'; -import { euiThemeVars } from '@kbn/ui-theme'; -import { - resourceHeaderTooltipParagraph, - resourceLabel, -} from '../../../../data_types/logs/translations'; -import * as constants from '../../../../../../common/data_types/logs/constants'; -import { TooltipButton } from './tooltip_button'; -import { FieldWithToken } from './field_with_token'; - -const spacingCSS = css` - margin-bottom: ${euiThemeVars.euiSizeS}; -`; - -export const ResourceColumnTooltip = ({ column, headerRowHeight }: CustomGridColumnProps) => { - return ( - -
- -

{resourceHeaderTooltipParagraph}

-
- {[ - constants.SERVICE_NAME_FIELD, - constants.CONTAINER_NAME_FIELD, - constants.ORCHESTRATOR_NAMESPACE_FIELD, - constants.HOST_NAME_FIELD, - constants.CLOUD_INSTANCE_ID_FIELD, - ].map((field) => ( - - ))} -
-
- ); -}; diff --git a/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/tooltip_button.tsx b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/tooltip_button.tsx deleted file mode 100644 index 716a345abb4395..00000000000000 --- a/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/tooltip_button.tsx +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import React, { useEffect, useRef, useState, useCallback, useMemo } from 'react'; -import { EuiIcon } from '@elastic/eui'; -import ColumnHeaderTruncateContainer from '@kbn/unified-data-table/src/components/column_header_truncate_container'; - -import { EuiPopover, EuiPopoverTitle } from '@elastic/eui'; - -export const TooltipButton = ({ - children, - popoverTitle, - displayText, - headerRowHeight, - iconType = 'questionInCircle', -}: { - children: React.ReactChild; - popoverTitle: string; - displayText?: string; - headerRowHeight?: number; - iconType?: string; -}) => { - const [isPopoverOpen, setIsPopoverOpen] = useState(false); - const leaveTimer = useRef(null); - - const clearTimer = useMemo( - () => () => { - if (leaveTimer.current) { - clearTimeout(leaveTimer.current); - } - }, - [] - ); - - const onMouseEnter = useCallback(() => { - clearTimer(); - setIsPopoverOpen(true); - }, [clearTimer]); - - const onMouseLeave = useCallback(() => { - leaveTimer.current = setTimeout(() => setIsPopoverOpen(false), 100); - }, []); - - useEffect(() => { - return () => { - clearTimer(); - }; - }, [clearTimer]); - - return ( - - {displayText}{' '} - - } - isOpen={isPopoverOpen} - anchorPosition="upCenter" - panelPaddingSize="s" - ownFocus={false} - > - {popoverTitle} - {children} - - - ); -}; diff --git a/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/content.tsx b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/content.tsx deleted file mode 100644 index 0cee9ee9cb267b..00000000000000 --- a/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/content.tsx +++ /dev/null @@ -1,165 +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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import React, { useMemo } from 'react'; -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 { - getLogDocumentOverview, - 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 * as constants from '../../../../../common/data_types/logs/constants'; -import { LogLevel } from '../../../data_types/logs/log_level'; - -const SourceDocument = dynamic( - () => import('@kbn/unified-data-table/src/components/source_document') -); - -const DiscoverSourcePopoverContent = dynamic( - () => import('@kbn/unified-data-table/src/components/source_popover_content') -); - -const sourceDocumentClassName = css` - display: inline !important; - margin-left: ${euiThemeVars.euiSizeXS}; -`; - -const LogMessage = ({ field, value }: { field?: string; value: string }) => { - const renderFieldPrefix = field && field !== constants.MESSAGE_FIELD; - return ( - - {renderFieldPrefix && {field}} - - {value} - - - ); -}; - -const SourcePopoverContent = ({ - row, - columnId, - closePopover, -}: { - row: DataTableRecord; - columnId: string; - closePopover: () => void; -}) => { - const closeButton = ( - - ); - return ( - - ); -}; - -export const Content = ({ - row, - dataView, - fieldFormats, - isDetails, - columnId, - closePopover, -}: DataGridCellValueElementProps) => { - const documentOverview = getLogDocumentOverview(row, { dataView, fieldFormats }); - const { field, value } = getMessageFieldWithFallbacks(documentOverview); - const renderLogMessage = field && value; - - const shouldShowFieldHandler = useMemo(() => { - const dataViewFields = dataView.fields.getAll().map((fld) => fld.name); - return getShouldShowFieldHandler(dataViewFields, dataView, true); - }, [dataView]); - - const formattedRow = useMemo(() => { - return formatJsonDocumentForContent(row); - }, [row]); - - if (isDetails && !renderLogMessage) { - return ( - - ); - } - - return ( - - {documentOverview[constants.LOG_LEVEL_FIELD] && ( - - )} - {renderLogMessage ? ( - - ) : ( - - )} - - ); -}; - -const formatJsonDocumentForContent = (row: DataTableRecord) => { - const flattenedResult: DataTableRecord['flattened'] = {}; - const rawFieldResult: DataTableRecord['raw']['fields'] = {}; - const { raw, flattened } = row; - const { fields } = raw; - - // We need 2 loops here for flattened and raw.fields. Flattened contains all fields, - // whereas raw.fields only contains certain fields excluding _ignored - for (const key in flattened) { - if ( - !constants.FILTER_OUT_FIELDS_PREFIXES_FOR_CONTENT.some((prefix) => key.startsWith(prefix)) - ) { - flattenedResult[key] = flattened[key]; - } - } - - for (const key in fields) { - if ( - !constants.FILTER_OUT_FIELDS_PREFIXES_FOR_CONTENT.some((prefix) => key.startsWith(prefix)) - ) { - rawFieldResult[key] = fields[key]; - } - } - - return { - ...row, - flattened: flattenedResult, - raw: { - ...raw, - fields: rawFieldResult, - }, - }; -}; diff --git a/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/resource.tsx b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/resource.tsx deleted file mode 100644 index e00a84228ed0fa..00000000000000 --- a/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/resource.tsx +++ /dev/null @@ -1,71 +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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import React from 'react'; -import type { DataGridCellValueElementProps } from '@kbn/unified-data-table'; -import { AgentName } from '@kbn/elastic-agent-utils'; -import { dynamic } from '@kbn/shared-ux-utility'; -import { LogDocument } from '@kbn/discover-utils/src'; -import * as constants from '../../../../../common/data_types/logs/constants'; -import { getUnformattedResourceFields } from './utils/resource'; -import { ChipWithPopover } from '../../../data_types/logs/popover_chip'; -import { ServiceNameChipWithPopover } from '../../../data_types/logs/service_name_chip_with_popover'; - -const AgentIcon = dynamic(() => import('@kbn/custom-icons/src/components/agent_icon')); - -export const Resource = ({ row }: DataGridCellValueElementProps) => { - const resourceDoc = getUnformattedResourceFields(row as LogDocument); - return ( -
- {(resourceDoc[constants.SERVICE_NAME_FIELD] as string) && ( - - ) - } - /> - )} - {resourceDoc[constants.CONTAINER_NAME_FIELD] && ( - - )} - {resourceDoc[constants.HOST_NAME_FIELD] && ( - - )} - {resourceDoc[constants.ORCHESTRATOR_NAMESPACE_FIELD] && ( - - )} - {resourceDoc[constants.CLOUD_INSTANCE_ID_FIELD] && ( - - )} -
- ); -}; diff --git a/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/utils/resource.ts b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/utils/resource.ts deleted file mode 100644 index f63c27e734af49..00000000000000 --- a/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/utils/resource.ts +++ /dev/null @@ -1,35 +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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { getFieldFromDoc, LogDocument, ResourceFields } from '@kbn/discover-utils/src'; -import * as constants from '../../../../../../common/data_types/logs/constants'; - -export const getUnformattedResourceFields = (doc: LogDocument): ResourceFields => { - const serviceName = getFieldFromDoc(doc, constants.SERVICE_NAME_FIELD); - const hostName = getFieldFromDoc(doc, constants.HOST_NAME_FIELD); - const agentName = getFieldFromDoc(doc, constants.AGENT_NAME_FIELD); - const orchestratorClusterName = getFieldFromDoc(doc, constants.ORCHESTRATOR_CLUSTER_NAME_FIELD); - const orchestratorResourceId = getFieldFromDoc(doc, constants.ORCHESTRATOR_RESOURCE_ID_FIELD); - const orchestratorNamespace = getFieldFromDoc(doc, constants.ORCHESTRATOR_NAMESPACE_FIELD); - const containerName = getFieldFromDoc(doc, constants.CONTAINER_NAME_FIELD); - const containerId = getFieldFromDoc(doc, constants.CONTAINER_ID_FIELD); - const cloudInstanceId = getFieldFromDoc(doc, constants.CLOUD_INSTANCE_ID_FIELD); - - return { - [constants.SERVICE_NAME_FIELD]: serviceName, - [constants.HOST_NAME_FIELD]: hostName, - [constants.AGENT_NAME_FIELD]: agentName, - [constants.ORCHESTRATOR_CLUSTER_NAME_FIELD]: orchestratorClusterName, - [constants.ORCHESTRATOR_RESOURCE_ID_FIELD]: orchestratorResourceId, - [constants.ORCHESTRATOR_NAMESPACE_FIELD]: orchestratorNamespace, - [constants.CONTAINER_NAME_FIELD]: containerName, - [constants.CONTAINER_ID_FIELD]: containerId, - [constants.CLOUD_INSTANCE_ID_FIELD]: cloudInstanceId, - }; -}; diff --git a/src/plugins/discover/public/context_awareness/__mocks__/index.tsx b/src/plugins/discover/public/context_awareness/__mocks__/index.tsx index c0e3d981668cf7..a15b7aa26a8a04 100644 --- a/src/plugins/discover/public/context_awareness/__mocks__/index.tsx +++ b/src/plugins/discover/public/context_awareness/__mocks__/index.tsx @@ -21,6 +21,7 @@ import { RootProfileService, SolutionType, } from '../profiles'; +import { ProfileProviderServices } from '../profile_providers/profile_provider_services'; import { ProfilesManager } from '../profiles_manager'; import { DiscoverEBTContextManager } from '../../services/discover_ebt_context_manager'; import { createLogsContextServiceMock } from '@kbn/discover-utils/src/__mocks__'; @@ -31,8 +32,8 @@ export const createContextAwarenessMocks = ({ const rootProfileProviderMock: RootProfileProvider = { profileId: 'root-profile', profile: { - getCellRenderers: jest.fn((prev) => () => ({ - ...prev(), + getCellRenderers: jest.fn((prev) => (params) => ({ + ...prev(params), rootProfile: () => <>root-profile, })), getAdditionalCellActions: jest.fn((prev) => () => [ @@ -59,8 +60,8 @@ export const createContextAwarenessMocks = ({ const dataSourceProfileProviderMock: DataSourceProfileProvider = { profileId: 'data-source-profile', profile: { - getCellRenderers: jest.fn((prev) => () => ({ - ...prev(), + getCellRenderers: jest.fn((prev) => (params) => ({ + ...prev(params), rootProfile: () => <>data-source-profile, })), getDefaultAppState: jest.fn(() => () => ({ @@ -179,5 +180,5 @@ export const createContextAwarenessMocks = ({ const createProfileProviderServicesMock = () => { return { logsContextService: createLogsContextServiceMock(), - }; + } as ProfileProviderServices; }; diff --git a/src/plugins/discover/public/context_awareness/composable_profile.test.ts b/src/plugins/discover/public/context_awareness/composable_profile.test.ts index 0e927090005965..34cf44449f20e0 100644 --- a/src/plugins/discover/public/context_awareness/composable_profile.test.ts +++ b/src/plugins/discover/public/context_awareness/composable_profile.test.ts @@ -7,14 +7,23 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import { DataGridDensity } from '@kbn/unified-data-table'; import { ComposableProfile, getMergedAccessor } from './composable_profile'; import { Profile } from './types'; +import { dataViewWithTimefieldMock } from '../__mocks__/data_view_with_timefield'; + +const getCellRenderersParams = { + actions: { addFilter: jest.fn() }, + dataView: dataViewWithTimefieldMock, + density: DataGridDensity.COMPACT, + rowHeight: 0, +}; describe('getMergedAccessor', () => { it('should return the base implementation if no profiles are provided', () => { const baseImpl: Profile['getCellRenderers'] = jest.fn(() => ({ base: jest.fn() })); const mergedAccessor = getMergedAccessor([], 'getCellRenderers', baseImpl); - const result = mergedAccessor(); + const result = mergedAccessor(getCellRenderersParams); expect(baseImpl).toHaveBeenCalled(); expect(result).toEqual({ base: expect.any(Function) }); }); @@ -22,19 +31,19 @@ describe('getMergedAccessor', () => { it('should merge the accessors in the correct order', () => { const baseImpl: Profile['getCellRenderers'] = jest.fn(() => ({ base: jest.fn() })); const profile1: ComposableProfile = { - getCellRenderers: jest.fn((prev) => () => ({ - ...prev(), + getCellRenderers: jest.fn((prev) => (params) => ({ + ...prev(params), profile1: jest.fn(), })), }; const profile2: ComposableProfile = { - getCellRenderers: jest.fn((prev) => () => ({ - ...prev(), + getCellRenderers: jest.fn((prev) => (params) => ({ + ...prev(params), profile2: jest.fn(), })), }; const mergedAccessor = getMergedAccessor([profile1, profile2], 'getCellRenderers', baseImpl); - const result = mergedAccessor(); + const result = mergedAccessor(getCellRenderersParams); expect(baseImpl).toHaveBeenCalled(); expect(profile1.getCellRenderers).toHaveBeenCalled(); expect(profile2.getCellRenderers).toHaveBeenCalled(); @@ -52,13 +61,13 @@ describe('getMergedAccessor', () => { getCellRenderers: jest.fn(() => () => ({ profile1: jest.fn() })), }; const profile2: ComposableProfile = { - getCellRenderers: jest.fn((prev) => () => ({ - ...prev(), + getCellRenderers: jest.fn((prev) => (params) => ({ + ...prev(params), profile2: jest.fn(), })), }; const mergedAccessor = getMergedAccessor([profile1, profile2], 'getCellRenderers', baseImpl); - const result = mergedAccessor(); + const result = mergedAccessor(getCellRenderersParams); expect(baseImpl).not.toHaveBeenCalled(); expect(profile1.getCellRenderers).toHaveBeenCalled(); expect(profile2.getCellRenderers).toHaveBeenCalled(); diff --git a/src/plugins/discover/public/context_awareness/hooks/use_profile_accessor.test.ts b/src/plugins/discover/public/context_awareness/hooks/use_profile_accessor.test.ts index 83d2381d79a5f8..3fe3c0387149e3 100644 --- a/src/plugins/discover/public/context_awareness/hooks/use_profile_accessor.test.ts +++ b/src/plugins/discover/public/context_awareness/hooks/use_profile_accessor.test.ts @@ -13,6 +13,7 @@ import { useProfileAccessor } from './use_profile_accessor'; import { getDataTableRecords } from '../../__fixtures__/real_hits'; import { dataViewWithTimefieldMock } from '../../__mocks__/data_view_with_timefield'; import { useProfiles } from './use_profiles'; +import { DataGridDensity } from '@kbn/unified-data-table'; let mockProfiles: ComposableProfile[] = []; @@ -30,12 +31,19 @@ jest.mock('../composable_profile', () => { const record = getDataTableRecords(dataViewWithTimefieldMock)[0]; +const getCellRenderersParams = { + actions: { addFilter: jest.fn() }, + dataView: dataViewWithTimefieldMock, + density: DataGridDensity.COMPACT, + rowHeight: 0, +}; + describe('useProfileAccessor', () => { beforeEach(() => { jest.clearAllMocks(); mockProfiles = [ - { getCellRenderers: (prev) => () => ({ ...prev(), profile1: jest.fn() }) }, - { getCellRenderers: (prev) => () => ({ ...prev(), profile2: jest.fn() }) }, + { getCellRenderers: (prev) => (params) => ({ ...prev(params), profile1: jest.fn() }) }, + { getCellRenderers: (prev) => (params) => ({ ...prev(params), profile2: jest.fn() }) }, ]; }); @@ -47,7 +55,7 @@ describe('useProfileAccessor', () => { const accessor = result.current(base); expect(getMergedAccessor).toHaveBeenCalledTimes(1); expect(getMergedAccessor).toHaveBeenCalledWith(mockProfiles, 'getCellRenderers', base); - const renderers = accessor(); + const renderers = accessor(getCellRenderersParams); expect(renderers).toEqual({ base: expect.any(Function), profile1: expect.any(Function), @@ -72,7 +80,9 @@ describe('useProfileAccessor', () => { useProfileAccessor('getCellRenderers', { record }) ); const prevResult = result.current; - mockProfiles = [{ getCellRenderers: (prev) => () => ({ ...prev(), profile3: jest.fn() }) }]; + mockProfiles = [ + { getCellRenderers: (prev) => (params) => ({ ...prev(params), profile3: jest.fn() }) }, + ]; rerender(); expect(result.current).not.toBe(prevResult); }); diff --git a/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/accessors/get_cell_renderers.tsx b/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/accessors/get_cell_renderers.tsx index 69a2009f0a4af6..9e458920701208 100644 --- a/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/accessors/get_cell_renderers.tsx +++ b/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/accessors/get_cell_renderers.tsx @@ -7,17 +7,19 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import { SOURCE_COLUMN } from '@kbn/unified-data-table'; +import { getSummaryColumn } from '../../../../../components/data_types/logs/summary_column'; import { LOG_LEVEL_FIELDS, SERVICE_NAME_FIELDS, } from '../../../../../../common/data_types/logs/constants'; import { getLogLevelBadgeCell } from '../../../../../components/data_types/logs/log_level_badge_cell'; import { getServiceNameCell } from '../../../../../components/data_types/logs/service_name_cell'; -import { DataSourceProfileProvider } from '../../../../profiles'; +import type { DataSourceProfileProvider } from '../../../../profiles'; export const getCellRenderers: DataSourceProfileProvider['profile']['getCellRenderers'] = - (prev) => () => ({ - ...prev(), + (prev) => (params) => ({ + ...prev(params), ...LOG_LEVEL_FIELDS.reduce( (acc, field) => ({ ...acc, @@ -34,4 +36,5 @@ export const getCellRenderers: DataSourceProfileProvider['profile']['getCellRend }), {} ), + [SOURCE_COLUMN]: getSummaryColumn(params), }); diff --git a/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/profile.test.ts b/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/profile.test.ts index 0240736000c66e..861ef0b5902248 100644 --- a/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/profile.test.ts +++ b/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/profile.test.ts @@ -14,6 +14,8 @@ import { createDataViewDataSource, createEsqlDataSource } from '../../../../../c import { DataSourceCategory, RootContext, SolutionType } from '../../../profiles'; import { createContextAwarenessMocks } from '../../../__mocks__'; import { createLogsDataSourceProfileProvider } from './profile'; +import { DataGridDensity } from '@kbn/unified-data-table'; +import { dataViewWithTimefieldMock } from '../../../../__mocks__/data_view_with_timefield'; const mockServices = createContextAwarenessMocks().profileProviderServices; @@ -151,7 +153,13 @@ describe('logsDataSourceProfileProvider', () => { describe('getCellRenderers', () => { it('should return cell renderers for log level fields', () => { const getCellRenderers = logsDataSourceProfileProvider.profile.getCellRenderers?.(() => ({})); - const cellRenderers = getCellRenderers?.(); + const getCellRenderersParams = { + actions: { addFilter: jest.fn() }, + dataView: dataViewWithTimefieldMock, + density: DataGridDensity.COMPACT, + rowHeight: 0, + }; + const cellRenderers = getCellRenderers?.(getCellRenderersParams); expect(cellRenderers).toBeDefined(); expect(cellRenderers?.['log.level']).toBeDefined(); diff --git a/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/profile.ts b/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/profile.ts index 7b9d5ef3e5961f..f2818c336bf40e 100644 --- a/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/profile.ts +++ b/src/plugins/discover/public/context_awareness/profile_providers/common/logs_data_source_profile/profile.ts @@ -21,8 +21,8 @@ export const createLogsDataSourceProfileProvider = ( ): DataSourceProfileProvider => ({ profileId: 'logs-data-source-profile', profile: { - getRowIndicatorProvider, getCellRenderers, + getRowIndicatorProvider, getRowAdditionalLeadingControls, }, resolve: (params) => { diff --git a/src/plugins/discover/public/context_awareness/profile_providers/example/example_data_source_profile/profile.tsx b/src/plugins/discover/public/context_awareness/profile_providers/example/example_data_source_profile/profile.tsx index ae1d29170c8524..c82cf1a893c8db 100644 --- a/src/plugins/discover/public/context_awareness/profile_providers/example/example_data_source_profile/profile.tsx +++ b/src/plugins/discover/public/context_awareness/profile_providers/example/example_data_source_profile/profile.tsx @@ -21,10 +21,10 @@ export const createExampleDataSourceProfileProvider = (): DataSourceProfileProvi profileId: 'example-data-source-profile', isExperimental: true, profile: { - getCellRenderers: (prev) => () => ({ - ...prev(), + getCellRenderers: (prev) => (params) => ({ + ...prev(params), 'log.level': (props) => { - const level = getFieldValue(props.row, 'log.level'); + const level = getFieldValue(props.row, 'log.level') as string; if (!level) { return ( diff --git a/src/plugins/discover/public/context_awareness/profile_providers/example/example_root_pofile/profile.tsx b/src/plugins/discover/public/context_awareness/profile_providers/example/example_root_pofile/profile.tsx index ad247eacee666f..125cb609fb8494 100644 --- a/src/plugins/discover/public/context_awareness/profile_providers/example/example_root_pofile/profile.tsx +++ b/src/plugins/discover/public/context_awareness/profile_providers/example/example_root_pofile/profile.tsx @@ -16,10 +16,10 @@ export const createExampleRootProfileProvider = (): RootProfileProvider => ({ profileId: 'example-root-profile', isExperimental: true, profile: { - getCellRenderers: (prev) => () => ({ - ...prev(), + getCellRenderers: (prev) => (params) => ({ + ...prev(params), '@timestamp': (props) => { - const timestamp = getFieldValue(props.row, '@timestamp'); + const timestamp = getFieldValue(props.row, '@timestamp') as string; return ( diff --git a/src/plugins/discover/public/context_awareness/profile_providers/profile_provider_services.ts b/src/plugins/discover/public/context_awareness/profile_providers/profile_provider_services.ts index a757f243081734..daa75586384aae 100644 --- a/src/plugins/discover/public/context_awareness/profile_providers/profile_provider_services.ts +++ b/src/plugins/discover/public/context_awareness/profile_providers/profile_provider_services.ts @@ -9,18 +9,19 @@ import { createLogsContextService, LogsContextService } from '@kbn/discover-utils'; import type { LogsDataAccessPluginStart } from '@kbn/logs-data-access-plugin/public'; +import type { DiscoverServices } from '../../build_services'; /** * Dependencies required by profile provider implementations */ -export interface ProfileProviderDeps { - logsDataAccessPlugin?: LogsDataAccessPluginStart; +export interface ProfileProviderDeps extends DiscoverServices { + logsDataAccess?: LogsDataAccessPluginStart; } /** * Services provided to profile provider implementations */ -export interface ProfileProviderServices { +export interface ProfileProviderServices extends DiscoverServices { /** * A service containing methods used for logs profiles */ @@ -33,11 +34,12 @@ export interface ProfileProviderServices { * @returns Profile provider services */ export const createProfileProviderServices = async ( - deps: ProfileProviderDeps = {} + discoverServices: ProfileProviderDeps ): Promise => { return { + ...discoverServices, logsContextService: await createLogsContextService({ - logsDataAccessPlugin: deps.logsDataAccessPlugin, + logsDataAccess: discoverServices.logsDataAccess, }), }; }; diff --git a/src/plugins/discover/public/context_awareness/profile_providers/register_profile_providers.test.ts b/src/plugins/discover/public/context_awareness/profile_providers/register_profile_providers.test.ts index 1269441df2f211..940eb6b67e591e 100644 --- a/src/plugins/discover/public/context_awareness/profile_providers/register_profile_providers.test.ts +++ b/src/plugins/discover/public/context_awareness/profile_providers/register_profile_providers.test.ts @@ -8,7 +8,6 @@ */ import { createEsqlDataSource } from '../../../common/data_sources'; -import { DiscoverStartPlugins } from '../../types'; import { createContextAwarenessMocks } from '../__mocks__'; import { createExampleRootProfileProvider } from './example/example_root_pofile'; import { createExampleDataSourceProfileProvider } from './example/example_data_source_profile/profile'; @@ -70,12 +69,15 @@ describe('registerEnabledProfileProviders', () => { describe('registerProfileProviders', () => { it('should register enabled experimental profile providers', async () => { - const { rootProfileServiceMock, dataSourceProfileServiceMock, documentProfileServiceMock } = - createContextAwarenessMocks({ - shouldRegisterProviders: false, - }); + const { + rootProfileServiceMock, + dataSourceProfileServiceMock, + documentProfileServiceMock, + profileProviderServices, + } = createContextAwarenessMocks({ + shouldRegisterProviders: false, + }); await registerProfileProviders({ - plugins: {} as DiscoverStartPlugins, rootProfileService: rootProfileServiceMock, dataSourceProfileService: dataSourceProfileServiceMock, documentProfileService: documentProfileServiceMock, @@ -84,6 +86,7 @@ describe('registerProfileProviders', () => { exampleDataSourceProfileProvider.profileId, exampleDocumentProfileProvider.profileId, ], + services: profileProviderServices, }); const rootContext = await rootProfileServiceMock.resolve({ solutionNavId: null }); const dataSourceContext = await dataSourceProfileServiceMock.resolve({ @@ -106,16 +109,20 @@ describe('registerProfileProviders', () => { }); it('should not register disabled experimental profile providers', async () => { - const { rootProfileServiceMock, dataSourceProfileServiceMock, documentProfileServiceMock } = - createContextAwarenessMocks({ - shouldRegisterProviders: false, - }); + const { + rootProfileServiceMock, + dataSourceProfileServiceMock, + documentProfileServiceMock, + profileProviderServices, + } = createContextAwarenessMocks({ + shouldRegisterProviders: false, + }); await registerProfileProviders({ - plugins: {} as DiscoverStartPlugins, rootProfileService: rootProfileServiceMock, dataSourceProfileService: dataSourceProfileServiceMock, documentProfileService: documentProfileServiceMock, enabledExperimentalProfileIds: [], + services: profileProviderServices, }); const rootContext = await rootProfileServiceMock.resolve({ solutionNavId: null }); const dataSourceContext = await dataSourceProfileServiceMock.resolve({ diff --git a/src/plugins/discover/public/context_awareness/profile_providers/register_profile_providers.ts b/src/plugins/discover/public/context_awareness/profile_providers/register_profile_providers.ts index 3bd7ee9926f2dc..58ff63ca35c196 100644 --- a/src/plugins/discover/public/context_awareness/profile_providers/register_profile_providers.ts +++ b/src/plugins/discover/public/context_awareness/profile_providers/register_profile_providers.ts @@ -23,20 +23,19 @@ import { createProfileProviderServices, ProfileProviderServices, } from './profile_provider_services'; -import type { DiscoverStartPlugins } from '../../types'; +import type { DiscoverServices } from '../../build_services'; /** * Register profile providers for root, data source, and document contexts to the profile profile services * @param options Register profile provider options */ export const registerProfileProviders = async ({ - plugins, rootProfileService, dataSourceProfileService, documentProfileService, enabledExperimentalProfileIds, + services, }: { - plugins: DiscoverStartPlugins; /** * Root profile service */ @@ -53,10 +52,9 @@ export const registerProfileProviders = async ({ * Array of experimental profile IDs which are enabled in `kibana.yml` */ enabledExperimentalProfileIds: string[]; + services: DiscoverServices; }) => { - const providerServices = await createProfileProviderServices({ - logsDataAccessPlugin: plugins.logsDataAccess, - }); + const providerServices = await createProfileProviderServices(services); const rootProfileProviders = createRootProfileProviders(providerServices); const dataSourceProfileProviders = createDataSourceProfileProviders(providerServices); const documentProfileProviders = createDocumentProfileProviders(providerServices); diff --git a/src/plugins/discover/public/context_awareness/profile_providers/security/security_root_profile/profile.tsx b/src/plugins/discover/public/context_awareness/profile_providers/security/security_root_profile/profile.tsx index aa74318f645e3b..238d29302a910e 100644 --- a/src/plugins/discover/public/context_awareness/profile_providers/security/security_root_profile/profile.tsx +++ b/src/plugins/discover/public/context_awareness/profile_providers/security/security_root_profile/profile.tsx @@ -19,8 +19,8 @@ export const createSecurityRootProfileProvider: SecurityProfileProviderFactory< profileId: 'security-root-profile', isExperimental: true, profile: { - getCellRenderers: (prev) => () => ({ - ...prev(), + getCellRenderers: (prev) => (params) => ({ + ...prev(params), 'host.name': (props) => { const CellRenderer = getDiscoverCellRenderer({ fieldName: 'host.name', diff --git a/src/plugins/discover/public/context_awareness/types.ts b/src/plugins/discover/public/context_awareness/types.ts index dacc2edbc218de..63c23bbb3d4b1a 100644 --- a/src/plugins/discover/public/context_awareness/types.ts +++ b/src/plugins/discover/public/context_awareness/types.ts @@ -8,7 +8,11 @@ */ import type { DataView } from '@kbn/data-views-plugin/common'; -import type { CustomCellRenderer, UnifiedDataTableProps } from '@kbn/unified-data-table'; +import type { + CustomCellRenderer, + DataGridDensity, + UnifiedDataTableProps, +} from '@kbn/unified-data-table'; import type { DocViewsRegistry } from '@kbn/unified-doc-viewer'; import type { DataTableRecord } from '@kbn/discover-utils'; import type { CellAction, CellActionExecutionContext, CellActionsData } from '@kbn/cell-actions'; @@ -16,6 +20,7 @@ import type { EuiIconType } from '@elastic/eui/src/components/icon/icon'; import type { AggregateQuery, Filter, Query, TimeRange } from '@kbn/es-query'; import type { OmitIndexSignature } from 'type-fest'; import type { Trigger } from '@kbn/ui-actions-plugin/public'; +import { DocViewFilterFn } from '@kbn/unified-doc-viewer/types'; import type { DiscoverDataSource } from '../../common/data_sources'; import type { DiscoverAppState } from '../application/main/state_management/discover_app_state_container'; @@ -96,6 +101,30 @@ export interface DefaultAppStateExtension { rowHeight?: number; } +/** + * Parameters passed to the cell renderers extension + */ +export interface CellRenderersExtensionParams { + /** + * Available actions for cell renderers + */ + actions: { + addFilter?: DocViewFilterFn; + }; + /** + * The current data view + */ + dataView: DataView; + /** + * The current density applied to the data grid component + */ + density: DataGridDensity | undefined; + /** + * The current row height mode applied to the data grid component + */ + rowHeight: number | undefined; +} + /** * Parameters passed to the row controls extension */ @@ -216,7 +245,7 @@ export interface Profile { * Gets a map of column names to custom cell renderers to use in the data grid * @returns The custom cell renderers to use in the data grid */ - getCellRenderers: () => CustomCellRenderer; + getCellRenderers: (params: CellRenderersExtensionParams) => CustomCellRenderer; /** * Gets a row indicator provider, allowing rows in the data grid to be given coloured highlights diff --git a/src/plugins/discover/public/embeddable/components/saved_search_grid.tsx b/src/plugins/discover/public/embeddable/components/saved_search_grid.tsx index aa484098fb8a34..23e06062d166b6 100644 --- a/src/plugins/discover/public/embeddable/components/saved_search_grid.tsx +++ b/src/plugins/discover/public/embeddable/components/saved_search_grid.tsx @@ -17,6 +17,8 @@ import { type DataTableColumnsMeta, DataLoadingState as DiscoverGridLoadingState, getRenderCustomToolbarWithElements, + getDataGridDensity, + getRowHeight, } from '@kbn/unified-data-table'; import { DiscoverGrid } from '../../components/discover_grid'; import './saved_search_grid.scss'; @@ -25,8 +27,7 @@ import { SavedSearchEmbeddableBase } from './saved_search_embeddable_base'; import { TotalDocuments } from '../../application/main/components/total_documents/total_documents'; import { useProfileAccessor } from '../../context_awareness'; -export interface DiscoverGridEmbeddableProps - extends Omit { +interface DiscoverGridEmbeddableProps extends Omit { sampleSizeState: number; // a required prop totalHitCount?: number; query?: AggregateQuery | Query; @@ -36,15 +37,11 @@ export interface DiscoverGridEmbeddableProps savedSearchId?: string; } -export type DiscoverGridEmbeddableSearchProps = Omit< - DiscoverGridEmbeddableProps, - 'sampleSizeState' | 'loadingState' | 'query' ->; - export const DiscoverGridMemoized = React.memo(DiscoverGrid); export function DiscoverGridEmbeddable(props: DiscoverGridEmbeddableProps) { const { interceptedWarnings, ...gridProps } = props; + const [expandedDoc, setExpandedDoc] = useState(undefined); const renderDocumentView = useCallback( @@ -94,8 +91,27 @@ export function DiscoverGridEmbeddable(props: DiscoverGridEmbeddableProps) { const getCellRenderersAccessor = useProfileAccessor('getCellRenderers'); const cellRenderers = useMemo(() => { const getCellRenderers = getCellRenderersAccessor(() => ({})); - return getCellRenderers(); - }, [getCellRenderersAccessor]); + return getCellRenderers({ + actions: { addFilter: props.onFilter }, + dataView: props.dataView, + density: + gridProps.dataGridDensityState ?? getDataGridDensity(props.services.storage, 'discover'), + rowHeight: getRowHeight({ + storage: props.services.storage, + consumer: 'discover', + rowHeightState: gridProps.rowHeightState, + configRowHeight: props.configRowHeight, + }), + }); + }, [ + getCellRenderersAccessor, + props.onFilter, + props.dataView, + props.services.storage, + props.configRowHeight, + gridProps.dataGridDensityState, + gridProps.rowHeightState, + ]); return ( { - const { registerProfileProviders } = await import('./context_awareness/profile_providers'); + private createProfileServices() { const rootProfileService = new RootProfileService(); const dataSourceProfileService = new DataSourceProfileService(); const documentProfileService = new DocumentProfileService(); + + return { rootProfileService, dataSourceProfileService, documentProfileService }; + } + + private createProfilesManager = async ( + core: CoreStart, + plugins: DiscoverStartPlugins, + ebtContextManager: DiscoverEBTContextManager + ) => { + const { registerProfileProviders } = await import('./context_awareness/profile_providers'); + const { rootProfileService, dataSourceProfileService, documentProfileService } = + this.createProfileServices(); + const enabledExperimentalProfileIds = this.experimentalFeatures.enabledProfiles ?? []; + const profilesManager = new ProfilesManager( + rootProfileService, + dataSourceProfileService, + documentProfileService, + ebtContextManager + ); + await registerProfileProviders({ - plugins, rootProfileService, dataSourceProfileService, documentProfileService, enabledExperimentalProfileIds, + services: this.getDiscoverServices(core, plugins, profilesManager, ebtContextManager), }); - return { rootProfileService, dataSourceProfileService, documentProfileService }; - }); - - private async createProfilesManager({ - plugins, - ebtContextManager, - }: { - plugins: DiscoverStartPlugins; - ebtContextManager: DiscoverEBTContextManager; - }) { + return profilesManager; + }; + + private createEmptyProfilesManager() { const { rootProfileService, dataSourceProfileService, documentProfileService } = - await this.createProfileServices({ plugins }); + this.createProfileServices(); return new ProfilesManager( rootProfileService, dataSourceProfileService, documentProfileService, - ebtContextManager - ); - } - - private createEmptyProfilesManager() { - return new ProfilesManager( - new RootProfileService(), - new DataSourceProfileService(), - new DocumentProfileService(), new DiscoverEBTContextManager() // it's not enabled outside of Discover ); } @@ -395,10 +399,7 @@ export class DiscoverPlugin const getDiscoverServicesInternal = async () => { const [coreStart, deps] = await core.getStartServices(); const ebtContextManager = new DiscoverEBTContextManager(); // it's not enabled outside of Discover - const profilesManager = await this.createProfilesManager({ - plugins: deps, - ebtContextManager, - }); + const profilesManager = await this.createProfilesManager(coreStart, deps, ebtContextManager); return this.getDiscoverServices(coreStart, deps, profilesManager, ebtContextManager); }; diff --git a/src/plugins/discover/public/types.ts b/src/plugins/discover/public/types.ts index 916833bdce1d54..3b24341e1a654d 100644 --- a/src/plugins/discover/public/types.ts +++ b/src/plugins/discover/public/types.ts @@ -154,8 +154,10 @@ export interface DiscoverStartPlugins { embeddable: EmbeddableStart; expressions: ExpressionsStart; fieldFormats: FieldFormatsStart; + fieldsMetadata: FieldsMetadataPublicStart; inspector: InspectorPublicPluginStart; lens: LensPublicStart; + logsDataAccess?: LogsDataAccessPluginStart; navigation: NavigationStart; noDataPage?: NoDataPagePluginStart; observabilityAIAssistant?: ObservabilityAIAssistantPublicStart; @@ -170,6 +172,4 @@ export interface DiscoverStartPlugins { unifiedSearch: UnifiedSearchPublicPluginStart; urlForwarding: UrlForwardingStart; usageCollection?: UsageCollectionSetup; - fieldsMetadata: FieldsMetadataPublicStart; - logsDataAccess?: LogsDataAccessPluginStart; } diff --git a/src/plugins/discover/public/utils/get_available_resource_fields.ts b/src/plugins/discover/public/utils/get_available_resource_fields.ts new file mode 100644 index 00000000000000..588194d2a13cab --- /dev/null +++ b/src/plugins/discover/public/utils/get_available_resource_fields.ts @@ -0,0 +1,23 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { ResourceFields } from '@kbn/discover-utils/src'; +import * as constants from '../../common/data_types/logs/constants'; + +export const getAvailableResourceFields = (resourceDoc: ResourceFields) => { + const resourceFields: Array = [ + constants.SERVICE_NAME_FIELD, + constants.CONTAINER_NAME_FIELD, + constants.HOST_NAME_FIELD, + constants.ORCHESTRATOR_NAMESPACE_FIELD, + constants.CLOUD_INSTANCE_ID_FIELD, + ]; + + return resourceFields.filter((fieldName) => Boolean(resourceDoc[fieldName])); +}; diff --git a/src/plugins/discover/tsconfig.json b/src/plugins/discover/tsconfig.json index 65b121f47d105d..fb6ec66985647d 100644 --- a/src/plugins/discover/tsconfig.json +++ b/src/plugins/discover/tsconfig.json @@ -98,6 +98,7 @@ "@kbn/security-solution-common", "@kbn/router-utils", "@kbn/management-settings-ids", + "@kbn/react-hooks", "@kbn/logs-data-access-plugin", "@kbn/core-lifecycle-browser" ], 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 bf7ea88be5907e..d4259c0e6d8522 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 @@ -81,9 +81,14 @@ export function LogsOverviewHeader({ doc }: { doc: LogDocumentOverview }) { {logLevelAndTimestamp} - - {value} - + ); diff --git a/test/functional/apps/discover/context_awareness/extensions/_get_cell_renderers.ts b/test/functional/apps/discover/context_awareness/extensions/_get_cell_renderers.ts index a1e884a82cb4fc..cb66afc7ebc571 100644 --- a/test/functional/apps/discover/context_awareness/extensions/_get_cell_renderers.ts +++ b/test/functional/apps/discover/context_awareness/extensions/_get_cell_renderers.ts @@ -85,6 +85,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); }); + describe('Service Name Cell', () => { it('should render service.name cell', async () => { const state = kbnRison.encode({ @@ -133,6 +134,40 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); }); + + describe('Summary column', () => { + it('should render a summary of the log entry replacing the original document', async () => { + const state = kbnRison.encode({ + dataSource: { type: 'esql' }, + query: { + esql: 'from my-example-logs,logstash* | sort @timestamp desc | where `message` is not null', + }, + }); + await common.navigateToActualUrl('discover', `?_a=${state}`, { + ensureCurrentUrl: false, + }); + await header.waitUntilLoadingHasFinished(); + await discover.waitUntilSearchingHasFinished(); + + await testSubjects.existOrFail('discoverDataTableMessageValue'); + }); + + it('should NOT render the summary column if the source does not match logs', async () => { + const state = kbnRison.encode({ + dataSource: { type: 'esql' }, + query: { + esql: 'from my-example-*', + }, + }); + await common.navigateToActualUrl('discover', `?_a=${state}`, { + ensureCurrentUrl: false, + }); + await header.waitUntilLoadingHasFinished(); + await discover.waitUntilSearchingHasFinished(); + + await testSubjects.missingOrFail('discoverDataTableMessageValue'); + }); + }); }); describe('data view mode', () => { @@ -278,6 +313,34 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); }); + + describe('Summary column', () => { + it('should render a summary of the log entry replacing the original document', async () => { + await common.navigateToActualUrl('discover', undefined, { + ensureCurrentUrl: false, + }); + await header.waitUntilLoadingHasFinished(); + await discover.waitUntilSearchingHasFinished(); + await dataViews.switchToAndValidate('my-example-logs,logstash*'); + + await retry.try(async () => { + await testSubjects.existOrFail('discoverDataTableMessageValue'); + }); + }); + + it('should NOT render the summary column if the source does not match logs', async () => { + await common.navigateToActualUrl('discover', undefined, { + ensureCurrentUrl: false, + }); + await header.waitUntilLoadingHasFinished(); + await discover.waitUntilSearchingHasFinished(); + await dataViews.switchToAndValidate('my-example-*'); + + await retry.try(async () => { + await testSubjects.missingOrFail('discoverDataTableMessageValue'); + }); + }); + }); }); }); } diff --git a/test/functional/apps/discover/context_awareness/extensions/_get_default_app_state.ts b/test/functional/apps/discover/context_awareness/extensions/_get_default_app_state.ts index 6ed126bad8c7e8..7e7a12840f76de 100644 --- a/test/functional/apps/discover/context_awareness/extensions/_get_default_app_state.ts +++ b/test/functional/apps/discover/context_awareness/extensions/_get_default_app_state.ts @@ -69,7 +69,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await common.navigateToActualUrl('discover', `?_a=${state}`, { ensureCurrentUrl: false, }); - await expectColumns(['@timestamp', 'Document']); + await expectColumns(['@timestamp', 'Summary']); await dataGrid.clickGridSettings(); let rowHeightValue = await dataGrid.getCurrentRowHeightValue(); expect(rowHeightValue).to.be('Custom'); @@ -99,7 +99,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await discover.waitUntilSearchingHasFinished(); await unifiedFieldList.clickFieldListItemRemove('log.level'); await unifiedFieldList.clickFieldListItemRemove('message'); - await expectColumns(['@timestamp', 'Document']); + await expectColumns(['@timestamp', 'Summary']); await dataGrid.clickGridSettings(); await dataGrid.changeRowHeightValue('Single'); let rowHeightValue = await dataGrid.getCurrentRowHeightValue(); @@ -153,7 +153,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await header.waitUntilLoadingHasFinished(); await discover.waitUntilSearchingHasFinished(); await dataViews.switchToAndValidate('my-example-*'); - await expectColumns(['@timestamp', 'Document']); + await expectColumns(['@timestamp', 'Summary']); await dataGrid.clickGridSettings(); let rowHeightValue = await dataGrid.getCurrentRowHeightValue(); expect(rowHeightValue).to.be('Custom'); @@ -178,7 +178,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await discover.waitUntilSearchingHasFinished(); await unifiedFieldList.clickFieldListItemRemove('log.level'); await unifiedFieldList.clickFieldListItemRemove('message'); - await expectColumns(['@timestamp', 'Document']); + await expectColumns(['@timestamp', 'Summary']); await dataGrid.clickGridSettings(); await dataGrid.changeRowHeightValue('Single'); let rowHeightValue = await dataGrid.getCurrentRowHeightValue(); diff --git a/test/functional/apps/discover/esql/_esql_columns.ts b/test/functional/apps/discover/esql/_esql_columns.ts index 2eba53aaab8060..59c4b8b816664e 100644 --- a/test/functional/apps/discover/esql/_esql_columns.ts +++ b/test/functional/apps/discover/esql/_esql_columns.ts @@ -57,7 +57,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('should render initial columns for non-transformational commands correctly', async () => { - const columns = ['@timestamp', 'Document']; + const columns = ['@timestamp', 'Summary']; expect(await dataGrid.getHeaderFields()).to.eql(columns); await browser.refresh(); @@ -91,7 +91,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('should reset columns only if index pattern changes in non-transformational query', async () => { - const columns = ['@timestamp', 'Document']; + const columns = ['@timestamp', 'Summary']; expect(await dataGrid.getHeaderFields()).to.eql(columns); await monacoEditor.setCodeEditorValue('from logstash-* | limit 500'); @@ -219,7 +219,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await discover.loadSavedSearch(SAVED_SEARCH_NON_TRANSFORMATIONAL_INITIAL_COLUMNS); await header.waitUntilLoadingHasFinished(); await discover.waitUntilSearchingHasFinished(); - expect(await dataGrid.getHeaderFields()).to.eql(['@timestamp', 'Document']); + expect(await dataGrid.getHeaderFields()).to.eql(['@timestamp', 'Summary']); await discover.loadSavedSearch(SAVED_SEARCH_NON_TRANSFORMATIONAL_CUSTOM_COLUMNS); await header.waitUntilLoadingHasFinished(); @@ -239,7 +239,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await discover.clickNewSearchButton(); await header.waitUntilLoadingHasFinished(); await discover.waitUntilSearchingHasFinished(); - expect(await dataGrid.getHeaderFields()).to.eql(['@timestamp', 'Document']); + expect(await dataGrid.getHeaderFields()).to.eql(['@timestamp', 'Summary']); }); }); } diff --git a/test/functional/apps/discover/group2_data_grid1/_data_grid.ts b/test/functional/apps/discover/group2_data_grid1/_data_grid.ts index 90cf9067cd852e..0891482f306ed0 100644 --- a/test/functional/apps/discover/group2_data_grid1/_data_grid.ts +++ b/test/functional/apps/discover/group2_data_grid1/_data_grid.ts @@ -39,7 +39,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const getTitles = async () => (await testSubjects.getVisibleText('dataGridHeader')).replace(/\s|\r?\n|\r/g, ' '); - expect(await getTitles()).to.be('@timestamp Document'); + expect(await getTitles()).to.be('@timestamp Summary'); await unifiedFieldList.clickFieldListItemAdd('bytes'); expect(await getTitles()).to.be('@timestamp bytes'); @@ -51,7 +51,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await getTitles()).to.be('@timestamp agent'); await unifiedFieldList.clickFieldListItemRemove('agent'); - expect(await getTitles()).to.be('@timestamp Document'); + expect(await getTitles()).to.be('@timestamp Summary'); }); const isVisible = async (selector: string) => { diff --git a/test/functional/apps/discover/group2_data_grid1/_data_grid_copy_to_clipboard.ts b/test/functional/apps/discover/group2_data_grid1/_data_grid_copy_to_clipboard.ts index a58dcf94e3a4a7..e0378a9fbf6c47 100644 --- a/test/functional/apps/discover/group2_data_grid1/_data_grid_copy_to_clipboard.ts +++ b/test/functional/apps/discover/group2_data_grid1/_data_grid_copy_to_clipboard.ts @@ -65,7 +65,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { if (canReadClipboard) { const copiedSourceData = await browser.getClipboardValue(); - expect(copiedSourceData.startsWith('Document\n{"@message":["238.171.34.42')).to.be(true); + expect(copiedSourceData.startsWith('Summary\n{"@message":["238.171.34.42')).to.be(true); expect(copiedSourceData.endsWith('}')).to.be(true); } @@ -90,7 +90,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { if (canReadClipboard) { const copiedSourceName = await browser.getClipboardValue(); - expect(copiedSourceName).to.be('Document'); + expect(copiedSourceName).to.be('Summary'); } expect(await toasts.getCount()).to.be(1); diff --git a/test/functional/apps/discover/group2_data_grid2/_data_grid_field_data.ts b/test/functional/apps/discover/group2_data_grid2/_data_grid_field_data.ts index 9f826f9a75ee51..60fdb106f3bc05 100644 --- a/test/functional/apps/discover/group2_data_grid2/_data_grid_field_data.ts +++ b/test/functional/apps/discover/group2_data_grid2/_data_grid_field_data.ts @@ -67,7 +67,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('doc view should show @timestamp and _source columns', async function () { - const expectedHeader = '@timestamp Document'; + const expectedHeader = '@timestamp Summary'; const DocHeader = await dataGrid.getHeaderFields(); expect(DocHeader.join(' ')).to.be(expectedHeader); }); diff --git a/test/functional/apps/discover/group3/_drag_drop.ts b/test/functional/apps/discover/group3/_drag_drop.ts index b6ea2186c0ce26..07d23ab28c2ea2 100644 --- a/test/functional/apps/discover/group3/_drag_drop.ts +++ b/test/functional/apps/discover/group3/_drag_drop.ts @@ -51,7 +51,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await unifiedFieldList.getSidebarAriaDescription()).to.be( '48 available fields. 5 empty fields. 4 meta fields.' ); - expect((await discover.getColumnHeaders()).join(', ')).to.be('@timestamp, Document'); + expect((await discover.getColumnHeaders()).join(', ')).to.be('@timestamp, Summary'); await discover.dragFieldToTable('extension'); @@ -71,7 +71,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await unifiedFieldList.getSidebarAriaDescription()).to.be( '48 available fields. 5 empty fields. 4 meta fields.' ); - expect((await discover.getColumnHeaders()).join(', ')).to.be('@timestamp, Document'); + expect((await discover.getColumnHeaders()).join(', ')).to.be('@timestamp, Summary'); await discover.dragFieldWithKeyboardToTable('@message'); diff --git a/test/functional/apps/discover/group4/_discover_fields_api.ts b/test/functional/apps/discover/group4/_discover_fields_api.ts index dd10c4020a87d2..1706da0ab3c79c 100644 --- a/test/functional/apps/discover/group4/_discover_fields_api.ts +++ b/test/functional/apps/discover/group4/_discover_fields_api.ts @@ -46,7 +46,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should correctly display documents', async function () { log.debug('check if Document title exists in the grid'); - expect(await discover.getDocHeader()).to.have.string('Document'); + expect(await discover.getDocHeader()).to.have.string('Summary'); const rowData = await discover.getDocTableIndex(1); log.debug('check the newest doc timestamp in UTC (check diff timezone in last test)'); expect(rowData.startsWith('Sep 22, 2015 @ 23:50:13.253')).to.be.ok(); @@ -59,13 +59,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('adding a column removes a default column', async function () { await unifiedFieldList.clickFieldListItemAdd('_score'); expect(await discover.getDocHeader()).to.have.string('_score'); - expect(await discover.getDocHeader()).not.to.have.string('Document'); + expect(await discover.getDocHeader()).not.to.have.string('Summary'); }); it('removing a column adds a default column', async function () { await unifiedFieldList.clickFieldListItemRemove('_score'); expect(await discover.getDocHeader()).not.to.have.string('_score'); - expect(await discover.getDocHeader()).to.have.string('Document'); + expect(await discover.getDocHeader()).to.have.string('Summary'); }); it('displays _source viewer in doc viewer', async function () { @@ -83,7 +83,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await common.navigateToApp('discover'); await timePicker.setDefaultAbsoluteRange(); - expect(await discover.getDocHeader()).to.have.string('Document'); + expect(await discover.getDocHeader()).to.have.string('Summary'); }); it('switches to Document column when fields API is used', async function () { @@ -94,7 +94,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await common.navigateToApp('discover'); await timePicker.setDefaultAbsoluteRange(); - expect(await discover.getDocHeader()).to.have.string('Document'); + expect(await discover.getDocHeader()).to.have.string('Summary'); }); }); } diff --git a/test/functional/apps/discover/group5/_field_data_with_fields_api.ts b/test/functional/apps/discover/group5/_field_data_with_fields_api.ts index 4b301c3d5e9460..4b9a225eb471f1 100644 --- a/test/functional/apps/discover/group5/_field_data_with_fields_api.ts +++ b/test/functional/apps/discover/group5/_field_data_with_fields_api.ts @@ -68,10 +68,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); - it('doc view should show @timestamp and Document columns', async function () { + it('doc view should show @timestamp and Summary columns', async function () { const Docheader = await discover.getDocHeader(); expect(Docheader).to.contain('@timestamp'); - expect(Docheader).to.contain('Document'); + expect(Docheader).to.contain('Summary'); }); it('a bad syntax query should show an error message', async function () { diff --git a/test/functional/apps/discover/group5/_source_filters.ts b/test/functional/apps/discover/group5/_source_filters.ts index 9c66cff8f0df3b..c74aa38628e7f6 100644 --- a/test/functional/apps/discover/group5/_source_filters.ts +++ b/test/functional/apps/discover/group5/_source_filters.ts @@ -46,7 +46,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await discover.waitUntilSearchingHasFinished(); await retry.try(async function () { - expect(await discover.getDocHeader()).to.have.string('Document'); + expect(await discover.getDocHeader()).to.have.string('Summary'); }); }); diff --git a/test/functional/apps/discover/group6/_sidebar.ts b/test/functional/apps/discover/group6/_sidebar.ts index 51066cd3c04e04..a88623bb58d120 100644 --- a/test/functional/apps/discover/group6/_sidebar.ts +++ b/test/functional/apps/discover/group6/_sidebar.ts @@ -806,7 +806,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { let selectedFields = await unifiedFieldList.getSidebarSectionFieldNames('selected'); expect(selectedFields.includes(newField)).to.be(false); - expect(await dataGrid.getHeaderFields()).to.eql(['@timestamp', 'Document']); + expect(await dataGrid.getHeaderFields()).to.eql(['@timestamp', 'Summary']); await unifiedFieldList.clickFieldListItemAdd(newField); await header.waitUntilLoadingHasFinished(); @@ -824,7 +824,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { return !(await unifiedFieldList.getAllFieldNames()).includes(newField); }); - expect(await dataGrid.getHeaderFields()).to.eql(['@timestamp', 'Document']); + expect(await dataGrid.getHeaderFields()).to.eql(['@timestamp', 'Summary']); }); }); }); diff --git a/test/functional/apps/discover/group6/_time_field_column.ts b/test/functional/apps/discover/group6/_time_field_column.ts index 7e058a71eac0b7..93b72ddcf36760 100644 --- a/test/functional/apps/discover/group6/_time_field_column.ts +++ b/test/functional/apps/discover/group6/_time_field_column.ts @@ -67,7 +67,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }) { // check in Discover expect(await dataGrid.getHeaderFields()).to.eql( - hideTimeFieldColumnSetting || !hasTimeField ? ['Document'] : ['@timestamp', 'Document'] + hideTimeFieldColumnSetting || !hasTimeField ? ['Summary'] : ['@timestamp', 'Summary'] ); await discover.saveSearch(`${SEARCH_NO_COLUMNS}${savedSearchSuffix}`); await discover.waitUntilSearchingHasFinished(); @@ -81,8 +81,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { !hasTimeField ? ['@timestamp'] : hideTimeFieldColumnSetting - ? ['Document'] // legacy behaviour - : ['@timestamp', 'Document'] // legacy behaviour + ? ['Summary'] // legacy behaviour + : ['@timestamp', 'Summary'] // legacy behaviour ); }); @@ -92,7 +92,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await unifiedFieldList.clickFieldListItemRemove('@timestamp'); await retry.try(async () => { expect(await dataGrid.getHeaderFields()).to.eql( - hideTimeFieldColumnSetting || !hasTimeField ? ['Document'] : ['@timestamp', 'Document'] + hideTimeFieldColumnSetting || !hasTimeField ? ['Summary'] : ['@timestamp', 'Summary'] ); }); } @@ -107,7 +107,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await retry.try(async () => { expect(await dataGrid.getHeaderFields()).to.eql( - hideTimeFieldColumnSetting || !hasTimeField ? ['Document'] : ['@timestamp', 'Document'] + hideTimeFieldColumnSetting || !hasTimeField ? ['Summary'] : ['@timestamp', 'Summary'] ); }); @@ -124,8 +124,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { !hasTimeField ? ['@timestamp'] : hideTimeFieldColumnSetting - ? ['Document'] // legacy behaviour - : ['@timestamp', 'Document'] // legacy behaviour + ? ['Summary'] // legacy behaviour + : ['@timestamp', 'Summary'] // legacy behaviour ); }); } @@ -338,22 +338,22 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await discover.loadSavedSearch(`${SEARCH_NO_COLUMNS}${savedSearchSuffix}`); await discover.waitUntilSearchingHasFinished(); expect(await docTable.getHeaderFields()).to.eql( - hideTimeFieldColumnSetting ? ['Document'] : ['@timestamp', 'Document'] + hideTimeFieldColumnSetting ? ['Summary'] : ['@timestamp', 'Summary'] ); await discover.loadSavedSearch(`${SEARCH_NO_COLUMNS}${savedSearchSuffix}-`); await discover.waitUntilSearchingHasFinished(); - expect(await docTable.getHeaderFields()).to.eql(['Document']); + expect(await docTable.getHeaderFields()).to.eql(['Summary']); await discover.loadSavedSearch(`${SEARCH_NO_COLUMNS}${savedSearchSuffix}ESQL`); await discover.waitUntilSearchingHasFinished(); expect(await dataGrid.getHeaderFields()).to.eql( - hideTimeFieldColumnSetting ? ['Document'] : ['@timestamp', 'Document'] + hideTimeFieldColumnSetting ? ['Summary'] : ['@timestamp', 'Summary'] ); await discover.loadSavedSearch(`${SEARCH_NO_COLUMNS}${savedSearchSuffix}ESQLdrop`); await discover.waitUntilSearchingHasFinished(); - expect(await dataGrid.getHeaderFields()).to.eql(['Document']); + expect(await dataGrid.getHeaderFields()).to.eql(['Summary']); // only @timestamp is selected await discover.loadSavedSearch(`${SEARCH_WITH_ONLY_TIMESTAMP}${savedSearchSuffix}`); @@ -369,7 +369,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await discover.loadSavedSearch(`${SEARCH_WITH_ONLY_TIMESTAMP}${savedSearchSuffix}ESQL`); await discover.waitUntilSearchingHasFinished(); expect(await dataGrid.getHeaderFields()).to.eql( - hideTimeFieldColumnSetting ? ['@timestamp'] : ['@timestamp', 'Document'] + hideTimeFieldColumnSetting ? ['@timestamp'] : ['@timestamp', 'Summary'] ); }); 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 54ef1b50c587c9..7054d8b49fbe16 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/common/constants.ts +++ b/x-pack/plugins/observability_solution/logs_explorer/common/constants.ts @@ -40,7 +40,16 @@ export const SMART_FALLBACK_FIELDS = { }; // UI preferences -export const DEFAULT_COLUMNS = [RESOURCE_FIELD_CONFIGURATION, CONTENT_FIELD_CONFIGURATION]; +export const DEFAULT_COLUMNS = [ + /** + * We are switching from these virtual columns to the One Discover Summary column. + * In this effort we don't want to immediately cleanup everything about these virtual columns, + * but only disable their instantiation. + * We'll clean this part as soon as we decide to definitevely discard these columns. + **/ + // RESOURCE_FIELD_CONFIGURATION, + // CONTENT_FIELD_CONFIGURATION +]; export const DEFAULT_ROWS_PER_PAGE = 100; // List of prefixes which needs to be filtered out for Display in Content Column 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 557cbe4dd2728f..19d0f539be6ce5 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 @@ -89,7 +89,13 @@ export const createLogsExplorerProfileCustomizations = customizations.set({ id: 'field_list', - logsFieldsEnabled: true, + /** + * We are switching from these virtual columns to the One Discover Summary column. + * In this effort we don't want to immediately cleanup everything about these virtual columns, + * but only disable their instantiation. + * We'll clean this part as soon as we decide to definitevely discard these columns. + **/ + logsFieldsEnabled: false, }); // Fix bug where filtering on histogram does not work diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/hooks/use_virtual_column_services.tsx b/x-pack/plugins/observability_solution/logs_explorer/public/hooks/use_virtual_column_services.tsx deleted file mode 100644 index 84963c166b77c4..00000000000000 --- a/x-pack/plugins/observability_solution/logs_explorer/public/hooks/use_virtual_column_services.tsx +++ /dev/null @@ -1,21 +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 { DataView } from '@kbn/data-views-plugin/common'; -import { LogsExplorerDiscoverServices } from '../controller'; - -export interface UseVirtualColumnServices { - services: { - data: LogsExplorerDiscoverServices['data']; - dataView: DataView; - }; -} - -const useVirtualColumns = ({ services }: UseVirtualColumnServices) => services; - -export const [VirtualColumnServiceProvider, useVirtualColumnServiceContext] = - createContainer(useVirtualColumns); diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 4dd7938aeee0ea..f012f3cdf627e5 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -2526,7 +2526,6 @@ "discover.logs.flyoutDetail.value.hover.filterForFieldPresent": "Filtrer sur le champ", "discover.logs.flyoutDetail.value.hover.filterOut": "Exclure cette {value}", "discover.logs.flyoutDetail.value.hover.toggleColumn": "Afficher/Masquer la colonne dans le tableau", - "discover.logs.grid.closePopover": "Fermer la fenêtre contextuelle", "discover.logs.popoverAction.closePopover": "Fermer la fenêtre contextuelle", "discover.logs.popoverAction.copyValue": "Copier la valeur", "discover.logs.popoverAction.copyValueAriaText": "Copier la valeur de {fieldName}", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 14827bdf3b7d1a..71e2496a7d5261 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -2525,7 +2525,6 @@ "discover.logs.flyoutDetail.value.hover.filterForFieldPresent": "フィールド表示のフィルター", "discover.logs.flyoutDetail.value.hover.filterOut": "この{value}を除外", "discover.logs.flyoutDetail.value.hover.toggleColumn": "表の列を切り替える", - "discover.logs.grid.closePopover": "ポップオーバーを閉じる", "discover.logs.popoverAction.closePopover": "ポップオーバーを閉じる", "discover.logs.popoverAction.copyValue": "値をコピー", "discover.logs.popoverAction.copyValueAriaText": "{fieldName}の値をコピー", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 116a07f28c7875..272cc11bfa08bc 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -2527,7 +2527,6 @@ "discover.logs.flyoutDetail.value.hover.filterForFieldPresent": "筛留存在的字段", "discover.logs.flyoutDetail.value.hover.filterOut": "筛除此 {value}", "discover.logs.flyoutDetail.value.hover.toggleColumn": "在表中切换列", - "discover.logs.grid.closePopover": "关闭弹出框", "discover.logs.popoverAction.closePopover": "关闭弹出框", "discover.logs.popoverAction.copyValue": "复制值", "discover.logs.popoverAction.copyValueAriaText": "复制 {fieldName} 的值", diff --git a/x-pack/test/functional/apps/observability_logs_explorer/columns_selection.ts b/x-pack/test/functional/apps/observability_logs_explorer/columns_selection.ts index 1308492044f671..295c06682af30f 100644 --- a/x-pack/test/functional/apps/observability_logs_explorer/columns_selection.ts +++ b/x-pack/test/functional/apps/observability_logs_explorer/columns_selection.ts @@ -9,7 +9,7 @@ import moment from 'moment/moment'; import { log, timerange } from '@kbn/apm-synthtrace-client'; import { FtrProviderContext } from './config'; -const defaultLogColumns = ['@timestamp', 'resource', 'content']; +const defaultLogColumns = ['@timestamp', 'Summary']; export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); @@ -57,32 +57,20 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { to, mode: 'absolute', }, - columns: [ - { - smartField: 'resource', - type: 'smart-field', - fallbackFields: ['host.name', 'service.name'], - }, - { - smartField: 'content', - type: 'smart-field', - fallbackFields: ['message'], - }, - { field: 'data_stream.namespace', type: 'document-field' }, - ], + columns: [{ field: 'data_stream.namespace', type: 'document-field' }], }, }); await retry.tryForTime(TEST_TIMEOUT, async () => { expect(await PageObjects.discover.getColumnHeaders()).to.eql([ - ...defaultLogColumns, + '@timestamp', 'data_stream.namespace', ]); }); }); }); - describe('render content virtual column properly', () => { + describe.skip('render content virtual column properly', () => { it('should render log level and log message when present', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { const cellElement = await dataGrid.getCellElementExcludingControlColumns(0, 2); @@ -151,7 +139,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); - describe('render resource virtual column properly', () => { + describe.skip('render resource virtual column properly', () => { it('should render service name and host name when present', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { const cellElement = await dataGrid.getCellElementExcludingControlColumns(0, 1); @@ -162,7 +150,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); - describe('virtual column cell actions', () => { + describe.skip('virtual column cell actions', () => { beforeEach(async () => { await navigateToLogsExplorer(); }); @@ -224,7 +212,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await retry.tryForTime(TEST_TIMEOUT, async () => { const cellElement = await dataGrid.getCellElementExcludingControlColumns(0, 1); const serviceNameChip = await cellElement.findByTestSubject( - 'dataTablePopoverChip_service.name' + 'dataTableCellActionsPopover_service.name' ); const actionSelector = 'dataTableCellAction_addToFilterAction_service.name'; @@ -238,7 +226,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await filterInButton.click(); const rowWithLogLevelInfo = await testSubjects.findAll( - 'dataTablePopoverChip_service.name' + 'dataTableCellActionsPopover_service.name' ); expect(rowWithLogLevelInfo.length).to.be(2); diff --git a/x-pack/test/functional/apps/observability_logs_explorer/field_list.ts b/x-pack/test/functional/apps/observability_logs_explorer/field_list.ts index 9c593a03ac4db2..977c951b2fd022 100644 --- a/x-pack/test/functional/apps/observability_logs_explorer/field_list.ts +++ b/x-pack/test/functional/apps/observability_logs_explorer/field_list.ts @@ -28,7 +28,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }, }); - describe('When virtual columns loads', () => { + describe.skip('When virtual columns loads', () => { before(async () => { await synthtrace.index(generateLogsData({ from, to })); await navigateToLogsExplorer(); diff --git a/x-pack/test/functional/apps/observability_logs_explorer/header_menu.ts b/x-pack/test/functional/apps/observability_logs_explorer/header_menu.ts index 71b5d77964b23d..feb2df2b6e40e4 100644 --- a/x-pack/test/functional/apps/observability_logs_explorer/header_menu.ts +++ b/x-pack/test/functional/apps/observability_logs_explorer/header_menu.ts @@ -66,12 +66,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await dataViews.waitForSwitcherToBe('All logs'); await retry.try(async () => { - expect(await PageObjects.discover.getColumnHeaders()).to.eql([ - '@timestamp', - 'host.name', - 'service.name', - 'message', - ]); + expect(await PageObjects.discover.getColumnHeaders()).to.eql(['@timestamp', 'Summary']); }); await retry.try(async () => { diff --git a/x-pack/test_serverless/functional/test_suites/common/discover/context_awareness/extensions/_get_cell_renderers.ts b/x-pack/test_serverless/functional/test_suites/common/discover/context_awareness/extensions/_get_cell_renderers.ts index 3816f61e83f05d..0cf8aeedd257a9 100644 --- a/x-pack/test_serverless/functional/test_suites/common/discover/context_awareness/extensions/_get_cell_renderers.ts +++ b/x-pack/test_serverless/functional/test_suites/common/discover/context_awareness/extensions/_get_cell_renderers.ts @@ -85,6 +85,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); }); + describe('Service Name Cell', () => { it('should render service.name cell', async () => { const state = kbnRison.encode({ @@ -133,6 +134,40 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); }); + + describe('Summary column', () => { + it('should render a summary of the log entry replacing the original document', async () => { + const state = kbnRison.encode({ + dataSource: { type: 'esql' }, + query: { + esql: 'from my-example-logs,logstash* | sort @timestamp desc | where `message` is not null', + }, + }); + await PageObjects.common.navigateToActualUrl('discover', `?_a=${state}`, { + ensureCurrentUrl: false, + }); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.discover.waitUntilSearchingHasFinished(); + + await testSubjects.existOrFail('discoverDataTableMessageValue'); + }); + + it('should NOT render the summary column if the source does not match logs', async () => { + const state = kbnRison.encode({ + dataSource: { type: 'esql' }, + query: { + esql: 'from my-example-*', + }, + }); + await PageObjects.common.navigateToActualUrl('discover', `?_a=${state}`, { + ensureCurrentUrl: false, + }); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.discover.waitUntilSearchingHasFinished(); + + await testSubjects.missingOrFail('discoverDataTableMessageValue'); + }); + }); }); describe('data view mode', () => { @@ -277,6 +312,34 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); }); + + describe('Summary column', () => { + it('should render a summary of the log entry replacing the original document', async () => { + await PageObjects.common.navigateToActualUrl('discover', undefined, { + ensureCurrentUrl: false, + }); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.discover.waitUntilSearchingHasFinished(); + await dataViews.switchToAndValidate('my-example-logs,logstash*'); + + await retry.try(async () => { + await testSubjects.existOrFail('discoverDataTableMessageValue'); + }); + }); + + it('should NOT render the summary column if the source does not match logs', async () => { + await PageObjects.common.navigateToActualUrl('discover', undefined, { + ensureCurrentUrl: false, + }); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.discover.waitUntilSearchingHasFinished(); + await dataViews.switchToAndValidate('my-example-*'); + + await retry.try(async () => { + await testSubjects.missingOrFail('discoverDataTableMessageValue'); + }); + }); + }); }); }); } diff --git a/x-pack/test_serverless/functional/test_suites/common/discover/context_awareness/extensions/_get_default_app_state.ts b/x-pack/test_serverless/functional/test_suites/common/discover/context_awareness/extensions/_get_default_app_state.ts index 0aa58e85056baa..5867eb528bf8a2 100644 --- a/x-pack/test_serverless/functional/test_suites/common/discover/context_awareness/extensions/_get_default_app_state.ts +++ b/x-pack/test_serverless/functional/test_suites/common/discover/context_awareness/extensions/_get_default_app_state.ts @@ -72,7 +72,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.common.navigateToActualUrl('discover', `?_a=${state}`, { ensureCurrentUrl: false, }); - await expectColumns(['@timestamp', 'Document']); + await expectColumns(['@timestamp', 'Summary']); await dataGrid.clickGridSettings(); let rowHeightValue = await dataGrid.getCurrentRowHeightValue(); expect(rowHeightValue).to.be('Custom'); @@ -102,7 +102,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.discover.waitUntilSearchingHasFinished(); await PageObjects.unifiedFieldList.clickFieldListItemRemove('log.level'); await PageObjects.unifiedFieldList.clickFieldListItemRemove('message'); - await expectColumns(['@timestamp', 'Document']); + await expectColumns(['@timestamp', 'Summary']); await dataGrid.clickGridSettings(); await dataGrid.changeRowHeightValue('Single'); let rowHeightValue = await dataGrid.getCurrentRowHeightValue(); @@ -156,7 +156,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.discover.waitUntilSearchingHasFinished(); await dataViews.switchToAndValidate('my-example-*'); - await expectColumns(['@timestamp', 'Document']); + await expectColumns(['@timestamp', 'Summary']); await dataGrid.clickGridSettings(); let rowHeightValue = await dataGrid.getCurrentRowHeightValue(); expect(rowHeightValue).to.be('Custom'); @@ -182,7 +182,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.discover.waitUntilSearchingHasFinished(); await PageObjects.unifiedFieldList.clickFieldListItemRemove('log.level'); await PageObjects.unifiedFieldList.clickFieldListItemRemove('message'); - await expectColumns(['@timestamp', 'Document']); + await expectColumns(['@timestamp', 'Summary']); await dataGrid.clickGridSettings(); await dataGrid.changeRowHeightValue('Single'); let rowHeightValue = await dataGrid.getCurrentRowHeightValue(); diff --git a/x-pack/test_serverless/functional/test_suites/common/discover/group6/_sidebar.ts b/x-pack/test_serverless/functional/test_suites/common/discover/group6/_sidebar.ts index 5c8fd77347abdc..c6ecd1386cbe97 100644 --- a/x-pack/test_serverless/functional/test_suites/common/discover/group6/_sidebar.ts +++ b/x-pack/test_serverless/functional/test_suites/common/discover/group6/_sidebar.ts @@ -726,7 +726,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 'selected' ); expect(selectedFields.includes(newField)).to.be(false); - expect(await dataGrid.getHeaderFields()).to.eql(['@timestamp', 'Document']); + expect(await dataGrid.getHeaderFields()).to.eql(['@timestamp', 'Summary']); await PageObjects.unifiedFieldList.clickFieldListItemAdd(newField); await PageObjects.header.waitUntilLoadingHasFinished(); @@ -744,7 +744,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { return !(await PageObjects.unifiedFieldList.getAllFieldNames()).includes(newField); }); - expect(await dataGrid.getHeaderFields()).to.eql(['@timestamp', 'Document']); + expect(await dataGrid.getHeaderFields()).to.eql(['@timestamp', 'Summary']); }); }); }); diff --git a/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/columns_selection.ts b/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/columns_selection.ts index c019b06b588b00..1668cc0478b1c4 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/columns_selection.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/columns_selection.ts @@ -9,7 +9,7 @@ import { log, timerange } from '@kbn/apm-synthtrace-client'; import moment from 'moment'; import { FtrProviderContext } from '../../../ftr_provider_context'; -const defaultLogColumns = ['@timestamp', 'resource', 'content']; +const defaultLogColumns = ['@timestamp', 'Summary']; export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); @@ -58,32 +58,20 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { to, mode: 'absolute', }, - columns: [ - { - smartField: 'resource', - type: 'smart-field', - fallbackFields: ['host.name', 'service.name'], - }, - { - smartField: 'content', - type: 'smart-field', - fallbackFields: ['message'], - }, - { field: 'data_stream.namespace', type: 'document-field' }, - ], + columns: [{ field: 'data_stream.namespace', type: 'document-field' }], }, }); await retry.tryForTime(TEST_TIMEOUT, async () => { expect(await PageObjects.discover.getColumnHeaders()).to.eql([ - ...defaultLogColumns, + '@timestamp', 'data_stream.namespace', ]); }); }); }); - describe('render content virtual column properly', () => { + describe.skip('render content virtual column properly', () => { it('should render log level and log message when present', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { const cellElement = await dataGrid.getCellElementExcludingControlColumns(0, 2); @@ -152,7 +140,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); - describe('render resource virtual column properly', () => { + describe.skip('render resource virtual column properly', () => { it('should render service name and host name when present', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { const cellElement = await dataGrid.getCellElementExcludingControlColumns(0, 1); @@ -163,7 +151,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); - describe('virtual column cell actions', () => { + describe.skip('virtual column cell actions', () => { beforeEach(async () => { await navigateToLogsExplorer(); }); @@ -225,7 +213,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await retry.tryForTime(TEST_TIMEOUT, async () => { const cellElement = await dataGrid.getCellElementExcludingControlColumns(0, 1); const serviceNameChip = await cellElement.findByTestSubject( - 'dataTablePopoverChip_service.name' + 'dataTableCellActionsPopover_service.name' ); const actionSelector = 'dataTableCellAction_addToFilterAction_service.name'; @@ -239,7 +227,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await filterInButton.click(); const rowWithLogLevelInfo = await testSubjects.findAll( - 'dataTablePopoverChip_service.name' + 'dataTableCellActionsPopover_service.name' ); expect(rowWithLogLevelInfo.length).to.be(2); diff --git a/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/field_list.ts b/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/field_list.ts index 858139757dc65a..547ce7c86339ce 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/field_list.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/field_list.ts @@ -32,7 +32,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }, }); - describe('When virtual columns loads', () => { + describe.skip('When virtual columns loads', () => { before(async () => { await synthtrace.index(generateLogsData({ from, to })); await PageObjects.svlCommonPage.loginWithPrivilegedRole(); diff --git a/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/header_menu.ts b/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/header_menu.ts index efdd818b0f71da..b9d514cd0b79de 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/header_menu.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/header_menu.ts @@ -88,12 +88,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await dataViews.waitForSwitcherToBe('All logs'); await retry.try(async () => { - expect(await PageObjects.discover.getColumnHeaders()).to.eql([ - '@timestamp', - 'host.name', - 'service.name', - 'message', - ]); + expect(await PageObjects.discover.getColumnHeaders()).to.eql(['@timestamp', 'Summary']); }); await retry.try(async () => { expect(await PageObjects.timePicker.getTimeConfig()).to.eql(timeConfig);