diff --git a/.buildkite/ftr_platform_stateful_configs.yml b/.buildkite/ftr_platform_stateful_configs.yml index bc323bfb6c5bf..c1236a04685fb 100644 --- a/.buildkite/ftr_platform_stateful_configs.yml +++ b/.buildkite/ftr_platform_stateful_configs.yml @@ -64,7 +64,6 @@ enabled: - test/functional/apps/dashboard/group5/config.ts - test/functional/apps/dashboard/group6/config.ts - test/functional/apps/discover/ccs_compatibility/config.ts - - test/functional/apps/discover/classic/config.ts - test/functional/apps/discover/embeddable/config.ts - test/functional/apps/discover/esql/config.ts - test/functional/apps/discover/group1/config.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 9b2b440b28f34..f5c2ed37db761 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -867,6 +867,7 @@ x-pack/plugins/ai_infra/llm_tasks @elastic/appex-ai-infra x-pack/plugins/ai_infra/product_doc_base @elastic/appex-ai-infra x-pack/plugins/aiops @elastic/ml-ui x-pack/plugins/alerting @elastic/response-ops +x-pack/plugins/asset_inventory @elastic/kibana-cloud-security-posture x-pack/plugins/banners @elastic/appex-sharedux x-pack/plugins/canvas @elastic/kibana-presentation x-pack/plugins/cases @elastic/response-ops diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 8e8ee80ff81be..00841c869ef4f 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -470,6 +470,10 @@ The plugin exposes the static DefaultEditorController class to consume. |WARNING: Missing README. +|{kib-repo}blob/{branch}/x-pack/plugins/asset_inventory/README.md[assetInventory] +|Centralized asset inventory experience within the Elastic Security solution. A central place for users to view and manage all their assets from different environments. + + |{kib-repo}blob/{branch}/x-pack/plugins/banners/README.md[banners] |Allow to add a header banner that will be displayed on every page of the Kibana application diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index f6b8e6844ce04..fde70b5ffca0e 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -208,10 +208,6 @@ The default refresh interval for the time filter. Example: [[timepicker-timedefaults]]`timepicker:timeDefaults`:: The default selection in the time filter. -[[truncate-maxheight]]`truncate:maxHeight`:: -deprecated:[8.16.0]The maximum height that a cell occupies in a table. Set to 0 to disable -truncation. - [[enableESQL]]`enableESQL`:: This setting enables ES|QL in Kibana. @@ -340,14 +336,6 @@ Hides the "Time" column in *Discover* and in all saved searches on dashboards. Highlights results in *Discover* and saved searches on dashboards. Highlighting slows requests when working on big documents. -[[doctable-legacy]]`doc_table:legacy`:: -deprecated:[8.15.0] Controls the way the document table looks and works. -To use the new *Document Explorer* instead of the classic view, turn off this option. -The *Document Explorer* offers better data sorting, resizable columns, and a full screen view. - -[[truncate-max-height]]`truncate:maxHeight`:: -The maximum height that a cell in a table can occupy. To disable truncation, set to 0. - [float] [[kibana-ml-settings]] diff --git a/package.json b/package.json index bdad42ff86bef..f6c3330adad25 100644 --- a/package.json +++ b/package.json @@ -191,6 +191,7 @@ "@kbn/apm-utils": "link:packages/kbn-apm-utils", "@kbn/app-link-test-plugin": "link:test/plugin_functional/plugins/app_link_test", "@kbn/application-usage-test-plugin": "link:x-pack/test/usage_collection/plugins/application_usage_test", + "@kbn/asset-inventory-plugin": "link:x-pack/plugins/asset_inventory", "@kbn/audit-log-plugin": "link:x-pack/test/security_api_integration/plugins/audit_log", "@kbn/avc-banner": "link:packages/kbn-avc-banner", "@kbn/banners-plugin": "link:x-pack/plugins/banners", diff --git a/packages/kbn-discover-utils/index.ts b/packages/kbn-discover-utils/index.ts index 4345c0f8fc6c4..93481c681d930 100644 --- a/packages/kbn-discover-utils/index.ts +++ b/packages/kbn-discover-utils/index.ts @@ -14,7 +14,6 @@ export { DEFAULT_ALLOWED_LOGS_BASE_PATTERNS, DEFAULT_COLUMNS_SETTING, DOC_HIDE_TIME_COLUMN_SETTING, - DOC_TABLE_LEGACY, FIELDS_LIMIT_SETTING, HIDE_ANNOUNCEMENTS, MAX_DOC_FIELDS_DISPLAYED, @@ -28,8 +27,6 @@ export { SHOW_FIELD_STATISTICS, SHOW_MULTIFIELDS, SORT_DEFAULT_ORDER_SETTING, - TRUNCATE_MAX_HEIGHT, - TRUNCATE_MAX_HEIGHT_DEFAULT_VALUE, IgnoredReason, buildDataTableRecord, buildDataTableRecordList, @@ -45,7 +42,6 @@ export { getMessageFieldWithFallbacks, getShouldShowFieldHandler, isNestedFieldParent, - isLegacyTableEnabled, usePager, calcFieldCounts, getLogLevelColor, diff --git a/packages/kbn-discover-utils/src/constants.ts b/packages/kbn-discover-utils/src/constants.ts index 1f8e3b9916b91..005f6f7687109 100644 --- a/packages/kbn-discover-utils/src/constants.ts +++ b/packages/kbn-discover-utils/src/constants.ts @@ -12,7 +12,6 @@ export const CONTEXT_STEP_SETTING = 'context:step'; export const CONTEXT_TIE_BREAKER_FIELDS_SETTING = 'context:tieBreakerFields'; export const DEFAULT_COLUMNS_SETTING = 'defaultColumns'; export const DOC_HIDE_TIME_COLUMN_SETTING = 'doc_table:hideTimeColumn'; -export const DOC_TABLE_LEGACY = 'doc_table:legacy'; export const FIELDS_LIMIT_SETTING = 'fields:popularLimit'; export const HIDE_ANNOUNCEMENTS = 'hideAnnouncements'; export const MAX_DOC_FIELDS_DISPLAYED = 'discover:maxDocFieldsDisplayed'; @@ -26,5 +25,3 @@ export const SEARCH_ON_PAGE_LOAD_SETTING = 'discover:searchOnPageLoad'; export const SHOW_FIELD_STATISTICS = 'discover:showFieldStatistics'; export const SHOW_MULTIFIELDS = 'discover:showMultiFields'; export const SORT_DEFAULT_ORDER_SETTING = 'discover:sort:defaultOrder'; -export const TRUNCATE_MAX_HEIGHT = 'truncate:maxHeight'; -export const TRUNCATE_MAX_HEIGHT_DEFAULT_VALUE = 115; diff --git a/packages/kbn-discover-utils/src/utils/build_data_record.ts b/packages/kbn-discover-utils/src/utils/build_data_record.ts index 2bfc9554754af..83e637f438119 100644 --- a/packages/kbn-discover-utils/src/utils/build_data_record.ts +++ b/packages/kbn-discover-utils/src/utils/build_data_record.ts @@ -17,7 +17,7 @@ import type { DataTableRecord, EsHitRecord } from '../types'; import { getDocId } from './get_doc_id'; /** - * Build a record for data table, explorer + classic one + * Build a record for data grid * @param doc the document returned from Elasticsearch * @param dataView this current data view * @param isAnchor determines if the given doc is the anchor doc when viewing surrounding documents diff --git a/packages/kbn-discover-utils/src/utils/format_hit.test.ts b/packages/kbn-discover-utils/src/utils/format_hit.test.ts index ba38812b0e142..6e2acbd43846b 100644 --- a/packages/kbn-discover-utils/src/utils/format_hit.test.ts +++ b/packages/kbn-discover-utils/src/utils/format_hit.test.ts @@ -106,6 +106,36 @@ describe('formatHit', () => { ]); }); + it('should highlight a subfield even shouldShowFieldHandler determines it should not be shown ', () => { + const highlightHit = buildDataTableRecord( + { + _id: '2', + _index: 'logs', + fields: { + object: ['object'], + 'object.value': [42, 13], + }, + highlight: { 'object.value': ['%%'] }, + }, + dataViewMock + ); + + const formatted = formatHit( + highlightHit, + dataViewMock, + (fieldName) => ['object'].includes(fieldName), + 220, + fieldFormatsMock + ); + + expect(formatted).toEqual([ + ['object.value', 'formatted:42,13', 'object.value'], + ['object', ['object'], 'object'], + ['_index', 'formatted:logs', '_index'], + ['_score', undefined, '_score'], + ]); + }); + it('should filter fields based on their real name not displayName', () => { const formatted = formatHit( row, diff --git a/packages/kbn-discover-utils/src/utils/format_hit.ts b/packages/kbn-discover-utils/src/utils/format_hit.ts index 99913e32cb78c..b29353253df51 100644 --- a/packages/kbn-discover-utils/src/utils/format_hit.ts +++ b/packages/kbn-discover-utils/src/utils/format_hit.ts @@ -70,9 +70,14 @@ export function formatHit( const pairs = highlights[key] ? renderedPairs : otherPairs; // If the field is a mapped field, we first check if it should be shown, - // if not we always include it into the result. + // or if it's highlighted, but the parent is not. + // If not we always include it into the result. if (displayKey) { - if (shouldShowFieldHandler(key)) { + const multiParent = field.getSubtypeMulti?.()?.multi.parent; + const isHighlighted = Boolean(highlights[key]); + const isParentHighlighted = Boolean(multiParent && highlights[multiParent]); + + if ((isHighlighted && !isParentHighlighted) || shouldShowFieldHandler(key)) { pairs.push([displayKey, undefined, key]); } } else { diff --git a/packages/kbn-discover-utils/src/utils/index.ts b/packages/kbn-discover-utils/src/utils/index.ts index 01ef6b83f83a8..a1811f5d93e89 100644 --- a/packages/kbn-discover-utils/src/utils/index.ts +++ b/packages/kbn-discover-utils/src/utils/index.ts @@ -21,5 +21,4 @@ export * from './nested_fields'; export * from './get_field_value'; export * from './calc_field_counts'; export * from './get_visible_columns'; -export { isLegacyTableEnabled } from './is_legacy_table_enabled'; export { DiscoverFlyouts, dismissAllFlyoutsExceptFor, dismissFlyouts } from './dismiss_flyouts'; diff --git a/packages/kbn-discover-utils/src/utils/is_legacy_table_enabled.ts b/packages/kbn-discover-utils/src/utils/is_legacy_table_enabled.ts deleted file mode 100644 index c6233622ea515..0000000000000 --- a/packages/kbn-discover-utils/src/utils/is_legacy_table_enabled.ts +++ /dev/null @@ -1,25 +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 type { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; -import { DOC_TABLE_LEGACY } from '../constants'; - -export function isLegacyTableEnabled({ - uiSettings, - isEsqlMode, -}: { - uiSettings: IUiSettingsClient; - isEsqlMode: boolean; -}): boolean { - if (isEsqlMode) { - return false; // only show the new data grid - } - - return uiSettings.get(DOC_TABLE_LEGACY); -} diff --git a/packages/kbn-discover-utils/tsconfig.json b/packages/kbn-discover-utils/tsconfig.json index c26624d139dec..f43aa1dfb4e05 100644 --- a/packages/kbn-discover-utils/tsconfig.json +++ b/packages/kbn-discover-utils/tsconfig.json @@ -24,7 +24,6 @@ "@kbn/field-formats-plugin", "@kbn/field-types", "@kbn/i18n", - "@kbn/core-ui-settings-browser", "@kbn/expressions-plugin", "@kbn/logs-data-access-plugin", "@kbn/i18n-react", diff --git a/packages/kbn-management/settings/setting_ids/index.ts b/packages/kbn-management/settings/setting_ids/index.ts index 9a7c95917878a..438e216ab8f32 100644 --- a/packages/kbn-management/settings/setting_ids/index.ts +++ b/packages/kbn-management/settings/setting_ids/index.ts @@ -87,8 +87,6 @@ export const DISCOVER_SHOW_MULTI_FIELDS_ID = 'discover:showMultiFields'; export const DISCOVER_SORT_DEFAULT_ORDER_ID = 'discover:sort:defaultOrder'; export const DOC_TABLE_HIDE_TIME_COLUMNS_ID = 'doc_table:hideTimeColumn'; export const DOC_TABLE_HIGHLIGHT_ID = 'doc_table:highlight'; -export const DOC_TABLE_LEGACY_ID = 'doc_table:legacy'; -export const TRUNCATE_MAX_HEIGHT_ID = 'truncate:maxHeight'; // Machine learning settings export const ML_ANOMALY_DETECTION_RESULTS_ENABLE_TIME_DEFAULTS_ID = diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 6f4f35cd72600..b1b4cb38d3f19 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -5,6 +5,7 @@ pageLoadAssetSize: aiops: 17680 alerting: 106936 apm: 64385 + assetInventory: 18478 banners: 17946 bfetch: 22837 canvas: 29355 diff --git a/packages/kbn-unified-data-table/src/components/source_document.test.tsx b/packages/kbn-unified-data-table/src/components/source_document.test.tsx index f48e2b58c8424..25d61312dc242 100644 --- a/packages/kbn-unified-data-table/src/components/source_document.test.tsx +++ b/packages/kbn-unified-data-table/src/components/source_document.test.tsx @@ -52,7 +52,7 @@ describe('Unified data table source document cell rendering', function () { /> ); expect(component.html()).toMatchInlineSnapshot( - `"
_index
test
_score
1
"` + `"
extension
.gz
_index
test
_score
1
"` ); }); }); diff --git a/packages/kbn-unified-doc-viewer/README.md b/packages/kbn-unified-doc-viewer/README.md index 825f6eee9f910..ea808e3fd4fe4 100644 --- a/packages/kbn-unified-doc-viewer/README.md +++ b/packages/kbn-unified-doc-viewer/README.md @@ -2,10 +2,6 @@ This package contains components and services for the unified doc viewer component. -Discover (Classic view → Expanded document) - -![image](https://github.com/elastic/kibana/assets/1178348/a0a360bf-2697-4427-a32e-c728f06f5a7e) - Discover (Document explorer → Toggle dialog with details) ![image](https://github.com/elastic/kibana/assets/1178348/c9c11587-c53f-4bcd-8d48-aaceb64981ea) diff --git a/packages/kbn-unified-doc-viewer/src/services/types.ts b/packages/kbn-unified-doc-viewer/src/services/types.ts index 04d88ba256a37..9c4d06f21b1cf 100644 --- a/packages/kbn-unified-doc-viewer/src/services/types.ts +++ b/packages/kbn-unified-doc-viewer/src/services/types.ts @@ -7,13 +7,9 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; +import type { DataView } from '@kbn/data-views-plugin/public'; import type { AggregateQuery, Query } from '@kbn/es-query'; -import type { - DataTableRecord, - DataTableColumnsMeta, - IgnoredReason, -} from '@kbn/discover-utils/types'; +import type { DataTableRecord, DataTableColumnsMeta } from '@kbn/discover-utils/types'; import { DocViewsRegistry } from './doc_views_registry'; export interface FieldMapping { @@ -77,23 +73,3 @@ interface ComponentDocViewInput extends BaseDocViewInput { export type DocView = ComponentDocViewInput | RenderDocViewInput; export type DocViewFactory = () => DocView; - -export interface FieldRecordLegacy { - action: { - isActive: boolean; - onFilter?: DocViewFilterFn; - onToggleColumn: ((field: string) => void) | undefined; - flattenedField: unknown; - }; - field: { - displayName: string; - field: string; - scripted: boolean; - fieldType?: string; - fieldMapping?: DataViewField; - }; - value: { - formattedValue: string; - ignored?: IgnoredReason; - }; -} diff --git a/packages/kbn-unified-doc-viewer/src/types.ts b/packages/kbn-unified-doc-viewer/src/types.ts index 01d59ff970d85..8a80382add748 100644 --- a/packages/kbn-unified-doc-viewer/src/types.ts +++ b/packages/kbn-unified-doc-viewer/src/types.ts @@ -12,5 +12,4 @@ export type { DocViewFilterFn, DocViewRenderFn, DocViewRenderProps, - FieldRecordLegacy, } from './services/types'; diff --git a/packages/kbn-unified-doc-viewer/types.ts b/packages/kbn-unified-doc-viewer/types.ts index 50fc73909e1db..aae317c8f8fba 100644 --- a/packages/kbn-unified-doc-viewer/types.ts +++ b/packages/kbn-unified-doc-viewer/types.ts @@ -8,4 +8,4 @@ */ export type { ElasticRequestState } from '.'; -export type { DocViewFilterFn, DocViewRenderProps, FieldRecordLegacy } from './src/types'; +export type { DocViewFilterFn, DocViewRenderProps } from './src/types'; diff --git a/src/plugins/console/public/application/containers/editor/components/context_menu/context_menu.tsx b/src/plugins/console/public/application/containers/editor/components/context_menu/context_menu.tsx index 3860f0b7bc704..daa1515d90e7c 100644 --- a/src/plugins/console/public/application/containers/editor/components/context_menu/context_menu.tsx +++ b/src/plugins/console/public/application/containers/editor/components/context_menu/context_menu.tsx @@ -28,7 +28,11 @@ import type { EditorRequest } from '../../types'; import { useServicesContext } from '../../../../contexts'; import { StorageKeys } from '../../../../../services'; -import { DEFAULT_LANGUAGE, AVAILABLE_LANGUAGES } from '../../../../../../common/constants'; +import { + DEFAULT_LANGUAGE, + AVAILABLE_LANGUAGES, + KIBANA_API_PREFIX, +} from '../../../../../../common/constants'; interface Props { getRequests: () => Promise; @@ -90,9 +94,24 @@ export const ContextMenu = ({ // Get all the selected requests const requests = await getRequests(); + // If we have any kbn requests, we should not allow the user to copy as + // anything other than curl + const hasKbnRequests = requests.some((req) => req.url.startsWith(KIBANA_API_PREFIX)); + + if (hasKbnRequests && withLanguage !== 'curl') { + notifications.toasts.addDanger({ + title: i18n.translate('console.consoleMenu.copyAsMixedRequestsMessage', { + defaultMessage: 'Kibana requests can only be copied as curl', + }), + }); + + return; + } + const { data: requestsAsCode, error: requestError } = await convertRequestToLanguage({ language: withLanguage, esHost: esHostService.getHost(), + kibanaHost: window.location.origin, requests, }); diff --git a/src/plugins/console/public/services/api.ts b/src/plugins/console/public/services/api.ts index c15e90dfd1bd4..c104a5fc16d09 100644 --- a/src/plugins/console/public/services/api.ts +++ b/src/plugins/console/public/services/api.ts @@ -14,15 +14,17 @@ export async function convertRequestToLanguage({ requests, language, esHost, + kibanaHost, }: { language: string; esHost: string; + kibanaHost: string; requests: EditorRequest[]; }) { return sendRequest({ path: `/api/console/convert_request_to_language`, method: 'post', - query: { language, esHost }, + query: { language, esHost, kibanaHost }, body: requests, }); } diff --git a/src/plugins/console/server/routes/api/console/convert_request_to_language/index.ts b/src/plugins/console/server/routes/api/console/convert_request_to_language/index.ts index 23577d7b200d9..115ece83859fa 100644 --- a/src/plugins/console/server/routes/api/console/convert_request_to_language/index.ts +++ b/src/plugins/console/server/routes/api/console/convert_request_to_language/index.ts @@ -18,6 +18,7 @@ const routeValidationConfig = { query: schema.object({ language: schema.string(), esHost: schema.string(), + kibanaHost: schema.string(), }), body: schema.maybe( schema.arrayOf( @@ -39,7 +40,7 @@ export const registerConvertRequestRoute = ({ }: RouteDependencies) => { const handler: RequestHandler = async (ctx, req, response) => { const { body, query } = req; - const { language, esHost } = query; + const { language, esHost, kibanaHost } = query; try { // Iterate over each request and build all the requests into a single string @@ -60,6 +61,9 @@ export const registerConvertRequestRoute = ({ printResponse: true, complete: true, elasticsearchUrl: esHost, + otherUrls: { + kbn: kibanaHost, + }, }); return response.ok({ diff --git a/src/plugins/discover/public/__mocks__/__storybook_mocks__/with_discover_services.tsx b/src/plugins/discover/public/__mocks__/__storybook_mocks__/with_discover_services.tsx index 3a41b32f2eb85..9def587a53506 100644 --- a/src/plugins/discover/public/__mocks__/__storybook_mocks__/with_discover_services.tsx +++ b/src/plugins/discover/public/__mocks__/__storybook_mocks__/with_discover_services.tsx @@ -15,7 +15,6 @@ import { identity } from 'lodash'; import { IUiSettingsClient } from '@kbn/core/public'; import { DEFAULT_COLUMNS_SETTING, - DOC_TABLE_LEGACY, MAX_DOC_FIELDS_DISPLAYED, ROW_HEIGHT_OPTION, SAMPLE_SIZE_SETTING, @@ -39,8 +38,6 @@ export const uiSettingsMock = { return 10; } else if (key === DEFAULT_COLUMNS_SETTING) { return ['default_column']; - } else if (key === DOC_TABLE_LEGACY) { - return false; } else if (key === SEARCH_FIELDS_FROM_SOURCE) { return false; } else if (key === SHOW_MULTIFIELDS) { diff --git a/src/plugins/discover/public/__mocks__/ui_settings.ts b/src/plugins/discover/public/__mocks__/ui_settings.ts index f9cc5dab7a64f..2bf4e9adff341 100644 --- a/src/plugins/discover/public/__mocks__/ui_settings.ts +++ b/src/plugins/discover/public/__mocks__/ui_settings.ts @@ -11,7 +11,6 @@ import { IUiSettingsClient } from '@kbn/core/public'; import { CONTEXT_TIE_BREAKER_FIELDS_SETTING, DEFAULT_COLUMNS_SETTING, - DOC_TABLE_LEGACY, SAMPLE_SIZE_SETTING, SAMPLE_ROWS_PER_PAGE_SETTING, SHOW_MULTIFIELDS, @@ -27,8 +26,6 @@ export const uiSettingsMock = { return 100; } else if (key === DEFAULT_COLUMNS_SETTING) { return ['default_column']; - } else if (key === DOC_TABLE_LEGACY) { - return true; } else if (key === CONTEXT_TIE_BREAKER_FIELDS_SETTING) { return ['_doc']; } else if (key === SEARCH_FIELDS_FROM_SOURCE) { diff --git a/src/plugins/discover/public/application/context/context_app.tsx b/src/plugins/discover/public/application/context/context_app.tsx index b0fc1342a8f72..41f6f2e2933d4 100644 --- a/src/plugins/discover/public/application/context/context_app.tsx +++ b/src/plugins/discover/public/application/context/context_app.tsx @@ -9,7 +9,6 @@ import React, { Fragment, memo, useEffect, useRef, useMemo, useCallback } from 'react'; import './context_app.scss'; -import classNames from 'classnames'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiText, EuiPage, EuiPageBody, EuiSpacer, useEuiPaddingSize } from '@elastic/eui'; import { css } from '@emotion/react'; @@ -19,11 +18,7 @@ import { useExecutionContext } from '@kbn/kibana-react-plugin/public'; import { generateFilters } from '@kbn/data-plugin/public'; import { i18n } from '@kbn/i18n'; import { reportPerformanceMetricEvent } from '@kbn/ebt-tools'; -import { - DOC_TABLE_LEGACY, - SEARCH_FIELDS_FROM_SOURCE, - SORT_DEFAULT_ORDER_SETTING, -} from '@kbn/discover-utils'; +import { SEARCH_FIELDS_FROM_SOURCE, SORT_DEFAULT_ORDER_SETTING } from '@kbn/discover-utils'; import { UseColumnsProps, popularizeField, useColumns } from '@kbn/unified-data-table'; import { DocViewFilterFn } from '@kbn/unified-doc-viewer/types'; import { DiscoverGridSettings } from '@kbn/saved-search-plugin/common'; @@ -60,7 +55,6 @@ export const ContextApp = ({ dataView, anchorId, referrer }: ContextAppProps) => fieldsMetadata, } = services; - const isLegacy = useMemo(() => uiSettings.get(DOC_TABLE_LEGACY), [uiSettings]); const useNewFieldsApi = useMemo(() => !uiSettings.get(SEARCH_FIELDS_FROM_SOURCE), [uiSettings]); /** @@ -266,7 +260,7 @@ export const ContextApp = ({ dataView, anchorId, referrer }: ContextAppProps) => })} - + { - const mountComponent = async ({ - anchorStatus, - isLegacy, - }: { - anchorStatus?: LoadingStatus; - isLegacy?: boolean; - }) => { + const mountComponent = async ({ anchorStatus }: { anchorStatus?: LoadingStatus }) => { const hit = { _id: '123', _index: 'test_index', @@ -75,7 +67,6 @@ describe('ContextAppContent test', () => { onRemoveColumn: () => {}, onSetColumns: () => {}, sort: [['order_date', 'desc']] as Array<[string, SortDirection]>, - isLegacy: isLegacy ?? true, setAppState: () => {}, addFilter: () => {}, interceptedWarnings: [], @@ -93,29 +84,14 @@ describe('ContextAppContent test', () => { return component; }; - it('should render legacy table correctly', async () => { - const component = await mountComponent({}); - expect(component.find(DocTableWrapper).length).toBe(1); - const loadingIndicator = findTestSubject(component, 'contextApp_loadingIndicator'); - expect(loadingIndicator.length).toBe(0); - expect(component.find(ActionBar).length).toBe(2); - }); - - it('renders loading indicator', async () => { - const component = await mountComponent({ anchorStatus: LoadingStatus.LOADING }); - const loadingIndicator = findTestSubject(component, 'contextApp_loadingIndicator'); - expect(component.find(DocTableWrapper).length).toBe(1); - expect(loadingIndicator.length).toBe(1); - }); - it('should render discover grid correctly', async () => { - const component = await mountComponent({ isLegacy: false }); + const component = await mountComponent({}); expect(component.find(UnifiedDataTable).length).toBe(1); expect(findTestSubject(component, 'unifiedDataTableToolbar').exists()).toBe(true); }); it('should not show display options button', async () => { - const component = await mountComponent({ isLegacy: false }); + const component = await mountComponent({}); expect(findTestSubject(component, 'unifiedDataTableToolbar').exists()).toBe(true); expect(findTestSubject(component, 'dataGridDisplaySelectorButton').exists()).toBe(false); }); 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 b6b4be221377a..ed54b4f8145e2 100644 --- a/src/plugins/discover/public/application/context/context_app_content.tsx +++ b/src/plugins/discover/public/application/context/context_app_content.tsx @@ -8,8 +8,7 @@ */ import React, { Fragment, useCallback, useMemo, useState, FC } from 'react'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiSpacer, EuiText, useEuiPaddingSize } from '@elastic/eui'; +import { EuiSpacer, useEuiPaddingSize } from '@elastic/eui'; import { css } from '@emotion/react'; import type { DataView } from '@kbn/data-views-plugin/public'; import { SortDirection } from '@kbn/data-plugin/public'; @@ -45,7 +44,6 @@ import { ActionBar } from './components/action_bar/action_bar'; import { AppState } from './services/context_state'; import { SurrDocType } from './services/context'; import { MAX_CONTEXT_SIZE, MIN_CONTEXT_SIZE } from './services/constants'; -import { DocTableContext } from '../../components/doc_table/doc_table_context'; import { useDiscoverServices } from '../../hooks/use_discover_services'; import { DiscoverGridFlyout } from '../../components/discover_grid_flyout'; import { onResizeGridColumn } from '../../utils/on_resize_grid_column'; @@ -73,7 +71,6 @@ export interface ContextAppContentProps { successorsStatus: LoadingStatus; interceptedWarnings: SearchResponseWarning[]; useNewFieldsApi: boolean; - isLegacy: boolean; setAppState: (newState: Partial) => void; addFilter: DocViewFilterFn; } @@ -85,7 +82,6 @@ export function clamp(value: number) { } const DiscoverGridMemoized = React.memo(DiscoverGrid); -const DocTableContextMemoized = React.memo(DocTableContext); const ActionBarMemoized = React.memo(ActionBar); export function ContextAppContent({ @@ -105,7 +101,6 @@ export function ContextAppContent({ successorsStatus, interceptedWarnings, useNewFieldsApi, - isLegacy, setAppState, addFilter, }: ContextAppContentProps) { @@ -127,17 +122,6 @@ export function ContextAppContent({ ); const defaultStepSize = useMemo(() => parseInt(config.get(CONTEXT_STEP_SETTING), 10), [config]); - const loadingFeedback = () => { - if (isLegacy && isAnchorLoading) { - return ( - - - - ); - } - return null; - }; - const onChangeCount = useCallback( (type: SurrDocType, count: number) => { const countKey = type === SurrDocType.SUCCESSORS ? 'successorCount' : 'predecessorCount'; @@ -224,59 +208,42 @@ export function ContextAppContent({ isLoading={arePredecessorsLoading} isDisabled={isAnchorLoading} /> - {loadingFeedback()} - {isLegacy && rows && rows.length !== 0 && ( - - )} - {!isLegacy && ( -
- - - -
- )} +
+ + + +
'', - docLinks: { links: { discover: { documentExplorer: '' } } }, - capabilities: { advancedSettings: { save: true } }, - storage: new LocalStorageMock({ [CALLOUT_STATE_KEY]: false }), -} as unknown as DiscoverServices; - -const mount = (services: DiscoverServices) => { - return mountWithIntl( - - - - ); -}; - -describe('Document Explorer callout', () => { - it('should render callout', () => { - const result = mount(defaultServices); - - expect(result.find('.dscDocumentExplorerCallout').exists()).toBeTruthy(); - }); - - it('should not render callout for user without permissions', () => { - const services = { - ...defaultServices, - capabilities: { advancedSettings: { save: false } }, - } as unknown as DiscoverServices; - const result = mount(services); - - expect(result.find('.dscDocumentExplorerCallout').exists()).toBeFalsy(); - }); - - it('should not render callout of it was closed', () => { - const services = { - ...defaultServices, - storage: new LocalStorageMock({ [CALLOUT_STATE_KEY]: true }), - } as unknown as DiscoverServices; - const result = mount(services); - - expect(result.find('.dscDocumentExplorerCallout').exists()).toBeFalsy(); - }); -}); diff --git a/src/plugins/discover/public/application/main/components/document_explorer_callout/document_explorer_callout.tsx b/src/plugins/discover/public/application/main/components/document_explorer_callout/document_explorer_callout.tsx deleted file mode 100644 index 20d5702d129a7..0000000000000 --- a/src/plugins/discover/public/application/main/components/document_explorer_callout/document_explorer_callout.tsx +++ /dev/null @@ -1,139 +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, { useCallback, useMemo, useState } from 'react'; -import './document_explorer_callout.scss'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { - EuiButton, - EuiButtonIcon, - EuiCallOut, - EuiFlexGroup, - EuiFlexItem, - EuiLink, - useEuiTheme, -} from '@elastic/eui'; -import { css } from '@emotion/react'; -import { Storage } from '@kbn/kibana-utils-plugin/public'; -import { DOC_TABLE_LEGACY } from '@kbn/discover-utils'; -import { useDiscoverServices } from '../../../../hooks/use_discover_services'; - -export const CALLOUT_STATE_KEY = 'discover:docExplorerCalloutClosed'; - -const getStoredCalloutState = (storage: Storage): boolean => { - const calloutClosed = storage.get(CALLOUT_STATE_KEY); - return Boolean(calloutClosed); -}; -const updateStoredCalloutState = (newState: boolean, storage: Storage) => { - storage.set(CALLOUT_STATE_KEY, newState); -}; - -/** - * The callout that's displayed when Document explorer is disabled - */ -export const DocumentExplorerCallout = () => { - const { euiTheme } = useEuiTheme(); - const { storage, capabilities, docLinks, addBasePath } = useDiscoverServices(); - const [calloutClosed, setCalloutClosed] = useState(getStoredCalloutState(storage)); - - const onCloseCallout = useCallback(() => { - updateStoredCalloutState(true, storage); - setCalloutClosed(true); - }, [storage]); - - const semiBoldStyle = useMemo( - () => css` - font-weight: ${euiTheme.font.weight.semiBold}; - `, - [euiTheme.font.weight.semiBold] - ); - - if (calloutClosed || !capabilities.advancedSettings.save) { - return null; - } - - return ( - } - iconType="search" - > -

- - - - ), - }} - /> -

- - - - - - - - - - - - -
- ); -}; - -function CalloutTitle({ onCloseCallout }: { onCloseCallout: () => void }) { - return ( - - - - - - - - - ); -} diff --git a/src/plugins/discover/public/application/main/components/document_explorer_callout/index.ts b/src/plugins/discover/public/application/main/components/document_explorer_callout/index.ts deleted file mode 100644 index 1c03e77fde661..0000000000000 --- a/src/plugins/discover/public/application/main/components/document_explorer_callout/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 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". - */ - -export { DocumentExplorerCallout } from './document_explorer_callout'; diff --git a/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_table.tsx b/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_table.tsx index db6acd4f6a952..7742fbbcc4140 100644 --- a/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_table.tsx +++ b/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_table.tsx @@ -33,9 +33,6 @@ const statsTableCss = css({ width: '100%', height: '100%', overflowY: 'auto', - '.kbnDocTableWrapper': { - overflowX: 'hidden', - }, }); const fallBacklastReloadRequestTime$ = new BehaviorSubject(0); 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 6092a59333290..9f929077bd7d9 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 @@ -37,8 +37,6 @@ import { } from '@kbn/unified-data-table'; import { DOC_HIDE_TIME_COLUMN_SETTING, - HIDE_ANNOUNCEMENTS, - isLegacyTableEnabled, MAX_DOC_FIELDS_DISPLAYED, ROW_HEIGHT_OPTION, SEARCH_FIELDS_FROM_SOURCE, @@ -58,8 +56,6 @@ import { useDiscoverServices } from '../../../../hooks/use_discover_services'; import { FetchStatus } from '../../../types'; import { DiscoverStateContainer } from '../../state_management/discover_state'; import { useDataState } from '../../hooks/use_data_state'; -import { DocTableInfinite } from '../../../../components/doc_table/doc_table_infinite'; -import { DocumentExplorerCallout } from '../document_explorer_callout'; import { getMaxAllowedSampleSize, getAllowedSampleSize, @@ -88,7 +84,6 @@ const progressStyle = css` z-index: 2; `; -const DocTableInfiniteMemoized = React.memo(DocTableInfinite); const DiscoverGridMemoized = React.memo(DiscoverGrid); // export needs for testing @@ -146,11 +141,6 @@ function DiscoverDocumentsComponent({ const expandedDoc = useInternalStateSelector((state) => state.expandedDoc); const isEsqlMode = useIsEsqlMode(); const useNewFieldsApi = useMemo(() => !uiSettings.get(SEARCH_FIELDS_FROM_SOURCE), [uiSettings]); - const hideAnnouncements = useMemo(() => uiSettings.get(HIDE_ANNOUNCEMENTS), [uiSettings]); - const isLegacy = useMemo( - () => isLegacyTableEnabled({ uiSettings, isEsqlMode }), - [uiSettings, isEsqlMode] - ); const documentState = useDataState(documents$); const isDataLoading = documentState.fetchStatus === FetchStatus.LOADING || @@ -186,7 +176,6 @@ function DiscoverDocumentsComponent({ columns: currentColumns, onAddColumn, onRemoveColumn, - onMoveColumn, onSetColumns, } = useColumns({ capabilities, @@ -417,115 +406,76 @@ function DiscoverDocumentsComponent({ } return ( - <> - {isLegacy && ( - <> - {viewModeToggle} - {callouts} - - )} - - -

- -

-
- {isLegacy && ( - <> - {rows && rows.length > 0 && ( - <> - {!hideAnnouncements && } - - - )} - {loadingIndicator} - - )} - {!isLegacy && ( -
- - - -
- )} -
- + + +

+ +

+
+
+ + + +
+
); } diff --git a/src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx b/src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx index bd8073c01f27f..63997c0d7b975 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx @@ -13,11 +13,9 @@ import { EuiFormRow, EuiSwitch } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { SavedObjectSaveModal, showSaveModal, OnSaveProps } from '@kbn/saved-objects-plugin/public'; import { SavedSearch, SaveSavedSearchOptions } from '@kbn/saved-search-plugin/public'; -import { isLegacyTableEnabled } from '@kbn/discover-utils'; import { DiscoverServices } from '../../../../build_services'; import { DiscoverStateContainer } from '../../state_management/discover_state'; import { getAllowedSampleSize } from '../../../../utils/get_allowed_sample_size'; -import { DataSourceType, isDataSourceType } from '../../../../../common/data_sources'; async function saveDataSource({ savedSearch, @@ -127,12 +125,7 @@ export async function onSaveSearch({ savedSearch.title = newTitle; savedSearch.description = newDescription; savedSearch.timeRestore = newTimeRestore; - savedSearch.rowsPerPage = isLegacyTableEnabled({ - uiSettings, - isEsqlMode: isDataSourceType(appState.dataSource, DataSourceType.Esql), - }) - ? currentRowsPerPage - : appState.rowsPerPage; + savedSearch.rowsPerPage = appState.rowsPerPage; // save the custom value or reset it if it's invalid const appStateSampleSize = appState.sampleSize; diff --git a/src/plugins/discover/public/components/doc_table/_doc_table.scss b/src/plugins/discover/public/components/doc_table/_doc_table.scss deleted file mode 100644 index a303d462a576b..0000000000000 --- a/src/plugins/discover/public/components/doc_table/_doc_table.scss +++ /dev/null @@ -1,136 +0,0 @@ -/** - * 1. Stack content vertically so the table can scroll when its constrained by a fixed container height. - */ -// stylelint-disable selector-no-qualifying-type -.kbnDocTableWrapper { - @include euiScrollBar; - @include euiOverflowShadow; - overflow: auto; - display: flex; - flex: 1 1 100%; - flex-direction: column; /* 1 */ - - th { - text-align: left; - font-weight: bold; - } - - .spinner { - position: absolute; - top: 40%; - left: 0; - right: 0; - z-index: $euiZLevel1; - opacity: .5; - } - - // SASSTODO: add a monospace modifier to the doc-table component - .kbnDocTable__row { - font-family: $euiCodeFontFamily; - font-size: $euiFontSizeXS; - } - - .embPanel & { - // Bug fix for dashboard panels https://github.com/elastic/kibana/issues/180828 - mask-image: none; - } -} - -.kbnDocTable__footer { - background-color: $euiColorLightShade; - padding: $euiSizeXS $euiSizeS; - text-align: center; -} - -.kbnDocTable__container.loading { - opacity: .5; -} - -.kbnDocTable { - th { - white-space: nowrap; - padding-right: $euiSizeS; - } -} - -.kbn-table, -.kbnDocTable { - /** - * Style ES document _source in table view
key:
value
- * Use alpha so this will stand out against non-white backgrounds, e.g. the highlighted - * row in the Context Log. - */ - - dl.source { - margin-bottom: 0; - line-height: 2em; - word-break: break-word; - - dt, - dd { - display: inline; - } - - dt { - background-color: transparentize(shade($euiColorPrimary, 20%), .9); - color: $euiTextColor; - padding: calc($euiSizeXS / 2) $euiSizeXS; - margin-right: $euiSizeXS; - word-break: normal; - border-radius: $euiBorderRadius; - } - } -} - -.kbnDocTable__row { - td { - position: relative; - - &:hover { - .kbnDocTableRowFilterButton { - opacity: 1; - } - } - } -} - -.kbnDocTable__row--highlight { - td, - .kbnDocTableRowFilterButton { - background-color: tintOrShade($euiColorPrimary, 90%, 70%); - } -} - -.kbnDocTable__error { - display: flex; - flex-direction: column; - justify-content: center; - flex: 1 0 100%; - text-align: center; -} - -.table { - // Nesting - .table { - background-color: $euiColorEmptyShade; - } -} - -.kbn-table { - // sub tables should not have a leading border - .table .table { - margin-bottom: 0; - - tr:first-child > td { - border-top: none; - } - - td.field-name { - font-weight: $euiFontWeightBold; - } - } -} - -.dscTruncateByHeight { - display: inline-block; -} diff --git a/src/plugins/discover/public/components/doc_table/components/_index.scss b/src/plugins/discover/public/components/doc_table/components/_index.scss deleted file mode 100644 index 6a294c1ed173d..0000000000000 --- a/src/plugins/discover/public/components/doc_table/components/_index.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import 'table_header'; -@import 'table_row/index'; diff --git a/src/plugins/discover/public/components/doc_table/components/_table_header.scss b/src/plugins/discover/public/components/doc_table/components/_table_header.scss deleted file mode 100644 index 3ea6fb5502764..0000000000000 --- a/src/plugins/discover/public/components/doc_table/components/_table_header.scss +++ /dev/null @@ -1,19 +0,0 @@ -.kbnDocTableHeader { - white-space: nowrap; -} -.kbnDocTableHeader button, -.kbnDocTableHeader svg { - margin-left: $euiSizeXS * .5; -} -.kbnDocTableHeader__move, -.kbnDocTableHeader__sortChange { - opacity: 0; - th:hover &, - &:focus { - opacity: 1; - } -} -.kbnDocTableHeader__actions { - display: flex; - align-items: center; -} diff --git a/src/plugins/discover/public/components/doc_table/components/pager/tool_bar_pagination.tsx b/src/plugins/discover/public/components/doc_table/components/pager/tool_bar_pagination.tsx deleted file mode 100644 index 9ab0530d56213..0000000000000 --- a/src/plugins/discover/public/components/doc_table/components/pager/tool_bar_pagination.tsx +++ /dev/null @@ -1,115 +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, { useState } from 'react'; -import { - EuiButtonEmpty, - EuiContextMenuItem, - EuiContextMenuPanel, - EuiFlexGroup, - EuiFlexItem, - EuiPagination, - EuiPopover, -} from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { i18n } from '@kbn/i18n'; -import { euiLightVars } from '@kbn/ui-theme'; -import { getRowsPerPageOptions } from '@kbn/unified-data-table'; - -export const MAX_ROWS_PER_PAGE_OPTION = 100; - -interface ToolBarPaginationProps { - pageSize: number; - pageCount: number; - activePage: number; - onPageClick: (page: number) => void; - onPageSizeChange: (size: number) => void; -} - -const TOOL_BAR_PAGINATION_STYLES = { - marginLeft: 'auto', - marginRight: euiLightVars.euiSizeL, -}; - -export const ToolBarPagination = ({ - pageSize, - pageCount, - activePage, - onPageSizeChange, - onPageClick, -}: ToolBarPaginationProps) => { - const [isPopoverOpen, setIsPopoverOpen] = useState(false); - const rowsWord = i18n.translate('discover.docTable.rows', { - defaultMessage: 'rows', - }); - - const onChooseRowsClick = () => setIsPopoverOpen((prevIsPopoverOpen) => !prevIsPopoverOpen); - - const closePopover = () => setIsPopoverOpen(false); - - const getIconType = (size: number) => { - return size === pageSize ? 'check' : 'empty'; - }; - - const rowsPerPageOptions = getRowsPerPageOptions(pageSize) - .filter((option) => option <= MAX_ROWS_PER_PAGE_OPTION) // legacy table is not optimized well for rendering more rows at once - .map((cur) => ( - { - closePopover(); - onPageSizeChange(cur); - }} - > - {cur} {rowsWord} - - )); - - return ( - - - - - - } - isOpen={isPopoverOpen} - closePopover={closePopover} - panelPaddingSize="none" - > - - - - - - - - - ); -}; diff --git a/src/plugins/discover/public/components/doc_table/components/table_header/__snapshots__/table_header.test.tsx.snap b/src/plugins/discover/public/components/doc_table/components/table_header/__snapshots__/table_header.test.tsx.snap deleted file mode 100644 index 2b44ac56c64ae..0000000000000 --- a/src/plugins/discover/public/components/doc_table/components/table_header/__snapshots__/table_header.test.tsx.snap +++ /dev/null @@ -1,373 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`TableHeader with time column renders correctly 1`] = ` - - - - - time - - - time - this field represents the time that events occurred. - - - - - - - - - - first - - - - - - - - - - - middle - - - - - - - - - - - - - - last - - - - - - - - - -`; - -exports[`TableHeader without time column renders correctly 1`] = ` - - - - - first - - - - - - - - - - - middle - - - - - - - - - - - - - - last - - - - - - - - - -`; diff --git a/src/plugins/discover/public/components/doc_table/components/table_header/helpers.tsx b/src/plugins/discover/public/components/doc_table/components/table_header/helpers.tsx deleted file mode 100644 index 6640164d76d50..0000000000000 --- a/src/plugins/discover/public/components/doc_table/components/table_header/helpers.tsx +++ /dev/null @@ -1,86 +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 { i18n } from '@kbn/i18n'; -import type { DataView } from '@kbn/data-views-plugin/public'; - -export interface ColumnProps { - name: string; - displayName: string; - isSortable: boolean; - isRemoveable: boolean; - colLeftIdx: number; - colRightIdx: number; -} - -/** - * Returns properties necessary to display the time column - * If it's an DataView with timefield, the time column is - * prepended, not moveable and removeable - * @param timeFieldName - */ -export function getTimeColumn(timeFieldName: string): ColumnProps { - return { - name: timeFieldName, - displayName: timeFieldName, - isSortable: true, - isRemoveable: false, - colLeftIdx: -1, - colRightIdx: -1, - }; -} -/** - * A given array of column names returns an array of properties - * necessary to display the columns. If the given dataView - * has a timefield, a time column is prepended - * @param columns - * @param dataView - * @param hideTimeField - * @param isShortDots - */ -export function getDisplayedColumns( - columns: string[], - dataView: DataView, - hideTimeField: boolean, - isShortDots: boolean -) { - if (!Array.isArray(columns) || typeof dataView !== 'object' || !dataView.getFieldByName) { - return []; - } - - const columnProps = - columns.length === 0 - ? [ - { - name: '__document__', - displayName: i18n.translate('discover.docTable.tableHeader.documentHeader', { - defaultMessage: 'Document', - }), - isSortable: false, - isRemoveable: false, - colLeftIdx: -1, - colRightIdx: -1, - }, - ] - : columns.map((column, idx) => { - const field = dataView.getFieldByName(column); - return { - name: column, - displayName: field?.displayName ?? column, - isSortable: !!(field && field.sortable), - isRemoveable: column !== '_source' || columns.length > 1, - colLeftIdx: idx - 1 < 0 ? -1 : idx - 1, - colRightIdx: idx + 1 >= columns.length ? -1 : idx + 1, - }; - }); - - return !hideTimeField && dataView.timeFieldName - ? [getTimeColumn(dataView.timeFieldName), ...columnProps] - : columnProps; -} diff --git a/src/plugins/discover/public/components/doc_table/components/table_header/score_sort_warning.tsx b/src/plugins/discover/public/components/doc_table/components/table_header/score_sort_warning.tsx deleted file mode 100644 index 188a92ea98579..0000000000000 --- a/src/plugins/discover/public/components/doc_table/components/table_header/score_sort_warning.tsx +++ /dev/null @@ -1,20 +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 { EuiIconTip } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; - -export function DocViewTableScoreSortWarning() { - const tooltipContent = i18n.translate('discover.docViews.table.scoreSortWarningTooltip', { - defaultMessage: 'In order to retrieve values for _score, you must sort by it.', - }); - - return ; -} diff --git a/src/plugins/discover/public/components/doc_table/components/table_header/table_header.test.tsx b/src/plugins/discover/public/components/doc_table/components/table_header/table_header.test.tsx deleted file mode 100644 index 7aa3988444388..0000000000000 --- a/src/plugins/discover/public/components/doc_table/components/table_header/table_header.test.tsx +++ /dev/null @@ -1,223 +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 { mountWithIntl } from '@kbn/test-jest-helpers'; -import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; -import type { SortOrder } from '@kbn/saved-search-plugin/public'; -import { TableHeader } from './table_header'; -import { findTestSubject } from '@elastic/eui/lib/test'; -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; -import { DOC_HIDE_TIME_COLUMN_SETTING } from '@kbn/discover-utils'; -import { FORMATS_UI_SETTINGS } from '@kbn/field-formats-plugin/common'; - -const defaultUiSettings = { - get: (key: string) => { - if (key === DOC_HIDE_TIME_COLUMN_SETTING) { - return false; - } else if (key === FORMATS_UI_SETTINGS.SHORT_DOTS_ENABLE) { - return false; - } - }, -}; - -function getMockDataView() { - return { - id: 'test', - title: 'Test', - timeFieldName: 'time', - fields: [], - isTimeNanosBased: () => false, - getFieldByName: (name: string) => { - if (name === 'test1') { - return { - name, - displayName: name, - type: 'string', - aggregatable: false, - searchable: true, - sortable: true, - } as DataViewField; - } else { - return { - name, - displayName: name, - type: 'string', - aggregatable: false, - searchable: true, - sortable: false, - } as DataViewField; - } - }, - } as unknown as DataView; -} - -function getMockProps(props = {}) { - const defaultProps = { - dataView: getMockDataView(), - hideTimeColumn: false, - columns: ['first', 'middle', 'last'], - defaultSortOrder: 'desc', - sortOrder: [['time', 'asc']] as SortOrder[], - isShortDots: true, - onRemoveColumn: jest.fn(), - onChangeSortOrder: jest.fn(), - onMoveColumn: jest.fn(), - onPageNext: jest.fn(), - onPagePrevious: jest.fn(), - }; - - return Object.assign({}, defaultProps, props); -} - -describe('TableHeader with time column', () => { - const props = getMockProps(); - - const wrapper = mountWithIntl( - - - - - -
-
- ); - - test('renders correctly', () => { - const docTableHeader = findTestSubject(wrapper, 'docTableHeader'); - expect(docTableHeader.getDOMNode()).toMatchSnapshot(); - }); - - test('time column is sortable with button, cycling sort direction', () => { - findTestSubject(wrapper, 'docTableHeaderFieldSort_time').simulate('click'); - expect(props.onChangeSortOrder).toHaveBeenCalledWith([['time', 'desc']]); - }); - - test('time column is not removeable, no button displayed', () => { - const removeButton = findTestSubject(wrapper, 'docTableRemoveHeader-time'); - expect(removeButton.length).toBe(0); - }); - - test('time column is not moveable, no button displayed', () => { - const moveButtonLeft = findTestSubject(wrapper, 'docTableMoveLeftHeader-time'); - expect(moveButtonLeft.length).toBe(0); - const moveButtonRight = findTestSubject(wrapper, 'docTableMoveRightHeader-time'); - expect(moveButtonRight.length).toBe(0); - }); - - test('first column is removeable', () => { - const removeButton = findTestSubject(wrapper, 'docTableRemoveHeader-first'); - expect(removeButton.length).toBe(1); - removeButton.simulate('click'); - expect(props.onRemoveColumn).toHaveBeenCalledWith('first'); - }); - - test('first column is not moveable to the left', () => { - const moveButtonLeft = findTestSubject(wrapper, 'docTableMoveLeftHeader-first'); - expect(moveButtonLeft.length).toBe(0); - }); - - test('first column is moveable to the right', () => { - const moveButtonRight = findTestSubject(wrapper, 'docTableMoveRightHeader-first'); - expect(moveButtonRight.length).toBe(1); - moveButtonRight.simulate('click'); - expect(props.onMoveColumn).toHaveBeenCalledWith('first', 1); - }); - - test('middle column is moveable to the left', () => { - const moveButtonLeft = findTestSubject(wrapper, 'docTableMoveLeftHeader-middle'); - expect(moveButtonLeft.length).toBe(1); - moveButtonLeft.simulate('click'); - expect(props.onMoveColumn).toHaveBeenCalledWith('middle', 0); - }); - - test('middle column is moveable to the right', () => { - const moveButtonRight = findTestSubject(wrapper, 'docTableMoveRightHeader-middle'); - expect(moveButtonRight.length).toBe(1); - moveButtonRight.simulate('click'); - expect(props.onMoveColumn).toHaveBeenCalledWith('middle', 2); - }); - - test('last column moveable to the left', () => { - const moveButtonLeft = findTestSubject(wrapper, 'docTableMoveLeftHeader-last'); - expect(moveButtonLeft.length).toBe(1); - moveButtonLeft.simulate('click'); - expect(props.onMoveColumn).toHaveBeenCalledWith('last', 1); - }); -}); - -describe('TableHeader without time column', () => { - const props = getMockProps({ hideTimeColumn: true }); - - const wrapper = mountWithIntl( - { - if (key === DOC_HIDE_TIME_COLUMN_SETTING) { - return true; - } - }, - }, - }} - > - - - - -
-
- ); - - test('renders correctly', () => { - const docTableHeader = findTestSubject(wrapper, 'docTableHeader'); - expect(docTableHeader.getDOMNode()).toMatchSnapshot(); - }); - - test('first column is removeable', () => { - const removeButton = findTestSubject(wrapper, 'docTableRemoveHeader-first'); - expect(removeButton.length).toBe(1); - removeButton.simulate('click'); - expect(props.onRemoveColumn).toHaveBeenCalledWith('first'); - }); - - test('first column is not moveable to the left', () => { - const moveButtonLeft = findTestSubject(wrapper, 'docTableMoveLeftHeader-first'); - expect(moveButtonLeft.length).toBe(0); - }); - - test('first column is moveable to the right', () => { - const moveButtonRight = findTestSubject(wrapper, 'docTableMoveRightHeader-first'); - expect(moveButtonRight.length).toBe(1); - moveButtonRight.simulate('click'); - expect(props.onMoveColumn).toHaveBeenCalledWith('first', 1); - }); - - test('middle column is moveable to the left', () => { - const moveButtonLeft = findTestSubject(wrapper, 'docTableMoveLeftHeader-middle'); - expect(moveButtonLeft.length).toBe(1); - moveButtonLeft.simulate('click'); - expect(props.onMoveColumn).toHaveBeenCalledWith('middle', 0); - }); - - test('middle column is moveable to the right', () => { - const moveButtonRight = findTestSubject(wrapper, 'docTableMoveRightHeader-middle'); - expect(moveButtonRight.length).toBe(1); - moveButtonRight.simulate('click'); - expect(props.onMoveColumn).toHaveBeenCalledWith('middle', 2); - }); - - test('last column moveable to the left', () => { - const moveButtonLeft = findTestSubject(wrapper, 'docTableMoveLeftHeader-last'); - expect(moveButtonLeft.length).toBe(1); - moveButtonLeft.simulate('click'); - expect(props.onMoveColumn).toHaveBeenCalledWith('last', 1); - }); -}); diff --git a/src/plugins/discover/public/components/doc_table/components/table_header/table_header.tsx b/src/plugins/discover/public/components/doc_table/components/table_header/table_header.tsx deleted file mode 100644 index 4a3e1e1897a9f..0000000000000 --- a/src/plugins/discover/public/components/doc_table/components/table_header/table_header.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, { useMemo } from 'react'; -import type { DataView } from '@kbn/data-views-plugin/public'; -import { FORMATS_UI_SETTINGS } from '@kbn/field-formats-plugin/common'; -import type { SortOrder } from '@kbn/saved-search-plugin/public'; -import { DOC_HIDE_TIME_COLUMN_SETTING, SORT_DEFAULT_ORDER_SETTING } from '@kbn/discover-utils'; -import { TableHeaderColumn } from './table_header_column'; -import { getDisplayedColumns } from './helpers'; -import { getDefaultSort } from '../../../../utils/sorting'; -import { useDiscoverServices } from '../../../../hooks/use_discover_services'; - -interface Props { - columns: string[]; - dataView: DataView; - onChangeSortOrder?: (sortOrder: SortOrder[]) => void; - onMoveColumn?: (name: string, index: number) => void; - onRemoveColumn?: (name: string) => void; - sortOrder: SortOrder[]; -} - -export function TableHeader({ - columns, - dataView, - onChangeSortOrder, - onMoveColumn, - onRemoveColumn, - sortOrder, -}: Props) { - const { uiSettings } = useDiscoverServices(); - const [defaultSortOrder, hideTimeColumn, isShortDots] = useMemo( - () => [ - uiSettings.get(SORT_DEFAULT_ORDER_SETTING, 'desc'), - uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false), - uiSettings.get(FORMATS_UI_SETTINGS.SHORT_DOTS_ENABLE), - ], - [uiSettings] - ); - const displayedColumns = getDisplayedColumns(columns, dataView, hideTimeColumn, isShortDots); - - return ( - - - {displayedColumns.map((col, index) => { - return ( - - ); - })} - - ); -} diff --git a/src/plugins/discover/public/components/doc_table/components/table_header/table_header_column.tsx b/src/plugins/discover/public/components/doc_table/components/table_header/table_header_column.tsx deleted file mode 100644 index cbb1cb598f2c5..0000000000000 --- a/src/plugins/discover/public/components/doc_table/components/table_header/table_header_column.tsx +++ /dev/null @@ -1,234 +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 { i18n } from '@kbn/i18n'; -import { EuiButtonIcon, EuiToolTip, EuiIconTip } from '@elastic/eui'; -import type { SortOrder } from '@kbn/saved-search-plugin/public'; -import { DocViewTableScoreSortWarning } from './score_sort_warning'; - -interface Props { - colLeftIdx: number; // idx of the column to the left, -1 if moving is not possible - colRightIdx: number; // idx of the column to the right, -1 if moving is not possible - displayName?: string; - isRemoveable: boolean; - isSortable: boolean; - isTimeColumn: boolean; - customLabel?: string; - name: string; - onChangeSortOrder?: (sortOrder: SortOrder[]) => void; - onMoveColumn?: (name: string, idx: number) => void; - onRemoveColumn?: (name: string) => void; - sortOrder: SortOrder[]; -} - -interface IconProps { - iconType: string; - color: 'primary' | 'text'; -} - -interface IconButtonProps { - active: boolean; - ariaLabel: string; - className: string; - iconProps: IconProps; - onClick: () => void | undefined; - testSubject: string; - tooltip: string; -} - -const sortDirectionToIcon: Record = { - desc: { iconType: 'sortDown', color: 'primary' }, - asc: { iconType: 'sortUp', color: 'primary' }, - '': { iconType: 'sortable', color: 'text' }, -}; - -const ICON_BUTTON_STYLE = { width: 12, height: 12 }; - -export function TableHeaderColumn({ - colLeftIdx, - colRightIdx, - displayName, - isRemoveable, - isSortable, - isTimeColumn, - customLabel, - name, - onChangeSortOrder, - onMoveColumn, - onRemoveColumn, - sortOrder, -}: Props) { - const [, sortDirection = ''] = sortOrder.find((sortPair) => name === sortPair[0]) || []; - const curSortWithoutCol = sortOrder.filter((pair) => pair[0] !== name); - const curColSort = sortOrder.find((pair) => pair[0] === name); - const curColSortDir = (curColSort && curColSort[1]) || ''; - - const fieldName = customLabel ?? displayName; - const timeAriaLabel = i18n.translate( - 'discover.docTable.tableHeader.timeFieldIconTooltipAriaLabel', - { - defaultMessage: '{timeFieldName} - this field represents the time that events occurred.', - values: { timeFieldName: fieldName }, - } - ); - const timeTooltip = i18n.translate('discover.docTable.tableHeader.timeFieldIconTooltip', { - defaultMessage: 'This field represents the time that events occurred.', - }); - - // If this is the _score column, and _score is not one of the columns inside the sort, show a - // warning that the _score will not be retrieved from Elasticsearch - const showScoreSortWarning = name === '_score' && !curColSort; - - const handleChangeSortOrder = () => { - if (!onChangeSortOrder) return; - - // Cycle goes Unsorted -> Asc -> Desc -> Unsorted - if (curColSort === undefined) { - onChangeSortOrder([...curSortWithoutCol, [name, 'asc']]); - } else if (curColSortDir === 'asc') { - onChangeSortOrder([...curSortWithoutCol, [name, 'desc']]); - } else if (curColSortDir === 'desc' && curSortWithoutCol.length === 0) { - // If we're at the end of the cycle and this is the only existing sort, we switch - // back to ascending sort instead of removing it. - onChangeSortOrder([[name, 'asc']]); - } else { - onChangeSortOrder(curSortWithoutCol); - } - }; - - const getSortButtonAriaLabel = () => { - const sortAscendingMessage = i18n.translate( - 'discover.docTable.tableHeader.sortByColumnAscendingAriaLabel', - { - defaultMessage: 'Sort {columnName} ascending', - values: { columnName: name }, - } - ); - const sortDescendingMessage = i18n.translate( - 'discover.docTable.tableHeader.sortByColumnDescendingAriaLabel', - { - defaultMessage: 'Sort {columnName} descending', - values: { columnName: name }, - } - ); - const stopSortingMessage = i18n.translate( - 'discover.docTable.tableHeader.sortByColumnUnsortedAriaLabel', - { - defaultMessage: 'Stop sorting on {columnName}', - values: { columnName: name }, - } - ); - - if (curColSort === undefined) { - return sortAscendingMessage; - } else if (sortDirection === 'asc') { - return sortDescendingMessage; - } else if (sortDirection === 'desc' && curSortWithoutCol.length === 0) { - return sortAscendingMessage; - } else { - return stopSortingMessage; - } - }; - - // action buttons displayed on the right side of the column name - const buttons: IconButtonProps[] = [ - // Sort Button - { - active: isSortable && typeof onChangeSortOrder === 'function', - ariaLabel: getSortButtonAriaLabel(), - className: !sortDirection ? 'kbnDocTableHeader__sortChange' : '', - iconProps: sortDirectionToIcon[sortDirection], - onClick: handleChangeSortOrder, - testSubject: `docTableHeaderFieldSort_${name}`, - tooltip: getSortButtonAriaLabel(), - }, - // Remove Button - { - active: isRemoveable && typeof onRemoveColumn === 'function', - ariaLabel: i18n.translate('discover.docTable.tableHeader.removeColumnButtonAriaLabel', { - defaultMessage: 'Remove {columnName} column', - values: { columnName: name }, - }), - className: 'kbnDocTableHeader__move', - iconProps: { iconType: 'cross', color: 'text' }, - onClick: () => onRemoveColumn && onRemoveColumn(name), - testSubject: `docTableRemoveHeader-${name}`, - tooltip: i18n.translate('discover.docTable.tableHeader.removeColumnButtonTooltip', { - defaultMessage: 'Remove Column', - }), - }, - // Move Left Button - { - active: colLeftIdx >= 0 && typeof onMoveColumn === 'function', - ariaLabel: i18n.translate('discover.docTable.tableHeader.moveColumnLeftButtonAriaLabel', { - defaultMessage: 'Move {columnName} column to the left', - values: { columnName: name }, - }), - className: 'kbnDocTableHeader__move', - iconProps: { iconType: 'sortLeft', color: 'text' }, - onClick: () => onMoveColumn && onMoveColumn(name, colLeftIdx), - testSubject: `docTableMoveLeftHeader-${name}`, - tooltip: i18n.translate('discover.docTable.tableHeader.moveColumnLeftButtonTooltip', { - defaultMessage: 'Move column to the left', - }), - }, - // Move Right Button - { - active: colRightIdx >= 0 && typeof onMoveColumn === 'function', - ariaLabel: i18n.translate('discover.docTable.tableHeader.moveColumnRightButtonAriaLabel', { - defaultMessage: 'Move {columnName} column to the right', - values: { columnName: name }, - }), - className: 'kbnDocTableHeader__move', - iconProps: { iconType: 'sortRight', color: 'text' }, - onClick: () => onMoveColumn && onMoveColumn(name, colRightIdx), - testSubject: `docTableMoveRightHeader-${name}`, - tooltip: i18n.translate('discover.docTable.tableHeader.moveColumnRightButtonTooltip', { - defaultMessage: 'Move column to the right', - }), - }, - ]; - - return ( - - - {showScoreSortWarning && } - {fieldName} - {isTimeColumn && ( - - )} - {buttons - .filter((button) => button.active) - .map((button, idx) => ( - - - - ))} - - - ); -} diff --git a/src/plugins/discover/public/components/doc_table/components/table_row.test.tsx b/src/plugins/discover/public/components/doc_table/components/table_row.test.tsx deleted file mode 100644 index 3452da4abc57e..0000000000000 --- a/src/plugins/discover/public/components/doc_table/components/table_row.test.tsx +++ /dev/null @@ -1,141 +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 { mountWithIntl, findTestSubject } from '@kbn/test-jest-helpers'; -import { TableRow, TableRowProps } from './table_row'; -import { createFilterManagerMock } from '@kbn/data-plugin/public/query/filter_manager/filter_manager.mock'; -import { dataViewWithTimefieldMock } from '../../../__mocks__/data_view_with_timefield'; -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; -import { discoverServiceMock } from '../../../__mocks__/services'; - -import { DOC_HIDE_TIME_COLUMN_SETTING, MAX_DOC_FIELDS_DISPLAYED } from '@kbn/discover-utils'; -import { buildDataTableRecord } from '@kbn/discover-utils'; -import type { EsHitRecord } from '@kbn/discover-utils/types'; - -jest.mock('../utils/row_formatter', () => { - const originalModule = jest.requireActual('../utils/row_formatter'); - return { - ...originalModule, - formatRow: () => { - return mocked_document_cell; - }, - }; -}); - -const mountComponent = (props: TableRowProps) => { - return mountWithIntl( - { - if (key === DOC_HIDE_TIME_COLUMN_SETTING) { - return true; - } else if (key === MAX_DOC_FIELDS_DISPLAYED) { - return 100; - } - }, - }, - }} - > - - - - -
-
- ); -}; - -const mockHit = { - _index: 'mock_index', - _id: '1', - _score: 1, - _type: '_doc', - fields: [ - { - timestamp: '2020-20-01T12:12:12.123', - }, - ], - _source: { message: 'mock_message', bytes: 20 }, -} as unknown as EsHitRecord; - -const mockFilterManager = createFilterManagerMock(); - -describe('Doc table row component', () => { - const mockInlineFilter = jest.fn(); - const defaultProps = { - columns: ['_source'], - filter: mockInlineFilter, - dataView: dataViewWithTimefieldMock, - row: buildDataTableRecord(mockHit, dataViewWithTimefieldMock), - useNewFieldsApi: true, - filterManager: mockFilterManager, - addBasePath: (path: string) => path, - } as unknown as TableRowProps; - - it('should render __document__ column', () => { - const component = mountComponent({ ...defaultProps, columns: [] }); - const docTableField = findTestSubject(component, 'docTableField'); - expect(docTableField.first().text()).toBe('mocked_document_cell'); - }); - - it('should render message, _index and bytes fields', () => { - const component = mountComponent({ ...defaultProps, columns: ['message', '_index', 'bytes'] }); - - const fields = findTestSubject(component, 'docTableField'); - expect(fields.first().text()).toBe('mock_message'); - expect(fields.last().text()).toBe('20'); - expect(fields.length).toBe(3); - }); - - it('should apply filter when pressed', () => { - const component = mountComponent({ ...defaultProps, columns: ['bytes'] }); - - const fields = findTestSubject(component, 'docTableField'); - expect(fields.first().text()).toBe('20'); - - const filterInButton = findTestSubject(component, 'docTableCellFilter'); - filterInButton.simulate('click'); - expect(mockInlineFilter).toHaveBeenCalledWith( - dataViewWithTimefieldMock.getFieldByName('bytes'), - 20, - '+' - ); - }); - - describe('details row', () => { - it('should be empty by default', () => { - const component = mountComponent(defaultProps); - expect(findTestSubject(component, 'docViewerRowDetailsTitle').exists()).toBeFalsy(); - }); - - it('should expand the detail row when the toggle arrow is clicked', () => { - const component = mountComponent(defaultProps); - const toggleButton = findTestSubject(component, 'docTableExpandToggleColumn'); - - expect(findTestSubject(component, 'docViewerRowDetailsTitle').exists()).toBeFalsy(); - toggleButton.simulate('click'); - expect(findTestSubject(component, 'docViewerRowDetailsTitle').exists()).toBeTruthy(); - }); - - it('should hide the single/surrounding views for ES|QL mode', () => { - const props = { - ...defaultProps, - isEsqlMode: true, - }; - const component = mountComponent(props); - const toggleButton = findTestSubject(component, 'docTableExpandToggleColumn'); - toggleButton.simulate('click'); - expect(findTestSubject(component, 'docViewerRowDetailsTitle').text()).toBe('Expanded result'); - expect(findTestSubject(component, 'docTableRowAction').length).toBeFalsy(); - }); - }); -}); diff --git a/src/plugins/discover/public/components/doc_table/components/table_row.tsx b/src/plugins/discover/public/components/doc_table/components/table_row.tsx deleted file mode 100644 index 40a3a81499a1d..0000000000000 --- a/src/plugins/discover/public/components/doc_table/components/table_row.tsx +++ /dev/null @@ -1,239 +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, { Fragment, useCallback, useMemo, useState } from 'react'; -import classNames from 'classnames'; -import { i18n } from '@kbn/i18n'; -import { EuiButtonEmpty, EuiIcon } from '@elastic/eui'; -import { DataView } from '@kbn/data-views-plugin/public'; -import { Filter } from '@kbn/es-query'; -import type { - DataTableRecord, - EsHitRecord, - ShouldShowFieldInTableHandler, -} from '@kbn/discover-utils/types'; -import { formatFieldValue } from '@kbn/discover-utils'; -import { DOC_HIDE_TIME_COLUMN_SETTING, MAX_DOC_FIELDS_DISPLAYED } from '@kbn/discover-utils'; -import type { DocViewFilterFn } from '@kbn/unified-doc-viewer/types'; -import { UnifiedDocViewer } from '@kbn/unified-doc-viewer-plugin/public'; -import { TableCell } from './table_row/table_cell'; -import { formatRow, formatTopLevelObject } from '../utils/row_formatter'; -import { TableRowDetails } from './table_row_details'; -import { useDiscoverServices } from '../../../hooks/use_discover_services'; - -export type DocTableRow = EsHitRecord & { - isAnchor?: boolean; -}; - -export interface TableRowProps { - columns: string[]; - filter?: DocViewFilterFn; - filters?: Filter[]; - isEsqlMode?: boolean; - savedSearchId?: string; - row: DataTableRecord; - rows: DataTableRecord[]; - dataView: DataView; - useNewFieldsApi: boolean; - shouldShowFieldHandler: ShouldShowFieldInTableHandler; - onAddColumn?: (column: string) => void; - onRemoveColumn?: (column: string) => void; -} - -export const TableRow = ({ - filters, - isEsqlMode, - columns, - filter, - savedSearchId, - row, - rows, - dataView, - useNewFieldsApi, - shouldShowFieldHandler, - onAddColumn, - onRemoveColumn, -}: TableRowProps) => { - const { uiSettings, fieldFormats } = useDiscoverServices(); - const [maxEntries, hideTimeColumn] = useMemo( - () => [ - uiSettings.get(MAX_DOC_FIELDS_DISPLAYED), - uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false), - ], - [uiSettings] - ); - const [open, setOpen] = useState(false); - const docTableRowClassName = classNames('kbnDocTable__row', { - 'kbnDocTable__row--highlight': row.isAnchor, - }); - const anchorDocTableRowSubj = row.isAnchor ? ' docTableAnchorRow' : ''; - - const mapping = useMemo(() => dataView.fields.getByName, [dataView]); - - // toggle display of the rows details, a full list of the fields from each row - const toggleRow = () => setOpen((prevOpen) => !prevOpen); - - /** - * Fill an element with the value of a field - */ - const displayField = (fieldName: string) => { - // If we're formatting the _source column, don't use the regular field formatter, - // but our Discover mechanism to format a hit in a better human-readable way. - if (fieldName === '_source') { - return formatRow(row, dataView, shouldShowFieldHandler, maxEntries, fieldFormats); - } - - const formattedField = formatFieldValue( - row.flattened[fieldName], - row.raw, - fieldFormats, - dataView, - mapping(fieldName) - ); - - return ( - // formatFieldValue always returns sanitized HTML - // eslint-disable-next-line react/no-danger -
- ); - }; - const inlineFilter = useCallback( - (column: string, type: '+' | '-') => { - const field = dataView.fields.getByName(column); - filter?.(field!, row.flattened[column], type); - }, - [filter, dataView.fields, row.flattened] - ); - - const rowCells = [ - - - {open ? ( - - ) : ( - - )} - - , - ]; - - if (dataView.timeFieldName && !hideTimeColumn) { - rowCells.push( - - ); - } - - if (columns.length === 0 && useNewFieldsApi) { - const formatted = formatRow(row, dataView, shouldShowFieldHandler, maxEntries, fieldFormats); - - rowCells.push( - - ); - } else { - columns.forEach(function (column: string, index) { - const cellKey = `${column}-${index}`; - if (useNewFieldsApi && !mapping(column) && row.raw.fields && !row.raw.fields[column]) { - const innerColumns = Object.fromEntries( - Object.entries(row.raw.fields).filter(([key]) => { - return key.indexOf(`${column}.`) === 0; - }) - ); - - rowCells.push( - - ); - } else { - // Check whether the field is defined as filterable in the mapping and does - // NOT have ignored values in it to determine whether we want to allow filtering. - // We should improve this and show a helpful tooltip why the filter buttons are not - // there/disabled when there are ignored values. - const isFilterable = Boolean( - mapping(column)?.filterable && - typeof filter === 'function' && - !row.raw._ignored?.includes(column) - ); - rowCells.push( - - ); - } - }); - } - - return ( - - - {rowCells} - - - {open && ( - - - - )} - - - ); -}; diff --git a/src/plugins/discover/public/components/doc_table/components/table_row/__snapshots__/table_cell.test.tsx.snap b/src/plugins/discover/public/components/doc_table/components/table_row/__snapshots__/table_cell.test.tsx.snap deleted file mode 100644 index b4f03267ec810..0000000000000 --- a/src/plugins/discover/public/components/doc_table/components/table_row/__snapshots__/table_cell.test.tsx.snap +++ /dev/null @@ -1,369 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Doc table cell component renders a cell with filter buttons if it is filterable 1`] = ` - - formatted content - - } - inlineFilter={[Function]} - sourcefield={false} - timefield={true} -> - - - formatted content - - - - - - - - - - , - "ctr": 2, - "insertionPoint": undefined, - "isSpeedy": false, - "key": "css", - "nonce": undefined, - "prepend": undefined, - "tags": Array [ - , - , - ], - }, - } - } - isStringTag={true} - serialized={ - Object { - "map": undefined, - "name": "jcaat8-euiToolTipAnchor-inlineBlock", - "next": undefined, - "styles": "*[disabled]{pointer-events:none;};label:euiToolTipAnchor;;;display:inline-block;label:inlineBlock;;;", - "toString": [Function], - } - } - /> - - - - - - - - - - - - - , - "ctr": 2, - "insertionPoint": undefined, - "isSpeedy": false, - "key": "css", - "nonce": undefined, - "prepend": undefined, - "tags": Array [ - , - , - ], - }, - } - } - isStringTag={true} - serialized={ - Object { - "map": undefined, - "name": "jcaat8-euiToolTipAnchor-inlineBlock", - "next": undefined, - "styles": "*[disabled]{pointer-events:none;};label:euiToolTipAnchor;;;display:inline-block;label:inlineBlock;;;", - "toString": [Function], - } - } - /> - - - - - - - - - - -`; - -exports[`Doc table cell component renders a cell without filter buttons if it is not filterable 1`] = ` - - formatted content - - } - inlineFilter={[Function]} - sourcefield={false} - timefield={true} -> - - - formatted content - - - - -`; - -exports[`Doc table cell component renders a field that is neither a timefield or sourcefield 1`] = ` - - formatted content - - } - inlineFilter={[Function]} - sourcefield={false} - timefield={false} -> - - - formatted content - - - - -`; - -exports[`Doc table cell component renders a sourcefield 1`] = ` - - formatted content - - } - inlineFilter={[Function]} - sourcefield={true} - timefield={false} -> - - - formatted content - - - - -`; diff --git a/src/plugins/discover/public/components/doc_table/components/table_row/_cell.scss b/src/plugins/discover/public/components/doc_table/components/table_row/_cell.scss deleted file mode 100644 index 2e643c195208c..0000000000000 --- a/src/plugins/discover/public/components/doc_table/components/table_row/_cell.scss +++ /dev/null @@ -1,47 +0,0 @@ -.kbnDocTableCell__dataField { - white-space: pre-wrap; -} - -.kbnDocTableCell__toggleDetails { - padding: $euiSizeXS 0 0 0 !important; -} - -/** - * Fixes time column width in Firefox after toggle display of the rows details. - * Described issue - https://github.com/elastic/kibana/pull/104361#issuecomment-894271241 - */ -.kbnDocTableCell--extraWidth { - width: 1%; -} - -.kbnDocTableCell__filter { - position: absolute; - white-space: nowrap; - right: 0; -} - -.kbnDocTableCell__filterButton { - font-size: $euiFontSizeXS; - padding: $euiSizeXS; -} - -/** - * 1. Align icon with text in cell. - * 2. Use opacity to make this element accessible to screen readers and keyboard. - * 3. Show on focus to enable keyboard accessibility. - */ - -.kbnDocTableRowFilterButton { - appearance: none; - background-color: $euiColorEmptyShade; - border: none; - padding: 0 $euiSizeXS; - font-size: $euiFontSizeS; - line-height: 1; /* 1 */ - display: inline-block; - opacity: 0; /* 2 */ - - &:focus { - opacity: 1; /* 3 */ - } -} diff --git a/src/plugins/discover/public/components/doc_table/components/table_row/_details.scss b/src/plugins/discover/public/components/doc_table/components/table_row/_details.scss deleted file mode 100644 index cc6ccfe9fd29f..0000000000000 --- a/src/plugins/discover/public/components/doc_table/components/table_row/_details.scss +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 1. Visually align the actions with the tabs. We can improve this by using flexbox instead, at a later point. - */ -.kbnDocTableDetails__actions { - float: right; - padding-top: $euiSizeS; /* 1 */ -} - -// Overwrite the border on the bootstrap table -.kbnDocTableDetails__row { - - > td { - // Offsets negative margins from an inner flex group - padding: $euiSizeL !important; - - tr:hover td { - background: tintOrShade($euiColorLightestShade, 50%, 20%); - } - } - - td { - border-top: none !important; - } -} diff --git a/src/plugins/discover/public/components/doc_table/components/table_row/_index.scss b/src/plugins/discover/public/components/doc_table/components/table_row/_index.scss deleted file mode 100644 index c7ccdaa39ff65..0000000000000 --- a/src/plugins/discover/public/components/doc_table/components/table_row/_index.scss +++ /dev/null @@ -1,3 +0,0 @@ -@import 'cell'; -@import 'details'; -@import 'open'; diff --git a/src/plugins/discover/public/components/doc_table/components/table_row/_open.scss b/src/plugins/discover/public/components/doc_table/components/table_row/_open.scss deleted file mode 100644 index 659481e0968b5..0000000000000 --- a/src/plugins/discover/public/components/doc_table/components/table_row/_open.scss +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 1. When switching between open and closed, the toggle changes size - * slightly which is a problem because it forces the entire table to - * re-render which is SLOW. - */ - -.kbnDocTableOpen__button { - appearance: none; - background-color: transparent; - padding: 0; - border: none; - width: 14px; /* 1 */ - height: 14px; -} diff --git a/src/plugins/discover/public/components/doc_table/components/table_row/table_cell.test.tsx b/src/plugins/discover/public/components/doc_table/components/table_row/table_cell.test.tsx deleted file mode 100644 index ed8d05d87949a..0000000000000 --- a/src/plugins/discover/public/components/doc_table/components/table_row/table_cell.test.tsx +++ /dev/null @@ -1,65 +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 { mount } from 'enzyme'; -import { CellProps, TableCell } from './table_cell'; - -const mountComponent = (props: Omit) => { - return mount( {}} />); -}; - -describe('Doc table cell component', () => { - test('renders a cell without filter buttons if it is not filterable', () => { - const component = mountComponent({ - filterable: false, - column: 'foo', - timefield: true, - sourcefield: false, - formatted: formatted content, - }); - expect(component).toMatchSnapshot(); - }); - - it('renders a cell with filter buttons if it is filterable', () => { - expect( - mountComponent({ - filterable: true, - column: 'foo', - timefield: true, - sourcefield: false, - formatted: formatted content, - }) - ).toMatchSnapshot(); - }); - - it('renders a sourcefield', () => { - expect( - mountComponent({ - filterable: false, - column: 'foo', - timefield: false, - sourcefield: true, - formatted: formatted content, - }) - ).toMatchSnapshot(); - }); - - it('renders a field that is neither a timefield or sourcefield', () => { - expect( - mountComponent({ - filterable: false, - column: 'foo', - timefield: false, - sourcefield: false, - formatted: formatted content, - }) - ).toMatchSnapshot(); - }); -}); diff --git a/src/plugins/discover/public/components/doc_table/components/table_row/table_cell.tsx b/src/plugins/discover/public/components/doc_table/components/table_row/table_cell.tsx deleted file mode 100644 index 83f60dd50587c..0000000000000 --- a/src/plugins/discover/public/components/doc_table/components/table_row/table_cell.tsx +++ /dev/null @@ -1,43 +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 classNames from 'classnames'; -import { TableCellActions } from './table_cell_actions'; -export interface CellProps { - timefield: boolean; - sourcefield?: boolean; - formatted: JSX.Element; - filterable: boolean; - column: string; - inlineFilter: (column: string, type: '+' | '-') => void; -} - -export const TableCell = (props: CellProps) => { - const classes = classNames({ - ['eui-textNoWrap kbnDocTableCell--extraWidth']: props.timefield, - ['eui-textBreakAll eui-textBreakWord']: props.sourcefield, - ['kbnDocTableCell__dataField eui-textBreakAll eui-textBreakWord']: - !props.timefield && !props.sourcefield, - }); - - const handleFilterFor = () => props.inlineFilter(props.column, '+'); - const handleFilterOut = () => props.inlineFilter(props.column, '-'); - - return ( - - {props.formatted} - {props.filterable ? ( - - ) : ( - - )} - - ); -}; diff --git a/src/plugins/discover/public/components/doc_table/components/table_row/table_cell_actions.tsx b/src/plugins/discover/public/components/doc_table/components/table_row/table_cell_actions.tsx deleted file mode 100644 index cc096bd1cafd5..0000000000000 --- a/src/plugins/discover/public/components/doc_table/components/table_row/table_cell_actions.tsx +++ /dev/null @@ -1,61 +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 { EuiIcon, EuiToolTip } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; - -interface TableCellActionsProps { - handleFilterFor: () => void; - handleFilterOut: () => void; -} - -export const TableCellActions = ({ handleFilterFor, handleFilterOut }: TableCellActionsProps) => { - return ( - - - - - - - - - - ); -}; diff --git a/src/plugins/discover/public/components/doc_table/components/table_row_details.tsx b/src/plugins/discover/public/components/doc_table/components/table_row_details.tsx deleted file mode 100644 index eefb0f638935f..0000000000000 --- a/src/plugins/discover/public/components/doc_table/components/table_row_details.tsx +++ /dev/null @@ -1,127 +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 { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiButtonEmpty, EuiTitle } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import type { DataView } from '@kbn/data-views-plugin/public'; -import type { Filter } from '@kbn/es-query'; -import { useNavigationProps } from '../../../hooks/use_navigation_props'; - -interface TableRowDetailsProps { - children: JSX.Element; - colLength: number; - rowIndex: string | undefined; - rowId: string | undefined; - columns: string[]; - isTimeBased: boolean; - dataView: DataView; - filters?: Filter[]; - savedSearchId?: string; - isEsqlMode?: boolean; -} - -export const TableRowDetails = ({ - colLength, - isTimeBased, - children, - dataView, - rowIndex, - rowId, - columns, - filters, - savedSearchId, - isEsqlMode, -}: TableRowDetailsProps) => { - const { singleDocHref, contextViewHref, onOpenSingleDoc, onOpenContextView } = useNavigationProps( - { - dataView, - rowIndex, - rowId, - columns, - filters, - savedSearchId, - } - ); - - return ( - - - - - - - - - -

- {isEsqlMode && ( - - )} - {!isEsqlMode && ( - - )} -

-
-
-
-
- {!isEsqlMode && ( - - - - {isTimeBased && ( - /* eslint-disable-next-line @elastic/eui/href-or-on-click */ - - - - )} - - - {/* eslint-disable-next-line @elastic/eui/href-or-on-click */} - - - - - - - )} -
-
{children}
- - ); -}; diff --git a/src/plugins/discover/public/components/doc_table/create_doc_table_embeddable.tsx b/src/plugins/discover/public/components/doc_table/create_doc_table_embeddable.tsx deleted file mode 100644 index 9d12c2fba3377..0000000000000 --- a/src/plugins/discover/public/components/doc_table/create_doc_table_embeddable.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 { DocTableEmbeddable, DocTableEmbeddableProps } from './doc_table_embeddable'; - -export function DiscoverDocTableEmbeddable(renderProps: DocTableEmbeddableProps) { - return ( - - ); -} diff --git a/src/plugins/discover/public/components/doc_table/doc_table_context.tsx b/src/plugins/discover/public/components/doc_table/doc_table_context.tsx deleted file mode 100644 index c1e685d1d8048..0000000000000 --- a/src/plugins/discover/public/components/doc_table/doc_table_context.tsx +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 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, { Fragment } from 'react'; -import './index.scss'; -import { SkipBottomButton } from '../../application/main/components/skip_bottom_button'; -import { DocTableProps, DocTableRenderProps, DocTableWrapper } from './doc_table_wrapper'; - -const DocTableWrapperMemoized = React.memo(DocTableWrapper); - -const renderDocTable = (tableProps: DocTableRenderProps) => { - return ( - - - - {tableProps.renderHeader()} - {tableProps.renderRows(tableProps.rows)} -
- - ​ - -
- ); -}; - -export const DocTableContext = (props: DocTableProps) => { - return ; -}; diff --git a/src/plugins/discover/public/components/doc_table/doc_table_embeddable.tsx b/src/plugins/discover/public/components/doc_table/doc_table_embeddable.tsx deleted file mode 100644 index 1c1274a739f1c..0000000000000 --- a/src/plugins/discover/public/components/doc_table/doc_table_embeddable.tsx +++ /dev/null @@ -1,141 +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, { memo, useCallback, useMemo, useRef } from 'react'; -import './index.scss'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiText } from '@elastic/eui'; -import { usePager } from '@kbn/discover-utils'; -import type { SearchResponseWarning } from '@kbn/search-response-warnings'; -import { - ToolBarPagination, - MAX_ROWS_PER_PAGE_OPTION, -} from './components/pager/tool_bar_pagination'; -import { DocTableProps, DocTableRenderProps, DocTableWrapper } from './doc_table_wrapper'; -import { SavedSearchEmbeddableBase } from '../../embeddable/components/saved_search_embeddable_base'; - -export interface DocTableEmbeddableProps extends Omit { - totalHitCount?: number; - rowsPerPageState?: number; - sampleSizeState: number; - interceptedWarnings?: SearchResponseWarning[]; - onUpdateRowsPerPage?: (rowsPerPage?: number) => void; -} - -export type DocTableEmbeddableSearchProps = Omit< - DocTableEmbeddableProps, - 'sampleSizeState' | 'isEsqlMode' ->; - -const DocTableWrapperMemoized = memo(DocTableWrapper); - -export const DocTableEmbeddable = (props: DocTableEmbeddableProps) => { - const onUpdateRowsPerPage = props.onUpdateRowsPerPage; - const tableWrapperRef = useRef(null); - const { - curPageIndex, - pageSize, - totalPages, - startIndex, - hasNextPage, - changePageIndex, - changePageSize, - } = usePager({ - initialPageSize: - typeof props.rowsPerPageState === 'number' && props.rowsPerPageState > 0 - ? Math.min(props.rowsPerPageState, MAX_ROWS_PER_PAGE_OPTION) - : 50, - totalItems: props.rows.length, - }); - const showPagination = totalPages !== 0; - - const scrollTop = useCallback(() => { - if (tableWrapperRef.current) { - tableWrapperRef.current.scrollTo(0, 0); - } - }, []); - - const pageOfItems = useMemo( - () => props.rows.slice(startIndex, pageSize + startIndex), - [pageSize, startIndex, props.rows] - ); - - const onPageChange = useCallback( - (page: number) => { - scrollTop(); - changePageIndex(page); - }, - [changePageIndex, scrollTop] - ); - - const onPageSizeChange = useCallback( - (size: number) => { - scrollTop(); - changePageSize(size); - onUpdateRowsPerPage?.(size); // to update `rowsPerPage` input param for the embeddable - }, - [changePageSize, scrollTop, onUpdateRowsPerPage] - ); - - const shouldShowLimitedResultsWarning = useMemo( - () => !hasNextPage && props.totalHitCount && props.rows.length < props.totalHitCount, - [hasNextPage, props.rows.length, props.totalHitCount] - ); - - const renderDocTable = useCallback( - (renderProps: DocTableRenderProps) => { - return ( -
- - {renderProps.renderHeader()} - {renderProps.renderRows(pageOfItems)} -
-
- ); - }, - [pageOfItems] - ); - - return ( - - - - ) : undefined - } - append={ - showPagination ? ( - - ) : undefined - } - > - - - ); -}; diff --git a/src/plugins/discover/public/components/doc_table/doc_table_infinite.tsx b/src/plugins/discover/public/components/doc_table/doc_table_infinite.tsx deleted file mode 100644 index 5a667ea6f70d4..0000000000000 --- a/src/plugins/discover/public/components/doc_table/doc_table_infinite.tsx +++ /dev/null @@ -1,155 +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, { Fragment, memo, useCallback, useEffect, useRef, useState } from 'react'; -import './index.scss'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { debounce } from 'lodash'; -import { EuiButtonEmpty } from '@elastic/eui'; -import { DocTableProps, DocTableRenderProps, DocTableWrapper } from './doc_table_wrapper'; -import { SkipBottomButton } from '../../application/main/components/skip_bottom_button'; -import { shouldLoadNextDocPatch } from './utils/should_load_next_doc_patch'; -import { useDiscoverServices } from '../../hooks/use_discover_services'; -import { getAllowedSampleSize } from '../../utils/get_allowed_sample_size'; -import { useAppStateSelector } from '../../application/main/state_management/discover_app_state_container'; - -const FOOTER_PADDING = { padding: 0 }; - -const DocTableWrapperMemoized = memo(DocTableWrapper); - -interface DocTableInfiniteContentProps extends DocTableRenderProps { - limit: number; - onSetMaxLimit: () => void; - onBackToTop: () => void; -} - -const DocTableInfiniteContent = ({ - rows, - columnLength, - limit, - onSkipBottomButtonClick, - renderHeader, - renderRows, - onSetMaxLimit, - onBackToTop, -}: DocTableInfiniteContentProps) => { - const { uiSettings } = useDiscoverServices(); - const sampleSize = useAppStateSelector((state) => - getAllowedSampleSize(state.sampleSize, uiSettings) - ); - - const onSkipBottomButton = useCallback(() => { - onSetMaxLimit(); - onSkipBottomButtonClick(); - }, [onSetMaxLimit, onSkipBottomButtonClick]); - - return ( - - - - {renderHeader()} - {renderRows(rows.slice(0, limit))} - - - - - -
- {rows.length === sampleSize ? ( -
- - - - -
- ) : ( - - ​ - - )} -
-
- ); -}; - -export const DocTableInfinite = (props: DocTableProps) => { - const tableWrapperRef = useRef(null); - const [limit, setLimit] = useState(50); - - /** - * depending on which version of Discover is displayed, different elements are scrolling - * and have therefore to be considered for calculation of infinite scrolling - */ - useEffect(() => { - // After mounting table wrapper should be initialized - const scrollDiv = tableWrapperRef.current as HTMLDivElement; - const scrollMobileElem = document.documentElement; - - const scheduleCheck = debounce(() => { - const isMobileView = document.getElementsByClassName('dscSidebar__mobile').length > 0; - - const usedScrollDiv = isMobileView ? scrollMobileElem : scrollDiv; - if (shouldLoadNextDocPatch(usedScrollDiv)) { - setLimit((prevLimit) => prevLimit + 50); - } - }, 50); - - scrollDiv.addEventListener('scroll', scheduleCheck); - window.addEventListener('scroll', scheduleCheck); - - scheduleCheck(); - - return () => { - scrollDiv.removeEventListener('scroll', scheduleCheck); - window.removeEventListener('scroll', scheduleCheck); - }; - }, []); - - const onBackToTop = useCallback(() => { - const isMobileView = document.getElementsByClassName('dscSidebar__mobile').length > 0; - const focusElem = document.querySelector('.dscSkipButton') as HTMLElement; - focusElem.focus(); - - // Only the desktop one needs to target a specific container - if (!isMobileView && tableWrapperRef.current) { - tableWrapperRef.current.scrollTo(0, 0); - } else if (window) { - window.scrollTo(0, 0); - } - }, []); - - const setMaxLimit = useCallback(() => setLimit(props.rows.length), [props.rows.length]); - - const renderDocTable = useCallback( - (tableProps: DocTableRenderProps) => ( - - ), - [limit, onBackToTop, setMaxLimit] - ); - - return ; -}; diff --git a/src/plugins/discover/public/components/doc_table/doc_table_wrapper.test.tsx b/src/plugins/discover/public/components/doc_table/doc_table_wrapper.test.tsx deleted file mode 100644 index fbbe460320cdb..0000000000000 --- a/src/plugins/discover/public/components/doc_table/doc_table_wrapper.test.tsx +++ /dev/null @@ -1,81 +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 { EuiIcon, EuiLoadingSpinner } from '@elastic/eui'; -import { findTestSubject, mountWithIntl } from '@kbn/test-jest-helpers'; -import { dataViewMock } from '@kbn/discover-utils/src/__mocks__'; -import { DocTableWrapper, DocTableWrapperProps } from './doc_table_wrapper'; -import { discoverServiceMock } from '../../__mocks__/services'; -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; -import { buildDataTableRecord } from '@kbn/discover-utils'; -import type { EsHitRecord } from '@kbn/discover-utils/types'; - -describe('Doc table component', () => { - const mountComponent = (customProps?: Partial) => { - const props = { - columns: ['_source'], - dataView: dataViewMock, - rows: [ - { - _index: 'mock_index', - _id: '1', - _score: 1, - fields: [ - { - timestamp: '2020-20-01T12:12:12.123', - }, - ], - _source: { message: 'mock_message', bytes: 20 }, - } as EsHitRecord, - ].map((row) => buildDataTableRecord(row, dataViewMock)), - sort: [['order_date', 'desc']], - isLoading: false, - searchDescription: '', - onAddColumn: () => {}, - onFilter: () => {}, - onMoveColumn: () => {}, - onRemoveColumn: () => {}, - onSort: () => {}, - useNewFieldsApi: true, - dataTestSubj: 'discoverDocTable', - render: () => { - return
mock
; - }, - ...(customProps || {}), - }; - - return mountWithIntl( - - - - ); - }; - - it('should render infinite table correctly', () => { - const component = mountComponent(); - expect(findTestSubject(component, 'discoverDocTable').exists()).toBeTruthy(); - expect(findTestSubject(component, 'docTable').exists()).toBeTruthy(); - expect(component.find('.kbnDocTable__error').exists()).toBeFalsy(); - }); - - it('should render error fallback if rows array is empty', () => { - const component = mountComponent({ rows: [], isLoading: false }); - expect(findTestSubject(component, 'discoverDocTable').exists()).toBeTruthy(); - expect(findTestSubject(component, 'docTable').exists()).toBeFalsy(); - expect(component.find('.kbnDocTable__error').find(EuiIcon).exists()).toBeTruthy(); - }); - - it('should render loading indicator', () => { - const component = mountComponent({ rows: [], isLoading: true }); - expect(findTestSubject(component, 'discoverDocTable').exists()).toBeTruthy(); - expect(findTestSubject(component, 'docTable').exists()).toBeFalsy(); - expect(component.find('.kbnDocTable__error').find(EuiLoadingSpinner).exists()).toBeTruthy(); - }); -}); diff --git a/src/plugins/discover/public/components/doc_table/doc_table_wrapper.tsx b/src/plugins/discover/public/components/doc_table/doc_table_wrapper.tsx deleted file mode 100644 index b9fb8b6681e20..0000000000000 --- a/src/plugins/discover/public/components/doc_table/doc_table_wrapper.tsx +++ /dev/null @@ -1,253 +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, { forwardRef, useCallback, useMemo } from 'react'; -import { EuiIcon, EuiLoadingSpinner, EuiSpacer, EuiText } from '@elastic/eui'; -import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; -import type { SortOrder } from '@kbn/saved-search-plugin/public'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { Filter } from '@kbn/es-query'; -import type { DataTableRecord } from '@kbn/discover-utils/types'; -import { SHOW_MULTIFIELDS, getShouldShowFieldHandler } from '@kbn/discover-utils'; -import type { DocViewFilterFn } from '@kbn/unified-doc-viewer/types'; -import { TableHeader } from './components/table_header/table_header'; -import { TableRow } from './components/table_row'; -import { useDiscoverServices } from '../../hooks/use_discover_services'; - -export interface DocTableProps { - /** - * Rows of classic table - */ - rows: DataTableRecord[]; - /** - * Columns of classic table - */ - columns: string[]; - /** - * Current DataView - */ - dataView: DataView; - /** - * Current sorting - */ - sort: string[][]; - /** - * New fields api switch - */ - useNewFieldsApi: boolean; - /** - * Current search description - */ - searchDescription?: string; - /** - * Current shared item title - */ - sharedItemTitle?: string; - /** - * Current data test subject - */ - dataTestSubj: string; - /** - * Loading state - */ - isLoading: boolean; - /** - * Filters applied by embeddalbe - */ - filters?: Filter[]; - /** - * Flag which identifies if Discover operates - * in ES|QL mode - */ - isEsqlMode?: boolean; - /** - * Saved search id - */ - savedSearchId?: string; - /** - * Filter callback - */ - onFilter?: DocViewFilterFn; - /** - * Sorting callback - */ - onSort?: (sort: string[][]) => void; - /** - * Add columns callback - */ - onAddColumn?: (column: string) => void; - /** - * Reordering column callback - */ - onMoveColumn?: (columns: string, newIdx: number) => void; - /** - * Remove column callback - */ - onRemoveColumn?: (column: string) => void; -} - -export interface DocTableRenderProps { - columnLength: number; - rows: DataTableRecord[]; - renderRows: (row: DataTableRecord[]) => JSX.Element[]; - renderHeader: () => JSX.Element; - onSkipBottomButtonClick: () => void; -} - -export interface DocTableWrapperProps extends DocTableProps { - /** - * Renders Doc table content - */ - render: (params: DocTableRenderProps) => JSX.Element; -} - -const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); - -export const DocTableWrapper = forwardRef( - ( - { - render, - columns, - filters, - isEsqlMode, - savedSearchId, - rows, - dataView, - onSort, - onAddColumn, - onMoveColumn, - onRemoveColumn, - sort, - onFilter, - useNewFieldsApi, - searchDescription, - sharedItemTitle, - dataTestSubj, - isLoading, - }: DocTableWrapperProps, - ref - ) => { - const { uiSettings } = useDiscoverServices(); - const showMultiFields = useMemo(() => uiSettings.get(SHOW_MULTIFIELDS, false), [uiSettings]); - - const onSkipBottomButtonClick = useCallback(async () => { - // delay scrolling to after the rows have been rendered - const bottomMarker = document.getElementById('discoverBottomMarker'); - - while (rows.length !== document.getElementsByClassName('kbnDocTable__row').length) { - await wait(50); - } - bottomMarker!.focus(); - await wait(50); - bottomMarker!.blur(); - }, [rows]); - - const shouldShowFieldHandler = useMemo( - () => - getShouldShowFieldHandler( - dataView.fields.map((field: DataViewField) => field.name), - dataView, - showMultiFields - ), - [dataView, showMultiFields] - ); - - const renderHeader = useCallback( - () => ( - - ), - [columns, dataView, onMoveColumn, onRemoveColumn, onSort, sort] - ); - - const renderRows = useCallback( - (rowsToRender: DataTableRecord[]) => { - return rowsToRender.map((current) => ( - - )); - }, - [ - columns, - filters, - savedSearchId, - onFilter, - dataView, - useNewFieldsApi, - shouldShowFieldHandler, - onAddColumn, - onRemoveColumn, - isEsqlMode, - rows, - ] - ); - - return ( -
} - > - {rows.length !== 0 && - render({ - columnLength: columns.length, - rows, - onSkipBottomButtonClick, - renderHeader, - renderRows, - })} - {!rows.length && ( -
- - {isLoading ? ( - <> - - - - - ) : ( - <> - - - - - )} - -
- )} -
- ); - } -); diff --git a/src/plugins/discover/public/components/doc_table/index.scss b/src/plugins/discover/public/components/doc_table/index.scss deleted file mode 100644 index 3663d807851c4..0000000000000 --- a/src/plugins/discover/public/components/doc_table/index.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import 'doc_table'; -@import 'components/index'; diff --git a/src/plugins/discover/public/components/doc_table/utils/row_formatter.scss b/src/plugins/discover/public/components/doc_table/utils/row_formatter.scss deleted file mode 100644 index ede39feed30b6..0000000000000 --- a/src/plugins/discover/public/components/doc_table/utils/row_formatter.scss +++ /dev/null @@ -1,7 +0,0 @@ -// Special handling for images coming from the image field formatter -// See discover_grid.scss for more explanation on those values -.rowFormatter__value img { - vertical-align: middle; - max-height: lineHeightFromBaseline(2) !important; - max-width: ($euiSizeXXL * 12.5) !important; -} diff --git a/src/plugins/discover/public/components/doc_table/utils/row_formatter.test.ts b/src/plugins/discover/public/components/doc_table/utils/row_formatter.test.ts deleted file mode 100644 index d7cb720b78814..0000000000000 --- a/src/plugins/discover/public/components/doc_table/utils/row_formatter.test.ts +++ /dev/null @@ -1,313 +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 ReactDOM from 'react-dom/server'; -import { formatRow, formatTopLevelObject } from './row_formatter'; -import { DataView } from '@kbn/data-views-plugin/public'; -import { fieldFormatsMock } from '@kbn/field-formats-plugin/common/mocks'; -import { DiscoverServices } from '../../../build_services'; -import { stubbedSavedObjectIndexPattern } from '@kbn/data-plugin/common/stubs'; -import { buildDataTableRecord } from '@kbn/discover-utils'; - -describe('Row formatter', () => { - let services: DiscoverServices; - - const createIndexPattern = () => { - const id = 'my-index'; - const { - type, - version, - attributes: { timeFieldName, fields, title }, - } = stubbedSavedObjectIndexPattern(id); - - return new DataView({ - spec: { id, type, version, timeFieldName, fields: JSON.parse(fields), title }, - fieldFormats: fieldFormatsMock, - shortDotsEnable: false, - metaFields: ['_id', '_type', '_score'], - }); - }; - - const dataView = createIndexPattern(); - const rawHit = { - _id: 'a', - _index: 'foo', - _score: 1, - _source: { - foo: 'bar', - number: 42, - hello: '

World

', - also: 'with "quotes" or \'single quotes\'', - }, - }; - const hit = buildDataTableRecord(rawHit, dataView); - - const shouldShowField = (fieldName: string) => - dataView.fields.getAll().some((fld) => fld.name === fieldName); - - beforeEach(() => { - services = { - fieldFormats: { - getDefaultInstance: jest.fn(() => ({ convert: (value: unknown) => value })), - getFormatterForField: jest.fn(() => ({ convert: (value: unknown) => value })), - }, - } as unknown as DiscoverServices; - }); - - it('formats document properly', () => { - expect(formatRow(hit, dataView, shouldShowField, 100, services.fieldFormats)) - .toMatchInlineSnapshot(` - World", - "hello", - ], - Array [ - "number", - 42, - "number", - ], - Array [ - "_id", - "a", - "_id", - ], - Array [ - "_score", - 1, - "_score", - ], - ] - } - /> - `); - }); - - it('limits number of rendered items', () => { - services = { - uiSettings: { - get: () => 1, - }, - fieldFormats: { - getDefaultInstance: jest.fn(() => ({ convert: (value: unknown) => value })), - getFormatterForField: jest.fn(() => ({ convert: (value: unknown) => value })), - }, - } as unknown as DiscoverServices; - expect(formatRow(hit, dataView, () => false, 1, services.fieldFormats)).toMatchInlineSnapshot(` - - `); - }); - - it('formats document with highlighted fields first', () => { - const highLightHit = buildDataTableRecord( - { ...rawHit, highlight: { number: ['42'] } }, - dataView - ); - - expect(formatRow(highLightHit, dataView, shouldShowField, 100, services.fieldFormats)) - .toMatchInlineSnapshot(` - World", - "hello", - ], - Array [ - "_id", - "a", - "_id", - ], - Array [ - "_score", - 1, - "_score", - ], - ] - } - /> - `); - }); - - it('formats top level objects using formatter', () => { - dataView.getFieldByName = jest.fn().mockReturnValue({ - name: 'subfield', - }); - dataView.getFormatterForField = jest.fn().mockReturnValue({ - convert: () => 'formatted', - }); - expect( - formatTopLevelObject( - { - fields: { - 'object.value': [5, 10], - }, - }, - { - 'object.value': [5, 10], - getByName: jest.fn(), - }, - dataView, - 100 - ) - ).toMatchInlineSnapshot(` - - `); - }); - - it('formats top level objects in alphabetical order', () => { - dataView.getFieldByName = jest.fn().mockReturnValue({ - name: 'subfield', - }); - dataView.getFormatterForField = jest.fn().mockReturnValue({ - convert: () => 'formatted', - }); - const formatted = ReactDOM.renderToStaticMarkup( - formatTopLevelObject( - { fields: { 'a.zzz': [100], 'a.ccc': [50] } }, - { 'a.zzz': [100], 'a.ccc': [50], getByName: jest.fn() }, - dataView, - 100 - ) - ); - expect(formatted.indexOf('
a.ccc:
')).toBeLessThan(formatted.indexOf('
a.zzz:
')); - }); - - it('formats top level objects with subfields and highlights', () => { - dataView.getFieldByName = jest.fn().mockReturnValue({ - name: 'subfield', - }); - dataView.getFormatterForField = jest.fn().mockReturnValue({ - convert: () => 'formatted', - }); - expect( - formatTopLevelObject( - { - fields: { - 'object.value': [5, 10], - 'object.keys': ['a', 'b'], - }, - highlight: { - 'object.keys': 'a', - }, - }, - { - 'object.value': [5, 10], - 'object.keys': ['a', 'b'], - getByName: jest.fn(), - }, - dataView, - 100 - ) - ).toMatchInlineSnapshot(` - - `); - }); - - it('formats top level objects, converting unknown fields to string', () => { - dataView.getFieldByName = jest.fn(); - dataView.getFormatterForField = jest.fn(); - expect( - formatTopLevelObject( - { - fields: { - 'object.value': [5, 10], - }, - }, - { - 'object.value': [5, 10], - getByName: jest.fn(), - }, - dataView, - 100 - ) - ).toMatchInlineSnapshot(` - - `); - }); -}); diff --git a/src/plugins/discover/public/components/doc_table/utils/row_formatter.tsx b/src/plugins/discover/public/components/doc_table/utils/row_formatter.tsx deleted file mode 100644 index ec33f5c16be09..0000000000000 --- a/src/plugins/discover/public/components/doc_table/utils/row_formatter.tsx +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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, { Fragment } from 'react'; -import type { DataView } from '@kbn/data-views-plugin/public'; -import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; -import type { - DataTableRecord, - ShouldShowFieldInTableHandler, - FormattedHit, -} from '@kbn/discover-utils/types'; -import { formatHit } from '@kbn/discover-utils'; - -import './row_formatter.scss'; - -interface Props { - defPairs: FormattedHit; -} -const TemplateComponent = ({ defPairs }: Props) => { - return ( -
- {defPairs.map((pair, idx) => ( - -
- {pair[0]} - {!!pair[1] && ':'} -
-
{' '} - - ))} -
- ); -}; - -export const formatRow = ( - hit: DataTableRecord, - dataView: DataView, - shouldShowFieldHandler: ShouldShowFieldInTableHandler, - maxEntries: number, - fieldFormats: FieldFormatsStart -) => { - const pairs = formatHit(hit, dataView, shouldShowFieldHandler, maxEntries, fieldFormats); - return ; -}; - -export const formatTopLevelObject = ( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - row: Record, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - fields: Record, - dataView: DataView, - maxEntries: number -) => { - const highlights = row.highlight ?? {}; - const highlightPairs: FormattedHit = []; - const sourcePairs: FormattedHit = []; - const sorted = Object.entries(fields).sort(([keyA], [keyB]) => keyA.localeCompare(keyB)); - sorted.forEach(([key, values]) => { - const field = dataView.getFieldByName(key); - const displayKey = fields.getByName ? fields.getByName(key)?.displayName : undefined; - const formatter = field - ? dataView.getFormatterForField(field) - : { convert: (v: unknown, ..._: unknown[]) => String(v) }; - if (!values.map) return; - const formatted = values - .map((val: unknown) => - formatter.convert(val, 'html', { - field, - hit: row, - }) - ) - .join(', '); - const pairs = highlights[key] ? highlightPairs : sourcePairs; - pairs.push([displayKey ? displayKey : key, formatted, key]); - }); - return ; -}; diff --git a/src/plugins/discover/public/components/doc_table/utils/should_load_next_doc_patch.test.ts b/src/plugins/discover/public/components/doc_table/utils/should_load_next_doc_patch.test.ts deleted file mode 100644 index 25d5ef59c5f4e..0000000000000 --- a/src/plugins/discover/public/components/doc_table/utils/should_load_next_doc_patch.test.ts +++ /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 { shouldLoadNextDocPatch } from './should_load_next_doc_patch'; - -describe('shouldLoadNextDocPatch', () => { - test('next patch should not be loaded', () => { - const scrollingDomEl = { - scrollHeight: 500, - scrollTop: 100, - clientHeight: 100, - } as HTMLElement; - - expect(shouldLoadNextDocPatch(scrollingDomEl)).toBeFalsy(); - }); - - test('next patch should be loaded', () => { - const scrollingDomEl = { - scrollHeight: 500, - scrollTop: 350, - clientHeight: 100, - } as HTMLElement; - - expect(shouldLoadNextDocPatch(scrollingDomEl)).toBeTruthy(); - }); - - test("next patch should be loaded even there's a decimal scroll height", () => { - const scrollingDomEl = { - scrollHeight: 500, - scrollTop: 350.34234234, - clientHeight: 100, - } as HTMLElement; - - expect(shouldLoadNextDocPatch(scrollingDomEl)).toBeTruthy(); - }); -}); diff --git a/src/plugins/discover/public/components/doc_table/utils/should_load_next_doc_patch.ts b/src/plugins/discover/public/components/doc_table/utils/should_load_next_doc_patch.ts deleted file mode 100644 index cfb4f93a2adb5..0000000000000 --- a/src/plugins/discover/public/components/doc_table/utils/should_load_next_doc_patch.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 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". - */ - -// use a buffer to start rendering more documents before the user completely scrolles down -const verticalScrollBuffer = 100; - -/** - * Helper function to determine if the next patch of 50 documents should be loaded - */ -export function shouldLoadNextDocPatch(domEl: HTMLElement) { - // the height of the scrolling div, including content not visible on the screen due to overflow. - const scrollHeight = domEl.scrollHeight; - // the number of pixels that the div is is scrolled vertically - const scrollTop = domEl.scrollTop; - // the inner height of the scrolling div, excluding content that's visible on the screen - const clientHeight = domEl.clientHeight; - - const consumedHeight = scrollTop + clientHeight; - const remainingHeight = scrollHeight - consumedHeight; - return remainingHeight < verticalScrollBuffer; -} diff --git a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx index 43960f98ea2b8..5b77a184d9253 100644 --- a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx +++ b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx @@ -11,7 +11,7 @@ import React, { useMemo, useEffect, useState, type ReactElement, useCallback } f import { EuiFlexGroup, EuiFlexItem, EuiTab, EuiTabs, useEuiTheme } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { css } from '@emotion/react'; -import { isLegacyTableEnabled, SHOW_FIELD_STATISTICS } from '@kbn/discover-utils'; +import { SHOW_FIELD_STATISTICS } from '@kbn/discover-utils'; import type { DataView } from '@kbn/data-views-plugin/common'; import useMountedState from 'react-use/lib/useMountedState'; import { VIEW_MODE } from '../../../common/constants'; @@ -42,10 +42,6 @@ export const DocumentViewModeToggle = ({ dataVisualizer: dataVisualizerService, aiops: aiopsService, } = useDiscoverServices(); - const isLegacy = useMemo( - () => isLegacyTableEnabled({ uiSettings, isEsqlMode }), - [uiSettings, isEsqlMode] - ); const [showPatternAnalysisTab, setShowPatternAnalysisTab] = useState(null); const showFieldStatisticsTab = useMemo( @@ -93,7 +89,7 @@ export const DocumentViewModeToggle = ({ } }, [showPatternAnalysisTab, viewMode, setDiscoverViewMode]); - const includesNormalTabsStyle = viewMode === VIEW_MODE.AGGREGATED_LEVEL || isLegacy; + const includesNormalTabsStyle = viewMode === VIEW_MODE.AGGREGATED_LEVEL; const containerPadding = includesNormalTabsStyle ? euiTheme.size.s : 0; const containerCss = css` diff --git a/src/plugins/discover/public/embeddable/components/search_embeddable_grid_component.tsx b/src/plugins/discover/public/embeddable/components/search_embeddable_grid_component.tsx index 9c7a54054d265..8375e72aa34de 100644 --- a/src/plugins/discover/public/embeddable/components/search_embeddable_grid_component.tsx +++ b/src/plugins/discover/public/embeddable/components/search_embeddable_grid_component.tsx @@ -15,7 +15,6 @@ import { DOC_HIDE_TIME_COLUMN_SETTING, SEARCH_FIELDS_FROM_SOURCE, SORT_DEFAULT_ORDER_SETTING, - isLegacyTableEnabled, } from '@kbn/discover-utils'; import { FetchContext, @@ -28,7 +27,6 @@ import { DataGridDensity, DataLoadingState, useColumns } from '@kbn/unified-data import { DocViewFilterFn } from '@kbn/unified-doc-viewer/types'; import { DiscoverGridSettings } from '@kbn/saved-search-plugin/common'; import useObservable from 'react-use/lib/useObservable'; -import { DiscoverDocTableEmbeddable } from '../../components/doc_table/create_doc_table_embeddable'; import { useDiscoverServices } from '../../hooks/use_discover_services'; import { getSortForEmbeddable } from '../../utils'; import { getAllowedSampleSize, getMaxAllowedSampleSize } from '../../utils/get_allowed_sample_size'; @@ -53,7 +51,6 @@ interface SavedSearchEmbeddableComponentProps { stateManager: SearchEmbeddableStateManager; } -const DiscoverDocTableEmbeddableMemoized = React.memo(DiscoverDocTableEmbeddable); const DiscoverGridEmbeddableMemoized = React.memo(DiscoverGridEmbeddable); export function SearchEmbeddableGridComponent({ @@ -105,14 +102,6 @@ export function SearchEmbeddableGridComponent({ ); const isEsql = useMemo(() => isEsqlMode(savedSearch), [savedSearch]); - const useLegacyTable = useMemo( - () => - isLegacyTableEnabled({ - uiSettings: discoverServices.uiSettings, - isEsqlMode: isEsql, - }), - [discoverServices, isEsql] - ); const sort = useMemo(() => { return getSortForEmbeddable(savedSearch.sort, dataView, discoverServices.uiSettings, isEsql); @@ -233,19 +222,6 @@ export function SearchEmbeddableGridComponent({ useNewFieldsApi, }; - if (useLegacyTable) { - return ( - - ); - } - return ( { - if (!maxHeight) { - return [ - ` - .dscTruncateByHeight { - max-height: none; - }`, - ]; - } - return [ - ` - .dscTruncateByHeight { - overflow: hidden; - max-height: ${maxHeight}px !important; - } - .dscTruncateByHeight:before { - top: ${maxHeight - TRUNCATE_GRADIENT_HEIGHT}px; - } - `, - ]; -}; - -export const injectTruncateStyles = (maxHeight: number) => { - const serialized = serializeStyles(buildStylesheet(maxHeight), cache.registered); - if (!globalThemeCache.inserted[serialized.name]) { - globalThemeCache.insert('', serialized, globalThemeCache.sheet, true); - } -}; diff --git a/src/plugins/discover/server/ui_settings.ts b/src/plugins/discover/server/ui_settings.ts index 9d0905b27bfad..7dd84c9728696 100644 --- a/src/plugins/discover/server/ui_settings.ts +++ b/src/plugins/discover/server/ui_settings.ts @@ -23,13 +23,10 @@ import { CONTEXT_DEFAULT_SIZE_SETTING, CONTEXT_STEP_SETTING, CONTEXT_TIE_BREAKER_FIELDS_SETTING, - DOC_TABLE_LEGACY, MODIFY_COLUMNS_ON_SWITCH, SEARCH_FIELDS_FROM_SOURCE, MAX_DOC_FIELDS_DISPLAYED, SHOW_MULTIFIELDS, - TRUNCATE_MAX_HEIGHT, - TRUNCATE_MAX_HEIGHT_DEFAULT_VALUE, SHOW_FIELD_STATISTICS, ROW_HEIGHT_OPTION, } from '@kbn/discover-utils'; @@ -183,42 +180,6 @@ export const getUiSettings: ( category: ['discover'], schema: schema.arrayOf(schema.string()), }, - [DOC_TABLE_LEGACY]: { - name: i18n.translate('discover.advancedSettings.disableDocumentExplorer', { - defaultMessage: 'Document Explorer or classic view', - }), - value: false, - description: i18n.translate('discover.advancedSettings.disableDocumentExplorerDescription', { - defaultMessage: - 'To use the new {documentExplorerDocs} instead of the classic view, turn off this option. ' + - 'The Document Explorer offers better data sorting, resizable columns, and a full screen view.', - values: { - documentExplorerDocs: - `` + - i18n.translate('discover.advancedSettings.documentExplorerLinkText', { - defaultMessage: 'Document Explorer', - }) + - '', - }, - }), - requiresPageReload: true, - category: ['discover'], - schema: schema.boolean(), - metric: { - type: METRIC_TYPE.CLICK, - name: 'discover:useLegacyDataGrid', - }, - deprecation: { - message: i18n.translate( - 'discover.advancedSettings.discover.disableDocumentExplorerDeprecation', - { - defaultMessage: 'This setting is deprecated and will be removed in Kibana 9.0.', - } - ), - docLinksKey: 'discoverSettings', - }, - }, [MODIFY_COLUMNS_ON_SWITCH]: { name: i18n.translate('discover.advancedSettings.discover.modifyColumnsOnSwitchTitle', { defaultMessage: 'Modify columns when changing data views', @@ -316,23 +277,4 @@ export const getUiSettings: ( }), schema: schema.number({ min: -1 }), }, - [TRUNCATE_MAX_HEIGHT]: { - name: i18n.translate('discover.advancedSettings.params.maxCellHeightTitle', { - defaultMessage: 'Maximum cell height in the classic table', - }), - value: TRUNCATE_MAX_HEIGHT_DEFAULT_VALUE, - category: ['discover'], - description: i18n.translate('discover.advancedSettings.params.maxCellHeightText', { - defaultMessage: - 'The maximum height that a cell in a table should occupy. Set to 0 to disable truncation.', - }), - schema: schema.number({ min: 0 }), - requiresPageReload: true, - deprecation: { - message: i18n.translate('discover.advancedSettings.discover.maxCellHeightDeprecation', { - defaultMessage: 'This setting is deprecated and will be removed in Kibana 9.0.', - }), - docLinksKey: 'discoverSettings', - }, - }, }); diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts index 0a3aeb2eaa1c8..6ad26b9dce724 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts @@ -244,10 +244,6 @@ export const stackManagementSchema: MakeSchemaFrom = { type: 'keyword', _meta: { description: 'Non-default value of setting.' }, }, - 'truncate:maxHeight': { - type: 'long', - _meta: { description: 'Non-default value of setting.' }, - }, 'timepicker:timeDefaults': { type: 'keyword', _meta: { description: 'Non-default value of setting.' }, @@ -424,10 +420,6 @@ export const stackManagementSchema: MakeSchemaFrom = { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, }, - 'doc_table:legacy': { - type: 'boolean', - _meta: { description: 'Non-default value of setting.' }, - }, 'discover:modifyColumnsOnSwitch': { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts index 9796436007357..4c6f17a85914c 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts @@ -30,7 +30,6 @@ export interface UsageStats { 'autocomplete:valueSuggestionMethod': string; 'search:timeout': number; 'visualization:visualize:legacyHeatmapChartsLibrary': boolean; - 'doc_table:legacy': boolean; 'discover:modifyColumnsOnSwitch': boolean; 'discover:searchFieldsFromSource': boolean; 'discover:showFieldStatistics': boolean; @@ -101,7 +100,6 @@ export interface UsageStats { 'fileUpload:maxFileSize': string; 'ml:anomalyDetection:results:enableTimeDefaults': boolean; 'ml:anomalyDetection:results:timeDefaults': string; - 'truncate:maxHeight': number; 'timepicker:timeDefaults': string; 'timepicker:refreshIntervalDefaults': string; 'timepicker:quickRanges': string; diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index fe0f599dd6ca1..35e0242b57624 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -10492,12 +10492,6 @@ "description": "Non-default value of setting." } }, - "truncate:maxHeight": { - "type": "long", - "_meta": { - "description": "Non-default value of setting." - } - }, "timepicker:timeDefaults": { "type": "keyword", "_meta": { @@ -10765,12 +10759,6 @@ "description": "Non-default value of setting." } }, - "doc_table:legacy": { - "type": "boolean", - "_meta": { - "description": "Non-default value of setting." - } - }, "discover:modifyColumnsOnSwitch": { "type": "boolean", "_meta": { diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/get_height.test.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/get_height.test.tsx index 5e97ff9cc55da..a3f70c2badc0c 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/get_height.test.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/get_height.test.tsx @@ -32,31 +32,17 @@ describe('getHeight', () => { test('when using document explorer, returning the available height in the flyout', () => { const monacoMock = getMonacoMock(500, 0); - const height = getHeight(monacoMock, true, DEFAULT_MARGIN_BOTTOM); + const height = getHeight(monacoMock, DEFAULT_MARGIN_BOTTOM); expect(height).toBe(484); - const heightCustom = getHeight(monacoMock, true, 80); + const heightCustom = getHeight(monacoMock, 80); expect(heightCustom).toBe(420); }); test('when using document explorer, returning the available height in the flyout has a minimun guarenteed height', () => { const monacoMock = getMonacoMock(500); - const height = getHeight(monacoMock, true, DEFAULT_MARGIN_BOTTOM); + const height = getHeight(monacoMock, DEFAULT_MARGIN_BOTTOM); expect(height).toBe(400); }); - - test('when using classic table, its displayed inline without scrolling', () => { - const monacoMock = getMonacoMock(100); - - const height = getHeight(monacoMock, false, DEFAULT_MARGIN_BOTTOM); - expect(height).toBe(1020); - }); - - test('when using classic table, limited height > 500 lines to allow scrolling', () => { - const monacoMock = getMonacoMock(1000); - - const height = getHeight(monacoMock, false, DEFAULT_MARGIN_BOTTOM); - expect(height).toBe(5020); - }); }); diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/get_height.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/get_height.tsx index 514c994e5d423..68619b6048338 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/get_height.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/get_height.tsx @@ -8,7 +8,7 @@ */ import { monaco } from '@kbn/monaco'; -import { MAX_LINES_CLASSIC_TABLE, MIN_HEIGHT } from './source'; +import { MIN_HEIGHT } from './source'; // Displayed margin of the tab content to the window bottom export const DEFAULT_MARGIN_BOTTOM = 16; @@ -28,7 +28,6 @@ export function getTabContentAvailableHeight( export function getHeight( editor: monaco.editor.IStandaloneCodeEditor, - useDocExplorer: boolean, decreaseAvailableHeightBy: number ) { const editorElement = editor?.getDomNode(); @@ -36,17 +35,6 @@ export function getHeight( return 0; } - let result; - if (useDocExplorer) { - result = getTabContentAvailableHeight(editorElement, decreaseAvailableHeightBy); - } else { - // takes care of the classic table, display a maximum of 500 lines - // why not display it all? Due to performance issues when the browser needs to render it all - const lineHeight = editor.getOption(monaco.editor.EditorOption.lineHeight); - const lineCount = editor.getModel()?.getLineCount() || 1; - const displayedLineCount = - lineCount > MAX_LINES_CLASSIC_TABLE ? MAX_LINES_CLASSIC_TABLE : lineCount; - result = editor.getTopForLineNumber(displayedLineCount + 1) + lineHeight; - } + const result = getTabContentAvailableHeight(editorElement, decreaseAvailableHeightBy); return Math.max(result, MIN_HEIGHT); } diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/source.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/source.tsx index 5b4ba36cd03f1..3bd1137cabccf 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/source.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/source.tsx @@ -17,7 +17,7 @@ import { i18n } from '@kbn/i18n'; import type { DataView } from '@kbn/data-views-plugin/public'; import type { DataTableRecord } from '@kbn/discover-utils/types'; import { ElasticRequestState } from '@kbn/unified-doc-viewer'; -import { isLegacyTableEnabled, SEARCH_FIELDS_FROM_SOURCE } from '@kbn/discover-utils'; +import { SEARCH_FIELDS_FROM_SOURCE } from '@kbn/discover-utils'; import { omit } from 'lodash'; import { getUnifiedDocViewerServices } from '../../plugin'; import { useEsDocSearch } from '../../hooks'; @@ -36,9 +36,6 @@ interface SourceViewerProps { onRefresh: () => void; } -// Ihe number of lines displayed without scrolling used for classic table, which renders the component -// inline limitation was necessary to enable virtualized scrolling, which improves performance -export const MAX_LINES_CLASSIC_TABLE = 500; // Minimum height for the source content to guarantee minimum space when the flyout is scrollable. export const MIN_HEIGHT = 400; @@ -57,10 +54,6 @@ export const DocViewerSource = ({ const [jsonValue, setJsonValue] = useState(''); const { uiSettings } = getUnifiedDocViewerServices(); const useNewFieldsApi = !uiSettings.get(SEARCH_FIELDS_FROM_SOURCE); - const useDocExplorer = !isLegacyTableEnabled({ - uiSettings, - isEsqlMode: Array.isArray(textBasedHits), - }); const [requestState, hit] = useEsDocSearch({ id, index, @@ -75,9 +68,7 @@ export const DocViewerSource = ({ } }, [requestState, hit]); - // setting editor height - // - classic view: based on lines height and count to stretch and fit its content - // - explorer: to fill the available space of the document flyout + // setting editor height to fill the available space of the document flyout useEffect(() => { if (!editor) { return; @@ -88,11 +79,7 @@ export const DocViewerSource = ({ return; } - const height = getHeight( - editor, - useDocExplorer, - decreaseAvailableHeightBy ?? DEFAULT_MARGIN_BOTTOM - ); + const height = getHeight(editor, decreaseAvailableHeightBy ?? DEFAULT_MARGIN_BOTTOM); if (height === 0) { return; } @@ -102,7 +89,7 @@ export const DocViewerSource = ({ } else { setEditorHeight(height); } - }, [editor, jsonValue, useDocExplorer, setEditorHeight, decreaseAvailableHeightBy]); + }, [editor, jsonValue, setEditorHeight, decreaseAvailableHeightBy]); const loadingState = (
diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/legacy/index.ts b/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/legacy/index.ts deleted file mode 100644 index 3e4ca39981359..0000000000000 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/legacy/index.ts +++ /dev/null @@ -1,14 +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 { DocViewerLegacyTable } from './table'; - -// Required for usage in React.lazy -// eslint-disable-next-line import/no-default-export -export default DocViewerLegacyTable; diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/legacy/table.test.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/legacy/table.test.tsx deleted file mode 100644 index 43f2d21eb8fd1..0000000000000 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/legacy/table.test.tsx +++ /dev/null @@ -1,452 +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 { mountWithIntl } from '@kbn/test-jest-helpers'; -import { findTestSubject } from '@elastic/eui/lib/test'; -import { DocViewerLegacyTable } from './table'; -import type { DataView } from '@kbn/data-views-plugin/public'; -import type { DocViewRenderProps } from '@kbn/unified-doc-viewer/types'; -import { buildDataTableRecord } from '@kbn/discover-utils'; -import { setUnifiedDocViewerServices } from '../../../plugin'; -import type { UnifiedDocViewerServices } from '../../../types'; - -const services = { - uiSettings: { - get: (key: string) => { - if (key === 'discover:showMultiFields') { - return true; - } - }, - }, - fieldFormats: { - getDefaultInstance: jest.fn(() => ({ convert: (value: unknown) => value })), - getFormatterForField: jest.fn(() => ({ convert: (value: unknown) => value })), - }, -}; - -const dataView = { - fields: { - getAll: () => [ - { - name: '_index', - type: 'string', - scripted: false, - filterable: true, - }, - { - name: 'message', - type: 'string', - scripted: false, - filterable: false, - }, - { - name: 'extension', - type: 'string', - scripted: false, - filterable: true, - }, - { - name: 'bytes', - type: 'number', - scripted: false, - filterable: true, - }, - { - name: 'scripted', - type: 'number', - scripted: true, - filterable: false, - }, - ], - }, - metaFields: ['_index', '_score'], - getFormatterForField: jest.fn(() => ({ convert: (value: unknown) => value })), -} as unknown as DataView; - -dataView.fields.getByName = (name: string) => { - return dataView.fields.getAll().find((field) => field.name === name); -}; - -const mountComponent = ( - props: DocViewRenderProps, - overrides?: Partial -) => { - setUnifiedDocViewerServices({ ...services, ...overrides } as UnifiedDocViewerServices); - return mountWithIntl(); -}; - -describe('DocViewTable at Discover', () => { - // At Discover's main view, all buttons are rendered - // check for existence of action buttons and warnings - - const hit = buildDataTableRecord( - { - _index: 'logstash-2014.09.09', - _id: 'id123', - _score: 1, - _source: { - message: - 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. \ - Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus \ - et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, \ - ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. \ - Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, \ - rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. \ - Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. \ - Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut', - extension: 'html', - not_mapped: 'yes', - bytes: 100, - objectArray: [{ foo: true }], - relatedContent: { - test: 1, - }, - scripted: 123, - _underscore: 123, - }, - }, - dataView - ); - - const props = { - hit, - columns: ['extension'], - dataView, - filter: jest.fn(), - onAddColumn: jest.fn(), - onRemoveColumn: jest.fn(), - }; - const component = mountComponent(props); - [ - { - _property: '_index', - addInclusiveFilterButton: true, - noMappingWarning: false, - toggleColumnButton: true, - underscoreWarning: false, - }, - { - _property: 'message', - addInclusiveFilterButton: false, - noMappingWarning: false, - toggleColumnButton: true, - underscoreWarning: false, - }, - { - _property: '_underscore', - addInclusiveFilterButton: false, - noMappingWarning: false, - toggleColumnButton: true, - underScoreWarning: true, - }, - { - _property: 'scripted', - addInclusiveFilterButton: false, - noMappingWarning: false, - toggleColumnButton: true, - underScoreWarning: false, - }, - { - _property: 'not_mapped', - addInclusiveFilterButton: false, - noMappingWarning: true, - toggleColumnButton: true, - underScoreWarning: false, - }, - ].forEach((check) => { - const rowComponent = findTestSubject(component, `tableDocViewRow-${check._property}`); - - it(`renders row for ${check._property}`, () => { - expect(rowComponent.length).toBe(1); - }); - - (['addInclusiveFilterButton', 'toggleColumnButton', 'underscoreWarning'] as const).forEach( - (element) => { - const elementExist = check[element]; - - if (typeof elementExist === 'boolean') { - const btn = findTestSubject(rowComponent, element, '^='); - - it(`renders ${element} for '${check._property}' correctly`, () => { - const disabled = btn.length ? btn.props().disabled : true; - const clickAble = btn.length && !disabled ? true : false; - expect(clickAble).toBe(elementExist); - }); - } - } - ); - }); -}); - -describe('DocViewTable at Discover Context', () => { - // here no toggleColumnButtons are rendered - const hit = buildDataTableRecord( - { - _index: 'logstash-2014.09.09', - _id: 'id123', - _score: 1, - _source: { - message: - 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. \ - Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus \ - et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, \ - ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. \ - Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, \ - rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. \ - Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. \ - Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut', - }, - }, - dataView - ); - const props = { - hit, - columns: ['extension'], - dataView, - filter: jest.fn(), - }; - - const component = mountComponent(props); - - it(`renders no toggleColumnButton`, () => { - const foundLength = findTestSubject(component, 'toggleColumnButtons').length; - expect(foundLength).toBe(0); - }); - - it(`renders addInclusiveFilterButton`, () => { - const row = findTestSubject(component, `tableDocViewRow-_index`); - const btn = findTestSubject(row, 'addInclusiveFilterButton'); - expect(btn.length).toBe(1); - btn.simulate('click'); - expect(props.filter).toBeCalled(); - }); -}); - -describe('DocViewTable at Discover Doc', () => { - const hit = buildDataTableRecord( - { - _index: 'logstash-2014.09.09', - _score: 1, - _id: 'id123', - _source: { - extension: 'html', - not_mapped: 'yes', - }, - }, - dataView - ); - // here no action buttons are rendered - const props = { - hit, - dataView, - hideActionsColumn: true, - }; - const component = mountComponent(props); - const foundLength = findTestSubject(component, 'addInclusiveFilterButton').length; - - it(`renders no action buttons`, () => { - expect(foundLength).toBe(0); - }); -}); - -describe('DocViewTable at Discover Doc with Fields API', () => { - const dataViewCommerce = { - fields: { - getAll: () => [ - { - name: '_index', - type: 'string', - scripted: false, - filterable: true, - }, - { - name: 'category', - type: 'string', - scripted: false, - filterable: true, - }, - { - name: 'category.keyword', - displayName: 'category.keyword', - type: 'string', - scripted: false, - filterable: true, - spec: { - subType: { - multi: { - parent: 'category', - }, - }, - }, - }, - { - name: 'customer_first_name', - type: 'string', - scripted: false, - filterable: true, - }, - { - name: 'customer_first_name.keyword', - displayName: 'customer_first_name.keyword', - type: 'string', - scripted: false, - filterable: false, - spec: { - subType: { - multi: { - parent: 'customer_first_name', - }, - }, - }, - }, - { - name: 'customer_first_name.nickname', - displayName: 'customer_first_name.nickname', - type: 'string', - scripted: false, - filterable: false, - spec: { - subType: { - multi: { - parent: 'customer_first_name', - }, - }, - }, - }, - { - name: 'city', - displayName: 'city', - type: 'keyword', - isMapped: true, - readFromDocValues: true, - searchable: true, - shortDotsEnable: false, - scripted: false, - filterable: false, - }, - { - name: 'city.raw', - displayName: 'city.raw', - type: 'string', - isMapped: true, - spec: { - subType: { - multi: { - parent: 'city', - }, - }, - }, - shortDotsEnable: false, - scripted: false, - filterable: false, - }, - ], - }, - metaFields: ['_index', '_type', '_score', '_id'], - getFormatterForField: jest.fn(() => ({ convert: (value: unknown) => value })), - } as unknown as DataView; - - dataViewCommerce.fields.getByName = (name: string) => { - return dataViewCommerce.fields.getAll().find((field) => field.name === name); - }; - - const fieldsHit = buildDataTableRecord( - { - _index: 'logstash-2014.09.09', - _id: 'id123', - _score: 1.0, - fields: { - category: "Women's Clothing", - 'category.keyword': "Women's Clothing", - customer_first_name: 'Betty', - 'customer_first_name.keyword': 'Betty', - 'customer_first_name.nickname': 'Betsy', - 'city.raw': 'Los Angeles', - }, - }, - dataView - ); - const props = { - hit: fieldsHit, - columns: ['Document'], - dataView: dataViewCommerce, - filter: jest.fn(), - onAddColumn: jest.fn(), - onRemoveColumn: jest.fn(), - }; - - it('renders multifield rows if showMultiFields flag is set', () => { - const component = mountComponent(props); - - const categoryKeywordRow = findTestSubject(component, 'tableDocViewRow-category.keyword'); - expect(categoryKeywordRow.length).toBe(1); - - expect(findTestSubject(component, 'tableDocViewRow-customer_first_name.keyword').length).toBe( - 1 - ); - expect(findTestSubject(component, 'tableDocViewRow-customer_first_name.nickname').length).toBe( - 1 - ); - - expect( - findTestSubject(component, 'tableDocViewRow-category.keyword-multifieldBadge').length - ).toBe(1); - - expect( - findTestSubject(component, 'tableDocViewRow-customer_first_name.keyword-multifieldBadge') - .length - ).toBe(1); - - expect( - findTestSubject(component, 'tableDocViewRow-customer_first_name.nickname-multifieldBadge') - .length - ).toBe(1); - - expect(findTestSubject(component, 'tableDocViewRow-city.raw').length).toBe(1); - }); - - it('does not render multifield rows if showMultiFields flag is not set', () => { - const overridedServices = { - uiSettings: { - get: (key: string) => { - if (key === 'discover:showMultiFields') { - return false; - } - }, - }, - } as unknown as UnifiedDocViewerServices; - const component = mountComponent(props, overridedServices); - - const categoryKeywordRow = findTestSubject(component, 'tableDocViewRow-category.keyword'); - expect(categoryKeywordRow.length).toBe(0); - - expect(findTestSubject(component, 'tableDocViewRow-customer_first_name.keyword').length).toBe( - 0 - ); - - expect(findTestSubject(component, 'tableDocViewRow-customer_first_name.nickname').length).toBe( - 0 - ); - - expect( - findTestSubject(component, 'tableDocViewRow-customer_first_name.keyword-multifieldBadge') - .length - ).toBe(0); - - expect(findTestSubject(component, 'tableDocViewRow-customer_first_name').length).toBe(1); - expect( - findTestSubject(component, 'tableDocViewRow-customer_first_name.nickname-multifieldBadge') - .length - ).toBe(0); - - expect(findTestSubject(component, 'tableDocViewRow-city').length).toBe(0); - expect(findTestSubject(component, 'tableDocViewRow-city.raw').length).toBe(1); - }); -}); diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/legacy/table.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/legacy/table.tsx deleted file mode 100644 index e834bef5d664e..0000000000000 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/legacy/table.tsx +++ /dev/null @@ -1,123 +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 '../table.scss'; -import React, { useCallback, useMemo } from 'react'; -import { EuiInMemoryTable } from '@elastic/eui'; -import { getFieldIconType } from '@kbn/field-utils/src/utils/get_field_icon_type'; -import { - SHOW_MULTIFIELDS, - formatFieldValue, - getIgnoredReason, - getShouldShowFieldHandler, - isNestedFieldParent, -} from '@kbn/discover-utils'; -import type { DocViewRenderProps, FieldRecordLegacy } from '@kbn/unified-doc-viewer/types'; -import { getUnifiedDocViewerServices } from '../../../plugin'; -import { ACTIONS_COLUMN, MAIN_COLUMNS } from './table_columns'; - -export const DocViewerLegacyTable = ({ - columns, - hit, - dataView, - hideActionsColumn, - filter, - onAddColumn, - onRemoveColumn, -}: DocViewRenderProps) => { - const { fieldFormats, uiSettings } = getUnifiedDocViewerServices(); - const showMultiFields = useMemo(() => uiSettings.get(SHOW_MULTIFIELDS), [uiSettings]); - - const mapping = useCallback((name: string) => dataView.fields.getByName(name), [dataView.fields]); - const tableColumns = useMemo(() => { - return !hideActionsColumn ? [ACTIONS_COLUMN, ...MAIN_COLUMNS] : MAIN_COLUMNS; - }, [hideActionsColumn]); - - const onToggleColumn = useMemo(() => { - if (!onRemoveColumn || !onAddColumn || !columns) { - return undefined; - } - return (field: string) => { - if (columns.includes(field)) { - onRemoveColumn(field); - } else { - onAddColumn(field); - } - }; - }, [onRemoveColumn, onAddColumn, columns]); - - const onSetRowProps = useCallback(({ field: { field } }: FieldRecordLegacy) => { - return { - key: field, - className: 'kbnDocViewer__tableRow', - 'data-test-subj': `tableDocViewRow-${field}`, - }; - }, []); - - const shouldShowFieldHandler = useMemo( - () => getShouldShowFieldHandler(Object.keys(hit.flattened), dataView, showMultiFields), - [hit.flattened, dataView, showMultiFields] - ); - - const items: FieldRecordLegacy[] = Object.keys(hit.flattened) - .filter(shouldShowFieldHandler) - .sort((fieldA, fieldB) => { - const mappingA = mapping(fieldA); - const mappingB = mapping(fieldB); - const nameA = !mappingA || !mappingA.displayName ? fieldA : mappingA.displayName; - const nameB = !mappingB || !mappingB.displayName ? fieldB : mappingB.displayName; - return nameA.localeCompare(nameB); - }) - .map((field) => { - const fieldMapping = mapping(field); - const displayName = fieldMapping?.displayName ?? field; - const fieldType = isNestedFieldParent(field, dataView) - ? 'nested' - : fieldMapping - ? getFieldIconType(fieldMapping) - : undefined; - const ignored = getIgnoredReason(fieldMapping ?? field, hit.raw._ignored); - return { - action: { - onToggleColumn, - onFilter: filter, - isActive: !!columns?.includes(field), - flattenedField: hit.flattened[field], - }, - field: { - field, - displayName, - fieldMapping, - fieldType, - scripted: Boolean(fieldMapping?.scripted), - }, - value: { - formattedValue: formatFieldValue( - hit.flattened[field], - hit.raw, - fieldFormats, - dataView, - fieldMapping - ), - ignored, - }, - }; - }); - - return ( - - ); -}; diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/legacy/table_cell_actions.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/legacy/table_cell_actions.tsx deleted file mode 100644 index c63c8c1ae70e9..0000000000000 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/legacy/table_cell_actions.tsx +++ /dev/null @@ -1,67 +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 { DataViewField } from '@kbn/data-views-plugin/public'; -import type { DocViewFilterFn } from '@kbn/unified-doc-viewer/types'; -import { DocViewTableRowBtnFilterRemove } from './table_row_btn_filter_remove'; -import { DocViewTableRowBtnFilterExists } from './table_row_btn_filter_exists'; -import { DocViewTableRowBtnToggleColumn } from './table_row_btn_toggle_column'; -import { DocViewTableRowBtnFilterAdd } from './table_row_btn_filter_add'; - -interface TableActionsProps { - field: string; - isActive: boolean; - flattenedField: unknown; - fieldMapping?: DataViewField; - onFilter: DocViewFilterFn; - onToggleColumn: ((field: string) => void) | undefined; - ignoredValue: boolean; -} - -export const TableActions = ({ - isActive, - field, - fieldMapping, - flattenedField, - onToggleColumn, - onFilter, - ignoredValue, -}: TableActionsProps) => { - return ( -
- {onFilter && ( - onFilter(fieldMapping, flattenedField, '+')} - /> - )} - {onFilter && ( - onFilter(fieldMapping, flattenedField, '-')} - /> - )} - {onToggleColumn && ( - onToggleColumn(field)} - /> - )} - {onFilter && ( - onFilter('_exists_', field, '+')} - scripted={fieldMapping && fieldMapping.scripted} - /> - )} -
- ); -}; diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/legacy/table_columns.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/legacy/table_columns.tsx deleted file mode 100644 index 3b510f6130229..0000000000000 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/legacy/table_columns.tsx +++ /dev/null @@ -1,115 +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 { EuiBasicTableColumn, EuiText } from '@elastic/eui'; -import React from 'react'; -import { FormattedMessage } from '@kbn/i18n-react'; -import type { FieldRecordLegacy } from '@kbn/unified-doc-viewer/types'; -import { FieldName } from '@kbn/unified-doc-viewer'; -import { TableActions } from './table_cell_actions'; -import { TableFieldValue } from '../table_cell_value'; - -export const ACTIONS_COLUMN: EuiBasicTableColumn = { - field: 'action', - className: 'kbnDocViewer__tableActionsCell', - width: '108px', - mobileOptions: { header: false }, - name: ( - - - - - - ), - render: ( - { flattenedField, isActive, onFilter, onToggleColumn }: FieldRecordLegacy['action'], - { field: { field, fieldMapping }, value: { ignored } }: FieldRecordLegacy - ) => { - return ( - - ); - }, -}; -export const MAIN_COLUMNS: Array> = [ - { - field: 'field', - className: 'kbnDocViewer__tableFieldNameCell', - mobileOptions: { header: false }, - width: '30%', - name: ( - - - - - - ), - render: ({ - field, - fieldType, - displayName, - fieldMapping, - scripted, - }: FieldRecordLegacy['field']) => { - return field ? ( - - ) : ( -   - ); - }, - }, - { - field: 'value', - className: 'kbnDocViewer__tableValueCell', - mobileOptions: { header: false }, - name: ( - - - - - - ), - render: ( - { formattedValue, ignored }: FieldRecordLegacy['value'], - { field: { field }, action: { flattenedField } }: FieldRecordLegacy - ) => { - return ( - - ); - }, - }, -]; diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/legacy/table_row_btn_filter_add.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/legacy/table_row_btn_filter_add.tsx deleted file mode 100644 index 12e3196cac1c9..0000000000000 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/legacy/table_row_btn_filter_add.tsx +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 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 { FormattedMessage } from '@kbn/i18n-react'; -import { EuiToolTip, EuiButtonIcon } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; - -export interface Props { - onClick: () => void; - disabled: boolean; -} - -export function DocViewTableRowBtnFilterAdd({ onClick, disabled = false }: Props) { - const tooltipContent = disabled ? ( - - ) : ( - - ); - - return ( - - - - ); -} diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/legacy/table_row_btn_filter_exists.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/legacy/table_row_btn_filter_exists.tsx deleted file mode 100644 index 2685b4b7b8e69..0000000000000 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/legacy/table_row_btn_filter_exists.tsx +++ /dev/null @@ -1,63 +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 { FormattedMessage } from '@kbn/i18n-react'; -import { EuiToolTip, EuiButtonIcon } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; - -export interface Props { - onClick: () => void; - disabled?: boolean; - scripted?: boolean; -} - -export function DocViewTableRowBtnFilterExists({ - onClick, - disabled = false, - scripted = false, -}: Props) { - const tooltipContent = disabled ? ( - scripted ? ( - - ) : ( - - ) - ) : ( - - ); - - return ( - - - - ); -} diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/legacy/table_row_btn_filter_remove.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/legacy/table_row_btn_filter_remove.tsx deleted file mode 100644 index c9073fc9831c6..0000000000000 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/legacy/table_row_btn_filter_remove.tsx +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 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 { FormattedMessage } from '@kbn/i18n-react'; -import { i18n } from '@kbn/i18n'; -import { EuiToolTip, EuiButtonIcon } from '@elastic/eui'; - -export interface Props { - onClick: () => void; - disabled?: boolean; -} - -export function DocViewTableRowBtnFilterRemove({ onClick, disabled = false }: Props) { - const tooltipContent = disabled ? ( - - ) : ( - - ); - - return ( - - - - ); -} diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/legacy/table_row_btn_toggle_column.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/legacy/table_row_btn_toggle_column.tsx deleted file mode 100644 index 9f999248cf269..0000000000000 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/legacy/table_row_btn_toggle_column.tsx +++ /dev/null @@ -1,70 +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 { FormattedMessage } from '@kbn/i18n-react'; -import { i18n } from '@kbn/i18n'; -import { EuiToolTip, EuiButtonIcon } from '@elastic/eui'; - -export interface Props { - active: boolean; - disabled?: boolean; - onClick: () => void; - fieldname: string; -} - -export function DocViewTableRowBtnToggleColumn({ - onClick, - active, - disabled = false, - fieldname = '', -}: Props) { - if (disabled) { - return ( - - ); - } - return ( - - } - > - - - ); -} diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/table.scss b/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/table.scss index 64e700c73fca5..348c8c9784ad8 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/table.scss +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/table.scss @@ -1,59 +1,3 @@ -.kbnDocViewer { - .euiTableRowCell { - vertical-align: top; - } -} - -.kbnDocViewer__tableRow { - font-size: $euiFontSizeXS; - font-family: $euiCodeFontFamily; - - // set min-width for each column except actions - .euiTableRowCell:nth-child(n+2) { - min-width: $euiSizeM * 9; - } - - .kbnDocViewer__buttons { - // Show all icons if one is focused, - &:focus-within { - .kbnDocViewer__actionButton { - opacity: 1; - } - } - } - - &:hover { - .kbnDocViewer__actionButton { - opacity: 1; - } - } - - .kbnDocViewer__actionButton { - @include euiBreakpoint('m', 'l', 'xl') { - opacity: 0; - } - - &:focus { - opacity: 1; - } - } -} - -.kbnDocViewer__tableActionsCell, -.kbnDocViewer__tableFieldNameCell { - .euiTableCellContent { - align-items: flex-start; - padding: $euiSizeXS; - } -} - -.kbnDocViewer__tableValueCell { - .euiTableCellContent { - flex-direction: column; - align-items: flex-start; - } -} - .kbnDocViewer__value { word-break: break-all; word-wrap: break-word; diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/table_cell_value.test.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/table_cell_value.test.tsx index ca68b57e563bd..d9470a7290647 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/table_cell_value.test.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/table_cell_value.test.tsx @@ -9,24 +9,10 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; -import { TRUNCATE_MAX_HEIGHT, TRUNCATE_MAX_HEIGHT_DEFAULT_VALUE } from '@kbn/discover-utils'; -import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import { TableFieldValue } from './table_cell_value'; import { setUnifiedDocViewerServices } from '../../plugin'; import { mockUnifiedDocViewerServices } from '../../__mocks__'; -const mockServices = { - ...mockUnifiedDocViewerServices, -}; - -let mockTruncateMaxHeightSetting: number | undefined; -mockServices.uiSettings.get = ((key: string) => { - if (key === TRUNCATE_MAX_HEIGHT) { - return mockTruncateMaxHeightSetting ?? TRUNCATE_MAX_HEIGHT_DEFAULT_VALUE; - } - return; -}) as IUiSettingsClient['get']; - setUnifiedDocViewerServices(mockUnifiedDocViewerServices); let mockScrollHeight = 0; @@ -35,7 +21,6 @@ jest.spyOn(HTMLElement.prototype, 'scrollHeight', 'get').mockImplementation(() = describe('TableFieldValue', () => { afterEach(() => { mockScrollHeight = 0; - mockTruncateMaxHeightSetting = undefined; }); it('should render correctly', async () => { @@ -121,97 +106,4 @@ describe('TableFieldValue', () => { expect(valueElement.getAttribute('css')).toBeNull(); expect(valueElement.classList.contains('kbnDocViewer__value--truncated')).toBe(false); }); - - it('should truncate a long value in legacy table correctly', async () => { - mockScrollHeight = 1000; - - const value = 'long value'.repeat(300); - render( - - ); - - expect(screen.getByText(value)).toBeInTheDocument(); - - let toggleButton = screen.getByTestId('toggleLongFieldValue-message'); - expect(toggleButton).toBeInTheDocument(); - expect(toggleButton.getAttribute('aria-expanded')).toBe('false'); - - let valueElement = screen.getByTestId('tableDocViewRow-message-value'); - expect(valueElement.getAttribute('css')).toBeDefined(); - expect(valueElement.classList.contains('kbnDocViewer__value--truncated')).toBe(true); - - toggleButton.click(); - - toggleButton = screen.getByTestId('toggleLongFieldValue-message'); - expect(toggleButton).toBeInTheDocument(); - expect(toggleButton.getAttribute('aria-expanded')).toBe('true'); - - valueElement = screen.getByTestId('tableDocViewRow-message-value'); - expect(valueElement.getAttribute('css')).toBeNull(); - expect(valueElement.classList.contains('kbnDocViewer__value--truncated')).toBe(false); - - toggleButton.click(); - - toggleButton = screen.getByTestId('toggleLongFieldValue-message'); - expect(toggleButton).toBeInTheDocument(); - expect(toggleButton.getAttribute('aria-expanded')).toBe('false'); - - valueElement = screen.getByTestId('tableDocViewRow-message-value'); - expect(valueElement.getAttribute('css')).toBeDefined(); - expect(valueElement.classList.contains('kbnDocViewer__value--truncated')).toBe(true); - }); - - it('should not truncate a long value in legacy table if limit is not reached', async () => { - mockScrollHeight = 112; - - const value = 'long value'.repeat(300); - render( - - ); - - expect(screen.getByText(value)).toBeInTheDocument(); - expect(screen.queryByTestId('toggleLongFieldValue-message')).toBeNull(); - - const valueElement = screen.getByTestId('tableDocViewRow-message-value'); - expect(valueElement.getAttribute('css')).toBeNull(); - expect(valueElement.classList.contains('kbnDocViewer__value--truncated')).toBe(false); - }); - - it('should not truncate a long value in legacy table if setting is 0', async () => { - mockScrollHeight = 1000; - mockTruncateMaxHeightSetting = 0; - - const value = 'long value'.repeat(300); - render( - - ); - - expect(screen.getByText(value)).toBeInTheDocument(); - expect(screen.queryByTestId('toggleLongFieldValue-message')).toBeNull(); - - const valueElement = screen.getByTestId('tableDocViewRow-message-value'); - expect(valueElement.getAttribute('css')).toBeNull(); - expect(valueElement.classList.contains('kbnDocViewer__value--truncated')).toBe(false); - }); }); diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/table_cell_value.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/table_cell_value.tsx index 3afe935307ab2..556f671e67cbd 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/table_cell_value.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/table_cell_value.tsx @@ -21,9 +21,7 @@ import { import classnames from 'classnames'; import React, { Fragment, useCallback, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { IgnoredReason, TRUNCATE_MAX_HEIGHT } from '@kbn/discover-utils'; -import { FieldRecordLegacy } from '@kbn/unified-doc-viewer/types'; -import { getUnifiedDocViewerServices } from '../../plugin'; +import { IgnoredReason } from '@kbn/discover-utils'; const DOC_VIEWER_DEFAULT_TRUNCATE_MAX_HEIGHT = 110; @@ -96,14 +94,14 @@ const IgnoreWarning: React.FC = React.memo(({ rawValue, reas ); }); -type TableFieldValueProps = Pick & { - formattedValue: FieldRecordLegacy['value']['formattedValue']; +interface TableFieldValueProps { + field: string; + formattedValue: string; rawValue: unknown; ignoreReason?: IgnoredReason; isDetails?: boolean; // true when inside EuiDataGrid cell popover - isLegacy?: boolean; // true when inside legacy table isHighlighted?: boolean; // whether it's matching a search term -}; +} export const TableFieldValue = ({ formattedValue, @@ -111,14 +109,10 @@ export const TableFieldValue = ({ rawValue, ignoreReason, isDetails, - isLegacy, isHighlighted, }: TableFieldValueProps) => { const { euiTheme } = useEuiTheme(); - const { uiSettings } = getUnifiedDocViewerServices(); - const truncationHeight = isLegacy - ? uiSettings.get(TRUNCATE_MAX_HEIGHT) - : DOC_VIEWER_DEFAULT_TRUNCATE_MAX_HEIGHT; + const truncationHeight = DOC_VIEWER_DEFAULT_TRUNCATE_MAX_HEIGHT; const [containerRef, setContainerRef] = useState(null); useResizeObserver(containerRef); diff --git a/src/plugins/unified_doc_viewer/public/plugin.tsx b/src/plugins/unified_doc_viewer/public/plugin.tsx index 41db23a52591c..4806d5fce0918 100644 --- a/src/plugins/unified_doc_viewer/public/plugin.tsx +++ b/src/plugins/unified_doc_viewer/public/plugin.tsx @@ -9,7 +9,6 @@ import React from 'react'; import type { CoreSetup, Plugin } from '@kbn/core/public'; -import { isLegacyTableEnabled } from '@kbn/discover-utils'; import { i18n } from '@kbn/i18n'; import { DocViewsRegistry } from '@kbn/unified-doc-viewer'; import { EuiDelayRender, EuiSkeletonText } from '@elastic/eui'; @@ -31,9 +30,6 @@ const fallback = ( ); -const LazyDocViewerLegacyTable = dynamic(() => import('./components/doc_viewer_table/legacy'), { - fallback, -}); const LazyDocViewerTable = dynamic(() => import('./components/doc_viewer_table'), { fallback }); const LazySourceViewer = dynamic(() => import('./components/doc_viewer_source'), { fallback }); @@ -65,17 +61,7 @@ export class UnifiedDocViewerPublicPlugin }), order: 10, component: (props) => { - const { textBasedHits } = props; - const { uiSettings } = getUnifiedDocViewerServices(); - - const LazyDocView = isLegacyTableEnabled({ - uiSettings, - isEsqlMode: Array.isArray(textBasedHits), - }) - ? LazyDocViewerLegacyTable - : LazyDocViewerTable; - - return ; + return ; }, }); diff --git a/test/functional/apps/console/_context_menu.ts b/test/functional/apps/console/_context_menu.ts index 4ee3c2cca40a7..0e126467c04c2 100644 --- a/test/functional/apps/console/_context_menu.ts +++ b/test/functional/apps/console/_context_menu.ts @@ -70,6 +70,55 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { } }); + it('doesnt allow to copy kbn requests as anything other than curl', async () => { + const canReadClipboard = await browser.checkBrowserPermission('clipboard-read'); + + await PageObjects.console.clearEditorText(); + await PageObjects.console.enterText('GET _search\n'); + + // Add a kbn request + // pressEnter + await PageObjects.console.enterText('GET kbn:/api/spaces/space'); + // Make sure to select the es and kbn request + await PageObjects.console.selectAllRequests(); + + await PageObjects.console.clickContextMenu(); + await PageObjects.console.clickCopyAsButton(); + + let resultToast = await toasts.getElementByIndex(1); + let toastText = await resultToast.getVisibleText(); + + expect(toastText).to.be('Requests copied to clipboard as curl'); + + // Check if the clipboard has the curl request + if (canReadClipboard) { + const clipboardText = await browser.getClipboardValue(); + expect(clipboardText).to.contain('curl -X GET'); + } + + // Wait until async operation is done + await PageObjects.common.sleep(1000); + + // Focus editor once again + await PageObjects.console.focusInputEditor(); + + // Try to copy as javascript + await PageObjects.console.clickContextMenu(); + await PageObjects.console.changeLanguageAndCopy('javascript'); + + resultToast = await toasts.getElementByIndex(2); + toastText = await resultToast.getVisibleText(); + + expect(toastText).to.be('Kibana requests can only be copied as curl'); + + // Since we tried to copy as javascript, the clipboard should still have + // the curl request + if (canReadClipboard) { + const clipboardText = await browser.getClipboardValue(); + expect(clipboardText).to.contain('curl -X GET'); + } + }); + it.skip('allows to change default language', async () => { await PageObjects.console.clickContextMenu(); diff --git a/test/functional/apps/context/classic/_discover_navigation.ts b/test/functional/apps/context/classic/_discover_navigation.ts deleted file mode 100644 index 476a4bfac9e18..0000000000000 --- a/test/functional/apps/context/classic/_discover_navigation.ts +++ /dev/null @@ -1,168 +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 expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../ftr_provider_context'; - -const TEST_COLUMN_NAMES = ['@message']; -const TEST_FILTER_COLUMN_NAMES = [ - ['extension', 'jpg', 'extension.raw'], - ['geo.src', 'IN', 'geo.src'], -]; - -export default function ({ getService, getPageObjects }: FtrProviderContext) { - const retry = getService('retry'); - const docTable = getService('docTable'); - const filterBar = getService('filterBar'); - const { common, discover, timePicker, dashboard, context, header, unifiedFieldList } = - getPageObjects([ - 'common', - 'discover', - 'timePicker', - 'dashboard', - 'context', - 'header', - 'unifiedFieldList', - ]); - const testSubjects = getService('testSubjects'); - const dashboardAddPanel = getService('dashboardAddPanel'); - const browser = getService('browser'); - const kibanaServer = getService('kibanaServer'); - - describe('context link in discover classic', () => { - before(async () => { - await timePicker.setDefaultAbsoluteRangeViaUiSettings(); - await kibanaServer.uiSettings.update({ - 'doc_table:legacy': true, - defaultIndex: 'logstash-*', - }); - await common.navigateToApp('discover'); - await header.waitUntilLoadingHasFinished(); - for (const columnName of TEST_COLUMN_NAMES) { - await unifiedFieldList.clickFieldListItemAdd(columnName); - } - - for (const [columnName, value] of TEST_FILTER_COLUMN_NAMES) { - await unifiedFieldList.clickFieldListItem(columnName); - await unifiedFieldList.clickFieldListPlusFilter(columnName, value); - } - }); - after(async () => { - await kibanaServer.uiSettings.replace({}); - }); - - it('should open the context view with the same columns', async () => { - const columnNames = await docTable.getHeaderFields(); - expect(columnNames).to.eql(['@timestamp', ...TEST_COLUMN_NAMES]); - }); - - it('should open the context view with the selected document as anchor and allows selecting next anchor', async () => { - /** - * Helper function to get the first timestamp of the document table - * @param isAnchorRow - determins if just the anchor row of context should be selected - */ - const getTimestamp = async (isAnchorRow: boolean = false) => { - const contextFields = await docTable.getFields({ isAnchorRow }); - return contextFields[0][0]; - }; - // get the timestamp of the first row - - const firstDiscoverTimestamp = await getTimestamp(); - - // check the anchor timestamp in the context view - await retry.waitFor('selected document timestamp matches anchor timestamp ', async () => { - // navigate to the context view - await docTable.clickRowToggle({ rowIndex: 0 }); - const rowActions = await docTable.getRowActions({ rowIndex: 0 }); - await rowActions[0].click(); - await context.waitUntilContextLoadingHasFinished(); - const anchorTimestamp = await getTimestamp(true); - return anchorTimestamp === firstDiscoverTimestamp; - }); - - await retry.waitFor('next anchor timestamp matches previous anchor timestamp', async () => { - // get the timestamp of the first row - const firstContextTimestamp = await getTimestamp(false); - await docTable.clickRowToggle({ rowIndex: 0 }); - const rowActions = await docTable.getRowActions({ rowIndex: 0 }); - await rowActions[0].click(); - await context.waitUntilContextLoadingHasFinished(); - const anchorTimestamp = await getTimestamp(true); - return anchorTimestamp === firstContextTimestamp; - }); - }); - - it('should open the context view with the filters disabled', async () => { - let disabledFilterCounter = 0; - for (const [_, value, columnId] of TEST_FILTER_COLUMN_NAMES) { - if (await filterBar.hasFilter(columnId, value, false)) { - disabledFilterCounter++; - } - } - expect(disabledFilterCounter).to.be(TEST_FILTER_COLUMN_NAMES.length); - }); - - // bugfix: https://github.com/elastic/kibana/issues/92099 - it('should navigate to the first document and then back to discover', async () => { - await context.waitUntilContextLoadingHasFinished(); - - // navigate to the doc view - await docTable.clickRowToggle({ rowIndex: 0 }); - - // click the open action - await retry.try(async () => { - const rowActions = await docTable.getRowActions({ rowIndex: 0 }); - if (!rowActions.length) { - throw new Error('row actions empty, trying again'); - } - await rowActions[1].click(); - }); - - const hasDocHit = await testSubjects.exists('doc-hit'); - expect(hasDocHit).to.be(true); - - await testSubjects.click('~breadcrumb & ~first'); - await discover.waitForDiscoverAppOnScreen(); - await discover.waitForDocTableLoadingComplete(); - }); - - it('navigates to doc view from embeddable', async () => { - await common.navigateToApp('discover'); - await discover.saveSearch('my classic search'); - await header.waitUntilLoadingHasFinished(); - - await dashboard.navigateToApp(); - await dashboard.gotoDashboardLandingPage(); - await dashboard.clickNewDashboard(); - - await dashboardAddPanel.addSavedSearch('my classic search'); - await header.waitUntilLoadingHasFinished(); - - await docTable.clickRowToggle({ rowIndex: 0 }); - const rowActions = await docTable.getRowActions({ rowIndex: 0 }); - await rowActions[1].click(); - await common.sleep(250); - - // close popup - const alert = await browser.getAlert(); - await alert?.accept(); - if (await testSubjects.exists('confirmModalConfirmButton')) { - await testSubjects.click('confirmModalConfirmButton'); - } - - await retry.waitFor('navigate to doc view', async () => { - const currentUrl = await browser.getCurrentUrl(); - return currentUrl.includes('#/doc'); - }); - await retry.waitFor('doc view being rendered', async () => { - return await discover.isShowingDocViewer(); - }); - }); - }); -} diff --git a/test/functional/apps/context/classic/_filters.ts b/test/functional/apps/context/classic/_filters.ts deleted file mode 100644 index fd2ce16982f31..0000000000000 --- a/test/functional/apps/context/classic/_filters.ts +++ /dev/null @@ -1,69 +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 { FtrProviderContext } from '../../../ftr_provider_context'; - -const TEST_INDEX_PATTERN = 'logstash-*'; -const TEST_ANCHOR_ID = 'AU_x3_BrGFA8no6QjjaI'; -const TEST_ANCHOR_FILTER_FIELD = 'geo.src'; -const TEST_ANCHOR_FILTER_VALUE = 'IN'; -const TEST_COLUMN_NAMES = ['extension', 'geo.src']; - -export default function ({ getService, getPageObjects }: FtrProviderContext) { - const docTable = getService('docTable'); - const filterBar = getService('filterBar'); - const retry = getService('retry'); - const kibanaServer = getService('kibanaServer'); - - const PageObjects = getPageObjects(['common', 'context']); - - describe('context filters', function contextSize() { - before(async function () { - await kibanaServer.uiSettings.update({ 'doc_table:legacy': true }); - }); - - after(async function () { - await kibanaServer.uiSettings.replace({}); - }); - - beforeEach(async function () { - await PageObjects.context.navigateTo(TEST_INDEX_PATTERN, TEST_ANCHOR_ID, { - columns: TEST_COLUMN_NAMES, - }); - }); - - it('inclusive filter should be addable via expanded doc table rows', async function () { - await retry.waitFor(`filter ${TEST_ANCHOR_FILTER_FIELD} in filterbar`, async () => { - await docTable.toggleRowExpanded({ isAnchorRow: true }); - const anchorDetailsRow = await docTable.getAnchorDetailsRow(); - await docTable.addInclusiveFilter(anchorDetailsRow, TEST_ANCHOR_FILTER_FIELD); - await PageObjects.context.waitUntilContextLoadingHasFinished(); - - return await filterBar.hasFilter(TEST_ANCHOR_FILTER_FIELD, TEST_ANCHOR_FILTER_VALUE, true); - }); - await retry.waitFor(`filter matching docs in docTable`, async () => { - const fields = await docTable.getFields(); - return fields - .map((row) => row[2]) - .every((fieldContent) => fieldContent === TEST_ANCHOR_FILTER_VALUE); - }); - }); - - it('filter for presence should be addable via expanded doc table rows', async function () { - await docTable.toggleRowExpanded({ isAnchorRow: true }); - - await retry.waitFor('an exists filter in the filterbar', async () => { - const anchorDetailsRow = await docTable.getAnchorDetailsRow(); - await docTable.addExistsFilter(anchorDetailsRow, TEST_ANCHOR_FILTER_FIELD); - await PageObjects.context.waitUntilContextLoadingHasFinished(); - return await filterBar.hasFilter(TEST_ANCHOR_FILTER_FIELD, 'exists', true); - }); - }); - }); -} diff --git a/test/functional/apps/context/index.ts b/test/functional/apps/context/index.ts index 2cfc8cf855a2d..5cccbf163a8fb 100644 --- a/test/functional/apps/context/index.ts +++ b/test/functional/apps/context/index.ts @@ -33,9 +33,7 @@ export default function ({ getService, getPageObjects, loadTestFile }: FtrProvid loadTestFile(require.resolve('./_context_accessibility')); loadTestFile(require.resolve('./_context_navigation')); loadTestFile(require.resolve('./_discover_navigation')); - loadTestFile(require.resolve('./classic/_discover_navigation')); loadTestFile(require.resolve('./_filters')); - loadTestFile(require.resolve('./classic/_filters')); loadTestFile(require.resolve('./_size')); loadTestFile(require.resolve('./_date_nanos')); loadTestFile(require.resolve('./_date_nanos_custom_timestamp')); diff --git a/test/functional/apps/dashboard/group1/embeddable_data_grid.ts b/test/functional/apps/dashboard/group1/embeddable_data_grid.ts index 07eb00d5817be..014d1dd15ed4f 100644 --- a/test/functional/apps/dashboard/group1/embeddable_data_grid.ts +++ b/test/functional/apps/dashboard/group1/embeddable_data_grid.ts @@ -30,7 +30,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', - 'doc_table:legacy': false, }); await dashboard.navigateToApp(); await filterBar.ensureFieldEditorModalIsClosed(); diff --git a/test/functional/apps/dashboard/group2/dashboard_filter_bar.ts b/test/functional/apps/dashboard/group2/dashboard_filter_bar.ts index e1935a0839966..c80ee8e844cfa 100644 --- a/test/functional/apps/dashboard/group2/dashboard_filter_bar.ts +++ b/test/functional/apps/dashboard/group2/dashboard_filter_bar.ts @@ -23,12 +23,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const browser = getService('browser'); const queryBar = getService('queryBar'); const security = getService('security'); - const { dashboard, discover, header, timePicker } = getPageObjects([ - 'dashboard', - 'discover', - 'header', - 'timePicker', - ]); + const { dashboard, header, timePicker } = getPageObjects(['dashboard', 'header', 'timePicker']); describe('dashboard filter bar', () => { before(async () => { @@ -201,12 +196,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('are added when a cell magnifying glass is clicked', async function () { await dashboardAddPanel.addSavedSearch('Rendering-Test:-saved-search'); await dashboard.waitForRenderComplete(); - const isLegacyDefault = await discover.useLegacyTable(); - if (isLegacyDefault) { - await testSubjects.click('docTableCellFilter'); - } else { - await dataGrid.clickCellFilterForButtonExcludingControlColumns(1, 1); - } + await dataGrid.clickCellFilterForButtonExcludingControlColumns(1, 1); const filterCount = await filterBar.getFilterCount(); expect(filterCount).to.equal(1); }); diff --git a/test/functional/apps/dashboard/group3/dashboard_time_picker.ts b/test/functional/apps/dashboard/group3/dashboard_time_picker.ts index 2e5d4217909f0..a1fb468a9dd28 100644 --- a/test/functional/apps/dashboard/group3/dashboard_time_picker.ts +++ b/test/functional/apps/dashboard/group3/dashboard_time_picker.ts @@ -13,16 +13,10 @@ import { PIE_CHART_VIS_NAME } from '../../../page_objects/dashboard_page'; import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { - const dashboardExpect = getService('dashboardExpect'); const pieChart = getService('pieChart'); const elasticChart = getService('elasticChart'); const dashboardVisualizations = getService('dashboardVisualizations'); - const { dashboard, header, timePicker, discover } = getPageObjects([ - 'dashboard', - 'header', - 'timePicker', - 'discover', - ]); + const { dashboard, header, timePicker } = getPageObjects(['dashboard', 'header', 'timePicker']); const browser = getService('browser'); const log = getService('log'); const kibanaServer = getService('kibanaServer'); @@ -59,28 +53,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { fields: ['bytes', 'agent'], }); - const isLegacyDefault = await discover.useLegacyTable(); - if (isLegacyDefault) { - await dashboardExpect.docTableFieldCount(150); + const docCount = await dataGrid.getDocCount(); + expect(docCount).to.above(10); - // Set to time range with no data - await timePicker.setAbsoluteRange( - 'Jan 1, 2000 @ 00:00:00.000', - 'Jan 1, 2000 @ 01:00:00.000' - ); - await dashboardExpect.docTableFieldCount(0); - } else { - const docCount = await dataGrid.getDocCount(); - expect(docCount).to.above(10); - - // Set to time range with no data - await timePicker.setAbsoluteRange( - 'Jan 1, 2000 @ 00:00:00.000', - 'Jan 1, 2000 @ 01:00:00.000' - ); - const noResults = await dataGrid.hasNoResults(); - expect(noResults).to.be.ok(); - } + // Set to time range with no data + await timePicker.setAbsoluteRange('Jan 1, 2000 @ 00:00:00.000', 'Jan 1, 2000 @ 01:00:00.000'); + const noResults = await dataGrid.hasNoResults(); + expect(noResults).to.be.ok(); }); it('Timepicker start, end, interval values are set by url', async () => { diff --git a/test/functional/apps/discover/classic/_classic_table_doc_navigation.ts b/test/functional/apps/discover/classic/_classic_table_doc_navigation.ts deleted file mode 100644 index c56ecc020f2bf..0000000000000 --- a/test/functional/apps/discover/classic/_classic_table_doc_navigation.ts +++ /dev/null @@ -1,75 +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 expect from '@kbn/expect'; - -import { FtrProviderContext } from '../ftr_provider_context'; - -export default function ({ getService, getPageObjects }: FtrProviderContext) { - const docTable = getService('docTable'); - const filterBar = getService('filterBar'); - const testSubjects = getService('testSubjects'); - const { common, discover, timePicker } = getPageObjects(['common', 'discover', 'timePicker']); - const esArchiver = getService('esArchiver'); - const retry = getService('retry'); - const kibanaServer = getService('kibanaServer'); - - describe('classic table doc link', function contextSize() { - before(async () => { - await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); - await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover'); - await timePicker.setDefaultAbsoluteRangeViaUiSettings(); - await kibanaServer.uiSettings.update({ - defaultIndex: 'logstash-*', - 'doc_table:legacy': true, - 'discover:searchFieldsFromSource': true, - }); - }); - after(async () => { - await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover'); - await kibanaServer.uiSettings.replace({}); - }); - - beforeEach(async function () { - await timePicker.setDefaultAbsoluteRangeViaUiSettings(); - await common.navigateToApp('discover'); - await discover.waitForDocTableLoadingComplete(); - }); - - it('should open the doc view of the selected document', async function () { - // navigate to the doc view - await docTable.clickRowToggle({ rowIndex: 0 }); - - // click the open action - await retry.try(async () => { - const rowActions = await docTable.getRowActions({ rowIndex: 0 }); - if (!rowActions.length) { - throw new Error('row actions empty, trying again'); - } - await rowActions[1].click(); - }); - - await retry.waitFor('hit loaded', async () => { - const hasDocHit = await testSubjects.exists('doc-hit'); - return !!hasDocHit; - }); - }); - - it('should create an exists filter from the doc view of the selected document', async function () { - await discover.waitUntilSearchingHasFinished(); - - await docTable.toggleRowExpanded(); - const detailsRow = await docTable.getDetailsRow(); - await docTable.addExistsFilter(detailsRow, '@timestamp'); - - const hasExistsFilter = await filterBar.hasFilter('@timestamp', 'exists', true, false, false); - expect(hasExistsFilter).to.be(true); - }); - }); -} diff --git a/test/functional/apps/discover/classic/_discover_fields_api.ts b/test/functional/apps/discover/classic/_discover_fields_api.ts deleted file mode 100644 index e0fe867a25c8b..0000000000000 --- a/test/functional/apps/discover/classic/_discover_fields_api.ts +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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 expect from '@kbn/expect'; -import { FtrProviderContext } from '../ftr_provider_context'; - -export default function ({ getService, getPageObjects }: FtrProviderContext) { - const log = getService('log'); - const retry = getService('retry'); - const esArchiver = getService('esArchiver'); - const kibanaServer = getService('kibanaServer'); - const { common, discover, timePicker, settings, unifiedFieldList } = getPageObjects([ - 'common', - 'discover', - 'timePicker', - 'settings', - 'unifiedFieldList', - ]); - const defaultSettings = { - defaultIndex: 'logstash-*', - 'discover:searchFieldsFromSource': false, - 'doc_table:legacy': true, - }; - describe('discover uses fields API test', function describeIndexTests() { - before(async function () { - log.debug('load kibana index with default index pattern'); - await kibanaServer.savedObjects.clean({ types: ['search', 'index-pattern'] }); - await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover.json'); - await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); - await kibanaServer.uiSettings.replace(defaultSettings); - await common.navigateToApp('discover'); - await timePicker.setDefaultAbsoluteRange(); - }); - - after(async () => { - await kibanaServer.uiSettings.replace({}); - }); - - 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'); - 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(); - const expectedHitCount = '14,004'; - await retry.try(async function () { - expect(await discover.getHitCount()).to.be(expectedHitCount); - }); - }); - - 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'); - }); - - 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'); - }); - - it('displays _source viewer in doc viewer', async function () { - await discover.clickDocTableRowToggle(0); - await discover.isShowingDocViewer(); - await discover.clickDocViewerTab('doc_view_source'); - await discover.expectSourceViewerToExist(); - }); - - it('switches to _source column when fields API is no longer used', async function () { - await settings.navigateTo(); - await settings.clickKibanaSettings(); - await settings.toggleAdvancedSettingCheckbox('discover:searchFieldsFromSource'); - - await common.navigateToApp('discover'); - await timePicker.setDefaultAbsoluteRange(); - - expect(await discover.getDocHeader()).to.have.string('_source'); - }); - - it('switches to Document column when fields API is used', async function () { - await settings.navigateTo(); - await settings.clickKibanaSettings(); - await settings.toggleAdvancedSettingCheckbox('discover:searchFieldsFromSource'); - - await common.navigateToApp('discover'); - await timePicker.setDefaultAbsoluteRange(); - - expect(await discover.getDocHeader()).to.have.string('Document'); - }); - }); -} diff --git a/test/functional/apps/discover/classic/_doc_table.ts b/test/functional/apps/discover/classic/_doc_table.ts deleted file mode 100644 index 5c765a6b2ef21..0000000000000 --- a/test/functional/apps/discover/classic/_doc_table.ts +++ /dev/null @@ -1,309 +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 expect from '@kbn/expect'; -import { FtrProviderContext } from '../ftr_provider_context'; - -export default function ({ getService, getPageObjects }: FtrProviderContext) { - const browser = getService('browser'); - const log = getService('log'); - const retry = getService('retry'); - const esArchiver = getService('esArchiver'); - const kibanaServer = getService('kibanaServer'); - const docTable = getService('docTable'); - const queryBar = getService('queryBar'); - const find = getService('find'); - const { common, discover, header, timePicker, unifiedFieldList } = getPageObjects([ - 'common', - 'discover', - 'header', - 'timePicker', - 'unifiedFieldList', - ]); - const defaultSettings = { - defaultIndex: 'logstash-*', - hideAnnouncements: true, - 'doc_table:legacy': true, - }; - const testSubjects = getService('testSubjects'); - - describe('discover doc table', function describeIndexTests() { - const rowsHardLimit = 500; - - before(async function () { - log.debug('load kibana index with default index pattern'); - await kibanaServer.savedObjects.cleanStandardList(); - await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover.json'); - - // and load a set of makelogs data - await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); - await kibanaServer.uiSettings.replace(defaultSettings); - await timePicker.setDefaultAbsoluteRangeViaUiSettings(); - log.debug('discover doc table'); - await common.navigateToApp('discover'); - }); - - after(async function () { - await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover.json'); - await kibanaServer.savedObjects.cleanStandardList(); - await kibanaServer.uiSettings.replace({}); - }); - - it('should show records by default', async function () { - // with the default range the number of hits is ~14000 - const rows = await discover.getDocTableRows(); - expect(rows.length).to.be.greaterThan(0); - }); - - it('should refresh the table content when changing time window', async function () { - const initialRows = await discover.getDocTableRows(); - - const fromTime = 'Sep 20, 2015 @ 23:00:00.000'; - const toTime = 'Sep 20, 2015 @ 23:14:00.000'; - - await timePicker.setAbsoluteRange(fromTime, toTime); - await discover.waitUntilSearchingHasFinished(); - - const finalRows = await discover.getDocTableRows(); - expect(finalRows.length).to.be.below(initialRows.length); - await timePicker.setDefaultAbsoluteRange(); - }); - - describe('classic table in window 900x700', function () { - before(async () => { - await browser.setWindowSize(900, 700); - await common.navigateToApp('discover'); - await discover.waitUntilSearchingHasFinished(); - }); - - it('should load more rows when scrolling down the document table', async function () { - const initialRows = await testSubjects.findAll('docTableRow'); - await testSubjects.scrollIntoView('discoverBackToTop'); - // now count the rows - await retry.waitFor('next batch of documents to be displayed', async () => { - const actual = await testSubjects.findAll('docTableRow'); - log.debug(`initial doc nr: ${initialRows.length}, actual doc nr: ${actual.length}`); - return actual.length > initialRows.length; - }); - }); - }); - - describe('classic table in window 600x700', function () { - before(async () => { - await browser.setWindowSize(600, 700); - await common.navigateToApp('discover'); - await discover.waitUntilSearchingHasFinished(); - }); - - it('should load more rows when scrolling down the document table', async function () { - const initialRows = await testSubjects.findAll('docTableRow'); - await testSubjects.scrollIntoView('discoverBackToTop'); - // now count the rows - await retry.waitFor('next batch of documents to be displayed', async () => { - const actual = await testSubjects.findAll('docTableRow'); - log.debug(`initial doc nr: ${initialRows.length}, actual doc nr: ${actual.length}`); - return actual.length > initialRows.length; - }); - }); - }); - - describe('legacy', function () { - before(async () => { - await common.navigateToApp('discover'); - await discover.waitUntilSearchingHasFinished(); - }); - after(async () => { - await kibanaServer.uiSettings.replace({}); - }); - it(`should load up to ${rowsHardLimit} rows when scrolling at the end of the table`, async function () { - const initialRows = await testSubjects.findAll('docTableRow'); - // click the Skip to the end of the table - await discover.skipToEndOfDocTable(); - // now count the rows - const finalRows = await testSubjects.findAll('docTableRow'); - expect(finalRows.length).to.be.above(initialRows.length); - expect(finalRows.length).to.be(rowsHardLimit); - await discover.backToTop(); - }); - - it('should go the end and back to top of the classic table when using the accessible buttons', async function () { - // click the Skip to the end of the table - await discover.skipToEndOfDocTable(); - // now check the footer text content - const footer = await discover.getDocTableFooter(); - expect(await footer.getVisibleText()).to.have.string(rowsHardLimit); - await discover.backToTop(); - // check that the skip to end of the table button now has focus - const skipButton = await testSubjects.find('discoverSkipTableButton'); - const activeElement = await find.activeElement(); - const activeElementText = await activeElement.getVisibleText(); - const skipButtonText = await skipButton.getVisibleText(); - expect(skipButtonText === activeElementText).to.be(true); - }); - - describe('expand a document row', function () { - const rowToInspect = 1; - beforeEach(async function () { - // close the toggle if open - const details = await docTable.getDetailsRows(); - if (details.length) { - await docTable.clickRowToggle({ isAnchorRow: false, rowIndex: rowToInspect - 1 }); - } - }); - - it('should expand the detail row when the toggle arrow is clicked', async function () { - await retry.try(async function () { - await docTable.clickRowToggle({ isAnchorRow: false, rowIndex: rowToInspect - 1 }); - const detailsEl = await docTable.getDetailsRows(); - const defaultMessageEl = await detailsEl[0].findByTestSubject( - 'docViewerRowDetailsTitle' - ); - expect(defaultMessageEl).to.be.ok(); - }); - }); - - it('should show the detail panel actions', async function () { - await retry.try(async function () { - await docTable.clickRowToggle({ isAnchorRow: false, rowIndex: rowToInspect - 1 }); - // const detailsEl = await discover.getDocTableRowDetails(rowToInspect); - const [surroundingActionEl, singleActionEl] = await docTable.getRowActions({ - isAnchorRow: false, - rowIndex: rowToInspect - 1, - }); - expect(surroundingActionEl).to.be.ok(); - expect(singleActionEl).to.be.ok(); - // TODO: test something more meaninful here? - }); - }); - - it('should not close the detail panel actions when data is re-requested', async function () { - await retry.try(async function () { - const nrOfFetches = await discover.getNrOfFetches(); - await docTable.clickRowToggle({ isAnchorRow: false, rowIndex: rowToInspect - 1 }); - const detailsEl = await docTable.getDetailsRows(); - const defaultMessageEl = await detailsEl[0].findByTestSubject( - 'docViewerRowDetailsTitle' - ); - expect(defaultMessageEl).to.be.ok(); - await queryBar.submitQuery(); - const nrOfFetchesResubmit = await discover.getNrOfFetches(); - expect(nrOfFetchesResubmit).to.be.above(nrOfFetches); - const defaultMessageElResubmit = await detailsEl[0].findByTestSubject( - 'docViewerRowDetailsTitle' - ); - - expect(defaultMessageElResubmit).to.be.ok(); - }); - }); - - it('should show allow toggling columns from the expanded document', async function () { - await discover.clickNewSearchButton(); - await retry.try(async function () { - await docTable.clickRowToggle({ isAnchorRow: false, rowIndex: rowToInspect - 1 }); - - // add columns - const fields = ['_id', '_index', 'agent']; - for (const field of fields) { - await testSubjects.click(`toggleColumnButton-${field}`); - await testSubjects.click(`tableDocViewRow-${field}`); // to suppress the appeared tooltip - } - - const headerWithFields = await docTable.getHeaderFields(); - expect(headerWithFields.join(' ')).to.contain(fields.join(' ')); - - // remove columns - for (const field of fields) { - await testSubjects.click(`toggleColumnButton-${field}`); - await testSubjects.click(`tableDocViewRow-${field}`); - } - - const headerWithoutFields = await docTable.getHeaderFields(); - expect(headerWithoutFields.join(' ')).not.to.contain(fields.join(' ')); - }); - }); - }); - - describe('add and remove columns', function () { - const extraColumns = ['phpmemory', 'ip']; - const expectedFieldLength: Record = { - phpmemory: 1, - ip: 4, - }; - afterEach(async function () { - for (const column of extraColumns) { - await unifiedFieldList.clickFieldListItemRemove(column); - await header.waitUntilLoadingHasFinished(); - } - }); - - it('should add more columns to the table', async function () { - for (const column of extraColumns) { - await unifiedFieldList.clearFieldSearchInput(); - await unifiedFieldList.findFieldByName(column); - await unifiedFieldList.waitUntilFieldlistHasCountOfFields(expectedFieldLength[column]); - await retry.waitFor('field to appear', async function () { - return await testSubjects.exists(`field-${column}`); - }); - await unifiedFieldList.clickFieldListItemAdd(column); - await header.waitUntilLoadingHasFinished(); - // test the header now - const docHeader = await find.byCssSelector('thead > tr:nth-child(1)'); - const docHeaderText = await docHeader.getVisibleText(); - expect(docHeaderText).to.have.string(column); - } - }); - - it('should remove columns from the table', async function () { - for (const column of extraColumns) { - await unifiedFieldList.clearFieldSearchInput(); - await unifiedFieldList.findFieldByName(column); - await unifiedFieldList.waitUntilFieldlistHasCountOfFields(expectedFieldLength[column]); - await unifiedFieldList.clickFieldListItemAdd(column); - await header.waitUntilLoadingHasFinished(); - } - // remove the second column - await unifiedFieldList.clickFieldListItemRemove(extraColumns[1]); - await header.waitUntilLoadingHasFinished(); - // test that the second column is no longer there - const docHeader = await find.byCssSelector('thead > tr:nth-child(1)'); - expect(await docHeader.getVisibleText()).to.not.have.string(extraColumns[1]); - }); - }); - - it('should make the document table scrollable', async function () { - await unifiedFieldList.clearFieldSearchInput(); - const dscTableWrapper = await find.byCssSelector('.kbnDocTableWrapper'); - const fieldNames = await unifiedFieldList.getAllFieldNames(); - const clientHeight = await dscTableWrapper.getAttribute('clientHeight'); - let fieldCounter = 0; - const checkScrollable = async () => { - const scrollWidth = await dscTableWrapper.getAttribute('scrollWidth'); - const clientWidth = await dscTableWrapper.getAttribute('clientWidth'); - log.debug(`scrollWidth: ${scrollWidth}, clientWidth: ${clientWidth}`); - return Number(scrollWidth) > Number(clientWidth); - }; - const addColumn = async () => { - await unifiedFieldList.clickFieldListItemAdd(fieldNames[fieldCounter++]); - }; - - await addColumn(); - const isScrollable = await checkScrollable(); - expect(isScrollable).to.be(false); - - await retry.waitForWithTimeout('container to be scrollable', 60 * 1000, async () => { - await addColumn(); - return await checkScrollable(); - }); - // so now we need to check if the horizontal scrollbar is displayed - const newClientHeight = await dscTableWrapper.getAttribute('clientHeight'); - expect(Number(clientHeight)).to.be.above(Number(newClientHeight)); - }); - }); - }); -} diff --git a/test/functional/apps/discover/classic/_doc_table_newline.ts b/test/functional/apps/discover/classic/_doc_table_newline.ts deleted file mode 100644 index 3c2b426a2dbe4..0000000000000 --- a/test/functional/apps/discover/classic/_doc_table_newline.ts +++ /dev/null @@ -1,56 +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 { FtrProviderContext } from '../ftr_provider_context'; - -export default function ({ getService, getPageObjects }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const kibanaServer = getService('kibanaServer'); - const { common, unifiedFieldList } = getPageObjects(['common', 'unifiedFieldList']); - const find = getService('find'); - const log = getService('log'); - const retry = getService('retry'); - const security = getService('security'); - - describe('discover doc table newline handling', function describeIndexTests() { - before(async function () { - await security.testUser.setRoles(['kibana_admin', 'kibana_message_with_newline']); - await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/message_with_newline'); - await kibanaServer.importExport.load( - 'test/functional/fixtures/kbn_archiver/message_with_newline.json' - ); - await kibanaServer.uiSettings.replace({ - defaultIndex: 'newline-test', - 'doc_table:legacy': true, - }); - await common.navigateToApp('discover'); - }); - - after(async () => { - await security.testUser.restoreDefaults(); - await esArchiver.unload('test/functional/fixtures/es_archiver/message_with_newline'); - await kibanaServer.savedObjects.cleanStandardList(); - await kibanaServer.uiSettings.replace({}); - }); - - it('should break text on newlines', async function () { - await unifiedFieldList.clickFieldListItemToggle('message'); - const dscTableRows = await find.allByCssSelector('.kbnDocTable__row'); - - await retry.waitFor('height of multi-line content > single-line content', async () => { - const heightWithoutNewline = await dscTableRows[0].getAttribute('clientHeight'); - const heightWithNewline = await dscTableRows[1].getAttribute('clientHeight'); - log.debug(`Without newlines: ${heightWithoutNewline}, With newlines: ${heightWithNewline}`); - - await common.sleep(10000); - return Number(heightWithNewline) > Number(heightWithoutNewline); - }); - }); - }); -} diff --git a/test/functional/apps/discover/classic/_esql_grid.ts b/test/functional/apps/discover/classic/_esql_grid.ts deleted file mode 100644 index 94dcabdba4efc..0000000000000 --- a/test/functional/apps/discover/classic/_esql_grid.ts +++ /dev/null @@ -1,95 +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 { FtrProviderContext } from '../ftr_provider_context'; - -export default function ({ getService, getPageObjects }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const kibanaServer = getService('kibanaServer'); - const testSubjects = getService('testSubjects'); - const security = getService('security'); - const dataGrid = getService('dataGrid'); - const dashboardAddPanel = getService('dashboardAddPanel'); - const dashboardPanelActions = getService('dashboardPanelActions'); - const { common, discover, dashboard, header, timePicker } = getPageObjects([ - 'common', - 'discover', - 'dashboard', - 'header', - 'timePicker', - ]); - - const defaultSettings = { - defaultIndex: 'logstash-*', - 'doc_table:legacy': true, - }; - - describe('discover esql grid with legacy setting', function () { - before(async () => { - await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader']); - await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover'); - await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); - await kibanaServer.uiSettings.replace(defaultSettings); - await common.navigateToApp('discover'); - await timePicker.setDefaultAbsoluteRange(); - }); - - after(async () => { - await kibanaServer.savedObjects.cleanStandardList(); - await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover'); - await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); - await kibanaServer.uiSettings.replace({}); - }); - - it('should render esql view correctly', async function () { - const savedSearchESQL = 'testESQLWithLegacySetting'; - await header.waitUntilLoadingHasFinished(); - await discover.waitUntilSearchingHasFinished(); - - await testSubjects.existOrFail('docTableHeader'); - await testSubjects.missingOrFail('euiDataGridBody'); - - await discover.selectTextBaseLang(); - - await header.waitUntilLoadingHasFinished(); - await discover.waitUntilSearchingHasFinished(); - - await testSubjects.missingOrFail('docTableHeader'); - await testSubjects.existOrFail('euiDataGridBody'); - - await dataGrid.clickRowToggle({ rowIndex: 0 }); - - await testSubjects.existOrFail('docViewerFlyout'); - - await discover.saveSearch(savedSearchESQL); - - await common.navigateToApp('dashboard'); - await dashboard.clickNewDashboard(); - await timePicker.setDefaultAbsoluteRange(); - await dashboardAddPanel.clickOpenAddPanel(); - await dashboardAddPanel.addSavedSearch(savedSearchESQL); - await header.waitUntilLoadingHasFinished(); - - await testSubjects.missingOrFail('docTableHeader'); - await testSubjects.existOrFail('euiDataGridBody'); - - await dataGrid.clickRowToggle({ rowIndex: 0 }); - - await testSubjects.existOrFail('docViewerFlyout'); - - await dashboardPanelActions.removePanelByTitle(savedSearchESQL); - - await dashboardAddPanel.addSavedSearch('A Saved Search'); - - await header.waitUntilLoadingHasFinished(); - await testSubjects.existOrFail('docTableHeader'); - await testSubjects.missingOrFail('euiDataGridBody'); - }); - }); -} diff --git a/test/functional/apps/discover/classic/_field_data.ts b/test/functional/apps/discover/classic/_field_data.ts deleted file mode 100644 index 2ebd49bb24ae2..0000000000000 --- a/test/functional/apps/discover/classic/_field_data.ts +++ /dev/null @@ -1,63 +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 expect from '@kbn/expect'; - -import { FtrProviderContext } from '../ftr_provider_context'; - -export default function ({ getService, getPageObjects }: FtrProviderContext) { - const retry = getService('retry'); - const esArchiver = getService('esArchiver'); - const kibanaServer = getService('kibanaServer'); - const { common, timePicker } = getPageObjects(['common', 'timePicker']); - const find = getService('find'); - const testSubjects = getService('testSubjects'); - - describe('discover tab', function describeIndexTests() { - this.tags('includeFirefox'); - before(async function () { - await kibanaServer.savedObjects.clean({ types: ['search', 'index-pattern'] }); - await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover.json'); - await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); - await kibanaServer.uiSettings.replace({ - defaultIndex: 'logstash-*', - 'discover:searchFieldsFromSource': true, - 'doc_table:legacy': true, - }); - await timePicker.setDefaultAbsoluteRangeViaUiSettings(); - await common.navigateToApp('discover'); - }); - - after(async function () { - await kibanaServer.uiSettings.replace({}); - }); - - it('doc view should show @timestamp and _source columns', async function () { - const expectedHeader = '@timestamp\n_source'; - const docHeader = await find.byCssSelector('thead > tr:nth-child(1)'); - const docHeaderText = await docHeader.getVisibleText(); - expect(docHeaderText).to.be(expectedHeader); - }); - - it('doc view should sort ascending', async function () { - const expectedTimeStamp = 'Sep 20, 2015 @ 00:00:00.000'; - await testSubjects.click('docTableHeaderFieldSort_@timestamp'); - - // we don't technically need this sleep here because the tryForTime will retry and the - // results will match on the 2nd or 3rd attempt, but that debug output is huge in this - // case and it can be avoided with just a few seconds sleep. - await common.sleep(2000); - await retry.try(async function tryingForTime() { - const row = await find.byCssSelector(`tr.kbnDocTable__row:nth-child(1)`); - const rowData = await row.getVisibleText(); - expect(rowData.startsWith(expectedTimeStamp)).to.be.ok(); - }); - }); - }); -} diff --git a/test/functional/apps/discover/classic/_field_data_with_fields_api.ts b/test/functional/apps/discover/classic/_field_data_with_fields_api.ts deleted file mode 100644 index 4932ba3e24348..0000000000000 --- a/test/functional/apps/discover/classic/_field_data_with_fields_api.ts +++ /dev/null @@ -1,56 +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 expect from '@kbn/expect'; - -import { FtrProviderContext } from '../ftr_provider_context'; - -export default function ({ getService, getPageObjects }: FtrProviderContext) { - const retry = getService('retry'); - const kibanaServer = getService('kibanaServer'); - const { common, timePicker } = getPageObjects(['common', 'timePicker']); - const find = getService('find'); - const esArchiver = getService('esArchiver'); - const testSubjects = getService('testSubjects'); - - describe('discover tab with new fields API', function describeIndexTests() { - before(async function () { - await kibanaServer.savedObjects.clean({ types: ['search', 'index-pattern'] }); - await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover.json'); - await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); - await kibanaServer.uiSettings.replace({ - defaultIndex: 'logstash-*', - 'discover:searchFieldsFromSource': false, - 'doc_table:legacy': true, - }); - await timePicker.setDefaultAbsoluteRangeViaUiSettings(); - await common.navigateToApp('discover'); - }); - - after(async function () { - await kibanaServer.uiSettings.replace({}); - }); - - it('doc view should sort ascending', async function () { - const expectedTimeStamp = 'Sep 20, 2015 @ 00:00:00.000'; - await testSubjects.click('docTableHeaderFieldSort_@timestamp'); - - // we don't technically need this sleep here because the tryForTime will retry and the - // results will match on the 2nd or 3rd attempt, but that debug output is huge in this - // case and it can be avoided with just a few seconds sleep. - await common.sleep(2000); - await retry.try(async function tryingForTime() { - const row = await find.byCssSelector(`tr.kbnDocTable__row:nth-child(1)`); - const rowData = await row.getVisibleText(); - - expect(rowData.startsWith(expectedTimeStamp)).to.be.ok(); - }); - }); - }); -} diff --git a/test/functional/apps/discover/classic/_hide_announcements.ts b/test/functional/apps/discover/classic/_hide_announcements.ts deleted file mode 100644 index df7fcbb628136..0000000000000 --- a/test/functional/apps/discover/classic/_hide_announcements.ts +++ /dev/null @@ -1,49 +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 expect from '@kbn/expect'; -import { FtrProviderContext } from '../ftr_provider_context'; - -export default function ({ getService, getPageObjects }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const { common, discover, timePicker } = getPageObjects(['common', 'discover', 'timePicker']); - const kibanaServer = getService('kibanaServer'); - const testSubjects = getService('testSubjects'); - const browser = getService('browser'); - - describe('test hide announcements', function () { - before(async function () { - await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover.json'); - await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); - await kibanaServer.uiSettings.replace({ - defaultIndex: 'logstash-*', - 'doc_table:legacy': true, - }); - await common.navigateToApp('discover'); - await timePicker.setDefaultAbsoluteRange(); - }); - - after(async () => { - await kibanaServer.uiSettings.replace({}); - }); - - it('should display try document explorer button', async function () { - await discover.selectIndexPattern('logstash-*'); - const tourButtonExists = await testSubjects.exists('tryDocumentExplorerButton'); - expect(tourButtonExists).to.be(true); - }); - - it('should not display try document explorer button', async function () { - await kibanaServer.uiSettings.update({ hideAnnouncements: true }); - await browser.refresh(); - const tourButtonExists = await testSubjects.exists('tryDocumentExplorerButton'); - expect(tourButtonExists).to.be(false); - }); - }); -} diff --git a/test/functional/apps/discover/classic/config.ts b/test/functional/apps/discover/classic/config.ts deleted file mode 100644 index 941eec8ca621c..0000000000000 --- a/test/functional/apps/discover/classic/config.ts +++ /dev/null @@ -1,19 +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 { FtrConfigProviderContext } from '@kbn/test'; - -export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js')); - - return { - ...functionalConfig.getAll(), - testFiles: [require.resolve('.')], - }; -} diff --git a/test/functional/apps/discover/classic/index.ts b/test/functional/apps/discover/classic/index.ts deleted file mode 100644 index fce8963d19082..0000000000000 --- a/test/functional/apps/discover/classic/index.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 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 { FtrProviderContext } from '../ftr_provider_context'; - -export default function ({ getService, loadTestFile }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const browser = getService('browser'); - - describe('discover/classic', function () { - before(async function () { - await browser.setWindowSize(1300, 800); - }); - - after(async function unloadMakelogs() { - await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); - }); - - loadTestFile(require.resolve('./_discover_fields_api')); - loadTestFile(require.resolve('./_doc_table')); - loadTestFile(require.resolve('./_doc_table_newline')); - loadTestFile(require.resolve('./_field_data')); - loadTestFile(require.resolve('./_field_data_with_fields_api')); - loadTestFile(require.resolve('./_classic_table_doc_navigation')); - loadTestFile(require.resolve('./_hide_announcements')); - loadTestFile(require.resolve('./_esql_grid')); - }); -} diff --git a/test/functional/apps/discover/group1/_date_nanos_mixed.ts b/test/functional/apps/discover/group1/_date_nanos_mixed.ts index 7162d01bb96cc..a30c4a316351c 100644 --- a/test/functional/apps/discover/group1/_date_nanos_mixed.ts +++ b/test/functional/apps/discover/group1/_date_nanos_mixed.ts @@ -45,14 +45,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('shows a list of records of indices with date & date_nanos fields in the right order', async function () { - const isLegacy = await discover.useLegacyTable(); const rowData1 = await discover.getDocTableIndex(1); expect(rowData1).to.contain('Jan 1, 2019 @ 12:10:30.124000000'); - const rowData2 = await discover.getDocTableIndex(isLegacy ? 3 : 2); + const rowData2 = await discover.getDocTableIndex(2); expect(rowData2).to.contain('Jan 1, 2019 @ 12:10:30.123498765'); - const rowData3 = await discover.getDocTableIndex(isLegacy ? 5 : 3); + const rowData3 = await discover.getDocTableIndex(3); expect(rowData3).to.contain('Jan 1, 2019 @ 12:10:30.123456789'); - const rowData4 = await discover.getDocTableIndex(isLegacy ? 7 : 4); + const rowData4 = await discover.getDocTableIndex(4); expect(rowData4).to.contain('Jan 1, 2019 @ 12:10:30.123000000'); }); }); diff --git a/test/functional/apps/discover/group6/_time_field_column.ts b/test/functional/apps/discover/group6/_time_field_column.ts index 5965b7765e182..9e5ce3f94a459 100644 --- a/test/functional/apps/discover/group6/_time_field_column.ts +++ b/test/functional/apps/discover/group6/_time_field_column.ts @@ -34,7 +34,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const dataViews = getService('dataViews'); const testSubjects = getService('testSubjects'); const security = getService('security'); - const docTable = getService('docTable'); const defaultSettings = { defaultIndex: 'logstash-*', hideAnnouncements: true, @@ -228,7 +227,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await timePicker.setDefaultAbsoluteRangeViaUiSettings(); await kibanaServer.uiSettings.update({ ...defaultSettings, - 'doc_table:legacy': false, 'doc_table:hideTimeColumn': hideTimeFieldColumnSetting, }); await common.navigateToApp('discover'); @@ -319,103 +317,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); }); - - // These tests are skipped as they take a lot of time to run. Temporary unskip them to validate current functionality if necessary. - describe.skip('legacy table', () => { - beforeEach(async () => { - await kibanaServer.uiSettings.update({ - ...defaultSettings, - 'doc_table:hideTimeColumn': hideTimeFieldColumnSetting, - 'doc_table:legacy': true, - }); - await common.navigateToApp('discover'); - await discover.waitUntilSearchingHasFinished(); - }); - - it('should render initial columns correctly', async () => { - // no columns - await discover.loadSavedSearch(`${SEARCH_NO_COLUMNS}${savedSearchSuffix}`); - await discover.waitUntilSearchingHasFinished(); - expect(await docTable.getHeaderFields()).to.eql( - hideTimeFieldColumnSetting ? ['Summary'] : ['@timestamp', 'Summary'] - ); - - await discover.loadSavedSearch(`${SEARCH_NO_COLUMNS}${savedSearchSuffix}-`); - await discover.waitUntilSearchingHasFinished(); - 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 ? ['Summary'] : ['@timestamp', 'Summary'] - ); - - await discover.loadSavedSearch(`${SEARCH_NO_COLUMNS}${savedSearchSuffix}ESQLdrop`); - await discover.waitUntilSearchingHasFinished(); - expect(await dataGrid.getHeaderFields()).to.eql(['Summary']); - - // only @timestamp is selected - await discover.loadSavedSearch(`${SEARCH_WITH_ONLY_TIMESTAMP}${savedSearchSuffix}`); - await discover.waitUntilSearchingHasFinished(); - expect(await docTable.getHeaderFields()).to.eql( - hideTimeFieldColumnSetting ? ['@timestamp'] : ['@timestamp', '@timestamp'] - ); - - await discover.loadSavedSearch(`${SEARCH_WITH_ONLY_TIMESTAMP}${savedSearchSuffix}-`); - await discover.waitUntilSearchingHasFinished(); - expect(await docTable.getHeaderFields()).to.eql(['@timestamp']); - - await discover.loadSavedSearch(`${SEARCH_WITH_ONLY_TIMESTAMP}${savedSearchSuffix}ESQL`); - await discover.waitUntilSearchingHasFinished(); - expect(await dataGrid.getHeaderFields()).to.eql( - hideTimeFieldColumnSetting ? ['@timestamp'] : ['@timestamp', 'Summary'] - ); - }); - - it('should render selected columns correctly', async () => { - // with selected columns - await discover.loadSavedSearch(`${SEARCH_WITH_SELECTED_COLUMNS}${savedSearchSuffix}`); - await discover.waitUntilSearchingHasFinished(); - expect(await docTable.getHeaderFields()).to.eql( - hideTimeFieldColumnSetting - ? ['bytes', 'extension'] - : ['@timestamp', 'bytes', 'extension'] - ); - - await discover.loadSavedSearch(`${SEARCH_WITH_SELECTED_COLUMNS}${savedSearchSuffix}-`); - await discover.waitUntilSearchingHasFinished(); - expect(await docTable.getHeaderFields()).to.eql(['bytes', 'extension']); - - await discover.loadSavedSearch( - `${SEARCH_WITH_SELECTED_COLUMNS}${savedSearchSuffix}ESQL` - ); - await discover.waitUntilSearchingHasFinished(); - expect(await dataGrid.getHeaderFields()).to.eql(['bytes', 'extension']); - - // with selected columns and @timestamp - await discover.loadSavedSearch( - `${SEARCH_WITH_SELECTED_COLUMNS_AND_TIMESTAMP}${savedSearchSuffix}` - ); - await discover.waitUntilSearchingHasFinished(); - expect(await docTable.getHeaderFields()).to.eql( - hideTimeFieldColumnSetting - ? ['bytes', 'extension', '@timestamp'] - : ['@timestamp', 'bytes', 'extension', '@timestamp'] - ); - - await discover.loadSavedSearch( - `${SEARCH_WITH_SELECTED_COLUMNS_AND_TIMESTAMP}${savedSearchSuffix}-` - ); - await discover.waitUntilSearchingHasFinished(); - expect(await docTable.getHeaderFields()).to.eql(['bytes', 'extension', '@timestamp']); - - await discover.loadSavedSearch( - `${SEARCH_WITH_SELECTED_COLUMNS_AND_TIMESTAMP}${savedSearchSuffix}ESQL` - ); - await discover.waitUntilSearchingHasFinished(); - expect(await dataGrid.getHeaderFields()).to.eql(['bytes', 'extension', '@timestamp']); - }); - }); }); }); }); diff --git a/test/functional/apps/discover/group6/_view_mode_toggle.ts b/test/functional/apps/discover/group6/_view_mode_toggle.ts index 2591716c87d00..4ff5339b80847 100644 --- a/test/functional/apps/discover/group6/_view_mode_toggle.ts +++ b/test/functional/apps/discover/group6/_view_mode_toggle.ts @@ -33,102 +33,75 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader']); await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover'); + await timePicker.setDefaultAbsoluteRangeViaUiSettings(); + await kibanaServer.uiSettings.update(defaultSettings); + await common.navigateToApp('discover'); + await discover.waitUntilSearchingHasFinished(); }); after(async () => { await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover'); await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.uiSettings.replace({}); }); - [true, false].forEach((useLegacyTable) => { - describe(`isLegacy: ${useLegacyTable}`, function () { - before(async function () { - await timePicker.setDefaultAbsoluteRangeViaUiSettings(); - await kibanaServer.uiSettings.update({ - ...defaultSettings, - 'doc_table:legacy': useLegacyTable, - }); - await common.navigateToApp('discover'); - await discover.waitUntilSearchingHasFinished(); - }); - - after(async () => { - await kibanaServer.uiSettings.replace({}); - }); - - it('should show Documents tab', async () => { - await testSubjects.existOrFail('dscViewModeToggle'); - - if (!useLegacyTable) { - await testSubjects.existOrFail('unifiedDataTableToolbar'); - } - - const documentsTab = await testSubjects.find('dscViewModeDocumentButton'); - expect(await documentsTab.getAttribute('aria-selected')).to.be('true'); - }); - - it('should show legacy Document Explorer info callout', async () => { - if (useLegacyTable) { - await testSubjects.existOrFail('dscDocumentExplorerLegacyCallout'); - } else { - await testSubjects.missingOrFail('dscDocumentExplorerLegacyCallout'); - } - }); - - it('should show an error callout', async () => { - await queryBar.setQuery('@message::'); // invalid - await queryBar.submitQuery(); - await header.waitUntilLoadingHasFinished(); - - await discover.showsErrorCallout(); - - await queryBar.clearQuery(); - await queryBar.submitQuery(); - await header.waitUntilLoadingHasFinished(); - - await testSubjects.missingOrFail('discoverErrorCalloutTitle'); - }); - - describe('Patterns tab (basic license)', function () { - this.tags('skipFIPS'); - - it('should not show', async function () { - await testSubjects.missingOrFail('dscViewModePatternAnalysisButton'); - }); - }); - - it('should not show Field Statistics tab', async () => { - await testSubjects.existOrFail('dscViewModeToggle'); - }); - - it('should not show view mode toggle for ES|QL searches', async () => { - await testSubjects.click('dscViewModeDocumentButton'); - - await retry.try(async () => { - const documentsTab = await testSubjects.find('dscViewModeDocumentButton'); - expect(await documentsTab.getAttribute('aria-selected')).to.be('true'); - }); - - await testSubjects.existOrFail('dscViewModeToggle'); - - await discover.selectTextBaseLang(); - - await testSubjects.missingOrFail('dscViewModeToggle'); - await testSubjects.existOrFail('discoverQueryTotalHits'); - - if (!useLegacyTable) { - await testSubjects.existOrFail('unifiedDataTableToolbar'); - } - }); - - it('should show ES|QL columns callout', async () => { - await testSubjects.missingOrFail('dscSelectedColumnsCallout'); - await unifiedFieldList.clickFieldListItemAdd('extension'); - await header.waitUntilLoadingHasFinished(); - await testSubjects.existOrFail('dscSelectedColumnsCallout'); - }); + it('should show Documents tab', async () => { + await testSubjects.existOrFail('dscViewModeToggle'); + await testSubjects.existOrFail('unifiedDataTableToolbar'); + + const documentsTab = await testSubjects.find('dscViewModeDocumentButton'); + expect(await documentsTab.getAttribute('aria-selected')).to.be('true'); + }); + + it('should show an error callout', async () => { + await queryBar.setQuery('@message::'); // invalid + await queryBar.submitQuery(); + await header.waitUntilLoadingHasFinished(); + + await discover.showsErrorCallout(); + + await queryBar.clearQuery(); + await queryBar.submitQuery(); + await header.waitUntilLoadingHasFinished(); + + await testSubjects.missingOrFail('discoverErrorCalloutTitle'); + }); + + describe('Patterns tab (basic license)', function () { + this.tags('skipFIPS'); + + it('should not show', async function () { + await testSubjects.missingOrFail('dscViewModePatternAnalysisButton'); + }); + }); + + it('should not show Field Statistics tab', async () => { + await testSubjects.existOrFail('dscViewModeToggle'); + }); + + it('should not show view mode toggle for ES|QL searches', async () => { + await testSubjects.click('dscViewModeDocumentButton'); + + await retry.try(async () => { + const documentsTab = await testSubjects.find('dscViewModeDocumentButton'); + expect(await documentsTab.getAttribute('aria-selected')).to.be('true'); }); + + await testSubjects.existOrFail('dscViewModeToggle'); + + await discover.selectTextBaseLang(); + + await testSubjects.missingOrFail('dscViewModeToggle'); + await testSubjects.existOrFail('discoverQueryTotalHits'); + await testSubjects.existOrFail('unifiedDataTableToolbar'); + }); + + it('should show ES|QL columns callout', async () => { + await testSubjects.missingOrFail('dscSelectedColumnsCallout'); + await unifiedFieldList.clickFieldListItemAdd('extension'); + await header.waitUntilLoadingHasFinished(); + await testSubjects.existOrFail('dscSelectedColumnsCallout'); }); }); } diff --git a/test/functional/apps/discover/group7/_runtime_fields_editor.ts b/test/functional/apps/discover/group7/_runtime_fields_editor.ts index ab8711c362b77..e93e73650b464 100644 --- a/test/functional/apps/discover/group7/_runtime_fields_editor.ts +++ b/test/functional/apps/discover/group7/_runtime_fields_editor.ts @@ -234,17 +234,15 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // navigate to doc view const fieldName = '_runtimefield-doc-view'; await createRuntimeField(fieldName); - const table = await discover.getDocTable(); - const useLegacyTable = await discover.useLegacyTable(); - await table.clickRowToggle(); + await dataGrid.clickRowToggle(); // click the open action await retry.try(async () => { - const rowActions = await table.getRowActions({ rowIndex: 0 }); + const rowActions = await dataGrid.getRowActions({ rowIndex: 0 }); if (!rowActions.length) { throw new Error('row actions empty, trying again'); } - const idxToClick = useLegacyTable ? 1 : 0; + const idxToClick = 0; await rowActions[idxToClick].click(); }); diff --git a/test/functional/apps/discover/group8/_hide_announcements.ts b/test/functional/apps/discover/group8/_hide_announcements.ts deleted file mode 100644 index 9dc11f9eefcac..0000000000000 --- a/test/functional/apps/discover/group8/_hide_announcements.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 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 expect from '@kbn/expect'; -import { FtrProviderContext } from '../ftr_provider_context'; - -export default function ({ getService, getPageObjects }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const { common, discover, timePicker } = getPageObjects(['common', 'discover', 'timePicker']); - const kibanaServer = getService('kibanaServer'); - const testSubjects = getService('testSubjects'); - const browser = getService('browser'); - const security = getService('security'); - - describe('test hide announcements', function () { - before(async function () { - await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader']); - await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover.json'); - await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); - await kibanaServer.uiSettings.replace({ - defaultIndex: 'logstash-*', - 'doc_table:legacy': true, - }); - await common.navigateToApp('discover'); - await timePicker.setDefaultAbsoluteRange(); - }); - - after(async () => { - await kibanaServer.uiSettings.replace({}); - }); - - it('should display callout', async function () { - await discover.selectIndexPattern('logstash-*'); - const calloutExists = await testSubjects.exists('dscDocumentExplorerLegacyCallout'); - expect(calloutExists).to.be(true); - }); - - it('should not display callout', async function () { - await kibanaServer.uiSettings.update({ hideAnnouncements: true }); - await browser.refresh(); - const calloutExists = await testSubjects.exists('dscDocumentExplorerLegacyCallout'); - expect(calloutExists).to.be(false); - }); - }); -} diff --git a/test/functional/apps/discover/group8/index.ts b/test/functional/apps/discover/group8/index.ts index 14b544e2c2015..46d5040fee298 100644 --- a/test/functional/apps/discover/group8/index.ts +++ b/test/functional/apps/discover/group8/index.ts @@ -23,7 +23,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { }); loadTestFile(require.resolve('./_default_route')); - loadTestFile(require.resolve('./_hide_announcements')); loadTestFile(require.resolve('./_flyouts')); }); } diff --git a/test/functional/apps/management/data_views/_scripted_fields.ts b/test/functional/apps/management/data_views/_scripted_fields.ts index f86ae72aa5047..df51514425a24 100644 --- a/test/functional/apps/management/data_views/_scripted_fields.ts +++ b/test/functional/apps/management/data_views/_scripted_fields.ts @@ -387,14 +387,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.click('docTableHeaderFieldSort_@timestamp'); await PageObjects.header.waitUntilLoadingHasFinished(); await retry.try(async function () { - const rowData = await PageObjects.discover.getDocTableIndexLegacy(1); + const rowData = await PageObjects.discover.getDocTableIndex(1); expect(rowData).to.be('updateExpectedResultHere\ntrue'); }); await testSubjects.click(`docTableHeaderFieldSort_${scriptedPainlessFieldName2}`); await PageObjects.header.waitUntilLoadingHasFinished(); await retry.try(async function () { - const rowData = await PageObjects.discover.getDocTableIndexLegacy(1); + const rowData = await PageObjects.discover.getDocTableIndex(1); expect(rowData).to.be('updateExpectedResultHere\nfalse'); }); }); @@ -469,14 +469,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.click('docTableHeaderFieldSort_@timestamp'); await PageObjects.header.waitUntilLoadingHasFinished(); await retry.try(async function () { - const rowData = await PageObjects.discover.getDocTableIndexLegacy(1); + const rowData = await PageObjects.discover.getDocTableIndex(1); expect(rowData).to.be('updateExpectedResultHere\n2015-09-18 07:00'); }); await testSubjects.click(`docTableHeaderFieldSort_${scriptedPainlessFieldName2}`); await PageObjects.header.waitUntilLoadingHasFinished(); await retry.try(async function () { - const rowData = await PageObjects.discover.getDocTableIndexLegacy(1); + const rowData = await PageObjects.discover.getDocTableIndex(1); expect(rowData).to.be('updateExpectedResultHere\n2015-09-18 07:00'); }); }); diff --git a/test/functional/apps/management/data_views/_scripted_fields_classic_table.ts b/test/functional/apps/management/data_views/_scripted_fields_classic_table.ts deleted file mode 100644 index 4f3d30222e496..0000000000000 --- a/test/functional/apps/management/data_views/_scripted_fields_classic_table.ts +++ /dev/null @@ -1,463 +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". - */ - -// Tests for 4 scripted fields; -// 1. Painless (number type) -// 2. Painless (string type) -// 3. Painless (boolean type) -// 4. Painless (date type) -// -// Each of these scripted fields has 4 tests (12 tests total); -// 1. Create scripted field -// 2. See the expected value of the scripted field in Discover doc view -// 3. Filter in Discover by the scripted field -// 4. Visualize with aggregation on the scripted field by clicking unifiedFieldList.clickFieldListItemVisualize - -import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../ftr_provider_context'; - -export default function ({ getService, getPageObjects }: FtrProviderContext) { - const kibanaServer = getService('kibanaServer'); - const log = getService('log'); - const browser = getService('browser'); - const retry = getService('retry'); - const testSubjects = getService('testSubjects'); - const filterBar = getService('filterBar'); - const docTable = getService('docTable'); - const PageObjects = getPageObjects([ - 'common', - 'header', - 'settings', - 'visualize', - 'discover', - 'timePicker', - 'unifiedFieldList', - ]); - - describe('scripted fields', function () { - this.tags(['skipFirefox']); - - before(async function () { - await browser.setWindowSize(1200, 800); - await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover'); - await kibanaServer.uiSettings.replace({}); - await kibanaServer.uiSettings.update({ - 'doc_table:legacy': true, - }); - }); - - after(async function afterAll() { - await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover'); - await kibanaServer.uiSettings.replace({}); - }); - - it('should not allow saving of invalid scripts', async function () { - await PageObjects.settings.navigateTo(); - await PageObjects.settings.clickKibanaIndexPatterns(); - await PageObjects.settings.clickIndexPatternLogstash(); - await PageObjects.settings.clickScriptedFieldsTab(); - await PageObjects.settings.clickAddScriptedField(); - await PageObjects.settings.setScriptedFieldName('doomedScriptedField'); - await PageObjects.settings.setScriptedFieldScript(`i n v a l i d s c r i p t`); - await PageObjects.settings.clickSaveScriptedField(); - await retry.try(async () => { - const invalidScriptErrorExists = await testSubjects.exists('invalidScriptError'); - expect(invalidScriptErrorExists).to.be(true); - }); - }); - - describe('testing regression for issue #33251', function describeIndexTests() { - const scriptedPainlessFieldName = 'ram_Pain_reg'; - - it('should create and edit scripted field', async function () { - await PageObjects.settings.navigateTo(); - await PageObjects.settings.clickKibanaIndexPatterns(); - await PageObjects.settings.clickIndexPatternLogstash(); - const startingCount = parseInt(await PageObjects.settings.getScriptedFieldsTabCount(), 10); - await PageObjects.settings.clickScriptedFieldsTab(); - await log.debug('add scripted field'); - const script = `1`; - await PageObjects.settings.addScriptedField( - scriptedPainlessFieldName, - 'painless', - 'number', - null, - '1', - script - ); - await retry.try(async function () { - expect(parseInt(await PageObjects.settings.getScriptedFieldsTabCount(), 10)).to.be( - startingCount + 1 - ); - }); - - for (let i = 0; i < 3; i++) { - await PageObjects.settings.editScriptedField(scriptedPainlessFieldName); - const fieldSaveButton = await testSubjects.exists('fieldSaveButton'); - expect(fieldSaveButton).to.be(true); - await PageObjects.settings.clickSaveScriptedField(); - } - }); - }); - - describe('creating and using Painless numeric scripted fields', function describeIndexTests() { - const scriptedPainlessFieldName = 'ram_Pain1'; - - it('should create scripted field', async function () { - await PageObjects.settings.navigateTo(); - await PageObjects.settings.clickKibanaIndexPatterns(); - await PageObjects.settings.clickIndexPatternLogstash(); - const startingCount = parseInt(await PageObjects.settings.getScriptedFieldsTabCount(), 10); - await PageObjects.settings.clickScriptedFieldsTab(); - await log.debug('add scripted field'); - const script = `if (doc['machine.ram'].size() == 0) return -1; - else return doc['machine.ram'].value / (1024 * 1024 * 1024); - `; - await PageObjects.settings.addScriptedField( - scriptedPainlessFieldName, - 'painless', - 'number', - null, - '100', - script - ); - await retry.try(async function () { - expect(parseInt(await PageObjects.settings.getScriptedFieldsTabCount(), 10)).to.be( - startingCount + 1 - ); - }); - }); - - it('should see scripted field value in Discover', async function () { - const fromTime = 'Sep 17, 2015 @ 06:31:44.000'; - const toTime = 'Sep 18, 2015 @ 18:31:44.000'; - await PageObjects.common.navigateToApp('discover'); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); - - await retry.try(async function () { - await PageObjects.unifiedFieldList.clickFieldListItemAdd(scriptedPainlessFieldName); - }); - await PageObjects.header.waitUntilLoadingHasFinished(); - - await retry.try(async function () { - const rowData = await PageObjects.discover.getDocTableIndexLegacy(1); - expect(rowData).to.be('Sep 18, 2015 @ 18:20:57.916\n18'); - }); - }); - - // add a test to sort numeric scripted field - it('should sort scripted field value in Discover', async function () { - await testSubjects.click(`docTableHeaderFieldSort_${scriptedPainlessFieldName}`); - // after the first click on the scripted field, it becomes secondary sort after time. - // click on the timestamp twice to make it be the secondary sort key. - await testSubjects.click('docTableHeaderFieldSort_@timestamp'); - await testSubjects.click('docTableHeaderFieldSort_@timestamp'); - await PageObjects.header.waitUntilLoadingHasFinished(); - await retry.try(async function () { - const rowData = await PageObjects.discover.getDocTableIndexLegacy(1); - expect(rowData).to.be('Sep 17, 2015 @ 10:53:14.181\n-1'); - }); - - await testSubjects.click(`docTableHeaderFieldSort_${scriptedPainlessFieldName}`); - await PageObjects.header.waitUntilLoadingHasFinished(); - await retry.try(async function () { - const rowData = await PageObjects.discover.getDocTableIndexLegacy(1); - expect(rowData).to.be('Sep 17, 2015 @ 06:32:29.479\n20'); - }); - }); - - it('should filter by scripted field value in Discover', async function () { - await PageObjects.unifiedFieldList.clickFieldListItem(scriptedPainlessFieldName); - await log.debug('filter by the first value (14) in the expanded scripted field list'); - await PageObjects.unifiedFieldList.clickFieldListPlusFilter( - scriptedPainlessFieldName, - '14' - ); - await PageObjects.header.waitUntilLoadingHasFinished(); - - await retry.try(async function () { - expect(await PageObjects.discover.getHitCount()).to.be('31'); - }); - }); - - it('should visualize scripted field in vertical bar chart', async function () { - await filterBar.removeAllFilters(); - await PageObjects.unifiedFieldList.clickFieldListItemVisualize(scriptedPainlessFieldName); - await PageObjects.header.waitUntilLoadingHasFinished(); - // verify Lens opens a visualization - expect(await testSubjects.getVisibleTextAll('lns-dimensionTrigger')).to.contain( - '@timestamp', - 'Median of ram_Pain1' - ); - }); - }); - - describe('creating and using Painless string scripted fields', function describeIndexTests() { - const scriptedPainlessFieldName2 = 'painString'; - - it('should create scripted field', async function () { - await PageObjects.settings.navigateTo(); - await PageObjects.settings.clickKibanaIndexPatterns(); - await PageObjects.settings.clickIndexPatternLogstash(); - const startingCount = parseInt(await PageObjects.settings.getScriptedFieldsTabCount(), 10); - await PageObjects.settings.clickScriptedFieldsTab(); - await log.debug('add scripted field'); - await PageObjects.settings.addScriptedField( - scriptedPainlessFieldName2, - 'painless', - 'string', - null, - '1', - "if (doc['response.raw'].value == '200') { return 'good'} else { return 'bad'}" - ); - await retry.try(async function () { - expect(parseInt(await PageObjects.settings.getScriptedFieldsTabCount(), 10)).to.be( - startingCount + 1 - ); - }); - }); - - it('should see scripted field value in Discover', async function () { - const fromTime = 'Sep 17, 2015 @ 06:31:44.000'; - const toTime = 'Sep 18, 2015 @ 18:31:44.000'; - await PageObjects.common.navigateToApp('discover'); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); - - await retry.try(async function () { - await PageObjects.unifiedFieldList.clickFieldListItemAdd(scriptedPainlessFieldName2); - }); - await PageObjects.header.waitUntilLoadingHasFinished(); - - await retry.try(async function () { - const rowData = await PageObjects.discover.getDocTableIndexLegacy(1); - expect(rowData).to.be('Sep 18, 2015 @ 18:20:57.916\ngood'); - }); - }); - - // add a test to sort string scripted field - it('should sort scripted field value in Discover', async function () { - await testSubjects.click(`docTableHeaderFieldSort_${scriptedPainlessFieldName2}`); - // after the first click on the scripted field, it becomes secondary sort after time. - // click on the timestamp twice to make it be the secondary sort key. - await testSubjects.click('docTableHeaderFieldSort_@timestamp'); - await testSubjects.click('docTableHeaderFieldSort_@timestamp'); - await PageObjects.header.waitUntilLoadingHasFinished(); - await retry.try(async function () { - const rowData = await PageObjects.discover.getDocTableIndexLegacy(1); - expect(rowData).to.be('Sep 17, 2015 @ 09:48:40.594\nbad'); - }); - - await testSubjects.click(`docTableHeaderFieldSort_${scriptedPainlessFieldName2}`); - await PageObjects.header.waitUntilLoadingHasFinished(); - await retry.try(async function () { - const rowData = await PageObjects.discover.getDocTableIndexLegacy(1); - expect(rowData).to.be('Sep 17, 2015 @ 06:32:29.479\ngood'); - }); - }); - - it('should filter by scripted field value in Discover', async function () { - await PageObjects.unifiedFieldList.clickFieldListItem(scriptedPainlessFieldName2); - await log.debug('filter by "bad" in the expanded scripted field list'); - await PageObjects.unifiedFieldList.clickFieldListPlusFilter( - scriptedPainlessFieldName2, - 'bad' - ); - await PageObjects.header.waitUntilLoadingHasFinished(); - - await retry.try(async function () { - expect(await PageObjects.discover.getHitCount()).to.be('27'); - }); - await filterBar.removeAllFilters(); - }); - - it('should visualize scripted field in vertical bar chart', async function () { - await PageObjects.unifiedFieldList.clickFieldListItemVisualize(scriptedPainlessFieldName2); - await PageObjects.header.waitUntilLoadingHasFinished(); - // verify Lens opens a visualization - expect(await testSubjects.getVisibleTextAll('lns-dimensionTrigger')).to.contain( - 'Top 5 values of painString' - ); - }); - }); - - describe('creating and using Painless boolean scripted fields', function describeIndexTests() { - const scriptedPainlessFieldName2 = 'painBool'; - - it('should create scripted field', async function () { - await PageObjects.settings.navigateTo(); - await PageObjects.settings.clickKibanaIndexPatterns(); - await PageObjects.settings.clickIndexPatternLogstash(); - const startingCount = parseInt(await PageObjects.settings.getScriptedFieldsTabCount(), 10); - await PageObjects.settings.clickScriptedFieldsTab(); - await log.debug('add scripted field'); - await PageObjects.settings.addScriptedField( - scriptedPainlessFieldName2, - 'painless', - 'boolean', - null, - '1', - "doc['response.raw'].value == '200'" - ); - await retry.try(async function () { - expect(parseInt(await PageObjects.settings.getScriptedFieldsTabCount(), 10)).to.be( - startingCount + 1 - ); - }); - }); - - it('should see scripted field value in Discover', async function () { - const fromTime = 'Sep 17, 2015 @ 06:31:44.000'; - const toTime = 'Sep 18, 2015 @ 18:31:44.000'; - await PageObjects.common.navigateToApp('discover'); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); - - await retry.try(async function () { - await PageObjects.unifiedFieldList.clickFieldListItemAdd(scriptedPainlessFieldName2); - }); - await PageObjects.header.waitUntilLoadingHasFinished(); - - await retry.try(async function () { - const rowData = await PageObjects.discover.getDocTableIndexLegacy(1); - expect(rowData).to.be('Sep 18, 2015 @ 18:20:57.916\ntrue'); - }); - }); - - it('should filter by scripted field value in Discover', async function () { - await PageObjects.unifiedFieldList.clickFieldListItem(scriptedPainlessFieldName2); - await log.debug('filter by "true" in the expanded scripted field list'); - await PageObjects.unifiedFieldList.clickFieldListPlusFilter( - scriptedPainlessFieldName2, - 'true' - ); - await PageObjects.header.waitUntilLoadingHasFinished(); - - await retry.try(async function () { - expect(await PageObjects.discover.getHitCount()).to.be('359'); - }); - await filterBar.removeAllFilters(); - }); - - // add a test to sort boolean - // existing bug: https://github.com/elastic/kibana/issues/75519 hence the issue is skipped. - it.skip('should sort scripted field value in Discover', async function () { - await testSubjects.click(`docTableHeaderFieldSort_${scriptedPainlessFieldName2}`); - // after the first click on the scripted field, it becomes secondary sort after time. - // click on the timestamp twice to make it be the secondary sort key. - await testSubjects.click('docTableHeaderFieldSort_@timestamp'); - await testSubjects.click('docTableHeaderFieldSort_@timestamp'); - await PageObjects.header.waitUntilLoadingHasFinished(); - await retry.try(async function () { - const rowData = await PageObjects.discover.getDocTableIndexLegacy(1); - expect(rowData).to.be('updateExpectedResultHere\ntrue'); - }); - - await testSubjects.click(`docTableHeaderFieldSort_${scriptedPainlessFieldName2}`); - await PageObjects.header.waitUntilLoadingHasFinished(); - await retry.try(async function () { - const rowData = await PageObjects.discover.getDocTableIndexLegacy(1); - expect(rowData).to.be('updateExpectedResultHere\nfalse'); - }); - }); - - it('should visualize scripted field in vertical bar chart', async function () { - await PageObjects.unifiedFieldList.clickFieldListItemVisualize(scriptedPainlessFieldName2); - await PageObjects.header.waitUntilLoadingHasFinished(); - // verify Lens opens a visualization - expect(await testSubjects.getVisibleTextAll('lns-dimensionTrigger')).to.contain( - 'Top 5 values of painBool' - ); - }); - }); - - describe('creating and using Painless date scripted fields', function describeIndexTests() { - const scriptedPainlessFieldName2 = 'painDate'; - - it('should create scripted field', async function () { - await PageObjects.settings.navigateTo(); - await PageObjects.settings.clickKibanaIndexPatterns(); - await PageObjects.settings.clickIndexPatternLogstash(); - const startingCount = parseInt(await PageObjects.settings.getScriptedFieldsTabCount(), 10); - await PageObjects.settings.clickScriptedFieldsTab(); - await log.debug('add scripted field'); - await PageObjects.settings.addScriptedField( - scriptedPainlessFieldName2, - 'painless', - 'date', - { format: 'date', datePattern: 'YYYY-MM-DD HH:00' }, - '1', - "doc['utc_time'].value.toEpochMilli() + (1000) * 60 * 60" - ); - await retry.try(async function () { - expect(parseInt(await PageObjects.settings.getScriptedFieldsTabCount(), 10)).to.be( - startingCount + 1 - ); - }); - }); - - it('should see scripted field value in Discover', async function () { - const fromTime = 'Sep 17, 2015 @ 19:22:00.000'; - const toTime = 'Sep 18, 2015 @ 07:00:00.000'; - await PageObjects.common.navigateToApp('discover'); - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); - - await retry.try(async function () { - await PageObjects.unifiedFieldList.clickFieldListItemAdd(scriptedPainlessFieldName2); - }); - await PageObjects.header.waitUntilLoadingHasFinished(); - - await retry.try(async function () { - const rowData = await PageObjects.discover.getDocTableIndexLegacy(1); - expect(rowData).to.be('Sep 18, 2015 @ 06:52:55.953\n2015-09-18 07:00'); - }); - }); - - // add a test to sort date scripted field - // https://github.com/elastic/kibana/issues/75711 - it.skip('should sort scripted field value in Discover', async function () { - await testSubjects.click(`docTableHeaderFieldSort_${scriptedPainlessFieldName2}`); - // after the first click on the scripted field, it becomes secondary sort after time. - // click on the timestamp twice to make it be the secondary sort key. - await testSubjects.click('docTableHeaderFieldSort_@timestamp'); - await testSubjects.click('docTableHeaderFieldSort_@timestamp'); - await PageObjects.header.waitUntilLoadingHasFinished(); - await retry.try(async function () { - const rowData = await PageObjects.discover.getDocTableIndexLegacy(1); - expect(rowData).to.be('updateExpectedResultHere\n2015-09-18 07:00'); - }); - - await testSubjects.click(`docTableHeaderFieldSort_${scriptedPainlessFieldName2}`); - await PageObjects.header.waitUntilLoadingHasFinished(); - await retry.try(async function () { - const rowData = await PageObjects.discover.getDocTableIndexLegacy(1); - expect(rowData).to.be('updateExpectedResultHere\n2015-09-18 07:00'); - }); - }); - - it('should filter by scripted field value in Discover', async function () { - await PageObjects.header.waitUntilLoadingHasFinished(); - await docTable.toggleRowExpanded(); - const firstRow = await docTable.getDetailsRow(); - await docTable.addInclusiveFilter(firstRow, scriptedPainlessFieldName2); - await PageObjects.header.waitUntilLoadingHasFinished(); - - await retry.try(async function () { - expect(await PageObjects.discover.getHitCount()).to.be('1'); - }); - await filterBar.removeAllFilters(); - }); - - it('should visualize scripted field in vertical bar chart', async function () { - await PageObjects.unifiedFieldList.clickFieldListItemVisualize(scriptedPainlessFieldName2); - await PageObjects.header.waitUntilLoadingHasFinished(); - // verify Lens opens a visualization - expect(await testSubjects.getVisibleTextAll('lns-dimensionTrigger')).to.contain('painDate'); - }); - }); - }); -} diff --git a/test/functional/apps/management/index.ts b/test/functional/apps/management/index.ts index 2300543f06d51..962aa41bd8ba3 100644 --- a/test/functional/apps/management/index.ts +++ b/test/functional/apps/management/index.ts @@ -31,7 +31,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./_mgmt_import_saved_objects')); loadTestFile(require.resolve('./data_views/_index_patterns_empty')); loadTestFile(require.resolve('./data_views/_scripted_fields')); - loadTestFile(require.resolve('./data_views/_scripted_fields_classic_table')); loadTestFile(require.resolve('./data_views/_runtime_fields')); loadTestFile(require.resolve('./data_views/_runtime_fields_composite')); loadTestFile(require.resolve('./data_views/_field_formatter')); diff --git a/test/functional/firefox/discover.config.ts b/test/functional/firefox/discover.config.ts index 7012a3bccad16..9c4c689f0e8ca 100644 --- a/test/functional/firefox/discover.config.ts +++ b/test/functional/firefox/discover.config.ts @@ -18,7 +18,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { ...baseConfig.getAll(), testFiles: [ - require.resolve('../apps/discover/classic'), require.resolve('../apps/discover/esql'), require.resolve('../apps/discover/group1'), require.resolve('../apps/discover/group2_data_grid1'), diff --git a/test/functional/page_objects/console_page.ts b/test/functional/page_objects/console_page.ts index a80f3426e256e..30a4b27c3f037 100644 --- a/test/functional/page_objects/console_page.ts +++ b/test/functional/page_objects/console_page.ts @@ -52,6 +52,13 @@ export class ConsolePageObject extends FtrService { await textArea.clearValueWithKeyboard(); } + public async focusInputEditor() { + const outputEditor = await this.testSubjects.find('consoleMonacoEditor'); + // Simply clicking on the editor doesn't focus it, so we need to click + // on the margin view overlays + await (await outputEditor.findByClassName('margin-view-overlays')).click(); + } + public async focusOutputEditor() { const outputEditor = await this.testSubjects.find('consoleMonacoOutput'); // Simply clicking on the output editor doesn't focus it, so we need to click diff --git a/test/functional/page_objects/dashboard_page.ts b/test/functional/page_objects/dashboard_page.ts index 2a263e9aa8ca7..c9ba5b0c5fa88 100644 --- a/test/functional/page_objects/dashboard_page.ts +++ b/test/functional/page_objects/dashboard_page.ts @@ -49,7 +49,6 @@ export class DashboardPageObject extends FtrService { private readonly common = this.ctx.getPageObject('common'); private readonly header = this.ctx.getPageObject('header'); private readonly visualize = this.ctx.getPageObject('visualize'); - private readonly discover = this.ctx.getPageObject('discover'); private readonly appsMenu = this.ctx.getService('appsMenu'); private readonly toasts = this.ctx.getService('toasts'); @@ -271,22 +270,12 @@ export class DashboardPageObject extends FtrService { */ public async expectToolbarPaginationDisplayed() { - const isLegacyDefault = await this.discover.useLegacyTable(); - if (isLegacyDefault) { - const subjects = [ - 'pagination-button-previous', - 'pagination-button-next', - 'toolBarTotalDocsText', - ]; - await Promise.all(subjects.map(async (subj) => await this.testSubjects.existOrFail(subj))); - } else { - const subjects = ['pagination-button-previous', 'pagination-button-next']; + const subjects = ['pagination-button-previous', 'pagination-button-next']; - await Promise.all(subjects.map(async (subj) => await this.testSubjects.existOrFail(subj))); - const paginationListExists = await this.find.existsByCssSelector('.euiPagination__list'); - if (!paginationListExists) { - throw new Error(`expected discover data grid pagination list to exist`); - } + await Promise.all(subjects.map(async (subj) => await this.testSubjects.existOrFail(subj))); + const paginationListExists = await this.find.existsByCssSelector('.euiPagination__list'); + if (!paginationListExists) { + throw new Error(`expected discover data grid pagination list to exist`); } } diff --git a/test/functional/page_objects/discover_page.ts b/test/functional/page_objects/discover_page.ts index 13289ae9b8aa5..1f5945ff379f3 100644 --- a/test/functional/page_objects/discover_page.ts +++ b/test/functional/page_objects/discover_page.ts @@ -22,10 +22,8 @@ export class DiscoverPageObject extends FtrService { private readonly browser = this.ctx.getService('browser'); private readonly globalNav = this.ctx.getService('globalNav'); private readonly elasticChart = this.ctx.getService('elasticChart'); - private readonly docTable = this.ctx.getService('docTable'); private readonly config = this.ctx.getService('config'); private readonly dataGrid = this.ctx.getService('dataGrid'); - private readonly kibanaServer = this.ctx.getService('kibanaServer'); private readonly fieldEditor = this.ctx.getService('fieldEditor'); private readonly queryBar = this.ctx.getService('queryBar'); private readonly savedObjectsFinder = this.ctx.getService('savedObjectsFinder'); @@ -42,15 +40,6 @@ export class DiscoverPageObject extends FtrService { return await this.testSubjects.getAttribute('unifiedHistogramChart', 'data-time-range'); } - public async getDocTable() { - const isLegacyDefault = await this.useLegacyTable(); - if (isLegacyDefault) { - return this.docTable; - } else { - return this.dataGrid; - } - } - public async saveSearch( searchName: string, saveAsNew?: boolean, @@ -117,12 +106,7 @@ export class DiscoverPageObject extends FtrService { } public async getColumnHeaders() { - const isLegacy = await this.useLegacyTable(); - if (isLegacy) { - return await this.docTable.getHeaderFields('embeddedSavedSearchDocTable'); - } - const table = await this.getDocTable(); - return await table.getHeaderFields(); + return await this.dataGrid.getHeaderFields(); } public async openLoadSavedSearchPanel() { @@ -361,28 +345,16 @@ export class DiscoverPageObject extends FtrService { } public async getDocHeader() { - const table = await this.getDocTable(); - const docHeader = await table.getHeaders(); + const docHeader = await this.dataGrid.getHeaders(); return docHeader.join(); } public async getDocTableRows() { await this.header.waitUntilLoadingHasFinished(); - const table = await this.getDocTable(); - return await table.getBodyRows(); - } - - public async useLegacyTable() { - return (await this.kibanaServer.uiSettings.get('doc_table:legacy')) === true; + return await this.dataGrid.getBodyRows(); } public async getDocTableIndex(index: number, visibleText = false) { - const isLegacyDefault = await this.useLegacyTable(); - if (isLegacyDefault) { - const row = await this.find.byCssSelector(`tr.kbnDocTable__row:nth-child(${index})`); - return await row.getVisibleText(); - } - const row = await this.dataGrid.getRow({ rowIndex: index - 1 }); const result = await Promise.all( row.map(async (cell) => { @@ -398,21 +370,10 @@ export class DiscoverPageObject extends FtrService { return result.slice(await this.dataGrid.getControlColumnsCount()).join(' '); } - public async getDocTableIndexLegacy(index: number) { - const row = await this.find.byCssSelector(`tr.kbnDocTable__row:nth-child(${index})`); - return await row.getVisibleText(); - } - public async getDocTableField(index: number, cellIdx: number = -1) { - const isLegacyDefault = await this.useLegacyTable(); - const usedDefaultCellIdx = isLegacyDefault ? 0 : await this.dataGrid.getControlColumnsCount(); + const usedDefaultCellIdx = await this.dataGrid.getControlColumnsCount(); const usedCellIdx = cellIdx === -1 ? usedDefaultCellIdx : cellIdx; - if (isLegacyDefault) { - const fields = await this.find.allByCssSelector( - `tr.kbnDocTable__row:nth-child(${index}) [data-test-subj='docTableField']` - ); - return await fields[usedCellIdx].getVisibleText(); - } + await this.testSubjects.click('dataGridFullScreenButton'); const row = await this.dataGrid.getRow({ rowIndex: index - 1 }); const result = await Promise.all(row.map(async (cell) => (await cell.getVisibleText()).trim())); @@ -420,33 +381,6 @@ export class DiscoverPageObject extends FtrService { return result[usedCellIdx]; } - public async clickDocTableRowToggle(rowIndex: number = 0) { - const docTable = await this.getDocTable(); - await docTable.clickRowToggle({ rowIndex }); - } - - public async skipToEndOfDocTable() { - // add the focus to the button to make it appear - const skipButton = await this.testSubjects.find('discoverSkipTableButton'); - // force focus on it, to make it interactable - await skipButton.focus(); - // now click it! - return skipButton.click(); - } - - /** - * When scrolling down the legacy table there's a link to scroll up - * So this is done by this function - */ - public async backToTop() { - const skipButton = await this.testSubjects.find('discoverBackToTop'); - return skipButton.click(); - } - - public async getDocTableFooter() { - return await this.testSubjects.find('discoverDocTableFooter'); - } - public isShowingDocViewer() { return this.dataGrid.isShowingDocViewer(); } @@ -478,7 +412,7 @@ export class DiscoverPageObject extends FtrService { } public async getMarks() { - const table = await this.docTable.getTable(); + const table = await this.dataGrid.getTable(); const marks = await table.findAllByTagName('mark'); return await Promise.all(marks.map((mark) => mark.getVisibleText())); } @@ -573,10 +507,6 @@ export class DiscoverPageObject extends FtrService { } public async clickFieldSort(field: string, text = 'Sort New-Old') { - const isLegacyDefault = await this.useLegacyTable(); - if (isLegacyDefault) { - return await this.testSubjects.click(`docTableHeaderFieldSort_${field}`); - } return await this.dataGrid.clickDocSortAsc(field, text); } @@ -608,13 +538,7 @@ export class DiscoverPageObject extends FtrService { } public async removeHeaderColumn(name: string) { - const isLegacyDefault = await this.useLegacyTable(); - if (isLegacyDefault) { - await this.testSubjects.moveMouseTo(`docTableHeader-${name}`); - await this.testSubjects.click(`docTableRemoveHeader-${name}`); - } else { - await this.dataGrid.clickRemoveColumn(name); - } + await this.dataGrid.clickRemoveColumn(name); } public async waitForChartLoadingComplete(renderCount: number) { diff --git a/test/functional/services/dashboard/expectations.ts b/test/functional/services/dashboard/expectations.ts index 414a44b24df97..ec43f1ec80120 100644 --- a/test/functional/services/dashboard/expectations.ts +++ b/test/functional/services/dashboard/expectations.ts @@ -81,14 +81,6 @@ export class DashboardExpectService extends FtrService { }); } - async docTableFieldCount(expectedCount: number) { - this.log.debug(`DashboardExpect.docTableFieldCount(${expectedCount})`); - await this.retry.try(async () => { - const docTableCells = await this.testSubjects.findAll('docTableField', this.findTimeout); - expect(docTableCells.length).to.be(expectedCount); - }); - } - async fieldSuggestions(expectedFields: string[]) { this.log.debug(`DashboardExpect.fieldSuggestions(${expectedFields})`); const fields = await this.filterBar.getFilterEditorFields(); diff --git a/test/functional/services/data_grid.ts b/test/functional/services/data_grid.ts index 3b17a31d624b6..4500bc5b97154 100644 --- a/test/functional/services/data_grid.ts +++ b/test/functional/services/data_grid.ts @@ -499,14 +499,6 @@ export class DataGridService extends FtrService { return await detailsRow.findAllByTestSubject('~docTableRowAction'); } - public async getAnchorDetailsRow(): Promise { - const table = await this.getTable(); - - return await table.findByCssSelector( - '[data-test-subj~="docTableAnchorRow"] + [data-test-subj~="docTableDetailsRow"]' - ); - } - public async openColMenuByField(field: string) { await this.retry.waitFor('header cell action being displayed', async () => { // to prevent flakiness @@ -671,24 +663,6 @@ export class DataGridService extends FtrService { await sampleSizeInput.type(String(newValue)); } - public async getDetailsRow(): Promise { - const detailRows = await this.getDetailsRows(); - return detailRows[0]; - } - - public async getTableDocViewRow( - detailsRow: WebElementWrapper, - fieldName: string - ): Promise { - return await detailsRow.findByTestSubject(`~tableDocViewRow-${fieldName}`); - } - - public async getRemoveInclusiveFilterButton( - tableDocViewRow: WebElementWrapper - ): Promise { - return await tableDocViewRow.findByTestSubject(`~removeInclusiveFilterButton`); - } - public async showFieldCellActionInFlyout(fieldName: string, actionName: string): Promise { const cellSelector = ['addFilterForValueButton', 'addFilterOutValueButton'].includes(actionName) ? `tableDocViewRow-${fieldName}-value` diff --git a/test/functional/services/doc_table.ts b/test/functional/services/doc_table.ts deleted file mode 100644 index 0ca243b7b412b..0000000000000 --- a/test/functional/services/doc_table.ts +++ /dev/null @@ -1,176 +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 { WebElementWrapper } from '@kbn/ftr-common-functional-ui-services'; -import { FtrService } from '../ftr_provider_context'; - -interface SelectOptions { - isAnchorRow?: boolean; - rowIndex?: number; -} - -export class DocTableService extends FtrService { - private readonly testSubjects = this.ctx.getService('testSubjects'); - private readonly retry = this.ctx.getService('retry'); - private readonly header = this.ctx.getPageObject('header'); - - public async getTable(selector?: string) { - return await this.testSubjects.find(selector ? selector : 'docTable'); - } - - public async getRowsText() { - const table = await this.getTable(); - const $ = await table.parseDomContent(); - return $.findTestSubjects('~docTableRow') - .toArray() - .map((row: any) => $(row).text().trim()); - } - - public async getBodyRows(): Promise { - const table = await this.getTable(); - return await table.findAllByTestSubject('~docTableRow'); - } - - public async getAnchorRow(): Promise { - const table = await this.getTable(); - return await table.findByTestSubject('~docTableAnchorRow'); - } - - public async getRow({ - isAnchorRow = false, - rowIndex = 0, - }: SelectOptions = {}): Promise { - return isAnchorRow ? await this.getAnchorRow() : (await this.getBodyRows())[rowIndex]; - } - - public async getDetailsRow(): Promise { - const table = await this.getTable(); - return await table.findByCssSelector('[data-test-subj~="docTableDetailsRow"]'); - } - - public async getAnchorDetailsRow(): Promise { - const table = await this.getTable(); - return await table.findByCssSelector( - '[data-test-subj~="docTableAnchorRow"] + [data-test-subj~="docTableDetailsRow"]' - ); - } - - public async clickRowToggle( - options: SelectOptions = { isAnchorRow: false, rowIndex: 0 } - ): Promise { - const row = await this.getRow(options); - const toggle = await row.findByTestSubject('~docTableExpandToggleColumn'); - await toggle.click(); - } - - public async getDetailsRows(): Promise { - const table = await this.getTable(); - return await table.findAllByCssSelector( - '[data-test-subj~="docTableRow"] + [data-test-subj~="docTableDetailsRow"]' - ); - } - - public async getRowActions({ isAnchorRow = false, rowIndex = 0 }: SelectOptions = {}): Promise< - WebElementWrapper[] - > { - const detailsRow = isAnchorRow - ? await this.getAnchorDetailsRow() - : (await this.getDetailsRows())[rowIndex]; - return await detailsRow.findAllByTestSubject('~docTableRowAction'); - } - - public async getFields(options: { isAnchorRow: boolean } = { isAnchorRow: false }) { - const table = await this.getTable(); - const $ = await table.parseDomContent(); - const rowLocator = options.isAnchorRow ? '~docTableAnchorRow' : '~docTableRow'; - const rows = $.findTestSubjects(rowLocator).toArray(); - return rows.map((row: any) => - $(row) - .find('[data-test-subj~="docTableField"]') - .toArray() - .map((field: any) => $(field).text()) - ); - } - - public async getHeaderFields(selector?: string): Promise { - const table = await this.getTable(selector); - const $ = await table.parseDomContent(); - return $.findTestSubjects('~docTableHeaderField') - .toArray() - .map((field: any) => $(field).text().trim()); - } - - public async getHeaders(selector?: string): Promise { - return this.getHeaderFields(selector); - } - - public async getTableDocViewRow( - detailsRow: WebElementWrapper, - fieldName: string - ): Promise { - return await detailsRow.findByTestSubject(`~tableDocViewRow-${fieldName}`); - } - - public async getAddInclusiveFilterButton( - tableDocViewRow: WebElementWrapper - ): Promise { - return await tableDocViewRow.findByTestSubject(`~addInclusiveFilterButton`); - } - - public async addInclusiveFilter(detailsRow: WebElementWrapper, fieldName: string): Promise { - const tableDocViewRow = await this.getTableDocViewRow(detailsRow, fieldName); - const addInclusiveFilterButton = await this.getAddInclusiveFilterButton(tableDocViewRow); - await addInclusiveFilterButton.click(); - await this.header.awaitGlobalLoadingIndicatorHidden(); - } - - public async getRemoveInclusiveFilterButton( - tableDocViewRow: WebElementWrapper - ): Promise { - return await tableDocViewRow.findByTestSubject(`~removeInclusiveFilterButton`); - } - - public async removeInclusiveFilter( - detailsRow: WebElementWrapper, - fieldName: string - ): Promise { - const tableDocViewRow = await this.getTableDocViewRow(detailsRow, fieldName); - const addInclusiveFilterButton = await this.getRemoveInclusiveFilterButton(tableDocViewRow); - await addInclusiveFilterButton.click(); - await this.header.awaitGlobalLoadingIndicatorHidden(); - } - - public async getAddExistsFilterButton( - tableDocViewRow: WebElementWrapper - ): Promise { - return await tableDocViewRow.findByTestSubject(`~addExistsFilterButton`); - } - - public async addExistsFilter(detailsRow: WebElementWrapper, fieldName: string): Promise { - const tableDocViewRow = await this.getTableDocViewRow(detailsRow, fieldName); - const addInclusiveFilterButton = await this.getAddExistsFilterButton(tableDocViewRow); - await addInclusiveFilterButton.click(); - await this.header.awaitGlobalLoadingIndicatorHidden(); - } - - public async toggleRowExpanded({ - isAnchorRow = false, - rowIndex = 0, - }: SelectOptions = {}): Promise { - await this.clickRowToggle({ isAnchorRow, rowIndex }); - await this.header.awaitGlobalLoadingIndicatorHidden(); - return await this.retry.try(async () => { - const row = isAnchorRow ? await this.getAnchorRow() : (await this.getBodyRows())[rowIndex]; - const detailsRow = await row.findByXpath( - './following-sibling::*[@data-test-subj="docTableDetailsRow"]' - ); - return detailsRow.findByTestSubject('~docViewer'); - }); - } -} diff --git a/test/functional/services/index.ts b/test/functional/services/index.ts index 77df85ef3e565..ccdc8cfec3957 100644 --- a/test/functional/services/index.ts +++ b/test/functional/services/index.ts @@ -30,7 +30,6 @@ import { DashboardDrilldownPanelActionsProvider, DashboardDrilldownsManageProvider, } from './dashboard'; -import { DocTableService } from './doc_table'; import { EmbeddingService } from './embedding'; import { FilterBarService } from './filter_bar'; import { FlyoutService } from './flyout'; @@ -62,7 +61,6 @@ export const services = { ...commonFunctionalUIServices, filterBar: FilterBarService, queryBar: QueryBarService, - docTable: DocTableService, png: PngService, screenshots: ScreenshotsService, snapshots: SnapshotsService, diff --git a/tsconfig.base.json b/tsconfig.base.json index ed8b402e2d179..c690ecf1a7f93 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -96,6 +96,8 @@ "@kbn/app-link-test-plugin/*": ["test/plugin_functional/plugins/app_link_test/*"], "@kbn/application-usage-test-plugin": ["x-pack/test/usage_collection/plugins/application_usage_test"], "@kbn/application-usage-test-plugin/*": ["x-pack/test/usage_collection/plugins/application_usage_test/*"], + "@kbn/asset-inventory-plugin": ["x-pack/plugins/asset_inventory"], + "@kbn/asset-inventory-plugin/*": ["x-pack/plugins/asset_inventory/*"], "@kbn/audit-log-plugin": ["x-pack/test/security_api_integration/plugins/audit_log"], "@kbn/audit-log-plugin/*": ["x-pack/test/security_api_integration/plugins/audit_log/*"], "@kbn/avc-banner": ["packages/kbn-avc-banner"], diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_discover.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_discover.tsx index 5bf415c5ccfc9..f4c25eafa5da4 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_discover.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_discover.tsx @@ -401,9 +401,6 @@ export const LogCategorizationDiscover: FC = ( ); const style = css({ overflowY: 'auto', - '.kbnDocTableWrapper': { - overflowX: 'hidden', - }, }); const actions = getActions(false); diff --git a/x-pack/plugins/asset_inventory/README.md b/x-pack/plugins/asset_inventory/README.md new file mode 100755 index 0000000000000..e1dd9d4ac8900 --- /dev/null +++ b/x-pack/plugins/asset_inventory/README.md @@ -0,0 +1,13 @@ +# Asset Inventory Kibana Plugin + +Centralized asset inventory experience within the Elastic Security solution. A central place for users to view and manage all their assets from different environments. + +--- + +## Development + +See the [kibana contributing guide](https://github.com/elastic/kibana/blob/main/CONTRIBUTING.md) for instructions setting up your development environment. + +## Testing + +For general guidelines, read [Kibana Testing Guide](https://www.elastic.co/guide/en/kibana/current/development-tests.html) for more details diff --git a/x-pack/plugins/asset_inventory/common/index.ts b/x-pack/plugins/asset_inventory/common/index.ts new file mode 100644 index 0000000000000..9f5311be877cc --- /dev/null +++ b/x-pack/plugins/asset_inventory/common/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +export const PLUGIN_ID = 'assetInventory'; +export const PLUGIN_NAME = 'assetInventory'; diff --git a/x-pack/plugins/asset_inventory/kibana.jsonc b/x-pack/plugins/asset_inventory/kibana.jsonc new file mode 100644 index 0000000000000..dbb813be7aa4e --- /dev/null +++ b/x-pack/plugins/asset_inventory/kibana.jsonc @@ -0,0 +1,17 @@ +{ + "type": "plugin", + "id": "@kbn/asset-inventory-plugin", + "owner": ["@elastic/kibana-cloud-security-posture"], + "group": "security", + "visibility": "private", + "description": "Centralized asset inventory experience within the Elastic Security solution. A central place for users to view and manage all their assets from different environments", + "plugin": { + "id": "assetInventory", + "browser": true, + "server": true, + "configPath": ["xpack", "assetInventory"], + "requiredPlugins": [], + "requiredBundles": [], + "optionalPlugins": [] + } +} diff --git a/x-pack/plugins/asset_inventory/package.json b/x-pack/plugins/asset_inventory/package.json new file mode 100644 index 0000000000000..7abfb7bf63378 --- /dev/null +++ b/x-pack/plugins/asset_inventory/package.json @@ -0,0 +1,7 @@ +{ + "author": "Elastic", + "name": "@kbn/asset-inventory-plugin", + "version": "1.0.0", + "private": true, + "license": "Elastic License 2.0" +} diff --git a/x-pack/plugins/asset_inventory/public/application.tsx b/x-pack/plugins/asset_inventory/public/application.tsx new file mode 100644 index 0000000000000..f442f01d17f7c --- /dev/null +++ b/x-pack/plugins/asset_inventory/public/application.tsx @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import type { AppMountParameters, CoreStart } from '@kbn/core/public'; +import type { AppPluginStartDependencies } from './types'; +import { AssetInventoryApp } from './components/app'; + +export const renderApp = ( + { notifications, http }: CoreStart, + {}: AppPluginStartDependencies, + { appBasePath, element }: AppMountParameters +) => { + ReactDOM.render( + , + element + ); + + return () => ReactDOM.unmountComponentAtNode(element); +}; diff --git a/x-pack/plugins/asset_inventory/public/components/app.tsx b/x-pack/plugins/asset_inventory/public/components/app.tsx new file mode 100644 index 0000000000000..924091034353b --- /dev/null +++ b/x-pack/plugins/asset_inventory/public/components/app.tsx @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { FormattedMessage, I18nProvider } from '@kbn/i18n-react'; +import { BrowserRouter as Router } from '@kbn/shared-ux-router'; +import { EuiPageTemplate, EuiTitle } from '@elastic/eui'; +import type { CoreStart } from '@kbn/core/public'; + +interface AssetInventoryAppDeps { + basename: string; + notifications: CoreStart['notifications']; + http: CoreStart['http']; +} + +export const AssetInventoryApp = ({ basename }: AssetInventoryAppDeps) => { + return ( + + + <> + + + +

+ +

+
+
+ +
+ +
+
+ ); +}; diff --git a/x-pack/plugins/asset_inventory/public/index.ts b/x-pack/plugins/asset_inventory/public/index.ts new file mode 100644 index 0000000000000..269cab2ad181a --- /dev/null +++ b/x-pack/plugins/asset_inventory/public/index.ts @@ -0,0 +1,14 @@ +/* + * 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 { AssetInventoryPlugin } from './plugin'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. +export function plugin() { + return new AssetInventoryPlugin(); +} +export type { AssetInventoryPluginSetup, AssetInventoryPluginStart } from './types'; diff --git a/x-pack/plugins/asset_inventory/public/plugin.ts b/x-pack/plugins/asset_inventory/public/plugin.ts new file mode 100644 index 0000000000000..fd2841f5b2335 --- /dev/null +++ b/x-pack/plugins/asset_inventory/public/plugin.ts @@ -0,0 +1,35 @@ +/* + * 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 type { AppMountParameters, CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; +import type { + AssetInventoryPluginSetup, + AssetInventoryPluginStart, + AppPluginStartDependencies, +} from './types'; + +export class AssetInventoryPlugin + implements Plugin +{ + public setup(core: CoreSetup): AssetInventoryPluginSetup { + return {}; + } + public start( + coreStart: CoreStart, + depsStart: AppPluginStartDependencies + ): AssetInventoryPluginStart { + return { + getAssetInventoryPage: async (params: AppMountParameters) => { + // Load application bundle + const { renderApp } = await import('./application'); + // Render the application + return renderApp(coreStart, depsStart as AppPluginStartDependencies, params); + }, + }; + } + + public stop() {} +} diff --git a/x-pack/plugins/asset_inventory/public/types.ts b/x-pack/plugins/asset_inventory/public/types.ts new file mode 100644 index 0000000000000..a551b4d231c3d --- /dev/null +++ b/x-pack/plugins/asset_inventory/public/types.ts @@ -0,0 +1,15 @@ +/* + * 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. + */ + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface AssetInventoryPluginSetup {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface AssetInventoryPluginStart {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface AppPluginStartDependencies {} diff --git a/x-pack/plugins/asset_inventory/server/create_transforms/create_transforms.ts b/x-pack/plugins/asset_inventory/server/create_transforms/create_transforms.ts new file mode 100644 index 0000000000000..0d3d0e8f8e0c4 --- /dev/null +++ b/x-pack/plugins/asset_inventory/server/create_transforms/create_transforms.ts @@ -0,0 +1,142 @@ +/* + * 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 { transformError } from '@kbn/securitysolution-es-utils'; +import { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types'; +import type { ElasticsearchClient, Logger } from '@kbn/core/server'; +import { errors } from '@elastic/elasticsearch'; + +// TODO: Move transforms to integration package +export const initializeTransforms = async ( + esClient: ElasticsearchClient, + logger: Logger +): Promise => { + // Deletes old assets from previous versions as part of upgrade process + await deletePreviousTransformsVersions(esClient, logger); + // TODO initialize transforms here + // await initializeTransform(esClient, , logger); +}; + +export const initializeTransform = async ( + esClient: ElasticsearchClient, + transform: TransformPutTransformRequest, + logger: Logger +) => { + const success = await createTransformIfNotExists(esClient, transform, logger); + + if (success) { + await startTransformIfNotStarted(esClient, transform.transform_id, logger); + } +}; + +/** + * Checks if a transform exists, And if not creates it + * + * @param transform - the transform to create. If a transform with the same transform_id already exists, nothing is created or updated. + * + * @return true if the transform exits or created, false otherwise. + */ +export const createTransformIfNotExists = async ( + esClient: ElasticsearchClient, + transform: TransformPutTransformRequest, + logger: Logger +) => { + try { + await esClient.transform.getTransform({ + transform_id: transform.transform_id, + }); + return true; + } catch (existErr) { + const existError = transformError(existErr); + if (existError.statusCode === 404) { + try { + await esClient.transform.putTransform(transform); + return true; + } catch (createErr) { + const createError = transformError(createErr); + logger.error( + `Failed to create transform ${transform.transform_id}: ${createError.message}` + ); + } + } else { + logger.error( + `Failed to check if transform ${transform.transform_id} exists: ${existError.message}` + ); + } + } + return false; +}; + +export const startTransformIfNotStarted = async ( + esClient: ElasticsearchClient, + transformId: string, + logger: Logger +) => { + try { + const transformStats = await esClient.transform.getTransformStats({ + transform_id: transformId, + }); + + if (transformStats.count <= 0) { + logger.error(`Failed starting transform ${transformId}: couldn't find transform`); + return; + } + + const fetchedTransformStats = transformStats.transforms[0]; + + // trying to restart the transform in case it comes to a full stop or failure + if (fetchedTransformStats.state === 'stopped' || fetchedTransformStats.state === 'failed') { + try { + return await esClient.transform.startTransform({ transform_id: transformId }); + } catch (startErr) { + const startError = transformError(startErr); + logger.error( + `Failed to start transform ${transformId}. Transform State: Transform State: ${fetchedTransformStats.state}. Error: ${startError.message}` + ); + } + } + + if (fetchedTransformStats.state === 'stopping' || fetchedTransformStats.state === 'aborting') { + logger.error( + `Not starting transform ${transformId} since it's state is: ${fetchedTransformStats.state}` + ); + } + } catch (statsErr) { + const statsError = transformError(statsErr); + logger.error(`Failed to check if transform ${transformId} is started: ${statsError.message}`); + } +}; + +const deletePreviousTransformsVersions = async (esClient: ElasticsearchClient, logger: Logger) => { + // TODO Concat all deprecated transforms versions + const deprecatedTransforms: string[] = []; + + for (const transform of deprecatedTransforms) { + const response = await deleteTransformSafe(esClient, logger, transform); + if (response) return; + } +}; + +const deleteTransformSafe = async ( + esClient: ElasticsearchClient, + logger: Logger, + name: string +): Promise => { + try { + await esClient.transform.deleteTransform({ transform_id: name, force: true }); + logger.info(`Deleted transform successfully [Name: ${name}]`); + return true; + } catch (e) { + if (e instanceof errors.ResponseError && e.statusCode === 404) { + logger.trace(`Transform not exists [Name: ${name}]`); + return false; + } else { + logger.error(`Failed to delete transform [Name: ${name}]`); + logger.error(e); + return false; + } + } +}; diff --git a/x-pack/plugins/asset_inventory/server/index.ts b/x-pack/plugins/asset_inventory/server/index.ts new file mode 100644 index 0000000000000..6306618078898 --- /dev/null +++ b/x-pack/plugins/asset_inventory/server/index.ts @@ -0,0 +1,17 @@ +/* + * 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 type { PluginInitializerContext } from '@kbn/core/server'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. + +export async function plugin(initializerContext: PluginInitializerContext) { + const { AssetInventoryPlugin } = await import('./plugin'); + return new AssetInventoryPlugin(initializerContext); +} + +export type { AssetInventoryPluginSetup, AssetInventoryPluginStart } from './types'; diff --git a/x-pack/plugins/asset_inventory/server/plugin.ts b/x-pack/plugins/asset_inventory/server/plugin.ts new file mode 100644 index 0000000000000..3f35991c379d5 --- /dev/null +++ b/x-pack/plugins/asset_inventory/server/plugin.ts @@ -0,0 +1,63 @@ +/* + * 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 type { + PluginInitializerContext, + CoreSetup, + CoreStart, + Plugin, + Logger, +} from '@kbn/core/server'; + +import type { AssetInventoryPluginSetup, AssetInventoryPluginStart } from './types'; +import { defineRoutes } from './routes'; +// TODO Uncomment this line when initialize() is enabled +// import { initializeTransforms } from './create_transforms/create_transforms'; + +export class AssetInventoryPlugin + implements Plugin +{ + private readonly logger: Logger; + + // TODO Uncomment this line when initialize() is enabled + // private isInitialized: boolean = false; + + constructor(initializerContext: PluginInitializerContext) { + this.logger = initializerContext.logger.get(); + } + + public setup(core: CoreSetup) { + this.logger.debug('assetInventory: Setup'); + const router = core.http.createRouter(); + + // Register server side APIs + defineRoutes(router); + + return {}; + } + + public start(core: CoreStart) { + this.logger.debug('assetInventory: Started'); + + // TODO Invoke initialize() when it's due + // this.initialize(core).catch(() => {}); + + return {}; + } + + public stop() {} + + /** + * Initialization is idempotent and required for (re)creating indices and transforms. + */ + // TODO Uncomment these lines when initialize() is enabled + // async initialize(core: CoreStart): Promise { + // this.logger.debug('initialize'); + // const esClient = core.elasticsearch.client.asInternalUser; + // await initializeTransforms(esClient, this.logger); + // this.isInitialized = true; + // } +} diff --git a/x-pack/plugins/asset_inventory/server/routes/index.ts b/x-pack/plugins/asset_inventory/server/routes/index.ts new file mode 100644 index 0000000000000..f577bfa19f719 --- /dev/null +++ b/x-pack/plugins/asset_inventory/server/routes/index.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import type { IRouter } from '@kbn/core/server'; + +export function defineRoutes(router: IRouter) { + router.get( + { + path: '/api/asset_inventory/example', + validate: false, + }, + async (context, request, response) => { + return response.ok({ + body: { + time: new Date().toISOString(), + }, + }); + } + ); +} diff --git a/x-pack/plugins/asset_inventory/server/types.ts b/x-pack/plugins/asset_inventory/server/types.ts new file mode 100644 index 0000000000000..1d9380e8514f8 --- /dev/null +++ b/x-pack/plugins/asset_inventory/server/types.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface AssetInventoryPluginSetup {} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface AssetInventoryPluginStart {} diff --git a/x-pack/plugins/asset_inventory/tsconfig.json b/x-pack/plugins/asset_inventory/tsconfig.json new file mode 100644 index 0000000000000..dc669eb3a6943 --- /dev/null +++ b/x-pack/plugins/asset_inventory/tsconfig.json @@ -0,0 +1,23 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types" + }, + "include": [ + "common/**/*.ts", + "common/**/*.json", + "public/**/*.ts", + "public/**/*.tsx", + "public/**/*.json", + "server/**/*.ts", + "server/**/*.json", + "../../../typings/**/*" + ], + "exclude": ["target/**/*"], + "kbn_references": [ + "@kbn/core", + "@kbn/i18n-react", + "@kbn/shared-ux-router", + "@kbn/securitysolution-es-utils" + ] +} diff --git a/x-pack/plugins/discover_enhanced/ui_tests/tests/saved_searches.spec.ts b/x-pack/plugins/discover_enhanced/ui_tests/tests/saved_searches.spec.ts index 184c9217bfe15..1398f5f24ab27 100644 --- a/x-pack/plugins/discover_enhanced/ui_tests/tests/saved_searches.spec.ts +++ b/x-pack/plugins/discover_enhanced/ui_tests/tests/saved_searches.spec.ts @@ -51,13 +51,12 @@ test.describe( await kbnClient.importExport.load(testData.KBN_ARCHIVES.ECOMMERCE); await uiSettings.set({ defaultIndex: testData.DATA_VIEW_ID.ECOMMERCE, - 'doc_table:legacy': false, 'timepicker:timeDefaults': `{ "from": "${START_TIME}", "to": "${END_TIME}"}`, }); }); test.afterAll(async ({ kbnClient, uiSettings }) => { - await uiSettings.unset('doc_table:legacy', 'defaultIndex', 'timepicker:timeDefaults'); + await uiSettings.unset('defaultIndex', 'timepicker:timeDefaults'); await kbnClient.savedObjects.cleanStandardList(); }); diff --git a/x-pack/plugins/discover_enhanced/ui_tests/tests/value_suggestions.spec.ts b/x-pack/plugins/discover_enhanced/ui_tests/tests/value_suggestions.spec.ts index f80daeea5b9c7..04836afb99b5b 100644 --- a/x-pack/plugins/discover_enhanced/ui_tests/tests/value_suggestions.spec.ts +++ b/x-pack/plugins/discover_enhanced/ui_tests/tests/value_suggestions.spec.ts @@ -17,13 +17,12 @@ test.describe( await kbnClient.importExport.load(testData.KBN_ARCHIVES.DASHBOARD_DRILLDOWNS); await uiSettings.set({ defaultIndex: testData.DATA_VIEW_ID.LOGSTASH, // TODO: investigate why it is required for `node scripts/playwright_test.js` run - 'doc_table:legacy': false, 'timepicker:timeDefaults': `{ "from": "${testData.LOGSTASH_DEFAULT_START_TIME}", "to": "${testData.LOGSTASH_DEFAULT_END_TIME}"}`, }); }); test.afterAll(async ({ kbnClient, uiSettings }) => { - await uiSettings.unset('doc_table:legacy', 'defaultIndex', 'timepicker:timeDefaults'); + await uiSettings.unset('defaultIndex', 'timepicker:timeDefaults'); await kbnClient.savedObjects.cleanStandardList(); }); diff --git a/x-pack/plugins/discover_enhanced/ui_tests/tests/value_suggestions_non_time_based.spec.ts b/x-pack/plugins/discover_enhanced/ui_tests/tests/value_suggestions_non_time_based.spec.ts index 0e4591ffd9e1e..319d8af3e93c9 100644 --- a/x-pack/plugins/discover_enhanced/ui_tests/tests/value_suggestions_non_time_based.spec.ts +++ b/x-pack/plugins/discover_enhanced/ui_tests/tests/value_suggestions_non_time_based.spec.ts @@ -17,12 +17,11 @@ test.describe( await kbnClient.importExport.load(testData.KBN_ARCHIVES.NO_TIME_FIELD); await uiSettings.set({ defaultIndex: 'without-timefield', - 'doc_table:legacy': false, }); }); test.afterAll(async ({ kbnClient, uiSettings }) => { - await uiSettings.unset('doc_table:legacy', 'defaultIndex'); + await uiSettings.unset('defaultIndex'); await kbnClient.savedObjects.cleanStandardList(); }); diff --git a/x-pack/plugins/discover_enhanced/ui_tests/tests/value_suggestions_use_time_range_disabled.spec.ts b/x-pack/plugins/discover_enhanced/ui_tests/tests/value_suggestions_use_time_range_disabled.spec.ts index c5f0ad9b43ef6..857709b091940 100644 --- a/x-pack/plugins/discover_enhanced/ui_tests/tests/value_suggestions_use_time_range_disabled.spec.ts +++ b/x-pack/plugins/discover_enhanced/ui_tests/tests/value_suggestions_use_time_range_disabled.spec.ts @@ -17,14 +17,13 @@ test.describe( await kbnClient.importExport.load(testData.KBN_ARCHIVES.DASHBOARD_DRILLDOWNS); await uiSettings.set({ defaultIndex: testData.DATA_VIEW_ID.LOGSTASH, // TODO: investigate why it is required for `node scripts/playwright_test.js` run - 'doc_table:legacy': false, 'timepicker:timeDefaults': `{ "from": "${testData.LOGSTASH_DEFAULT_START_TIME}", "to": "${testData.LOGSTASH_DEFAULT_END_TIME}"}`, 'autocomplete:useTimeRange': false, }); }); test.afterAll(async ({ uiSettings, kbnClient }) => { - await uiSettings.unset('doc_table:legacy', 'defaultIndex', 'timepicker:timeDefaults'); + await uiSettings.unset('defaultIndex', 'timepicker:timeDefaults'); await uiSettings.set({ 'autocomplete:useTimeRange': true }); await kbnClient.savedObjects.cleanStandardList(); }); diff --git a/x-pack/plugins/security_solution/docs/siem_migration/img/agent_graph.png b/x-pack/plugins/security_solution/docs/siem_migration/img/agent_graph.png index a4039ef4a7446..ecccb602e2016 100644 Binary files a/x-pack/plugins/security_solution/docs/siem_migration/img/agent_graph.png and b/x-pack/plugins/security_solution/docs/siem_migration/img/agent_graph.png differ diff --git a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/graph.ts b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/graph.ts index 4f2d2a74ff611..4ce5e2d87a3fe 100644 --- a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/graph.ts +++ b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/graph.ts @@ -48,5 +48,5 @@ const matchedPrebuiltRuleConditional = (state: MigrateRuleState) => { if (state.elastic_rule?.prebuilt_rule_id) { return END; } - return 'translation'; + return 'translationSubGraph'; }; diff --git a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/graph.ts b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/graph.ts index f986098e9deb0..32f41e54619be 100644 --- a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/graph.ts +++ b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/graph.ts @@ -6,11 +6,18 @@ */ import { END, START, StateGraph } from '@langchain/langgraph'; +import { isEmpty } from 'lodash/fp'; +import { SiemMigrationRuleTranslationResult } from '../../../../../../../../common/siem_migrations/constants'; +import { getFixQueryErrorsNode } from './nodes/fix_query_errors'; import { getProcessQueryNode } from './nodes/process_query'; import { getRetrieveIntegrationsNode } from './nodes/retrieve_integrations'; import { getTranslateRuleNode } from './nodes/translate_rule'; +import { getValidationNode } from './nodes/validation'; import { translateRuleState } from './state'; -import type { TranslateRuleGraphParams } from './types'; +import type { TranslateRuleGraphParams, TranslateRuleState } from './types'; + +// How many times we will try to self-heal when validation fails, to prevent infinite graph recursions +const MAX_VALIDATION_ITERATIONS = 3; export function getTranslateRuleGraph({ model, @@ -35,19 +42,37 @@ export function getTranslateRuleGraph({ model, integrationRetriever, }); + const validationNode = getValidationNode({ logger }); + const fixQueryErrorsNode = getFixQueryErrorsNode({ inferenceClient, connectorId, logger }); const translateRuleGraph = new StateGraph(translateRuleState) // Nodes .addNode('processQuery', processQueryNode) .addNode('retrieveIntegrations', retrieveIntegrationsNode) .addNode('translateRule', translateRuleNode) + .addNode('validation', validationNode) + .addNode('fixQueryErrors', fixQueryErrorsNode) // Edges .addEdge(START, 'processQuery') .addEdge('processQuery', 'retrieveIntegrations') .addEdge('retrieveIntegrations', 'translateRule') - .addEdge('translateRule', END); + .addEdge('translateRule', 'validation') + .addEdge('fixQueryErrors', 'validation') + .addConditionalEdges('validation', validationRouter); const graph = translateRuleGraph.compile(); graph.name = 'Translate Rule Graph'; return graph; } + +const validationRouter = (state: TranslateRuleState) => { + if ( + state.validation_errors.iterations <= MAX_VALIDATION_ITERATIONS && + state.translation_result === SiemMigrationRuleTranslationResult.FULL + ) { + if (!isEmpty(state.validation_errors?.esql_errors)) { + return 'fixQueryErrors'; + } + } + return END; +}; diff --git a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/fix_query_errors/fix_query_errors.ts b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/fix_query_errors/fix_query_errors.ts new file mode 100644 index 0000000000000..a21aceee70f95 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/fix_query_errors/fix_query_errors.ts @@ -0,0 +1,38 @@ +/* + * 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 type { Logger } from '@kbn/core/server'; +import type { InferenceClient } from '@kbn/inference-plugin/server'; +import { getEsqlKnowledgeBase } from '../../../../../util/esql_knowledge_base_caller'; +import type { GraphNode } from '../../types'; +import { RESOLVE_ESQL_ERRORS_TEMPLATE } from './prompts'; + +interface GetFixQueryErrorsNodeParams { + inferenceClient: InferenceClient; + connectorId: string; + logger: Logger; +} + +export const getFixQueryErrorsNode = ({ + inferenceClient, + connectorId, + logger, +}: GetFixQueryErrorsNodeParams): GraphNode => { + const esqlKnowledgeBaseCaller = getEsqlKnowledgeBase({ inferenceClient, connectorId, logger }); + return async (state) => { + const rule = state.elastic_rule; + const prompt = await RESOLVE_ESQL_ERRORS_TEMPLATE.format({ + esql_errors: state.validation_errors.esql_errors, + esql_query: rule.query, + }); + const response = await esqlKnowledgeBaseCaller(prompt); + + const esqlQuery = response.match(/```esql\n([\s\S]*?)\n```/)?.[1] ?? ''; + rule.query = esqlQuery; + return { elastic_rule: rule }; + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/fix_query_errors/index.ts b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/fix_query_errors/index.ts new file mode 100644 index 0000000000000..a805331675389 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/fix_query_errors/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +export { getFixQueryErrorsNode } from './fix_query_errors'; diff --git a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/fix_query_errors/prompts.ts b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/fix_query_errors/prompts.ts new file mode 100644 index 0000000000000..ca5fe67097d12 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/fix_query_errors/prompts.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ChatPromptTemplate } from '@langchain/core/prompts'; + +export const RESOLVE_ESQL_ERRORS_TEMPLATE = + ChatPromptTemplate.fromTemplate(`You are a helpful cybersecurity (SIEM) expert agent. Your task is to resolve the errors in the Elasticsearch Query Language (ES|QL) query provided by the user. + +Below is the relevant errors related to the ES|SQL query: + + + +{esql_errors} + + +{esql_query} + + + + +- You will be provided with the currentl ES|QL query and its related errors. +- Try to resolve the errors in the ES|QL query as best as you can to make it work. +- You must respond only with the modified query inside a \`\`\`esql code block, nothing else similar to the example response below. + + + +A: Please find the modified ES|QL query below: +\`\`\`esql +FROM logs-endpoint.events.process-* +| WHERE process.executable LIKE \"%chown root%\" +| STATS count = COUNT(*), firstTime = MIN(@timestamp), lastTime = MAX(@timestamp) BY process.executable, + process.command_line, + host.name +| EVAL firstTime = TO_DATETIME(firstTime), lastTime = TO_DATETIME(lastTime) +\`\`\` + + +`); diff --git a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/process_query/process_query.ts b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/process_query/process_query.ts index 97d4168f1283e..ae0e93ee0c4bb 100644 --- a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/process_query/process_query.ts +++ b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/process_query/process_query.ts @@ -29,11 +29,15 @@ export const getProcessQueryNode = ({ const replaceQueryResourcePrompt = REPLACE_QUERY_RESOURCE_PROMPT.pipe(model).pipe(replaceQueryParser); const resourceContext = getResourcesContext(resources); - query = await replaceQueryResourcePrompt.invoke({ + const response = await replaceQueryResourcePrompt.invoke({ query: state.original_rule.query, macros: resourceContext.macros, lookup_tables: resourceContext.lists, }); + const splQuery = response.match(/```spl\n([\s\S]*?)\n```/)?.[1] ?? ''; + if (splQuery) { + query = splQuery; + } } return { inline_query: query }; }; diff --git a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/process_query/prompts.ts b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/process_query/prompts.ts index f074da6b27d1a..b4c6b0e74aaa9 100644 --- a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/process_query/prompts.ts +++ b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/process_query/prompts.ts @@ -140,8 +140,14 @@ Divide the query up into separate section and go through each section one at a t A: Please find the modified SPL query below: -\`\`\`json -{{"match": "Linux User Account Creation"}} +\`\`\`spl +sourcetype="linux:audit" \`linux_auditd_normalized_proctitle_process\` +| rename host as dest +| where LIKE (process_exec, "%chown root%") +| stats count min(_time) as firstTime max(_time) as lastTime by process_exec proctitle normalized_proctitle_delimiter dest +| convert timeformat="%Y-%m-%dT%H:%M:%S" ctime(firstTime) +| convert timeformat="%Y-%m-%dT%H:%M:%S" ctime(lastTime) +| search * \`\`\` diff --git a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/translate_rule/cim_ecs_map.ts b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/translate_rule/cim_ecs_map.ts new file mode 100644 index 0000000000000..3bafaf2fc6518 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/translate_rule/cim_ecs_map.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const SIEM_RULE_MIGRATION_CIM_ECS_MAP = ` +datamodel,object,source_field,ecs_field,data_type +Application_State,All_Application_State,dest,service.node.name,string +Application_State,All_Application_State,process,process.title,string +Application_State,All_Application_State,user,user.name,string +Application_State,Ports,dest_port,destination.port,number +Application_State,Ports,transport,network.transport,string +Application_State,Ports,transport_dest_port,destination.port,string +Application_State,Services,service,service.name,string +Application_State,Services,service_id,service.id,string +Application_State,Services,status,service.state,string +Authentication,Authentication,action,event.action,string +Authentication,Authentication,app,process.name,string +Authentication,Authentication,dest,host.name,string +Authentication,Authentication,duration,event.duration,number +Authentication,Authentication,signature,event.code,string +Authentication,Authentication,signature_id,event.reason,string +Authentication,Authentication,src,source.address,string +Authentication,Authentication,src_nt_domain,source.domain,string +Authentication,Authentication,user,user.name,string +Certificates,All_Certificates,dest_port,destination.port,number +Certificates,All_Certificates,duration,event.duration,number +Certificates,All_Certificates,src,source.address,string +Certificates,All_Certificates,src_port,source.port,number +Certificates,All_Certificates,transport,network.protocol,string +Certificates,SSL,ssl_end_time,tls.server.not_after,time +Certificates,SSL,ssl_hash,tls.server.hash,string +Certificates,SSL,ssl_issuer_common_name,tls.server.issuer,string +Certificates,SSL,ssl_issuer_locality,x509.issuer.locality,string +Certificates,SSL,ssl_issuer_organization,x509.issuer.organization,string +Certificates,SSL,ssl_issuer_state,x509.issuer.state_or_province,string +Certificates,SSL,ssl_issuer_unit,x509.issuer.organizational_unit,string +Certificates,SSL,ssl_publickey_algorithm,x509.public_key_algorithm,string +Certificates,SSL,ssl_serial,x509.serial_number,string +Certificates,SSL,ssl_signature_algorithm,x509.signature_algorithm,string +Certificates,SSL,ssl_start_time,x509.not_before,time +Certificates,SSL,ssl_subject,x509.subject.distinguished_name,string +Certificates,SSL,ssl_subject_common_name,x509.subject.common_name,string +Certificates,SSL,ssl_subject_locality,x509.subject.locality,string +Certificates,SSL,ssl_subject_organization,x509.subject.organization,string +Certificates,SSL,ssl_subject_state,x509.subject.state_or_province,string +Certificates,SSL,ssl_subject_unit,x509.subject.organizational_unit,string +Certificates,SSL,ssl_version,tls.version,string +Change,All_Changes,action,event.action,string +Change,Account_Management,dest_nt_domain,destination.domain,string +Change,Account_Management,src_nt_domain,source.domain,string +Change,Account_Management,src_user,source.user,string +Intrusion_Detection,IDS_Attacks,action,event.action,string +Intrusion_Detection,IDS_Attacks,dest,destination.address,string +Intrusion_Detection,IDS_Attacks,dest_port,destination.port,number +Intrusion_Detection,IDS_Attacks,dvc,observer.hostname,string +Intrusion_Detection,IDS_Attacks,severity,event.severity,string +Intrusion_Detection,IDS_Attacks,src,source.ip,string +Intrusion_Detection,IDS_Attacks,user,source.user,string +JVM,OS,os,host.os.name,string +JVM,OS,os_architecture,host.architecture,string +JVM,OS,os_version,host.os.version,string +Malware,Malware_Attacks,action,event.action,string +Malware,Malware_Attacks,date,event.created,string +Malware,Malware_Attacks,dest,host.hostname,string +Malware,Malware_Attacks,file_hash,file.hash.*,string +Malware,Malware_Attacks,file_name,file.name,string +Malware,Malware_Attacks,file_path,file.path,string +Malware,Malware_Attacks,Sender,source.user.email,string +Malware,Malware_Attacks,src,source.ip,string +Malware,Malware_Attacks,user,related.user,string +Malware,Malware_Attacks,url,rule.reference,string +Network_Resolution,DNS,answer,dns.answers,string +Network_Resolution,DNS,dest,destination.address,string +Network_Resolution,DNS,dest_port,destination.port,number +Network_Resolution,DNS,duration,event.duration,number +Network_Resolution,DNS,message_type,dns.type,string +Network_Resolution,DNS,name,dns.question.name,string +Network_Resolution,DNS,query,dns.question.name,string +Network_Resolution,DNS,query_type,dns.op_code,string +Network_Resolution,DNS,record_type,dns.question.type,string +Network_Resolution,DNS,reply_code,dns.response_code,string +Network_Resolution,DNS,reply_code_id,dns.id,number +Network_Resolution,DNS,response_time,event.duration,number +Network_Resolution,DNS,src,source.address,string +Network_Resolution,DNS,src_port,source.port,number +Network_Resolution,DNS,transaction_id,dns.id,number +Network_Resolution,DNS,transport,network.transport,string +Network_Resolution,DNS,ttl,dns.answers.ttl,number +Network_Sessions,All_Sessions,action,event.action,string +Network_Sessions,All_Sessions,dest_ip,destination.ip,string +Network_Sessions,All_Sessions,dest_mac,destination.mac,string +Network_Sessions,All_Sessions,duration,event.duration,number +Network_Sessions,All_Sessions,src_dns,source.registered_domain,string +Network_Sessions,All_Sessions,src_ip,source.ip,string +Network_Sessions,All_Sessions,src_mac,source.mac,string +Network_Sessions,All_Sessions,user,user.name,string +Network_Traffic,All_Traffic,action,event.action,string +Network_Traffic,All_Traffic,app,network.protocol,string +Network_Traffic,All_Traffic,bytes,network.bytes,number +Network_Traffic,All_Traffic,dest,destination.ip,string +Network_Traffic,All_Traffic,dest_ip,destination.ip,string +Network_Traffic,All_Traffic,dest_mac,destination.mac,string +Network_Traffic,All_Traffic,dest_port,destination.port,number +Network_Traffic,All_Traffic,dest_translated_ip,destination.nat.ip,string +Network_Traffic,All_Traffic,dest_translated_port,destination.nat.port,number +Network_Traffic,All_Traffic,direction,network.direction,string +Network_Traffic,All_Traffic,duration,event.duration,number +Network_Traffic,All_Traffic,dvc,observer.name,string +Network_Traffic,All_Traffic,dvc_ip,observer.ip,string +Network_Traffic,All_Traffic,dvc_mac,observer.mac,string +Network_Traffic,All_Traffic,dvc_zone,observer.egress.zone,string +Network_Traffic,All_Traffic,packets,network.packets,number +Network_Traffic,All_Traffic,packets_in,source.packets,number +Network_Traffic,All_Traffic,packets_out,destination.packets,number +Network_Traffic,All_Traffic,protocol,network.protocol,string +Network_Traffic,All_Traffic,rule,rule.name,string +Network_Traffic,All_Traffic,src,source.address,string +Network_Traffic,All_Traffic,src_ip,source.ip,string +Network_Traffic,All_Traffic,src_mac,source.mac,string +Network_Traffic,All_Traffic,src_port,source.port,number +Network_Traffic,All_Traffic,src_translated_ip,source.nat.ip,string +Network_Traffic,All_Traffic,src_translated_port,source.nat.port,number +Network_Traffic,All_Traffic,transport,network.transport,string +Network_Traffic,All_Traffic,vlan,vlan.name,string +Vulnerabilities,Vulnerabilities,category,vulnerability.category,string +Vulnerabilities,Vulnerabilities,cve,vulnerability.id,string +Vulnerabilities,Vulnerabilities,cvss,vulnerability.score.base,number +Vulnerabilities,Vulnerabilities,dest,host.name,string +Vulnerabilities,Vulnerabilities,dvc,vulnerability.scanner.vendor,string +Vulnerabilities,Vulnerabilities,severity,vulnerability.severity,string +Vulnerabilities,Vulnerabilities,url,vulnerability.reference,string +Vulnerabilities,Vulnerabilities,user,related.user,string +Vulnerabilities,Vulnerabilities,vendor_product,vulnerability.scanner.vendor,string +Endpoint,Ports,creation_time,@timestamp,timestamp +Endpoint,Ports,dest_port,destination.port,number +Endpoint,Ports,process_id,process.pid,string +Endpoint,Ports,transport,network.transport,string +Endpoint,Ports,transport_dest_port,destination.port,string +Endpoint,Processes,action,event.action,string +Endpoint,Processes,os,os.full,string +Endpoint,Processes,parent_process_exec,process.parent.name,string +Endpoint,Processes,parent_process_id,process.ppid,number +Endpoint,Processes,parent_process_guid,process.parent.entity_id,string +Endpoint,Processes,parent_process_path,process.parent.executable,string +Endpoint,Processes,process_current_directory,process.parent.working_directory, +Endpoint,Processes,process_exec,process.name,string +Endpoint,Processes,process_hash,process.hash.*,string +Endpoint,Processes,process_guid,process.entity_id,string +Endpoint,Processes,process_id,process.pid,number +Endpoint,Processes,process_path,process.executable,string +Endpoint,Processes,user_id,related.user,string +Endpoint,Services,description,service.name,string +Endpoint,Services,process_id,service.id,string +Endpoint,Services,service_dll,dll.name,string +Endpoint,Services,service_dll_path,dll.path,string +Endpoint,Services,service_dll_hash,dll.hash.*,string +Endpoint,Services,service_dll_signature_exists,dll.code_signature.exists,boolean +Endpoint,Services,service_dll_signature_verified,dll.code_signature.valid,boolean +Endpoint,Services,service_exec,service.name,string +Endpoint,Services,service_hash,hash.*,string +Endpoint,Filesystem,file_access_time,file.accessed,timestamp +Endpoint,Filesystem,file_create_time,file.created,timestamp +Endpoint,Filesystem,file_modify_time,file.mtime,timestamp +Endpoint,Filesystem,process_id,process.pid,string +Endpoint,Registry,process_id,process.id,string +Web,Web,action,event.action,string +Web,Web,app,observer.product,string +Web,Web,bytes_in,http.request.bytes,number +Web,Web,bytes_out,http.response.bytes,number +Web,Web,dest,destination.ip,string +Web,Web,duration,event.duration,number +Web,Web,http_method,http.request.method,string +Web,Web,http_referrer,http.request.referrer,string +Web,Web,http_user_agent,user_agent.name,string +Web,Web,status,http.response.status_code,string +Web,Web,url,url.full,string +Web,Web,user,url.username,string +Web,Web,vendor_product,observer.product,string`; diff --git a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/translate_rule/prompts.ts b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/translate_rule/prompts.ts index 3e77353bba8b1..9749dfd96efba 100644 --- a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/translate_rule/prompts.ts +++ b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/translate_rule/prompts.ts @@ -5,14 +5,18 @@ * 2.0. */ -import type { TranslateRuleState } from '../../types'; +import { ChatPromptTemplate } from '@langchain/core/prompts'; -export const getEsqlTranslationPrompt = ( - state: TranslateRuleState, - indexPatterns: string -): string => { - return `You are a helpful cybersecurity (SIEM) expert agent. Your task is to migrate "detection rules" from Splunk to Elastic Security. +export const ESQL_TRANSLATION_PROMPT = + ChatPromptTemplate.fromTemplate(`You are a helpful cybersecurity (SIEM) expert agent. Your task is to migrate "detection rules" from Splunk to Elastic Security. Your goal is to translate the SPL query into an equivalent Elastic Security Query Language (ES|QL) query. +Below is the relevant context used when deciding which Elastic Common Schema field to use when translating from Splunk CIM fields: + + + +{field_mapping} + + ## Splunk rule Information provided: - Below you will find Splunk rule information: the title (<>), the description (<<DESCRIPTION>>), and the SPL (Search Processing Language) query (<<SPL_QUERY>>). @@ -22,7 +26,7 @@ Your goal is to translate the SPL query into an equivalent Elastic Security Quer ## Guidelines: - Analyze the SPL query and identify the key components. - Translate the SPL query into an equivalent ES|QL query using ECS (Elastic Common Schema) field names. -- Always start the generated ES|QL query by filtering FROM using these index patterns in the translated query: ${indexPatterns}. +- Always start the generated ES|QL query by filtering FROM using these index patterns in the translated query: {indexPatterns}. - If, in the SPL query, you find a lookup list or macro call, mention it in the summary and add a placeholder in the query with the format [macro:<macro_name>(argumentCount)] or [lookup:<lookup_name>] including the [] keys, - Examples: - \`get_duration(firstDate,secondDate)\` -> [macro:get_duration(2)] @@ -35,15 +39,14 @@ Your goal is to translate the SPL query into an equivalent Elastic Security Quer Find the Splunk rule information below: <<TITLE>> -${state.original_rule.title} +{title} <> <> -${state.original_rule.description} +{description} <> <> -${state.inline_query} +{inline_query} <> -`; -}; +`); diff --git a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/translate_rule/translate_rule.ts b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/translate_rule/translate_rule.ts index a39e7c10146c0..6ba5edee11b22 100644 --- a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/translate_rule/translate_rule.ts +++ b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/translate_rule/translate_rule.ts @@ -9,10 +9,11 @@ import type { Logger } from '@kbn/core/server'; import type { InferenceClient } from '@kbn/inference-plugin/server'; import { SiemMigrationRuleTranslationResult } from '../../../../../../../../../../common/siem_migrations/constants'; import type { ChatModel } from '../../../../../util/actions_client_chat'; +import { getEsqlKnowledgeBase } from '../../../../../util/esql_knowledge_base_caller'; import type { RuleResourceRetriever } from '../../../../../util/rule_resource_retriever'; import type { GraphNode } from '../../types'; -import { getEsqlKnowledgeBase } from './esql_knowledge_base_caller'; -import { getEsqlTranslationPrompt } from './prompts'; +import { SIEM_RULE_MIGRATION_CIM_ECS_MAP } from './cim_ecs_map'; +import { ESQL_TRANSLATION_PROMPT } from './prompts'; interface GetTranslateRuleNodeParams { model: ChatModel; @@ -29,12 +30,20 @@ export const getTranslateRuleNode = ({ }: GetTranslateRuleNodeParams): GraphNode => { const esqlKnowledgeBaseCaller = getEsqlKnowledgeBase({ inferenceClient, connectorId, logger }); return async (state) => { - const indexPatterns = state.integrations.flatMap((integration) => - integration.data_streams.map((dataStream) => dataStream.index_pattern) - ); + const indexPatterns = state.integrations + .flatMap((integration) => + integration.data_streams.map((dataStream) => dataStream.index_pattern) + ) + .join(','); const integrationIds = state.integrations.map((integration) => integration.id); - const prompt = getEsqlTranslationPrompt(state, indexPatterns.join(',')); + const prompt = await ESQL_TRANSLATION_PROMPT.format({ + title: state.original_rule.title, + description: state.original_rule.description, + field_mapping: SIEM_RULE_MIGRATION_CIM_ECS_MAP, + inline_query: state.inline_query, + indexPatterns, + }); const response = await esqlKnowledgeBaseCaller(prompt); const esqlQuery = response.match(/```esql\n([\s\S]*?)\n```/)?.[1] ?? ''; diff --git a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/validation/esql_query.ts b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/validation/esql_query.ts new file mode 100644 index 0000000000000..a8c1d6acff408 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/validation/esql_query.ts @@ -0,0 +1,62 @@ +/* + * 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 type { ESQLAstQueryExpression, ESQLCommandOption, EditorError } from '@kbn/esql-ast'; +import { parse } from '@kbn/esql-ast'; +import { isColumnItem, isOptionItem } from '@kbn/esql-validation-autocomplete'; +import { isAggregatingQuery } from '@kbn/securitysolution-utils'; + +interface ParseEsqlQueryResult { + errors: EditorError[]; + isEsqlQueryAggregating: boolean; + hasMetadataOperator: boolean; +} + +function computeHasMetadataOperator(astExpression: ESQLAstQueryExpression): boolean { + // Check whether the `from` command has `metadata` operator + const metadataOption = getMetadataOption(astExpression); + if (!metadataOption) { + return false; + } + + // Check whether the `metadata` operator has `_id` argument + const idColumnItem = metadataOption.args.find( + (fromArg) => isColumnItem(fromArg) && fromArg.name === '_id' + ); + if (!idColumnItem) { + return false; + } + + return true; +} + +function getMetadataOption(astExpression: ESQLAstQueryExpression): ESQLCommandOption | undefined { + const fromCommand = astExpression.commands.find((x) => x.name === 'from'); + + if (!fromCommand?.args) { + return undefined; + } + + // Check whether the `from` command has `metadata` operator + for (const fromArg of fromCommand.args) { + if (isOptionItem(fromArg) && fromArg.name === 'metadata') { + return fromArg; + } + } + + return undefined; +} + +export const parseEsqlQuery = (query: string): ParseEsqlQueryResult => { + const { root, errors } = parse(query); + const isEsqlQueryAggregating = isAggregatingQuery(root); + return { + errors, + isEsqlQueryAggregating, + hasMetadataOperator: computeHasMetadataOperator(root), + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/validation/index.ts b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/validation/index.ts new file mode 100644 index 0000000000000..a8c0f55191e42 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/validation/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +export { getValidationNode } from './validation'; diff --git a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/validation/validation.ts b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/validation/validation.ts new file mode 100644 index 0000000000000..272aedfe4793d --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/validation/validation.ts @@ -0,0 +1,43 @@ +/* + * 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 type { Logger } from '@kbn/core/server'; +import { isEmpty } from 'lodash/fp'; +import type { GraphNode } from '../../types'; +import { parseEsqlQuery } from './esql_query'; + +interface GetValidationNodeParams { + logger: Logger; +} + +/** + * This node runs all validation steps, and will redirect to the END of the graph if no errors are found. + * Any new validation steps should be added here. + */ +export const getValidationNode = ({ logger }: GetValidationNodeParams): GraphNode => { + return async (state) => { + const query = state.elastic_rule.query; + + // We want to prevent infinite loops, so we increment the iterations counter for each validation run. + const currentIteration = ++state.validation_errors.iterations; + let esqlErrors: string = ''; + if (!isEmpty(query)) { + const { errors, isEsqlQueryAggregating, hasMetadataOperator } = parseEsqlQuery(query); + if (!isEmpty(errors)) { + esqlErrors = JSON.stringify(errors); + } else if (!isEsqlQueryAggregating && !hasMetadataOperator) { + esqlErrors = + 'Queries that don’t use the STATS...BY function (non-aggregating queries) must include the "metadata _id, _version, _index" operator after the source command. For example: FROM logs* metadata _id, _version, _index.'; + } + } + if (esqlErrors) { + logger.debug(`ESQL query validation failed: ${esqlErrors}`); + } + + return { validation_errors: { iterations: currentIteration, esql_errors: esqlErrors } }; + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/state.ts b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/state.ts index 8c8e9780aedf8..391d7a54f9ea8 100644 --- a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/state.ts +++ b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/state.ts @@ -14,6 +14,7 @@ import type { RuleMigration, } from '../../../../../../../../common/siem_migrations/model/rule_migration.gen'; import type { Integration } from '../../../../types'; +import type { TranslateRuleValidationErrors } from './types'; export const translateRuleState = Annotation.Root({ messages: Annotation({ @@ -33,6 +34,10 @@ export const translateRuleState = Annotation.Root({ reducer: (state, action) => ({ ...state, ...action }), default: () => ({} as ElasticRule), }), + validation_errors: Annotation({ + reducer: (current, value) => value ?? current, + default: () => ({ iterations: 0 } as TranslateRuleValidationErrors), + }), translation_result: Annotation({ reducer: (current, value) => value ?? current, default: () => SiemMigrationRuleTranslationResult.UNTRANSLATABLE, diff --git a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/types.ts b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/types.ts index 42bf8e14c5924..44a5750812be0 100644 --- a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/types.ts +++ b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/types.ts @@ -23,3 +23,8 @@ export interface TranslateRuleGraphParams { integrationRetriever: IntegrationRetriever; logger: Logger; } + +export interface TranslateRuleValidationErrors { + iterations: number; + esql_errors?: string; +} diff --git a/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/translate_rule/esql_knowledge_base_caller.ts b/x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/util/esql_knowledge_base_caller.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/agent/sub_graphs/translate_rule/nodes/translate_rule/esql_knowledge_base_caller.ts rename to x-pack/plugins/security_solution/server/lib/siem_migrations/rules/task/util/esql_knowledge_base_caller.ts diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index de10ed0c3dbb7..c7a1ebde48eda 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -2728,11 +2728,7 @@ "discover.advancedSettings.context.tieBreakerFieldsTitle": "Champs de départage", "discover.advancedSettings.defaultColumnsText": "Les colonnes affichées par défaut dans l'application Discover. Si elles sont vides, un résumé du document s’affiche.", "discover.advancedSettings.defaultColumnsTitle": "Colonnes par défaut", - "discover.advancedSettings.disableDocumentExplorer": "Explorateur de documents ou vue classique", - "discover.advancedSettings.disableDocumentExplorerDescription": "Désactivez cette option pour utiliser le nouveau {documentExplorerDocs} au lieu de la vue classique. l'explorateur de documents offre un meilleur tri des données, des colonnes redimensionnables et une vue en plein écran.", - "discover.advancedSettings.discover.disableDocumentExplorerDeprecation": "Ce paramètre est déclassé et sera supprimé dans la version 9.0 de Kibana.", "discover.advancedSettings.discover.fieldStatisticsLinkText": "Vue des statistiques de champ", - "discover.advancedSettings.discover.maxCellHeightDeprecation": "Ce paramètre est déclassé et sera supprimé dans la version 9.0 de Kibana.", "discover.advancedSettings.discover.modifyColumnsOnSwitchText": "Supprimez les colonnes qui ne sont pas disponibles dans la nouvelle vue de données.", "discover.advancedSettings.discover.modifyColumnsOnSwitchTitle": "Modifier les colonnes en cas de changement des vues de données", "discover.advancedSettings.discover.multiFieldsLinkText": "champs multiples", @@ -2745,13 +2741,10 @@ "discover.advancedSettings.discover.showMultifieldsDescription": "Détermine si les {multiFields} doivent s'afficher dans la fenêtre de document étendue. Dans la plupart des cas, les champs multiples sont les mêmes que les champs d'origine. Cette option est uniquement disponible lorsque le paramètre `searchFieldsFromSource` est désactivé.", "discover.advancedSettings.docTableHideTimeColumnText": "Permet de masquer la colonne ''Time'' dans Discover et dans toutes les recherches enregistrées des tableaux de bord.", "discover.advancedSettings.docTableHideTimeColumnTitle": "Masquer la colonne ''Time''", - "discover.advancedSettings.documentExplorerLinkText": "Explorateur de documents", "discover.advancedSettings.fieldsPopularLimitText": "Les N champs les plus populaires à afficher", "discover.advancedSettings.fieldsPopularLimitTitle": "Limite de champs populaires", "discover.advancedSettings.maxDocFieldsDisplayedText": "Le nombre maximal de champs renvoyés dans le résumé du document", "discover.advancedSettings.maxDocFieldsDisplayedTitle": "Nombre maximal de champs de document affichés", - "discover.advancedSettings.params.maxCellHeightText": "La hauteur maximale qu'une cellule de tableau peut atteindre. Définissez ce paramètre sur 0 pour désactiver la troncation.", - "discover.advancedSettings.params.maxCellHeightTitle": "Hauteur de cellule maximale dans le tableau classique", "discover.advancedSettings.params.rowHeightText": "Nombre de sous-lignes à autoriser dans une ligne. La valeur -1 ajuste automatiquement la hauteur de ligne selon le contenu. La valeur 0 affiche le contenu en une seule ligne.", "discover.advancedSettings.params.rowHeightTitle": "Hauteur de ligne dans l'explorateur de documents", "discover.advancedSettings.sampleRowsPerPageText": "Limite le nombre de lignes par page dans le tableau de documents.", @@ -2767,7 +2760,6 @@ "discover.alerts.createSearchThreshold": "Créer une règle de seuil de recherche", "discover.alerts.manageRulesAndConnectors": "Gérer les règles et les connecteurs", "discover.alerts.missedTimeFieldToolTip": "La vue de données ne possède pas de champ temporel.", - "discover.backToTopLinkText": "Revenir en haut de la page.", "discover.badge.readOnly.text": "Lecture seule", "discover.badge.readOnly.tooltip": "Impossible d’enregistrer les recherches", "discover.context.breadcrumb": "Documents relatifs", @@ -2776,7 +2768,6 @@ "discover.context.failedToLoadAnchorDocumentErrorDescription": "Le document ancré n’a pas pu être chargé.", "discover.context.invalidTieBreakerFiledSetting": "Paramètre de champ de départage non valide", "discover.context.loadButtonLabel": "Charger", - "discover.context.loadingDescription": "Chargement...", "discover.context.newerDocumentsAriaLabel": "Nombre de documents plus récents", "discover.context.newerDocumentsDescription": "documents plus récents", "discover.context.newerDocumentsWarning": "Seuls {docCount} documents plus récents que le document ancré ont été trouvés.", @@ -2813,39 +2804,8 @@ "discover.doc.pageTitle": "Document unique - #{id}", "discover.doc.somethingWentWrongDescription": "Index {indexName} manquant.", "discover.doc.somethingWentWrongDescriptionAddon": "Veuillez vérifier que cet index existe.", - "discover.docExplorerCallout.closeButtonAriaLabel": "Fermer", - "discover.docExplorerCallout.documentExplorer": "Explorateur de documents", - "discover.docExplorerCallout.headerMessage": "Une meilleure façon d'explorer", - "discover.docExplorerCallout.learnMore": "En savoir plus", - "discover.docExplorerCallout.tryDocumentExplorer": "Testez l'explorateur de documents", - "discover.docTable.documentsNavigation": "Navigation dans les documents", - "discover.docTable.limitedSearchResultLabel": "Limité à {resultCount} résultats. Veuillez affiner votre recherche.", - "discover.docTable.noResultsTitle": "Résultat introuvable", - "discover.docTable.rows": "lignes", - "discover.docTable.rowsPerPage": "Lignes par page : {pageSize}", - "discover.docTable.tableHeader.documentHeader": "Document", - "discover.docTable.tableHeader.moveColumnLeftButtonAriaLabel": "Déplacer la colonne {columnName} vers la gauche", - "discover.docTable.tableHeader.moveColumnLeftButtonTooltip": "Déplacer la colonne vers la gauche", - "discover.docTable.tableHeader.moveColumnRightButtonAriaLabel": "Déplacer la colonne {columnName} vers la droite", - "discover.docTable.tableHeader.moveColumnRightButtonTooltip": "Déplacer la colonne vers la droite", - "discover.docTable.tableHeader.removeColumnButtonAriaLabel": "Supprimer la colonne {columnName}", - "discover.docTable.tableHeader.removeColumnButtonTooltip": "Supprimer la colonne", - "discover.docTable.tableHeader.sortByColumnAscendingAriaLabel": "Trier la colonne {columnName} par ordre croissant", - "discover.docTable.tableHeader.sortByColumnDescendingAriaLabel": "Trier la colonne {columnName} par ordre décroissant", - "discover.docTable.tableHeader.sortByColumnUnsortedAriaLabel": "Arrêter de trier la colonne {columnName}", - "discover.docTable.tableHeader.timeFieldIconTooltip": "Ce champ représente l'heure à laquelle les événements se sont produits.", - "discover.docTable.tableHeader.timeFieldIconTooltipAriaLabel": "{timeFieldName} : ce champ représente l'heure à laquelle les événements se sont produits.", - "discover.docTable.tableRow.detailHeading": "Document développé", - "discover.docTable.tableRow.filterForValueButtonAriaLabel": "Filtrer sur la valeur", - "discover.docTable.tableRow.filterForValueButtonTooltip": "Filtrer sur la valeur", - "discover.docTable.tableRow.filterOutValueButtonAriaLabel": "Exclure la valeur", - "discover.docTable.tableRow.filterOutValueButtonTooltip": "Exclure la valeur", - "discover.docTable.tableRow.toggleRowDetailsButtonAriaLabel": "Afficher/Masquer les détails de la ligne", - "discover.docTable.tableRow.viewSingleDocumentLinkText": "Afficher un seul document", - "discover.docTable.tableRow.viewSurroundingDocumentsLinkText": "Afficher les documents alentour", "discover.documentsAriaLabel": "Documents", "discover.docViews.logsOverview.title": "Aperçu du log", - "discover.docViews.table.scoreSortWarningTooltip": "Filtrez sur _score pour pouvoir récupérer les valeurs correspondantes.", "discover.dropZoneTableLabel": "Abandonner la zone pour ajouter un champ en tant que colonne dans la table", "discover.embeddable.inspectorRequestDataTitle": "Données", "discover.embeddable.inspectorRequestDescription": "Cette requête interroge Elasticsearch afin de récupérer les données pour la recherche.", @@ -2867,7 +2827,6 @@ "discover.globalSearch.esqlSearchTitle": "Créer des recherches ES|QL", "discover.goToDiscoverButtonText": "Aller à Discover", "discover.grid.tableRow.actionsLabel": "Actions", - "discover.grid.tableRow.esqlDetailHeading": "Résultat étendu", "discover.grid.tableRow.mobileFlyoutActionsButton": "Actions", "discover.grid.tableRow.moreFlyoutActionsButton": "Plus d'actions", "discover.grid.tableRow.viewSingleDocumentLinkLabel": "Afficher un seul document", @@ -2878,7 +2837,6 @@ "discover.hitsCounter.hitsPluralTitle": "{formattedHits} {hits, plural, one {résultat} other {résultats}}", "discover.hitsCounter.partialHits": "≥{formattedHits}", "discover.hitsCounter.partialHitsPluralTitle": "≥{formattedHits} {hits, plural, one {résultat} other {résultats}}", - "discover.howToSeeOtherMatchingDocumentsDescription": "Voici les {sampleSize} premiers documents correspondant à votre recherche. Veuillez affiner celle-ci pour en voir plus.", "discover.inspectorEsqlRequestDescription": "Cette requête interroge Elasticsearch afin de récupérer les résultats pour le tableau.", "discover.inspectorEsqlRequestTitle": "Tableau", "discover.inspectorRequestDataTitleDocuments": "Documents", @@ -2887,7 +2845,6 @@ "discover.invalidFiltersWarnToast.description": "Les références d'ID de la vue de données dans certains filtres appliqués diffèrent de la vue de données actuelle.", "discover.invalidFiltersWarnToast.title": "Références d'index différentes", "discover.loadingDocuments": "Chargement des documents", - "discover.loadingResults": "Chargement des résultats", "discover.localMenu.alertsDescription": "Alertes", "discover.localMenu.esqlTooltipLabel": "ES|QL est le nouveau langage de requête canalisé puissant d'Elastic.", "discover.localMenu.fallbackReportTitle": "Recherche Discover sans titre", @@ -8308,24 +8265,16 @@ "unifiedDocViewer.docView.table.searchPlaceHolder": "Rechercher des noms ou valeurs de champs", "unifiedDocViewer.docViews.json.jsonTitle": "JSON", "unifiedDocViewer.docViews.table.esqlMultivalueFilteringDisabled": "Le filtrage multivalué n'est pas pris en charge dans ES|QL", - "unifiedDocViewer.docViews.table.filterForFieldPresentButtonAriaLabel": "Filtrer sur le champ", "unifiedDocViewer.docViews.table.filterForFieldPresentButtonTooltip": "Filtrer sur le champ", - "unifiedDocViewer.docViews.table.filterForValueButtonAriaLabel": "Filtrer sur la valeur", "unifiedDocViewer.docViews.table.filterForValueButtonTooltip": "Filtrer sur la valeur", - "unifiedDocViewer.docViews.table.filterOutValueButtonAriaLabel": "Exclure la valeur", "unifiedDocViewer.docViews.table.filterOutValueButtonTooltip": "Exclure la valeur", "unifiedDocViewer.docViews.table.ignored.multiValueLabel": "Contient des valeurs ignorées", "unifiedDocViewer.docViews.table.ignored.singleValueLabel": "Valeur ignorée", "unifiedDocViewer.docViews.table.ignoredValuesCanNotBeSearchedWarningMessage": "Les valeurs ignorées ne peuvent pas être recherchées", "unifiedDocViewer.docViews.table.pinFieldLabel": "Épingler le champ", "unifiedDocViewer.docViews.table.tableTitle": "Tableau", - "unifiedDocViewer.docViews.table.toggleColumnInTableButtonAriaLabel": "Afficher/Masquer la colonne dans le tableau", - "unifiedDocViewer.docViews.table.toggleColumnInTableButtonTooltip": "Afficher/Masquer la colonne dans le tableau", "unifiedDocViewer.docViews.table.toggleColumnTableButtonTooltip": "Afficher/Masquer la colonne dans le tableau", - "unifiedDocViewer.docViews.table.unableToFilterForPresenceOfMetaFieldsTooltip": "Impossible de filtrer sur les champs méta", - "unifiedDocViewer.docViews.table.unableToFilterForPresenceOfScriptedFieldsTooltip": "Impossible de filtrer sur les champs scriptés", "unifiedDocViewer.docViews.table.unableToFilterForPresenceOfScriptedFieldsWarningMessage": "Impossible de filtrer sur les champs scriptés", - "unifiedDocViewer.docViews.table.unindexedFieldsCanNotBeSearchedTooltip": "Les champs non indexés ou les valeurs ignorées ne peuvent pas être recherchés", "unifiedDocViewer.docViews.table.unindexedFieldsCanNotBeSearchedWarningMessage": "Il est impossible d’effectuer une recherche sur des champs non indexés", "unifiedDocViewer.docViews.table.unpinFieldLabel": "Désépingler le champ", "unifiedDocViewer.docViews.table.viewLessButton": "Afficher moins", @@ -8335,7 +8284,6 @@ "unifiedDocViewer.fieldActions.filterForValue": "Filtrer sur la valeur", "unifiedDocViewer.fieldActions.filterOutValue": "Exclure la valeur", "unifiedDocViewer.fieldActions.toggleColumn": "Afficher/Masquer la colonne dans le tableau", - "unifiedDocViewer.fieldChooser.discoverField.actions": "Actions", "unifiedDocViewer.fieldChooser.discoverField.multiField": "champ multiple", "unifiedDocViewer.fieldChooser.discoverField.multiFieldTooltipContent": "Les champs multiples peuvent avoir plusieurs valeurs.", "unifiedDocViewer.fieldChooser.discoverField.name": "Champ", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 400a114b8bb3c..6d0ceeed9b77c 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -2727,11 +2727,7 @@ "discover.advancedSettings.context.tieBreakerFieldsTitle": "タイブレーカーフィールド", "discover.advancedSettings.defaultColumnsText": "デフォルトでDiscoverアプリに表示される列。空の場合、ドキュメントの概要が表示されます。", "discover.advancedSettings.defaultColumnsTitle": "デフォルトの列", - "discover.advancedSettings.disableDocumentExplorer": "ドキュメントエクスプローラーまたはクラシックビュー", - "discover.advancedSettings.disableDocumentExplorerDescription": "クラシックビューではなく、新しい{documentExplorerDocs}を使用するには、このオプションをオフにします。ドキュメントエクスプローラーでは、データの並べ替え、列のサイズ変更、全画面表示といった優れた機能を使用できます。", - "discover.advancedSettings.discover.disableDocumentExplorerDeprecation": "この設定はサポートが終了し、Kibana 9.0では削除されます。", "discover.advancedSettings.discover.fieldStatisticsLinkText": "フィールド統計情報ビュー", - "discover.advancedSettings.discover.maxCellHeightDeprecation": "この設定はサポートが終了し、Kibana 9.0では削除されます。", "discover.advancedSettings.discover.modifyColumnsOnSwitchText": "新しいデータビューで使用できない列を削除します。", "discover.advancedSettings.discover.modifyColumnsOnSwitchTitle": "データビューを変更するときに列を修正", "discover.advancedSettings.discover.multiFieldsLinkText": "マルチフィールド", @@ -2744,13 +2740,10 @@ "discover.advancedSettings.discover.showMultifieldsDescription": "拡張ドキュメントビューに{multiFields}が表示されるかどうかを制御します。ほとんどの場合、マルチフィールドは元のフィールドと同じです。「searchFieldsFromSource」がオフのときにのみこのオプションを使用できます。", "discover.advancedSettings.docTableHideTimeColumnText": "Discover と、ダッシュボードのすべての保存された検索で、「時刻」列を非表示にします。", "discover.advancedSettings.docTableHideTimeColumnTitle": "「時刻」列を非表示", - "discover.advancedSettings.documentExplorerLinkText": "ドキュメントエクスプローラー", "discover.advancedSettings.fieldsPopularLimitText": "最も頻繁に使用されるフィールドのトップNを表示します", "discover.advancedSettings.fieldsPopularLimitTitle": "頻繁に使用されるフィールドの制限", "discover.advancedSettings.maxDocFieldsDisplayedText": "ドキュメント概要でレンダリングされるフィールドの最大数", "discover.advancedSettings.maxDocFieldsDisplayedTitle": "表示される最大ドキュメントフィールド数", - "discover.advancedSettings.params.maxCellHeightText": "表のセルが使用する高さの上限です。切り捨てを無効にするには0に設定します。", - "discover.advancedSettings.params.maxCellHeightTitle": "クラシック表の最大セル高さ", "discover.advancedSettings.params.rowHeightText": "行に追加できる線数。値-1は、コンテンツに合わせて、行の高さを自動的に調整します。値0はコンテンツが1行に表示されます。", "discover.advancedSettings.params.rowHeightTitle": "ドキュメントエクスプローラーの行高さ", "discover.advancedSettings.sampleRowsPerPageText": "ドキュメントテーブルのページごとの行数を制限します。", @@ -2766,7 +2759,6 @@ "discover.alerts.createSearchThreshold": "検索しきい値ルールの作成", "discover.alerts.manageRulesAndConnectors": "ルールとコネクターの管理", "discover.alerts.missedTimeFieldToolTip": "データビューには時間フィールドがありません。", - "discover.backToTopLinkText": "最上部へ戻る。", "discover.badge.readOnly.text": "読み取り専用", "discover.badge.readOnly.tooltip": "検索を保存できません", "discover.context.breadcrumb": "周りのドキュメント", @@ -2775,7 +2767,6 @@ "discover.context.failedToLoadAnchorDocumentErrorDescription": "アンカードキュメントの読み込みに失敗しました。", "discover.context.invalidTieBreakerFiledSetting": "無効なタイブレーカーフィールド設定", "discover.context.loadButtonLabel": "読み込み", - "discover.context.loadingDescription": "読み込み中...", "discover.context.newerDocumentsAriaLabel": "新しいドキュメントの数", "discover.context.newerDocumentsDescription": "新しいドキュメント", "discover.context.newerDocumentsWarning": "アンカーよりも新しいドキュメントは{docCount}件しか見つかりませんでした。", @@ -2812,38 +2803,8 @@ "discover.doc.pageTitle": "1つのドキュメント - #{id}", "discover.doc.somethingWentWrongDescription": "{indexName}が見つかりません。", "discover.doc.somethingWentWrongDescriptionAddon": "インデックスが存在することを確認してください。", - "discover.docExplorerCallout.closeButtonAriaLabel": "閉じる", - "discover.docExplorerCallout.documentExplorer": "ドキュメントエクスプローラー", - "discover.docExplorerCallout.headerMessage": "より効率的な探索方法", - "discover.docExplorerCallout.learnMore": "詳細", - "discover.docExplorerCallout.tryDocumentExplorer": "ドキュメントエクスプローラーを試す", - "discover.docTable.documentsNavigation": "ドキュメントナビゲーション", - "discover.docTable.limitedSearchResultLabel": "{resultCount}件の結果のみが表示されます。検索結果を絞り込みます。", - "discover.docTable.noResultsTitle": "結果が見つかりませんでした", - "discover.docTable.rows": "行", - "discover.docTable.tableHeader.documentHeader": "ドキュメント", - "discover.docTable.tableHeader.moveColumnLeftButtonAriaLabel": "{columnName}列を左に移動", - "discover.docTable.tableHeader.moveColumnLeftButtonTooltip": "列を左に移動", - "discover.docTable.tableHeader.moveColumnRightButtonAriaLabel": "{columnName}列を右に移動", - "discover.docTable.tableHeader.moveColumnRightButtonTooltip": "列を右に移動", - "discover.docTable.tableHeader.removeColumnButtonAriaLabel": "{columnName}列を削除", - "discover.docTable.tableHeader.removeColumnButtonTooltip": "列の削除", - "discover.docTable.tableHeader.sortByColumnAscendingAriaLabel": "{columnName}を昇順に並べ替える", - "discover.docTable.tableHeader.sortByColumnDescendingAriaLabel": "{columnName}を降順に並べ替える", - "discover.docTable.tableHeader.sortByColumnUnsortedAriaLabel": "{columnName}で並べ替えを止める", - "discover.docTable.tableHeader.timeFieldIconTooltip": "このフィールドはイベントの発生時刻を表します。", - "discover.docTable.tableHeader.timeFieldIconTooltipAriaLabel": "{timeFieldName} - このフィールドはイベントの発生時刻を表します。", - "discover.docTable.tableRow.detailHeading": "拡張ドキュメント", - "discover.docTable.tableRow.filterForValueButtonAriaLabel": "値でフィルター", - "discover.docTable.tableRow.filterForValueButtonTooltip": "値でフィルター", - "discover.docTable.tableRow.filterOutValueButtonAriaLabel": "値を除外", - "discover.docTable.tableRow.filterOutValueButtonTooltip": "値を除外", - "discover.docTable.tableRow.toggleRowDetailsButtonAriaLabel": "行の詳細を切り替える", - "discover.docTable.tableRow.viewSingleDocumentLinkText": "単一のドキュメントを表示", - "discover.docTable.tableRow.viewSurroundingDocumentsLinkText": "周りのドキュメントを表示", "discover.documentsAriaLabel": "ドキュメント", "discover.docViews.logsOverview.title": "ログ概要", - "discover.docViews.table.scoreSortWarningTooltip": "_scoreの値を取得するには、並べ替える必要があります。", "discover.dropZoneTableLabel": "フィールドを列として表に追加するには、ゾーンをドロップします", "discover.embeddable.inspectorRequestDataTitle": "データ", "discover.embeddable.inspectorRequestDescription": "このリクエストはElasticsearchにクエリーをかけ、検索データを取得します。", @@ -2865,7 +2826,6 @@ "discover.globalSearch.esqlSearchTitle": "ES|QLクエリーを作成", "discover.goToDiscoverButtonText": "Discoverに移動", "discover.grid.tableRow.actionsLabel": "アクション", - "discover.grid.tableRow.esqlDetailHeading": "展開された結果", "discover.grid.tableRow.mobileFlyoutActionsButton": "アクション", "discover.grid.tableRow.moreFlyoutActionsButton": "さらにアクションを表示", "discover.grid.tableRow.viewSingleDocumentLinkLabel": "単一のドキュメントを表示", @@ -2876,7 +2836,6 @@ "discover.hitsCounter.hitsPluralTitle": "{formattedHits} {hits, plural, other {結果}}", "discover.hitsCounter.partialHits": "≥{formattedHits}", "discover.hitsCounter.partialHitsPluralTitle": "≥{formattedHits} {hits, plural, other {結果}}", - "discover.howToSeeOtherMatchingDocumentsDescription": "これらは検索条件に一致した初めの {sampleSize} 件のドキュメントです。他の結果を表示するには検索条件を絞ってください。", "discover.inspectorEsqlRequestDescription": "このリクエストはElasticsearchに対してクエリーを実行し、テーブルの結果を取得します。", "discover.inspectorEsqlRequestTitle": "表", "discover.inspectorRequestDataTitleDocuments": "ドキュメント", @@ -2885,7 +2844,6 @@ "discover.invalidFiltersWarnToast.description": "一部の適用されたフィルターのデータビューID参照は、現在のデータビューとは異なります。", "discover.invalidFiltersWarnToast.title": "別のインデックス参照", "discover.loadingDocuments": "ドキュメントを読み込み中", - "discover.loadingResults": "結果を読み込み中", "discover.localMenu.alertsDescription": "アラート", "discover.localMenu.esqlTooltipLabel": "ES|QLはElasticの強力な新しいパイプクエリ言語です。", "discover.localMenu.fallbackReportTitle": "無題のDiscover検索", @@ -8298,24 +8256,16 @@ "unifiedDocViewer.docView.table.searchPlaceHolder": "検索フィールド名または値", "unifiedDocViewer.docViews.json.jsonTitle": "JSON", "unifiedDocViewer.docViews.table.esqlMultivalueFilteringDisabled": "ES|QLでは、多値フィルタリングはサポートされていません。", - "unifiedDocViewer.docViews.table.filterForFieldPresentButtonAriaLabel": "フィールド表示のフィルター", "unifiedDocViewer.docViews.table.filterForFieldPresentButtonTooltip": "フィールド表示のフィルター", - "unifiedDocViewer.docViews.table.filterForValueButtonAriaLabel": "値でフィルター", "unifiedDocViewer.docViews.table.filterForValueButtonTooltip": "値でフィルター", - "unifiedDocViewer.docViews.table.filterOutValueButtonAriaLabel": "値を除外", "unifiedDocViewer.docViews.table.filterOutValueButtonTooltip": "値を除外", "unifiedDocViewer.docViews.table.ignored.multiValueLabel": "無視された値を含む", "unifiedDocViewer.docViews.table.ignored.singleValueLabel": "無視された値", "unifiedDocViewer.docViews.table.ignoredValuesCanNotBeSearchedWarningMessage": "無視された値は検索できません", "unifiedDocViewer.docViews.table.pinFieldLabel": "フィールドを固定", "unifiedDocViewer.docViews.table.tableTitle": "表", - "unifiedDocViewer.docViews.table.toggleColumnInTableButtonAriaLabel": "表の列を切り替える", - "unifiedDocViewer.docViews.table.toggleColumnInTableButtonTooltip": "表の列を切り替える", "unifiedDocViewer.docViews.table.toggleColumnTableButtonTooltip": "表の列を切り替える", - "unifiedDocViewer.docViews.table.unableToFilterForPresenceOfMetaFieldsTooltip": "メタフィールドの有無でフィルタリングできません", - "unifiedDocViewer.docViews.table.unableToFilterForPresenceOfScriptedFieldsTooltip": "スクリプトフィールドの有無でフィルタリングできません", "unifiedDocViewer.docViews.table.unableToFilterForPresenceOfScriptedFieldsWarningMessage": "スクリプトフィールドの有無でフィルタリングできません", - "unifiedDocViewer.docViews.table.unindexedFieldsCanNotBeSearchedTooltip": "インデックスがないフィールドまたは無視された値は検索できません", "unifiedDocViewer.docViews.table.unindexedFieldsCanNotBeSearchedWarningMessage": "インデックスがないフィールドは検索できません", "unifiedDocViewer.docViews.table.unpinFieldLabel": "フィールドを固定解除", "unifiedDocViewer.docViews.table.viewLessButton": "簡易表示", @@ -8325,7 +8275,6 @@ "unifiedDocViewer.fieldActions.filterForValue": "値でフィルター", "unifiedDocViewer.fieldActions.filterOutValue": "値を除外", "unifiedDocViewer.fieldActions.toggleColumn": "表の列を切り替える", - "unifiedDocViewer.fieldChooser.discoverField.actions": "アクション", "unifiedDocViewer.fieldChooser.discoverField.multiField": "複数フィールド", "unifiedDocViewer.fieldChooser.discoverField.multiFieldTooltipContent": "複数フィールドにはフィールドごとに複数の値を入力できます", "unifiedDocViewer.fieldChooser.discoverField.name": "フィールド", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 231078191b316..9c52ac8a5101a 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -2688,11 +2688,7 @@ "discover.advancedSettings.context.tieBreakerFieldsTitle": "平分决胜字段", "discover.advancedSettings.defaultColumnsText": "Discover 应用中默认显示的列。如果为空,将显示文档摘要。", "discover.advancedSettings.defaultColumnsTitle": "默认列", - "discover.advancedSettings.disableDocumentExplorer": "Document Explorer 或经典视图", - "discover.advancedSettings.disableDocumentExplorerDescription": "要使用新的 {documentExplorerDocs},而非经典视图,请关闭此选项。Document Explorer 提供了更合理的数据排序、可调整大小的列和全屏视图。", - "discover.advancedSettings.discover.disableDocumentExplorerDeprecation": "此设置已过时,将在 Kibana 9.0 中移除。", "discover.advancedSettings.discover.fieldStatisticsLinkText": "字段统计信息视图", - "discover.advancedSettings.discover.maxCellHeightDeprecation": "此设置已过时,将在 Kibana 9.0 中移除。", "discover.advancedSettings.discover.modifyColumnsOnSwitchText": "移除新数据视图中不存在的列。", "discover.advancedSettings.discover.modifyColumnsOnSwitchTitle": "在更改数据视图时修改列", "discover.advancedSettings.discover.multiFieldsLinkText": "多字段", @@ -2705,13 +2701,10 @@ "discover.advancedSettings.discover.showMultifieldsDescription": "控制 {multiFields} 是否显示在展开的文档视图中。多数情况下,多字段与原始字段相同。此选项仅在 `searchFieldsFromSource` 关闭时可用。", "discover.advancedSettings.docTableHideTimeColumnText": "在 Discover 中和仪表板上的所有已保存搜索中隐藏'时间'列。", "discover.advancedSettings.docTableHideTimeColumnTitle": "隐藏'时间'列", - "discover.advancedSettings.documentExplorerLinkText": "Document Explorer", "discover.advancedSettings.fieldsPopularLimitText": "要显示的排名前 N 最常见字段", "discover.advancedSettings.fieldsPopularLimitTitle": "常见字段限制", "discover.advancedSettings.maxDocFieldsDisplayedText": "在文档摘要中渲染的最大字段数目", "discover.advancedSettings.maxDocFieldsDisplayedTitle": "显示的最大文档字段数", - "discover.advancedSettings.params.maxCellHeightText": "表单元格应占用的最大高度。设置为 0 可禁用截断。", - "discover.advancedSettings.params.maxCellHeightTitle": "经典表中的最大单元格高度", "discover.advancedSettings.params.rowHeightText": "一行中允许的文本行数。值为 -1 时,会自动调整行高以适应内容。值为 0 时,会在单文本行中显示内容。", "discover.advancedSettings.params.rowHeightTitle": "Document Explorer 中的行高", "discover.advancedSettings.sampleRowsPerPageText": "限制文档表中每页的行数。", @@ -2727,7 +2720,6 @@ "discover.alerts.createSearchThreshold": "创建搜索阈值规则", "discover.alerts.manageRulesAndConnectors": "管理规则和连接器", "discover.alerts.missedTimeFieldToolTip": "数据视图没有时间字段。", - "discover.backToTopLinkText": "返回顶部。", "discover.badge.readOnly.text": "只读", "discover.badge.readOnly.tooltip": "无法保存搜索", "discover.context.breadcrumb": "周围文档", @@ -2736,7 +2728,6 @@ "discover.context.failedToLoadAnchorDocumentErrorDescription": "无法加载定位点文档。", "discover.context.invalidTieBreakerFiledSetting": "无效的平分决胜字段设置", "discover.context.loadButtonLabel": "加载", - "discover.context.loadingDescription": "正在加载……", "discover.context.newerDocumentsAriaLabel": "较新文档数目", "discover.context.newerDocumentsDescription": "较新文档", "discover.context.newerDocumentsWarning": "仅可以找到 {docCount} 个比定位标记新的文档。", @@ -2773,34 +2764,8 @@ "discover.doc.pageTitle": "单个文档 - #{id}", "discover.doc.somethingWentWrongDescription": "{indexName} 缺失。", "discover.doc.somethingWentWrongDescriptionAddon": "请确保索引存在。", - "discover.docExplorerCallout.bodyMessage": "使用 {documentExplorer} 快速排序、选择和比较数据,调整列大小并以全屏方式查看文档。", - "discover.docExplorerCallout.closeButtonAriaLabel": "关闭", - "discover.docExplorerCallout.documentExplorer": "Document Explorer", - "discover.docExplorerCallout.headerMessage": "更好的浏览方式", - "discover.docExplorerCallout.learnMore": "了解详情", - "discover.docExplorerCallout.tryDocumentExplorer": "试用 Document Explorer", - "discover.docTable.documentsNavigation": "文档导航", - "discover.docTable.limitedSearchResultLabel": "仅限于 {resultCount} 个结果。优化您的搜索。", - "discover.docTable.noResultsTitle": "找不到结果", - "discover.docTable.rows": "行", - "discover.docTable.rowsPerPage": "每页行数:{pageSize}", - "discover.docTable.tableHeader.documentHeader": "文档", - "discover.docTable.tableHeader.moveColumnLeftButtonTooltip": "向左移动列", - "discover.docTable.tableHeader.moveColumnRightButtonTooltip": "向右移动列", - "discover.docTable.tableHeader.removeColumnButtonTooltip": "移除列", - "discover.docTable.tableHeader.timeFieldIconTooltip": "此字段表示事件发生的时间。", - "discover.docTable.tableHeader.timeFieldIconTooltipAriaLabel": "{timeFieldName} - 此字段表示事件发生的时间。", - "discover.docTable.tableRow.detailHeading": "已展开文档", - "discover.docTable.tableRow.filterForValueButtonAriaLabel": "筛留值", - "discover.docTable.tableRow.filterForValueButtonTooltip": "筛留值", - "discover.docTable.tableRow.filterOutValueButtonAriaLabel": "筛除值", - "discover.docTable.tableRow.filterOutValueButtonTooltip": "筛除值", - "discover.docTable.tableRow.toggleRowDetailsButtonAriaLabel": "切换行详细信息", - "discover.docTable.tableRow.viewSingleDocumentLinkText": "查看单个文档", - "discover.docTable.tableRow.viewSurroundingDocumentsLinkText": "查看周围文档", "discover.documentsAriaLabel": "文档", "discover.docViews.logsOverview.title": "日志概览", - "discover.docViews.table.scoreSortWarningTooltip": "要检索 _score 的值,必须按其筛选。", "discover.dropZoneTableLabel": "放置区域以将字段作为列添加到表中", "discover.embeddable.inspectorRequestDataTitle": "数据", "discover.embeddable.inspectorRequestDescription": "此请求将查询 Elasticsearch 以获取搜索的数据。", @@ -2822,7 +2787,6 @@ "discover.globalSearch.esqlSearchTitle": "创建 ES|QL 查询", "discover.goToDiscoverButtonText": "前往 Discover", "discover.grid.tableRow.actionsLabel": "操作", - "discover.grid.tableRow.esqlDetailHeading": "已展开结果", "discover.grid.tableRow.mobileFlyoutActionsButton": "操作", "discover.grid.tableRow.moreFlyoutActionsButton": "更多操作", "discover.grid.tableRow.viewSingleDocumentLinkLabel": "查看单个文档", @@ -2833,7 +2797,6 @@ "discover.hitsCounter.hitsPluralTitle": "{formattedHits} 个{hits, plural, other {结果}}", "discover.hitsCounter.partialHits": "≥{formattedHits}", "discover.hitsCounter.partialHitsPluralTitle": "≥{formattedHits} 个{hits, plural, other {结果}}", - "discover.howToSeeOtherMatchingDocumentsDescription": "下面是与您的搜索匹配的前 {sampleSize} 个文档,请优化您的搜索以查看其他文档。", "discover.inspectorEsqlRequestDescription": "此请求将查询 Elasticsearch 以获取该表的结果。", "discover.inspectorEsqlRequestTitle": "表", "discover.inspectorRequestDataTitleDocuments": "文档", @@ -2842,7 +2805,6 @@ "discover.invalidFiltersWarnToast.description": "某些应用的筛选中的数据视图 ID 引用与当前数据视图不同。", "discover.invalidFiltersWarnToast.title": "不同的索引引用", "discover.loadingDocuments": "正在加载文档", - "discover.loadingResults": "正在加载结果", "discover.localMenu.alertsDescription": "告警", "discover.localMenu.esqlTooltipLabel": "ES|QL 是 Elastic 支持的功能强大的全新管道查询语言。", "discover.localMenu.fallbackReportTitle": "未命名 Discover 搜索", @@ -8141,24 +8103,16 @@ "unifiedDocViewer.docView.table.searchPlaceHolder": "搜索字段名称或值", "unifiedDocViewer.docViews.json.jsonTitle": "JSON", "unifiedDocViewer.docViews.table.esqlMultivalueFilteringDisabled": "ES|QL 中不支持多值筛选", - "unifiedDocViewer.docViews.table.filterForFieldPresentButtonAriaLabel": "筛留存在的字段", "unifiedDocViewer.docViews.table.filterForFieldPresentButtonTooltip": "字段是否存在筛选", - "unifiedDocViewer.docViews.table.filterForValueButtonAriaLabel": "筛留值", "unifiedDocViewer.docViews.table.filterForValueButtonTooltip": "筛留值", - "unifiedDocViewer.docViews.table.filterOutValueButtonAriaLabel": "筛除值", "unifiedDocViewer.docViews.table.filterOutValueButtonTooltip": "筛除值", "unifiedDocViewer.docViews.table.ignored.multiValueLabel": "包含被忽略的值", "unifiedDocViewer.docViews.table.ignored.singleValueLabel": "被忽略的值", "unifiedDocViewer.docViews.table.ignoredValuesCanNotBeSearchedWarningMessage": "无法搜索被忽略的值", "unifiedDocViewer.docViews.table.pinFieldLabel": "固定字段", "unifiedDocViewer.docViews.table.tableTitle": "表", - "unifiedDocViewer.docViews.table.toggleColumnInTableButtonAriaLabel": "在表中切换列", - "unifiedDocViewer.docViews.table.toggleColumnInTableButtonTooltip": "在表中切换列", "unifiedDocViewer.docViews.table.toggleColumnTableButtonTooltip": "在表中切换列", - "unifiedDocViewer.docViews.table.unableToFilterForPresenceOfMetaFieldsTooltip": "无法筛选元数据字段是否存在", - "unifiedDocViewer.docViews.table.unableToFilterForPresenceOfScriptedFieldsTooltip": "无法筛选脚本字段是否存在", "unifiedDocViewer.docViews.table.unableToFilterForPresenceOfScriptedFieldsWarningMessage": "无法筛选脚本字段是否存在", - "unifiedDocViewer.docViews.table.unindexedFieldsCanNotBeSearchedTooltip": "无法搜索未编入索引的字段或被忽略的值", "unifiedDocViewer.docViews.table.unindexedFieldsCanNotBeSearchedWarningMessage": "无法搜索未编入索引的字段", "unifiedDocViewer.docViews.table.unpinFieldLabel": "取消固定字段", "unifiedDocViewer.docViews.table.viewLessButton": "查看更少", @@ -8168,7 +8122,6 @@ "unifiedDocViewer.fieldActions.filterForValue": "筛留值", "unifiedDocViewer.fieldActions.filterOutValue": "筛除值", "unifiedDocViewer.fieldActions.toggleColumn": "在表中切换列", - "unifiedDocViewer.fieldChooser.discoverField.actions": "操作", "unifiedDocViewer.fieldChooser.discoverField.multiField": "多字段", "unifiedDocViewer.fieldChooser.discoverField.multiFieldTooltipContent": "多字段的每个字段可以有多个值", "unifiedDocViewer.fieldChooser.discoverField.name": "字段", diff --git a/x-pack/test/functional/apps/dashboard/group3/reporting/screenshots.ts b/x-pack/test/functional/apps/dashboard/group3/reporting/screenshots.ts index 60a7f754933de..42c933f8792a8 100644 --- a/x-pack/test/functional/apps/dashboard/group3/reporting/screenshots.ts +++ b/x-pack/test/functional/apps/dashboard/group3/reporting/screenshots.ts @@ -221,7 +221,6 @@ export default function ({ }); it('downloads a PDF file with saved search given EuiDataGrid enabled', async function () { - await kibanaServer.uiSettings.update({ 'doc_table:legacy': false }); this.timeout(300000); await dashboard.navigateToApp(); await dashboard.loadSavedDashboard('Ecom Dashboard'); diff --git a/x-pack/test/functional/apps/discover/saved_searches.ts b/x-pack/test/functional/apps/discover/saved_searches.ts index 3b039c8da9eee..ee733b4c4c46d 100644 --- a/x-pack/test/functional/apps/discover/saved_searches.ts +++ b/x-pack/test/functional/apps/discover/saved_searches.ts @@ -28,7 +28,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const ecommerceSOPath = 'x-pack/test/functional/fixtures/kbn_archiver/reporting/ecommerce.json'; const defaultSettings = { defaultIndex: 'logstash-*', - 'doc_table:legacy': false, }; const from = 'Apr 27, 2019 @ 23:56:51.374'; @@ -47,7 +46,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await esArchiver.unload('x-pack/test/functional/es_archives/reporting/ecommerce'); await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover'); await kibanaServer.importExport.unload(ecommerceSOPath); - await kibanaServer.uiSettings.unset('doc_table:legacy'); await common.unsetTime(); }); diff --git a/x-pack/test/functional/apps/discover/value_suggestions.ts b/x-pack/test/functional/apps/discover/value_suggestions.ts index f949a890d90c2..21a577ece13bf 100644 --- a/x-pack/test/functional/apps/discover/value_suggestions.ts +++ b/x-pack/test/functional/apps/discover/value_suggestions.ts @@ -35,14 +35,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await kibanaServer.importExport.load( 'x-pack/test/functional/fixtures/kbn_archiver/dashboard_drilldowns/drilldowns' ); - await kibanaServer.uiSettings.update({ - 'doc_table:legacy': false, - }); await timePicker.setDefaultAbsoluteRangeViaUiSettings(); }); after(async () => { - await kibanaServer.uiSettings.unset('doc_table:legacy'); await common.unsetTime(); await kibanaServer.savedObjects.cleanStandardList(); }); diff --git a/x-pack/test/functional/apps/discover/value_suggestions_non_timebased.ts b/x-pack/test/functional/apps/discover/value_suggestions_non_timebased.ts index b40c74bf06e41..47cc04b5b60fd 100644 --- a/x-pack/test/functional/apps/discover/value_suggestions_non_timebased.ts +++ b/x-pack/test/functional/apps/discover/value_suggestions_non_timebased.ts @@ -23,9 +23,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 'test/functional/fixtures/kbn_archiver/index_pattern_without_timefield' ); await kibanaServer.uiSettings.replace({ defaultIndex: 'without-timefield' }); - await kibanaServer.uiSettings.update({ - 'doc_table:legacy': false, - }); }); after(async () => { @@ -34,7 +31,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ); await kibanaServer.savedObjects.clean({ types: ['search', 'index-pattern'] }); await kibanaServer.uiSettings.unset('defaultIndex'); - await kibanaServer.uiSettings.unset('doc_table:legacy'); }); it('shows all autosuggest options for a filter in discover context app', async () => { diff --git a/x-pack/test/search_sessions_integration/tests/apps/discover/async_search.ts b/x-pack/test/search_sessions_integration/tests/apps/discover/async_search.ts index d4c87209c64c7..eadd2b544953f 100644 --- a/x-pack/test/search_sessions_integration/tests/apps/discover/async_search.ts +++ b/x-pack/test/search_sessions_integration/tests/apps/discover/async_search.ts @@ -28,6 +28,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const retry = getService('retry'); const kibanaServer = getService('kibanaServer'); const toasts = getService('toasts'); + const dataGrid = getService('dataGrid'); // FLAKY: https://github.com/elastic/kibana/issues/195955 describe.skip('discover async search', () => { @@ -95,16 +96,14 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); it('navigation to context cleans the session', async () => { - const table = await discover.getDocTable(); - const isLegacy = await discover.useLegacyTable(); - await table.clickRowToggle({ rowIndex: 0 }); + await dataGrid.clickRowToggle({ rowIndex: 0 }); await retry.try(async () => { - const rowActions = await table.getRowActions({ rowIndex: 0 }); + const rowActions = await dataGrid.getRowActions({ rowIndex: 0 }); if (!rowActions.length) { throw new Error('row actions empty, trying again'); } - const idxToClick = isLegacy ? 0 : 1; + const idxToClick = 1; await rowActions[idxToClick].click(); }); diff --git a/x-pack/test_serverless/functional/services/deployment_agnostic_services.ts b/x-pack/test_serverless/functional/services/deployment_agnostic_services.ts index 314ecf93fb7be..1f09e7353763f 100644 --- a/x-pack/test_serverless/functional/services/deployment_agnostic_services.ts +++ b/x-pack/test_serverless/functional/services/deployment_agnostic_services.ts @@ -36,7 +36,6 @@ const deploymentAgnosticFunctionalServices = _.pick(functionalServices, [ 'dashboardVisualizations', 'dataGrid', 'dataStreams', - 'docTable', 'dataViews', 'elasticChart', 'embedding', diff --git a/yarn.lock b/yarn.lock index eb4fac92cdd26..90429bc21f489 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4000,6 +4000,10 @@ version "0.0.0" uid "" +"@kbn/asset-inventory-plugin@link:x-pack/plugins/asset_inventory": + version "0.0.0" + uid "" + "@kbn/audit-log-plugin@link:x-pack/test/security_api_integration/plugins/audit_log": version "0.0.0" uid ""