From e6fd5f594e91a8d5dd5c61ee2ba076dd8917e890 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 | 166 ++++++++++++++++++
.../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 +
.../components/saved_search_grid.tsx | 5 +-
.../search_embeddable_grid_component.tsx | 3 +
.../get_search_embeddable_factory.tsx | 22 ++-
.../initialize_search_embeddable_api.tsx | 28 ++-
.../discover/public/embeddable/types.ts | 20 ++-
.../embeddable/utils/serialization_utils.ts | 1 +
src/plugins/discover/public/index.ts | 3 +
src/plugins/saved_search/common/types.ts | 2 +
tsconfig.base.json | 2 +
.../log_categories_result_content.tsx | 1 -
.../log_category_details_flyout.tsx | 76 ++++----
.../log_category_document_examples_table.tsx | 154 +++-------------
.../category_details_service.ts | 141 ++-------------
.../category_documents.ts | 63 -------
.../category_details_service/queries.ts | 58 ------
.../category_details_service/types.ts | 7 -
.../observability/logs_overview/tsconfig.json | 7 +-
.../observability_solution/apm/kibana.jsonc | 3 +-
.../components/app/service_logs/index.tsx | 49 ++++--
.../apm/public/plugin.ts | 4 +
.../observability_solution/apm/tsconfig.json | 2 +
.../services/log_sources_service/index.ts | 45 +++--
.../logs_shared/kibana.jsonc | 4 +-
.../logs_shared/public/plugin.tsx | 5 +-
.../logs_shared/public/types.ts | 4 +
.../logs_shared/tsconfig.json | 2 +
yarn.lock | 4 +
39 files changed, 548 insertions(+), 473 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
delete mode 100644 x-pack/packages/observability/logs_overview/src/services/category_details_service/category_documents.ts
delete mode 100644 x-pack/packages/observability/logs_overview/src/services/category_details_service/queries.ts
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 6a0a36feb9e21..5f50fcb5a14d0 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -464,6 +464,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 c93eead578c2f..4928267b6b1b3 100644
--- a/package.json
+++ b/package.json
@@ -786,6 +786,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 0000000000000..296ddb9079bcf
--- /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 0000000000000..80617ebfa46ee
--- /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 0000000000000..dedff3f69781d
--- /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 0000000000000..d0de843443d12
--- /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 0000000000000..917956fd69ba0
--- /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 0000000000000..e5783b75e198a
--- /dev/null
+++ b/packages/kbn-saved-search-component/src/components/saved_search.tsx
@@ -0,0 +1,166 @@
+/*
+ * 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;
+ const { enableFlyout: flyoutEnabled = true, enableFilters: filtersEnabled = true } =
+ props.displayOptions ?? {};
+ // 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,
+ nonPersistedDisplayOptions: {
+ enableFlyout: flyoutEnabled,
+ enableFilters: filtersEnabled,
+ },
+ } as SearchEmbeddableSerializedState,
+ references,
+ });
+ }
+ }
+
+ createInitialSerializedState();
+
+ return () => {
+ abortController.abort();
+ };
+ }, [dependencies, filters, index, props.displayOptions, 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 0000000000000..d77304949f893
--- /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 { Filter, Query, TimeRange } from '@kbn/es-query';
+import { DataViewsContract, ISearchStartSearchSource } from '@kbn/data-plugin/public';
+import type { NonPersistedDisplayOptions } from '@kbn/discover-plugin/public';
+
+export interface SavedSearchComponentDependencies {
+ embeddable: EmbeddableStart;
+ searchSource: ISearchStartSearchSource;
+ dataViews: DataViewsContract;
+}
+
+export interface SavedSearchComponentProps {
+ dependencies: SavedSearchComponentDependencies;
+ index: string;
+ timeRange?: TimeRange;
+ query?: Query;
+ filters?: Filter[];
+ timestampField?: string;
+ height?: string | number;
+ displayOptions?: NonPersistedDisplayOptions;
+}
diff --git a/packages/kbn-saved-search-component/tsconfig.json b/packages/kbn-saved-search-component/tsconfig.json
new file mode 100644
index 0000000000000..7230bab371c51
--- /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 2b96c6d353eee..801b64e10d903 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 d160cdb6e4195..50649b1764c32 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/components/saved_search_grid.tsx b/src/plugins/discover/public/embeddable/components/saved_search_grid.tsx
index f6c77dc6cddf5..2d74f47549f9f 100644
--- a/src/plugins/discover/public/embeddable/components/saved_search_grid.tsx
+++ b/src/plugins/discover/public/embeddable/components/saved_search_grid.tsx
@@ -36,12 +36,13 @@ interface DiscoverGridEmbeddableProps extends Omit void;
onRemoveColumn: (column: string) => void;
savedSearchId?: string;
+ enableFlyout: boolean;
}
export const DiscoverGridMemoized = React.memo(DiscoverGrid);
export function DiscoverGridEmbeddable(props: DiscoverGridEmbeddableProps) {
- const { interceptedWarnings, ...gridProps } = props;
+ const { interceptedWarnings, enableFlyout, ...gridProps } = props;
const [expandedDoc, setExpandedDoc] = useState(undefined);
@@ -131,7 +132,7 @@ export function DiscoverGridEmbeddable(props: DiscoverGridEmbeddableProps) {
expandedDoc={expandedDoc}
showMultiFields={props.services.uiSettings.get(SHOW_MULTIFIELDS)}
maxDocFieldsDisplayed={props.services.uiSettings.get(MAX_DOC_FIELDS_DISPLAYED)}
- renderDocumentView={renderDocumentView}
+ renderDocumentView={enableFlyout ? renderDocumentView : undefined}
renderCustomToolbar={renderCustomToolbarWithElements}
externalCustomRenderers={cellRenderers}
enableComparisonMode
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 44d3c1685cbfe..57cdf95b809bf 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
@@ -49,6 +49,7 @@ interface SavedSearchEmbeddableComponentProps {
};
dataView: DataView;
onAddFilter?: DocViewFilterFn;
+ enableFlyout: boolean;
stateManager: SearchEmbeddableStateManager;
}
@@ -59,6 +60,7 @@ export function SearchEmbeddableGridComponent({
api,
dataView,
onAddFilter,
+ enableFlyout,
stateManager,
}: SavedSearchEmbeddableComponentProps) {
const discoverServices = useDiscoverServices();
@@ -272,6 +274,7 @@ export function SearchEmbeddableGridComponent({
services={discoverServices}
showTimeCol={!discoverServices.uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false)}
dataGridDensityState={savedSearch.density}
+ enableFlyout={enableFlyout}
/>
);
}
diff --git a/src/plugins/discover/public/embeddable/get_search_embeddable_factory.tsx b/src/plugins/discover/public/embeddable/get_search_embeddable_factory.tsx
index 37213b17c377d..f9919ba9563ab 100644
--- a/src/plugins/discover/public/embeddable/get_search_embeddable_factory.tsx
+++ b/src/plugins/discover/public/embeddable/get_search_embeddable_factory.tsx
@@ -37,6 +37,7 @@ import { initializeEditApi } from './initialize_edit_api';
import { initializeFetch, isEsqlMode } from './initialize_fetch';
import { initializeSearchEmbeddableApi } from './initialize_search_embeddable_api';
import {
+ NonPersistedDisplayOptions,
SearchEmbeddableApi,
SearchEmbeddableRuntimeState,
SearchEmbeddableSerializedState,
@@ -84,6 +85,11 @@ export const getSearchEmbeddableFactory = ({
initialState?.savedObjectDescription
);
+ /** By-value SavedSearchComponent package (non-dashboard contexts) state, to adhere to the comparator contract of an embeddable. */
+ const nonPersistedDisplayOptions$ = new BehaviorSubject<
+ NonPersistedDisplayOptions | undefined
+ >(initialState?.nonPersistedDisplayOptions);
+
/** All other state */
const blockingError$ = new BehaviorSubject(undefined);
const dataLoading$ = new BehaviorSubject(true);
@@ -201,6 +207,10 @@ export const getSearchEmbeddableFactory = ({
defaultPanelDescription$,
(value) => defaultPanelDescription$.next(value),
],
+ nonPersistedDisplayOptions: [
+ nonPersistedDisplayOptions$,
+ (value) => nonPersistedDisplayOptions$.next(value),
+ ],
}
);
@@ -304,7 +314,17 @@ export const getSearchEmbeddableFactory = ({
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 e824fb6fdc021..c9c7dfe922e02 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 1b7ab4a96c2de..60cc3520d3683 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<
@@ -59,6 +59,13 @@ export type SearchEmbeddableSerializedAttributes = Omit<
> &
Pick;
+// These are options that are not persisted in the saved object, but can be used by solutions
+// when utilising the SavedSearchComponent package outside of dashboard contexts.
+export interface NonPersistedDisplayOptions {
+ enableFlyout?: boolean;
+ enableFilters?: boolean;
+}
+
export type SearchEmbeddableSerializedState = SerializedTitles &
SerializedTimeRange &
Partial> & {
@@ -66,6 +73,8 @@ export type SearchEmbeddableSerializedState = SerializedTitles &
attributes?: SavedSearchAttributes & { references: SavedSearch['references'] };
// by reference
savedObjectId?: string;
+ } & {
+ nonPersistedDisplayOptions?: NonPersistedDisplayOptions;
};
export type SearchEmbeddableRuntimeState = SearchEmbeddableSerializedAttributes &
@@ -74,20 +83,21 @@ export type SearchEmbeddableRuntimeState = SearchEmbeddableSerializedAttributes
savedObjectTitle?: string;
savedObjectId?: string;
savedObjectDescription?: string;
+ } & {
+ nonPersistedDisplayOptions?: NonPersistedDisplayOptions;
};
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/embeddable/utils/serialization_utils.ts b/src/plugins/discover/public/embeddable/utils/serialization_utils.ts
index 191a37fe3326e..f193d52054a3c 100644
--- a/src/plugins/discover/public/embeddable/utils/serialization_utils.ts
+++ b/src/plugins/discover/public/embeddable/utils/serialization_utils.ts
@@ -67,6 +67,7 @@ export const deserializeState = async ({
return {
...savedSearch,
...panelState,
+ nonPersistedDisplayOptions: serializedState.rawState.nonPersistedDisplayOptions,
};
}
};
diff --git a/src/plugins/discover/public/index.ts b/src/plugins/discover/public/index.ts
index b5d4308010f1f..d15ed42aacf51 100644
--- a/src/plugins/discover/public/index.ts
+++ b/src/plugins/discover/public/index.ts
@@ -35,6 +35,9 @@ export {
type PublishesSavedSearch,
type HasTimeRange,
type SearchEmbeddableSerializedState,
+ type SearchEmbeddableRuntimeState,
+ type SearchEmbeddableApi,
+ type NonPersistedDisplayOptions,
} from './embeddable';
export { loadSharingDataHelpers } from './utils';
export { LogsExplorerTabs, type LogsExplorerTabsProps } from './components/logs_explorer_tabs';
diff --git a/src/plugins/saved_search/common/types.ts b/src/plugins/saved_search/common/types.ts
index ca6fe1949fd1a..9a4417b496d58 100644
--- a/src/plugins/saved_search/common/types.ts
+++ b/src/plugins/saved_search/common/types.ts
@@ -68,6 +68,8 @@ export interface SavedSearchAttributes {
breakdownField?: string;
density?: DataGridDensity;
visContext?: VisContextUnmapped;
+
+ enableFlyout?: boolean;
}
/** @internal **/
diff --git a/tsconfig.base.json b/tsconfig.base.json
index 3e1d80208f5b4..ff6b1f0653c85 100644
--- a/tsconfig.base.json
+++ b/tsconfig.base.json
@@ -1526,6 +1526,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_categories/log_categories_result_content.tsx b/x-pack/packages/observability/logs_overview/src/components/log_categories/log_categories_result_content.tsx
index c2b1a0989c2ec..fdb059d971943 100644
--- a/x-pack/packages/observability/logs_overview/src/components/log_categories/log_categories_result_content.tsx
+++ b/x-pack/packages/observability/logs_overview/src/components/log_categories/log_categories_result_content.tsx
@@ -76,7 +76,6 @@ export const LogCategoriesResultContent: React.FC void;
logCategory: LogCategory;
- categoryDetailsServiceState: StateFrom;
dependencies: LogCategoriesFlyoutDependencies;
logsSource: ResolvedIndexNameLogsSourceConfiguration;
documentFilters?: QueryDslQueryContainer[];
@@ -51,7 +55,6 @@ interface LogCategoryDetailsFlyoutProps {
export const LogCategoryDetailsFlyout: React.FC = ({
onCloseFlyout,
logCategory,
- categoryDetailsServiceState,
dependencies,
logsSource,
documentFilters,
@@ -61,11 +64,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 +90,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,32 +131,12 @@ export const LogCategoryDetailsFlyout: React.FC =
-
- {categoryDetailsServiceState.matches({ hasCategory: 'fetchingDocuments' }) ? (
-
- ) : categoryDetailsServiceState.matches({ hasCategory: 'error' }) ? (
-
- ) : (
-
- )}
+
+
);
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 6b43fa86fe49e..3e882911e4021 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,45 @@
* 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 { 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 React from 'react';
+import { LazySavedSearchComponent } from '@kbn/saved-search-component';
+import { EmbeddableStart } from '@kbn/embeddable-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;
+ 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/src/services/category_details_service/category_details_service.ts b/x-pack/packages/observability/logs_overview/src/services/category_details_service/category_details_service.ts
index 958f717548600..40dc0d1eca122 100644
--- a/x-pack/packages/observability/logs_overview/src/services/category_details_service/category_details_service.ts
+++ b/x-pack/packages/observability/logs_overview/src/services/category_details_service/category_details_service.ts
@@ -7,40 +7,24 @@
import { MachineImplementationsFrom, assign, setup } from 'xstate5';
import { LogCategory } from '../../types';
-import { getPlaceholderFor } from '../../utils/xstate5_utils';
-import {
- CategoryDetailsServiceDependencies,
- LogCategoryDocument,
- LogCategoryDetailsParams,
-} from './types';
-import { getCategoryDocuments } from './category_documents';
+import { CategoryDetailsServiceDependencies, LogCategoryDetailsParams } from './types';
export const categoryDetailsService = setup({
types: {
input: {} as LogCategoryDetailsParams,
- output: {} as {
- categoryDocuments: LogCategoryDocument[] | null;
- },
+ output: {} as {},
context: {} as {
parameters: LogCategoryDetailsParams;
- error?: Error;
expandedRowIndex: number | null;
expandedCategory: LogCategory | null;
- categoryDocuments: LogCategoryDocument[];
},
- events: {} as
- | {
- type: 'cancel';
- }
- | {
- type: 'setExpandedCategory';
- rowIndex: number | null;
- category: LogCategory | null;
- },
- },
- actors: {
- getCategoryDocuments: getPlaceholderFor(getCategoryDocuments),
+ events: {} as {
+ type: 'setExpandedCategory';
+ rowIndex: number | null;
+ category: LogCategory | null;
+ },
},
+ actors: {},
actions: {
storeCategory: assign(
({ context, event }, params: { category: LogCategory | null; rowIndex: number | null }) => ({
@@ -48,22 +32,10 @@ export const categoryDetailsService = setup({
expandedRowIndex: params.rowIndex,
})
),
- storeDocuments: assign(
- ({ context, event }, params: { categoryDocuments: LogCategoryDocument[] }) => ({
- categoryDocuments: params.categoryDocuments,
- })
- ),
- storeError: assign((_, params: { error: unknown }) => ({
- error: params.error instanceof Error ? params.error : new Error(String(params.error)),
- })),
},
guards: {
hasCategory: (_guardArgs, params: { expandedCategory: LogCategory | null }) =>
params.expandedCategory !== null,
- hasDocumentExamples: (
- _guardArgs,
- params: { categoryDocuments: LogCategoryDocument[] | null }
- ) => params.categoryDocuments !== null && params.categoryDocuments.length > 0,
},
}).createMachine({
/** @xstate-layout N4IgpgJg5mDOIC5QGMCGAXMUD2AnAlgF5gAy2UsAdMtgK4B26+9UAItsrQLZiOwDEEbPTCVmAN2wBrUWkw4CxMhWp1GzNh2690sBBI4Z8wgNoAGALrmLiUAAdssfE2G2QAD0QBmMwA5KACy+AQFmob4AjABMwQBsADQgAJ6IkYEAnJkA7FmxZlERmQGxAL4liXJYeESk5FQ0DEws7Jw8fILCogYy1BhVirUqDerNWm26+vSScsb01iYRNkggDk4u9G6eCD7+QSFhftFxiSkIvgCsWZSxEVlRsbFZ52Zm515lFX0KNcr1ak2aVo6ARCERiKbSWRfapKOqqRoaFraPiTaZGUyWExRJb2RzOWabbx+QLBULhI7FE7eWL+F45GnRPIRZkfECVb6wob-RFjYH8MC4XB4Sh2AA2GAAZnguL15DDBn8EaMgSiDDMMVZLG5VvjXMstjsSftyTFKclEOdzgFKF5zukvA8zBFnl50udWez5b94SNAcjdPw0PRkGBRdZtXj1oTtsS9mTDqaEuaEBF8udKFkIr5fK6olkzOksgEPdCBt6JWB0MgABYaADKqC4YsgAGFS-g4B0wd0oXKBg2m6LW+24OHljqo-rEMzbpQos8-K7fC9CknTrF0rEbbb0oVMoWIgF3eU2e3OVQK1XaywB82IG2+x2BAKhbgReL0FLcDLPf3G3eH36J8x1xNYCSnFNmSuecXhzdJlydTcqQQLJfHSOc0PyLJN3SMxYiPEtH3PShLxret-yHe8RwEIMQzDLVx0jcDQC2GdoIXOCENXZDsyiOcAiiKJ0iiPDLi8V1CKA4jSOvKAACUwC4VBmA0QDvk7UEughHpfxqBSlJUlg1OqUcGNA3UNggrMs347IjzdaIvGQwSvECXI8k3Z43gEiJJI5BUSMrMiWH05T6FU6j+UFYUxUlaVZSksBQsMqBjIIUycRWJi9RY6dIn8KIAjsu1zkc5CAmiG1fBiaIzB8B0QmPT4iICmSNGS8KjMi2jQxArKwJyjw8pswriocqInOTLwIi3ASD1yQpswCd5WXobAIDgNxdPPCMBss3KEAAWjXRBDvTfcLsu9Jlr8r04WGAEkXGeBGL26MBOQzIt2ut4cwmirCt8W6yzhNqbwo4dH0216LOjTMIjnBdYhK1DYgdHjihtZbUIdWIXJuYGflBoLZI6iKoZe8zJwOw9KtGt1kbuTcsmQrwi0oeCQjzZ5blwt1Cek5TKN22GIIKZbAgKC45pyLyeLwtz4Kyabs1QgWAs0kXqaGhBxdcnzpaE2XXmch0MORmaBJeLwjbKMogA */
@@ -71,7 +43,6 @@ export const categoryDetailsService = setup({
context: ({ input }) => ({
expandedCategory: null,
expandedRowIndex: null,
- categoryDocuments: [],
parameters: input,
}),
initial: 'idle',
@@ -79,38 +50,6 @@ export const categoryDetailsService = setup({
idle: {
on: {
setExpandedCategory: {
- target: 'checkingCategoryState',
- actions: [
- {
- type: 'storeCategory',
- params: ({ event }) => event,
- },
- ],
- },
- },
- },
- checkingCategoryState: {
- always: [
- {
- guard: {
- type: 'hasCategory',
- params: ({ event, context }) => {
- return {
- expandedCategory: context.expandedCategory,
- };
- },
- },
- target: '#hasCategory.fetchingDocuments',
- },
- { target: 'idle' },
- ],
- },
- hasCategory: {
- id: 'hasCategory',
- initial: 'fetchingDocuments',
- on: {
- setExpandedCategory: {
- target: 'checkingCategoryState',
actions: [
{
type: 'storeCategory',
@@ -119,73 +58,13 @@ export const categoryDetailsService = setup({
],
},
},
- states: {
- fetchingDocuments: {
- invoke: {
- src: 'getCategoryDocuments',
- id: 'fetchCategoryDocumentExamples',
- input: ({ context }) => ({
- ...context.parameters,
- categoryTerms: context.expandedCategory!.terms,
- }),
- onDone: [
- {
- guard: {
- type: 'hasDocumentExamples',
- params: ({ event }) => {
- return event.output;
- },
- },
- target: 'hasData',
- actions: [
- {
- type: 'storeDocuments',
- params: ({ event }) => {
- return event.output;
- },
- },
- ],
- },
- {
- target: 'noData',
- actions: [
- {
- type: 'storeDocuments',
- params: ({ event }) => {
- return { categoryDocuments: [] };
- },
- },
- ],
- },
- ],
- onError: {
- target: 'error',
- actions: [
- {
- type: 'storeError',
- params: ({ event }) => ({ error: event.error }),
- },
- ],
- },
- },
- },
- hasData: {},
- noData: {},
- error: {},
- },
},
},
- output: ({ context }) => ({
- categoryDocuments: context.categoryDocuments,
- }),
+ output: ({ context }) => ({}),
});
export const createCategoryDetailsServiceImplementations = ({
search,
}: CategoryDetailsServiceDependencies): MachineImplementationsFrom<
typeof categoryDetailsService
-> => ({
- actors: {
- getCategoryDocuments: getCategoryDocuments({ search }),
- },
-});
+> => ({});
diff --git a/x-pack/packages/observability/logs_overview/src/services/category_details_service/category_documents.ts b/x-pack/packages/observability/logs_overview/src/services/category_details_service/category_documents.ts
deleted file mode 100644
index b513fa79fc686..0000000000000
--- a/x-pack/packages/observability/logs_overview/src/services/category_details_service/category_documents.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; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { ISearchGeneric } from '@kbn/search-types';
-import { fromPromise } from 'xstate5';
-import { lastValueFrom } from 'rxjs';
-import { flattenHit } from '@kbn/data-service';
-import { LogCategoryDocument, LogCategoryDocumentsParams } from './types';
-import { createGetLogCategoryDocumentsRequestParams } from './queries';
-
-export const getCategoryDocuments = ({ search }: { search: ISearchGeneric }) =>
- fromPromise<
- {
- categoryDocuments: LogCategoryDocument[];
- },
- LogCategoryDocumentsParams
- >(
- async ({
- input: {
- index,
- endTimestamp,
- startTimestamp,
- timeField,
- messageField,
- categoryTerms,
- additionalFilters = [],
- dataView,
- },
- signal,
- }) => {
- const requestParams = createGetLogCategoryDocumentsRequestParams({
- index,
- timeField,
- messageField,
- startTimestamp,
- endTimestamp,
- additionalFilters,
- categoryTerms,
- });
-
- const { rawResponse } = await lastValueFrom(
- search({ params: requestParams }, { abortSignal: signal })
- );
-
- const categoryDocuments: LogCategoryDocument[] =
- rawResponse.hits?.hits.map((hit) => {
- return {
- row: {
- raw: hit._source,
- flattened: flattenHit(hit, dataView),
- },
- };
- }) ?? [];
-
- return {
- categoryDocuments,
- };
- }
- );
diff --git a/x-pack/packages/observability/logs_overview/src/services/category_details_service/queries.ts b/x-pack/packages/observability/logs_overview/src/services/category_details_service/queries.ts
deleted file mode 100644
index cd1053077c334..0000000000000
--- a/x-pack/packages/observability/logs_overview/src/services/category_details_service/queries.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
-import { createCategoryQuery } from '../categorize_logs_service/queries';
-
-export const createGetLogCategoryDocumentsRequestParams = ({
- index,
- timeField,
- messageField,
- startTimestamp,
- endTimestamp,
- additionalFilters = [],
- categoryTerms = '',
- documentCount = 20,
-}: {
- startTimestamp: string;
- endTimestamp: string;
- index: string;
- timeField: string;
- messageField: string;
- additionalFilters?: QueryDslQueryContainer[];
- categoryTerms?: string;
- documentCount?: number;
-}) => {
- return {
- index,
- size: documentCount,
- track_total_hits: false,
- sort: [{ [timeField]: { order: 'desc' } }],
- query: {
- bool: {
- filter: [
- {
- exists: {
- field: messageField,
- },
- },
- {
- range: {
- [timeField]: {
- gte: startTimestamp,
- lte: endTimestamp,
- format: 'strict_date_time',
- },
- },
- },
- createCategoryQuery(messageField)(categoryTerms),
- ...additionalFilters,
- ],
- },
- },
- };
-};
diff --git a/x-pack/packages/observability/logs_overview/src/services/category_details_service/types.ts b/x-pack/packages/observability/logs_overview/src/services/category_details_service/types.ts
index 72369275578e3..9c3327632055a 100644
--- a/x-pack/packages/observability/logs_overview/src/services/category_details_service/types.ts
+++ b/x-pack/packages/observability/logs_overview/src/services/category_details_service/types.ts
@@ -8,11 +8,6 @@
import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
import { ISearchGeneric } from '@kbn/search-types';
import { type DataView } from '@kbn/data-views-plugin/common';
-import type { DataTableRecord } from '@kbn/discover-utils';
-
-export interface LogCategoryDocument {
- row: Pick;
-}
export interface LogCategoryDetailsParams {
additionalFilters: QueryDslQueryContainer[];
@@ -27,5 +22,3 @@ export interface LogCategoryDetailsParams {
export interface CategoryDetailsServiceDependencies {
search: ISearchGeneric;
}
-
-export type LogCategoryDocumentsParams = LogCategoryDetailsParams & { categoryTerms: string };
diff --git a/x-pack/packages/observability/logs_overview/tsconfig.json b/x-pack/packages/observability/logs_overview/tsconfig.json
index 29595ce0162fe..03e2c8b09af74 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 656f898f24064..bcb0801fc6394 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 a1dadbf186b91..74b6d7f64c5dd 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,30 @@ 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 b21bdedac9ef8..f9627692b2e38 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 0f08bf3143cd2..2dc38f84130b2 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 f329907f145ef..e44b9cadcae08 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 f5e9f76c2ace6..69539098f2463 100644
--- a/x-pack/plugins/observability_solution/logs_shared/kibana.jsonc
+++ b/x-pack/plugins/observability_solution/logs_shared/kibana.jsonc
@@ -18,11 +18,13 @@
"observabilityShared",
"share",
"usageCollection",
+ "embeddable",
],
"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 0321651607ed1..e6ec419ae8b0f 100644
--- a/x-pack/plugins/observability_solution/logs_shared/public/plugin.tsx
+++ b/x-pack/plugins/observability_solution/logs_shared/public/plugin.tsx
@@ -61,7 +61,6 @@ export class LogsSharedPlugin implements LogsSharedClientPluginClass {
logsDataAccess,
observabilityAIAssistant,
share,
- fieldFormats,
} = plugins;
const logViews = this.logViews.start({
@@ -72,14 +71,14 @@ export class LogsSharedPlugin implements LogsSharedClientPluginClass {
});
const LogsOverview = createLogsOverview({
- core,
charts,
logsDataAccess,
search: data.search.search,
+ searchSource: data.search.searchSource,
uiSettings: settings,
share,
dataViews,
- fieldFormats,
+ embeddable: plugins.embeddable,
});
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 e2435fa1f4915..90bbd89f2481d 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 f171c79afccd0..acaed5073a176 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 9bf235438f586..25d9fe815d2c7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6845,6 +6845,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 ""