From 45ae4a7c087ea0b64db91b1d7311cbb6a0d791c4 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher <471693+Kerry350@users.noreply.github.com> Date: Tue, 5 Nov 2024 12:39:34 +0000 Subject: [PATCH] Add new saved search component --- .github/CODEOWNERS | 1 + package.json | 1 + packages/kbn-saved-search-component/README.md | 26 +++ packages/kbn-saved-search-component/index.ts | 18 ++ .../kbn-saved-search-component/jest.config.js | 14 ++ .../kbn-saved-search-component/kibana.jsonc | 5 + .../kbn-saved-search-component/package.json | 7 + .../src/components/saved_search.tsx | 160 ++++++++++++++++++ .../kbn-saved-search-component/src/types.ts | 30 ++++ .../kbn-saved-search-component/tsconfig.json | 28 +++ .../presentation_publishing/index.ts | 6 +- .../interfaces/publishes_data_views.ts | 4 + .../initialize_search_embeddable_api.tsx | 28 ++- .../discover/public/embeddable/types.ts | 9 +- src/plugins/discover/public/index.ts | 2 + tsconfig.base.json | 2 + .../log_category_details_flyout.tsx | 42 ++++- .../log_category_document_examples_table.tsx | 151 +++-------------- .../observability/logs_overview/tsconfig.json | 7 +- .../observability_solution/apm/kibana.jsonc | 3 +- .../components/app/service_logs/index.tsx | 45 +++-- .../apm/public/plugin.ts | 4 + .../observability_solution/apm/tsconfig.json | 2 + .../services/log_sources_service/index.ts | 45 +++-- .../logs_shared/kibana.jsonc | 5 +- .../logs_shared/public/plugin.tsx | 4 +- .../logs_shared/public/types.ts | 4 + .../logs_shared/tsconfig.json | 2 + yarn.lock | 4 + 29 files changed, 483 insertions(+), 176 deletions(-) create mode 100644 packages/kbn-saved-search-component/README.md create mode 100644 packages/kbn-saved-search-component/index.ts create mode 100644 packages/kbn-saved-search-component/jest.config.js create mode 100644 packages/kbn-saved-search-component/kibana.jsonc create mode 100644 packages/kbn-saved-search-component/package.json create mode 100644 packages/kbn-saved-search-component/src/components/saved_search.tsx create mode 100644 packages/kbn-saved-search-component/src/types.ts create mode 100644 packages/kbn-saved-search-component/tsconfig.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 73a670d14534..2f3429e52324 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -462,6 +462,7 @@ packages/kbn-rrule @elastic/response-ops packages/kbn-rule-data-utils @elastic/security-detections-response @elastic/response-ops @elastic/obs-ux-management-team packages/kbn-safer-lodash-set @elastic/kibana-security packages/kbn-saved-objects-settings @elastic/appex-sharedux +packages/kbn-saved-search-component @elastic/obs-ux-logs-team packages/kbn-screenshotting-server @elastic/appex-sharedux packages/kbn-search-api-keys-components @elastic/search-kibana packages/kbn-search-api-keys-server @elastic/search-kibana diff --git a/package.json b/package.json index afda7cd4c912..13d45d425e57 100644 --- a/package.json +++ b/package.json @@ -777,6 +777,7 @@ "@kbn/saved-objects-settings": "link:packages/kbn-saved-objects-settings", "@kbn/saved-objects-tagging-oss-plugin": "link:src/plugins/saved_objects_tagging_oss", "@kbn/saved-objects-tagging-plugin": "link:x-pack/plugins/saved_objects_tagging", + "@kbn/saved-search-component": "link:packages/kbn-saved-search-component", "@kbn/saved-search-plugin": "link:src/plugins/saved_search", "@kbn/screenshot-mode-example-plugin": "link:examples/screenshot_mode_example", "@kbn/screenshot-mode-plugin": "link:src/plugins/screenshot_mode", diff --git a/packages/kbn-saved-search-component/README.md b/packages/kbn-saved-search-component/README.md new file mode 100644 index 000000000000..296ddb9079bc --- /dev/null +++ b/packages/kbn-saved-search-component/README.md @@ -0,0 +1,26 @@ +# @kbn/saved-search-component + +A component wrapper around Discover's Saved Search embeddable. This can be used in solutions without being within a Dasboard context. + +This can be used to render a context-aware (logs etc) "document table". + +In the past you may have used the Log Stream Component to achieve this, this component supersedes that. + +## Basic usage + +``` +import { LazySavedSearchComponent } from '@kbn/saved-search-component'; + + +``` \ No newline at end of file diff --git a/packages/kbn-saved-search-component/index.ts b/packages/kbn-saved-search-component/index.ts new file mode 100644 index 000000000000..80617ebfa46e --- /dev/null +++ b/packages/kbn-saved-search-component/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 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 { dynamic } from '@kbn/shared-ux-utility'; + +export type { SavedSearchComponentDependencies, SavedSearchComponentProps } from './src/types'; + +export const LazySavedSearchComponent = dynamic(() => + import('./src/components/saved_search').then((mod) => ({ + default: mod.SavedSearchComponent, + })) +); diff --git a/packages/kbn-saved-search-component/jest.config.js b/packages/kbn-saved-search-component/jest.config.js new file mode 100644 index 000000000000..dedff3f69781 --- /dev/null +++ b/packages/kbn-saved-search-component/jest.config.js @@ -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", 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". + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-saved-search-component'], +}; diff --git a/packages/kbn-saved-search-component/kibana.jsonc b/packages/kbn-saved-search-component/kibana.jsonc new file mode 100644 index 000000000000..d0de843443d1 --- /dev/null +++ b/packages/kbn-saved-search-component/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-browser", + "id": "@kbn/saved-search-component", + "owner": "@elastic/obs-ux-logs-team" +} diff --git a/packages/kbn-saved-search-component/package.json b/packages/kbn-saved-search-component/package.json new file mode 100644 index 000000000000..917956fd69ba --- /dev/null +++ b/packages/kbn-saved-search-component/package.json @@ -0,0 +1,7 @@ +{ + "name": "@kbn/saved-search-component", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0", + "sideEffects": false +} \ No newline at end of file diff --git a/packages/kbn-saved-search-component/src/components/saved_search.tsx b/packages/kbn-saved-search-component/src/components/saved_search.tsx new file mode 100644 index 000000000000..ac88fcdeb47a --- /dev/null +++ b/packages/kbn-saved-search-component/src/components/saved_search.tsx @@ -0,0 +1,160 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React, { useEffect, useMemo, useRef, useState } from 'react'; +import { ReactEmbeddableRenderer } from '@kbn/embeddable-plugin/public'; +import { SEARCH_EMBEDDABLE_TYPE } from '@kbn/discover-utils'; +import type { + SearchEmbeddableSerializedState, + SearchEmbeddableRuntimeState, + SearchEmbeddableApi, +} from '@kbn/discover-plugin/public'; +import { SerializedPanelState } from '@kbn/presentation-containers'; +import { SavedSearchComponentProps } from '../types'; + +const TIMESTAMP_FIELD = '@timestamp'; + +export const SavedSearchComponent: React.FC = (props) => { + // Creates our *initial* search source and set of attributes. + // Future changes to these properties will be facilitated by the Parent API from the embeddable. + const [initialSerializedState, setInitialSerializedState] = + useState>(); + + const { dependencies, timeRange, query, filters, index, timestampField, height } = props; + + useEffect(() => { + // Ensure we get a stabilised set of initial state incase dependencies change, as + // the data view creation process is async. + const abortController = new AbortController(); + + async function createInitialSerializedState() { + const { dataViews, searchSource: searchSourceService } = dependencies; + // Ad-hoc data view + const dataView = await dataViews.create({ + title: index, + timeFieldName: timestampField ?? TIMESTAMP_FIELD, + }); + if (!abortController.signal.aborted) { + // Search source + const searchSource = searchSourceService.createEmpty(); + searchSource.setField('index', dataView); + searchSource.setField('query', query); + searchSource.setField('filter', filters); + const { searchSourceJSON, references } = searchSource.serialize(); + // By-value saved object structure + const attributes = { + kibanaSavedObjectMeta: { + searchSourceJSON, + }, + }; + setInitialSerializedState({ + rawState: { + attributes: { ...attributes, references }, + timeRange, + } as SearchEmbeddableSerializedState, + references, + }); + } + } + + createInitialSerializedState(); + + return () => { + abortController.abort(); + }; + }, [dependencies, filters, index, query, timeRange, timestampField]); + + return initialSerializedState ? ( +
+ +
+ ) : null; +}; + +const SavedSearchComponentTable: React.FC< + SavedSearchComponentProps & { initialSerializedState: any } +> = (props) => { + const { dependencies, initialSerializedState, filters, query, timeRange, timestampField, index } = + props; + const embeddableApi = useRef(undefined); + + const parentApi = useMemo(() => { + return { + getSerializedStateForChild: () => { + return initialSerializedState; + }, + }; + }, [initialSerializedState]); + + useEffect( + function syncIndex() { + if (!embeddableApi.current) return; + + const abortController = new AbortController(); + + async function updateDataView(indexPattern: string) { + const { dataViews } = dependencies; + // Ad-hoc data view + const dataView = await dataViews.create({ + title: index, + timeFieldName: timestampField ?? TIMESTAMP_FIELD, + }); + if (!abortController.signal.aborted) { + embeddableApi?.current?.setDataViews([dataView]); + } + } + + updateDataView(index); + + return () => { + abortController.abort(); + }; + }, + [dependencies, index, timestampField] + ); + + useEffect( + function syncFilters() { + if (!embeddableApi.current) return; + embeddableApi.current.setFilters(filters); + }, + [filters] + ); + + useEffect( + function syncQuery() { + if (!embeddableApi.current) return; + embeddableApi.current.setQuery(query); + }, + [query] + ); + + useEffect( + function syncTimeRange() { + if (!embeddableApi.current) return; + embeddableApi.current.setTimeRange(timeRange); + }, + [timeRange] + ); + + return ( + + maybeId={undefined} + type={SEARCH_EMBEDDABLE_TYPE} + getParentApi={() => parentApi} + onApiAvailable={(api) => { + embeddableApi.current = api; + }} + /> + ); +}; diff --git a/packages/kbn-saved-search-component/src/types.ts b/packages/kbn-saved-search-component/src/types.ts new file mode 100644 index 000000000000..0e0ba8363659 --- /dev/null +++ b/packages/kbn-saved-search-component/src/types.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 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 { EmbeddableStart } from '@kbn/embeddable-plugin/public'; +import { SavedSearchPublicPluginStart } from '@kbn/saved-search-plugin/public'; +import { Filter, Query, TimeRange } from '@kbn/es-query'; +import { DataViewsContract, ISearchStartSearchSource } from '@kbn/data-plugin/public'; + +export interface SavedSearchComponentDependencies { + embeddable: EmbeddableStart; + savedSearch: SavedSearchPublicPluginStart; + searchSource: ISearchStartSearchSource; + dataViews: DataViewsContract; +} + +export interface SavedSearchComponentProps { + dependencies: SavedSearchComponentDependencies; + index: string; + timeRange?: TimeRange; + query?: Query; + filters?: Filter[]; + timestampField?: string; + height?: string | number; +} diff --git a/packages/kbn-saved-search-component/tsconfig.json b/packages/kbn-saved-search-component/tsconfig.json new file mode 100644 index 000000000000..7230bab371c5 --- /dev/null +++ b/packages/kbn-saved-search-component/tsconfig.json @@ -0,0 +1,28 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "react" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/embeddable-plugin", + "@kbn/shared-ux-utility", + "@kbn/discover-utils", + "@kbn/saved-search-plugin", + "@kbn/es-query", + "@kbn/data-plugin", + "@kbn/discover-plugin", + "@kbn/presentation-containers", + ] +} diff --git a/packages/presentation/presentation_publishing/index.ts b/packages/presentation/presentation_publishing/index.ts index 2b96c6d353ee..801b64e10d90 100644 --- a/packages/presentation/presentation_publishing/index.ts +++ b/packages/presentation/presentation_publishing/index.ts @@ -97,7 +97,11 @@ export { apiPublishesDataLoading, type PublishesDataLoading, } from './interfaces/publishes_data_loading'; -export { apiPublishesDataViews, type PublishesDataViews } from './interfaces/publishes_data_views'; +export { + apiPublishesDataViews, + type PublishesDataViews, + type PublishesWritableDataViews, +} from './interfaces/publishes_data_views'; export { apiPublishesDisabledActionIds, type PublishesDisabledActionIds, diff --git a/packages/presentation/presentation_publishing/interfaces/publishes_data_views.ts b/packages/presentation/presentation_publishing/interfaces/publishes_data_views.ts index d160cdb6e419..50649b1764c3 100644 --- a/packages/presentation/presentation_publishing/interfaces/publishes_data_views.ts +++ b/packages/presentation/presentation_publishing/interfaces/publishes_data_views.ts @@ -14,6 +14,10 @@ export interface PublishesDataViews { dataViews: PublishingSubject; } +export type PublishesWritableDataViews = PublishesDataViews & { + setDataViews: (dataViews: DataView[]) => void; +}; + export const apiPublishesDataViews = ( unknownApi: null | unknown ): unknownApi is PublishesDataViews => { diff --git a/src/plugins/discover/public/embeddable/initialize_search_embeddable_api.tsx b/src/plugins/discover/public/embeddable/initialize_search_embeddable_api.tsx index e824fb6fdc02..c9c7dfe922e0 100644 --- a/src/plugins/discover/public/embeddable/initialize_search_embeddable_api.tsx +++ b/src/plugins/discover/public/embeddable/initialize_search_embeddable_api.tsx @@ -15,8 +15,8 @@ import { ISearchSource, SerializedSearchSourceFields } from '@kbn/data-plugin/co import { DataView } from '@kbn/data-views-plugin/common'; import { DataTableRecord } from '@kbn/discover-utils/types'; import type { - PublishesDataViews, - PublishesUnifiedSearch, + PublishesWritableUnifiedSearch, + PublishesWritableDataViews, StateComparators, } from '@kbn/presentation-publishing'; import { DiscoverGridSettings, SavedSearch } from '@kbn/saved-search-plugin/common'; @@ -71,7 +71,7 @@ export const initializeSearchEmbeddableApi = async ( discoverServices: DiscoverServices; } ): Promise<{ - api: PublishesSavedSearch & PublishesDataViews & Partial; + api: PublishesSavedSearch & PublishesWritableDataViews & Partial; stateManager: SearchEmbeddableStateManager; comparators: StateComparators; cleanup: () => void; @@ -144,6 +144,25 @@ export const initializeSearchEmbeddableApi = async ( pick(stateManager, EDITABLE_SAVED_SEARCH_KEYS) ); + /** APIs for updating search source properties */ + const setDataViews = async (nextDataViews: DataView[]) => { + searchSource.setField('index', nextDataViews[0]); + dataViews.next(nextDataViews); + searchSource$.next(searchSource); + }; + + const setFilters = (filters: Filter[] | undefined) => { + searchSource.setField('filter', filters); + filters$.next(filters); + searchSource$.next(searchSource); + }; + + const setQuery = (query: Query | AggregateQuery | undefined) => { + searchSource.setField('query', query); + query$.next(query); + searchSource$.next(searchSource); + }; + /** Keep the saved search in sync with any state changes */ const syncSavedSearch = combineLatest([onAnyStateChange, searchSource$]) .pipe( @@ -163,10 +182,13 @@ export const initializeSearchEmbeddableApi = async ( syncSavedSearch.unsubscribe(); }, api: { + setDataViews, dataViews, savedSearch$, filters$, + setFilters, query$, + setQuery, }, stateManager, comparators: { diff --git a/src/plugins/discover/public/embeddable/types.ts b/src/plugins/discover/public/embeddable/types.ts index 1b7ab4a96c2d..030ae2e82633 100644 --- a/src/plugins/discover/public/embeddable/types.ts +++ b/src/plugins/discover/public/embeddable/types.ts @@ -15,10 +15,9 @@ import { HasInPlaceLibraryTransforms, PublishesBlockingError, PublishesDataLoading, - PublishesDataViews, PublishesSavedObjectId, - PublishesUnifiedSearch, PublishesWritablePanelTitle, + PublishesWritableUnifiedSearch, PublishingSubject, SerializedTimeRange, SerializedTitles, @@ -30,6 +29,7 @@ import { } from '@kbn/saved-search-plugin/common/types'; import { DataTableColumnsMeta } from '@kbn/unified-data-table'; import { BehaviorSubject } from 'rxjs'; +import { PublishesWritableDataViews } from '@kbn/presentation-publishing/interfaces/publishes_data_views'; import { EDITABLE_SAVED_SEARCH_KEYS } from './constants'; export type SearchEmbeddableState = Pick< @@ -80,14 +80,13 @@ export type SearchEmbeddableApi = DefaultEmbeddableApi< SearchEmbeddableSerializedState, SearchEmbeddableRuntimeState > & - PublishesDataViews & PublishesSavedObjectId & PublishesDataLoading & PublishesBlockingError & PublishesWritablePanelTitle & PublishesSavedSearch & - PublishesDataViews & - PublishesUnifiedSearch & + PublishesWritableDataViews & + PublishesWritableUnifiedSearch & HasInPlaceLibraryTransforms & HasTimeRange & Partial; diff --git a/src/plugins/discover/public/index.ts b/src/plugins/discover/public/index.ts index b5d4308010f1..2d3fe785579c 100644 --- a/src/plugins/discover/public/index.ts +++ b/src/plugins/discover/public/index.ts @@ -35,6 +35,8 @@ export { type PublishesSavedSearch, type HasTimeRange, type SearchEmbeddableSerializedState, + type SearchEmbeddableRuntimeState, + type SearchEmbeddableApi, } from './embeddable'; export { loadSharingDataHelpers } from './utils'; export { LogsExplorerTabs, type LogsExplorerTabsProps } from './components/logs_explorer_tabs'; diff --git a/tsconfig.base.json b/tsconfig.base.json index 68faf44ed74d..42b98f074c8a 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1512,6 +1512,8 @@ "@kbn/saved-objects-tagging-oss-plugin/*": ["src/plugins/saved_objects_tagging_oss/*"], "@kbn/saved-objects-tagging-plugin": ["x-pack/plugins/saved_objects_tagging"], "@kbn/saved-objects-tagging-plugin/*": ["x-pack/plugins/saved_objects_tagging/*"], + "@kbn/saved-search-component": ["packages/kbn-saved-search-component"], + "@kbn/saved-search-component/*": ["packages/kbn-saved-search-component/*"], "@kbn/saved-search-plugin": ["src/plugins/saved_search"], "@kbn/saved-search-plugin/*": ["src/plugins/saved_search/*"], "@kbn/screenshot-mode-example-plugin": ["examples/screenshot_mode_example"], diff --git a/x-pack/packages/observability/logs_overview/src/components/log_category_details/log_category_details_flyout.tsx b/x-pack/packages/observability/logs_overview/src/components/log_category_details/log_category_details_flyout.tsx index 2f478c771dbf..cf57ec3eab35 100644 --- a/x-pack/packages/observability/logs_overview/src/components/log_category_details/log_category_details_flyout.tsx +++ b/x-pack/packages/observability/logs_overview/src/components/log_category_details/log_category_details_flyout.tsx @@ -20,6 +20,8 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { StateFrom } from 'xstate5'; import { i18n } from '@kbn/i18n'; import { QueryDslQueryContainer } from '@kbn/data-views-plugin/common/types'; +import { css } from '@emotion/react'; +import { FilterStateStore, buildCustomFilter } from '@kbn/es-query'; import { LogCategory } from '../../types'; import { LogCategoryPattern } from '../shared/log_category_pattern'; import { categoryDetailsService } from '../../services/category_details_service'; @@ -30,10 +32,17 @@ import { import { type ResolvedIndexNameLogsSourceConfiguration } from '../../utils/logs_source'; import { LogCategoryDetailsLoadingContent } from './log_category_details_loading_content'; import { LogCategoryDetailsErrorContent } from './log_category_details_error_content'; -import { DiscoverLink } from '../discover_link'; +import { DiscoverLink, DiscoverLinkDependencies } from '../discover_link'; import { createCategoryQuery } from '../../services/categorize_logs_service/queries'; -export type LogCategoriesFlyoutDependencies = LogCategoryDocumentExamplesTableDependencies; +export type LogCategoriesFlyoutDependencies = LogCategoryDocumentExamplesTableDependencies & + DiscoverLinkDependencies; + +const flyoutBodyCss = css` + .euiFlyoutBody__overflowContent { + height: 100%; + } +`; interface LogCategoryDetailsFlyoutProps { onCloseFlyout: () => void; @@ -61,11 +70,19 @@ export const LogCategoryDetailsFlyout: React.FC = prefix: 'flyoutTitle', }); + const categoryFilter = useMemo(() => { + return createCategoryQuery(logsSource.messageField)(logCategory.terms); + }, [logCategory.terms, logsSource.messageField]); + + const documentAndCategoryFilters = useMemo(() => { + return [...(documentFilters ?? []), categoryFilter]; + }, [categoryFilter, documentFilters]); + const linkFilters = useMemo(() => { return [ ...(documentFilters ? documentFilters.map((filter) => ({ filter })) : []), { - filter: createCategoryQuery(logsSource.messageField)(logCategory.terms), + filter: categoryFilter, meta: { name: i18n.translate( 'xpack.observabilityLogsOverview.logCategoryDetailsFlyout.discoverLinkFilterName', @@ -79,7 +96,20 @@ export const LogCategoryDetailsFlyout: React.FC = }, }, ]; - }, [documentFilters, logCategory.terms, logsSource.messageField]); + }, [categoryFilter, documentFilters, logCategory.terms]); + + const filters = useMemo(() => { + return documentAndCategoryFilters.map((filter) => + buildCustomFilter( + logsSource.indexName, + filter, + false, + false, + 'Document filters', + FilterStateStore.APP_STATE + ) + ); + }, [documentAndCategoryFilters, logsSource.indexName]); return ( onCloseFlyout()} aria-labelledby={flyoutTitleId}> @@ -107,7 +137,7 @@ export const LogCategoryDetailsFlyout: React.FC = - + {categoryDetailsServiceState.matches({ hasCategory: 'fetchingDocuments' }) ? ( = ) : ( )} diff --git a/x-pack/packages/observability/logs_overview/src/components/log_category_details/log_category_document_examples_table.tsx b/x-pack/packages/observability/logs_overview/src/components/log_category_details/log_category_document_examples_table.tsx index 6b43fa86fe49..cdd9ad3f3804 100644 --- a/x-pack/packages/observability/logs_overview/src/components/log_category_details/log_category_document_examples_table.tsx +++ b/x-pack/packages/observability/logs_overview/src/components/log_category_details/log_category_document_examples_table.tsx @@ -5,147 +5,46 @@ * 2.0. */ -import { EuiBasicTable, EuiBasicTableColumn, EuiSpacer, EuiText } from '@elastic/eui'; -import React, { useMemo } from 'react'; -import { i18n } from '@kbn/i18n'; -import { DataGridDensity, ROWS_HEIGHT_OPTIONS } from '@kbn/unified-data-table'; -import moment from 'moment'; -import type { SettingsStart } from '@kbn/core-ui-settings-browser'; -import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; -import type { SharePluginStart } from '@kbn/share-plugin/public'; +import React from 'react'; import { CoreStart } from '@kbn/core-lifecycle-browser'; -import { getLogLevelBadgeCell, LazySummaryColumn } from '@kbn/discover-contextual-components'; -import type { LogCategoryDocument } from '../../services/category_details_service/types'; -import { type ResolvedIndexNameLogsSourceConfiguration } from '../../utils/logs_source'; +import { LazySavedSearchComponent } from '@kbn/saved-search-component'; +import { EmbeddableStart } from '@kbn/embeddable-plugin/public'; +import { SavedSearchPublicPluginStart } from '@kbn/saved-search-plugin/public'; +import { DataViewsContract } from '@kbn/data-views-plugin/public'; +import { ISearchStartSearchSource } from '@kbn/data-plugin/common'; +import { Filter } from '@kbn/es-query'; +import { ResolvedIndexNameLogsSourceConfiguration } from '../../utils/logs_source'; export interface LogCategoryDocumentExamplesTableDependencies { core: CoreStart; - uiSettings: SettingsStart; - fieldFormats: FieldFormatsStart; - share: SharePluginStart; + embeddable: EmbeddableStart; + savedSearch: SavedSearchPublicPluginStart; + dataViews: DataViewsContract; + searchSource: ISearchStartSearchSource; } export interface LogCategoryDocumentExamplesTableProps { dependencies: LogCategoryDocumentExamplesTableDependencies; - categoryDocuments: LogCategoryDocument[]; logsSource: ResolvedIndexNameLogsSourceConfiguration; + filters: Filter[]; } -const TimestampCell = ({ - dependencies, - timestamp, -}: { - dependencies: LogCategoryDocumentExamplesTableDependencies; - timestamp?: string | number; -}) => { - const dateFormat = useMemo( - () => dependencies.uiSettings.client.get('dateFormat'), - [dependencies.uiSettings.client] - ); - if (!timestamp) return null; - - if (dateFormat) { - return <>{moment(timestamp).format(dateFormat)}; - } else { - return <>{timestamp}; - } -}; - -const LogLevelBadgeCell = getLogLevelBadgeCell('log.level'); - export const LogCategoryDocumentExamplesTable: React.FC = ({ - categoryDocuments, dependencies, + filters, logsSource, }) => { - const columns: Array> = [ - { - field: 'row', - name: 'Timestamp', - width: '25%', - render: (row: any) => { - return ( - - ); - }, - }, - { - field: 'row', - name: 'Log level', - width: '10%', - render: (row: any) => { - return ( - {}} - closePopover={() => {}} - /> - ); - }, - }, - { - field: 'row', - name: 'Summary', - width: '65%', - render: (row: any) => { - return ( - {}} - closePopover={() => {}} - density={DataGridDensity.COMPACT} - rowHeight={ROWS_HEIGHT_OPTIONS.single} - shouldShowFieldHandler={() => false} - core={dependencies.core} - share={dependencies.share} - /> - ); - }, - }, - ]; return ( - <> - - {i18n.translate( - 'xpack.observabilityLogsOverview.logCategoryDocumentExamplesTable.documentCountText', - { - defaultMessage: 'Displaying the latest {documentsCount} documents.', - values: { - documentsCount: categoryDocuments.length, - }, - } - )} - - - - + ); }; diff --git a/x-pack/packages/observability/logs_overview/tsconfig.json b/x-pack/packages/observability/logs_overview/tsconfig.json index 29595ce0162f..03e2c8b09af7 100644 --- a/x-pack/packages/observability/logs_overview/tsconfig.json +++ b/x-pack/packages/observability/logs_overview/tsconfig.json @@ -34,12 +34,13 @@ "@kbn/es-query", "@kbn/router-utils", "@kbn/share-plugin", - "@kbn/field-formats-plugin", "@kbn/data-service", "@kbn/discover-utils", "@kbn/discover-plugin", - "@kbn/unified-data-table", - "@kbn/discover-contextual-components", "@kbn/core-lifecycle-browser", + "@kbn/embeddable-plugin", + "@kbn/saved-search-plugin", + "@kbn/data-plugin", + "@kbn/saved-search-component", ] } diff --git a/x-pack/plugins/observability_solution/apm/kibana.jsonc b/x-pack/plugins/observability_solution/apm/kibana.jsonc index 656f898f2406..bcb0801fc639 100644 --- a/x-pack/plugins/observability_solution/apm/kibana.jsonc +++ b/x-pack/plugins/observability_solution/apm/kibana.jsonc @@ -37,7 +37,8 @@ "lens", "maps", "uiActions", - "logsDataAccess" + "logsDataAccess", + "savedSearch", ], "optionalPlugins": [ "actions", diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/service_logs/index.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/service_logs/index.tsx index a1dadbf186b9..4e2bfa430a2c 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/service_logs/index.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/service_logs/index.tsx @@ -6,9 +6,9 @@ */ import React, { useMemo } from 'react'; -import moment from 'moment'; import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; -import { LogStream } from '@kbn/logs-shared-plugin/public'; +import { LazySavedSearchComponent } from '@kbn/saved-search-component'; +import useAsync from 'react-use/lib/useAsync'; import { ENVIRONMENT_ALL } from '../../../../common/environment_filter_values'; import { CONTAINER_ID, SERVICE_ENVIRONMENT, SERVICE_NAME } from '../../../../common/es_fields/apm'; import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; @@ -35,6 +35,20 @@ export function ServiceLogs() { } export function ClassicServiceLogsStream() { + const { + services: { + logsDataAccess: { + services: { logSourcesService }, + }, + embeddable, + savedSearch, + dataViews, + data: { + search: { searchSource }, + }, + }, + } = useKibana(); + const { serviceName } = useApmServiceContext(); const { @@ -62,17 +76,26 @@ export function ClassicServiceLogsStream() { [environment, kuery, serviceName, start, end] ); - return ( - { + return { embeddable, savedSearch, searchSource, dataViews }; + }, [dataViews, embeddable, savedSearch, searchSource]); + + const timeRange = useMemo(() => ({ from: start, to: end }), [start, end]); + + return logSources.value ? ( + - ); + ) : null; } export function ServiceLogsOverview() { diff --git a/x-pack/plugins/observability_solution/apm/public/plugin.ts b/x-pack/plugins/observability_solution/apm/public/plugin.ts index b21bdedac9ef..f9627692b2e3 100644 --- a/x-pack/plugins/observability_solution/apm/public/plugin.ts +++ b/x-pack/plugins/observability_solution/apm/public/plugin.ts @@ -70,6 +70,8 @@ import { map } from 'rxjs'; import type { CloudSetup } from '@kbn/cloud-plugin/public'; import type { ServerlessPluginStart } from '@kbn/serverless/public'; import { LogsSharedClientStartExports } from '@kbn/logs-shared-plugin/public'; +import { LogsDataAccessPluginStart } from '@kbn/logs-data-access-plugin/public'; +import { SavedSearchPublicPluginStart } from '@kbn/saved-search-plugin/public'; import type { ConfigSchema } from '.'; import { registerApmRuleTypes } from './components/alerting/rule_types/register_apm_rule_types'; import { registerEmbeddables } from './embeddable/register_embeddables'; @@ -144,6 +146,8 @@ export interface ApmPluginStartDeps { metricsDataAccess: MetricsDataPluginStart; uiSettings: IUiSettingsClient; logsShared: LogsSharedClientStartExports; + logsDataAccess: LogsDataAccessPluginStart; + savedSearch: SavedSearchPublicPluginStart; } const applicationsTitle = i18n.translate('xpack.apm.navigation.rootTitle', { diff --git a/x-pack/plugins/observability_solution/apm/tsconfig.json b/x-pack/plugins/observability_solution/apm/tsconfig.json index 0f08bf3143cd..2dc38f84130b 100644 --- a/x-pack/plugins/observability_solution/apm/tsconfig.json +++ b/x-pack/plugins/observability_solution/apm/tsconfig.json @@ -128,6 +128,8 @@ "@kbn/router-utils", "@kbn/react-hooks", "@kbn/alerting-comparators", + "@kbn/saved-search-component", + "@kbn/saved-search-plugin", ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/observability_solution/logs_data_access/public/services/log_sources_service/index.ts b/x-pack/plugins/observability_solution/logs_data_access/public/services/log_sources_service/index.ts index f329907f145e..e44b9cadcae0 100644 --- a/x-pack/plugins/observability_solution/logs_data_access/public/services/log_sources_service/index.ts +++ b/x-pack/plugins/observability_solution/logs_data_access/public/services/log_sources_service/index.ts @@ -12,23 +12,32 @@ import { RegisterServicesParams } from '../register_services'; export function createLogSourcesService(params: RegisterServicesParams): LogSourcesService { const { uiSettings } = params.deps; - return { - async getLogSources() { - const logSources = uiSettings.get(OBSERVABILITY_LOGS_DATA_ACCESS_LOG_SOURCES_ID); - return logSources.map((logSource) => ({ - indexPattern: logSource, - })); - }, - async getFlattenedLogSources() { - const logSources = await this.getLogSources(); - return flattenLogSources(logSources); - }, - async setLogSources(sources: LogSource[]) { - await uiSettings.set( - OBSERVABILITY_LOGS_DATA_ACCESS_LOG_SOURCES_ID, - sources.map((source) => source.indexPattern) - ); - return; - }, + + const getLogSources = async () => { + const logSources = uiSettings.get(OBSERVABILITY_LOGS_DATA_ACCESS_LOG_SOURCES_ID); + return logSources.map((logSource) => ({ + indexPattern: logSource, + })); + }; + + const getFlattenedLogSources = async () => { + const logSources = await getLogSources(); + return flattenLogSources(logSources); + }; + + const setLogSources = async (sources: LogSource[]) => { + await uiSettings.set( + OBSERVABILITY_LOGS_DATA_ACCESS_LOG_SOURCES_ID, + sources.map((source) => source.indexPattern) + ); + return; + }; + + const logSourcesService: LogSourcesService = { + getLogSources, + getFlattenedLogSources, + setLogSources, }; + + return logSourcesService; } diff --git a/x-pack/plugins/observability_solution/logs_shared/kibana.jsonc b/x-pack/plugins/observability_solution/logs_shared/kibana.jsonc index f5e9f76c2ace..a31d27bed8dd 100644 --- a/x-pack/plugins/observability_solution/logs_shared/kibana.jsonc +++ b/x-pack/plugins/observability_solution/logs_shared/kibana.jsonc @@ -18,11 +18,14 @@ "observabilityShared", "share", "usageCollection", + "embeddable", + "savedSearch" ], "optionalPlugins": [ "observabilityAIAssistant", ], - "requiredBundles": ["kibanaUtils", "kibanaReact", "unifiedDocViewer"], + "requiredBundles": ["kibanaUtils", "kibanaReact"], "extraPublicDirs": ["common"] } } + \ No newline at end of file diff --git a/x-pack/plugins/observability_solution/logs_shared/public/plugin.tsx b/x-pack/plugins/observability_solution/logs_shared/public/plugin.tsx index 0321651607ed..5f86c26941ce 100644 --- a/x-pack/plugins/observability_solution/logs_shared/public/plugin.tsx +++ b/x-pack/plugins/observability_solution/logs_shared/public/plugin.tsx @@ -76,10 +76,12 @@ export class LogsSharedPlugin implements LogsSharedClientPluginClass { charts, logsDataAccess, search: data.search.search, + searchSource: data.search.searchSource, uiSettings: settings, share, dataViews, - fieldFormats, + embeddable: plugins.embeddable, + savedSearch: plugins.savedSearch, }); if (!observabilityAIAssistant) { diff --git a/x-pack/plugins/observability_solution/logs_shared/public/types.ts b/x-pack/plugins/observability_solution/logs_shared/public/types.ts index e2435fa1f491..90bbd89f2481 100644 --- a/x-pack/plugins/observability_solution/logs_shared/public/types.ts +++ b/x-pack/plugins/observability_solution/logs_shared/public/types.ts @@ -15,6 +15,8 @@ import type { ObservabilityAIAssistantPublicStart } from '@kbn/observability-ai- import type { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public'; import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; +import { EmbeddableStart } from '@kbn/embeddable-plugin/public'; +import { SavedSearchPublicPluginStart } from '@kbn/saved-search-plugin/public'; import type { LogsSharedLocators } from '../common/locators'; import type { LogAIAssistantProps } from './components/log_ai_assistant/log_ai_assistant'; import type { SelfContainedLogsOverview } from './components/logs_overview'; @@ -46,6 +48,8 @@ export interface LogsSharedClientStartDeps { share: SharePluginStart; uiActions: UiActionsStart; fieldFormats: FieldFormatsStart; + embeddable: EmbeddableStart; + savedSearch: SavedSearchPublicPluginStart; } export type LogsSharedClientCoreSetup = CoreSetup< diff --git a/x-pack/plugins/observability_solution/logs_shared/tsconfig.json b/x-pack/plugins/observability_solution/logs_shared/tsconfig.json index f171c79afccd..acaed5073a17 100644 --- a/x-pack/plugins/observability_solution/logs_shared/tsconfig.json +++ b/x-pack/plugins/observability_solution/logs_shared/tsconfig.json @@ -49,5 +49,7 @@ "@kbn/charts-plugin", "@kbn/core-ui-settings-common", "@kbn/field-formats-plugin", + "@kbn/embeddable-plugin", + "@kbn/saved-search-plugin", ] } diff --git a/yarn.lock b/yarn.lock index 5faf426cf4d2..6b9e4804b467 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6278,6 +6278,10 @@ version "0.0.0" uid "" +"@kbn/saved-search-component@link:packages/kbn-saved-search-component": + version "0.0.0" + uid "" + "@kbn/saved-search-plugin@link:src/plugins/saved_search": version "0.0.0" uid ""