From 9b695eb3090f2a3acf2e25eac167b6c95ee4328e Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Mon, 18 Mar 2024 21:23:17 +0000 Subject: [PATCH 01/92] [ML] [AIOps] Pattern analysis tab in Discover --- src/plugins/discover/common/constants.ts | 1 + .../layout/discover_main_content.tsx | 55 +- .../log_categorization/constants.ts | 12 + .../components/log_categorization/index.ts | 13 + .../log_categorization_tab.tsx | 35 ++ .../log_categorization_table.tsx | 292 ++++++++++ .../panels_toggle/panels_toggle.tsx | 5 + .../view_mode_toggle/view_mode_toggle.tsx | 16 +- x-pack/plugins/aiops/common/constants.ts | 4 + ...egister_change_point_charts_attachment.tsx | 3 +- .../max_series_control.tsx | 2 +- .../category_table/category_table.tsx | 34 +- .../log_categorization/embeddable_menu.tsx | 121 ++++ .../log_categorization_for_embeddable.tsx | 526 ++++++++++++++++++ .../log_categorization_page.tsx | 6 +- .../sampling_menu/random_sampler.ts | 55 ++ .../sampling_menu/sampling_menu.tsx | 149 +---- .../sampling_menu/sampling_panel.tsx | 104 ++++ .../change_point_chart_initializer.tsx | 20 +- .../change_point_chart}/const.ts | 0 .../embeddable_change_point_chart.tsx | 6 +- ...mbeddable_change_point_chart_component.tsx | 6 +- .../embeddable_change_point_chart_factory.ts | 4 +- .../embeddable_chart_component_wrapper.tsx | 22 +- .../handle_explicit_input.tsx | 6 +- .../change_point_chart}/index.ts | 5 +- .../change_point_chart}/types.ts | 2 +- .../embeddables/log_categorization/index.ts | 8 + .../log_categorization_embeddable.tsx | 201 +++++++ .../log_categorization_embeddable_factory.ts | 85 +++ .../register_embeddables.ts} | 18 +- x-pack/plugins/aiops/public/plugin.tsx | 9 +- .../edit_change_point_charts_panel.tsx | 4 +- 33 files changed, 1633 insertions(+), 196 deletions(-) create mode 100644 src/plugins/discover/public/application/main/components/log_categorization/constants.ts create mode 100644 src/plugins/discover/public/application/main/components/log_categorization/index.ts create mode 100644 src/plugins/discover/public/application/main/components/log_categorization/log_categorization_tab.tsx create mode 100644 src/plugins/discover/public/application/main/components/log_categorization/log_categorization_table.tsx create mode 100644 x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx create mode 100644 x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx create mode 100644 x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/sampling_panel.tsx rename x-pack/plugins/aiops/public/{embeddable => embeddables/change_point_chart}/change_point_chart_initializer.tsx (90%) rename x-pack/plugins/aiops/public/{embeddable => embeddables/change_point_chart}/const.ts (100%) rename x-pack/plugins/aiops/public/{embeddable => embeddables/change_point_chart}/embeddable_change_point_chart.tsx (95%) rename x-pack/plugins/aiops/public/{embeddable => embeddables/change_point_chart}/embeddable_change_point_chart_component.tsx (92%) rename x-pack/plugins/aiops/public/{embeddable => embeddables/change_point_chart}/embeddable_change_point_chart_factory.ts (98%) rename x-pack/plugins/aiops/public/{embeddable => embeddables/change_point_chart}/embeddable_chart_component_wrapper.tsx (88%) rename x-pack/plugins/aiops/public/{embeddable => embeddables/change_point_chart}/handle_explicit_input.tsx (90%) rename x-pack/plugins/aiops/public/{embeddable => embeddables/change_point_chart}/index.ts (72%) rename x-pack/plugins/aiops/public/{embeddable => embeddables/change_point_chart}/types.ts (89%) create mode 100644 x-pack/plugins/aiops/public/embeddables/log_categorization/index.ts create mode 100644 x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx create mode 100644 x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable_factory.ts rename x-pack/plugins/aiops/public/{embeddable/register_embeddable.ts => embeddables/register_embeddables.ts} (57%) diff --git a/src/plugins/discover/common/constants.ts b/src/plugins/discover/common/constants.ts index 8ac1280592ff6..5a203cba55878 100644 --- a/src/plugins/discover/common/constants.ts +++ b/src/plugins/discover/common/constants.ts @@ -15,6 +15,7 @@ export const ROWS_PER_PAGE_OPTIONS = [10, 25, 50, DEFAULT_ROWS_PER_PAGE, 250, 50 export enum VIEW_MODE { DOCUMENT_LEVEL = 'documents', AGGREGATED_LEVEL = 'aggregated', + PATTERN_LEVEL = 'patterns', } export const getDefaultRowsPerPage = (uiSettings: IUiSettingsClient): number => { diff --git a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx index 07a37e3ba1bc3..86309910d1fd2 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx @@ -13,6 +13,7 @@ import { DataView } from '@kbn/data-views-plugin/common'; import { METRIC_TYPE } from '@kbn/analytics'; import { i18n } from '@kbn/i18n'; import type { DocViewFilterFn } from '@kbn/unified-doc-viewer/types'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { VIEW_MODE } from '../../../../../common/constants'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; import { DocumentViewModeToggle } from '../../../../components/view_mode_toggle'; @@ -22,6 +23,7 @@ import { DiscoverDocuments } from './discover_documents'; import { DOCUMENTS_VIEW_CLICK, FIELD_STATISTICS_VIEW_CLICK } from '../field_stats_table/constants'; import { useAppStateSelector } from '../../services/discover_app_state_container'; import type { PanelsToggleProps } from '../../../../components/panels_toggle'; +import { LogCategorizationTab } from '../log_categorization/log_categorization_tab'; const DROP_PROPS = { value: { @@ -62,9 +64,10 @@ export const DiscoverMainContent = ({ isChartAvailable, }: DiscoverMainContentProps) => { const { trackUiMetric } = useDiscoverServices(); - + const services = useDiscoverServices(); const setDiscoverViewMode = useCallback( (mode: VIEW_MODE) => { + // @ts-expect-error why is this wrong???? stateContainer.appState.update({ viewMode: mode }); if (trackUiMetric) { @@ -103,6 +106,26 @@ export const DiscoverMainContent = ({ isChartAvailable, ]); + const viewModeToggle2 = useCallback( + (patternCount: number) => { + return ( + + ); + }, + [viewMode, setDiscoverViewMode, isPlainRecord, stateContainer] + ); + const showChart = useAppStateSelector((state) => !state.hideChart); return ( @@ -130,7 +153,8 @@ export const DiscoverMainContent = ({ stateContainer={stateContainer} onFieldEdited={!isPlainRecord ? onFieldEdited : undefined} /> - ) : ( + ) : null} + {viewMode === VIEW_MODE.AGGREGATED_LEVEL ? ( <> {viewModeToggle} - )} + ) : null} + {viewMode === VIEW_MODE.PATTERN_LEVEL ? ( + <> + {/* {viewModeToggle} */} + setDiscoverViewMode(VIEW_MODE.DOCUMENT_LEVEL)} + trackUiMetric={trackUiMetric} + getViewModeToggle={(patternCount: number) => { + try { + return ( + + {viewModeToggle2(patternCount)} + + {/*
Hello there
*/} +
+ ); + } catch (error) { + // console.log(error); + } + }} + /> + + ) : null} diff --git a/src/plugins/discover/public/application/main/components/log_categorization/constants.ts b/src/plugins/discover/public/application/main/components/log_categorization/constants.ts new file mode 100644 index 0000000000000..bf1a36da59ecf --- /dev/null +++ b/src/plugins/discover/public/application/main/components/log_categorization/constants.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/** Telemetry related to field statistics table **/ +export const FIELD_STATISTICS_LOADED = 'field_statistics_loaded'; +export const FIELD_STATISTICS_VIEW_CLICK = 'field_statistics_view_click'; +export const DOCUMENTS_VIEW_CLICK = 'documents_view_click'; diff --git a/src/plugins/discover/public/application/main/components/log_categorization/index.ts b/src/plugins/discover/public/application/main/components/log_categorization/index.ts new file mode 100644 index 0000000000000..deeb28928163d --- /dev/null +++ b/src/plugins/discover/public/application/main/components/log_categorization/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { + LogCategorizationTable, + type EmbeddableLogCategorizationInput, +} from './log_categorization_table'; +export { LogCategorizationTab } from './log_categorization_tab'; diff --git a/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_tab.tsx b/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_tab.tsx new file mode 100644 index 0000000000000..23c4d24fac9c9 --- /dev/null +++ b/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_tab.tsx @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { useQuerySubscriber } from '@kbn/unified-field-list/src/hooks/use_query_subscriber'; +import { useSavedSearch } from '../../services/discover_state_provider'; +import { + LogCategorizationTable, + type LogCategorizationTableProps, +} from './log_categorization_table'; +import { useDiscoverServices } from '../../../../hooks/use_discover_services'; + +export const LogCategorizationTab: React.FC< + Omit +> = React.memo((props) => { + const services = useDiscoverServices(); + const querySubscriberResult = useQuerySubscriber({ + data: services.data, + }); + const savedSearch = useSavedSearch(); + + return ( + + ); +}); diff --git a/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_table.tsx b/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_table.tsx new file mode 100644 index 0000000000000..3778e83f5ac90 --- /dev/null +++ b/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_table.tsx @@ -0,0 +1,292 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useEffect, useMemo, useRef, useState } from 'react'; +import type { Filter, Query, AggregateQuery } from '@kbn/es-query'; +import { METRIC_TYPE, UiCounterMetricType } from '@kbn/analytics'; +import type { DataViewField, DataView } from '@kbn/data-views-plugin/public'; +import { + EmbeddableInput, + EmbeddableOutput, + ErrorEmbeddable, + IEmbeddable, + isErrorEmbeddable, +} from '@kbn/embeddable-plugin/public'; +import type { SavedSearch } from '@kbn/saved-search-plugin/public'; +import { EuiFlexItem } from '@elastic/eui'; +import { css } from '@emotion/react'; +import useObservable from 'react-use/lib/useObservable'; +import { of } from 'rxjs'; +import { useDiscoverServices } from '../../../../hooks/use_discover_services'; +import { FIELD_STATISTICS_LOADED } from './constants'; +import type { DiscoverStateContainer } from '../../services/discover_state'; +export interface RandomSamplingOption { + mode: 'random_sampling'; + seed: string; + probability: number; +} + +export interface NormalSamplingOption { + mode: 'normal_sampling'; + seed: string; + shardSize: number; +} + +export interface NoSamplingOption { + mode: 'no_sampling'; + seed: string; +} + +export type SamplingOption = RandomSamplingOption | NormalSamplingOption | NoSamplingOption; + +interface EmbeddableLogCategorizationProps { + dataView: DataView; + savedSearch?: SavedSearch | null; + query?: T; + filters?: Filter[]; + id?: string; + embeddingOrigin?: string; + onAddFilter?: () => void; + getViewModeToggle: (patternCount: number) => React.ReactElement | undefined; + /** + * Callback to add a filter to filter bar + */ + // onAddFilter?: (field: DataViewField | string, value: string, type: '+' | '-') => void; +} + +export type EmbeddableLogCategorizationInput = EmbeddableInput & EmbeddableLogCategorizationProps; + +export type EmbeddableLogCategorizationOutput = EmbeddableOutput & { indexPatterns?: DataView[] }; + +export interface DataVisualizerGridEmbeddableInput extends EmbeddableInput { + dataView: DataView; + savedSearch?: SavedSearch; + query?: Query | AggregateQuery; + visibleFieldNames?: string[]; + filters?: Filter[]; + showPreviewByDefault?: boolean; + /** + * Callback to add a filter to filter bar + */ + onAddFilter?: (field: DataViewField | string, value: string, type: '+' | '-') => void; + sessionId?: string; + fieldsToFetch?: string[]; + totalDocuments?: number; + samplingOption?: SamplingOption; + getViewModeToggle: (patternCount: number) => React.ReactElement | undefined; +} +export interface DataVisualizerGridEmbeddableOutput extends EmbeddableOutput { + showDistributions?: boolean; +} + +export interface LogCategorizationTableProps { + /** + * Determines which columns are displayed + */ + columns: string[]; + /** + * The used data view + */ + dataView: DataView; + /** + * Saved search description + */ + searchDescription?: string; + /** + * Saved search title + */ + searchTitle?: string; + /** + * Optional saved search + */ + savedSearch?: SavedSearch; + /** + * Optional query to update the table content + */ + query?: Query | AggregateQuery; + /** + * Filters query to update the table content + */ + filters?: Filter[]; + /** + * State container with persisted settings + */ + stateContainer?: DiscoverStateContainer; + /** + * Callback to add a filter to filter bar + */ + // onAddFilter?: (field: DataViewField | string, value: string, type: '+' | '-') => void; + onAddFilter?: () => void; + /** + * Metric tracking function + * @param metricType + * @param eventName + */ + trackUiMetric?: (metricType: UiCounterMetricType, eventName: string | string[]) => void; + searchSessionId?: string; + getViewModeToggle: (patternCount: number) => React.ReactElement | undefined; +} + +export const LogCategorizationTable = (props: LogCategorizationTableProps) => { + const { + dataView, + savedSearch, + query, + columns, + filters, + stateContainer, + onAddFilter, + trackUiMetric, + searchSessionId, + getViewModeToggle, + } = props; + + const totalHits = useObservable(stateContainer?.dataState.data$.totalHits$ ?? of(undefined)); + const totalDocuments = useMemo(() => totalHits?.result, [totalHits]); + + const services = useDiscoverServices(); + const [embeddable, setEmbeddable] = useState< + | ErrorEmbeddable + | IEmbeddable + | undefined + >(); + const embeddableRoot: React.RefObject = useRef(null); + + const showPreviewByDefault = useMemo( + () => (stateContainer ? !stateContainer.appState.getState().hideAggregatedPreview : true), + [stateContainer] + ); + + useEffect(() => { + const availableFields$ = stateContainer?.dataState.data$.availableFields$; + const sub = embeddable?.getOutput$().subscribe((output: DataVisualizerGridEmbeddableOutput) => { + if (output.showDistributions !== undefined && stateContainer) { + stateContainer.appState.update({ hideAggregatedPreview: !output.showDistributions }); + } + }); + + const refetch = stateContainer?.dataState.refetch$.subscribe(() => { + if (embeddable && !isErrorEmbeddable(embeddable)) { + embeddable.updateInput({ lastReloadRequestTime: Date.now() }); + } + }); + + // const fields = availableFields$?.subscribe(() => { + // if (embeddable && !isErrorEmbeddable(embeddable) && !availableFields$?.getValue().error) { + // embeddable.updateInput({ fieldsToFetch: availableFields$?.getValue().fields }); + // } + // }); + + return () => { + sub?.unsubscribe(); + refetch?.unsubscribe(); + // fields?.unsubscribe(); + }; + }, [embeddable, stateContainer]); + + useEffect(() => { + if (embeddable && !isErrorEmbeddable(embeddable)) { + // Update embeddable whenever one of the important input changes + embeddable.updateInput({ + dataView, + savedSearch, + query, + filters, + // visibleFieldNames: columns, + // onAddFilter, + }); + embeddable.reload(); + } + }, [ + embeddable, + dataView, + savedSearch, + query, + columns, + filters, + onAddFilter, + searchSessionId, + totalDocuments, + stateContainer, + ]); + + useEffect(() => { + if (showPreviewByDefault && embeddable && !isErrorEmbeddable(embeddable)) { + // Update embeddable whenever one of the important input changes + // embeddable.updateInput({ + // showPreviewByDefault, + // }); + + embeddable.reload(); + } + }, [showPreviewByDefault, embeddable]); + + useEffect(() => { + let unmounted = false; + const loadEmbeddable = async () => { + if (services.embeddable) { + const factory = services.embeddable.getEmbeddableFactory< + EmbeddableLogCategorizationInput, + EmbeddableLogCategorizationOutput + >('aiopsLogCategorization'); + if (factory) { + // Initialize embeddable with information available at mount + const initializedEmbeddable = await factory.create({ + id: 'aiopsLogCategorization', + dataView, + savedSearch, + query, + onAddFilter, + getViewModeToggle, + }); + if (!unmounted) { + setEmbeddable(initializedEmbeddable); + } + } + } + }; + loadEmbeddable(); + return () => { + unmounted = true; + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [services.embeddable, showPreviewByDefault]); + + // We can only render after embeddable has already initialized + useEffect(() => { + if (embeddableRoot.current && embeddable) { + embeddable.render(embeddableRoot.current); + + trackUiMetric?.(METRIC_TYPE.LOADED, FIELD_STATISTICS_LOADED); + } + + return () => { + // Clean up embeddable upon unmounting + embeddable?.destroy(); + }; + }, [embeddable, embeddableRoot, trackUiMetric]); + + const statsTableCss = css` + overflow-y: auto; + + .kbnDocTableWrapper { + overflow-x: hidden; + } + `; + + return ( + +
+ + ); +}; diff --git a/src/plugins/discover/public/components/panels_toggle/panels_toggle.tsx b/src/plugins/discover/public/components/panels_toggle/panels_toggle.tsx index bd04823affd80..b746499b48bb5 100644 --- a/src/plugins/discover/public/components/panels_toggle/panels_toggle.tsx +++ b/src/plugins/discover/public/components/panels_toggle/panels_toggle.tsx @@ -36,6 +36,11 @@ export const PanelsToggle: React.FC = ({ renderedFor, isChartAvailable, }) => { + try { + useAppStateSelector((state) => Boolean(state.hideChart)); + } catch (error) { + // console.log(error); + } const isChartHidden = useAppStateSelector((state) => Boolean(state.hideChart)); const onToggleChart = useCallback(() => { diff --git a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx index 147486ac6dc6e..370ff8cc2a0de 100644 --- a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx +++ b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx @@ -22,17 +22,20 @@ export const DocumentViewModeToggle = ({ prepend, stateContainer, setDiscoverViewMode, + patternCount, }: { viewMode: VIEW_MODE; isTextBasedQuery: boolean; prepend?: ReactElement; stateContainer: DiscoverStateContainer; setDiscoverViewMode: (viewMode: VIEW_MODE) => void; + patternCount?: number; }) => { const { euiTheme } = useEuiTheme(); const { uiSettings } = useDiscoverServices(); const isLegacy = useMemo(() => uiSettings.get(DOC_TABLE_LEGACY), [uiSettings]); - const includesNormalTabsStyle = viewMode === VIEW_MODE.AGGREGATED_LEVEL || isLegacy; + const includesNormalTabsStyle = + viewMode === VIEW_MODE.AGGREGATED_LEVEL || viewMode === VIEW_MODE.PATTERN_LEVEL || isLegacy; const containerPadding = includesNormalTabsStyle ? euiTheme.size.s : 0; const containerCss = css` @@ -90,6 +93,17 @@ export const DocumentViewModeToggle = ({ defaultMessage="Field statistics" /> + setDiscoverViewMode(VIEW_MODE.PATTERN_LEVEL)} + data-test-subj="dscViewModeFieldStatsButton" + > + + )} diff --git a/x-pack/plugins/aiops/common/constants.ts b/x-pack/plugins/aiops/common/constants.ts index c689333857083..8b9867dda9ca7 100644 --- a/x-pack/plugins/aiops/common/constants.ts +++ b/x-pack/plugins/aiops/common/constants.ts @@ -50,3 +50,7 @@ export const CHANGE_POINT_DETECTION_VIEW_TYPE = { export type ChangePointDetectionViewType = typeof CHANGE_POINT_DETECTION_VIEW_TYPE[keyof typeof CHANGE_POINT_DETECTION_VIEW_TYPE]; + +export const EMBEDDABLE_LOG_CATEGORIZATION_TYPE = 'aiopsLogCategorization' as const; + +export type EmbeddableLogCategorizationType = typeof EMBEDDABLE_LOG_CATEGORIZATION_TYPE; diff --git a/x-pack/plugins/aiops/public/cases/register_change_point_charts_attachment.tsx b/x-pack/plugins/aiops/public/cases/register_change_point_charts_attachment.tsx index 1ce6098d0a788..0f1cdbda9ffd9 100644 --- a/x-pack/plugins/aiops/public/cases/register_change_point_charts_attachment.tsx +++ b/x-pack/plugins/aiops/public/cases/register_change_point_charts_attachment.tsx @@ -14,8 +14,9 @@ import { CASES_ATTACHMENT_CHANGE_POINT_CHART, EMBEDDABLE_CHANGE_POINT_CHART_TYPE, } from '../../common/constants'; -import { getEmbeddableChangePointChart } from '../embeddable/embeddable_change_point_chart_component'; + import type { AiopsPluginStartDeps } from '../types'; +import { getEmbeddableChangePointChart } from '../embeddables/change_point_chart'; export function registerChangePointChartsAttachment( cases: CasesPublicSetup, diff --git a/x-pack/plugins/aiops/public/components/change_point_detection/max_series_control.tsx b/x-pack/plugins/aiops/public/components/change_point_detection/max_series_control.tsx index 4f2929751006a..82c8a6fe6e131 100644 --- a/x-pack/plugins/aiops/public/components/change_point_detection/max_series_control.tsx +++ b/x-pack/plugins/aiops/public/components/change_point_detection/max_series_control.tsx @@ -10,7 +10,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { EuiFieldNumber, EuiFormRow, EuiIcon, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { type NumberValidationResult, numberValidator } from '@kbn/ml-agg-utils'; -import { MAX_SERIES } from '../../embeddable/const'; +import { MAX_SERIES } from '../../embeddables/change_point_chart/const'; const maxSeriesValidator = numberValidator({ min: 1, max: MAX_SERIES, integerOnly: true }); diff --git a/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx b/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx index f519d3d2b4529..5cfaeb2e6bdf0 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx @@ -26,6 +26,7 @@ import type { Filter } from '@kbn/es-query'; import { useTableState } from '@kbn/ml-in-memory-table'; import moment from 'moment'; +import { css } from '@emotion/react'; import type { CategorizationAdditionalFilter } from '../../../../common/api/log_categorization/create_category_request'; import { type QueryMode, @@ -63,6 +64,7 @@ interface Props { additionalFilter?: CategorizationAdditionalFilter; navigateToDiscover?: boolean; displayExamples?: boolean; + displayHeader?: boolean; } export const CategoryTable: FC = ({ @@ -82,6 +84,7 @@ export const CategoryTable: FC = ({ additionalFilter, navigateToDiscover = true, displayExamples = true, + displayHeader = true, }) => { const euiTheme = useEuiTheme(); const primaryBackgroundColor = useEuiBackgroundColor('primary'); @@ -306,16 +309,30 @@ export const CategoryTable: FC = ({ }; }; + const tableStyle = css({ + thead: { + position: 'sticky', + insetBlockStart: 0, + zIndex: 1, + backgroundColor: euiTheme.euiColorEmptyShade, + boxShadow: `inset 0 0px 0, inset 0 -1px 0 ${euiTheme.euiBorderColor}`, + }, + }); + return ( <> - openInDiscover(queryMode)} - /> - - + {displayHeader ? ( + <> + openInDiscover(queryMode)} + /> + + + + ) : null} compressed @@ -330,6 +347,7 @@ export const CategoryTable: FC = ({ data-test-subj="aiopsLogPatternsTable" isExpandable={true} itemIdToExpandedRowMap={itemIdToExpandedRowMap} + css={tableStyle} rowProps={(category) => { return enableRowActions ? { diff --git a/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx b/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx new file mode 100644 index 0000000000000..1adff6ebbf55a --- /dev/null +++ b/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx @@ -0,0 +1,121 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { EuiComboBoxOptionOption } from '@elastic/eui'; +import { EuiFormRow } from '@elastic/eui'; +import { + EuiButtonEmpty, + EuiComboBox, + EuiPanel, + EuiPopover, + EuiSpacer, + EuiTitle, + EuiHorizontalRule, +} from '@elastic/eui'; +import type { FC } from 'react'; +import { useCallback } from 'react'; +import React, { useState, useMemo } from 'react'; +import type { DataViewField } from '@kbn/data-views-plugin/public'; +import { i18n } from '@kbn/i18n'; +import type { RandomSampler } from './sampling_menu'; +import { SamplingPanel } from './sampling_menu/sampling_panel'; + +interface Props { + randomSampler: RandomSampler; + fields: DataViewField[]; + selectedField: DataViewField | null; + setSelectedField: (field: DataViewField) => void; + reload: () => void; +} + +export const EmbeddableMenu: FC = ({ + randomSampler, + fields, + selectedField, + setSelectedField, + reload, +}) => { + const [showPopover, setShowPopover] = useState(false); + const togglePopover = () => setShowPopover(!showPopover); + + const fieldOptions = useMemo( + () => fields.map((field) => ({ label: field.name, field })), + [fields] + ); + + const onSelectedFieldChange = useCallback( + (selectedOptions: EuiComboBoxOptionOption[]) => { + if (selectedOptions.length) { + const field = fields.find((f) => f.name === selectedOptions[0].label); + if (field) { + setSelectedField(field); + } + } + }, + [fields, setSelectedField] + ); + + const selectedFieldOptions = useMemo(() => { + if (selectedField) { + return [{ label: selectedField.name }]; + } + }, [selectedField]); + + const button = ( + togglePopover()} + css={{ marginTop: '4px', marginRight: '4px' }} + > + Options + + ); + + return ( + togglePopover()} + panelPaddingSize="s" + anchorPosition="downLeft" + > + + +

Pattern analysis settings

+
+ + + + + + + + +
+
+ ); +}; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx new file mode 100644 index 0000000000000..519bbcad2c287 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx @@ -0,0 +1,526 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import type { FC } from 'react'; +import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react'; + +import { + // EuiTitle, + // EuiFlyoutHeader, + // EuiFlyoutBody, + EuiFlexGroup, + EuiFlexItem, + useEuiTheme, + EuiTabs, + EuiTab, + EuiSpacer, + EuiToolTip, + EuiIcon, +} from '@elastic/eui'; + +import type { SavedSearch } from '@kbn/saved-search-plugin/public'; +import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import type { Filter } from '@kbn/es-query'; +import { buildEmptyFilter } from '@kbn/es-query'; +import { usePageUrlState } from '@kbn/ml-url-state'; +import type { FieldValidationResults } from '@kbn/ml-category-validator'; +import { stringHash } from '@kbn/ml-string-hash'; +import useMount from 'react-use/lib/useMount'; +import { ES_FIELD_TYPES } from '@kbn/field-types'; +import type { CategorizationAdditionalFilter } from '../../../common/api/log_categorization/create_category_request'; +import { AIOPS_TELEMETRY_ID } from '../../../common/constants'; + +import type { Category } from '../../../common/api/log_categorization/types'; + +import { + type LogCategorizationPageUrlState, + getDefaultLogCategorizationAppState, +} from '../../application/url_state/log_pattern_analysis'; +import { createMergedEsQuery } from '../../application/utils/search_utils'; +import { useData } from '../../hooks/use_data'; +import { useSearch } from '../../hooks/use_search'; +import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; + +import { useCategorizeRequest } from './use_categorize_request'; +import type { EventRate } from './use_categorize_request'; +import { CategoryTable } from './category_table'; +import { InformationText } from './information_text'; +// import { SamplingMenu } from './sampling_menu'; +// import { TechnicalPreviewBadge } from './technical_preview_badge'; +import { LoadingCategorization } from './loading_categorization'; +import { useValidateFieldRequest } from './use_validate_category_field'; +import { FieldValidationCallout } from './category_validation_callout'; +import type { DocumentStats } from '../../hooks/use_document_count_stats'; +import { EmbeddableMenu } from './embeddable_menu'; +// import { CreateCategorizationJobButton } from './create_categorization_job'; + +enum SELECTED_TAB { + BUCKET, + FULL_TIME_RANGE, +} + +export interface LogCategorizationPageProps { + dataView: DataView; + savedSearch: SavedSearch | null; + onClose: () => void; + /** Identifier to indicate the plugin utilizing the component */ + embeddingOrigin: string; + additionalFilter?: CategorizationAdditionalFilter; + onAddFilter?: () => void; + getViewModeToggle: (patternCount: number) => React.ReactElement | undefined; +} + +const BAR_TARGET = 20; + +export const LogCategorizationEmbeddable: FC = ({ + dataView, + savedSearch, + onClose, + embeddingOrigin, + additionalFilter, + onAddFilter, + getViewModeToggle, +}) => { + const { + notifications: { toasts }, + data: { + query: { getState, filterManager }, + }, + uiSettings, + } = useAiopsAppContext(); + + const { runValidateFieldRequest, cancelRequest: cancelValidationRequest } = + useValidateFieldRequest(); + const { euiTheme } = useEuiTheme(); + const { filters, query } = useMemo(() => getState(), [getState]); + + const mounted = useRef(false); + const { + runCategorizeRequest, + cancelRequest: cancelCategorizationRequest, + randomSampler, + } = useCategorizeRequest(); + const [stateFromUrl] = usePageUrlState( + 'logCategorization', + getDefaultLogCategorizationAppState({ + searchQuery: createMergedEsQuery(query, filters, dataView, uiSettings), + }) + ); + const [selectedCategory, setSelectedCategory] = useState(null); + const [selectedField, setSelectedField] = useState(null); + const [fields, setFields] = useState([]); + const [selectedSavedSearch /* , setSelectedSavedSearch*/] = useState(savedSearch); + const [previousDocumentStatsHash, setPreviousDocumentStatsHash] = useState(0); + const [loading, setLoading] = useState(true); + const [eventRate, setEventRate] = useState([]); + const [pinnedCategory, setPinnedCategory] = useState(null); + const [data, setData] = useState<{ + categories: Category[]; + categoriesInBucket: Category[] | null; + displayExamples: boolean; + } | null>(null); + const [fieldValidationResult, setFieldValidationResult] = useState( + null + ); + const [showTabs, setShowTabs] = useState(false); + const [selectedTab, setSelectedTab] = useState(SELECTED_TAB.FULL_TIME_RANGE); + + const cancelRequest = useCallback(() => { + cancelValidationRequest(); + cancelCategorizationRequest(); + }, [cancelCategorizationRequest, cancelValidationRequest]); + + useMount(function loadFields() { + const dataViewFields = dataView.fields.filter((f) => f.esTypes?.includes(ES_FIELD_TYPES.TEXT)); + setFields(dataViewFields); + const messageField = dataViewFields.find((f) => f.name === 'message'); + if (messageField !== undefined) { + setSelectedField(messageField); + } + }); + + useEffect( + function cancelRequestOnLeave() { + mounted.current = true; + return () => { + mounted.current = false; + cancelRequest(); + }; + }, + [cancelRequest, mounted] + ); + + const { searchQueryLanguage, searchString, searchQuery } = useSearch( + { dataView, savedSearch: selectedSavedSearch }, + stateFromUrl, + true + ); + + const { documentStats, timefilter, earliest, latest, intervalMs } = useData( + dataView, + 'log_categorization', + searchQuery, + () => {}, + undefined, + undefined, + BAR_TARGET + ); + + const loadCategories = useCallback(async () => { + // eslint-disable-next-line no-console + console.log('loadCategories'); + + const { getIndexPattern, timeFieldName: timeField } = dataView; + const index = getIndexPattern(); + + if ( + selectedField === null || + timeField === undefined || + earliest === undefined || + latest === undefined + ) { + return; + } + + cancelRequest(); + + setLoading(true); + setData(null); + setFieldValidationResult(null); + + const timeRange = { + from: earliest, + to: latest, + }; + + try { + const [validationResult, categorizationResult] = await Promise.all([ + runValidateFieldRequest(index, selectedField.name, timeField, timeRange, searchQuery, { + [AIOPS_TELEMETRY_ID.AIOPS_ANALYSIS_RUN_ORIGIN]: embeddingOrigin, + }), + runCategorizeRequest( + index, + selectedField.name, + timeField, + timeRange, + searchQuery, + intervalMs, + additionalFilter + ), + ]); + + if (mounted.current === true) { + setFieldValidationResult(validationResult); + const { categories, hasExamples } = categorizationResult; + + const hasBucketCategories = categories.some((c) => c.subTimeRangeCount !== undefined); + let categoriesInBucket: any | null = null; + if (additionalFilter !== undefined) { + categoriesInBucket = categorizationResult.categories + .map((category) => ({ + ...category, + count: category.subFieldCount ?? category.subTimeRangeCount!, + examples: category.subFieldExamples!, + sparkline: undefined, + })) + .filter((category) => category.count > 0) + .sort((a, b) => b.count - a.count); + } + + setData({ + categories, + categoriesInBucket, + displayExamples: hasExamples, + }); + + setShowTabs(hasBucketCategories); + setSelectedTab(SELECTED_TAB.BUCKET); + } + } catch (error) { + toasts.addError(error, { + title: i18n.translate('xpack.aiops.logCategorization.errorLoadingCategories', { + defaultMessage: 'Error loading categories', + }), + }); + } + + if (mounted.current === true) { + setLoading(false); + } + }, [ + dataView, + selectedField, + earliest, + latest, + cancelRequest, + runValidateFieldRequest, + searchQuery, + embeddingOrigin, + runCategorizeRequest, + intervalMs, + additionalFilter, + toasts, + ]); + + const onAddFilter2 = useCallback( + (values: Filter, alias?: string) => { + const filter = buildEmptyFilter(false, dataView.id); + if (alias) { + filter.meta.alias = alias; + } + filter.query = values.query; + if (onAddFilter !== undefined) { + onAddFilter(); + } + filterManager.addFilters([filter]); + }, + [dataView.id, filterManager, onAddFilter] + ); + + useEffect(() => { + const buckets = documentStats.documentCountStats?.buckets; + if (buckets === undefined || selectedField === null) { + return; + } + + const hash = createDocumentStatsHash(documentStats, selectedField.name); + if (hash !== previousDocumentStatsHash) { + randomSampler.setDocCount(documentStats.totalCount); + setEventRate( + Object.entries(buckets).map(([key, docCount]) => ({ + key: +key, + docCount, + })) + ); + // setData(null); + // if (fieldValidationResult !== null) { + loadCategories(); + // } + setPreviousDocumentStatsHash(hash); + } + }, [ + documentStats, + earliest, + latest, + searchQueryLanguage, + searchString, + searchQuery, + loadCategories, + randomSampler, + previousDocumentStatsHash, + fieldValidationResult, + selectedField, + ]); + // console.log(viewModeToggle); + + const infoIconCss = { marginTop: euiTheme.size.m, marginLeft: euiTheme.size.xxs }; + + return ( + <> + + + + <>{getViewModeToggle(data?.categories.length ?? 0)} + + + {randomSampler !== undefined ? ( + loadCategories()} + fields={fields} + setSelectedField={setSelectedField} + selectedField={selectedField} + /> + ) : null} + + + + {/* + + + +

+ +

+
+
+ + + + + + forceRefresh()} /> + +
+
*/} + {/* */} + + <> + {/* {showTabs === false && loading === false ? ( + + ) : null} */} + + {/* + + + forceRefresh()} /> + + */} + + + {loading === true ? : null} + + {loading === false && + data !== null && + data.categories.length > 0 && + selectedField !== null ? ( + <> + {showTabs ? ( + <> + + setSelectedTab(SELECTED_TAB.BUCKET)} + > + + + + + + + + + + + + + setSelectedTab(SELECTED_TAB.FULL_TIME_RANGE)} + > + + + + + + + + + + + + + + + ) : null} + + + + ) : null} + + +
+ + ); +}; + +/** + * Creates a hash from the document stats to determine if the document stats have changed. + */ +function createDocumentStatsHash(documentStats: DocumentStats, selectedFieldName: string) { + const lastTimeStampMs = documentStats.documentCountStats?.lastDocTimeStampMs; + const totalCount = documentStats.documentCountStats?.totalCount; + const times = Object.keys(documentStats.documentCountStats?.buckets ?? {}); + const firstBucketTimeStamp = times.length ? times[0] : undefined; + const lastBucketTimeStamp = times.length ? times[times.length - 1] : undefined; + return stringHash( + `${lastTimeStampMs}${totalCount}${firstBucketTimeStamp}${lastBucketTimeStamp}${selectedFieldName}` + ); +} diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx index 5997bb0d12fc1..202ff11a69c06 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx @@ -178,6 +178,8 @@ export const LogCategorizationPage: FC = ({ embeddin ); const loadCategories = useCallback(async () => { + // eslint-disable-next-line no-console + console.log('loadCategories'); setLoading(true); setData(null); setFieldValidationResult(null); @@ -254,8 +256,8 @@ export const LogCategorizationPage: FC = ({ embeddin docCount, })) ); - setData(null); - setFieldValidationResult(null); + // setData(null); + // setFieldValidationResult(null); setTotalCount(documentStats.totalCount); if (fieldValidationResult !== null) { loadCategories(); diff --git a/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/random_sampler.ts b/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/random_sampler.ts index dac084c605d82..cd9eb911608c5 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/random_sampler.ts +++ b/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/random_sampler.ts @@ -126,3 +126,58 @@ export class RandomSampler { return wrapper; } } + +export const randomSamplerText = (randomSamplerPreference: RandomSamplerOption) => { + switch (randomSamplerPreference) { + case RANDOM_SAMPLER_OPTION.OFF: + return { + calloutInfoMessage: i18n.translate( + 'xpack.aiops.logCategorization.randomSamplerSettingsPopUp.offCallout.message', + { + defaultMessage: + 'Random sampling can be turned on to increase the speed of analysis, although some accuracy will be lost.', + } + ), + buttonText: i18n.translate( + 'xpack.aiops.logCategorization.randomSamplerSettingsPopUp.offCallout.button', + { + defaultMessage: 'No sampling', + } + ), + }; + case RANDOM_SAMPLER_OPTION.ON_AUTOMATIC: + return { + calloutInfoMessage: i18n.translate( + 'xpack.aiops.logCategorization.randomSamplerSettingsPopUp.onAutomaticCallout.message', + { + defaultMessage: + 'The pattern analysis will use random sampler aggregations. The probability is automatically set to balance accuracy and speed.', + } + ), + buttonText: i18n.translate( + 'xpack.aiops.logCategorization.randomSamplerSettingsPopUp.onAutomaticCallout.button', + { + defaultMessage: 'Auto sampling', + } + ), + }; + + case RANDOM_SAMPLER_OPTION.ON_MANUAL: + default: + return { + calloutInfoMessage: i18n.translate( + 'xpack.aiops.logCategorization.randomSamplerSettingsPopUp.onManualCallout.message', + { + defaultMessage: + 'The pattern analysis will use random sampler aggregations. A lower percentage probability increases performance, but some accuracy is lost.', + } + ), + buttonText: i18n.translate( + 'xpack.aiops.logCategorization.randomSamplerSettingsPopUp.onManualCallout.button', + { + defaultMessage: 'Manual sampling', + } + ), + }; + } +}; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/sampling_menu.tsx b/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/sampling_menu.tsx index 92eeeb0fb0c1e..d2dd9591e76f7 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/sampling_menu.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/sampling_menu.tsx @@ -6,24 +6,14 @@ */ import type { FC } from 'react'; -import React, { useCallback, useMemo, useState } from 'react'; -import { - EuiFlexItem, - EuiPopover, - EuiPanel, - EuiSpacer, - EuiCallOut, - EuiSelect, - EuiFormRow, - EuiButtonEmpty, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; -import useObservable from 'react-use/lib/useObservable'; +import { useMemo } from 'react'; +import React, { useState } from 'react'; +import { EuiPopover, EuiButtonEmpty, EuiPanel } from '@elastic/eui'; -import { RandomSamplerRangeSlider } from './random_sampler_range_slider'; -import type { RandomSampler, RandomSamplerOption } from './random_sampler'; -import { RANDOM_SAMPLER_OPTION, RANDOM_SAMPLER_SELECT_OPTIONS } from './random_sampler'; +import useObservable from 'react-use/lib/useObservable'; +import type { RandomSampler } from './random_sampler'; +import { randomSamplerText } from './random_sampler'; +import { SamplingPanel } from './sampling_panel'; interface Props { randomSampler: RandomSampler; @@ -33,82 +23,12 @@ interface Props { export const SamplingMenu: FC = ({ randomSampler, reload }) => { const [showSamplingOptionsPopover, setShowSamplingOptionsPopover] = useState(false); - const samplingProbability = useObservable( - randomSampler.getProbability$(), - randomSampler.getProbability() - ); - const setSamplingProbability = useCallback( - (probability: number | null) => { - randomSampler.setProbability(probability); - reload(); - }, - [reload, randomSampler] - ); - const randomSamplerPreference = useObservable(randomSampler.getMode$(), randomSampler.getMode()); - const setRandomSamplerPreference = useCallback( - (mode: RandomSamplerOption) => { - randomSampler.setMode(mode); - reload(); - }, - [randomSampler, reload] + const { buttonText } = useMemo( + () => randomSamplerText(randomSamplerPreference), + [randomSamplerPreference] ); - const { calloutInfoMessage, buttonText } = useMemo(() => { - switch (randomSamplerPreference) { - case RANDOM_SAMPLER_OPTION.OFF: - return { - calloutInfoMessage: i18n.translate( - 'xpack.aiops.logCategorization.randomSamplerSettingsPopUp.offCallout.message', - { - defaultMessage: - 'Random sampling can be turned on to increase the speed of analysis, although some accuracy will be lost.', - } - ), - buttonText: i18n.translate( - 'xpack.aiops.logCategorization.randomSamplerSettingsPopUp.offCallout.button', - { - defaultMessage: 'No sampling', - } - ), - }; - case RANDOM_SAMPLER_OPTION.ON_AUTOMATIC: - return { - calloutInfoMessage: i18n.translate( - 'xpack.aiops.logCategorization.randomSamplerSettingsPopUp.onAutomaticCallout.message', - { - defaultMessage: - 'The pattern analysis will use random sampler aggregations. The probability is automatically set to balance accuracy and speed.', - } - ), - buttonText: i18n.translate( - 'xpack.aiops.logCategorization.randomSamplerSettingsPopUp.onAutomaticCallout.button', - { - defaultMessage: 'Auto sampling', - } - ), - }; - - case RANDOM_SAMPLER_OPTION.ON_MANUAL: - default: - return { - calloutInfoMessage: i18n.translate( - 'xpack.aiops.logCategorization.randomSamplerSettingsPopUp.onManualCallout.message', - { - defaultMessage: - 'The pattern analysis will use random sampler aggregations. A lower percentage probability increases performance, but some accuracy is lost.', - } - ), - buttonText: i18n.translate( - 'xpack.aiops.logCategorization.randomSamplerSettingsPopUp.onManualCallout.button', - { - defaultMessage: 'Manual sampling', - } - ), - }; - } - }, [randomSamplerPreference]); - return ( = ({ randomSampler, reload }) => { anchorPosition="downLeft" > - - - - - - - setRandomSamplerPreference(e.target.value as RandomSamplerOption)} - /> - - - {randomSamplerPreference === RANDOM_SAMPLER_OPTION.ON_MANUAL ? ( - - ) : null} - - {randomSamplerPreference === RANDOM_SAMPLER_OPTION.ON_AUTOMATIC ? ( - - ) : null} + ); }; - -const ProbabilityUsedMessage: FC<{ samplingProbability: number | null }> = ({ - samplingProbability, -}) => { - return samplingProbability !== null ? ( -
- - - -
- ) : null; -}; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/sampling_panel.tsx b/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/sampling_panel.tsx new file mode 100644 index 0000000000000..f00fdf3baa100 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/sampling_panel.tsx @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FC } from 'react'; +import React, { useCallback, useMemo } from 'react'; +import { EuiFlexItem, EuiSpacer, EuiCallOut, EuiSelect, EuiFormRow } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import useObservable from 'react-use/lib/useObservable'; + +import { RandomSamplerRangeSlider } from './random_sampler_range_slider'; +import type { RandomSampler, RandomSamplerOption } from './random_sampler'; +import { randomSamplerText } from './random_sampler'; +import { RANDOM_SAMPLER_OPTION, RANDOM_SAMPLER_SELECT_OPTIONS } from './random_sampler'; + +interface Props { + randomSampler: RandomSampler; + reload: () => void; +} + +export const SamplingPanel: FC = ({ randomSampler, reload }) => { + const samplingProbability = useObservable( + randomSampler.getProbability$(), + randomSampler.getProbability() + ); + const setSamplingProbability = useCallback( + (probability: number | null) => { + randomSampler.setProbability(probability); + reload(); + }, + [reload, randomSampler] + ); + + const randomSamplerPreference = useObservable(randomSampler.getMode$(), randomSampler.getMode()); + const setRandomSamplerPreference = useCallback( + (mode: RandomSamplerOption) => { + randomSampler.setMode(mode); + reload(); + }, + [randomSampler, reload] + ); + + const { calloutInfoMessage } = useMemo( + () => randomSamplerText(randomSamplerPreference), + [randomSamplerPreference] + ); + + return ( + <> + + + + + + + setRandomSamplerPreference(e.target.value as RandomSamplerOption)} + /> + + + {randomSamplerPreference === RANDOM_SAMPLER_OPTION.ON_MANUAL ? ( + + ) : null} + + {randomSamplerPreference === RANDOM_SAMPLER_OPTION.ON_AUTOMATIC ? ( + + ) : null} + + ); +}; + +const ProbabilityUsedMessage: FC<{ samplingProbability: number | null }> = ({ + samplingProbability, +}) => { + return samplingProbability !== null ? ( +
+ + + +
+ ) : null; +}; diff --git a/x-pack/plugins/aiops/public/embeddable/change_point_chart_initializer.tsx b/x-pack/plugins/aiops/public/embeddables/change_point_chart/change_point_chart_initializer.tsx similarity index 90% rename from x-pack/plugins/aiops/public/embeddable/change_point_chart_initializer.tsx rename to x-pack/plugins/aiops/public/embeddables/change_point_chart/change_point_chart_initializer.tsx index bbcf42a351786..c19328de98b34 100644 --- a/x-pack/plugins/aiops/public/embeddable/change_point_chart_initializer.tsx +++ b/x-pack/plugins/aiops/public/embeddables/change_point_chart/change_point_chart_initializer.tsx @@ -28,16 +28,16 @@ import usePrevious from 'react-use/lib/usePrevious'; import { ChangePointDetectionControlsContextProvider, useChangePointDetectionControlsContext, -} from '../components/change_point_detection/change_point_detection_context'; -import { DEFAULT_AGG_FUNCTION } from '../components/change_point_detection/constants'; -import { FunctionPicker } from '../components/change_point_detection/function_picker'; -import { MaxSeriesControl } from '../components/change_point_detection/max_series_control'; -import { MetricFieldSelector } from '../components/change_point_detection/metric_field_selector'; -import { PartitionsSelector } from '../components/change_point_detection/partitions_selector'; -import { SplitFieldSelector } from '../components/change_point_detection/split_field_selector'; -import { ViewTypeSelector } from '../components/change_point_detection/view_type_selector'; -import { useAiopsAppContext } from '../hooks/use_aiops_app_context'; -import { DataSourceContextProvider } from '../hooks/use_data_source'; +} from '../../components/change_point_detection/change_point_detection_context'; +import { DEFAULT_AGG_FUNCTION } from '../../components/change_point_detection/constants'; +import { FunctionPicker } from '../../components/change_point_detection/function_picker'; +import { MaxSeriesControl } from '../../components/change_point_detection/max_series_control'; +import { MetricFieldSelector } from '../../components/change_point_detection/metric_field_selector'; +import { PartitionsSelector } from '../../components/change_point_detection/partitions_selector'; +import { SplitFieldSelector } from '../../components/change_point_detection/split_field_selector'; +import { ViewTypeSelector } from '../../components/change_point_detection/view_type_selector'; +import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; +import { DataSourceContextProvider } from '../../hooks/use_data_source'; import { DEFAULT_SERIES } from './const'; import type { EmbeddableChangePointChartInput } from './embeddable_change_point_chart'; import type { EmbeddableChangePointChartProps } from './embeddable_change_point_chart_component'; diff --git a/x-pack/plugins/aiops/public/embeddable/const.ts b/x-pack/plugins/aiops/public/embeddables/change_point_chart/const.ts similarity index 100% rename from x-pack/plugins/aiops/public/embeddable/const.ts rename to x-pack/plugins/aiops/public/embeddables/change_point_chart/const.ts diff --git a/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart.tsx b/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart.tsx similarity index 95% rename from x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart.tsx rename to x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart.tsx index 38a31b35049a8..e6cf83cc79136 100644 --- a/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart.tsx +++ b/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart.tsx @@ -23,9 +23,9 @@ import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/common'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { EmbeddableInputTracker } from './embeddable_chart_component_wrapper'; -import type { EmbeddableChangePointType } from '../../common/constants'; -import { EMBEDDABLE_ORIGIN } from '../../common/constants'; -import { AiopsAppContext, type AiopsAppDependencies } from '../hooks/use_aiops_app_context'; +import type { EmbeddableChangePointType } from '../../../common/constants'; +import { EMBEDDABLE_ORIGIN } from '../../../common/constants'; +import { AiopsAppContext, type AiopsAppDependencies } from '../../hooks/use_aiops_app_context'; import type { EmbeddableChangePointChartProps } from './embeddable_change_point_chart_component'; diff --git a/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart_component.tsx b/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_component.tsx similarity index 92% rename from x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart_component.tsx rename to x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_component.tsx index c2dca6215abbd..68e2e18123b22 100644 --- a/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart_component.tsx +++ b/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_component.tsx @@ -14,10 +14,10 @@ import { EuiLoadingChart } from '@elastic/eui'; import { type ChangePointDetectionViewType, type EmbeddableChangePointType, -} from '../../common/constants'; -import type { AiopsPluginStartDeps } from '../types'; +} from '../../../common/constants'; +import type { AiopsPluginStartDeps } from '../../types'; import type { EmbeddableChangePointChartInput } from './embeddable_change_point_chart'; -import type { ChangePointAnnotation } from '../components/change_point_detection/change_point_detection_context'; +import type { ChangePointAnnotation } from '../../components/change_point_detection/change_point_detection_context'; export interface EmbeddableChangePointChartProps { viewType?: ChangePointDetectionViewType; diff --git a/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart_factory.ts b/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_factory.ts similarity index 98% rename from x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart_factory.ts rename to x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_factory.ts index feda6673f5df5..31515b442acad 100644 --- a/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart_factory.ts +++ b/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_factory.ts @@ -13,8 +13,8 @@ import type { StartServicesAccessor } from '@kbn/core-lifecycle-browser'; import type { EMBEDDABLE_CHANGE_POINT_CHART_TYPE, EmbeddableChangePointType, -} from '../../common/constants'; -import type { AiopsPluginStart, AiopsPluginStartDeps } from '../types'; +} from '../../../common/constants'; +import type { AiopsPluginStart, AiopsPluginStartDeps } from '../../types'; import type { EmbeddableChangePointChartInput } from './embeddable_change_point_chart'; import { EmbeddableChangePointChart } from './embeddable_change_point_chart'; diff --git a/x-pack/plugins/aiops/public/embeddable/embeddable_chart_component_wrapper.tsx b/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_chart_component_wrapper.tsx similarity index 88% rename from x-pack/plugins/aiops/public/embeddable/embeddable_chart_component_wrapper.tsx rename to x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_chart_component_wrapper.tsx index 03509938b05bd..acaed408b4327 100644 --- a/x-pack/plugins/aiops/public/embeddable/embeddable_chart_component_wrapper.tsx +++ b/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_chart_component_wrapper.tsx @@ -11,26 +11,26 @@ import type { FC } from 'react'; import React, { useEffect, useMemo, useState } from 'react'; import { css } from '@emotion/react'; import useObservable from 'react-use/lib/useObservable'; -import { ChangePointsTable } from '../components/change_point_detection/change_points_table'; -import { CHANGE_POINT_DETECTION_VIEW_TYPE } from '../../common/constants'; -import { ReloadContextProvider } from '../hooks/use_reload'; +import { ChangePointsTable } from '../../components/change_point_detection/change_points_table'; +import { CHANGE_POINT_DETECTION_VIEW_TYPE } from '../../../common/constants'; +import { ReloadContextProvider } from '../../hooks/use_reload'; import { type ChangePointAnnotation, ChangePointDetectionControlsContextProvider, type ChangePointDetectionRequestParams, -} from '../components/change_point_detection/change_point_detection_context'; +} from '../../components/change_point_detection/change_point_detection_context'; import type { EmbeddableChangePointChartInput, EmbeddableChangePointChartOutput, } from './embeddable_change_point_chart'; import type { EmbeddableChangePointChartProps } from './embeddable_change_point_chart_component'; -import { FilterQueryContextProvider, useFilterQueryUpdates } from '../hooks/use_filters_query'; -import { DataSourceContextProvider, useDataSource } from '../hooks/use_data_source'; -import { useAiopsAppContext } from '../hooks/use_aiops_app_context'; -import { createMergedEsQuery } from '../application/utils/search_utils'; -import { useChangePointResults } from '../components/change_point_detection/use_change_point_agg_request'; -import { ChartsGrid } from '../components/change_point_detection/charts_grid'; -import { NoChangePointsWarning } from '../components/change_point_detection/no_change_points_warning'; +import { FilterQueryContextProvider, useFilterQueryUpdates } from '../../hooks/use_filters_query'; +import { DataSourceContextProvider, useDataSource } from '../../hooks/use_data_source'; +import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; +import { createMergedEsQuery } from '../../application/utils/search_utils'; +import { useChangePointResults } from '../../components/change_point_detection/use_change_point_agg_request'; +import { ChartsGrid } from '../../components/change_point_detection/charts_grid'; +import { NoChangePointsWarning } from '../../components/change_point_detection/no_change_points_warning'; const defaultSort = { field: 'p_value' as keyof ChangePointAnnotation, diff --git a/x-pack/plugins/aiops/public/embeddable/handle_explicit_input.tsx b/x-pack/plugins/aiops/public/embeddables/change_point_chart/handle_explicit_input.tsx similarity index 90% rename from x-pack/plugins/aiops/public/embeddable/handle_explicit_input.tsx rename to x-pack/plugins/aiops/public/embeddables/change_point_chart/handle_explicit_input.tsx index cd64daee4b849..1ee80ae90a061 100644 --- a/x-pack/plugins/aiops/public/embeddable/handle_explicit_input.tsx +++ b/x-pack/plugins/aiops/public/embeddables/change_point_chart/handle_explicit_input.tsx @@ -9,9 +9,9 @@ import type { CoreStart } from '@kbn/core/public'; import { toMountPoint } from '@kbn/react-kibana-mount'; import React from 'react'; import type { EmbeddableChangePointChartExplicitInput } from './types'; -import type { AiopsAppDependencies } from '..'; -import { AiopsAppContext } from '../hooks/use_aiops_app_context'; -import type { AiopsPluginStartDeps } from '../types'; +import type { AiopsAppDependencies } from '../..'; +import { AiopsAppContext } from '../../hooks/use_aiops_app_context'; +import type { AiopsPluginStartDeps } from '../../types'; import { ChangePointChartInitializer } from './change_point_chart_initializer'; import type { EmbeddableChangePointChartInput } from './embeddable_change_point_chart'; diff --git a/x-pack/plugins/aiops/public/embeddable/index.ts b/x-pack/plugins/aiops/public/embeddables/change_point_chart/index.ts similarity index 72% rename from x-pack/plugins/aiops/public/embeddable/index.ts rename to x-pack/plugins/aiops/public/embeddables/change_point_chart/index.ts index d742a1321c101..53f38418f9fe4 100644 --- a/x-pack/plugins/aiops/public/embeddable/index.ts +++ b/x-pack/plugins/aiops/public/embeddables/change_point_chart/index.ts @@ -6,4 +6,7 @@ */ export { EmbeddableChangePointChartFactory } from './embeddable_change_point_chart_factory'; -export { type EmbeddableChangePointChartProps } from './embeddable_change_point_chart_component'; +export { + type EmbeddableChangePointChartProps, + getEmbeddableChangePointChart, +} from './embeddable_change_point_chart_component'; diff --git a/x-pack/plugins/aiops/public/embeddable/types.ts b/x-pack/plugins/aiops/public/embeddables/change_point_chart/types.ts similarity index 89% rename from x-pack/plugins/aiops/public/embeddable/types.ts rename to x-pack/plugins/aiops/public/embeddables/change_point_chart/types.ts index 783bffa7ca9a8..a13eb415cd614 100644 --- a/x-pack/plugins/aiops/public/embeddable/types.ts +++ b/x-pack/plugins/aiops/public/embeddables/change_point_chart/types.ts @@ -7,7 +7,7 @@ import type { FC } from 'react'; import type { IEmbeddable } from '@kbn/embeddable-plugin/public'; -import type { SelectedChangePoint } from '../components/change_point_detection/change_point_detection_context'; +import type { SelectedChangePoint } from '../../components/change_point_detection/change_point_detection_context'; import type { EmbeddableChangePointChartInput, EmbeddableChangePointChartOutput, diff --git a/x-pack/plugins/aiops/public/embeddables/log_categorization/index.ts b/x-pack/plugins/aiops/public/embeddables/log_categorization/index.ts new file mode 100644 index 0000000000000..bddf0723be84b --- /dev/null +++ b/x-pack/plugins/aiops/public/embeddables/log_categorization/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { EmbeddableLogCategorizationFactory } from './log_categorization_embeddable_factory'; diff --git a/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx b/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx new file mode 100644 index 0000000000000..fcfcba56f2dac --- /dev/null +++ b/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx @@ -0,0 +1,201 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { Suspense } from 'react'; +import ReactDOM from 'react-dom'; +import type { EmbeddableInput, EmbeddableOutput, IContainer } from '@kbn/embeddable-plugin/public'; +import { Embeddable as AbstractEmbeddable } from '@kbn/embeddable-plugin/public'; +import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; +import type { ThemeServiceStart } from '@kbn/core-theme-browser'; +import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import { UI_SETTINGS } from '@kbn/data-plugin/public'; +import type { IUiSettingsClient } from '@kbn/core/public'; +import { type CoreStart } from '@kbn/core/public'; +import { DatePickerContextProvider } from '@kbn/ml-date-picker'; +import { pick } from 'lodash'; +import type { LensPublicStart } from '@kbn/lens-plugin/public'; +import { Subject } from 'rxjs'; +import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; +import type { DataView } from '@kbn/data-views-plugin/common'; +import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; +// import { EmbeddableInputTracker } from './embeddable_chart_component_wrapper'; +import type { SavedSearch } from '@kbn/saved-search-plugin/public'; +import type { Filter } from '@kbn/es-query'; +import type { Query, AggregateQuery } from '@kbn/es-query'; +import { StorageContextProvider } from '@kbn/ml-local-storage'; +import { Storage } from '@kbn/kibana-utils-plugin/public'; +import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; +import type { EmbeddableLogCategorizationType } from '../../../common/constants'; +import { EMBEDDABLE_ORIGIN } from '../../../common/constants'; +import { AiopsAppContext, type AiopsAppDependencies } from '../../hooks/use_aiops_app_context'; +import { AIOPS_STORAGE_KEYS } from '../../types/storage'; +import { LogCategorizationEmbeddable } from '../../components/log_categorization/log_categorization_for_embeddable'; + +// import type { EmbeddableChangePointChartProps } from './embeddable_change_point_chart_component'; + +interface EmbeddableLogCategorizationProps { + dataView: DataView; + savedSearch?: SavedSearch | null; + query?: T; + filters?: Filter[]; + id?: string; + embeddingOrigin?: string; + /** + * Callback to add a filter to filter bar + */ + onAddFilter?: () => void; + getViewModeToggle: () => React.ReactElement | undefined; +} + +const localStorage = new Storage(window.localStorage); + +export type EmbeddableLogCategorizationInput = EmbeddableInput & EmbeddableLogCategorizationProps; + +export type EmbeddableLogCategorizationOutput = EmbeddableOutput & { indexPatterns?: DataView[] }; + +export interface EmbeddableLogCategorizationDeps { + theme: ThemeServiceStart; + data: DataPublicPluginStart; + uiSettings: IUiSettingsClient; + http: CoreStart['http']; + notifications: CoreStart['notifications']; + i18n: CoreStart['i18n']; + lens: LensPublicStart; + usageCollection: UsageCollectionSetup; + fieldFormats: FieldFormatsStart; + application: CoreStart['application']; + charts: ChartsPluginStart; +} + +export type IEmbeddableLogCategorization = typeof EmbeddableLogCategorization; + +export class EmbeddableLogCategorization extends AbstractEmbeddable< + EmbeddableLogCategorizationInput, + EmbeddableLogCategorizationOutput +> { + private reload$ = new Subject(); + + public reload(): void { + this.reload$.next(Date.now()); + } + + private node?: HTMLElement; + + // Need to defer embeddable load in order to resolve data views + deferEmbeddableLoad = true; + + constructor( + public readonly type: EmbeddableLogCategorizationType, + private readonly deps: EmbeddableLogCategorizationDeps, + initialInput: EmbeddableLogCategorizationInput, + parent?: IContainer + ) { + super(initialInput, {}, parent); + + this.initOutput().finally(() => this.setInitializationFinished()); + } + + private async initOutput() { + // const { + // data: { dataViews: dataViewsService }, + // } = this.deps; + + const { dataView } = this.getInput(); + + // const dataView = await dataViewsService.get(dataViewId); + + this.updateOutput({ + indexPatterns: [dataView], + }); + } + + public reportsEmbeddableLoad() { + return true; + } + + public onLoading(isLoading: boolean) { + this.renderComplete.dispatchInProgress(); + this.updateOutput({ loading: isLoading, error: undefined }); + } + + public onError(error: Error) { + this.renderComplete.dispatchError(); + this.updateOutput({ loading: false, error: { name: error.name, message: error.message } }); + } + + public onRenderComplete() { + this.renderComplete.dispatchComplete(); + this.updateOutput({ loading: false, rendered: true, error: undefined }); + } + + render(el: HTMLElement): void { + super.render(el); + + this.node = el; + // required for the export feature to work + this.node.setAttribute('data-shared-item', ''); + + // test subject selector for functional tests + this.node.setAttribute('data-test-subj', 'aiopsEmbeddableChangePointChart'); + + const I18nContext = this.deps.i18n.Context; + + const datePickerDeps = { + ...pick(this.deps, ['data', 'http', 'notifications', 'theme', 'uiSettings', 'i18n']), + uiSettingsKeys: UI_SETTINGS, + }; + + const input = this.getInput(); + const input$ = this.getInput$(); + const field = input.dataView.fields.find((f) => f.name === 'message')!; + + const aiopsAppContextValue = { + embeddingOrigin: this.parent?.type ?? input.embeddingOrigin ?? EMBEDDABLE_ORIGIN, + ...this.deps, + } as unknown as AiopsAppDependencies; + + ReactDOM.render( + + + + + + + this.destroy()} + embeddingOrigin={'discover-change-me'} + onAddFilter={input.onAddFilter ?? (() => {})} + getViewModeToggle={input.getViewModeToggle} + /> + {/* */} + + + + + + , + el + ); + } + + public destroy() { + super.destroy(); + if (this.node) { + ReactDOM.unmountComponentAtNode(this.node); + } + } +} diff --git a/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable_factory.ts b/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable_factory.ts new file mode 100644 index 0000000000000..659f5e34bf509 --- /dev/null +++ b/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable_factory.ts @@ -0,0 +1,85 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import type { StartServicesAccessor } from '@kbn/core/public'; +import type { EmbeddableFactoryDefinition, IContainer } from '@kbn/embeddable-plugin/public'; +// import { DATA_VISUALIZER_GRID_EMBEDDABLE_TYPE } from './constants'; +// import type { DataVisualizerGridEmbeddableServices } from './grid_embeddable'; +// import type { DataVisualizerGridEmbeddableInput } from './types'; +// import type { EmbeddableLogCategorizationType } from '../../../common/constants'; +import { EMBEDDABLE_LOG_CATEGORIZATION_TYPE } from '../../../common/constants'; +import type { AiopsPluginStart, AiopsPluginStartDeps } from '../../types'; +import type { + EmbeddableLogCategorizationDeps, + EmbeddableLogCategorizationInput, +} from './log_categorization_embeddable'; + +export class EmbeddableLogCategorizationFactory + implements EmbeddableFactoryDefinition +{ + public readonly type = EMBEDDABLE_LOG_CATEGORIZATION_TYPE; + + public readonly grouping = [ + { + id: 'data_visualizer_grid', + getDisplayName: () => 'Data Visualizer Grid', + }, + ]; + + constructor( + private readonly name: string, + private readonly getStartServices: StartServicesAccessor + ) {} + + public async isEditable() { + return false; + } + + public canCreateNew() { + return false; + } + + public getDisplayName() { + return i18n.translate('xpack.dataVisualizer.index.components.grid.displayName', { + defaultMessage: 'Data visualizer grid', + }); + } + + public getDescription() { + return i18n.translate('xpack.dataVisualizer.index.components.grid.description', { + defaultMessage: 'Visualize data', + }); + } + + private async getServices(): Promise { + const [ + { i18n: i18nService, theme, http, uiSettings, notifications, application }, + { lens, data, usageCollection, fieldFormats, charts }, + ] = await this.getStartServices(); + + return { + i18n: i18nService, + theme, + data, + uiSettings, + http, + notifications, + lens, + usageCollection, + fieldFormats, + application, + charts, + }; + } + + public async create(input: EmbeddableLogCategorizationInput, parent?: IContainer) { + const deps = await this.getServices(); + const { EmbeddableLogCategorization } = await import('./log_categorization_embeddable'); + return new EmbeddableLogCategorization(this.type, deps, input, parent); + } +} diff --git a/x-pack/plugins/aiops/public/embeddable/register_embeddable.ts b/x-pack/plugins/aiops/public/embeddables/register_embeddables.ts similarity index 57% rename from x-pack/plugins/aiops/public/embeddable/register_embeddable.ts rename to x-pack/plugins/aiops/public/embeddables/register_embeddables.ts index 219b628552557..ddaef62ab8eda 100644 --- a/x-pack/plugins/aiops/public/embeddable/register_embeddable.ts +++ b/x-pack/plugins/aiops/public/embeddables/register_embeddables.ts @@ -10,9 +10,10 @@ import type { EmbeddableSetup } from '@kbn/embeddable-plugin/public'; import { i18n } from '@kbn/i18n'; import { EMBEDDABLE_CHANGE_POINT_CHART_TYPE } from '../../common/constants'; import type { AiopsPluginStart, AiopsPluginStartDeps } from '../types'; -import { EmbeddableChangePointChartFactory } from './embeddable_change_point_chart_factory'; +import { EmbeddableChangePointChartFactory } from './change_point_chart/embeddable_change_point_chart_factory'; +import { EmbeddableLogCategorizationFactory } from './log_categorization/log_categorization_embeddable_factory'; -export const registerEmbeddable = ( +export const registerChangePointChartEmbeddable = ( core: CoreSetup, embeddable: EmbeddableSetup ) => { @@ -25,3 +26,16 @@ export const registerEmbeddable = ( ); embeddable.registerEmbeddableFactory(changePointChartFactory.type, changePointChartFactory); }; + +export const registerLogCategorizationEmbeddable = ( + core: CoreSetup, + embeddable: EmbeddableSetup +) => { + const changePointChartFactory = new EmbeddableLogCategorizationFactory( + i18n.translate('xpack.aiops.embeddableChangePointChartDisplayName', { + defaultMessage: 'Change point detection', + }), + core.getStartServices + ); + embeddable.registerEmbeddableFactory(changePointChartFactory.type, changePointChartFactory); +}; diff --git a/x-pack/plugins/aiops/public/plugin.tsx b/x-pack/plugins/aiops/public/plugin.tsx index e9f470cd6e04d..f10662fcdd02b 100755 --- a/x-pack/plugins/aiops/public/plugin.tsx +++ b/x-pack/plugins/aiops/public/plugin.tsx @@ -15,7 +15,7 @@ import type { AiopsPluginStart, AiopsPluginStartDeps, } from './types'; -import { getEmbeddableChangePointChart } from './embeddable/embeddable_change_point_chart_component'; +import { getEmbeddableChangePointChart } from './embeddables/change_point_chart'; export type AiopsCoreSetup = CoreSetup; @@ -28,21 +28,22 @@ export class AiopsPlugin ) { Promise.all([ firstValueFrom(licensing.license$), - import('./embeddable/register_embeddable'), + import('./embeddables/register_embeddables'), import('./ui_actions'), import('./cases/register_change_point_charts_attachment'), core.getStartServices(), ]).then( ([ license, - { registerEmbeddable }, + { registerChangePointChartEmbeddable, registerLogCategorizationEmbeddable }, { registerAiopsUiActions }, { registerChangePointChartsAttachment }, [coreStart, pluginStart], ]) => { if (license.hasAtLeast('platinum')) { if (embeddable) { - registerEmbeddable(core, embeddable); + registerChangePointChartEmbeddable(core, embeddable); + registerLogCategorizationEmbeddable(core, embeddable); } if (uiActions) { diff --git a/x-pack/plugins/aiops/public/ui_actions/edit_change_point_charts_panel.tsx b/x-pack/plugins/aiops/public/ui_actions/edit_change_point_charts_panel.tsx index d9401f6064cbf..f01d889722267 100644 --- a/x-pack/plugins/aiops/public/ui_actions/edit_change_point_charts_panel.tsx +++ b/x-pack/plugins/aiops/public/ui_actions/edit_change_point_charts_panel.tsx @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import { ViewMode } from '@kbn/embeddable-plugin/common'; import type { CoreStart } from '@kbn/core/public'; import { EMBEDDABLE_CHANGE_POINT_CHART_TYPE } from '../../common/constants'; -import type { EditChangePointChartsPanelContext } from '../embeddable/types'; +import type { EditChangePointChartsPanelContext } from '../embeddables/change_point_chart/types'; import type { AiopsPluginStartDeps } from '../types'; export const EDIT_CHANGE_POINT_CHARTS_ACTION = 'editChangePointChartsPanelAction'; @@ -36,7 +36,7 @@ export function createEditChangePointChartsPanelAction( try { const { resolveEmbeddableChangePointUserInput } = await import( - '../embeddable/handle_explicit_input' + '../embeddables/change_point_chart/handle_explicit_input' ); const result = await resolveEmbeddableChangePointUserInput( From aa0b8cc7ea0ddfedc77cfa2e487b8883b8b5b2ec Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 22 Mar 2024 16:52:39 +0000 Subject: [PATCH 02/92] adding wider time range selection --- x-pack/packages/ml/date_picker/index.ts | 1 + .../create_category_request.ts | 30 ++- .../process_category_results.ts | 22 ++- .../common/api/log_categorization/types.ts | 10 +- .../log_categorization/embeddable_menu.tsx | 73 ++++--- .../log_categorization/format_category.tsx | 26 ++- .../log_categorization_for_embeddable.tsx | 186 +++++++++--------- .../sampling_menu/random_sampler.ts | 22 ++- .../sampling_menu/sampling_panel.tsx | 8 +- .../use_wider_time_range.ts | 75 +++++++ x-pack/plugins/aiops/public/types/storage.ts | 6 + 11 files changed, 293 insertions(+), 166 deletions(-) create mode 100644 x-pack/plugins/aiops/public/components/log_categorization/use_wider_time_range.ts diff --git a/x-pack/packages/ml/date_picker/index.ts b/x-pack/packages/ml/date_picker/index.ts index 1a949a5d1e1d1..b68ce8d9314c0 100644 --- a/x-pack/packages/ml/date_picker/index.ts +++ b/x-pack/packages/ml/date_picker/index.ts @@ -5,6 +5,7 @@ * 2.0. */ +export { getTimeFieldRange } from './src/services/time_field_range'; export { DatePickerContextProvider, type DatePickerDependencies, diff --git a/x-pack/plugins/aiops/common/api/log_categorization/create_category_request.ts b/x-pack/plugins/aiops/common/api/log_categorization/create_category_request.ts index 0a15fd50e1c4b..645b1e5c33491 100644 --- a/x-pack/plugins/aiops/common/api/log_categorization/create_category_request.ts +++ b/x-pack/plugins/aiops/common/api/log_categorization/create_category_request.ts @@ -50,16 +50,16 @@ export function createCategoryRequest( _source: field, }, }, - ...(intervalMs - ? { - sparkline: { - date_histogram: { - field: timeField, - fixed_interval: `${intervalMs}ms`, - }, - }, - } - : {}), + // ...(intervalMs + // ? { + // sparkline: { + // date_histogram: { + // field: timeField, + // fixed_interval: `${intervalMs}ms`, + // }, + // }, + // } + // : {}), ...(additionalFilter ? { sub_time_range: { @@ -76,6 +76,16 @@ export function createCategoryRequest( _source: field, }, }, + ...(intervalMs + ? { + sparkline: { + date_histogram: { + field: timeField, + fixed_interval: `${intervalMs}ms`, + }, + }, + } + : {}), ...(additionalFilter.field ? { sub_field: { diff --git a/x-pack/plugins/aiops/common/api/log_categorization/process_category_results.ts b/x-pack/plugins/aiops/common/api/log_categorization/process_category_results.ts index af98fa0d71e02..6a98c0ac4a5ea 100644 --- a/x-pack/plugins/aiops/common/api/log_categorization/process_category_results.ts +++ b/x-pack/plugins/aiops/common/api/log_categorization/process_category_results.ts @@ -11,7 +11,7 @@ import type { estypes } from '@elastic/elasticsearch'; import type { createRandomSamplerWrapper } from '@kbn/ml-random-sampler-utils'; -import type { Category, CategoriesAgg, CatResponse } from './types'; +import type { Category, CategoriesAgg, CatResponse, Sparkline } from './types'; export function processCategoryResults( result: CatResponse, @@ -29,24 +29,17 @@ export function processCategoryResults( ) as CategoriesAgg; const categories: Category[] = buckets.map((b) => { - const sparkline = - b.sparkline === undefined - ? {} - : b.sparkline.buckets.reduce>((acc2, cur2) => { - acc2[cur2.key] = cur2.doc_count; - return acc2; - }, {}); - return { key: b.key, count: b.doc_count, examples: b.examples.hits.hits.map((h) => get(h._source, field)), - sparkline, + sparkline: getSparkline(b.sparkline), subTimeRangeCount: b.sub_time_range?.buckets[0].doc_count ?? undefined, subFieldCount: b.sub_time_range?.buckets[0].sub_field?.doc_count ?? undefined, subFieldExamples: b.sub_time_range?.buckets[0].examples.hits.hits.map((h) => get(h._source, field)) ?? undefined, + subFieldSparkline: getSparkline(b.sub_time_range?.buckets[0].sparkline), regex: b.regex, }; }); @@ -59,3 +52,12 @@ export function processCategoryResults( hasExamples, }; } + +function getSparkline(sparkline?: Sparkline) { + return sparkline === undefined + ? {} + : sparkline.buckets.reduce>((acc, cur) => { + acc[cur.key] = cur.doc_count; + return acc; + }, {}); +} diff --git a/x-pack/plugins/aiops/common/api/log_categorization/types.ts b/x-pack/plugins/aiops/common/api/log_categorization/types.ts index 06179262946e2..2ea11170df226 100644 --- a/x-pack/plugins/aiops/common/api/log_categorization/types.ts +++ b/x-pack/plugins/aiops/common/api/log_categorization/types.ts @@ -13,6 +13,7 @@ export interface Category { subTimeRangeCount?: number; subFieldCount?: number; subFieldExamples?: string[]; + subFieldSparkline?: Record; examples: string[]; sparkline?: Record; regex: string; @@ -22,6 +23,10 @@ interface CategoryExamples { hits: { hits: Array<{ _source: { message: string } }> }; } +export interface Sparkline { + buckets: Array<{ key_as_string: string; key: number; doc_count: number }>; +} + export interface CategoriesAgg { categories: { buckets: Array<{ @@ -29,9 +34,7 @@ export interface CategoriesAgg { doc_count: number; examples: CategoryExamples; regex: string; - sparkline: { - buckets: Array<{ key_as_string: string; key: number; doc_count: number }>; - }; + sparkline: Sparkline; sub_time_range?: { buckets: Array<{ key: number; @@ -44,6 +47,7 @@ export interface CategoriesAgg { doc_count: number; }; examples: CategoryExamples; + sparkline: Sparkline; }>; }; }>; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx b/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx index 1adff6ebbf55a..13db868376799 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx @@ -5,11 +5,10 @@ * 2.0. */ -import type { EuiComboBoxOptionOption } from '@elastic/eui'; +import { EuiSuperSelect } from '@elastic/eui'; import { EuiFormRow } from '@elastic/eui'; import { EuiButtonEmpty, - EuiComboBox, EuiPanel, EuiPopover, EuiSpacer, @@ -17,18 +16,23 @@ import { EuiHorizontalRule, } from '@elastic/eui'; import type { FC } from 'react'; -import { useCallback } from 'react'; import React, { useState, useMemo } from 'react'; import type { DataViewField } from '@kbn/data-views-plugin/public'; import { i18n } from '@kbn/i18n'; +import type { Category } from '../../../common/api/log_categorization/types'; import type { RandomSampler } from './sampling_menu'; import { SamplingPanel } from './sampling_menu/sampling_panel'; +import type { WidenessOption } from './log_categorization_for_embeddable'; +import { WIDENESS } from './log_categorization_for_embeddable'; interface Props { randomSampler: RandomSampler; fields: DataViewField[]; selectedField: DataViewField | null; setSelectedField: (field: DataViewField) => void; + widenessOption: WidenessOption; + setWidenessOption: (w: WidenessOption) => void; + categories: Category[] | undefined; reload: () => void; } @@ -37,33 +41,23 @@ export const EmbeddableMenu: FC = ({ fields, selectedField, setSelectedField, + widenessOption, + setWidenessOption, + categories, reload, }) => { const [showPopover, setShowPopover] = useState(false); const togglePopover = () => setShowPopover(!showPopover); const fieldOptions = useMemo( - () => fields.map((field) => ({ label: field.name, field })), + () => fields.map((field) => ({ inputDisplay: field.name, value: field })), [fields] ); - const onSelectedFieldChange = useCallback( - (selectedOptions: EuiComboBoxOptionOption[]) => { - if (selectedOptions.length) { - const field = fields.find((f) => f.name === selectedOptions[0].label); - if (field) { - setSelectedField(field); - } - } - }, - [fields, setSelectedField] - ); - - const selectedFieldOptions = useMemo(() => { - if (selectedField) { - return [{ label: selectedField.name }]; - } - }, [selectedField]); + const widenessOptions = Object.keys(WIDENESS).map((value) => ({ + inputDisplay: value, + value: value as WidenessOption, + })); const button = ( = ({ } )} > - + + + + + + {categories !== undefined ? ( + <> + Total categories in {widenessOption}: {categories.length} + + ) : null} + + } + > + diff --git a/x-pack/plugins/aiops/public/components/log_categorization/format_category.tsx b/x-pack/plugins/aiops/public/components/log_categorization/format_category.tsx index 2d801d1fa161a..357ef73adb6bd 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/format_category.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/format_category.tsx @@ -95,12 +95,15 @@ export const useCreateFormattedExample = () => { const elements: JSX.Element[] = []; let pos = 0; for (let i = 0; i < positions.length; i++) { + const elementKey = `${key}-token-${i}`; elements.push( - {tempExample.substring(pos, positions[i].start)} + + {tempExample.substring(pos, positions[i].start)} + ); elements.push( - + {tempExample.substring(positions[i].start, positions[i].end)} ); @@ -108,7 +111,7 @@ export const useCreateFormattedExample = () => { } elements.push( - + {tempExample.substring(positions[positions.length - 1].end)} ); @@ -131,10 +134,10 @@ export const FormattedPatternExamples: FC = ({ category, count }) => { .fill(0) .map((_, i) => createFormattedExample(key, examples[i])); return formattedExamples.map((example, i) => ( - <> + {example} {i < formattedExamples.length - 1 ? : null} - + )); }, [category, count, createFormattedExample]); @@ -150,10 +153,19 @@ export const FormattedRegex: FC = ({ category }) => { const elements: JSX.Element[] = []; for (let i = 0; i < regexTokens.length; i++) { const token = regexTokens[i]; + const key = `regex-${i}`; if (token.match(/\.\*\?|\.\+\?/)) { - elements.push({token}); + elements.push( + + {token} + + ); } else { - elements.push({token}); + elements.push( + + {token} + + ); } } return elements; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx index 519bbcad2c287..8e7617cfdd33e 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx @@ -7,24 +7,11 @@ import type { FC } from 'react'; import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react'; -import { - // EuiTitle, - // EuiFlyoutHeader, - // EuiFlyoutBody, - EuiFlexGroup, - EuiFlexItem, - useEuiTheme, - EuiTabs, - EuiTab, - EuiSpacer, - EuiToolTip, - EuiIcon, -} from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, useEuiTheme } from '@elastic/eui'; import type { SavedSearch } from '@kbn/saved-search-plugin/public'; import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; import type { Filter } from '@kbn/es-query'; import { buildEmptyFilter } from '@kbn/es-query'; import { usePageUrlState } from '@kbn/ml-url-state'; @@ -32,6 +19,10 @@ import type { FieldValidationResults } from '@kbn/ml-category-validator'; import { stringHash } from '@kbn/ml-string-hash'; import useMount from 'react-use/lib/useMount'; import { ES_FIELD_TYPES } from '@kbn/field-types'; +import type { unitOfTime } from 'moment'; +import moment from 'moment'; +import { useStorage } from '@kbn/ml-local-storage'; + import type { CategorizationAdditionalFilter } from '../../../common/api/log_categorization/create_category_request'; import { AIOPS_TELEMETRY_ID } from '../../../common/constants'; @@ -57,13 +48,26 @@ import { useValidateFieldRequest } from './use_validate_category_field'; import { FieldValidationCallout } from './category_validation_callout'; import type { DocumentStats } from '../../hooks/use_document_count_stats'; import { EmbeddableMenu } from './embeddable_menu'; -// import { CreateCategorizationJobButton } from './create_categorization_job'; +import { useWiderTimeRange } from './use_wider_time_range'; +import type { AiOpsKey, AiOpsStorageMapped } from '../../types/storage'; +import { AIOPS_PATTERN_ANALYSIS_WIDENESS_PREFERENCE } from '../../types/storage'; enum SELECTED_TAB { BUCKET, FULL_TIME_RANGE, } +export type WidenessOption = '1 week' | '1 month' | '3 months' | '6 months'; + +type Wideness = Record; + +export const WIDENESS: Wideness = { + '1 week': { factor: 1, unit: 'w' }, + '1 month': { factor: 1, unit: 'M' }, + '3 months': { factor: 3, unit: 'M' }, + '6 months': { factor: 6, unit: 'M' }, +}; + export interface LogCategorizationPageProps { dataView: DataView; savedSearch: SavedSearch | null; @@ -94,8 +98,14 @@ export const LogCategorizationEmbeddable: FC = ({ uiSettings, } = useAiopsAppContext(); + const [widenessOption, setWidenessOption] = useStorage< + AiOpsKey, + AiOpsStorageMapped + >(AIOPS_PATTERN_ANALYSIS_WIDENESS_PREFERENCE, '1 week'); + const { runValidateFieldRequest, cancelRequest: cancelValidationRequest } = useValidateFieldRequest(); + const { getWiderTimeRange, cancelRequest: cancelWiderTimeRangeRequest } = useWiderTimeRange(); const { euiTheme } = useEuiTheme(); const { filters, query } = useMemo(() => getState(), [getState]); @@ -113,6 +123,7 @@ export const LogCategorizationEmbeddable: FC = ({ ); const [selectedCategory, setSelectedCategory] = useState(null); const [selectedField, setSelectedField] = useState(null); + // const [wideness, setWideness] = useState('1 week'); const [fields, setFields] = useState([]); const [selectedSavedSearch /* , setSelectedSavedSearch*/] = useState(savedSearch); const [previousDocumentStatsHash, setPreviousDocumentStatsHash] = useState(0); @@ -131,14 +142,24 @@ export const LogCategorizationEmbeddable: FC = ({ const [selectedTab, setSelectedTab] = useState(SELECTED_TAB.FULL_TIME_RANGE); const cancelRequest = useCallback(() => { + cancelWiderTimeRangeRequest(); cancelValidationRequest(); cancelCategorizationRequest(); - }, [cancelCategorizationRequest, cancelValidationRequest]); + }, [cancelCategorizationRequest, cancelValidationRequest, cancelWiderTimeRangeRequest]); useMount(function loadFields() { const dataViewFields = dataView.fields.filter((f) => f.esTypes?.includes(ES_FIELD_TYPES.TEXT)); setFields(dataViewFields); - const messageField = dataViewFields.find((f) => f.name === 'message'); + let messageField = dataViewFields.find((f) => f.name === 'message'); + if (messageField === undefined) { + messageField = dataViewFields.find((f) => f.name === 'error.message'); + } + if (messageField === undefined) { + messageField = dataViewFields.find((f) => f.name === 'event.original '); + } + if (messageField === undefined) { + messageField = dataViewFields[0]; + } if (messageField !== undefined) { setSelectedField(messageField); } @@ -193,11 +214,36 @@ export const LogCategorizationEmbeddable: FC = ({ setData(null); setFieldValidationResult(null); - const timeRange = { + const tempAdditionalFilter: CategorizationAdditionalFilter = { from: earliest, to: latest, }; + // const timeRange: TimeRange = { + // from: moment(earliest).add(-5, 'w').valueOf(), + // to: latest, + // }; + const timeRange = await getWiderTimeRange( + index, + timeField, + tempAdditionalFilter, + widenessOption, + searchQuery + ); + // eslint-disable-next-line no-console + console.log( + 'sub agg s', + moment(tempAdditionalFilter.from).toISOString(), + moment(tempAdditionalFilter.to).toISOString() + ); + + // eslint-disable-next-line no-console + console.log( + 'full range', + moment(timeRange.from).toISOString(), + moment(timeRange.to).toISOString() + ); + try { const [validationResult, categorizationResult] = await Promise.all([ runValidateFieldRequest(index, selectedField.name, timeField, timeRange, searchQuery, { @@ -210,7 +256,7 @@ export const LogCategorizationEmbeddable: FC = ({ timeRange, searchQuery, intervalMs, - additionalFilter + tempAdditionalFilter ), ]); @@ -220,18 +266,23 @@ export const LogCategorizationEmbeddable: FC = ({ const hasBucketCategories = categories.some((c) => c.subTimeRangeCount !== undefined); let categoriesInBucket: any | null = null; - if (additionalFilter !== undefined) { + if (tempAdditionalFilter !== undefined) { categoriesInBucket = categorizationResult.categories .map((category) => ({ ...category, count: category.subFieldCount ?? category.subTimeRangeCount!, examples: category.subFieldExamples!, - sparkline: undefined, + sparkline: category.subFieldSparkline, })) .filter((category) => category.count > 0) .sort((a, b) => b.count - a.count); } + // eslint-disable-next-line no-console + console.log('categories', categories); + // eslint-disable-next-line no-console + console.log('categoriesInBucket', categoriesInBucket); + setData({ categories, categoriesInBucket, @@ -258,12 +309,13 @@ export const LogCategorizationEmbeddable: FC = ({ earliest, latest, cancelRequest, - runValidateFieldRequest, + getWiderTimeRange, + widenessOption, searchQuery, + runValidateFieldRequest, embeddingOrigin, runCategorizeRequest, intervalMs, - additionalFilter, toasts, ]); @@ -288,7 +340,7 @@ export const LogCategorizationEmbeddable: FC = ({ return; } - const hash = createDocumentStatsHash(documentStats, selectedField.name); + const hash = createDocumentStatsHash(documentStats, selectedField.name, widenessOption); if (hash !== previousDocumentStatsHash) { randomSampler.setDocCount(documentStats.totalCount); setEventRate( @@ -315,10 +367,11 @@ export const LogCategorizationEmbeddable: FC = ({ previousDocumentStatsHash, fieldValidationResult, selectedField, + widenessOption, ]); // console.log(viewModeToggle); - const infoIconCss = { marginTop: euiTheme.size.m, marginLeft: euiTheme.size.xxs }; + // const infoIconCss = { marginTop: euiTheme.size.m, marginLeft: euiTheme.size.xxs }; return ( <> @@ -331,7 +384,7 @@ export const LogCategorizationEmbeddable: FC = ({ > - <>{getViewModeToggle(data?.categories.length ?? 0)} + <>{getViewModeToggle(data?.categoriesInBucket?.length ?? 0)} {randomSampler !== undefined ? ( @@ -341,6 +394,9 @@ export const LogCategorizationEmbeddable: FC = ({ fields={fields} setSelectedField={setSelectedField} selectedField={selectedField} + widenessOption={widenessOption} + setWidenessOption={setWidenessOption} + categories={data?.categories} /> ) : null} @@ -405,76 +461,6 @@ export const LogCategorizationEmbeddable: FC = ({ data.categories.length > 0 && selectedField !== null ? ( <> - {showTabs ? ( - <> - - setSelectedTab(SELECTED_TAB.BUCKET)} - > - - - - - - - - - - - - - setSelectedTab(SELECTED_TAB.FULL_TIME_RANGE)} - > - - - - - - - - - - - - - - - ) : null} - = ({ /** * Creates a hash from the document stats to determine if the document stats have changed. */ -function createDocumentStatsHash(documentStats: DocumentStats, selectedFieldName: string) { +function createDocumentStatsHash( + documentStats: DocumentStats, + selectedFieldName: string, + wideness: WidenessOption +) { const lastTimeStampMs = documentStats.documentCountStats?.lastDocTimeStampMs; const totalCount = documentStats.documentCountStats?.totalCount; const times = Object.keys(documentStats.documentCountStats?.buckets ?? {}); const firstBucketTimeStamp = times.length ? times[0] : undefined; const lastBucketTimeStamp = times.length ? times[times.length - 1] : undefined; return stringHash( - `${lastTimeStampMs}${totalCount}${firstBucketTimeStamp}${lastBucketTimeStamp}${selectedFieldName}` + `${lastTimeStampMs}${totalCount}${firstBucketTimeStamp}${lastBucketTimeStamp}${selectedFieldName}${wideness}` ); } diff --git a/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/random_sampler.ts b/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/random_sampler.ts index cd9eb911608c5..d30950e93a379 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/random_sampler.ts +++ b/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/random_sampler.ts @@ -28,27 +28,33 @@ export type RandomSamplerProbability = number | null; export const RANDOM_SAMPLER_SELECT_OPTIONS: Array<{ value: RandomSamplerOption; - text: string; + inputDisplay: string; 'data-test-subj': string; }> = [ { 'data-test-subj': 'aiopsRandomSamplerOptionOnAutomatic', value: RANDOM_SAMPLER_OPTION.ON_AUTOMATIC, - text: i18n.translate('xpack.aiops.logCategorization.randomSamplerPreference.onAutomaticLabel', { - defaultMessage: 'On - automatic', - }), + inputDisplay: i18n.translate( + 'xpack.aiops.logCategorization.randomSamplerPreference.onAutomaticLabel', + { + defaultMessage: 'On - automatic', + } + ), }, { 'data-test-subj': 'aiopsRandomSamplerOptionOnManual', value: RANDOM_SAMPLER_OPTION.ON_MANUAL, - text: i18n.translate('xpack.aiops.logCategorization.randomSamplerPreference.onManualLabel', { - defaultMessage: 'On - manual', - }), + inputDisplay: i18n.translate( + 'xpack.aiops.logCategorization.randomSamplerPreference.onManualLabel', + { + defaultMessage: 'On - manual', + } + ), }, { 'data-test-subj': 'aiopsRandomSamplerOptionOff', value: RANDOM_SAMPLER_OPTION.OFF, - text: i18n.translate('xpack.aiops.logCategorization.randomSamplerPreference.offLabel', { + inputDisplay: i18n.translate('xpack.aiops.logCategorization.randomSamplerPreference.offLabel', { defaultMessage: 'Off', }), }, diff --git a/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/sampling_panel.tsx b/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/sampling_panel.tsx index f00fdf3baa100..ce91662bfcc91 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/sampling_panel.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/sampling_panel.tsx @@ -7,7 +7,7 @@ import type { FC } from 'react'; import React, { useCallback, useMemo } from 'react'; -import { EuiFlexItem, EuiSpacer, EuiCallOut, EuiSelect, EuiFormRow } from '@elastic/eui'; +import { EuiFlexItem, EuiSpacer, EuiCallOut, EuiFormRow, EuiSuperSelect } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import useObservable from 'react-use/lib/useObservable'; @@ -65,11 +65,11 @@ export const SamplingPanel: FC = ({ randomSampler, reload }) => { } )} > - setRandomSamplerPreference(e.target.value as RandomSamplerOption)} + valueOfSelected={randomSamplerPreference} + onChange={setRandomSamplerPreference} /> diff --git a/x-pack/plugins/aiops/public/components/log_categorization/use_wider_time_range.ts b/x-pack/plugins/aiops/public/components/log_categorization/use_wider_time_range.ts new file mode 100644 index 0000000000000..10126eb635338 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/log_categorization/use_wider_time_range.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useRef, useCallback } from 'react'; + +import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; +import type { HttpFetchOptions } from '@kbn/core/public'; +import { getTimeFieldRange } from '@kbn/ml-date-picker'; +import moment from 'moment'; +import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; +import type { WidenessOption } from './log_categorization_for_embeddable'; +import { WIDENESS } from './log_categorization_for_embeddable'; + +export function useWiderTimeRange() { + const { http } = useAiopsAppContext(); + const abortController = useRef(new AbortController()); + + const getWiderTimeRange = useCallback( + async ( + index: string, + timeField: string, + timeRange: { from: number; to: number }, + widenessOption: WidenessOption, + queryIn: QueryDslQueryContainer, + headers?: HttpFetchOptions['headers'] + ) => { + const wideness = WIDENESS[widenessOption]; + const widenessMs = moment.duration(wideness.factor, wideness.unit).asMilliseconds(); + const currentWideness = timeRange.to - timeRange.from; + + // the time range is already wide enough + if (currentWideness > widenessMs) { + return timeRange; + } + + const resp = await getTimeFieldRange({ + http, + index, + timeFieldName: timeField, + query: queryIn, + path: '/internal/file_upload/time_field_range', + }); + + // the index isn't big enough to get a wider time range + const indexWideness = resp.end.epoch - resp.start.epoch; + if (indexWideness < widenessMs) { + return { + from: resp.start.epoch, + to: resp.end.epoch, + }; + } + + const widenessRemainder = widenessMs - currentWideness; + const newFrom = Math.max(timeRange.from - widenessRemainder, resp.start.epoch); + const newTo = Math.min(newFrom + widenessMs, resp.end.epoch); + + return { + from: newFrom, + to: newTo, + }; + }, + [http] + ); + + const cancelRequest = useCallback(() => { + abortController.current.abort(); + abortController.current = new AbortController(); + }, []); + + return { getWiderTimeRange, cancelRequest }; +} diff --git a/x-pack/plugins/aiops/public/types/storage.ts b/x-pack/plugins/aiops/public/types/storage.ts index e6a1f8d46730b..fa7ec700cbe84 100644 --- a/x-pack/plugins/aiops/public/types/storage.ts +++ b/x-pack/plugins/aiops/public/types/storage.ts @@ -6,6 +6,7 @@ */ import { type FrozenTierPreference } from '@kbn/ml-date-picker'; +import type { WidenessOption } from '../components/log_categorization/log_categorization_for_embeddable'; import { type RandomSamplerOption, type RandomSamplerProbability, @@ -15,11 +16,13 @@ export const AIOPS_FROZEN_TIER_PREFERENCE = 'aiops.frozenDataTierPreference'; export const AIOPS_RANDOM_SAMPLING_MODE_PREFERENCE = 'aiops.randomSamplingModePreference'; export const AIOPS_RANDOM_SAMPLING_PROBABILITY_PREFERENCE = 'aiops.randomSamplingProbabilityPreference'; +export const AIOPS_PATTERN_ANALYSIS_WIDENESS_PREFERENCE = 'aiops.patternAnalysisWidenessPreference'; export type AiOps = Partial<{ [AIOPS_FROZEN_TIER_PREFERENCE]: FrozenTierPreference; [AIOPS_RANDOM_SAMPLING_MODE_PREFERENCE]: RandomSamplerOption; [AIOPS_RANDOM_SAMPLING_PROBABILITY_PREFERENCE]: number; + [AIOPS_PATTERN_ANALYSIS_WIDENESS_PREFERENCE]: WidenessOption; }> | null; export type AiOpsKey = keyof Exclude; @@ -30,10 +33,13 @@ export type AiOpsStorageMapped = T extends typeof AIOPS_FROZ ? RandomSamplerOption : T extends typeof AIOPS_RANDOM_SAMPLING_PROBABILITY_PREFERENCE ? RandomSamplerProbability + : T extends typeof AIOPS_PATTERN_ANALYSIS_WIDENESS_PREFERENCE + ? WidenessOption : null; export const AIOPS_STORAGE_KEYS = [ AIOPS_FROZEN_TIER_PREFERENCE, AIOPS_RANDOM_SAMPLING_MODE_PREFERENCE, AIOPS_RANDOM_SAMPLING_PROBABILITY_PREFERENCE, + AIOPS_PATTERN_ANALYSIS_WIDENESS_PREFERENCE, ] as const; From 6a1a025add30c7d2cca71a65309afc59639a4500 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 28 Mar 2024 14:44:57 +0000 Subject: [PATCH 03/92] fixing timefilter updates --- .../log_categorization_table.tsx | 57 ++++++++++--------- .../view_mode_toggle/view_mode_toggle.tsx | 20 ++++--- .../log_categorization/embeddable_menu.tsx | 4 +- .../log_categorization_for_embeddable.tsx | 53 +++++++++-------- .../log_categorization_embeddable.tsx | 10 ++-- x-pack/plugins/aiops/public/hooks/use_data.ts | 5 +- 6 files changed, 80 insertions(+), 69 deletions(-) diff --git a/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_table.tsx b/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_table.tsx index 3778e83f5ac90..17a6c7d31619a 100644 --- a/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_table.tsx +++ b/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_table.tsx @@ -9,7 +9,7 @@ import React, { useEffect, useMemo, useRef, useState } from 'react'; import type { Filter, Query, AggregateQuery } from '@kbn/es-query'; import { METRIC_TYPE, UiCounterMetricType } from '@kbn/analytics'; -import type { DataViewField, DataView } from '@kbn/data-views-plugin/public'; +import type { DataView } from '@kbn/data-views-plugin/public'; import { EmbeddableInput, EmbeddableOutput, @@ -63,26 +63,27 @@ export type EmbeddableLogCategorizationInput = EmbeddableInput & EmbeddableLogCa export type EmbeddableLogCategorizationOutput = EmbeddableOutput & { indexPatterns?: DataView[] }; -export interface DataVisualizerGridEmbeddableInput extends EmbeddableInput { - dataView: DataView; - savedSearch?: SavedSearch; - query?: Query | AggregateQuery; - visibleFieldNames?: string[]; - filters?: Filter[]; - showPreviewByDefault?: boolean; - /** - * Callback to add a filter to filter bar - */ - onAddFilter?: (field: DataViewField | string, value: string, type: '+' | '-') => void; - sessionId?: string; - fieldsToFetch?: string[]; - totalDocuments?: number; - samplingOption?: SamplingOption; - getViewModeToggle: (patternCount: number) => React.ReactElement | undefined; -} -export interface DataVisualizerGridEmbeddableOutput extends EmbeddableOutput { - showDistributions?: boolean; -} +// export interface DataVisualizerGridEmbeddableInput extends EmbeddableInput { +// // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!rename +// dataView: DataView; +// savedSearch?: SavedSearch; +// query?: Query | AggregateQuery; +// visibleFieldNames?: string[]; +// filters?: Filter[]; +// showPreviewByDefault?: boolean; +// /** +// * Callback to add a filter to filter bar +// */ +// onAddFilter?: (field: DataViewField | string, value: string, type: '+' | '-') => void; +// sessionId?: string; +// fieldsToFetch?: string[]; +// totalDocuments?: number; +// samplingOption?: SamplingOption; +// getViewModeToggle: (patternCount: number) => React.ReactElement | undefined; +// } +// export interface DataVisualizerGridEmbeddableOutput extends EmbeddableOutput { +// showDistributions?: boolean; +// } export interface LogCategorizationTableProps { /** @@ -163,12 +164,12 @@ export const LogCategorizationTable = (props: LogCategorizationTableProps) => { ); useEffect(() => { - const availableFields$ = stateContainer?.dataState.data$.availableFields$; - const sub = embeddable?.getOutput$().subscribe((output: DataVisualizerGridEmbeddableOutput) => { - if (output.showDistributions !== undefined && stateContainer) { - stateContainer.appState.update({ hideAggregatedPreview: !output.showDistributions }); - } - }); + // const availableFields$ = stateContainer?.dataState.data$.availableFields$; + // const sub = embeddable?.getOutput$().subscribe((output: DataVisualizerGridEmbeddableOutput) => { + // if (output.showDistributions !== undefined && stateContainer) { + // stateContainer.appState.update({ hideAggregatedPreview: !output.showDistributions }); + // } + // }); const refetch = stateContainer?.dataState.refetch$.subscribe(() => { if (embeddable && !isErrorEmbeddable(embeddable)) { @@ -183,7 +184,7 @@ export const LogCategorizationTable = (props: LogCategorizationTableProps) => { // }); return () => { - sub?.unsubscribe(); + // sub?.unsubscribe(); refetch?.unsubscribe(); // fields?.unsubscribe(); }; diff --git a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx index 370ff8cc2a0de..d798feafc09ce 100644 --- a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx +++ b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx @@ -83,25 +83,27 @@ export const DocumentViewModeToggle = ({ + setDiscoverViewMode(VIEW_MODE.AGGREGATED_LEVEL)} + isSelected={viewMode === VIEW_MODE.PATTERN_LEVEL} + onClick={() => setDiscoverViewMode(VIEW_MODE.PATTERN_LEVEL)} data-test-subj="dscViewModeFieldStatsButton" > + setDiscoverViewMode(VIEW_MODE.PATTERN_LEVEL)} + isSelected={viewMode === VIEW_MODE.AGGREGATED_LEVEL} + onClick={() => setDiscoverViewMode(VIEW_MODE.AGGREGATED_LEVEL)} data-test-subj="dscViewModeFieldStatsButton" > diff --git a/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx b/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx index 13db868376799..6d1871934efd1 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx @@ -111,14 +111,14 @@ export const EmbeddableMenu: FC = ({ label={i18n.translate( 'xpack.aiops.logCategorization.randomSamplerSettingsPopUp.randomSamplerRowLabel', { - defaultMessage: 'Wideness', + defaultMessage: 'Additional time', } )} helpText={ <> {categories !== undefined ? ( <> - Total categories in {widenessOption}: {categories.length} + Total patterns in {widenessOption}: {categories.length} ) : null} diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx index 8e7617cfdd33e..d96cb1a948d5b 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx @@ -7,10 +7,9 @@ import type { FC } from 'react'; import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react'; -import { EuiFlexGroup, EuiFlexItem, useEuiTheme } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import type { SavedSearch } from '@kbn/saved-search-plugin/public'; -import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; +import type { DataViewField } from '@kbn/data-views-plugin/public'; import { i18n } from '@kbn/i18n'; import type { Filter } from '@kbn/es-query'; import { buildEmptyFilter } from '@kbn/es-query'; @@ -51,6 +50,7 @@ import { EmbeddableMenu } from './embeddable_menu'; import { useWiderTimeRange } from './use_wider_time_range'; import type { AiOpsKey, AiOpsStorageMapped } from '../../types/storage'; import { AIOPS_PATTERN_ANALYSIS_WIDENESS_PREFERENCE } from '../../types/storage'; +import type { EmbeddableLogCategorizationInput } from '../../embeddables/log_categorization/log_categorization_embeddable'; enum SELECTED_TAB { BUCKET, @@ -69,26 +69,21 @@ export const WIDENESS: Wideness = { }; export interface LogCategorizationPageProps { - dataView: DataView; - savedSearch: SavedSearch | null; onClose: () => void; /** Identifier to indicate the plugin utilizing the component */ embeddingOrigin: string; additionalFilter?: CategorizationAdditionalFilter; - onAddFilter?: () => void; - getViewModeToggle: (patternCount: number) => React.ReactElement | undefined; + input: Readonly; + // embeddableInput: Readonly>; } const BAR_TARGET = 20; export const LogCategorizationEmbeddable: FC = ({ - dataView, - savedSearch, onClose, embeddingOrigin, additionalFilter, - onAddFilter, - getViewModeToggle, + input, }) => { const { notifications: { toasts }, @@ -97,6 +92,11 @@ export const LogCategorizationEmbeddable: FC = ({ }, uiSettings, } = useAiopsAppContext(); + // console.log(lastReloadRequestTime); + // const input = useObservable(embeddableInput)!; + const { dataView, savedSearch } = input; + const getViewModeToggle: (patternCount: number) => React.ReactElement | undefined = + input.getViewModeToggle; const [widenessOption, setWidenessOption] = useStorage< AiOpsKey, @@ -106,7 +106,6 @@ export const LogCategorizationEmbeddable: FC = ({ const { runValidateFieldRequest, cancelRequest: cancelValidationRequest } = useValidateFieldRequest(); const { getWiderTimeRange, cancelRequest: cancelWiderTimeRangeRequest } = useWiderTimeRange(); - const { euiTheme } = useEuiTheme(); const { filters, query } = useMemo(() => getState(), [getState]); const mounted = useRef(false); @@ -125,7 +124,7 @@ export const LogCategorizationEmbeddable: FC = ({ const [selectedField, setSelectedField] = useState(null); // const [wideness, setWideness] = useState('1 week'); const [fields, setFields] = useState([]); - const [selectedSavedSearch /* , setSelectedSavedSearch*/] = useState(savedSearch); + const [selectedSavedSearch /* , setSelectedSavedSearch*/] = useState(savedSearch ?? null); const [previousDocumentStatsHash, setPreviousDocumentStatsHash] = useState(0); const [loading, setLoading] = useState(true); const [eventRate, setEventRate] = useState([]); @@ -138,7 +137,6 @@ export const LogCategorizationEmbeddable: FC = ({ const [fieldValidationResult, setFieldValidationResult] = useState( null ); - const [showTabs, setShowTabs] = useState(false); const [selectedTab, setSelectedTab] = useState(SELECTED_TAB.FULL_TIME_RANGE); const cancelRequest = useCallback(() => { @@ -182,7 +180,7 @@ export const LogCategorizationEmbeddable: FC = ({ true ); - const { documentStats, timefilter, earliest, latest, intervalMs } = useData( + const { documentStats, timefilter, earliest, latest, intervalMs, forceRefresh } = useData( dataView, 'log_categorization', searchQuery, @@ -264,7 +262,6 @@ export const LogCategorizationEmbeddable: FC = ({ setFieldValidationResult(validationResult); const { categories, hasExamples } = categorizationResult; - const hasBucketCategories = categories.some((c) => c.subTimeRangeCount !== undefined); let categoriesInBucket: any | null = null; if (tempAdditionalFilter !== undefined) { categoriesInBucket = categorizationResult.categories @@ -289,7 +286,6 @@ export const LogCategorizationEmbeddable: FC = ({ displayExamples: hasExamples, }); - setShowTabs(hasBucketCategories); setSelectedTab(SELECTED_TAB.BUCKET); } } catch (error) { @@ -319,19 +315,23 @@ export const LogCategorizationEmbeddable: FC = ({ toasts, ]); - const onAddFilter2 = useCallback( + const onAddFilter = useCallback( (values: Filter, alias?: string) => { + if (input.onAddFilter === undefined) { + return; + } + const filter = buildEmptyFilter(false, dataView.id); if (alias) { filter.meta.alias = alias; } filter.query = values.query; if (onAddFilter !== undefined) { - onAddFilter(); + input.onAddFilter(); } filterManager.addFilters([filter]); }, - [dataView.id, filterManager, onAddFilter] + [dataView.id, filterManager, input] ); useEffect(() => { @@ -369,9 +369,16 @@ export const LogCategorizationEmbeddable: FC = ({ selectedField, widenessOption, ]); - // console.log(viewModeToggle); - // const infoIconCss = { marginTop: euiTheme.size.m, marginLeft: euiTheme.size.xxs }; + useEffect( + function refreshTriggeredFromButton() { + if (input?.lastReloadRequestTime !== undefined) { + forceRefresh(); + } + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [input?.lastReloadRequestTime] + ); return ( <> @@ -476,7 +483,7 @@ export const LogCategorizationEmbeddable: FC = ({ selectedCategory={selectedCategory} setSelectedCategory={setSelectedCategory} timefilter={timefilter} - onAddFilter={onAddFilter2} + onAddFilter={onAddFilter} onClose={onClose} enableRowActions={false} additionalFilter={ diff --git a/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx b/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx index fcfcba56f2dac..2a3a1554cc569 100644 --- a/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx +++ b/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx @@ -29,6 +29,7 @@ import type { Query, AggregateQuery } from '@kbn/es-query'; import { StorageContextProvider } from '@kbn/ml-local-storage'; import { Storage } from '@kbn/kibana-utils-plugin/public'; import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; +// import useObservable from 'react-use/lib/useObservable'; import type { EmbeddableLogCategorizationType } from '../../../common/constants'; import { EMBEDDABLE_ORIGIN } from '../../../common/constants'; import { AiopsAppContext, type AiopsAppDependencies } from '../../hooks/use_aiops_app_context'; @@ -150,8 +151,8 @@ export class EmbeddableLogCategorization extends AbstractEmbeddable< }; const input = this.getInput(); - const input$ = this.getInput$(); - const field = input.dataView.fields.find((f) => f.name === 'message')!; + // const input$ = this.getInput$(); + // const input = useObservable(input$); const aiopsAppContextValue = { embeddingOrigin: this.parent?.type ?? input.embeddingOrigin ?? EMBEDDABLE_ORIGIN, @@ -166,12 +167,9 @@ export class EmbeddableLogCategorization extends AbstractEmbeddable< this.destroy()} embeddingOrigin={'discover-change-me'} - onAddFilter={input.onAddFilter ?? (() => {})} - getViewModeToggle={input.getViewModeToggle} + input={input} /> {/* { + ).subscribe((done) => { if (onUpdate) { onUpdate({ time: timefilter.getTime(), refreshInterval: timefilter.getRefreshInterval(), }); setLastRefresh(Date.now()); + if (typeof done === 'function') { + done(); + } } }); From a77e0012abe4c518cc4fbef0b50d3d7140548189 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 28 Mar 2024 19:31:37 +0000 Subject: [PATCH 04/92] types fixes after merge with main --- .../ml/aiops_log_rate_analysis/constants.ts | 21 ------------------- .../cases/change_point_charts_attachment.tsx | 2 +- .../log_categorization/embeddable_menu.tsx | 3 ++- .../log_categorization_for_embeddable.tsx | 2 +- .../embeddable_change_point_chart.tsx | 3 ++- .../embeddable_change_point_chart_factory.ts | 2 +- .../log_categorization_embeddable.tsx | 3 ++- .../aiops/public/hooks/use_cases_modal.ts | 4 ++-- 8 files changed, 11 insertions(+), 29 deletions(-) diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/constants.ts b/x-pack/packages/ml/aiops_log_rate_analysis/constants.ts index a9da346377fc6..054bb876a4f7a 100644 --- a/x-pack/packages/ml/aiops_log_rate_analysis/constants.ts +++ b/x-pack/packages/ml/aiops_log_rate_analysis/constants.ts @@ -31,26 +31,5 @@ export const LOG_RATE_ANALYSIS_SETTINGS = { */ export const RANDOM_SAMPLER_SEED = 3867412; -export const CASES_ATTACHMENT_CHANGE_POINT_CHART = 'aiopsChangePointChart'; - -export const EMBEDDABLE_CHANGE_POINT_CHART_TYPE = 'aiopsChangePointChart' as const; - -export type EmbeddableChangePointType = typeof EMBEDDABLE_CHANGE_POINT_CHART_TYPE; - -export const AIOPS_TELEMETRY_ID = { - AIOPS_DEFAULT_SOURCE: 'ml_aiops_labs', - AIOPS_ANALYSIS_RUN_ORIGIN: 'aiops-analysis-run-origin', -} as const; - -export const EMBEDDABLE_ORIGIN = 'embeddable'; - -export const CHANGE_POINT_DETECTION_VIEW_TYPE = { - CHARTS: 'charts', - TABLE: 'table', -} as const; - -export type ChangePointDetectionViewType = - typeof CHANGE_POINT_DETECTION_VIEW_TYPE[keyof typeof CHANGE_POINT_DETECTION_VIEW_TYPE]; - /** Highlighting color for charts */ export const LOG_RATE_ANALYSIS_HIGHLIGHT_COLOR = 'orange'; diff --git a/x-pack/plugins/aiops/public/cases/change_point_charts_attachment.tsx b/x-pack/plugins/aiops/public/cases/change_point_charts_attachment.tsx index 042acaeaef7b6..3cfa205cc4096 100644 --- a/x-pack/plugins/aiops/public/cases/change_point_charts_attachment.tsx +++ b/x-pack/plugins/aiops/public/cases/change_point_charts_attachment.tsx @@ -14,7 +14,7 @@ import { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiDescriptionList } from '@elastic/eui'; import deepEqual from 'fast-deep-equal'; -import type { EmbeddableChangePointChartProps } from '../embeddable'; +import type { EmbeddableChangePointChartProps } from '../embeddables/change_point_chart'; export const initComponent = memoize( (fieldFormats: FieldFormatsStart, EmbeddableComponent: FC) => { diff --git a/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx b/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx index 6d1871934efd1..149ede7d8a01a 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx @@ -19,7 +19,8 @@ import type { FC } from 'react'; import React, { useState, useMemo } from 'react'; import type { DataViewField } from '@kbn/data-views-plugin/public'; import { i18n } from '@kbn/i18n'; -import type { Category } from '../../../common/api/log_categorization/types'; + +import type { Category } from '@kbn/aiops-log-pattern-analysis/types'; import type { RandomSampler } from './sampling_menu'; import { SamplingPanel } from './sampling_menu/sampling_panel'; import type { WidenessOption } from './log_categorization_for_embeddable'; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx index 01bdff7ee906b..4db46dd4bd1fc 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx @@ -24,8 +24,8 @@ import { useStorage } from '@kbn/ml-local-storage'; import type { Category } from '@kbn/aiops-log-pattern-analysis/types'; -import { AIOPS_TELEMETRY_ID } from '@kbn/aiops-log-rate-analysis/constants'; import type { CategorizationAdditionalFilter } from '@kbn/aiops-log-pattern-analysis/create_category_request'; +import { AIOPS_TELEMETRY_ID } from '@kbn/aiops-common/constants'; import { type LogCategorizationPageUrlState, getDefaultLogCategorizationAppState, diff --git a/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart.tsx b/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart.tsx index 2422ebbe135c9..2b0450d396026 100644 --- a/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart.tsx +++ b/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart.tsx @@ -23,7 +23,8 @@ import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/common'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { EMBEDDABLE_ORIGIN } from '@kbn/aiops-common/constants'; -import type { EmbeddableChangePointType } from '@kbn/aiops-log-rate-analysis/constants'; + +import type { EmbeddableChangePointType } from '@kbn/aiops-change-point-detection/constants'; import { EmbeddableInputTracker } from './embeddable_chart_component_wrapper'; import { AiopsAppContext, type AiopsAppDependencies } from '../../hooks/use_aiops_app_context'; diff --git a/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_factory.ts b/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_factory.ts index 5a231eb9706fc..7b8bdf4c64686 100644 --- a/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_factory.ts +++ b/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_factory.ts @@ -14,7 +14,7 @@ import type { StartServicesAccessor } from '@kbn/core-lifecycle-browser'; import type { EmbeddableChangePointType, EMBEDDABLE_CHANGE_POINT_CHART_TYPE, -} from '@kbn/aiops-log-rate-analysis/constants'; +} from '@kbn/aiops-change-point-detection/constants'; import type { EmbeddableChangePointChartInput } from './embeddable_change_point_chart'; import { EmbeddableChangePointChart } from './embeddable_change_point_chart'; import type { AiopsPluginStart, AiopsPluginStartDeps } from '../../types'; diff --git a/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx b/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx index 4377dfdeb8847..bac2ffb177f02 100644 --- a/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx +++ b/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx @@ -32,7 +32,8 @@ import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; // import useObservable from 'react-use/lib/useObservable'; import type { EmbeddableLogCategorizationType } from '@kbn/aiops-log-pattern-analysis/constants'; -import { EMBEDDABLE_ORIGIN } from '@kbn/aiops-log-rate-analysis/constants'; + +import { EMBEDDABLE_ORIGIN } from '@kbn/aiops-common/constants'; import { AiopsAppContext, type AiopsAppDependencies } from '../../hooks/use_aiops_app_context'; import { AIOPS_STORAGE_KEYS } from '../../types/storage'; import { LogCategorizationEmbeddable } from '../../components/log_categorization/log_categorization_for_embeddable'; diff --git a/x-pack/plugins/aiops/public/hooks/use_cases_modal.ts b/x-pack/plugins/aiops/public/hooks/use_cases_modal.ts index ad75051975505..05fbaf2d39660 100644 --- a/x-pack/plugins/aiops/public/hooks/use_cases_modal.ts +++ b/x-pack/plugins/aiops/public/hooks/use_cases_modal.ts @@ -8,9 +8,9 @@ import { useCallback } from 'react'; import { stringHash } from '@kbn/ml-string-hash'; import { AttachmentType } from '@kbn/cases-plugin/common'; -import type { EmbeddableChangePointChartInput } from '../embeddable/embeddable_change_point_chart'; -import type { EmbeddableChangePointChartType } from '../embeddable/embeddable_change_point_chart_factory'; import { useAiopsAppContext } from './use_aiops_app_context'; +import type { EmbeddableChangePointChartType } from '../embeddables/change_point_chart/embeddable_change_point_chart_factory'; +import type { EmbeddableChangePointChartInput } from '../embeddables/change_point_chart/embeddable_change_point_chart'; /** * Returns a callback for opening the cases modal with provided attachment state. From 7dd6e131b98953d6cc30410cad4f28a5f5624cbb Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 28 Mar 2024 19:48:47 +0000 Subject: [PATCH 05/92] fixes after merge with main --- .../main/components/layout/discover_main_content.tsx | 2 -- .../public/components/change_point_detection/fields_config.tsx | 2 +- .../public/components/log_categorization/embeddable_menu.tsx | 2 +- .../change_point_chart/embeddable_change_point_chart.tsx | 1 - .../embeddable_change_point_chart_factory.ts | 3 +-- 5 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx index 869d97de9eac7..84c1ddf00064f 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx @@ -179,8 +179,6 @@ export const DiscoverMainContent = ({ return ( {viewModeToggle2(patternCount)} - - {/*
Hello there
*/}
); } catch (error) { diff --git a/x-pack/plugins/aiops/public/components/change_point_detection/fields_config.tsx b/x-pack/plugins/aiops/public/components/change_point_detection/fields_config.tsx index 74fb33e136213..2028c75ae1c6d 100644 --- a/x-pack/plugins/aiops/public/components/change_point_detection/fields_config.tsx +++ b/x-pack/plugins/aiops/public/components/change_point_detection/fields_config.tsx @@ -39,6 +39,7 @@ import { } from '@kbn/aiops-change-point-detection/constants'; import { MaxSeriesControl } from './max_series_control'; import { useCasesModal } from '../../hooks/use_cases_modal'; +import type { EmbeddableChangePointChartInput } from '../../embeddables/change_point_chart/embeddable_change_point_chart'; import { useDataSource } from '../../hooks/use_data_source'; import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; import { ChangePointsTable } from './change_points_table'; @@ -55,7 +56,6 @@ import { import { useChangePointResults } from './use_change_point_agg_request'; import { useSplitFieldCardinality } from './use_split_field_cardinality'; import { ViewTypeSelector } from './view_type_selector'; -import type { EmbeddableChangePointChartInput } from '../../embeddables/change_point_chart/embeddable_change_point_chart'; const selectControlCss = { width: '350px' }; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx b/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx index 149ede7d8a01a..f4042e8537bac 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx @@ -112,7 +112,7 @@ export const EmbeddableMenu: FC = ({ label={i18n.translate( 'xpack.aiops.logCategorization.randomSamplerSettingsPopUp.randomSamplerRowLabel', { - defaultMessage: 'Additional time', + defaultMessage: 'Minimum time range', } )} helpText={ diff --git a/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart.tsx b/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart.tsx index 2b0450d396026..c92aed7c3ae15 100644 --- a/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart.tsx +++ b/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart.tsx @@ -23,7 +23,6 @@ import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/common'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { EMBEDDABLE_ORIGIN } from '@kbn/aiops-common/constants'; - import type { EmbeddableChangePointType } from '@kbn/aiops-change-point-detection/constants'; import { EmbeddableInputTracker } from './embeddable_chart_component_wrapper'; import { AiopsAppContext, type AiopsAppDependencies } from '../../hooks/use_aiops_app_context'; diff --git a/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_factory.ts b/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_factory.ts index 7b8bdf4c64686..e429f3c58b0db 100644 --- a/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_factory.ts +++ b/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_factory.ts @@ -10,10 +10,9 @@ import { ErrorEmbeddable } from '@kbn/embeddable-plugin/public'; import { i18n } from '@kbn/i18n'; import { type DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { StartServicesAccessor } from '@kbn/core-lifecycle-browser'; - import type { - EmbeddableChangePointType, EMBEDDABLE_CHANGE_POINT_CHART_TYPE, + EmbeddableChangePointType, } from '@kbn/aiops-change-point-detection/constants'; import type { EmbeddableChangePointChartInput } from './embeddable_change_point_chart'; import { EmbeddableChangePointChart } from './embeddable_change_point_chart'; From f3676a46c7722017df3726da29cfb3d3e14998af Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Tue, 2 Apr 2024 15:04:40 +0100 Subject: [PATCH 06/92] allow no minimum time range --- .../create_category_request.ts | 23 ++++----- .../log_categorization/embeddable_menu.tsx | 9 ++-- .../log_categorization_for_embeddable.tsx | 47 +++++++++---------- .../use_categorize_request.ts | 4 +- .../use_wider_time_range.ts | 4 +- 5 files changed, 44 insertions(+), 43 deletions(-) diff --git a/x-pack/packages/ml/aiops_log_pattern_analysis/create_category_request.ts b/x-pack/packages/ml/aiops_log_pattern_analysis/create_category_request.ts index 645b1e5c33491..d32970cf4c519 100644 --- a/x-pack/packages/ml/aiops_log_pattern_analysis/create_category_request.ts +++ b/x-pack/packages/ml/aiops_log_pattern_analysis/create_category_request.ts @@ -32,7 +32,8 @@ export function createCategoryRequest( wrap: ReturnType['wrap'], intervalMs?: number, additionalFilter?: CategorizationAdditionalFilter, - useStandardTokenizer: boolean = true + useStandardTokenizer: boolean = true, + includeSparkline: boolean = true ) { const query = createCategorizeQuery(queryIn, timeField, timeRange); const aggs = { @@ -50,16 +51,16 @@ export function createCategoryRequest( _source: field, }, }, - // ...(intervalMs - // ? { - // sparkline: { - // date_histogram: { - // field: timeField, - // fixed_interval: `${intervalMs}ms`, - // }, - // }, - // } - // : {}), + ...(intervalMs && includeSparkline + ? { + sparkline: { + date_histogram: { + field: timeField, + fixed_interval: `${intervalMs}ms`, + }, + }, + } + : {}), ...(additionalFilter ? { sub_time_range: { diff --git a/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx b/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx index f4042e8537bac..00b577205eb37 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx @@ -20,7 +20,6 @@ import React, { useState, useMemo } from 'react'; import type { DataViewField } from '@kbn/data-views-plugin/public'; import { i18n } from '@kbn/i18n'; -import type { Category } from '@kbn/aiops-log-pattern-analysis/types'; import type { RandomSampler } from './sampling_menu'; import { SamplingPanel } from './sampling_menu/sampling_panel'; import type { WidenessOption } from './log_categorization_for_embeddable'; @@ -33,7 +32,7 @@ interface Props { setSelectedField: (field: DataViewField) => void; widenessOption: WidenessOption; setWidenessOption: (w: WidenessOption) => void; - categories: Category[] | undefined; + categoryCount: number | undefined; reload: () => void; } @@ -44,7 +43,7 @@ export const EmbeddableMenu: FC = ({ setSelectedField, widenessOption, setWidenessOption, - categories, + categoryCount, reload, }) => { const [showPopover, setShowPopover] = useState(false); @@ -117,9 +116,9 @@ export const EmbeddableMenu: FC = ({ )} helpText={ <> - {categories !== undefined ? ( + {categoryCount !== undefined ? ( <> - Total patterns in {widenessOption}: {categories.length} + Total patterns in {widenessOption}: {categoryCount} ) : null} diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx index 4db46dd4bd1fc..ef1f762b020bb 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx @@ -56,11 +56,12 @@ enum SELECTED_TAB { FULL_TIME_RANGE, } -export type WidenessOption = '1 week' | '1 month' | '3 months' | '6 months'; +export type WidenessOption = 'No minimum' | '1 week' | '1 month' | '3 months' | '6 months'; type Wideness = Record; export const WIDENESS: Wideness = { + 'No minimum': { factor: 0, unit: 'w' }, '1 week': { factor: 1, unit: 'w' }, '1 month': { factor: 1, unit: 'M' }, '3 months': { factor: 3, unit: 'M' }, @@ -130,8 +131,8 @@ export const LogCategorizationEmbeddable: FC = ({ const [pinnedCategory, setPinnedCategory] = useState(null); const [data, setData] = useState<{ categories: Category[]; - categoriesInBucket: Category[] | null; displayExamples: boolean; + totalCategories: number; } | null>(null); const [fieldValidationResult, setFieldValidationResult] = useState( null @@ -216,10 +217,6 @@ export const LogCategorizationEmbeddable: FC = ({ to: latest, }; - // const timeRange: TimeRange = { - // from: moment(earliest).add(-5, 'w').valueOf(), - // to: latest, - // }; const timeRange = await getWiderTimeRange( index, timeField, @@ -250,10 +247,10 @@ export const LogCategorizationEmbeddable: FC = ({ index, selectedField.name, timeField, - timeRange, + { to: timeRange.to, from: timeRange.from }, searchQuery, intervalMs, - tempAdditionalFilter + timeRange.useSubAgg ? tempAdditionalFilter : undefined ), ]); @@ -261,9 +258,8 @@ export const LogCategorizationEmbeddable: FC = ({ setFieldValidationResult(validationResult); const { categories, hasExamples } = categorizationResult; - let categoriesInBucket: any | null = null; - if (tempAdditionalFilter !== undefined) { - categoriesInBucket = categorizationResult.categories + if (timeRange.useSubAgg) { + const categoriesInBucket = categorizationResult.categories .map((category) => ({ ...category, count: category.subFieldCount ?? category.subTimeRangeCount!, @@ -272,18 +268,23 @@ export const LogCategorizationEmbeddable: FC = ({ })) .filter((category) => category.count > 0) .sort((a, b) => b.count - a.count); + setData({ + categories: categoriesInBucket, + displayExamples: hasExamples, + totalCategories: categories.length, + }); + } else { + setData({ + categories, + displayExamples: hasExamples, + totalCategories: categories.length, + }); } // eslint-disable-next-line no-console console.log('categories', categories); - // eslint-disable-next-line no-console - console.log('categoriesInBucket', categoriesInBucket); - setData({ - categories, - categoriesInBucket, - displayExamples: hasExamples, - }); + // console.log('categoriesInBucket', categoriesInBucket); setSelectedTab(SELECTED_TAB.BUCKET); } @@ -390,7 +391,7 @@ export const LogCategorizationEmbeddable: FC = ({ > - <>{getViewModeToggle(data?.categoriesInBucket?.length ?? 0)} + <>{getViewModeToggle(data?.categories?.length ?? 0)} {randomSampler !== undefined ? ( @@ -402,7 +403,7 @@ export const LogCategorizationEmbeddable: FC = ({ selectedField={selectedField} widenessOption={widenessOption} setWidenessOption={setWidenessOption} - categories={data?.categories} + categoryCount={data?.totalCategories} /> ) : null} @@ -468,11 +469,7 @@ export const LogCategorizationEmbeddable: FC = ({ selectedField !== null ? ( <> widenessMs) { - return timeRange; + return { ...timeRange, useSubAgg: false }; } const resp = await getTimeFieldRange({ @@ -51,6 +51,7 @@ export function useWiderTimeRange() { return { from: resp.start.epoch, to: resp.end.epoch, + useSubAgg: true, }; } @@ -61,6 +62,7 @@ export function useWiderTimeRange() { return { from: newFrom, to: newTo, + useSubAgg: true, }; }, [http] From b6013389b52c31fbe2f4b34122e57876561293c6 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Tue, 2 Apr 2024 16:09:57 +0100 Subject: [PATCH 07/92] changing pattern count in tab --- .../layout/discover_main_content.tsx | 70 ++++++++++--------- .../log_categorization_table.tsx | 7 +- .../log_categorization_for_embeddable.tsx | 9 ++- .../log_categorization_embeddable.tsx | 1 + 4 files changed, 50 insertions(+), 37 deletions(-) diff --git a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx index 84c1ddf00064f..04ab5d8fe65ce 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx @@ -13,7 +13,7 @@ import { DataView } from '@kbn/data-views-plugin/common'; import { METRIC_TYPE } from '@kbn/analytics'; import { i18n } from '@kbn/i18n'; import type { DocViewFilterFn } from '@kbn/unified-doc-viewer/types'; -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +// import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { VIEW_MODE } from '../../../../../common/constants'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; import { DocumentViewModeToggle } from '../../../../components/view_mode_toggle'; @@ -64,7 +64,8 @@ export const DiscoverMainContent = ({ isChartAvailable, }: DiscoverMainContentProps) => { const { trackUiMetric } = useDiscoverServices(); - const services = useDiscoverServices(); + // const services = useDiscoverServices(); + const [patternCount, setPatternCount] = React.useState(undefined); const setDiscoverViewMode = useCallback( (mode: VIEW_MODE) => { // @ts-expect-error why is this wrong???? @@ -90,6 +91,7 @@ export const DiscoverMainContent = ({ isTextBasedQuery={isPlainRecord} stateContainer={stateContainer} setDiscoverViewMode={setDiscoverViewMode} + patternCount={patternCount} prepend={ React.isValidElement(panelsToggle) ? React.cloneElement(panelsToggle, { renderedFor: 'tabs', isChartAvailable }) @@ -104,27 +106,28 @@ export const DiscoverMainContent = ({ stateContainer, panelsToggle, isChartAvailable, + patternCount, ]); - const viewModeToggle2 = useCallback( - (patternCount: number) => { - return ( - - ); - }, - [viewMode, setDiscoverViewMode, isPlainRecord, stateContainer] - ); + // const viewModeToggle2 = useCallback( + // (patternCount: number) => { + // return ( + // + // ); + // }, + // [viewMode, setDiscoverViewMode, isPlainRecord, stateContainer] + // ); const showChart = useAppStateSelector((state) => !state.hideChart); @@ -167,24 +170,25 @@ export const DiscoverMainContent = ({ ) : null} {viewMode === VIEW_MODE.PATTERN_LEVEL ? ( <> - {/* {viewModeToggle} */} + {viewModeToggle} setDiscoverViewMode(VIEW_MODE.DOCUMENT_LEVEL)} trackUiMetric={trackUiMetric} - getViewModeToggle={(patternCount: number) => { - try { - return ( - - {viewModeToggle2(patternCount)} - - ); - } catch (error) { - // console.log(error); - } - }} + setPatternCount={setPatternCount} + // getViewModeToggle={(patternCount: number) => { + // try { + // return ( + // + // {viewModeToggle2(patternCount)} + // + // ); + // } catch (error) { + // // console.log(error); + // } + // }} /> ) : null} diff --git a/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_table.tsx b/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_table.tsx index 17a6c7d31619a..a0f525e3a5d57 100644 --- a/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_table.tsx +++ b/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_table.tsx @@ -53,6 +53,7 @@ interface EmbeddableLogCategorizationProps { embeddingOrigin?: string; onAddFilter?: () => void; getViewModeToggle: (patternCount: number) => React.ReactElement | undefined; + setPatternCount: (patternCount: number | undefined) => void; /** * Callback to add a filter to filter bar */ @@ -130,7 +131,8 @@ export interface LogCategorizationTableProps { */ trackUiMetric?: (metricType: UiCounterMetricType, eventName: string | string[]) => void; searchSessionId?: string; - getViewModeToggle: (patternCount: number) => React.ReactElement | undefined; + getViewModeToggle?: (patternCount: number) => React.ReactElement | undefined; + setPatternCount: (patternCount: number | undefined) => void; } export const LogCategorizationTable = (props: LogCategorizationTableProps) => { @@ -243,7 +245,8 @@ export const LogCategorizationTable = (props: LogCategorizationTableProps) => { savedSearch, query, onAddFilter, - getViewModeToggle, + getViewModeToggle: getViewModeToggle ?? ((p: number) => <>), + setPatternCount: props.setPatternCount, }); if (!unmounted) { setEmbeddable(initializedEmbeddable); diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx index ef1f762b020bb..c14e1a98c1bed 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx @@ -94,7 +94,7 @@ export const LogCategorizationEmbeddable: FC = ({ } = useAiopsAppContext(); // console.log(lastReloadRequestTime); // const input = useObservable(embeddableInput)!; - const { dataView, savedSearch } = input; + const { dataView, savedSearch, setPatternCount } = input; const getViewModeToggle: (patternCount: number) => React.ReactElement | undefined = input.getViewModeToggle; @@ -169,9 +169,10 @@ export const LogCategorizationEmbeddable: FC = ({ return () => { mounted.current = false; cancelRequest(); + setPatternCount(undefined); }; }, - [cancelRequest, mounted] + [cancelRequest, mounted, setPatternCount] ); const { searchQueryLanguage, searchString, searchQuery } = useSearch( @@ -315,6 +316,10 @@ export const LogCategorizationEmbeddable: FC = ({ toasts, ]); + useEffect(() => { + setPatternCount(data?.categories.length ?? 0); + }, [data, setPatternCount]); + const onAddFilter = useCallback( (values: Filter, alias?: string) => { if (input.onAddFilter === undefined) { diff --git a/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx b/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx index bac2ffb177f02..a56c38883f46f 100644 --- a/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx +++ b/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx @@ -52,6 +52,7 @@ interface EmbeddableLogCategorizationProps { */ onAddFilter?: () => void; getViewModeToggle: () => React.ReactElement | undefined; + setPatternCount: (patternCount: number | undefined) => void; } const localStorage = new Storage(window.localStorage); From 571f863ee49e331751690b60a8f9ac2b0c0770a7 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 4 Apr 2024 11:12:55 +0100 Subject: [PATCH 08/92] fixing options menu and discover tabs --- .../layout/discover_main_content.tsx | 38 +--- .../components/log_categorization/index.ts | 5 +- .../log_categorization_tab.tsx | 26 ++- .../log_categorization_table.tsx | 150 ++------------- .../view_mode_toggle/view_mode_toggle.tsx | 2 +- src/plugins/discover/tsconfig.json | 3 +- src/plugins/saved_search/common/index.ts | 1 + .../aiops_log_pattern_analysis/embeddable.ts | 31 +++ .../log_categorization/embeddable_menu.tsx | 2 +- .../log_categorization_for_embeddable.tsx | 177 +++++++----------- .../log_categorization_page.tsx | 17 +- .../components/log_categorization/utils.ts | 28 +++ .../log_categorization_embeddable.tsx | 55 +----- .../log_categorization_embeddable_factory.ts | 20 +- 14 files changed, 182 insertions(+), 373 deletions(-) create mode 100644 x-pack/packages/ml/aiops_log_pattern_analysis/embeddable.ts create mode 100644 x-pack/plugins/aiops/public/components/log_categorization/utils.ts diff --git a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx index 04ab5d8fe65ce..36e0a31a576f1 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx @@ -13,7 +13,6 @@ import { DataView } from '@kbn/data-views-plugin/common'; import { METRIC_TYPE } from '@kbn/analytics'; import { i18n } from '@kbn/i18n'; import type { DocViewFilterFn } from '@kbn/unified-doc-viewer/types'; -// import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { VIEW_MODE } from '../../../../../common/constants'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; import { DocumentViewModeToggle } from '../../../../components/view_mode_toggle'; @@ -64,11 +63,10 @@ export const DiscoverMainContent = ({ isChartAvailable, }: DiscoverMainContentProps) => { const { trackUiMetric } = useDiscoverServices(); - // const services = useDiscoverServices(); const [patternCount, setPatternCount] = React.useState(undefined); + const setDiscoverViewMode = useCallback( (mode: VIEW_MODE) => { - // @ts-expect-error why is this wrong???? stateContainer.appState.update({ viewMode: mode }); if (trackUiMetric) { @@ -109,26 +107,6 @@ export const DiscoverMainContent = ({ patternCount, ]); - // const viewModeToggle2 = useCallback( - // (patternCount: number) => { - // return ( - // - // ); - // }, - // [viewMode, setDiscoverViewMode, isPlainRecord, stateContainer] - // ); - const showChart = useAppStateSelector((state) => !state.hideChart); return ( @@ -170,25 +148,13 @@ export const DiscoverMainContent = ({ ) : null} {viewMode === VIEW_MODE.PATTERN_LEVEL ? ( <> - {viewModeToggle} setDiscoverViewMode(VIEW_MODE.DOCUMENT_LEVEL)} trackUiMetric={trackUiMetric} setPatternCount={setPatternCount} - // getViewModeToggle={(patternCount: number) => { - // try { - // return ( - // - // {viewModeToggle2(patternCount)} - // - // ); - // } catch (error) { - // // console.log(error); - // } - // }} + viewModeToggle={viewModeToggle} /> ) : null} diff --git a/src/plugins/discover/public/application/main/components/log_categorization/index.ts b/src/plugins/discover/public/application/main/components/log_categorization/index.ts index deeb28928163d..01018047813a8 100644 --- a/src/plugins/discover/public/application/main/components/log_categorization/index.ts +++ b/src/plugins/discover/public/application/main/components/log_categorization/index.ts @@ -6,8 +6,5 @@ * Side Public License, v 1. */ -export { - LogCategorizationTable, - type EmbeddableLogCategorizationInput, -} from './log_categorization_table'; +export { LogCategorizationTable } from './log_categorization_table'; export { LogCategorizationTab } from './log_categorization_tab'; diff --git a/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_tab.tsx b/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_tab.tsx index 23c4d24fac9c9..3abf5e9444b91 100644 --- a/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_tab.tsx +++ b/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_tab.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { useQuerySubscriber } from '@kbn/unified-field-list/src/hooks/use_query_subscriber'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { useSavedSearch } from '../../services/discover_state_provider'; import { LogCategorizationTable, @@ -16,20 +17,31 @@ import { import { useDiscoverServices } from '../../../../hooks/use_discover_services'; export const LogCategorizationTab: React.FC< - Omit + Omit > = React.memo((props) => { const services = useDiscoverServices(); const querySubscriberResult = useQuerySubscriber({ data: services.data, }); const savedSearch = useSavedSearch(); + const [optionsMenu, setOptionsMenu] = React.useState(undefined); return ( - + <> + + + {props.viewModeToggle} + + {optionsMenu} + + + + ); }); diff --git a/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_table.tsx b/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_table.tsx index a0f525e3a5d57..52b24b7e80de8 100644 --- a/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_table.tsx +++ b/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_table.tsx @@ -7,17 +7,13 @@ */ import React, { useEffect, useMemo, useRef, useState } from 'react'; -import type { Filter, Query, AggregateQuery } from '@kbn/es-query'; import { METRIC_TYPE, UiCounterMetricType } from '@kbn/analytics'; -import type { DataView } from '@kbn/data-views-plugin/public'; -import { - EmbeddableInput, - EmbeddableOutput, - ErrorEmbeddable, - IEmbeddable, - isErrorEmbeddable, -} from '@kbn/embeddable-plugin/public'; -import type { SavedSearch } from '@kbn/saved-search-plugin/public'; +import { ErrorEmbeddable, IEmbeddable, isErrorEmbeddable } from '@kbn/embeddable-plugin/public'; +import type { + EmbeddableLogCategorizationInput, + EmbeddableLogCategorizationOutput, + EmbeddableLogCategorizationProps, +} from '@kbn/aiops-log-pattern-analysis/embeddable'; import { EuiFlexItem } from '@elastic/eui'; import { css } from '@emotion/react'; import useObservable from 'react-use/lib/useObservable'; @@ -25,128 +21,30 @@ import { of } from 'rxjs'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; import { FIELD_STATISTICS_LOADED } from './constants'; import type { DiscoverStateContainer } from '../../services/discover_state'; -export interface RandomSamplingOption { - mode: 'random_sampling'; - seed: string; - probability: number; -} -export interface NormalSamplingOption { - mode: 'normal_sampling'; - seed: string; - shardSize: number; -} - -export interface NoSamplingOption { - mode: 'no_sampling'; - seed: string; -} - -export type SamplingOption = RandomSamplingOption | NormalSamplingOption | NoSamplingOption; - -interface EmbeddableLogCategorizationProps { - dataView: DataView; - savedSearch?: SavedSearch | null; - query?: T; - filters?: Filter[]; - id?: string; - embeddingOrigin?: string; - onAddFilter?: () => void; - getViewModeToggle: (patternCount: number) => React.ReactElement | undefined; - setPatternCount: (patternCount: number | undefined) => void; - /** - * Callback to add a filter to filter bar - */ - // onAddFilter?: (field: DataViewField | string, value: string, type: '+' | '-') => void; -} - -export type EmbeddableLogCategorizationInput = EmbeddableInput & EmbeddableLogCategorizationProps; - -export type EmbeddableLogCategorizationOutput = EmbeddableOutput & { indexPatterns?: DataView[] }; - -// export interface DataVisualizerGridEmbeddableInput extends EmbeddableInput { -// // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!rename -// dataView: DataView; -// savedSearch?: SavedSearch; -// query?: Query | AggregateQuery; -// visibleFieldNames?: string[]; -// filters?: Filter[]; -// showPreviewByDefault?: boolean; -// /** -// * Callback to add a filter to filter bar -// */ -// onAddFilter?: (field: DataViewField | string, value: string, type: '+' | '-') => void; -// sessionId?: string; -// fieldsToFetch?: string[]; -// totalDocuments?: number; -// samplingOption?: SamplingOption; -// getViewModeToggle: (patternCount: number) => React.ReactElement | undefined; -// } -// export interface DataVisualizerGridEmbeddableOutput extends EmbeddableOutput { -// showDistributions?: boolean; -// } - -export interface LogCategorizationTableProps { - /** - * Determines which columns are displayed - */ - columns: string[]; - /** - * The used data view - */ - dataView: DataView; - /** - * Saved search description - */ +export type LogCategorizationTableProps = EmbeddableLogCategorizationProps & { searchDescription?: string; - /** - * Saved search title - */ - searchTitle?: string; - /** - * Optional saved search - */ - savedSearch?: SavedSearch; - /** - * Optional query to update the table content - */ - query?: Query | AggregateQuery; - /** - * Filters query to update the table content - */ - filters?: Filter[]; + /** * State container with persisted settings */ stateContainer?: DiscoverStateContainer; - /** - * Callback to add a filter to filter bar - */ - // onAddFilter?: (field: DataViewField | string, value: string, type: '+' | '-') => void; - onAddFilter?: () => void; - /** - * Metric tracking function - * @param metricType - * @param eventName - */ trackUiMetric?: (metricType: UiCounterMetricType, eventName: string | string[]) => void; searchSessionId?: string; - getViewModeToggle?: (patternCount: number) => React.ReactElement | undefined; - setPatternCount: (patternCount: number | undefined) => void; -} + viewModeToggle: React.ReactElement; + setOptionsMenu: (optionsMenu: React.ReactElement | undefined) => void; +}; export const LogCategorizationTable = (props: LogCategorizationTableProps) => { const { dataView, savedSearch, query, - columns, filters, stateContainer, onAddFilter, trackUiMetric, searchSessionId, - getViewModeToggle, } = props; const totalHits = useObservable(stateContainer?.dataState.data$.totalHits$ ?? of(undefined)); @@ -166,29 +64,14 @@ export const LogCategorizationTable = (props: LogCategorizationTableProps) => { ); useEffect(() => { - // const availableFields$ = stateContainer?.dataState.data$.availableFields$; - // const sub = embeddable?.getOutput$().subscribe((output: DataVisualizerGridEmbeddableOutput) => { - // if (output.showDistributions !== undefined && stateContainer) { - // stateContainer.appState.update({ hideAggregatedPreview: !output.showDistributions }); - // } - // }); - const refetch = stateContainer?.dataState.refetch$.subscribe(() => { if (embeddable && !isErrorEmbeddable(embeddable)) { embeddable.updateInput({ lastReloadRequestTime: Date.now() }); } }); - // const fields = availableFields$?.subscribe(() => { - // if (embeddable && !isErrorEmbeddable(embeddable) && !availableFields$?.getValue().error) { - // embeddable.updateInput({ fieldsToFetch: availableFields$?.getValue().fields }); - // } - // }); - return () => { - // sub?.unsubscribe(); refetch?.unsubscribe(); - // fields?.unsubscribe(); }; }, [embeddable, stateContainer]); @@ -200,8 +83,6 @@ export const LogCategorizationTable = (props: LogCategorizationTableProps) => { savedSearch, query, filters, - // visibleFieldNames: columns, - // onAddFilter, }); embeddable.reload(); } @@ -210,9 +91,7 @@ export const LogCategorizationTable = (props: LogCategorizationTableProps) => { dataView, savedSearch, query, - columns, filters, - onAddFilter, searchSessionId, totalDocuments, stateContainer, @@ -220,11 +99,6 @@ export const LogCategorizationTable = (props: LogCategorizationTableProps) => { useEffect(() => { if (showPreviewByDefault && embeddable && !isErrorEmbeddable(embeddable)) { - // Update embeddable whenever one of the important input changes - // embeddable.updateInput({ - // showPreviewByDefault, - // }); - embeddable.reload(); } }, [showPreviewByDefault, embeddable]); @@ -245,8 +119,8 @@ export const LogCategorizationTable = (props: LogCategorizationTableProps) => { savedSearch, query, onAddFilter, - getViewModeToggle: getViewModeToggle ?? ((p: number) => <>), setPatternCount: props.setPatternCount, + setOptionsMenu: props.setOptionsMenu, }); if (!unmounted) { setEmbeddable(initializedEmbeddable); diff --git a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx index d798feafc09ce..d83016096e8cb 100644 --- a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx +++ b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx @@ -92,7 +92,7 @@ export const DocumentViewModeToggle = ({ diff --git a/src/plugins/discover/tsconfig.json b/src/plugins/discover/tsconfig.json index 4e6213e745796..0e17ef1ea7131 100644 --- a/src/plugins/discover/tsconfig.json +++ b/src/plugins/discover/tsconfig.json @@ -84,7 +84,8 @@ "@kbn/deeplinks-analytics", "@kbn/shared-ux-markdown", "@kbn/data-view-utils", - "@kbn/presentation-publishing" + "@kbn/presentation-publishing", + "@kbn/aiops-log-pattern-analysis", ], "exclude": ["target/**/*"] } diff --git a/src/plugins/saved_search/common/index.ts b/src/plugins/saved_search/common/index.ts index 0ac92232fb3b8..f0569a86ca39a 100644 --- a/src/plugins/saved_search/common/index.ts +++ b/src/plugins/saved_search/common/index.ts @@ -19,6 +19,7 @@ export type { export enum VIEW_MODE { DOCUMENT_LEVEL = 'documents', AGGREGATED_LEVEL = 'aggregated', + PATTERN_LEVEL = 'patterns', } export { diff --git a/x-pack/packages/ml/aiops_log_pattern_analysis/embeddable.ts b/x-pack/packages/ml/aiops_log_pattern_analysis/embeddable.ts new file mode 100644 index 0000000000000..c448250259b16 --- /dev/null +++ b/x-pack/packages/ml/aiops_log_pattern_analysis/embeddable.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { EmbeddableInput, EmbeddableOutput } from '@kbn/embeddable-plugin/public'; +import type { Query, AggregateQuery, Filter } from '@kbn/es-query'; +import type { SavedSearch } from '@kbn/saved-search-plugin/public'; +import type { DataView } from '@kbn/data-views-plugin/public'; + +export interface EmbeddableLogCategorizationProps { + dataView: DataView; + savedSearch?: SavedSearch | null; + query?: T; + filters?: Filter[]; + id?: string; + embeddingOrigin?: string; + /** + * Callback to add a filter to filter bar + */ + onAddFilter?: () => void; + // getViewModeToggle: (patternCount: number) => React.ReactElement | undefined; + setPatternCount: (patternCount: number | undefined) => void; + setOptionsMenu: (optionsMenu: React.ReactElement | undefined) => void; +} + +export type EmbeddableLogCategorizationInput = EmbeddableInput & EmbeddableLogCategorizationProps; + +export type EmbeddableLogCategorizationOutput = EmbeddableOutput & { indexPatterns?: DataView[] }; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx b/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx index 00b577205eb37..cce9390da3016 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx @@ -116,7 +116,7 @@ export const EmbeddableMenu: FC = ({ )} helpText={ <> - {categoryCount !== undefined ? ( + {categoryCount !== undefined && widenessOption !== 'No minimum' ? ( <> Total patterns in {widenessOption}: {categoryCount} diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx index c14e1a98c1bed..a1a627d72495e 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx @@ -15,7 +15,6 @@ import type { Filter } from '@kbn/es-query'; import { buildEmptyFilter } from '@kbn/es-query'; import { usePageUrlState } from '@kbn/ml-url-state'; import type { FieldValidationResults } from '@kbn/ml-category-validator'; -import { stringHash } from '@kbn/ml-string-hash'; import useMount from 'react-use/lib/useMount'; import { ES_FIELD_TYPES } from '@kbn/field-types'; import type { unitOfTime } from 'moment'; @@ -26,6 +25,7 @@ import type { Category } from '@kbn/aiops-log-pattern-analysis/types'; import type { CategorizationAdditionalFilter } from '@kbn/aiops-log-pattern-analysis/create_category_request'; import { AIOPS_TELEMETRY_ID } from '@kbn/aiops-common/constants'; +import type { EmbeddableLogCategorizationInput } from '@kbn/aiops-log-pattern-analysis/embeddable'; import { type LogCategorizationPageUrlState, getDefaultLogCategorizationAppState, @@ -39,17 +39,14 @@ import { useCategorizeRequest } from './use_categorize_request'; import type { EventRate } from './use_categorize_request'; import { CategoryTable } from './category_table'; import { InformationText } from './information_text'; -// import { SamplingMenu } from './sampling_menu'; -// import { TechnicalPreviewBadge } from './technical_preview_badge'; import { LoadingCategorization } from './loading_categorization'; import { useValidateFieldRequest } from './use_validate_category_field'; import { FieldValidationCallout } from './category_validation_callout'; -import type { DocumentStats } from '../../hooks/use_document_count_stats'; import { EmbeddableMenu } from './embeddable_menu'; import { useWiderTimeRange } from './use_wider_time_range'; import type { AiOpsKey, AiOpsStorageMapped } from '../../types/storage'; import { AIOPS_PATTERN_ANALYSIS_WIDENESS_PREFERENCE } from '../../types/storage'; -import type { EmbeddableLogCategorizationInput } from '../../embeddables/log_categorization/log_categorization_embeddable'; +import { createDocumentStatsHash } from './utils'; enum SELECTED_TAB { BUCKET, @@ -74,7 +71,6 @@ export interface LogCategorizationPageProps { embeddingOrigin: string; additionalFilter?: CategorizationAdditionalFilter; input: Readonly; - // embeddableInput: Readonly>; } const BAR_TARGET = 20; @@ -92,11 +88,9 @@ export const LogCategorizationEmbeddable: FC = ({ }, uiSettings, } = useAiopsAppContext(); - // console.log(lastReloadRequestTime); - // const input = useObservable(embeddableInput)!; - const { dataView, savedSearch, setPatternCount } = input; - const getViewModeToggle: (patternCount: number) => React.ReactElement | undefined = - input.getViewModeToggle; + const { dataView, savedSearch, setPatternCount, setOptionsMenu } = input; + // const getViewModeToggle: (patternCount: number) => React.ReactElement | undefined = + // input.getViewModeToggle; const [widenessOption, setWidenessOption] = useStorage< AiOpsKey, @@ -122,9 +116,7 @@ export const LogCategorizationEmbeddable: FC = ({ ); const [selectedCategory, setSelectedCategory] = useState(null); const [selectedField, setSelectedField] = useState(null); - // const [wideness, setWideness] = useState('1 week'); const [fields, setFields] = useState([]); - const [selectedSavedSearch /* , setSelectedSavedSearch*/] = useState(savedSearch ?? null); const [previousDocumentStatsHash, setPreviousDocumentStatsHash] = useState(0); const [loading, setLoading] = useState(true); const [eventRate, setEventRate] = useState([]); @@ -176,7 +168,7 @@ export const LogCategorizationEmbeddable: FC = ({ ); const { searchQueryLanguage, searchString, searchQuery } = useSearch( - { dataView, savedSearch: selectedSavedSearch }, + { dataView, savedSearch: savedSearch ?? null }, stateFromUrl, true ); @@ -202,7 +194,8 @@ export const LogCategorizationEmbeddable: FC = ({ selectedField === null || timeField === undefined || earliest === undefined || - latest === undefined + latest === undefined || + widenessOption === undefined ) { return; } @@ -284,9 +277,6 @@ export const LogCategorizationEmbeddable: FC = ({ // eslint-disable-next-line no-console console.log('categories', categories); - - // console.log('categoriesInBucket', categoriesInBucket); - setSelectedTab(SELECTED_TAB.BUCKET); } } catch (error) { @@ -317,8 +307,37 @@ export const LogCategorizationEmbeddable: FC = ({ ]); useEffect(() => { - setPatternCount(data?.categories.length ?? 0); - }, [data, setPatternCount]); + setPatternCount(data?.categories.length); + setOptionsMenu( + <> + {randomSampler !== undefined ? ( + loadCategories()} + fields={fields} + setSelectedField={setSelectedField} + selectedField={selectedField} + widenessOption={widenessOption} + setWidenessOption={setWidenessOption} + categoryCount={data?.totalCategories} + /> + ) : null} + + ); + return () => { + setOptionsMenu(undefined); + }; + }, [ + data, + fields, + loadCategories, + randomSampler, + selectedField, + setOptionsMenu, + setPatternCount, + setWidenessOption, + widenessOption, + ]); const onAddFilter = useCallback( (values: Filter, alias?: string) => { @@ -345,7 +364,7 @@ export const LogCategorizationEmbeddable: FC = ({ return; } - const hash = createDocumentStatsHash(documentStats, selectedField.name, widenessOption); + const hash = createDocumentStatsHash(documentStats, [selectedField.name, widenessOption]); if (hash !== previousDocumentStatsHash) { randomSampler.setDocCount(documentStats.totalCount); setEventRate( @@ -354,10 +373,7 @@ export const LogCategorizationEmbeddable: FC = ({ docCount, })) ); - // setData(null); - // if (fieldValidationResult !== null) { loadCategories(); - // } setPreviousDocumentStatsHash(hash); } }, [ @@ -392,9 +408,8 @@ export const LogCategorizationEmbeddable: FC = ({ direction="column" gutterSize="none" responsive={false} - // data-test-subj="dscMainContent" > - + {/* <>{getViewModeToggle(data?.categories?.length ?? 0)} @@ -413,53 +428,13 @@ export const LogCategorizationEmbeddable: FC = ({ ) : null} - - {/* - - - -

- -

-
-
- - - - - - forceRefresh()} /> - -
-
*/} - {/* */} +
*/} + <> - {/* {showTabs === false && loading === false ? ( - - ) : null} */} - - {/* - - - forceRefresh()} /> - - */} - {loading === true ? : null} = ({ data !== null && data.categories.length > 0 && selectedField !== null ? ( - <> - - + ) : null} @@ -504,21 +477,3 @@ export const LogCategorizationEmbeddable: FC = ({ ); }; - -/** - * Creates a hash from the document stats to determine if the document stats have changed. - */ -function createDocumentStatsHash( - documentStats: DocumentStats, - selectedFieldName: string, - wideness: WidenessOption -) { - const lastTimeStampMs = documentStats.documentCountStats?.lastDocTimeStampMs; - const totalCount = documentStats.documentCountStats?.totalCount; - const times = Object.keys(documentStats.documentCountStats?.buckets ?? {}); - const firstBucketTimeStamp = times.length ? times[0] : undefined; - const lastBucketTimeStamp = times.length ? times[times.length - 1] : undefined; - return stringHash( - `${lastTimeStampMs}${totalCount}${firstBucketTimeStamp}${lastBucketTimeStamp}${selectedFieldName}${wideness}` - ); -} diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx index 5dd2e260624c0..2ff408d4054d4 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx @@ -27,7 +27,6 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { usePageUrlState, useUrlState } from '@kbn/ml-url-state'; import type { FieldValidationResults } from '@kbn/ml-category-validator'; import type { SearchQueryLanguage } from '@kbn/ml-query-utils'; -import { stringHash } from '@kbn/ml-string-hash'; import { AIOPS_TELEMETRY_ID } from '@kbn/aiops-common/constants'; import type { Category } from '@kbn/aiops-log-pattern-analysis/types'; @@ -51,7 +50,7 @@ import { InformationText } from './information_text'; import { SamplingMenu } from './sampling_menu'; import { useValidateFieldRequest } from './use_validate_category_field'; import { FieldValidationCallout } from './category_validation_callout'; -import type { DocumentStats } from '../../hooks/use_document_count_stats'; +import { createDocumentStatsHash } from './utils'; const BAR_TARGET = 20; const DEFAULT_SELECTED_FIELD = 'message'; @@ -255,8 +254,6 @@ export const LogCategorizationPage: FC = ({ embeddin docCount, })) ); - // setData(null); - // setFieldValidationResult(null); setTotalCount(documentStats.totalCount); if (fieldValidationResult !== null) { loadCategories(); @@ -411,15 +408,3 @@ export const LogCategorizationPage: FC = ({ embeddin ); }; - -/** - * Creates a hash from the document stats to determine if the document stats have changed. - */ -function createDocumentStatsHash(documentStats: DocumentStats) { - const lastTimeStampMs = documentStats.documentCountStats?.lastDocTimeStampMs; - const totalCount = documentStats.documentCountStats?.totalCount; - const times = Object.keys(documentStats.documentCountStats?.buckets ?? {}); - const firstBucketTimeStamp = times.length ? times[0] : undefined; - const lastBucketTimeStamp = times.length ? times[times.length - 1] : undefined; - return stringHash(`${lastTimeStampMs}${totalCount}${firstBucketTimeStamp}${lastBucketTimeStamp}`); -} diff --git a/x-pack/plugins/aiops/public/components/log_categorization/utils.ts b/x-pack/plugins/aiops/public/components/log_categorization/utils.ts new file mode 100644 index 0000000000000..4881b754438f4 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/log_categorization/utils.ts @@ -0,0 +1,28 @@ +/* + * 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 { stringHash } from '@kbn/ml-string-hash'; +import type { DocumentStats } from '../../hooks/use_document_count_stats'; + +/** + * Creates a hash from the document stats to determine if the document stats have changed. + */ +export function createDocumentStatsHash( + documentStats: DocumentStats, + additionalStrings: string[] = [] +) { + const lastTimeStampMs = documentStats.documentCountStats?.lastDocTimeStampMs; + const totalCount = documentStats.documentCountStats?.totalCount; + const times = Object.keys(documentStats.documentCountStats?.buckets ?? {}); + const firstBucketTimeStamp = times.length ? times[0] : undefined; + const lastBucketTimeStamp = times.length ? times[times.length - 1] : undefined; + return stringHash( + `${lastTimeStampMs}${totalCount}${firstBucketTimeStamp}${lastBucketTimeStamp}${additionalStrings.join( + '' + )}` + ); +} diff --git a/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx b/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx index a56c38883f46f..7e25250f89ce5 100644 --- a/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx +++ b/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx @@ -7,7 +7,7 @@ import React, { Suspense } from 'react'; import ReactDOM from 'react-dom'; -import type { EmbeddableInput, EmbeddableOutput, IContainer } from '@kbn/embeddable-plugin/public'; +import type { IContainer } from '@kbn/embeddable-plugin/public'; import { Embeddable as AbstractEmbeddable } from '@kbn/embeddable-plugin/public'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import type { ThemeServiceStart } from '@kbn/core-theme-browser'; @@ -20,47 +20,24 @@ import { pick } from 'lodash'; import type { LensPublicStart } from '@kbn/lens-plugin/public'; import { Subject } from 'rxjs'; import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; -import type { DataView } from '@kbn/data-views-plugin/common'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; -// import { EmbeddableInputTracker } from './embeddable_chart_component_wrapper'; -import type { SavedSearch } from '@kbn/saved-search-plugin/public'; -import type { Filter } from '@kbn/es-query'; -import type { Query, AggregateQuery } from '@kbn/es-query'; import { StorageContextProvider } from '@kbn/ml-local-storage'; import { Storage } from '@kbn/kibana-utils-plugin/public'; import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; -// import useObservable from 'react-use/lib/useObservable'; import type { EmbeddableLogCategorizationType } from '@kbn/aiops-log-pattern-analysis/constants'; import { EMBEDDABLE_ORIGIN } from '@kbn/aiops-common/constants'; +import type { + EmbeddableLogCategorizationInput, + EmbeddableLogCategorizationOutput, +} from '@kbn/aiops-log-pattern-analysis/embeddable'; import { AiopsAppContext, type AiopsAppDependencies } from '../../hooks/use_aiops_app_context'; import { AIOPS_STORAGE_KEYS } from '../../types/storage'; import { LogCategorizationEmbeddable } from '../../components/log_categorization/log_categorization_for_embeddable'; -// import type { EmbeddableChangePointChartProps } from './embeddable_change_point_chart_component'; - -interface EmbeddableLogCategorizationProps { - dataView: DataView; - savedSearch?: SavedSearch | null; - query?: T; - filters?: Filter[]; - id?: string; - embeddingOrigin?: string; - /** - * Callback to add a filter to filter bar - */ - onAddFilter?: () => void; - getViewModeToggle: () => React.ReactElement | undefined; - setPatternCount: (patternCount: number | undefined) => void; -} - const localStorage = new Storage(window.localStorage); -export type EmbeddableLogCategorizationInput = EmbeddableInput & EmbeddableLogCategorizationProps; - -export type EmbeddableLogCategorizationOutput = EmbeddableOutput & { indexPatterns?: DataView[] }; - export interface EmbeddableLogCategorizationDeps { theme: ThemeServiceStart; data: DataPublicPluginStart; @@ -104,14 +81,7 @@ export class EmbeddableLogCategorization extends AbstractEmbeddable< } private async initOutput() { - // const { - // data: { dataViews: dataViewsService }, - // } = this.deps; - const { dataView } = this.getInput(); - - // const dataView = await dataViewsService.get(dataViewId); - this.updateOutput({ indexPatterns: [dataView], }); @@ -144,7 +114,7 @@ export class EmbeddableLogCategorization extends AbstractEmbeddable< this.node.setAttribute('data-shared-item', ''); // test subject selector for functional tests - this.node.setAttribute('data-test-subj', 'aiopsEmbeddableChangePointChart'); + this.node.setAttribute('data-test-subj', 'aiopsEmbeddableLogCategorization'); const I18nContext = this.deps.i18n.Context; @@ -154,8 +124,6 @@ export class EmbeddableLogCategorization extends AbstractEmbeddable< }; const input = this.getInput(); - // const input$ = this.getInput$(); - // const input = useObservable(input$); const aiopsAppContextValue = { embeddingOrigin: this.parent?.type ?? input.embeddingOrigin ?? EMBEDDABLE_ORIGIN, @@ -171,18 +139,9 @@ export class EmbeddableLogCategorization extends AbstractEmbeddable< this.destroy()} - embeddingOrigin={'discover-change-me'} + embeddingOrigin={'discover-change-me'} // !!!!!!!!!!!!!! input={input} /> - {/* */}
diff --git a/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable_factory.ts b/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable_factory.ts index 9f60aa8c9ab29..db8d480d0f0f9 100644 --- a/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable_factory.ts +++ b/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable_factory.ts @@ -8,17 +8,11 @@ import { i18n } from '@kbn/i18n'; import type { StartServicesAccessor } from '@kbn/core/public'; import type { EmbeddableFactoryDefinition, IContainer } from '@kbn/embeddable-plugin/public'; -// import { DATA_VISUALIZER_GRID_EMBEDDABLE_TYPE } from './constants'; -// import type { DataVisualizerGridEmbeddableServices } from './grid_embeddable'; -// import type { DataVisualizerGridEmbeddableInput } from './types'; -// import type { EmbeddableLogCategorizationType } from '../../../common/constants'; import { EMBEDDABLE_LOG_CATEGORIZATION_TYPE } from '@kbn/aiops-log-pattern-analysis/constants'; +import type { EmbeddableLogCategorizationInput } from '@kbn/aiops-log-pattern-analysis/embeddable'; import type { AiopsPluginStart, AiopsPluginStartDeps } from '../../types'; -import type { - EmbeddableLogCategorizationDeps, - EmbeddableLogCategorizationInput, -} from './log_categorization_embeddable'; +import type { EmbeddableLogCategorizationDeps } from './log_categorization_embeddable'; export class EmbeddableLogCategorizationFactory implements EmbeddableFactoryDefinition @@ -37,6 +31,10 @@ export class EmbeddableLogCategorizationFactory private readonly getStartServices: StartServicesAccessor ) {} + public getName() { + return this.name; + } + public async isEditable() { return false; } @@ -79,8 +77,10 @@ export class EmbeddableLogCategorizationFactory } public async create(input: EmbeddableLogCategorizationInput, parent?: IContainer) { - const deps = await this.getServices(); - const { EmbeddableLogCategorization } = await import('./log_categorization_embeddable'); + const [deps, { EmbeddableLogCategorization }] = await Promise.all([ + this.getServices(), + import('./log_categorization_embeddable'), + ]); return new EmbeddableLogCategorization(this.type, deps, input, parent); } } From 8c0d25b5ab13453668c97e4ed99914f82ba23889 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 4 Apr 2024 13:49:21 +0100 Subject: [PATCH 09/92] code clean up --- .../layout/discover_main_content.tsx | 3 ++ .../log_categorization/constants.ts | 5 ++- .../log_categorization_tab.tsx | 8 ++--- .../log_categorization_table.tsx | 21 +++--------- .../panels_toggle/panels_toggle.tsx | 5 --- .../aiops_log_pattern_analysis/constants.ts | 10 ------ .../aiops_log_pattern_analysis/embeddable.ts | 5 ++- .../log_categorization/embeddable_menu.tsx | 34 ++++++++++++------- .../log_categorization_for_embeddable.tsx | 28 +-------------- .../log_categorization_embeddable.tsx | 2 +- .../log_categorization_embeddable_factory.ts | 14 ++++---- .../embeddables/register_embeddables.ts | 13 ++++--- 12 files changed, 56 insertions(+), 92 deletions(-) delete mode 100644 x-pack/packages/ml/aiops_log_pattern_analysis/constants.ts diff --git a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx index 36e0a31a576f1..cddab7d1464b2 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx @@ -23,6 +23,7 @@ import { DOCUMENTS_VIEW_CLICK, FIELD_STATISTICS_VIEW_CLICK } from '../field_stat import { useAppStateSelector } from '../../services/discover_app_state_container'; import type { PanelsToggleProps } from '../../../../components/panels_toggle'; import { LogCategorizationTab } from '../log_categorization/log_categorization_tab'; +import { PATTERN_ANALYSIS_LOADED } from '../log_categorization/constants'; const DROP_PROPS = { value: { @@ -72,6 +73,8 @@ export const DiscoverMainContent = ({ if (trackUiMetric) { if (mode === VIEW_MODE.AGGREGATED_LEVEL) { trackUiMetric(METRIC_TYPE.CLICK, FIELD_STATISTICS_VIEW_CLICK); + } else if (mode === VIEW_MODE.PATTERN_LEVEL) { + trackUiMetric(METRIC_TYPE.CLICK, PATTERN_ANALYSIS_LOADED); } else { trackUiMetric(METRIC_TYPE.CLICK, DOCUMENTS_VIEW_CLICK); } diff --git a/src/plugins/discover/public/application/main/components/log_categorization/constants.ts b/src/plugins/discover/public/application/main/components/log_categorization/constants.ts index bf1a36da59ecf..8441a308965e7 100644 --- a/src/plugins/discover/public/application/main/components/log_categorization/constants.ts +++ b/src/plugins/discover/public/application/main/components/log_categorization/constants.ts @@ -7,6 +7,5 @@ */ /** Telemetry related to field statistics table **/ -export const FIELD_STATISTICS_LOADED = 'field_statistics_loaded'; -export const FIELD_STATISTICS_VIEW_CLICK = 'field_statistics_view_click'; -export const DOCUMENTS_VIEW_CLICK = 'documents_view_click'; +export const PATTERN_ANALYSIS_LOADED = 'pattern_analysis_loaded'; +export const PATTERN_ANALYSIS_VIEW_CLICK = 'pattern_analysis_view_click'; diff --git a/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_tab.tsx b/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_tab.tsx index 3abf5e9444b91..991d3e7aa2eee 100644 --- a/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_tab.tsx +++ b/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_tab.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React from 'react'; +import React, { useState, memo, type ReactElement, type FC } from 'react'; import { useQuerySubscriber } from '@kbn/unified-field-list/src/hooks/use_query_subscriber'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { useSavedSearch } from '../../services/discover_state_provider'; @@ -16,15 +16,15 @@ import { } from './log_categorization_table'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; -export const LogCategorizationTab: React.FC< +export const LogCategorizationTab: FC< Omit -> = React.memo((props) => { +> = memo((props) => { const services = useDiscoverServices(); const querySubscriberResult = useQuerySubscriber({ data: services.data, }); const savedSearch = useSavedSearch(); - const [optionsMenu, setOptionsMenu] = React.useState(undefined); + const [optionsMenu, setOptionsMenu] = useState(undefined); return ( <> diff --git a/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_table.tsx b/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_table.tsx index 52b24b7e80de8..7e1e971d3edbd 100644 --- a/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_table.tsx +++ b/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_table.tsx @@ -19,7 +19,7 @@ import { css } from '@emotion/react'; import useObservable from 'react-use/lib/useObservable'; import { of } from 'rxjs'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; -import { FIELD_STATISTICS_LOADED } from './constants'; +import { PATTERN_ANALYSIS_LOADED } from './constants'; import type { DiscoverStateContainer } from '../../services/discover_state'; export type LogCategorizationTableProps = EmbeddableLogCategorizationProps & { @@ -58,11 +58,6 @@ export const LogCategorizationTable = (props: LogCategorizationTableProps) => { >(); const embeddableRoot: React.RefObject = useRef(null); - const showPreviewByDefault = useMemo( - () => (stateContainer ? !stateContainer.appState.getState().hideAggregatedPreview : true), - [stateContainer] - ); - useEffect(() => { const refetch = stateContainer?.dataState.refetch$.subscribe(() => { if (embeddable && !isErrorEmbeddable(embeddable)) { @@ -97,12 +92,6 @@ export const LogCategorizationTable = (props: LogCategorizationTableProps) => { stateContainer, ]); - useEffect(() => { - if (showPreviewByDefault && embeddable && !isErrorEmbeddable(embeddable)) { - embeddable.reload(); - } - }, [showPreviewByDefault, embeddable]); - useEffect(() => { let unmounted = false; const loadEmbeddable = async () => { @@ -133,14 +122,14 @@ export const LogCategorizationTable = (props: LogCategorizationTableProps) => { unmounted = true; }; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [services.embeddable, showPreviewByDefault]); + }, [services.embeddable]); // We can only render after embeddable has already initialized useEffect(() => { if (embeddableRoot.current && embeddable) { embeddable.render(embeddableRoot.current); - trackUiMetric?.(METRIC_TYPE.LOADED, FIELD_STATISTICS_LOADED); + trackUiMetric?.(METRIC_TYPE.LOADED, PATTERN_ANALYSIS_LOADED); } return () => { @@ -149,7 +138,7 @@ export const LogCategorizationTable = (props: LogCategorizationTableProps) => { }; }, [embeddable, embeddableRoot, trackUiMetric]); - const statsTableCss = css` + const style = css` overflow-y: auto; .kbnDocTableWrapper { @@ -158,7 +147,7 @@ export const LogCategorizationTable = (props: LogCategorizationTableProps) => { `; return ( - +
= ({ renderedFor, isChartAvailable, }) => { - try { - useAppStateSelector((state) => Boolean(state.hideChart)); - } catch (error) { - // console.log(error); - } const isChartHidden = useAppStateSelector((state) => Boolean(state.hideChart)); const onToggleChart = useCallback(() => { diff --git a/x-pack/packages/ml/aiops_log_pattern_analysis/constants.ts b/x-pack/packages/ml/aiops_log_pattern_analysis/constants.ts deleted file mode 100644 index 4f5f4efd64906..0000000000000 --- a/x-pack/packages/ml/aiops_log_pattern_analysis/constants.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export const EMBEDDABLE_LOG_CATEGORIZATION_TYPE = 'aiopsLogCategorization' as const; - -export type EmbeddableLogCategorizationType = typeof EMBEDDABLE_LOG_CATEGORIZATION_TYPE; diff --git a/x-pack/packages/ml/aiops_log_pattern_analysis/embeddable.ts b/x-pack/packages/ml/aiops_log_pattern_analysis/embeddable.ts index c448250259b16..0b952dacf3436 100644 --- a/x-pack/packages/ml/aiops_log_pattern_analysis/embeddable.ts +++ b/x-pack/packages/ml/aiops_log_pattern_analysis/embeddable.ts @@ -21,7 +21,6 @@ export interface EmbeddableLogCategorizationProps { * Callback to add a filter to filter bar */ onAddFilter?: () => void; - // getViewModeToggle: (patternCount: number) => React.ReactElement | undefined; setPatternCount: (patternCount: number | undefined) => void; setOptionsMenu: (optionsMenu: React.ReactElement | undefined) => void; } @@ -29,3 +28,7 @@ export interface EmbeddableLogCategorizationProps { export type EmbeddableLogCategorizationInput = EmbeddableInput & EmbeddableLogCategorizationProps; export type EmbeddableLogCategorizationOutput = EmbeddableOutput & { indexPatterns?: DataView[] }; + +export const EMBEDDABLE_LOG_CATEGORIZATION_TYPE = 'aiopsLogCategorization' as const; + +export type EmbeddableLogCategorizationType = typeof EMBEDDABLE_LOG_CATEGORIZATION_TYPE; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx b/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx index cce9390da3016..de5565a5edfde 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx @@ -20,6 +20,7 @@ import React, { useState, useMemo } from 'react'; import type { DataViewField } from '@kbn/data-views-plugin/public'; import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; import type { RandomSampler } from './sampling_menu'; import { SamplingPanel } from './sampling_menu/sampling_panel'; import type { WidenessOption } from './log_categorization_for_embeddable'; @@ -46,8 +47,8 @@ export const EmbeddableMenu: FC = ({ categoryCount, reload, }) => { - const [showPopover, setShowPopover] = useState(false); - const togglePopover = () => setShowPopover(!showPopover); + const [showMenu, setShowMenu] = useState(false); + const togglePopover = () => setShowMenu(!showMenu); const fieldOptions = useMemo( () => fields.map((field) => ({ inputDisplay: field.name, value: field })), @@ -61,7 +62,7 @@ export const EmbeddableMenu: FC = ({ const button = ( = ({ togglePopover()} panelPaddingSize="s" anchorPosition="downLeft" > -

Pattern analysis settings

+

+ +

= ({ = ({ <> {categoryCount !== undefined && widenessOption !== 'No minimum' ? ( <> - Total patterns in {widenessOption}: {categoryCount} + ) : null} } > = ({ uiSettings, } = useAiopsAppContext(); const { dataView, savedSearch, setPatternCount, setOptionsMenu } = input; - // const getViewModeToggle: (patternCount: number) => React.ReactElement | undefined = - // input.getViewModeToggle; const [widenessOption, setWidenessOption] = useStorage< AiOpsKey, @@ -409,31 +407,7 @@ export const LogCategorizationEmbeddable: FC = ({ gutterSize="none" responsive={false} > - {/* - - <>{getViewModeToggle(data?.categories?.length ?? 0)} - - - {randomSampler !== undefined ? ( - loadCategories()} - fields={fields} - setSelectedField={setSelectedField} - selectedField={selectedField} - widenessOption={widenessOption} - setWidenessOption={setWidenessOption} - categoryCount={data?.totalCategories} - /> - ) : null} - - - */} - - + <> {loading === true ? : null} diff --git a/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx b/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx index 7e25250f89ce5..fd4f1280d5b48 100644 --- a/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx +++ b/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx @@ -25,7 +25,7 @@ import { StorageContextProvider } from '@kbn/ml-local-storage'; import { Storage } from '@kbn/kibana-utils-plugin/public'; import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; -import type { EmbeddableLogCategorizationType } from '@kbn/aiops-log-pattern-analysis/constants'; +import type { EmbeddableLogCategorizationType } from '@kbn/aiops-log-pattern-analysis/embeddable'; import { EMBEDDABLE_ORIGIN } from '@kbn/aiops-common/constants'; import type { diff --git a/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable_factory.ts b/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable_factory.ts index db8d480d0f0f9..4457539e97911 100644 --- a/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable_factory.ts +++ b/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable_factory.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { StartServicesAccessor } from '@kbn/core/public'; import type { EmbeddableFactoryDefinition, IContainer } from '@kbn/embeddable-plugin/public'; -import { EMBEDDABLE_LOG_CATEGORIZATION_TYPE } from '@kbn/aiops-log-pattern-analysis/constants'; +import { EMBEDDABLE_LOG_CATEGORIZATION_TYPE } from '@kbn/aiops-log-pattern-analysis/embeddable'; import type { EmbeddableLogCategorizationInput } from '@kbn/aiops-log-pattern-analysis/embeddable'; import type { AiopsPluginStart, AiopsPluginStartDeps } from '../../types'; import type { EmbeddableLogCategorizationDeps } from './log_categorization_embeddable'; @@ -21,8 +21,8 @@ export class EmbeddableLogCategorizationFactory public readonly grouping = [ { - id: 'data_visualizer_grid', - getDisplayName: () => 'Data Visualizer Grid', + id: 'embeddable_log_categorization', + getDisplayName: () => 'Pattern Analysis', }, ]; @@ -44,14 +44,14 @@ export class EmbeddableLogCategorizationFactory } public getDisplayName() { - return i18n.translate('xpack.dataVisualizer.index.components.grid.displayName', { - defaultMessage: 'Data visualizer grid', + return i18n.translate('xpack.aiops.embeddableLogCategorization.displayName', { + defaultMessage: 'Pattern analysis', }); } public getDescription() { - return i18n.translate('xpack.dataVisualizer.index.components.grid.description', { - defaultMessage: 'Visualize data', + return i18n.translate('xpack.aiops.embeddableLogCategorization.description', { + defaultMessage: 'Pattern analysis', }); } diff --git a/x-pack/plugins/aiops/public/embeddables/register_embeddables.ts b/x-pack/plugins/aiops/public/embeddables/register_embeddables.ts index e2530409b6773..db11ebdcbdf05 100644 --- a/x-pack/plugins/aiops/public/embeddables/register_embeddables.ts +++ b/x-pack/plugins/aiops/public/embeddables/register_embeddables.ts @@ -19,7 +19,7 @@ export const registerChangePointChartEmbeddable = ( ) => { const changePointChartFactory = new EmbeddableChangePointChartFactory( EMBEDDABLE_CHANGE_POINT_CHART_TYPE, - i18n.translate('xpack.aiops.embeddableChangePointChartDisplayName', { + i18n.translate('xpack.aiops.embeddableLogCategorizationDisplayName', { defaultMessage: 'Change point detection', }), core.getStartServices @@ -31,11 +31,14 @@ export const registerLogCategorizationEmbeddable = ( core: CoreSetup, embeddable: EmbeddableSetup ) => { - const changePointChartFactory = new EmbeddableLogCategorizationFactory( - i18n.translate('xpack.aiops.embeddableChangePointChartDisplayName', { - defaultMessage: 'Change point detection', + const embeddableLogCategorizationFactory = new EmbeddableLogCategorizationFactory( + i18n.translate('xpack.aiops.embeddableLogCategorizationDisplayName', { + defaultMessage: 'Pattern analysis', }), core.getStartServices ); - embeddable.registerEmbeddableFactory(changePointChartFactory.type, changePointChartFactory); + embeddable.registerEmbeddableFactory( + embeddableLogCategorizationFactory.type, + embeddableLogCategorizationFactory + ); }; From 342fd4df78f5cbae72648369ad4dd834269309aa Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 4 Apr 2024 15:13:07 +0100 Subject: [PATCH 10/92] code clean up --- .../embeddable_menu.tsx | 34 +-- .../index.ts | 8 + .../log_categorization_for_embeddable.tsx | 200 +++++++----------- .../minimum_time_range.ts | 19 ++ .../use_minimum_time_range.ts | 92 ++++++++ .../use_wider_time_range.ts | 77 ------- .../components/log_categorization/utils.ts | 25 +++ x-pack/plugins/aiops/public/types/storage.ts | 13 +- 8 files changed, 242 insertions(+), 226 deletions(-) rename x-pack/plugins/aiops/public/components/log_categorization/{ => log_categorization_for_embeddable}/embeddable_menu.tsx (77%) create mode 100644 x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/index.ts rename x-pack/plugins/aiops/public/components/log_categorization/{ => log_categorization_for_embeddable}/log_categorization_for_embeddable.tsx (69%) create mode 100644 x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/minimum_time_range.ts create mode 100644 x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/use_minimum_time_range.ts delete mode 100644 x-pack/plugins/aiops/public/components/log_categorization/use_wider_time_range.ts diff --git a/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx similarity index 77% rename from x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx rename to x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx index de5565a5edfde..09291b28714ab 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/embeddable_menu.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx @@ -21,18 +21,18 @@ import type { DataViewField } from '@kbn/data-views-plugin/public'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { RandomSampler } from './sampling_menu'; -import { SamplingPanel } from './sampling_menu/sampling_panel'; -import type { WidenessOption } from './log_categorization_for_embeddable'; -import { WIDENESS } from './log_categorization_for_embeddable'; +import type { RandomSampler } from '../sampling_menu'; +import { SamplingPanel } from '../sampling_menu/sampling_panel'; +import type { MinimumTimeRangeOption } from './minimum_time_range'; +import { MINIMUM_TIME_RANGE } from './minimum_time_range'; interface Props { randomSampler: RandomSampler; fields: DataViewField[]; selectedField: DataViewField | null; setSelectedField: (field: DataViewField) => void; - widenessOption: WidenessOption; - setWidenessOption: (w: WidenessOption) => void; + minimumTimeRangeOption: MinimumTimeRangeOption; + setMinimumTimeRangeOption: (w: MinimumTimeRangeOption) => void; categoryCount: number | undefined; reload: () => void; } @@ -42,8 +42,8 @@ export const EmbeddableMenu: FC = ({ fields, selectedField, setSelectedField, - widenessOption, - setWidenessOption, + minimumTimeRangeOption, + setMinimumTimeRangeOption, categoryCount, reload, }) => { @@ -55,9 +55,9 @@ export const EmbeddableMenu: FC = ({ [fields] ); - const widenessOptions = Object.keys(WIDENESS).map((value) => ({ + const minimumTimeRangeOptions = Object.keys(MINIMUM_TIME_RANGE).map((value) => ({ inputDisplay: value, - value: value as WidenessOption, + value: value as MinimumTimeRangeOption, })); const button = ( @@ -114,19 +114,19 @@ export const EmbeddableMenu: FC = ({ - {categoryCount !== undefined && widenessOption !== 'No minimum' ? ( + {categoryCount !== undefined && minimumTimeRangeOption !== 'No minimum' ? ( <> ) : null} @@ -135,9 +135,9 @@ export const EmbeddableMenu: FC = ({ > diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/index.ts b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/index.ts new file mode 100644 index 0000000000000..224a4a35e6802 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { LogCategorizationEmbeddable } from './log_categorization_for_embeddable'; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx similarity index 69% rename from x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx rename to x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx index c7e3d87772331..408b21f8472c3 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx @@ -16,10 +16,6 @@ import { buildEmptyFilter } from '@kbn/es-query'; import { usePageUrlState } from '@kbn/ml-url-state'; import type { FieldValidationResults } from '@kbn/ml-category-validator'; import useMount from 'react-use/lib/useMount'; -import { ES_FIELD_TYPES } from '@kbn/field-types'; -import type { unitOfTime } from 'moment'; -import moment from 'moment'; -import { useStorage } from '@kbn/ml-local-storage'; import type { Category } from '@kbn/aiops-log-pattern-analysis/types'; @@ -29,45 +25,26 @@ import type { EmbeddableLogCategorizationInput } from '@kbn/aiops-log-pattern-an import { type LogCategorizationPageUrlState, getDefaultLogCategorizationAppState, -} from '../../application/url_state/log_pattern_analysis'; -import { createMergedEsQuery } from '../../application/utils/search_utils'; -import { useData } from '../../hooks/use_data'; -import { useSearch } from '../../hooks/use_search'; -import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; - -import { useCategorizeRequest } from './use_categorize_request'; -import type { EventRate } from './use_categorize_request'; -import { CategoryTable } from './category_table'; -import { InformationText } from './information_text'; -import { LoadingCategorization } from './loading_categorization'; -import { useValidateFieldRequest } from './use_validate_category_field'; -import { FieldValidationCallout } from './category_validation_callout'; +} from '../../../application/url_state/log_pattern_analysis'; +import { createMergedEsQuery } from '../../../application/utils/search_utils'; +import { useData } from '../../../hooks/use_data'; +import { useSearch } from '../../../hooks/use_search'; +import { useAiopsAppContext } from '../../../hooks/use_aiops_app_context'; + +import { useCategorizeRequest } from '../use_categorize_request'; +import type { EventRate } from '../use_categorize_request'; +import { CategoryTable } from '../category_table'; +import { InformationText } from '../information_text'; +import { LoadingCategorization } from '../loading_categorization'; +import { useValidateFieldRequest } from '../use_validate_category_field'; +import { FieldValidationCallout } from '../category_validation_callout'; import { EmbeddableMenu } from './embeddable_menu'; -import { useWiderTimeRange } from './use_wider_time_range'; -import type { AiOpsKey, AiOpsStorageMapped } from '../../types/storage'; -import { AIOPS_PATTERN_ANALYSIS_WIDENESS_PREFERENCE } from '../../types/storage'; -import { createDocumentStatsHash } from './utils'; - -enum SELECTED_TAB { - BUCKET, - FULL_TIME_RANGE, -} - -export type WidenessOption = 'No minimum' | '1 week' | '1 month' | '3 months' | '6 months'; - -type Wideness = Record; +import { useMinimumTimeRange } from './use_minimum_time_range'; -export const WIDENESS: Wideness = { - 'No minimum': { factor: 0, unit: 'w' }, - '1 week': { factor: 1, unit: 'w' }, - '1 month': { factor: 1, unit: 'M' }, - '3 months': { factor: 3, unit: 'M' }, - '6 months': { factor: 6, unit: 'M' }, -}; +import { createDocumentStatsHash, getMessageField } from '../utils'; export interface LogCategorizationPageProps { onClose: () => void; - /** Identifier to indicate the plugin utilizing the component */ embeddingOrigin: string; additionalFilter?: CategorizationAdditionalFilter; input: Readonly; @@ -90,14 +67,14 @@ export const LogCategorizationEmbeddable: FC = ({ } = useAiopsAppContext(); const { dataView, savedSearch, setPatternCount, setOptionsMenu } = input; - const [widenessOption, setWidenessOption] = useStorage< - AiOpsKey, - AiOpsStorageMapped - >(AIOPS_PATTERN_ANALYSIS_WIDENESS_PREFERENCE, '1 week'); - const { runValidateFieldRequest, cancelRequest: cancelValidationRequest } = useValidateFieldRequest(); - const { getWiderTimeRange, cancelRequest: cancelWiderTimeRangeRequest } = useWiderTimeRange(); + const { + getMinimumTimeRange, + cancelRequest: cancelWiderTimeRangeRequest, + minimumTimeRangeOption, + setMinimumTimeRangeOption, + } = useMinimumTimeRange(); const { filters, query } = useMemo(() => getState(), [getState]); const mounted = useRef(false); @@ -127,32 +104,21 @@ export const LogCategorizationEmbeddable: FC = ({ const [fieldValidationResult, setFieldValidationResult] = useState( null ); - const [selectedTab, setSelectedTab] = useState(SELECTED_TAB.FULL_TIME_RANGE); - - const cancelRequest = useCallback(() => { - cancelWiderTimeRangeRequest(); - cancelValidationRequest(); - cancelCategorizationRequest(); - }, [cancelCategorizationRequest, cancelValidationRequest, cancelWiderTimeRangeRequest]); useMount(function loadFields() { - const dataViewFields = dataView.fields.filter((f) => f.esTypes?.includes(ES_FIELD_TYPES.TEXT)); + const { dataViewFields, messageField } = getMessageField(dataView); setFields(dataViewFields); - let messageField = dataViewFields.find((f) => f.name === 'message'); - if (messageField === undefined) { - messageField = dataViewFields.find((f) => f.name === 'error.message'); - } - if (messageField === undefined) { - messageField = dataViewFields.find((f) => f.name === 'event.original '); - } - if (messageField === undefined) { - messageField = dataViewFields[0]; - } if (messageField !== undefined) { setSelectedField(messageField); } }); + const cancelRequest = useCallback(() => { + cancelWiderTimeRangeRequest(); + cancelValidationRequest(); + cancelCategorizationRequest(); + }, [cancelCategorizationRequest, cancelValidationRequest, cancelWiderTimeRangeRequest]); + useEffect( function cancelRequestOnLeave() { mounted.current = true; @@ -182,9 +148,6 @@ export const LogCategorizationEmbeddable: FC = ({ ); const loadCategories = useCallback(async () => { - // eslint-disable-next-line no-console - console.log('loadCategories'); - const { getIndexPattern, timeFieldName: timeField } = dataView; const index = getIndexPattern(); @@ -193,7 +156,7 @@ export const LogCategorizationEmbeddable: FC = ({ timeField === undefined || earliest === undefined || latest === undefined || - widenessOption === undefined + minimumTimeRangeOption === undefined ) { return; } @@ -209,26 +172,13 @@ export const LogCategorizationEmbeddable: FC = ({ to: latest, }; - const timeRange = await getWiderTimeRange( + const timeRange = await getMinimumTimeRange( index, timeField, tempAdditionalFilter, - widenessOption, + minimumTimeRangeOption, searchQuery ); - // eslint-disable-next-line no-console - console.log( - 'sub agg s', - moment(tempAdditionalFilter.from).toISOString(), - moment(tempAdditionalFilter.to).toISOString() - ); - - // eslint-disable-next-line no-console - console.log( - 'full range', - moment(timeRange.from).toISOString(), - moment(timeRange.to).toISOString() - ); try { const [validationResult, categorizationResult] = await Promise.all([ @@ -272,10 +222,6 @@ export const LogCategorizationEmbeddable: FC = ({ totalCategories: categories.length, }); } - - // eslint-disable-next-line no-console - console.log('categories', categories); - setSelectedTab(SELECTED_TAB.BUCKET); } } catch (error) { toasts.addError(error, { @@ -294,8 +240,8 @@ export const LogCategorizationEmbeddable: FC = ({ earliest, latest, cancelRequest, - getWiderTimeRange, - widenessOption, + getMinimumTimeRange, + minimumTimeRangeOption, searchQuery, runValidateFieldRequest, embeddingOrigin, @@ -304,38 +250,41 @@ export const LogCategorizationEmbeddable: FC = ({ toasts, ]); - useEffect(() => { - setPatternCount(data?.categories.length); - setOptionsMenu( - <> - {randomSampler !== undefined ? ( - loadCategories()} - fields={fields} - setSelectedField={setSelectedField} - selectedField={selectedField} - widenessOption={widenessOption} - setWidenessOption={setWidenessOption} - categoryCount={data?.totalCategories} - /> - ) : null} - - ); - return () => { - setOptionsMenu(undefined); - }; - }, [ - data, - fields, - loadCategories, - randomSampler, - selectedField, - setOptionsMenu, - setPatternCount, - setWidenessOption, - widenessOption, - ]); + useEffect( + function initOptionsMenu() { + setPatternCount(data?.categories.length); + setOptionsMenu( + <> + {randomSampler !== undefined ? ( + loadCategories()} + fields={fields} + setSelectedField={setSelectedField} + selectedField={selectedField} + minimumTimeRangeOption={minimumTimeRangeOption} + setMinimumTimeRangeOption={setMinimumTimeRangeOption} + categoryCount={data?.totalCategories} + /> + ) : null} + + ); + return () => { + setOptionsMenu(undefined); + }; + }, + [ + data, + fields, + loadCategories, + randomSampler, + selectedField, + setOptionsMenu, + setPatternCount, + setMinimumTimeRangeOption, + minimumTimeRangeOption, + ] + ); const onAddFilter = useCallback( (values: Filter, alias?: string) => { @@ -362,7 +311,10 @@ export const LogCategorizationEmbeddable: FC = ({ return; } - const hash = createDocumentStatsHash(documentStats, [selectedField.name, widenessOption]); + const hash = createDocumentStatsHash(documentStats, [ + selectedField.name, + minimumTimeRangeOption, + ]); if (hash !== previousDocumentStatsHash) { randomSampler.setDocCount(documentStats.totalCount); setEventRate( @@ -386,7 +338,7 @@ export const LogCategorizationEmbeddable: FC = ({ previousDocumentStatsHash, fieldValidationResult, selectedField, - widenessOption, + minimumTimeRangeOption, ]); useEffect( @@ -435,11 +387,7 @@ export const LogCategorizationEmbeddable: FC = ({ onAddFilter={onAddFilter} onClose={onClose} enableRowActions={false} - additionalFilter={ - selectedTab === SELECTED_TAB.BUCKET && additionalFilter !== undefined - ? additionalFilter - : undefined - } + additionalFilter={additionalFilter} navigateToDiscover={additionalFilter !== undefined} displayExamples={data.displayExamples} displayHeader={false} diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/minimum_time_range.ts b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/minimum_time_range.ts new file mode 100644 index 0000000000000..07b5485be7bbd --- /dev/null +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/minimum_time_range.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import type { unitOfTime } from 'moment'; + +export type MinimumTimeRangeOption = 'No minimum' | '1 week' | '1 month' | '3 months' | '6 months'; + +type MinimumTimeRange = Record; + +export const MINIMUM_TIME_RANGE: MinimumTimeRange = { + 'No minimum': { factor: 0, unit: 'w' }, + '1 week': { factor: 1, unit: 'w' }, + '1 month': { factor: 1, unit: 'M' }, + '3 months': { factor: 3, unit: 'M' }, + '6 months': { factor: 6, unit: 'M' }, +}; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/use_minimum_time_range.ts b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/use_minimum_time_range.ts new file mode 100644 index 0000000000000..9a98bf016ba58 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/use_minimum_time_range.ts @@ -0,0 +1,92 @@ +/* + * 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 { useRef, useCallback } from 'react'; + +import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; +import type { HttpFetchOptions } from '@kbn/core/public'; +import { getTimeFieldRange } from '@kbn/ml-date-picker'; +import moment from 'moment'; +import { useStorage } from '@kbn/ml-local-storage'; +import { useAiopsAppContext } from '../../../hooks/use_aiops_app_context'; +import type { MinimumTimeRangeOption } from './minimum_time_range'; +import { MINIMUM_TIME_RANGE } from './minimum_time_range'; +import type { AiOpsKey, AiOpsStorageMapped } from '../../../types/storage'; +import { AIOPS_PATTERN_ANALYSIS_MINIMUM_TIME_RANGE_PREFERENCE } from '../../../types/storage'; + +export function useMinimumTimeRange() { + const { http } = useAiopsAppContext(); + const abortController = useRef(new AbortController()); + + const getMinimumTimeRange = useCallback( + async ( + index: string, + timeField: string, + timeRange: { from: number; to: number }, + minimumTimeRangeOption: MinimumTimeRangeOption, + queryIn: QueryDslQueryContainer, + headers?: HttpFetchOptions['headers'] + ) => { + const minimumTimeRange = MINIMUM_TIME_RANGE[minimumTimeRangeOption]; + const minimumTimeRangeMs = moment + .duration(minimumTimeRange.factor, minimumTimeRange.unit) + .asMilliseconds(); + const currentMinimumTimeRange = timeRange.to - timeRange.from; + + // the time range is already wide enough + if (currentMinimumTimeRange > minimumTimeRangeMs) { + return { ...timeRange, useSubAgg: false }; + } + + const resp = await getTimeFieldRange({ + http, + index, + timeFieldName: timeField, + query: queryIn, + path: '/internal/file_upload/time_field_range', + }); + + // the index isn't big enough to get a wider time range + const indexTimeRangeMs = resp.end.epoch - resp.start.epoch; + if (indexTimeRangeMs < minimumTimeRangeMs) { + return { + from: resp.start.epoch, + to: resp.end.epoch, + useSubAgg: true, + }; + } + + const remainder = minimumTimeRangeMs - currentMinimumTimeRange; + const newFrom = Math.max(timeRange.from - remainder, resp.start.epoch); + const newTo = Math.min(newFrom + minimumTimeRangeMs, resp.end.epoch); + + return { + from: newFrom, + to: newTo, + useSubAgg: true, + }; + }, + [http] + ); + + const [minimumTimeRangeOption, setMinimumTimeRangeOption] = useStorage< + AiOpsKey, + AiOpsStorageMapped + >(AIOPS_PATTERN_ANALYSIS_MINIMUM_TIME_RANGE_PREFERENCE, '1 week'); + + const cancelRequest = useCallback(() => { + abortController.current.abort(); + abortController.current = new AbortController(); + }, []); + + return { + getMinimumTimeRange, + cancelRequest, + minimumTimeRangeOption, + setMinimumTimeRangeOption, + }; +} diff --git a/x-pack/plugins/aiops/public/components/log_categorization/use_wider_time_range.ts b/x-pack/plugins/aiops/public/components/log_categorization/use_wider_time_range.ts deleted file mode 100644 index 2bf5b54f07ef8..0000000000000 --- a/x-pack/plugins/aiops/public/components/log_categorization/use_wider_time_range.ts +++ /dev/null @@ -1,77 +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 { useRef, useCallback } from 'react'; - -import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; -import type { HttpFetchOptions } from '@kbn/core/public'; -import { getTimeFieldRange } from '@kbn/ml-date-picker'; -import moment from 'moment'; -import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; -import type { WidenessOption } from './log_categorization_for_embeddable'; -import { WIDENESS } from './log_categorization_for_embeddable'; - -export function useWiderTimeRange() { - const { http } = useAiopsAppContext(); - const abortController = useRef(new AbortController()); - - const getWiderTimeRange = useCallback( - async ( - index: string, - timeField: string, - timeRange: { from: number; to: number }, - widenessOption: WidenessOption, - queryIn: QueryDslQueryContainer, - headers?: HttpFetchOptions['headers'] - ) => { - const wideness = WIDENESS[widenessOption]; - const widenessMs = moment.duration(wideness.factor, wideness.unit).asMilliseconds(); - const currentWideness = timeRange.to - timeRange.from; - - // the time range is already wide enough - if (currentWideness > widenessMs) { - return { ...timeRange, useSubAgg: false }; - } - - const resp = await getTimeFieldRange({ - http, - index, - timeFieldName: timeField, - query: queryIn, - path: '/internal/file_upload/time_field_range', - }); - - // the index isn't big enough to get a wider time range - const indexWideness = resp.end.epoch - resp.start.epoch; - if (indexWideness < widenessMs) { - return { - from: resp.start.epoch, - to: resp.end.epoch, - useSubAgg: true, - }; - } - - const widenessRemainder = widenessMs - currentWideness; - const newFrom = Math.max(timeRange.from - widenessRemainder, resp.start.epoch); - const newTo = Math.min(newFrom + widenessMs, resp.end.epoch); - - return { - from: newFrom, - to: newTo, - useSubAgg: true, - }; - }, - [http] - ); - - const cancelRequest = useCallback(() => { - abortController.current.abort(); - abortController.current = new AbortController(); - }, []); - - return { getWiderTimeRange, cancelRequest }; -} diff --git a/x-pack/plugins/aiops/public/components/log_categorization/utils.ts b/x-pack/plugins/aiops/public/components/log_categorization/utils.ts index 4881b754438f4..ad45252a5c73d 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/utils.ts +++ b/x-pack/plugins/aiops/public/components/log_categorization/utils.ts @@ -6,6 +6,8 @@ */ import { stringHash } from '@kbn/ml-string-hash'; +import type { DataView } from '@kbn/data-views-plugin/public'; +import { ES_FIELD_TYPES } from '@kbn/field-types'; import type { DocumentStats } from '../../hooks/use_document_count_stats'; /** @@ -26,3 +28,26 @@ export function createDocumentStatsHash( )}` ); } + +/** + * Retrieves the message field from a DataView object. + * If the message field is not found, it falls back to error.message or event.original or the first field in the DataView. + * + * @param dataView - The DataView object containing the fields. + * @returns An object containing the message field and all the fields in the DataView. + */ +export function getMessageField(dataView: DataView) { + const dataViewFields = dataView.fields.filter((f) => f.esTypes?.includes(ES_FIELD_TYPES.TEXT)); + // setFields(dataViewFields); + let messageField = dataViewFields.find((f) => f.name === 'message'); + if (messageField === undefined) { + messageField = dataViewFields.find((f) => f.name === 'error.message'); + } + if (messageField === undefined) { + messageField = dataViewFields.find((f) => f.name === 'event.original '); + } + if (messageField === undefined) { + messageField = dataViewFields[0]; + } + return { messageField, dataViewFields }; +} diff --git a/x-pack/plugins/aiops/public/types/storage.ts b/x-pack/plugins/aiops/public/types/storage.ts index fa7ec700cbe84..a83bee80c79ab 100644 --- a/x-pack/plugins/aiops/public/types/storage.ts +++ b/x-pack/plugins/aiops/public/types/storage.ts @@ -6,7 +6,7 @@ */ import { type FrozenTierPreference } from '@kbn/ml-date-picker'; -import type { WidenessOption } from '../components/log_categorization/log_categorization_for_embeddable'; +import type { MinimumTimeRangeOption } from '../components/log_categorization/log_categorization_for_embeddable/minimum_time_range'; import { type RandomSamplerOption, type RandomSamplerProbability, @@ -16,13 +16,14 @@ export const AIOPS_FROZEN_TIER_PREFERENCE = 'aiops.frozenDataTierPreference'; export const AIOPS_RANDOM_SAMPLING_MODE_PREFERENCE = 'aiops.randomSamplingModePreference'; export const AIOPS_RANDOM_SAMPLING_PROBABILITY_PREFERENCE = 'aiops.randomSamplingProbabilityPreference'; -export const AIOPS_PATTERN_ANALYSIS_WIDENESS_PREFERENCE = 'aiops.patternAnalysisWidenessPreference'; +export const AIOPS_PATTERN_ANALYSIS_MINIMUM_TIME_RANGE_PREFERENCE = + 'aiops.patternAnalysisMinimumTimeRangePreference'; export type AiOps = Partial<{ [AIOPS_FROZEN_TIER_PREFERENCE]: FrozenTierPreference; [AIOPS_RANDOM_SAMPLING_MODE_PREFERENCE]: RandomSamplerOption; [AIOPS_RANDOM_SAMPLING_PROBABILITY_PREFERENCE]: number; - [AIOPS_PATTERN_ANALYSIS_WIDENESS_PREFERENCE]: WidenessOption; + [AIOPS_PATTERN_ANALYSIS_MINIMUM_TIME_RANGE_PREFERENCE]: MinimumTimeRangeOption; }> | null; export type AiOpsKey = keyof Exclude; @@ -33,13 +34,13 @@ export type AiOpsStorageMapped = T extends typeof AIOPS_FROZ ? RandomSamplerOption : T extends typeof AIOPS_RANDOM_SAMPLING_PROBABILITY_PREFERENCE ? RandomSamplerProbability - : T extends typeof AIOPS_PATTERN_ANALYSIS_WIDENESS_PREFERENCE - ? WidenessOption + : T extends typeof AIOPS_PATTERN_ANALYSIS_MINIMUM_TIME_RANGE_PREFERENCE + ? MinimumTimeRangeOption : null; export const AIOPS_STORAGE_KEYS = [ AIOPS_FROZEN_TIER_PREFERENCE, AIOPS_RANDOM_SAMPLING_MODE_PREFERENCE, AIOPS_RANDOM_SAMPLING_PROBABILITY_PREFERENCE, - AIOPS_PATTERN_ANALYSIS_WIDENESS_PREFERENCE, + AIOPS_PATTERN_ANALYSIS_MINIMUM_TIME_RANGE_PREFERENCE, ] as const; From e7c6463bcb6df0ad6ca996a7c7fc24ef081c35c5 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 4 Apr 2024 14:25:33 +0000 Subject: [PATCH 11/92] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- x-pack/packages/ml/aiops_log_pattern_analysis/tsconfig.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x-pack/packages/ml/aiops_log_pattern_analysis/tsconfig.json b/x-pack/packages/ml/aiops_log_pattern_analysis/tsconfig.json index edf2bc92db1a3..d6b25c65903bb 100644 --- a/x-pack/packages/ml/aiops_log_pattern_analysis/tsconfig.json +++ b/x-pack/packages/ml/aiops_log_pattern_analysis/tsconfig.json @@ -20,5 +20,9 @@ "@kbn/config-schema", "@kbn/i18n", "@kbn/ml-runtime-field-utils", + "@kbn/embeddable-plugin", + "@kbn/es-query", + "@kbn/saved-search-plugin", + "@kbn/data-views-plugin", ] } From 3971813c9c4efaf3d5ea1ff19141d486602ab2c8 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 4 Apr 2024 16:24:00 +0100 Subject: [PATCH 12/92] fixing update on index change --- .../log_categorization_table.tsx | 19 +++++++++----- .../log_categorization_embeddable.tsx | 26 +++++++++++++++---- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_table.tsx b/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_table.tsx index 7e1e971d3edbd..243fff75bfd6f 100644 --- a/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_table.tsx +++ b/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_table.tsx @@ -8,11 +8,16 @@ import React, { useEffect, useMemo, useRef, useState } from 'react'; import { METRIC_TYPE, UiCounterMetricType } from '@kbn/analytics'; -import { ErrorEmbeddable, IEmbeddable, isErrorEmbeddable } from '@kbn/embeddable-plugin/public'; -import type { - EmbeddableLogCategorizationInput, - EmbeddableLogCategorizationOutput, - EmbeddableLogCategorizationProps, +import { + ErrorEmbeddable, + type IEmbeddable, + isErrorEmbeddable, +} from '@kbn/embeddable-plugin/public'; +import { + type EmbeddableLogCategorizationInput, + type EmbeddableLogCategorizationOutput, + type EmbeddableLogCategorizationProps, + EMBEDDABLE_LOG_CATEGORIZATION_TYPE, } from '@kbn/aiops-log-pattern-analysis/embeddable'; import { EuiFlexItem } from '@elastic/eui'; import { css } from '@emotion/react'; @@ -99,11 +104,11 @@ export const LogCategorizationTable = (props: LogCategorizationTableProps) => { const factory = services.embeddable.getEmbeddableFactory< EmbeddableLogCategorizationInput, EmbeddableLogCategorizationOutput - >('aiopsLogCategorization'); + >(EMBEDDABLE_LOG_CATEGORIZATION_TYPE); if (factory) { // Initialize embeddable with information available at mount const initializedEmbeddable = await factory.create({ - id: 'aiopsLogCategorization', + id: EMBEDDABLE_LOG_CATEGORIZATION_TYPE, dataView, savedSearch, query, diff --git a/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx b/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx index fd4f1280d5b48..ba9e35e1ce10d 100644 --- a/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx +++ b/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx @@ -32,6 +32,7 @@ import type { EmbeddableLogCategorizationInput, EmbeddableLogCategorizationOutput, } from '@kbn/aiops-log-pattern-analysis/embeddable'; +import useObservable from 'react-use/lib/useObservable'; import { AiopsAppContext, type AiopsAppDependencies } from '../../hooks/use_aiops_app_context'; import { AIOPS_STORAGE_KEYS } from '../../types/storage'; import { LogCategorizationEmbeddable } from '../../components/log_categorization/log_categorization_for_embeddable'; @@ -54,6 +55,25 @@ export interface EmbeddableLogCategorizationDeps { export type IEmbeddableLogCategorization = typeof EmbeddableLogCategorization; +const LogCategorizationEmbeddableWrapper = (props: { + embeddableContext: InstanceType; +}) => { + const { embeddableContext } = props; + const input$ = embeddableContext.getInput$(); + const input = useObservable(input$); + if (input === undefined) { + return null; + } + + return ( + embeddableContext.destroy()} + embeddingOrigin={'discover-embedded'} + input={input} + /> + ); +}; + export class EmbeddableLogCategorization extends AbstractEmbeddable< EmbeddableLogCategorizationInput, EmbeddableLogCategorizationOutput @@ -137,11 +157,7 @@ export class EmbeddableLogCategorization extends AbstractEmbeddable< - this.destroy()} - embeddingOrigin={'discover-change-me'} // !!!!!!!!!!!!!! - input={input} - /> + From fbff484268108cc8435dfbca4f72d559d97834e0 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 4 Apr 2024 16:46:17 +0100 Subject: [PATCH 13/92] translations --- x-pack/plugins/aiops/public/embeddables/register_embeddables.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/aiops/public/embeddables/register_embeddables.ts b/x-pack/plugins/aiops/public/embeddables/register_embeddables.ts index db11ebdcbdf05..54ecb3dac8d1a 100644 --- a/x-pack/plugins/aiops/public/embeddables/register_embeddables.ts +++ b/x-pack/plugins/aiops/public/embeddables/register_embeddables.ts @@ -19,7 +19,7 @@ export const registerChangePointChartEmbeddable = ( ) => { const changePointChartFactory = new EmbeddableChangePointChartFactory( EMBEDDABLE_CHANGE_POINT_CHART_TYPE, - i18n.translate('xpack.aiops.embeddableLogCategorizationDisplayName', { + i18n.translate('xpack.aiops.embeddableChangePointChartDisplayName', { defaultMessage: 'Change point detection', }), core.getStartServices From 18b97832b0e59de64ff22a7d2bac10d7d47b8f63 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 5 Apr 2024 14:32:21 +0100 Subject: [PATCH 14/92] options menu refactor --- .../embeddable_menu.tsx | 2 +- .../sampling_menu/sampling_panel.tsx | 40 ++++++++++++++----- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx index 09291b28714ab..0561f8ec34bb8 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx @@ -143,7 +143,7 @@ export const EmbeddableMenu: FC = ({ - +
); diff --git a/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/sampling_panel.tsx b/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/sampling_panel.tsx index ce91662bfcc91..f6c82117215f2 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/sampling_panel.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/sampling_panel.tsx @@ -7,7 +7,7 @@ import type { FC } from 'react'; import React, { useCallback, useMemo } from 'react'; -import { EuiFlexItem, EuiSpacer, EuiCallOut, EuiFormRow, EuiSuperSelect } from '@elastic/eui'; +import { EuiSpacer, EuiCallOut, EuiFormRow, EuiSuperSelect } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import useObservable from 'react-use/lib/useObservable'; @@ -19,10 +19,11 @@ import { RANDOM_SAMPLER_OPTION, RANDOM_SAMPLER_SELECT_OPTIONS } from './random_s interface Props { randomSampler: RandomSampler; + calloutPosition?: 'top' | 'bottom'; reload: () => void; } -export const SamplingPanel: FC = ({ randomSampler, reload }) => { +export const SamplingPanel: FC = ({ randomSampler, reload, calloutPosition = 'top' }) => { const samplingProbability = useObservable( randomSampler.getProbability$(), randomSampler.getProbability() @@ -51,10 +52,12 @@ export const SamplingPanel: FC = ({ randomSampler, reload }) => { return ( <> - - - - + {calloutPosition === 'top' ? ( + + ) : null} = ({ randomSampler, reload }) => { defaultMessage: 'Random sampling', } )} + helpText={ + randomSamplerPreference === RANDOM_SAMPLER_OPTION.ON_AUTOMATIC ? ( + + ) : null + } > = ({ randomSampler, reload }) => { /> ) : null} - {randomSamplerPreference === RANDOM_SAMPLER_OPTION.ON_AUTOMATIC ? ( - + {calloutPosition === 'bottom' ? ( + ) : null} ); @@ -92,8 +103,6 @@ const ProbabilityUsedMessage: FC<{ samplingProbability: number | null }> = ({ }) => { return samplingProbability !== null ? (
- - = ({
) : null; }; + +const CalloutInfoMessage: FC<{ + calloutInfoMessage: string; + calloutPosition: 'top' | 'bottom'; +}> = ({ calloutInfoMessage, calloutPosition }) => ( + <> + {calloutPosition === 'bottom' ? : null} + + {calloutPosition === 'top' ? : null} + +); From b1caafef04a28c0ac8a7b42549690429a6d00700 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 5 Apr 2024 16:02:41 +0100 Subject: [PATCH 15/92] fix race condition when switching index --- .../log_categorization_for_embeddable.tsx | 61 +++++++++++-------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx index 408b21f8472c3..1e0243b007b9f 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx @@ -156,7 +156,8 @@ export const LogCategorizationEmbeddable: FC = ({ timeField === undefined || earliest === undefined || latest === undefined || - minimumTimeRangeOption === undefined + minimumTimeRangeOption === undefined || + mounted.current !== true ) { return; } @@ -180,6 +181,10 @@ export const LogCategorizationEmbeddable: FC = ({ searchQuery ); + if (mounted.current !== true) { + return; + } + try { const [validationResult, categorizationResult] = await Promise.all([ runValidateFieldRequest(index, selectedField.name, timeField, timeRange, searchQuery, { @@ -196,32 +201,34 @@ export const LogCategorizationEmbeddable: FC = ({ ), ]); - if (mounted.current === true) { - setFieldValidationResult(validationResult); - const { categories, hasExamples } = categorizationResult; - - if (timeRange.useSubAgg) { - const categoriesInBucket = categorizationResult.categories - .map((category) => ({ - ...category, - count: category.subFieldCount ?? category.subTimeRangeCount!, - examples: category.subFieldExamples!, - sparkline: category.subFieldSparkline, - })) - .filter((category) => category.count > 0) - .sort((a, b) => b.count - a.count); - setData({ - categories: categoriesInBucket, - displayExamples: hasExamples, - totalCategories: categories.length, - }); - } else { - setData({ - categories, - displayExamples: hasExamples, - totalCategories: categories.length, - }); - } + if (mounted.current !== true) { + return; + } + + setFieldValidationResult(validationResult); + const { categories, hasExamples } = categorizationResult; + + if (timeRange.useSubAgg) { + const categoriesInBucket = categorizationResult.categories + .map((category) => ({ + ...category, + count: category.subFieldCount ?? category.subTimeRangeCount!, + examples: category.subFieldExamples!, + sparkline: category.subFieldSparkline, + })) + .filter((category) => category.count > 0) + .sort((a, b) => b.count - a.count); + setData({ + categories: categoriesInBucket, + displayExamples: hasExamples, + totalCategories: categories.length, + }); + } else { + setData({ + categories, + displayExamples: hasExamples, + totalCategories: categories.length, + }); } } catch (error) { toasts.addError(error, { From 976e3abb25bb62700b37ce65f7819b19e9a12eab Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Tue, 9 Apr 2024 16:56:27 +0100 Subject: [PATCH 16/92] fixing multiple refresh bug --- .../log_categorization_for_embeddable.tsx | 130 ++++++++++++------ .../components/log_categorization/utils.ts | 35 +++-- 2 files changed, 106 insertions(+), 59 deletions(-) diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx index 1e0243b007b9f..0d0d01048d7a5 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx @@ -15,7 +15,6 @@ import type { Filter } from '@kbn/es-query'; import { buildEmptyFilter } from '@kbn/es-query'; import { usePageUrlState } from '@kbn/ml-url-state'; import type { FieldValidationResults } from '@kbn/ml-category-validator'; -import useMount from 'react-use/lib/useMount'; import type { Category } from '@kbn/aiops-log-pattern-analysis/types'; @@ -41,7 +40,7 @@ import { FieldValidationCallout } from '../category_validation_callout'; import { EmbeddableMenu } from './embeddable_menu'; import { useMinimumTimeRange } from './use_minimum_time_range'; -import { createDocumentStatsHash, getMessageField } from '../utils'; +import { createAdditionalConfigHash, createDocumentStatsHash, getMessageField } from '../utils'; export interface LogCategorizationPageProps { onClose: () => void; @@ -92,8 +91,15 @@ export const LogCategorizationEmbeddable: FC = ({ const [selectedCategory, setSelectedCategory] = useState(null); const [selectedField, setSelectedField] = useState(null); const [fields, setFields] = useState([]); + const [currentDocumentStatsHash, setCurrentDocumentStatsHash] = useState(null); const [previousDocumentStatsHash, setPreviousDocumentStatsHash] = useState(0); - const [loading, setLoading] = useState(true); + const [currentAdditionalConfigsHash, setCurrentAdditionalConfigsHash] = useState( + null + ); + const [previousAdditionalConfigsHash, setPreviousAdditionalConfigsHash] = useState( + null + ); + const [loading, setLoading] = useState(false); const [eventRate, setEventRate] = useState([]); const [pinnedCategory, setPinnedCategory] = useState(null); const [data, setData] = useState<{ @@ -105,13 +111,16 @@ export const LogCategorizationEmbeddable: FC = ({ null ); - useMount(function loadFields() { - const { dataViewFields, messageField } = getMessageField(dataView); - setFields(dataViewFields); - if (messageField !== undefined) { + useEffect( + function initFields() { + setCurrentDocumentStatsHash(null); + setSelectedField(null); + const { dataViewFields, messageField } = getMessageField(dataView); + setFields(dataViewFields); setSelectedField(messageField); - } - }); + }, + [dataView] + ); const cancelRequest = useCallback(() => { cancelWiderTimeRangeRequest(); @@ -131,7 +140,7 @@ export const LogCategorizationEmbeddable: FC = ({ [cancelRequest, mounted, setPatternCount] ); - const { searchQueryLanguage, searchString, searchQuery } = useSearch( + const { searchQuery } = useSearch( { dataView, savedSearch: savedSearch ?? null }, stateFromUrl, true @@ -147,11 +156,40 @@ export const LogCategorizationEmbeddable: FC = ({ BAR_TARGET ); + useEffect( + function createDocumentStatHash() { + if (documentStats.documentCountStats === undefined) { + return; + } + + const hash = createDocumentStatsHash(documentStats); + if (hash !== previousDocumentStatsHash) { + setCurrentDocumentStatsHash(hash); + } + }, + [documentStats, previousDocumentStatsHash] + ); + + useEffect( + function createAdditionalConfigHash2() { + if (!selectedField?.name) { + return; + } + + const hash = createAdditionalConfigHash([selectedField.name, minimumTimeRangeOption]); + if (hash !== previousAdditionalConfigsHash) { + setCurrentAdditionalConfigsHash(hash); + } + }, + [minimumTimeRangeOption, previousAdditionalConfigsHash, selectedField] + ); + const loadCategories = useCallback(async () => { const { getIndexPattern, timeFieldName: timeField } = dataView; const index = getIndexPattern(); if ( + loading === true || selectedField === null || timeField === undefined || earliest === undefined || @@ -243,12 +281,13 @@ export const LogCategorizationEmbeddable: FC = ({ } }, [ dataView, + loading, selectedField, earliest, latest, + minimumTimeRangeOption, cancelRequest, getMinimumTimeRange, - minimumTimeRangeOption, searchQuery, runValidateFieldRequest, embeddingOrigin, @@ -312,41 +351,42 @@ export const LogCategorizationEmbeddable: FC = ({ [dataView.id, filterManager, input] ); - useEffect(() => { - const buckets = documentStats.documentCountStats?.buckets; - if (buckets === undefined || selectedField === null) { - return; - } + useEffect( + function triggerAnalysis() { + const buckets = documentStats.documentCountStats?.buckets; + if (buckets === undefined || currentDocumentStatsHash === null) { + return; + } - const hash = createDocumentStatsHash(documentStats, [ - selectedField.name, - minimumTimeRangeOption, - ]); - if (hash !== previousDocumentStatsHash) { - randomSampler.setDocCount(documentStats.totalCount); - setEventRate( - Object.entries(buckets).map(([key, docCount]) => ({ - key: +key, - docCount, - })) - ); - loadCategories(); - setPreviousDocumentStatsHash(hash); - } - }, [ - documentStats, - earliest, - latest, - searchQueryLanguage, - searchString, - searchQuery, - loadCategories, - randomSampler, - previousDocumentStatsHash, - fieldValidationResult, - selectedField, - minimumTimeRangeOption, - ]); + if ( + currentDocumentStatsHash !== previousDocumentStatsHash || + (currentAdditionalConfigsHash !== previousAdditionalConfigsHash && + currentDocumentStatsHash !== null) + ) { + randomSampler.setDocCount(documentStats.totalCount); + setEventRate( + Object.entries(buckets).map(([key, docCount]) => ({ + key: +key, + docCount, + })) + ); + loadCategories(); + setPreviousDocumentStatsHash(currentDocumentStatsHash); + setPreviousAdditionalConfigsHash(currentAdditionalConfigsHash); + } + }, + [ + loadCategories, + randomSampler, + previousDocumentStatsHash, + fieldValidationResult, + currentDocumentStatsHash, + currentAdditionalConfigsHash, + documentStats.documentCountStats?.buckets, + documentStats.totalCount, + previousAdditionalConfigsHash, + ] + ); useEffect( function refreshTriggeredFromButton() { diff --git a/x-pack/plugins/aiops/public/components/log_categorization/utils.ts b/x-pack/plugins/aiops/public/components/log_categorization/utils.ts index ad45252a5c73d..c045bf748c2c4 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/utils.ts +++ b/x-pack/plugins/aiops/public/components/log_categorization/utils.ts @@ -6,27 +6,25 @@ */ import { stringHash } from '@kbn/ml-string-hash'; -import type { DataView } from '@kbn/data-views-plugin/public'; +import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; import { ES_FIELD_TYPES } from '@kbn/field-types'; import type { DocumentStats } from '../../hooks/use_document_count_stats'; /** * Creates a hash from the document stats to determine if the document stats have changed. */ -export function createDocumentStatsHash( - documentStats: DocumentStats, - additionalStrings: string[] = [] -) { +export function createDocumentStatsHash(documentStats: DocumentStats) { const lastTimeStampMs = documentStats.documentCountStats?.lastDocTimeStampMs; const totalCount = documentStats.documentCountStats?.totalCount; const times = Object.keys(documentStats.documentCountStats?.buckets ?? {}); const firstBucketTimeStamp = times.length ? times[0] : undefined; const lastBucketTimeStamp = times.length ? times[times.length - 1] : undefined; - return stringHash( - `${lastTimeStampMs}${totalCount}${firstBucketTimeStamp}${lastBucketTimeStamp}${additionalStrings.join( - '' - )}` - ); + + return stringHash(`${lastTimeStampMs}${totalCount}${firstBucketTimeStamp}${lastBucketTimeStamp}`); +} + +export function createAdditionalConfigHash(additionalStrings: string[] = []) { + return stringHash(`${additionalStrings.join('')}`); } /** @@ -36,10 +34,15 @@ export function createDocumentStatsHash( * @param dataView - The DataView object containing the fields. * @returns An object containing the message field and all the fields in the DataView. */ -export function getMessageField(dataView: DataView) { +export function getMessageField(dataView: DataView): { + messageField: DataViewField | null; + dataViewFields: DataViewField[]; +} { const dataViewFields = dataView.fields.filter((f) => f.esTypes?.includes(ES_FIELD_TYPES.TEXT)); - // setFields(dataViewFields); - let messageField = dataViewFields.find((f) => f.name === 'message'); + + let messageField: DataViewField | null | undefined = dataViewFields.find( + (f) => f.name === 'message' + ); if (messageField === undefined) { messageField = dataViewFields.find((f) => f.name === 'error.message'); } @@ -47,7 +50,11 @@ export function getMessageField(dataView: DataView) { messageField = dataViewFields.find((f) => f.name === 'event.original '); } if (messageField === undefined) { - messageField = dataViewFields[0]; + if (dataViewFields.length > 0) { + messageField = dataViewFields[0]; + } else { + messageField = null; + } } return { messageField, dataViewFields }; } From 7a663efee4a256c1b0d91bc8b559bdba2a73e140 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Tue, 9 Apr 2024 17:13:49 +0100 Subject: [PATCH 17/92] clear pattern count on config change --- .../log_categorization_for_embeddable.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx index 0d0d01048d7a5..b3087a1cc1e3d 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx @@ -165,6 +165,7 @@ export const LogCategorizationEmbeddable: FC = ({ const hash = createDocumentStatsHash(documentStats); if (hash !== previousDocumentStatsHash) { setCurrentDocumentStatsHash(hash); + setData(null); } }, [documentStats, previousDocumentStatsHash] @@ -179,6 +180,7 @@ export const LogCategorizationEmbeddable: FC = ({ const hash = createAdditionalConfigHash([selectedField.name, minimumTimeRangeOption]); if (hash !== previousAdditionalConfigsHash) { setCurrentAdditionalConfigsHash(hash); + setData(null); } }, [minimumTimeRangeOption, previousAdditionalConfigsHash, selectedField] @@ -316,6 +318,7 @@ export const LogCategorizationEmbeddable: FC = ({ ); return () => { + setPatternCount(undefined); setOptionsMenu(undefined); }; }, From e6bb05666d993b18db72bc9bb55cf94601a498d4 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 10 Apr 2024 14:10:57 +0100 Subject: [PATCH 18/92] updating info when no text fields --- .../log_categorization/information_text.tsx | 175 +++++++++++------- .../log_categorization_for_embeddable.tsx | 4 +- 2 files changed, 106 insertions(+), 73 deletions(-) diff --git a/x-pack/plugins/aiops/public/components/log_categorization/information_text.tsx b/x-pack/plugins/aiops/public/components/log_categorization/information_text.tsx index c4fed642b7d0d..5096f42e2082c 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/information_text.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/information_text.tsx @@ -10,93 +10,124 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiEmptyPrompt } from '@elastic/eui'; +import type { DataViewField } from '@kbn/data-views-plugin/public'; interface Props { eventRateLength: number; - fieldSelected: boolean; + fields?: DataViewField[]; categoriesLength: number | null; loading: boolean; } export const InformationText: FC = ({ eventRateLength, - fieldSelected, + fields, categoriesLength, loading, }) => { if (loading === true) { return null; } - return ( - <> - {eventRateLength === 0 ? ( - - - - } - titleSize="xs" - body={ -

- -

- } - data-test-subj="aiopsNoWindowParametersEmptyPrompt" - /> - ) : null} - {eventRateLength > 0 && categoriesLength === null ? ( - - - - } - titleSize="xs" - body={ -

- -

- } - data-test-subj="aiopsNoWindowParametersEmptyPrompt" - /> - ) : null} + if (fields?.length === 0) { + return ( + + + + } + titleSize="xs" + body={ +

+ +

+ } + data-test-subj="aiopsNoTextFieldsEmptyPrompt" + /> + ); + } + + if (eventRateLength === 0) { + return ( + + + + } + titleSize="xs" + body={ +

+ +

+ } + data-test-subj="aiopsNoDocsEmptyPrompt" + /> + ); + } + + if (eventRateLength > 0 && categoriesLength === null) { + return ( + + + + } + titleSize="xs" + body={ +

+ +

+ } + data-test-subj="aiopsNoWindowParametersEmptyPrompt" + /> + ); + } + + if (eventRateLength > 0 && categoriesLength !== null && categoriesLength === 0) { + return ( + + + + } + titleSize="xs" + body={ +

+ +

+ } + data-test-subj="aiopsNoCategoriesEmptyPrompt" + /> + ); + } - {eventRateLength > 0 && categoriesLength !== null && categoriesLength === 0 ? ( - - - - } - titleSize="xs" - body={ -

- -

- } - data-test-subj="aiopsNoWindowParametersEmptyPrompt" - /> - ) : null} - - ); + return null; }; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx index b3087a1cc1e3d..4cea7f2ed2191 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx @@ -166,6 +166,7 @@ export const LogCategorizationEmbeddable: FC = ({ if (hash !== previousDocumentStatsHash) { setCurrentDocumentStatsHash(hash); setData(null); + setFieldValidationResult(null); } }, [documentStats, previousDocumentStatsHash] @@ -181,6 +182,7 @@ export const LogCategorizationEmbeddable: FC = ({ if (hash !== previousAdditionalConfigsHash) { setCurrentAdditionalConfigsHash(hash); setData(null); + setFieldValidationResult(null); } }, [minimumTimeRangeOption, previousAdditionalConfigsHash, selectedField] @@ -417,7 +419,7 @@ export const LogCategorizationEmbeddable: FC = ({ loading={loading} categoriesLength={data?.categories?.length ?? null} eventRateLength={eventRate.length} - fieldSelected={selectedField !== null} + fields={fields} /> {loading === false && data !== null && From 953120d854c17518857a4fc76902cbfc60b088c3 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 10 Apr 2024 17:43:56 +0100 Subject: [PATCH 19/92] removing run pattern analysis button from field list --- .../categorize_trigger_utils.test.ts | 74 --------------- .../categorize_trigger_utils.ts | 58 ------------ .../field_categorize_button.test.tsx | 94 ------------------- .../field_categorize_button.tsx | 58 ------------ .../field_categorize_button_inner.tsx | 42 --------- .../field_categorize_button/index.tsx | 15 --- .../field_popover/field_popover_footer.tsx | 20 +--- .../log_categorization_for_flyout.tsx | 1 - .../log_categorization_page.tsx | 1 - 9 files changed, 3 insertions(+), 360 deletions(-) delete mode 100644 packages/kbn-unified-field-list/src/components/field_categorize_button/categorize_trigger_utils.test.ts delete mode 100644 packages/kbn-unified-field-list/src/components/field_categorize_button/categorize_trigger_utils.ts delete mode 100644 packages/kbn-unified-field-list/src/components/field_categorize_button/field_categorize_button.test.tsx delete mode 100644 packages/kbn-unified-field-list/src/components/field_categorize_button/field_categorize_button.tsx delete mode 100644 packages/kbn-unified-field-list/src/components/field_categorize_button/field_categorize_button_inner.tsx delete mode 100755 packages/kbn-unified-field-list/src/components/field_categorize_button/index.tsx diff --git a/packages/kbn-unified-field-list/src/components/field_categorize_button/categorize_trigger_utils.test.ts b/packages/kbn-unified-field-list/src/components/field_categorize_button/categorize_trigger_utils.test.ts deleted file mode 100644 index a0efbaf8de869..0000000000000 --- a/packages/kbn-unified-field-list/src/components/field_categorize_button/categorize_trigger_utils.test.ts +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import type { DataViewField, DataView } from '@kbn/data-views-plugin/public'; -import type { Action, UiActionsStart } from '@kbn/ui-actions-plugin/public'; -import { canCategorize } from './categorize_trigger_utils'; - -const textField = { - name: 'fieldName', - type: 'string', - esTypes: ['text'], - count: 1, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - visualizable: true, -} as DataViewField; - -const numberField = { - name: 'fieldName', - type: 'number', - esTypes: ['double'], - count: 1, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - visualizable: true, -} as DataViewField; - -const mockGetActions = jest.fn>>, [string, { fieldName: string }]>( - () => Promise.resolve([]) -); - -const uiActions = { - getTriggerCompatibleActions: mockGetActions, -} as unknown as UiActionsStart; - -const action: Action = { - id: 'action', - type: 'CATEGORIZE_FIELD', - getIconType: () => undefined, - getDisplayName: () => 'Action', - isCompatible: () => Promise.resolve(true), - execute: () => Promise.resolve(), -}; - -const dataViewMock = { id: '1', toSpec: () => ({}), isTimeBased: () => true } as DataView; - -describe('categorize_trigger_utils', () => { - afterEach(() => { - mockGetActions.mockReset(); - }); - - describe('getCategorizeInformation', () => { - it('should return true for a categorizable field with an action', async () => { - mockGetActions.mockResolvedValue([action]); - const resp = await canCategorize(uiActions, textField, dataViewMock); - expect(resp).toBe(true); - }); - - it('should return false for a non-categorizable field with an action', async () => { - mockGetActions.mockResolvedValue([action]); - const resp = await canCategorize(uiActions, numberField, dataViewMock); - expect(resp).toBe(false); - }); - }); -}); diff --git a/packages/kbn-unified-field-list/src/components/field_categorize_button/categorize_trigger_utils.ts b/packages/kbn-unified-field-list/src/components/field_categorize_button/categorize_trigger_utils.ts deleted file mode 100644 index 007a88b2c7f97..0000000000000 --- a/packages/kbn-unified-field-list/src/components/field_categorize_button/categorize_trigger_utils.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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; -import { CATEGORIZE_FIELD_TRIGGER, type CategorizeFieldContext } from '@kbn/ml-ui-actions'; -import type { DataViewField, DataView } from '@kbn/data-views-plugin/public'; - -async function getCompatibleActions( - uiActions: UiActionsStart, - field: DataViewField, - dataView: DataView, - trigger: typeof CATEGORIZE_FIELD_TRIGGER -) { - const compatibleActions = await uiActions.getTriggerCompatibleActions(trigger, { - dataView, - field, - }); - return compatibleActions; -} - -export function triggerCategorizeActions( - uiActions: UiActionsStart, - field: DataViewField, - originatingApp: string, - dataView?: DataView -) { - if (!dataView) return; - const triggerOptions: CategorizeFieldContext = { - dataView, - field, - originatingApp, - }; - uiActions.getTrigger(CATEGORIZE_FIELD_TRIGGER).exec(triggerOptions); -} - -export async function canCategorize( - uiActions: UiActionsStart, - field: DataViewField, - dataView: DataView | undefined -): Promise { - if ( - field.name === '_id' || - !dataView?.id || - !dataView.isTimeBased() || - !field.esTypes?.includes('text') - ) { - return false; - } - - const actions = await getCompatibleActions(uiActions, field, dataView, CATEGORIZE_FIELD_TRIGGER); - - return actions.length > 0; -} diff --git a/packages/kbn-unified-field-list/src/components/field_categorize_button/field_categorize_button.test.tsx b/packages/kbn-unified-field-list/src/components/field_categorize_button/field_categorize_button.test.tsx deleted file mode 100644 index 45569b3443370..0000000000000 --- a/packages/kbn-unified-field-list/src/components/field_categorize_button/field_categorize_button.test.tsx +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { act } from 'react-dom/test-utils'; -import { ReactWrapper } from 'enzyme'; -import { EuiButton } from '@elastic/eui'; -import { mountWithIntl } from '@kbn/test-jest-helpers'; -import { stubLogstashDataView as dataView } from '@kbn/data-views-plugin/common/data_view.stub'; -import { ActionInternal } from '@kbn/ui-actions-plugin/public'; -import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks'; -import { getFieldCategorizeButton } from './field_categorize_button'; -import { - CATEGORIZE_FIELD_TRIGGER, - ACTION_CATEGORIZE_FIELD, - type CategorizeFieldContext, -} from '@kbn/ml-ui-actions'; -import { TriggerContract } from '@kbn/ui-actions-plugin/public/triggers'; - -const ORIGINATING_APP = 'test'; -const mockExecuteAction = jest.fn(); -const uiActions = uiActionsPluginMock.createStartContract(); -const categorizeAction = new ActionInternal({ - type: ACTION_CATEGORIZE_FIELD, - id: ACTION_CATEGORIZE_FIELD, - getDisplayName: () => 'test', - isCompatible: async () => true, - execute: async (context: CategorizeFieldContext) => { - mockExecuteAction(context); - }, - getHref: async () => '/app/test', -}); - -jest - .spyOn(uiActions, 'getTriggerCompatibleActions') - .mockResolvedValue([categorizeAction as ActionInternal]); -jest.spyOn(uiActions, 'getTrigger').mockReturnValue({ - id: ACTION_CATEGORIZE_FIELD, - exec: mockExecuteAction, -} as unknown as TriggerContract); - -describe('UnifiedFieldList ', () => { - it('should render correctly', async () => { - const fieldName = 'extension'; - const field = dataView.fields.find((f) => f.name === fieldName)!; - let wrapper: ReactWrapper; - - const button = await getFieldCategorizeButton({ - field, - dataView, - originatingApp: ORIGINATING_APP, - uiActions, - }); - await act(async () => { - wrapper = await mountWithIntl(button!); - }); - - await wrapper!.update(); - - expect(uiActions.getTriggerCompatibleActions).toHaveBeenCalledWith(CATEGORIZE_FIELD_TRIGGER, { - dataView, - field, - }); - - expect(wrapper!.text()).toBe('Run pattern analysis'); - wrapper!.find(`button[data-test-subj="fieldCategorize-${fieldName}"]`).simulate('click'); - - expect(mockExecuteAction).toHaveBeenCalledWith({ - dataView, - field, - originatingApp: ORIGINATING_APP, - }); - - expect(wrapper!.find(EuiButton).exists()).toBeTruthy(); - }); - - it('should not render for non text field', async () => { - const fieldName = 'phpmemory'; - const field = dataView.fields.find((f) => f.name === fieldName)!; - - const button = await getFieldCategorizeButton({ - field, - dataView, - originatingApp: ORIGINATING_APP, - uiActions, - }); - - expect(button).toBe(null); - }); -}); diff --git a/packages/kbn-unified-field-list/src/components/field_categorize_button/field_categorize_button.tsx b/packages/kbn-unified-field-list/src/components/field_categorize_button/field_categorize_button.tsx deleted file mode 100644 index 5914c330f6661..0000000000000 --- a/packages/kbn-unified-field-list/src/components/field_categorize_button/field_categorize_button.tsx +++ /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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React from 'react'; -import { EuiButtonProps } from '@elastic/eui'; -import { METRIC_TYPE, UiCounterMetricType } from '@kbn/analytics'; -import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; -import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; -import { FieldCategorizeButtonInner } from './field_categorize_button_inner'; -import { triggerCategorizeActions, canCategorize } from './categorize_trigger_utils'; - -export interface FieldCategorizeButtonProps { - field: DataViewField; - dataView: DataView; - originatingApp: string; // plugin id - uiActions: UiActionsStart; - contextualFields?: string[]; // names of fields which were also selected (like columns in Discover grid) - trackUiMetric?: (metricType: UiCounterMetricType, eventName: string | string[]) => void; - buttonProps?: Partial; - closePopover?: () => void; -} - -export const FieldCategorizeButton: React.FC = React.memo( - ({ field, dataView, trackUiMetric, originatingApp, uiActions, buttonProps, closePopover }) => { - const handleVisualizeLinkClick = async ( - event: React.MouseEvent - ) => { - // regular link click. let the uiActions code handle the navigation and show popup if needed - event.preventDefault(); - const triggerVisualization = (updatedDataView: DataView) => { - trackUiMetric?.(METRIC_TYPE.CLICK, 'categorize_link_click'); - triggerCategorizeActions(uiActions, field, originatingApp, updatedDataView); - }; - triggerVisualization(dataView); - if (closePopover) { - closePopover(); - } - }; - - return ( - - ); - } -); - -export async function getFieldCategorizeButton(props: FieldCategorizeButtonProps) { - const showButton = await canCategorize(props.uiActions, props.field, props.dataView); - return showButton ? : null; -} diff --git a/packages/kbn-unified-field-list/src/components/field_categorize_button/field_categorize_button_inner.tsx b/packages/kbn-unified-field-list/src/components/field_categorize_button/field_categorize_button_inner.tsx deleted file mode 100644 index 8571822dcd8e9..0000000000000 --- a/packages/kbn-unified-field-list/src/components/field_categorize_button/field_categorize_button_inner.tsx +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React from 'react'; -import { EuiButton, EuiButtonProps } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; - -interface FieldVisualizeButtonInnerProps { - fieldName: string; - handleVisualizeLinkClick: (event: React.MouseEvent) => void; - buttonProps?: Partial; -} - -export const FieldCategorizeButtonInner: React.FC = ({ - fieldName, - handleVisualizeLinkClick, - buttonProps, -}) => { - return ( - <> - - - - - ); -}; diff --git a/packages/kbn-unified-field-list/src/components/field_categorize_button/index.tsx b/packages/kbn-unified-field-list/src/components/field_categorize_button/index.tsx deleted file mode 100755 index 8a07a8290ca84..0000000000000 --- a/packages/kbn-unified-field-list/src/components/field_categorize_button/index.tsx +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -export { - type FieldCategorizeButtonProps, - FieldCategorizeButton, - getFieldCategorizeButton, -} from './field_categorize_button'; - -export { triggerCategorizeActions, canCategorize } from './categorize_trigger_utils'; diff --git a/packages/kbn-unified-field-list/src/components/field_popover/field_popover_footer.tsx b/packages/kbn-unified-field-list/src/components/field_popover/field_popover_footer.tsx index bde3120708ef6..95378ba8b327b 100644 --- a/packages/kbn-unified-field-list/src/components/field_popover/field_popover_footer.tsx +++ b/packages/kbn-unified-field-list/src/components/field_popover/field_popover_footer.tsx @@ -7,16 +7,14 @@ */ import React, { useEffect, useState } from 'react'; -import { EuiPopoverFooter, EuiSpacer } from '@elastic/eui'; +import { EuiPopoverFooter } from '@elastic/eui'; import { type FieldVisualizeButtonProps, getFieldVisualizeButton } from '../field_visualize_button'; -import { FieldCategorizeButtonProps, getFieldCategorizeButton } from '../field_categorize_button'; import { ErrorBoundary } from '../error_boundary'; -export type FieldPopoverFooterProps = FieldVisualizeButtonProps | FieldCategorizeButtonProps; +export type FieldPopoverFooterProps = FieldVisualizeButtonProps; const FieldPopoverFooterComponent: React.FC = (props) => { const [visualizeButton, setVisualizeButton] = useState(null); - const [categorizeButton, setCategorizeButton] = useState(null); useEffect(() => { getFieldVisualizeButton(props) @@ -25,21 +23,9 @@ const FieldPopoverFooterComponent: React.FC = (props) = // eslint-disable-next-line no-console console.error(error); }); - getFieldCategorizeButton(props) - .then(setCategorizeButton) - .catch((error) => { - // eslint-disable-next-line no-console - console.error(error); - }); }, [props]); - return visualizeButton || categorizeButton ? ( - - {visualizeButton} - {visualizeButton && categorizeButton ? : null} - {categorizeButton} - - ) : null; + return visualizeButton ? {visualizeButton} : null; }; export const FieldPopoverFooter: React.FC = (props) => { diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_flyout.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_flyout.tsx index 31a34e0e3655a..3c6df53c59a92 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_flyout.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_flyout.tsx @@ -321,7 +321,6 @@ export const LogCategorizationFlyout: FC = ({ loading={loading} categoriesLength={data?.categories?.length ?? null} eventRateLength={eventRate.length} - fieldSelected={selectedField !== null} /> {loading === false && data !== null && data.categories.length > 0 ? ( <> diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx index 2ff408d4054d4..8fcfda30b89a3 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx @@ -387,7 +387,6 @@ export const LogCategorizationPage: FC = ({ embeddin loading={loading} categoriesLength={data?.categories?.length ?? null} eventRateLength={eventRate.length} - fieldSelected={selectedField !== null} /> {selectedField !== undefined && data !== null && data.categories.length > 0 ? ( From 8194d5d0f444c0994f599c4c139074b7d95c553b Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 12 Apr 2024 09:54:43 +0000 Subject: [PATCH 20/92] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- packages/kbn-unified-field-list/tsconfig.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/kbn-unified-field-list/tsconfig.json b/packages/kbn-unified-field-list/tsconfig.json index 657498734e3d8..94aab09459be2 100644 --- a/packages/kbn-unified-field-list/tsconfig.json +++ b/packages/kbn-unified-field-list/tsconfig.json @@ -31,7 +31,6 @@ "@kbn/ebt-tools", "@kbn/shared-ux-button-toolbar", "@kbn/field-utils", - "@kbn/ml-ui-actions", "@kbn/visualization-utils", "@kbn/esql-utils" ], From fef75c816bdc3b033b0a20fe9fb44c86cda9411e Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 12 Apr 2024 11:26:27 +0100 Subject: [PATCH 21/92] translations --- x-pack/plugins/translations/translations/fr-FR.json | 1 - x-pack/plugins/translations/translations/ja-JP.json | 1 - x-pack/plugins/translations/translations/zh-CN.json | 1 - 3 files changed, 3 deletions(-) diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 8d5815e668f60..a0c22f25fc50d 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -6803,7 +6803,6 @@ "unifiedFieldList.fieldStats.filterOutValueButtonAriaLabel": "Exclure le {field} : \"{value}\"", "unifiedFieldList.fieldStats.filterValueButtonAriaLabel": "Filtrer sur le {field} : \"{value}\"", "unifiedFieldList.fieldStats.noFieldDataInSampleDescription": "Aucune donnée de champ pour {sampledDocumentsFormatted} {sampledDocuments, plural, one {exemple d'enregistrement} other {exemples d'enregistrement}}.", - "unifiedFieldList.fieldCategorizeButton.label": "Exécuter l'analyse du modèle", "unifiedFieldList.fieldItemButton.mappingConflictDescription": "Ce champ est défini avec plusieurs types (chaîne, entier, etc.) dans les différents index qui correspondent à ce modèle. Vous pouvez toujours utiliser ce champ conflictuel, mais il sera indisponible pour les fonctions qui nécessitent que Kibana en connaisse le type. Pour corriger ce problème, vous devrez réindexer vos données.", "unifiedFieldList.fieldItemButton.mappingConflictTitle": "Conflit de mapping", "unifiedFieldList.fieldList.noFieldsCallout.noDataLabel": "Aucun champ.", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 4cb86cc5f0cad..38b8972b2b7eb 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -6792,7 +6792,6 @@ "unifiedFieldList.fieldStats.filterOutValueButtonAriaLabel": "{field}を除外:\"{value}\"", "unifiedFieldList.fieldStats.filterValueButtonAriaLabel": "{field}を除外:\"{value}\"", "unifiedFieldList.fieldStats.noFieldDataInSampleDescription": "{sampledDocumentsFormatted}サンプル{sampledDocuments, plural, other {レコード}}のフィールドデータがありません。", - "unifiedFieldList.fieldCategorizeButton.label": "パターン分析を実行", "unifiedFieldList.fieldItemButton.mappingConflictDescription": "このフィールドは、このパターンと一致するインデックス全体に対して複数の型(文字列、整数など)として定義されています。この競合フィールドを使用することはできますが、Kibana で型を認識する必要がある関数では使用できません。この問題を修正するにはデータのレンダリングが必要です。", "unifiedFieldList.fieldItemButton.mappingConflictTitle": "マッピングの矛盾", "unifiedFieldList.fieldList.noFieldsCallout.noDataLabel": "フィールドがありません。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 7e688fd7baed4..68475c40b099b 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -6806,7 +6806,6 @@ "unifiedFieldList.fieldStats.filterOutValueButtonAriaLabel": "筛除 {field}:“{value}”", "unifiedFieldList.fieldStats.filterValueButtonAriaLabel": "筛留 {field}:“{value}”", "unifiedFieldList.fieldStats.noFieldDataInSampleDescription": "{sampledDocumentsFormatted} 个样例{sampledDocuments, plural, other {记录}}无字段数据。", - "unifiedFieldList.fieldCategorizeButton.label": "运行模式分析", "unifiedFieldList.fieldItemButton.mappingConflictDescription": "此字段在匹配此模式的各个索引中已定义为若干类型(字符串、整数等)。您可能仍可以使用此冲突字段,但它无法用于需要 Kibana 知道其类型的函数。要解决此问题,需要重新索引您的数据。", "unifiedFieldList.fieldItemButton.mappingConflictTitle": "映射冲突", "unifiedFieldList.fieldList.noFieldsCallout.noDataLabel": "无字段。", From d6e8f215f747bc1d8493ce0aeac8c25e34655d1c Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 12 Apr 2024 12:29:40 +0100 Subject: [PATCH 22/92] fixing unused closePopover --- .../src/containers/unified_field_list_item/field_list_item.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/kbn-unified-field-list/src/containers/unified_field_list_item/field_list_item.tsx b/packages/kbn-unified-field-list/src/containers/unified_field_list_item/field_list_item.tsx index 409184c4d8e04..d9e02d423cd9e 100644 --- a/packages/kbn-unified-field-list/src/containers/unified_field_list_item/field_list_item.tsx +++ b/packages/kbn-unified-field-list/src/containers/unified_field_list_item/field_list_item.tsx @@ -309,7 +309,6 @@ function UnifiedFieldListItemComponent({ contextualFields={workspaceSelectedFieldNames} originatingApp={stateService.creationOptions.originatingApp} uiActions={services.uiActions} - closePopover={() => closePopover()} /> )} From a0ec3b0ff9e450741684d51f2e629a7a0f32dd49 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 12 Apr 2024 12:32:28 +0100 Subject: [PATCH 23/92] fixing duplicate id --- .../public/components/view_mode_toggle/view_mode_toggle.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx index e95d462da9b54..ba89f39de1ab9 100644 --- a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx +++ b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx @@ -90,7 +90,7 @@ export const DocumentViewModeToggle = ({ setDiscoverViewMode(VIEW_MODE.PATTERN_LEVEL)} - data-test-subj="dscViewModeFieldStatsButton" + data-test-subj="dscViewModePatternAnalysisButton" > Date: Fri, 12 Apr 2024 14:13:27 +0100 Subject: [PATCH 24/92] fixing tests --- .../view_mode_toggle/view_mode_toggle.test.tsx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx index e1788389d3caf..33a5cdb9bdfe2 100644 --- a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx +++ b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx @@ -74,6 +74,13 @@ describe('Document view mode toggle component', () => { expect(setDiscoverViewMode).toHaveBeenCalledWith(VIEW_MODE.DOCUMENT_LEVEL); }); + it('should set the view mode to VIEW_MODE.PATTERN_LEVEL when dscViewModePatternAnalysisButton is clicked', () => { + const setDiscoverViewMode = jest.fn(); + const component = mountComponent({ setDiscoverViewMode }); + component.find('[data-test-subj="dscViewModePatternAnalysisButton"]').at(0).simulate('click'); + expect(setDiscoverViewMode).toHaveBeenCalledWith(VIEW_MODE.PATTERN_LEVEL); + }); + it('should set the view mode to VIEW_MODE.AGGREGATED_LEVEL when dscViewModeFieldStatsButton is clicked', () => { const setDiscoverViewMode = jest.fn(); const component = mountComponent({ setDiscoverViewMode }); @@ -86,8 +93,13 @@ describe('Document view mode toggle component', () => { expect(component.find(EuiTab).at(0).prop('isSelected')).toBe(true); }); + it('should select the Patter Analysis tab if viewMode is VIEW_MODE.PATTERN_LEVEL', () => { + const component = mountComponent({ viewMode: VIEW_MODE.PATTERN_LEVEL }); + expect(component.find(EuiTab).at(1).prop('isSelected')).toBe(true); + }); + it('should select the Field statistics tab if viewMode is VIEW_MODE.AGGREGATED_LEVEL', () => { const component = mountComponent({ viewMode: VIEW_MODE.AGGREGATED_LEVEL }); - expect(component.find(EuiTab).at(1).prop('isSelected')).toBe(true); + expect(component.find(EuiTab).at(2).prop('isSelected')).toBe(true); }); }); From 683b07e99e10aab8ebf43adc0471492a5ed03c00 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 12 Apr 2024 16:05:49 +0100 Subject: [PATCH 25/92] updating tests --- .../layout/discover_main_content.tsx | 2 + .../view_mode_toggle.test.tsx | 34 +++++++++++++- .../view_mode_toggle/view_mode_toggle.tsx | 44 +++++++++++++------ 3 files changed, 66 insertions(+), 14 deletions(-) diff --git a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx index cddab7d1464b2..490e609575331 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx @@ -93,6 +93,7 @@ export const DiscoverMainContent = ({ stateContainer={stateContainer} setDiscoverViewMode={setDiscoverViewMode} patternCount={patternCount} + dataView={dataView} prepend={ React.isValidElement(panelsToggle) ? React.cloneElement(panelsToggle, { renderedFor: 'tabs', isChartAvailable }) @@ -108,6 +109,7 @@ export const DiscoverMainContent = ({ panelsToggle, isChartAvailable, patternCount, + dataView, ]); const showChart = useAppStateSelector((state) => !state.hideChart); diff --git a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx index 33a5cdb9bdfe2..3fe532186aa75 100644 --- a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx +++ b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx @@ -12,6 +12,7 @@ import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import React from 'react'; import { findTestSubject } from '@elastic/eui/lib/test'; +import type { DataView } from '@kbn/data-views-plugin/common'; import { DocumentViewModeToggle } from './view_mode_toggle'; import { BehaviorSubject } from 'rxjs'; import { getDiscoverStateMock } from '../../__mocks__/discover_state.mock'; @@ -24,6 +25,7 @@ describe('Document view mode toggle component', () => { viewMode = VIEW_MODE.DOCUMENT_LEVEL, isTextBasedQuery = false, setDiscoverViewMode = jest.fn(), + useDataViewWithTextFields = true, } = {}) => { const serivces = { uiSettings: { @@ -31,6 +33,24 @@ describe('Document view mode toggle component', () => { }, }; + const dataViewWithTextFields = { + fields: [ + { + name: 'field1', + esTypes: ['text'], + }, + ], + } as unknown as DataView; + + const dataViewWithoutTextFields = { + fields: [ + { + name: 'field1', + esTypes: ['float'], + }, + ], + } as unknown as DataView; + const stateContainer = getDiscoverStateMock({ isTimeBased: true }); stateContainer.dataState.data$.totalHits$ = new BehaviorSubject({ fetchStatus: FetchStatus.COMPLETE, @@ -44,6 +64,7 @@ describe('Document view mode toggle component', () => { isTextBasedQuery={isTextBasedQuery} stateContainer={stateContainer} setDiscoverViewMode={setDiscoverViewMode} + dataView={useDataViewWithTextFields ? dataViewWithTextFields : dataViewWithoutTextFields} /> ); @@ -93,7 +114,7 @@ describe('Document view mode toggle component', () => { expect(component.find(EuiTab).at(0).prop('isSelected')).toBe(true); }); - it('should select the Patter Analysis tab if viewMode is VIEW_MODE.PATTERN_LEVEL', () => { + it('should select the Pattern Analysis tab if viewMode is VIEW_MODE.PATTERN_LEVEL', () => { const component = mountComponent({ viewMode: VIEW_MODE.PATTERN_LEVEL }); expect(component.find(EuiTab).at(1).prop('isSelected')).toBe(true); }); @@ -102,4 +123,15 @@ describe('Document view mode toggle component', () => { const component = mountComponent({ viewMode: VIEW_MODE.AGGREGATED_LEVEL }); expect(component.find(EuiTab).at(2).prop('isSelected')).toBe(true); }); + + it('should switch to document and hide pattern tab when there are no text fields', () => { + const setDiscoverViewMode = jest.fn(); + const component = mountComponent({ + viewMode: VIEW_MODE.PATTERN_LEVEL, + useDataViewWithTextFields: false, + setDiscoverViewMode, + }); + expect(setDiscoverViewMode).toHaveBeenCalledWith(VIEW_MODE.DOCUMENT_LEVEL); + expect(component.find(EuiTab).length).toBe(2); + }); }); diff --git a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx index ba89f39de1ab9..849e273dfe2ea 100644 --- a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx +++ b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx @@ -6,14 +6,16 @@ * Side Public License, v 1. */ -import React, { useMemo, ReactElement } from 'react'; +import React, { useMemo, useEffect, type ReactElement } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiTab, EuiTabs, useEuiTheme } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { css } from '@emotion/react'; import { isLegacyTableEnabled, SHOW_FIELD_STATISTICS } from '@kbn/discover-utils'; +import type { DataView } from '@kbn/data-views-plugin/common'; +import { ES_FIELD_TYPES } from '@kbn/field-types'; import { VIEW_MODE } from '../../../common/constants'; import { useDiscoverServices } from '../../hooks/use_discover_services'; -import { DiscoverStateContainer } from '../../application/main/services/discover_state'; +import type { DiscoverStateContainer } from '../../application/main/services/discover_state'; import { HitsCounter, HitsCounterMode } from '../hits_counter'; export const DocumentViewModeToggle = ({ @@ -23,6 +25,7 @@ export const DocumentViewModeToggle = ({ stateContainer, setDiscoverViewMode, patternCount, + dataView, }: { viewMode: VIEW_MODE; isTextBasedQuery: boolean; @@ -30,6 +33,7 @@ export const DocumentViewModeToggle = ({ stateContainer: DiscoverStateContainer; setDiscoverViewMode: (viewMode: VIEW_MODE) => void; patternCount?: number; + dataView: DataView; }) => { const { euiTheme } = useEuiTheme(); const { uiSettings } = useDiscoverServices(); @@ -37,6 +41,18 @@ export const DocumentViewModeToggle = ({ () => isLegacyTableEnabled({ uiSettings, isTextBasedQueryMode: isTextBasedQuery }), [uiSettings, isTextBasedQuery] ); + + const showPatternAnalysisTab = useMemo( + () => dataView.fields.some((f) => f.esTypes?.includes(ES_FIELD_TYPES.TEXT)), + [dataView.fields] + ); + + useEffect(() => { + if (showPatternAnalysisTab === false && viewMode === VIEW_MODE.PATTERN_LEVEL) { + setDiscoverViewMode(VIEW_MODE.DOCUMENT_LEVEL); + } + }, [showPatternAnalysisTab, viewMode, setDiscoverViewMode]); + const includesNormalTabsStyle = viewMode === VIEW_MODE.AGGREGATED_LEVEL || viewMode === VIEW_MODE.PATTERN_LEVEL || isLegacy; @@ -87,17 +103,19 @@ export const DocumentViewModeToggle = ({ - setDiscoverViewMode(VIEW_MODE.PATTERN_LEVEL)} - data-test-subj="dscViewModePatternAnalysisButton" - > - - + {showPatternAnalysisTab ? ( + setDiscoverViewMode(VIEW_MODE.PATTERN_LEVEL)} + data-test-subj="dscViewModePatternAnalysisButton" + > + + + ) : null} Date: Fri, 12 Apr 2024 15:15:57 +0000 Subject: [PATCH 26/92] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- src/plugins/discover/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/discover/tsconfig.json b/src/plugins/discover/tsconfig.json index 0e17ef1ea7131..1e4eb98d0429f 100644 --- a/src/plugins/discover/tsconfig.json +++ b/src/plugins/discover/tsconfig.json @@ -86,6 +86,7 @@ "@kbn/data-view-utils", "@kbn/presentation-publishing", "@kbn/aiops-log-pattern-analysis", + "@kbn/field-types", ], "exclude": ["target/**/*"] } From c479cd78bb71d30c3c0f0a2d79bfa42df439657f Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 12 Apr 2024 18:04:08 +0100 Subject: [PATCH 27/92] adding constant --- .../components/view_mode_toggle/view_mode_toggle.test.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx index 3fe532186aa75..6fa9d4e7404c3 100644 --- a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx +++ b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx @@ -18,6 +18,7 @@ import { BehaviorSubject } from 'rxjs'; import { getDiscoverStateMock } from '../../__mocks__/discover_state.mock'; import { DataTotalHits$ } from '../../application/main/services/discover_data_state_container'; import { FetchStatus } from '../../application/types'; +import { ES_FIELD_TYPES } from '@kbn/field-types'; describe('Document view mode toggle component', () => { const mountComponent = ({ @@ -37,7 +38,7 @@ describe('Document view mode toggle component', () => { fields: [ { name: 'field1', - esTypes: ['text'], + esTypes: [ES_FIELD_TYPES.TEXT], }, ], } as unknown as DataView; @@ -46,7 +47,7 @@ describe('Document view mode toggle component', () => { fields: [ { name: 'field1', - esTypes: ['float'], + esTypes: [ES_FIELD_TYPES.FLOAT], }, ], } as unknown as DataView; From 8d6e59d42b9167b20ca88e0d9b7e6c6215f0b701 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 12 Apr 2024 18:14:49 +0100 Subject: [PATCH 28/92] testing bundle size --- x-pack/plugins/aiops/public/types/storage.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/aiops/public/types/storage.ts b/x-pack/plugins/aiops/public/types/storage.ts index a83bee80c79ab..dc44beae64b30 100644 --- a/x-pack/plugins/aiops/public/types/storage.ts +++ b/x-pack/plugins/aiops/public/types/storage.ts @@ -6,7 +6,9 @@ */ import { type FrozenTierPreference } from '@kbn/ml-date-picker'; -import type { MinimumTimeRangeOption } from '../components/log_categorization/log_categorization_for_embeddable/minimum_time_range'; + +type MinimumTimeRangeOption = 'No minimum' | '1 week' | '1 month' | '3 months' | '6 months'; + import { type RandomSamplerOption, type RandomSamplerProbability, From cd8deea2886c99dc0488bfac5c0e677f16733cbf Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 12 Apr 2024 18:38:43 +0100 Subject: [PATCH 29/92] better loading state --- .../log_categorization/loading_categorization.tsx | 6 +++--- .../log_categorization_for_embeddable.tsx | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/aiops/public/components/log_categorization/loading_categorization.tsx b/x-pack/plugins/aiops/public/components/log_categorization/loading_categorization.tsx index cd0e5cd7ad297..3e2707d507e9d 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/loading_categorization.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/loading_categorization.tsx @@ -29,16 +29,16 @@ export const LoadingCategorization: FC = ({ onClose }) => ( - + -

+

-

+
diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx index 4cea7f2ed2191..f3459ba16057f 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx @@ -99,7 +99,7 @@ export const LogCategorizationEmbeddable: FC = ({ const [previousAdditionalConfigsHash, setPreviousAdditionalConfigsHash] = useState( null ); - const [loading, setLoading] = useState(false); + const [loading, setLoading] = useState(null); const [eventRate, setEventRate] = useState([]); const [pinnedCategory, setPinnedCategory] = useState(null); const [data, setData] = useState<{ @@ -115,6 +115,7 @@ export const LogCategorizationEmbeddable: FC = ({ function initFields() { setCurrentDocumentStatsHash(null); setSelectedField(null); + setLoading(null); const { dataViewFields, messageField } = getMessageField(dataView); setFields(dataViewFields); setSelectedField(messageField); @@ -414,9 +415,9 @@ export const LogCategorizationEmbeddable: FC = ({ <> - {loading === true ? : null} + {(loading ?? true) === true ? : null} Date: Mon, 22 Apr 2024 19:24:19 +0100 Subject: [PATCH 30/92] fixing bundle size --- x-pack/plugins/aiops/public/plugin.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/aiops/public/plugin.tsx b/x-pack/plugins/aiops/public/plugin.tsx index 7de01aeebe7a3..0d0f0e9e687c7 100755 --- a/x-pack/plugins/aiops/public/plugin.tsx +++ b/x-pack/plugins/aiops/public/plugin.tsx @@ -15,7 +15,7 @@ import type { AiopsPluginStart, AiopsPluginStartDeps, } from './types'; -import { getEmbeddableChangePointChart } from './embeddables/change_point_chart'; +import { getEmbeddableChangePointChart } from './embeddables/change_point_chart/embeddable_change_point_chart_component'; export type AiopsCoreSetup = CoreSetup; From 9fb9eb67dc1ec3b81d05a282ed78eb2bdcfeae6d Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 24 Apr 2024 15:56:20 +0100 Subject: [PATCH 31/92] refactoring table header --- .../category_table/category_table.tsx | 187 +++++------------- .../category_table/table_header.tsx | 13 +- .../category_table/use_open_in_discover.ts | 99 ++++++++++ .../loading_categorization.tsx | 6 +- .../log_categorization_for_embeddable.tsx | 79 +++++--- .../log_categorization_for_flyout.tsx | 62 +++--- .../log_categorization_page.tsx | 54 +++-- .../components/annotations/description.tsx | 2 +- 8 files changed, 278 insertions(+), 224 deletions(-) create mode 100644 x-pack/plugins/aiops/public/components/log_categorization/category_table/use_open_in_discover.ts diff --git a/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx b/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx index 7e26306d92a84..2e1d70ad5cad8 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx @@ -7,138 +7,70 @@ import type { FC } from 'react'; import React, { useCallback, useMemo, useState } from 'react'; -import moment from 'moment'; import type { EuiBasicTableColumn, EuiTableSelectionType } from '@elastic/eui'; import { useEuiBackgroundColor, EuiInMemoryTable, - EuiHorizontalRule, - EuiSpacer, EuiButtonIcon, EuiToolTip, EuiIcon, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import type { TimefilterContract } from '@kbn/data-plugin/public'; -import type { DataViewField } from '@kbn/data-views-plugin/common'; -import type { Filter } from '@kbn/es-query'; import { useTableState } from '@kbn/ml-in-memory-table'; import { css } from '@emotion/react'; -import type { CategorizationAdditionalFilter } from '@kbn/aiops-log-pattern-analysis/create_category_request'; -import { type QueryMode, QUERY_MODE } from '@kbn/aiops-log-pattern-analysis/get_category_query'; +import { QUERY_MODE } from '@kbn/aiops-log-pattern-analysis/get_category_query'; import type { Category } from '@kbn/aiops-log-pattern-analysis/types'; import { useEuiTheme } from '../../../hooks/use_eui_theme'; -import type { LogCategorizationAppState } from '../../../application/url_state/log_pattern_analysis'; import { MiniHistogram } from '../../mini_histogram'; -import { useDiscoverLinks, createFilter } from '../use_discover_links'; import type { EventRate } from '../use_categorize_request'; -import { getLabels } from './labels'; -import { TableHeader } from './table_header'; import { ExpandedRow } from './expanded_row'; import { FormattedPatternExamples, FormattedTokens } from '../format_category'; +import type { OpenInDiscover } from './use_open_in_discover'; interface Props { categories: Category[]; eventRate: EventRate; - dataViewId: string; - selectedField: DataViewField | string | undefined; - timefilter: TimefilterContract; - aiopsListState: LogCategorizationAppState; pinnedCategory: Category | null; setPinnedCategory: (category: Category | null) => void; - selectedCategory: Category | null; - setSelectedCategory: (category: Category | null) => void; - onAddFilter?: (values: Filter, alias?: string) => void; - onClose?: () => void; + highlightedCategory: Category | null; + setHighlightedCategory: (category: Category | null) => void; + setSelectedCategories: (category: Category[]) => void; + openInDiscover: OpenInDiscover; enableRowActions?: boolean; - additionalFilter?: CategorizationAdditionalFilter; - navigateToDiscover?: boolean; displayExamples?: boolean; - displayHeader?: boolean; } export const CategoryTable: FC = ({ categories, eventRate, - dataViewId, - selectedField, - timefilter, - aiopsListState, pinnedCategory, setPinnedCategory, - selectedCategory, - setSelectedCategory, - onAddFilter, - onClose = () => {}, + highlightedCategory, + setHighlightedCategory, + setSelectedCategories, + openInDiscover, enableRowActions = true, - additionalFilter, - navigateToDiscover = true, displayExamples = true, - displayHeader = true, }) => { const euiTheme = useEuiTheme(); const primaryBackgroundColor = useEuiBackgroundColor('primary'); - const { openInDiscoverWithFilter } = useDiscoverLinks(); - const [selectedCategories, setSelectedCategories] = useState([]); const { onTableChange, pagination, sorting } = useTableState(categories ?? [], 'key'); const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState>( {} ); - const labels = useMemo(() => { - const isFlyout = onAddFilter !== undefined && onClose !== undefined; - return getLabels(isFlyout && navigateToDiscover === false); - }, [navigateToDiscover, onAddFilter, onClose]); - const showSparkline = useMemo(() => { return categories.some((category) => category.sparkline !== undefined); }, [categories]); - const openInDiscover = (mode: QueryMode, category?: Category) => { - if ( - onAddFilter !== undefined && - selectedField !== undefined && - typeof selectedField !== 'string' && - navigateToDiscover === false - ) { - onAddFilter( - createFilter('', selectedField.name, selectedCategories, mode, category), - `Patterns - ${selectedField.name}` - ); - onClose(); - return; - } - - const timefilterActiveBounds = - additionalFilter !== undefined - ? { - min: moment(additionalFilter.from), - max: moment(additionalFilter.to), - } - : timefilter.getActiveBounds(); - - if (timefilterActiveBounds === undefined || selectedField === undefined) { - return; - } - - openInDiscoverWithFilter( - dataViewId, - typeof selectedField === 'string' ? selectedField : selectedField.name, - selectedCategories, - aiopsListState, - timefilterActiveBounds, - mode, - category, - additionalFilter?.field - ); - }; + const { labels: openInDiscoverLabels, openFunction: openInDiscoverFunction } = openInDiscover; const toggleDetails = useCallback( (category: Category) => { @@ -198,23 +130,23 @@ export const CategoryTable: FC = ({ defaultMessage: 'Actions', }), sortable: false, - width: '60px', + width: '70px', actions: [ { - name: labels.singleSelect.in, - description: labels.singleSelect.in, + name: openInDiscoverLabels.singleSelect.in, + description: openInDiscoverLabels.singleSelect.in, icon: 'plusInCircle', type: 'icon', 'data-test-subj': 'aiopsLogPatternsActionFilterInButton', - onClick: (category) => openInDiscover(QUERY_MODE.INCLUDE, category), + onClick: (category) => openInDiscoverFunction(QUERY_MODE.INCLUDE, category), }, { - name: labels.singleSelect.out, - description: labels.singleSelect.out, + name: openInDiscoverLabels.singleSelect.out, + description: openInDiscoverLabels.singleSelect.out, icon: 'minusInCircle', type: 'icon', 'data-test-subj': 'aiopsLogPatternsActionFilterOutButton', - onClick: (category) => openInDiscover(QUERY_MODE.EXCLUDE, category), + onClick: (category) => openInDiscoverFunction(QUERY_MODE.EXCLUDE, category), }, ], }, @@ -295,7 +227,7 @@ export const CategoryTable: FC = ({ }; } - if (selectedCategory && selectedCategory.key === category.key) { + if (highlightedCategory && highlightedCategory.key === category.key) { return { backgroundColor: euiTheme.euiColorLightestShade, }; @@ -317,53 +249,38 @@ export const CategoryTable: FC = ({ }); return ( - <> - {displayHeader ? ( - <> - openInDiscover(queryMode)} - /> - - - - ) : null} - - - compressed - items={categories} - columns={columns} - selection={selectionValue} - itemId="key" - onTableChange={onTableChange} - pagination={pagination} - sorting={sorting} - data-test-subj="aiopsLogPatternsTable" - itemIdToExpandedRowMap={itemIdToExpandedRowMap} - css={tableStyle} - rowProps={(category) => { - return enableRowActions - ? { - onClick: () => { - if (category.key === pinnedCategory?.key) { - setPinnedCategory(null); - } else { - setPinnedCategory(category); - } - }, - onMouseEnter: () => { - setSelectedCategory(category); - }, - onMouseLeave: () => { - setSelectedCategory(null); - }, - style: getRowStyle(category), - } - : undefined; - }} - /> - + + compressed + items={categories} + columns={columns} + selection={selectionValue} + itemId="key" + onTableChange={onTableChange} + pagination={pagination} + sorting={sorting} + data-test-subj="aiopsLogPatternsTable" + itemIdToExpandedRowMap={itemIdToExpandedRowMap} + css={tableStyle} + rowProps={(category) => { + return enableRowActions + ? { + onClick: () => { + if (category.key === pinnedCategory?.key) { + setPinnedCategory(null); + } else { + setPinnedCategory(category); + } + }, + onMouseEnter: () => { + setHighlightedCategory(category); + }, + onMouseLeave: () => { + setHighlightedCategory(null); + }, + style: getRowStyle(category), + } + : undefined; + }} + /> ); }; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/category_table/table_header.tsx b/x-pack/plugins/aiops/public/components/log_categorization/category_table/table_header.tsx index 08ac019b8fdc3..208f04b600081 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/category_table/table_header.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/category_table/table_header.tsx @@ -9,24 +9,23 @@ import type { FC } from 'react'; import React from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiText, EuiButtonEmpty } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { type QueryMode, QUERY_MODE } from '@kbn/aiops-log-pattern-analysis/get_category_query'; +import { QUERY_MODE } from '@kbn/aiops-log-pattern-analysis/get_category_query'; import { useEuiTheme } from '../../../hooks/use_eui_theme'; -import type { getLabels } from './labels'; +import type { OpenInDiscover } from './use_open_in_discover'; interface Props { categoriesCount: number; selectedCategoriesCount: number; - labels: ReturnType; - openInDiscover: (mode: QueryMode) => void; + openInDiscover: OpenInDiscover; } export const TableHeader: FC = ({ categoriesCount, selectedCategoriesCount, - labels, openInDiscover, }) => { const euiTheme = useEuiTheme(); + const { labels, openFunction } = openInDiscover; return ( <> @@ -54,7 +53,7 @@ export const TableHeader: FC = ({ openInDiscover(QUERY_MODE.INCLUDE)} + onClick={() => openFunction(QUERY_MODE.INCLUDE)} iconType="plusInCircle" iconSide="left" > @@ -65,7 +64,7 @@ export const TableHeader: FC = ({ openInDiscover(QUERY_MODE.EXCLUDE)} + onClick={() => openFunction(QUERY_MODE.EXCLUDE)} iconType="minusInCircle" iconSide="left" > diff --git a/x-pack/plugins/aiops/public/components/log_categorization/category_table/use_open_in_discover.ts b/x-pack/plugins/aiops/public/components/log_categorization/category_table/use_open_in_discover.ts new file mode 100644 index 0000000000000..2ffe48cc4afef --- /dev/null +++ b/x-pack/plugins/aiops/public/components/log_categorization/category_table/use_open_in_discover.ts @@ -0,0 +1,99 @@ +/* + * 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 { useCallback, useMemo } from 'react'; + +import moment from 'moment'; + +import { type QueryMode } from '@kbn/aiops-log-pattern-analysis/get_category_query'; +import type { Filter } from '@kbn/es-query'; +import type { Category } from '@kbn/aiops-log-pattern-analysis/types'; +import type { DataViewField } from '@kbn/data-views-plugin/common'; +import type { TimefilterContract } from '@kbn/data-plugin/public'; +import type { CategorizationAdditionalFilter } from '@kbn/aiops-log-pattern-analysis/create_category_request'; +import { useDiscoverLinks, createFilter } from '../use_discover_links'; +import type { LogCategorizationAppState } from '../../../application/url_state/log_pattern_analysis'; +import { getLabels } from './labels'; + +export interface OpenInDiscover { + openFunction: (mode: QueryMode, category?: Category) => void; + labels: any; +} + +export function useOpenInDiscover( + dataViewId: string, + selectedField: DataViewField | string | undefined, + selectedCategories: Category[], + aiopsListState: LogCategorizationAppState, + timefilter: TimefilterContract, + navigateToDiscover?: boolean, + onAddFilter?: (values: Filter, alias?: string) => void, + additionalFilter?: CategorizationAdditionalFilter, + onClose: () => void = () => {} +): OpenInDiscover { + const { openInDiscoverWithFilter } = useDiscoverLinks(); + + const openFunction = useCallback( + (mode: QueryMode, category?: Category) => { + if ( + onAddFilter !== undefined && + selectedField !== undefined && + typeof selectedField !== 'string' && + navigateToDiscover === false + ) { + onAddFilter( + createFilter('', selectedField.name, selectedCategories, mode, category), + `Patterns - ${selectedField.name}` + ); + onClose(); + return; + } + + const timefilterActiveBounds = + additionalFilter !== undefined + ? { + min: moment(additionalFilter.from), + max: moment(additionalFilter.to), + } + : timefilter.getActiveBounds(); + + if (timefilterActiveBounds === undefined || selectedField === undefined) { + return; + } + + openInDiscoverWithFilter( + dataViewId, + typeof selectedField === 'string' ? selectedField : selectedField.name, + selectedCategories, + aiopsListState, + timefilterActiveBounds, + mode, + category, + additionalFilter?.field + ); + }, + [ + onAddFilter, + selectedField, + navigateToDiscover, + additionalFilter, + timefilter, + openInDiscoverWithFilter, + dataViewId, + selectedCategories, + aiopsListState, + onClose, + ] + ); + + const labels = useMemo(() => { + const isFlyout = onAddFilter !== undefined && onClose !== undefined; + return getLabels(isFlyout && navigateToDiscover === false); + }, [navigateToDiscover, onAddFilter, onClose]); + + return { openFunction, labels }; +} diff --git a/x-pack/plugins/aiops/public/components/log_categorization/loading_categorization.tsx b/x-pack/plugins/aiops/public/components/log_categorization/loading_categorization.tsx index 3e2707d507e9d..1d98325f2d987 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/loading_categorization.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/loading_categorization.tsx @@ -19,10 +19,10 @@ import { import { FormattedMessage } from '@kbn/i18n-react'; interface Props { - onClose: () => void; + onCancel: () => void; } -export const LoadingCategorization: FC = ({ onClose }) => ( +export const LoadingCategorization: FC = ({ onCancel }) => ( <> @@ -46,7 +46,7 @@ export const LoadingCategorization: FC = ({ onClose }) => ( onClose()} + onClick={() => onCancel()} > Cancel diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx index f3459ba16057f..1fbbe701dafae 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx @@ -41,6 +41,7 @@ import { EmbeddableMenu } from './embeddable_menu'; import { useMinimumTimeRange } from './use_minimum_time_range'; import { createAdditionalConfigHash, createDocumentStatsHash, getMessageField } from '../utils'; +import { useOpenInDiscover } from '../category_table/use_open_in_discover'; export interface LogCategorizationPageProps { onClose: () => void; @@ -88,7 +89,8 @@ export const LogCategorizationEmbeddable: FC = ({ searchQuery: createMergedEsQuery(query, filters, dataView, uiSettings), }) ); - const [selectedCategory, setSelectedCategory] = useState(null); + const [highlightedCategory, setHighlightedCategory] = useState(null); + const [selectedCategories, setSelectedCategories] = useState([]); const [selectedField, setSelectedField] = useState(null); const [fields, setFields] = useState([]); const [currentDocumentStatsHash, setCurrentDocumentStatsHash] = useState(null); @@ -157,6 +159,36 @@ export const LogCategorizationEmbeddable: FC = ({ BAR_TARGET ); + const onAddFilter = useCallback( + (values: Filter, alias?: string) => { + if (input.onAddFilter === undefined) { + return; + } + + const filter = buildEmptyFilter(false, dataView.id); + if (alias) { + filter.meta.alias = alias; + } + filter.query = values.query; + if (onAddFilter !== undefined) { + input.onAddFilter(); + } + filterManager.addFilters([filter]); + }, + [dataView.id, filterManager, input] + ); + + const openInDiscover = useOpenInDiscover( + dataView.id!, + selectedField?.displayName ?? undefined, + selectedCategories, + stateFromUrl, + timefilter, + false, + onAddFilter, + undefined + ); + useEffect( function createDocumentStatHash() { if (documentStats.documentCountStats === undefined) { @@ -243,6 +275,8 @@ export const LogCategorizationEmbeddable: FC = ({ timeRange.useSubAgg ? tempAdditionalFilter : undefined ), ]); + // eslint-disable-next-line no-console + console.log('categorizationResult', categorizationResult); if (mounted.current !== true) { return; @@ -338,25 +372,6 @@ export const LogCategorizationEmbeddable: FC = ({ ] ); - const onAddFilter = useCallback( - (values: Filter, alias?: string) => { - if (input.onAddFilter === undefined) { - return; - } - - const filter = buildEmptyFilter(false, dataView.id); - if (alias) { - filter.meta.alias = alias; - } - filter.query = values.query; - if (onAddFilter !== undefined) { - input.onAddFilter(); - } - filterManager.addFilters([filter]); - }, - [dataView.id, filterManager, input] - ); - useEffect( function triggerAnalysis() { const buckets = documentStats.documentCountStats?.buckets; @@ -369,6 +384,8 @@ export const LogCategorizationEmbeddable: FC = ({ (currentAdditionalConfigsHash !== previousAdditionalConfigsHash && currentDocumentStatsHash !== null) ) { + // eslint-disable-next-line no-console + console.log('trigger', currentDocumentStatsHash, previousDocumentStatsHash); randomSampler.setDocCount(documentStats.totalCount); setEventRate( Object.entries(buckets).map(([key, docCount]) => ({ @@ -379,6 +396,9 @@ export const LogCategorizationEmbeddable: FC = ({ loadCategories(); setPreviousDocumentStatsHash(currentDocumentStatsHash); setPreviousAdditionalConfigsHash(currentAdditionalConfigsHash); + } else { + // eslint-disable-next-line no-console + console.log('no trigger', currentDocumentStatsHash, previousDocumentStatsHash); } }, [ @@ -397,6 +417,8 @@ export const LogCategorizationEmbeddable: FC = ({ useEffect( function refreshTriggeredFromButton() { if (input?.lastReloadRequestTime !== undefined) { + setPreviousDocumentStatsHash(0); + setPreviousAdditionalConfigsHash(null); forceRefresh(); } }, @@ -415,7 +437,7 @@ export const LogCategorizationEmbeddable: FC = ({ <> - {(loading ?? true) === true ? : null} + {(loading ?? true) === true ? : null} = ({ selectedField !== null ? ( ) : null} diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_flyout.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_flyout.tsx index ac4605d6181e7..92b140e0a557a 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_flyout.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_flyout.tsx @@ -19,14 +19,13 @@ import { EuiSpacer, EuiToolTip, EuiIcon, + EuiHorizontalRule, } from '@elastic/eui'; import type { SavedSearch } from '@kbn/saved-search-plugin/public'; import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { Filter } from '@kbn/es-query'; -import { buildEmptyFilter } from '@kbn/es-query'; import { usePageUrlState } from '@kbn/ml-url-state'; import type { FieldValidationResults } from '@kbn/ml-category-validator'; import { AIOPS_TELEMETRY_ID } from '@kbn/aiops-common/constants'; @@ -51,6 +50,8 @@ import { LoadingCategorization } from './loading_categorization'; import { useValidateFieldRequest } from './use_validate_category_field'; import { FieldValidationCallout } from './category_validation_callout'; import { CreateCategorizationJobButton } from './create_categorization_job'; +import { TableHeader } from './category_table/table_header'; +import { useOpenInDiscover } from './category_table/use_open_in_discover'; enum SELECTED_TAB { BUCKET, @@ -80,7 +81,7 @@ export const LogCategorizationFlyout: FC = ({ const { notifications: { toasts }, data: { - query: { getState, filterManager }, + query: { getState }, }, uiSettings, } = useAiopsAppContext(); @@ -102,7 +103,8 @@ export const LogCategorizationFlyout: FC = ({ searchQuery: createMergedEsQuery(query, filters, dataView, uiSettings), }) ); - const [selectedCategory, setSelectedCategory] = useState(null); + const [highlightedCategory, setHighlightedCategory] = useState(null); + const [selectedCategories, setSelectedCategories] = useState([]); const [selectedSavedSearch /* , setSelectedSavedSearch*/] = useState(savedSearch); const [loading, setLoading] = useState(true); const [eventRate, setEventRate] = useState([]); @@ -150,6 +152,17 @@ export const LogCategorizationFlyout: FC = ({ BAR_TARGET ); + const openInDiscover = useOpenInDiscover( + dataView.id!, + selectedField, + selectedCategories, + stateFromUrl, + timefilter, + true, + undefined, + undefined + ); + const loadCategories = useCallback(async () => { const { getIndexPattern, timeFieldName: timeField } = dataView; const index = getIndexPattern(); @@ -243,18 +256,6 @@ export const LogCategorizationFlyout: FC = ({ toasts, ]); - const onAddFilter = useCallback( - (values: Filter, alias?: string) => { - const filter = buildEmptyFilter(false, dataView.id); - if (alias) { - filter.meta.alias = alias; - } - filter.query = values.query; - filterManager.addFilters([filter]); - }, - [dataView, filterManager] - ); - useEffect(() => { if (documentStats.documentCountStats?.buckets) { randomSampler.setDocCount(documentStats.totalCount); @@ -312,7 +313,7 @@ export const LogCategorizationFlyout: FC = ({ /> ) : null} - {loading === true ? : null} + {loading === true ? : null} = ({ ) : null} + + + + + ) : null} diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx index 8fcfda30b89a3..f0f252cbc8345 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx @@ -10,6 +10,7 @@ import React, { useState, useEffect, useCallback, useMemo } from 'react'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { EuiComboBoxOptionOption } from '@elastic/eui'; +import { EuiHorizontalRule } from '@elastic/eui'; import { EuiButton, EuiSpacer, @@ -51,6 +52,8 @@ import { SamplingMenu } from './sampling_menu'; import { useValidateFieldRequest } from './use_validate_category_field'; import { FieldValidationCallout } from './category_validation_callout'; import { createDocumentStatsHash } from './utils'; +import { TableHeader } from './category_table/table_header'; +import { useOpenInDiscover } from './category_table/use_open_in_discover'; const BAR_TARGET = 20; const DEFAULT_SELECTED_FIELD = 'message'; @@ -79,7 +82,8 @@ export const LogCategorizationPage: FC = ({ embeddin ); const [globalState, setGlobalState] = useUrlState('_g'); const [selectedField, setSelectedField] = useState(); - const [selectedCategory, setSelectedCategory] = useState(null); + const [highlightedCategory, setHighlightedCategory] = useState(null); + const [selectedCategories, setSelectedCategories] = useState([]); const [selectedSavedSearch, setSelectedSavedSearch] = useState(savedSearch); const [previousDocumentStatsHash, setPreviousDocumentStatsHash] = useState(0); const [loading, setLoading] = useState(false); @@ -153,6 +157,17 @@ export const LogCategorizationPage: FC = ({ embeddin BAR_TARGET ); + const openInDiscover = useOpenInDiscover( + dataView.id!, + selectedField, + selectedCategories, + stateFromUrl, + timefilter, + true, + undefined, + undefined + ); + useEffect(() => { if (globalState?.time !== undefined) { timefilter.setTime({ @@ -371,7 +386,7 @@ export const LogCategorizationPage: FC = ({ embeddin @@ -390,19 +405,28 @@ export const LogCategorizationPage: FC = ({ embeddin /> {selectedField !== undefined && data !== null && data.categories.length > 0 ? ( - + <> + + + + + + + ) : null} ); diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/components/annotations/description.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/components/annotations/description.tsx index d7ecc537cdeaa..c5f35c6ff4e1e 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/components/annotations/description.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/components/annotations/description.tsx @@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiDescribedFormGroup, EuiFormRow } from '@elastic/eui'; -export const Description: FC = memo(({ children }) => { +export const Description: FC<{ children?: React.ReactNode }> = memo(({ children }) => { const title = i18n.translate( 'xpack.ml.newJob.wizard.jobDetailsStep.advancedSection.enableModelPlotAnnotations.title', { From 8917824b1e72568328ee68b76ecd68d19dae6ff8 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 25 Apr 2024 14:21:04 +0100 Subject: [PATCH 32/92] adding multiselect buttons --- .../category_table/table_header.tsx | 80 ++++++++++++------- .../category_table/use_open_in_discover.ts | 2 +- .../embeddable_menu.tsx | 2 +- .../log_categorization_for_embeddable.tsx | 37 ++++++--- 4 files changed, 81 insertions(+), 40 deletions(-) diff --git a/x-pack/plugins/aiops/public/components/log_categorization/category_table/table_header.tsx b/x-pack/plugins/aiops/public/components/log_categorization/category_table/table_header.tsx index 208f04b600081..66968861465a3 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/category_table/table_header.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/category_table/table_header.tsx @@ -5,9 +5,9 @@ * 2.0. */ -import type { FC } from 'react'; +import type { FC, PropsWithChildren } from 'react'; import React from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiText, EuiButtonEmpty } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiText, EuiButtonEmpty, EuiToolTip } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { QUERY_MODE } from '@kbn/aiops-log-pattern-analysis/get_category_query'; import { useEuiTheme } from '../../../hooks/use_eui_theme'; @@ -25,7 +25,6 @@ export const TableHeader: FC = ({ openInDiscover, }) => { const euiTheme = useEuiTheme(); - const { labels, openFunction } = openInDiscover; return ( <> @@ -48,32 +47,59 @@ export const TableHeader: FC = ({ {selectedCategoriesCount > 0 ? ( - <> - - openFunction(QUERY_MODE.INCLUDE)} - iconType="plusInCircle" - iconSide="left" - > - {labels.multiSelect.in} - - - - openFunction(QUERY_MODE.EXCLUDE)} - iconType="minusInCircle" - iconSide="left" - > - {labels.multiSelect.out} - - - + + + ) : null} ); }; + +export const OpenInDiscoverButtons: FC<{ openInDiscover: OpenInDiscover; showText?: boolean }> = ({ + openInDiscover, + showText = true, +}) => { + const { labels, openFunction } = openInDiscover; + const TooltipWrapper: FC> = ({ text, children }) => { + return showText ? ( + <>{children} + ) : ( + + <>{children} + + ); + }; + return ( + + + + openFunction(QUERY_MODE.INCLUDE)} + iconType="plusInCircle" + iconSide="left" + > + {labels.multiSelect.in} + {/* {showText ? labels.multiSelect.in : null} */} + + + + + + openFunction(QUERY_MODE.EXCLUDE)} + iconType="minusInCircle" + iconSide="left" + > + {labels.multiSelect.out} + {/* {showText ? labels.multiSelect.out : null} */} + + + + + ); +}; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/category_table/use_open_in_discover.ts b/x-pack/plugins/aiops/public/components/log_categorization/category_table/use_open_in_discover.ts index 2ffe48cc4afef..b92e1da7f9564 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/category_table/use_open_in_discover.ts +++ b/x-pack/plugins/aiops/public/components/log_categorization/category_table/use_open_in_discover.ts @@ -21,7 +21,7 @@ import { getLabels } from './labels'; export interface OpenInDiscover { openFunction: (mode: QueryMode, category?: Category) => void; - labels: any; + labels: ReturnType; } export function useOpenInDiscover( diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx index 0561f8ec34bb8..cca4c417070dc 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx @@ -67,7 +67,7 @@ export const EmbeddableMenu: FC = ({ iconType="arrowDown" iconSide="right" onClick={() => togglePopover()} - css={{ marginTop: '4px', marginRight: '4px' }} + css={{ marginRight: '4px' }} > Options diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx index 1fbbe701dafae..609a72d57288e 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx @@ -7,7 +7,7 @@ import type { FC } from 'react'; import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import type { DataViewField } from '@kbn/data-views-plugin/public'; import { i18n } from '@kbn/i18n'; @@ -42,6 +42,7 @@ import { useMinimumTimeRange } from './use_minimum_time_range'; import { createAdditionalConfigHash, createDocumentStatsHash, getMessageField } from '../utils'; import { useOpenInDiscover } from '../category_table/use_open_in_discover'; +import { OpenInDiscoverButtons } from '../category_table/table_header'; export interface LogCategorizationPageProps { onClose: () => void; @@ -341,16 +342,28 @@ export const LogCategorizationEmbeddable: FC = ({ setOptionsMenu( <> {randomSampler !== undefined ? ( - loadCategories()} - fields={fields} - setSelectedField={setSelectedField} - selectedField={selectedField} - minimumTimeRangeOption={minimumTimeRangeOption} - setMinimumTimeRangeOption={setMinimumTimeRangeOption} - categoryCount={data?.totalCategories} - /> + <> + + + {selectedCategories.length > 0 ? ( + + + + ) : null} + + loadCategories()} + fields={fields} + setSelectedField={setSelectedField} + selectedField={selectedField} + minimumTimeRangeOption={minimumTimeRangeOption} + setMinimumTimeRangeOption={setMinimumTimeRangeOption} + categoryCount={data?.totalCategories} + /> + + + ) : null} ); @@ -369,6 +382,8 @@ export const LogCategorizationEmbeddable: FC = ({ setPatternCount, setMinimumTimeRangeOption, minimumTimeRangeOption, + selectedCategories.length, + openInDiscover, ] ); From 1f12dd5093b1596ad22cb8f48b3a3fc739254f04 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 25 Apr 2024 16:05:15 +0100 Subject: [PATCH 33/92] fixing cancel button --- .../src/services/time_field_range.ts | 5 ++- .../log_categorization_for_embeddable.tsx | 42 ++++++++++--------- .../use_minimum_time_range.ts | 1 + .../use_validate_category_field.ts | 1 + 4 files changed, 28 insertions(+), 21 deletions(-) diff --git a/x-pack/packages/ml/date_picker/src/services/time_field_range.ts b/x-pack/packages/ml/date_picker/src/services/time_field_range.ts index 92d71f582a1ef..f935c4577b394 100644 --- a/x-pack/packages/ml/date_picker/src/services/time_field_range.ts +++ b/x-pack/packages/ml/date_picker/src/services/time_field_range.ts @@ -40,6 +40,8 @@ interface GetTimeFieldRangeOptions { * API path ('/internal/file_upload/time_field_range') */ path: string; + + signal?: AbortSignal; } /** @@ -48,12 +50,13 @@ interface GetTimeFieldRangeOptions { * @returns GetTimeFieldRangeResponse */ export async function getTimeFieldRange(options: GetTimeFieldRangeOptions) { - const { http, path, ...body } = options; + const { http, path, signal, ...body } = options; return await http.fetch({ path, method: 'POST', body: JSON.stringify(body), version: '1', + ...(signal ? { signal } : {}), }); } diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx index 609a72d57288e..b2bc8191da9d0 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx @@ -47,7 +47,6 @@ import { OpenInDiscoverButtons } from '../category_table/table_header'; export interface LogCategorizationPageProps { onClose: () => void; embeddingOrigin: string; - additionalFilter?: CategorizationAdditionalFilter; input: Readonly; } @@ -56,7 +55,6 @@ const BAR_TARGET = 20; export const LogCategorizationEmbeddable: FC = ({ onClose, embeddingOrigin, - additionalFilter, input, }) => { const { @@ -244,24 +242,24 @@ export const LogCategorizationEmbeddable: FC = ({ setData(null); setFieldValidationResult(null); - const tempAdditionalFilter: CategorizationAdditionalFilter = { + const additionalFilter: CategorizationAdditionalFilter = { from: earliest, to: latest, }; - const timeRange = await getMinimumTimeRange( - index, - timeField, - tempAdditionalFilter, - minimumTimeRangeOption, - searchQuery - ); + try { + const timeRange = await getMinimumTimeRange( + index, + timeField, + additionalFilter, + minimumTimeRangeOption, + searchQuery + ); - if (mounted.current !== true) { - return; - } + if (mounted.current !== true) { + return; + } - try { const [validationResult, categorizationResult] = await Promise.all([ runValidateFieldRequest(index, selectedField.name, timeField, timeRange, searchQuery, { [AIOPS_TELEMETRY_ID.AIOPS_ANALYSIS_RUN_ORIGIN]: embeddingOrigin, @@ -273,7 +271,7 @@ export const LogCategorizationEmbeddable: FC = ({ { to: timeRange.to, from: timeRange.from }, searchQuery, intervalMs, - timeRange.useSubAgg ? tempAdditionalFilter : undefined + timeRange.useSubAgg ? additionalFilter : undefined ), ]); // eslint-disable-next-line no-console @@ -309,11 +307,15 @@ export const LogCategorizationEmbeddable: FC = ({ }); } } catch (error) { - toasts.addError(error, { - title: i18n.translate('xpack.aiops.logCategorization.errorLoadingCategories', { - defaultMessage: 'Error loading categories', - }), - }); + if (error.name === 'AbortError') { + // ignore error + } else { + toasts.addError(error, { + title: i18n.translate('xpack.aiops.logCategorization.errorLoadingCategories', { + defaultMessage: 'Error loading categories', + }), + }); + } } if (mounted.current === true) { diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/use_minimum_time_range.ts b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/use_minimum_time_range.ts index 9a98bf016ba58..e8a47546608da 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/use_minimum_time_range.ts +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/use_minimum_time_range.ts @@ -48,6 +48,7 @@ export function useMinimumTimeRange() { timeFieldName: timeField, query: queryIn, path: '/internal/file_upload/time_field_range', + signal: abortController.current.signal, }); // the index isn't big enough to get a wider time range diff --git a/x-pack/plugins/aiops/public/components/log_categorization/use_validate_category_field.ts b/x-pack/plugins/aiops/public/components/log_categorization/use_validate_category_field.ts index b2e16363aba35..e771a981a9c49 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/use_validate_category_field.ts +++ b/x-pack/plugins/aiops/public/components/log_categorization/use_validate_category_field.ts @@ -51,6 +51,7 @@ export function useValidateFieldRequest() { }), headers, version: '1', + signal: abortController.current.signal, } ); From 4720ebc18f78ffa04e4adb7cf3a488415f256118 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 25 Apr 2024 17:03:54 +0100 Subject: [PATCH 34/92] reverting unrelated change --- .../advanced_section/components/annotations/description.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/components/annotations/description.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/components/annotations/description.tsx index c5f35c6ff4e1e..d7ecc537cdeaa 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/components/annotations/description.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/advanced_section/components/annotations/description.tsx @@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiDescribedFormGroup, EuiFormRow } from '@elastic/eui'; -export const Description: FC<{ children?: React.ReactNode }> = memo(({ children }) => { +export const Description: FC = memo(({ children }) => { const title = i18n.translate( 'xpack.ml.newJob.wizard.jobDetailsStep.advancedSection.enableModelPlotAnnotations.title', { From 8fc020aeee1fa131def04a7e98798521f7ddd928 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 25 Apr 2024 17:05:37 +0100 Subject: [PATCH 35/92] reverting type change --- x-pack/plugins/aiops/public/types/storage.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x-pack/plugins/aiops/public/types/storage.ts b/x-pack/plugins/aiops/public/types/storage.ts index dc44beae64b30..ea6fde6b06552 100644 --- a/x-pack/plugins/aiops/public/types/storage.ts +++ b/x-pack/plugins/aiops/public/types/storage.ts @@ -6,8 +6,7 @@ */ import { type FrozenTierPreference } from '@kbn/ml-date-picker'; - -type MinimumTimeRangeOption = 'No minimum' | '1 week' | '1 month' | '3 months' | '6 months'; +import type { MinimumTimeRangeOption } from '../components/log_categorization/log_categorization_for_embeddable/minimum_time_range'; import { type RandomSamplerOption, From 19ae83937433d48b957c61bf14d571e553550080 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 26 Apr 2024 13:27:38 +0100 Subject: [PATCH 36/92] fixing behavour when adding filter --- .../log_categorization_for_embeddable.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx index b2bc8191da9d0..1e2e5c455779c 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx @@ -169,9 +169,7 @@ export const LogCategorizationEmbeddable: FC = ({ filter.meta.alias = alias; } filter.query = values.query; - if (onAddFilter !== undefined) { - input.onAddFilter(); - } + input.onAddFilter(); filterManager.addFilters([filter]); }, [dataView.id, filterManager, input] From a5f30446c4191714687215c1bfb1ec092d10ced0 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 26 Apr 2024 15:51:30 +0100 Subject: [PATCH 37/92] fixing filter in discover --- .../log_categorization_for_embeddable.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx index 1e2e5c455779c..99895dd10ac0b 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx @@ -177,7 +177,7 @@ export const LogCategorizationEmbeddable: FC = ({ const openInDiscover = useOpenInDiscover( dataView.id!, - selectedField?.displayName ?? undefined, + selectedField ?? undefined, selectedCategories, stateFromUrl, timefilter, From 9324d33fe31e3127f51aa19b1e628617512f8600 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 29 Apr 2024 08:37:16 +0000 Subject: [PATCH 38/92] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- x-pack/plugins/aiops/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/aiops/tsconfig.json b/x-pack/plugins/aiops/tsconfig.json index 6b11a3c6b91f4..e29fe3d0b4c22 100644 --- a/x-pack/plugins/aiops/tsconfig.json +++ b/x-pack/plugins/aiops/tsconfig.json @@ -70,6 +70,7 @@ "@kbn/aiops-change-point-detection", "@kbn/react-kibana-context-theme", "@kbn/react-kibana-context-render", + "@kbn/core-theme-browser", ], "exclude": [ "target/**/*", From 9dccb07ca63ea2b3d559f65adc671e1effa4f336 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Mon, 29 Apr 2024 15:20:56 +0100 Subject: [PATCH 39/92] renaming log categorization for discover code --- .../layout/discover_main_content.tsx | 6 ++--- .../constants.ts | 0 .../index.ts | 4 ++-- .../pattern_analysis_tab.tsx} | 11 ++++------ .../pattern_analysis_table.tsx} | 22 +++++++++---------- .../aiops_log_pattern_analysis/embeddable.ts | 10 ++++----- .../log_categorization_for_embeddable.tsx | 4 ++-- .../log_categorization_embeddable.tsx | 14 ++++++------ .../log_categorization_embeddable_factory.ts | 10 ++++----- 9 files changed, 39 insertions(+), 42 deletions(-) rename src/plugins/discover/public/application/main/components/{log_categorization => pattern_analysis}/constants.ts (100%) rename src/plugins/discover/public/application/main/components/{log_categorization => pattern_analysis}/index.ts (72%) rename src/plugins/discover/public/application/main/components/{log_categorization/log_categorization_tab.tsx => pattern_analysis/pattern_analysis_tab.tsx} (85%) rename src/plugins/discover/public/application/main/components/{log_categorization/log_categorization_table.tsx => pattern_analysis/pattern_analysis_table.tsx} (87%) diff --git a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx index 490e609575331..e994e144f8245 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx @@ -22,8 +22,8 @@ import { DiscoverDocuments } from './discover_documents'; import { DOCUMENTS_VIEW_CLICK, FIELD_STATISTICS_VIEW_CLICK } from '../field_stats_table/constants'; import { useAppStateSelector } from '../../services/discover_app_state_container'; import type { PanelsToggleProps } from '../../../../components/panels_toggle'; -import { LogCategorizationTab } from '../log_categorization/log_categorization_tab'; -import { PATTERN_ANALYSIS_LOADED } from '../log_categorization/constants'; +import { PatternAnalysisTab } from '../pattern_analysis/pattern_analysis_tab'; +import { PATTERN_ANALYSIS_LOADED } from '../pattern_analysis/constants'; const DROP_PROPS = { value: { @@ -153,7 +153,7 @@ export const DiscoverMainContent = ({ ) : null} {viewMode === VIEW_MODE.PATTERN_LEVEL ? ( <> - setDiscoverViewMode(VIEW_MODE.DOCUMENT_LEVEL)} diff --git a/src/plugins/discover/public/application/main/components/log_categorization/constants.ts b/src/plugins/discover/public/application/main/components/pattern_analysis/constants.ts similarity index 100% rename from src/plugins/discover/public/application/main/components/log_categorization/constants.ts rename to src/plugins/discover/public/application/main/components/pattern_analysis/constants.ts diff --git a/src/plugins/discover/public/application/main/components/log_categorization/index.ts b/src/plugins/discover/public/application/main/components/pattern_analysis/index.ts similarity index 72% rename from src/plugins/discover/public/application/main/components/log_categorization/index.ts rename to src/plugins/discover/public/application/main/components/pattern_analysis/index.ts index 01018047813a8..8741f1040edb4 100644 --- a/src/plugins/discover/public/application/main/components/log_categorization/index.ts +++ b/src/plugins/discover/public/application/main/components/pattern_analysis/index.ts @@ -6,5 +6,5 @@ * Side Public License, v 1. */ -export { LogCategorizationTable } from './log_categorization_table'; -export { LogCategorizationTab } from './log_categorization_tab'; +export { PatternAnalysisTable } from './pattern_analysis_table'; +export { PatternAnalysisTab } from './pattern_analysis_tab'; diff --git a/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_tab.tsx b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_tab.tsx similarity index 85% rename from src/plugins/discover/public/application/main/components/log_categorization/log_categorization_tab.tsx rename to src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_tab.tsx index 991d3e7aa2eee..693213c9ff740 100644 --- a/src/plugins/discover/public/application/main/components/log_categorization/log_categorization_tab.tsx +++ b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_tab.tsx @@ -10,14 +10,11 @@ import React, { useState, memo, type ReactElement, type FC } from 'react'; import { useQuerySubscriber } from '@kbn/unified-field-list/src/hooks/use_query_subscriber'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { useSavedSearch } from '../../services/discover_state_provider'; -import { - LogCategorizationTable, - type LogCategorizationTableProps, -} from './log_categorization_table'; +import { PatternAnalysisTable, type PatternAnalysisTableProps } from './pattern_analysis_table'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; -export const LogCategorizationTab: FC< - Omit +export const PatternAnalysisTab: FC< + Omit > = memo((props) => { const services = useDiscoverServices(); const querySubscriberResult = useQuerySubscriber({ @@ -35,7 +32,7 @@ export const LogCategorizationTab: FC< {optionsMenu} - void; }; -export const LogCategorizationTable = (props: LogCategorizationTableProps) => { +export const PatternAnalysisTable = (props: PatternAnalysisTableProps) => { const { dataView, savedSearch, @@ -58,7 +58,7 @@ export const LogCategorizationTable = (props: LogCategorizationTableProps) => { const services = useDiscoverServices(); const [embeddable, setEmbeddable] = useState< | ErrorEmbeddable - | IEmbeddable + | IEmbeddable | undefined >(); const embeddableRoot: React.RefObject = useRef(null); @@ -102,13 +102,13 @@ export const LogCategorizationTable = (props: LogCategorizationTableProps) => { const loadEmbeddable = async () => { if (services.embeddable) { const factory = services.embeddable.getEmbeddableFactory< - EmbeddableLogCategorizationInput, - EmbeddableLogCategorizationOutput - >(EMBEDDABLE_LOG_CATEGORIZATION_TYPE); + EmbeddablePatternAnalysisInput, + EmbeddablePatternAnalysisOutput + >(EMBEDDABLE_PATTERN_ANALYSIS_TYPE); if (factory) { // Initialize embeddable with information available at mount const initializedEmbeddable = await factory.create({ - id: EMBEDDABLE_LOG_CATEGORIZATION_TYPE, + id: EMBEDDABLE_PATTERN_ANALYSIS_TYPE, dataView, savedSearch, query, diff --git a/x-pack/packages/ml/aiops_log_pattern_analysis/embeddable.ts b/x-pack/packages/ml/aiops_log_pattern_analysis/embeddable.ts index 0b952dacf3436..04ceed7548cca 100644 --- a/x-pack/packages/ml/aiops_log_pattern_analysis/embeddable.ts +++ b/x-pack/packages/ml/aiops_log_pattern_analysis/embeddable.ts @@ -10,7 +10,7 @@ import type { Query, AggregateQuery, Filter } from '@kbn/es-query'; import type { SavedSearch } from '@kbn/saved-search-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/public'; -export interface EmbeddableLogCategorizationProps { +export interface EmbeddablePatternAnalysisProps { dataView: DataView; savedSearch?: SavedSearch | null; query?: T; @@ -25,10 +25,10 @@ export interface EmbeddableLogCategorizationProps { setOptionsMenu: (optionsMenu: React.ReactElement | undefined) => void; } -export type EmbeddableLogCategorizationInput = EmbeddableInput & EmbeddableLogCategorizationProps; +export type EmbeddablePatternAnalysisInput = EmbeddableInput & EmbeddablePatternAnalysisProps; -export type EmbeddableLogCategorizationOutput = EmbeddableOutput & { indexPatterns?: DataView[] }; +export type EmbeddablePatternAnalysisOutput = EmbeddableOutput & { indexPatterns?: DataView[] }; -export const EMBEDDABLE_LOG_CATEGORIZATION_TYPE = 'aiopsLogCategorization' as const; +export const EMBEDDABLE_PATTERN_ANALYSIS_TYPE = 'aiopsPatternAnalysis' as const; -export type EmbeddableLogCategorizationType = typeof EMBEDDABLE_LOG_CATEGORIZATION_TYPE; +export type EmbeddablePatternAnalysisType = typeof EMBEDDABLE_PATTERN_ANALYSIS_TYPE; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx index 99895dd10ac0b..5173066cc6afd 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx @@ -20,7 +20,7 @@ import type { Category } from '@kbn/aiops-log-pattern-analysis/types'; import type { CategorizationAdditionalFilter } from '@kbn/aiops-log-pattern-analysis/create_category_request'; import { AIOPS_TELEMETRY_ID } from '@kbn/aiops-common/constants'; -import type { EmbeddableLogCategorizationInput } from '@kbn/aiops-log-pattern-analysis/embeddable'; +import type { EmbeddablePatternAnalysisInput } from '@kbn/aiops-log-pattern-analysis/embeddable'; import { type LogCategorizationPageUrlState, getDefaultLogCategorizationAppState, @@ -47,7 +47,7 @@ import { OpenInDiscoverButtons } from '../category_table/table_header'; export interface LogCategorizationPageProps { onClose: () => void; embeddingOrigin: string; - input: Readonly; + input: Readonly; } const BAR_TARGET = 20; diff --git a/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx b/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx index ba9e35e1ce10d..31050ebcbf497 100644 --- a/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx +++ b/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx @@ -25,12 +25,12 @@ import { StorageContextProvider } from '@kbn/ml-local-storage'; import { Storage } from '@kbn/kibana-utils-plugin/public'; import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; -import type { EmbeddableLogCategorizationType } from '@kbn/aiops-log-pattern-analysis/embeddable'; +import type { EmbeddablePatternAnalysisType } from '@kbn/aiops-log-pattern-analysis/embeddable'; import { EMBEDDABLE_ORIGIN } from '@kbn/aiops-common/constants'; import type { - EmbeddableLogCategorizationInput, - EmbeddableLogCategorizationOutput, + EmbeddablePatternAnalysisInput, + EmbeddablePatternAnalysisOutput, } from '@kbn/aiops-log-pattern-analysis/embeddable'; import useObservable from 'react-use/lib/useObservable'; import { AiopsAppContext, type AiopsAppDependencies } from '../../hooks/use_aiops_app_context'; @@ -75,8 +75,8 @@ const LogCategorizationEmbeddableWrapper = (props: { }; export class EmbeddableLogCategorization extends AbstractEmbeddable< - EmbeddableLogCategorizationInput, - EmbeddableLogCategorizationOutput + EmbeddablePatternAnalysisInput, + EmbeddablePatternAnalysisOutput > { private reload$ = new Subject(); @@ -90,9 +90,9 @@ export class EmbeddableLogCategorization extends AbstractEmbeddable< deferEmbeddableLoad = true; constructor( - public readonly type: EmbeddableLogCategorizationType, + public readonly type: EmbeddablePatternAnalysisType, private readonly deps: EmbeddableLogCategorizationDeps, - initialInput: EmbeddableLogCategorizationInput, + initialInput: EmbeddablePatternAnalysisInput, parent?: IContainer ) { super(initialInput, {}, parent); diff --git a/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable_factory.ts b/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable_factory.ts index 4457539e97911..895e71596ca00 100644 --- a/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable_factory.ts +++ b/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable_factory.ts @@ -9,15 +9,15 @@ import { i18n } from '@kbn/i18n'; import type { StartServicesAccessor } from '@kbn/core/public'; import type { EmbeddableFactoryDefinition, IContainer } from '@kbn/embeddable-plugin/public'; -import { EMBEDDABLE_LOG_CATEGORIZATION_TYPE } from '@kbn/aiops-log-pattern-analysis/embeddable'; -import type { EmbeddableLogCategorizationInput } from '@kbn/aiops-log-pattern-analysis/embeddable'; +import { EMBEDDABLE_PATTERN_ANALYSIS_TYPE } from '@kbn/aiops-log-pattern-analysis/embeddable'; +import type { EmbeddablePatternAnalysisInput } from '@kbn/aiops-log-pattern-analysis/embeddable'; import type { AiopsPluginStart, AiopsPluginStartDeps } from '../../types'; import type { EmbeddableLogCategorizationDeps } from './log_categorization_embeddable'; export class EmbeddableLogCategorizationFactory - implements EmbeddableFactoryDefinition + implements EmbeddableFactoryDefinition { - public readonly type = EMBEDDABLE_LOG_CATEGORIZATION_TYPE; + public readonly type = EMBEDDABLE_PATTERN_ANALYSIS_TYPE; public readonly grouping = [ { @@ -76,7 +76,7 @@ export class EmbeddableLogCategorizationFactory }; } - public async create(input: EmbeddableLogCategorizationInput, parent?: IContainer) { + public async create(input: EmbeddablePatternAnalysisInput, parent?: IContainer) { const [deps, { EmbeddableLogCategorization }] = await Promise.all([ this.getServices(), import('./log_categorization_embeddable'), From 8853668ebb1699e997884b0828344177987a2cdf Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Mon, 29 Apr 2024 18:31:20 +0100 Subject: [PATCH 40/92] comments --- .../pattern_analysis/pattern_analysis_table.tsx | 2 -- .../view_mode_toggle/view_mode_toggle.tsx | 1 + .../category_table/table_header.tsx | 2 -- .../embeddable_menu.tsx | 15 ++++++++------- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx index 8058e71f9dac3..ed7e7bee03bf9 100644 --- a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx +++ b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx @@ -77,7 +77,6 @@ export const PatternAnalysisTable = (props: PatternAnalysisTableProps) => { useEffect(() => { if (embeddable && !isErrorEmbeddable(embeddable)) { - // Update embeddable whenever one of the important input changes embeddable.updateInput({ dataView, savedSearch, @@ -106,7 +105,6 @@ export const PatternAnalysisTable = (props: PatternAnalysisTableProps) => { EmbeddablePatternAnalysisOutput >(EMBEDDABLE_PATTERN_ANALYSIS_TYPE); if (factory) { - // Initialize embeddable with information available at mount const initializedEmbeddable = await factory.create({ id: EMBEDDABLE_PATTERN_ANALYSIS_TYPE, dataView, diff --git a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx index 849e273dfe2ea..eb2db51edbb7b 100644 --- a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx +++ b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx @@ -49,6 +49,7 @@ export const DocumentViewModeToggle = ({ useEffect(() => { if (showPatternAnalysisTab === false && viewMode === VIEW_MODE.PATTERN_LEVEL) { + // switch to document view if no text fields are available setDiscoverViewMode(VIEW_MODE.DOCUMENT_LEVEL); } }, [showPatternAnalysisTab, viewMode, setDiscoverViewMode]); diff --git a/x-pack/plugins/aiops/public/components/log_categorization/category_table/table_header.tsx b/x-pack/plugins/aiops/public/components/log_categorization/category_table/table_header.tsx index 66968861465a3..35a6443c19d4a 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/category_table/table_header.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/category_table/table_header.tsx @@ -82,7 +82,6 @@ export const OpenInDiscoverButtons: FC<{ openInDiscover: OpenInDiscover; showTex iconSide="left" > {labels.multiSelect.in} - {/* {showText ? labels.multiSelect.in : null} */} @@ -96,7 +95,6 @@ export const OpenInDiscoverButtons: FC<{ openInDiscover: OpenInDiscover; showTex iconSide="left" > {labels.multiSelect.out} - {/* {showText ? labels.multiSelect.out : null} */}
diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx index cca4c417070dc..d1ed4c46862d6 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { EuiSuperSelect } from '@elastic/eui'; +import { EuiSuperSelect, useEuiPaddingSize } from '@elastic/eui'; import { EuiFormRow } from '@elastic/eui'; import { EuiButtonEmpty, @@ -37,6 +37,11 @@ interface Props { reload: () => void; } +const minimumTimeRangeOptions = Object.keys(MINIMUM_TIME_RANGE).map((value) => ({ + inputDisplay: value, + value: value as MinimumTimeRangeOption, +})); + export const EmbeddableMenu: FC = ({ randomSampler, fields, @@ -49,17 +54,13 @@ export const EmbeddableMenu: FC = ({ }) => { const [showMenu, setShowMenu] = useState(false); const togglePopover = () => setShowMenu(!showMenu); + const xs = useEuiPaddingSize('xs'); const fieldOptions = useMemo( () => fields.map((field) => ({ inputDisplay: field.name, value: field })), [fields] ); - const minimumTimeRangeOptions = Object.keys(MINIMUM_TIME_RANGE).map((value) => ({ - inputDisplay: value, - value: value as MinimumTimeRangeOption, - })); - const button = ( = ({ iconType="arrowDown" iconSide="right" onClick={() => togglePopover()} - css={{ marginRight: '4px' }} + css={{ marginRight: xs }} > Options From 0f76a357dbcb0a88d66be481529a60152ff41bbc Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 3 May 2024 13:38:24 +0100 Subject: [PATCH 41/92] removing embeddable --- src/plugins/discover/kibana.jsonc | 3 +- .../layout/discover_main_content.tsx | 63 ++--- .../pattern_analysis/pattern_analysis_tab.tsx | 55 ++-- .../pattern_analysis_table.tsx | 163 ++++-------- src/plugins/discover/public/build_services.ts | 3 + src/plugins/discover/public/plugin.tsx | 2 + .../aiops_log_pattern_analysis/embeddable.ts | 4 +- .../index.ts | 1 + .../log_categorization_for_embeddable.tsx | 236 +++++++++++------- .../log_categorization_wrapper.tsx | 85 +++++++ .../change_point_chart_initializer.tsx | 20 +- .../const.ts | 0 .../embeddable_change_point_chart.tsx | 2 +- ...mbeddable_change_point_chart_component.tsx | 4 +- .../embeddable_change_point_chart_factory.ts | 2 +- .../embeddable_chart_component_wrapper.tsx | 20 +- .../handle_explicit_input.tsx | 6 +- .../index.ts | 5 +- .../register_embeddable.ts} | 21 +- .../types.ts | 2 +- .../embeddables/log_categorization/index.ts | 8 - .../log_categorization_embeddable.tsx | 177 ------------- .../log_categorization_embeddable_factory.ts | 86 ------- x-pack/plugins/aiops/public/index.ts | 2 + x-pack/plugins/aiops/public/plugin.tsx | 16 +- x-pack/plugins/aiops/public/types.ts | 4 +- 26 files changed, 383 insertions(+), 607 deletions(-) create mode 100644 x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_wrapper.tsx rename x-pack/plugins/aiops/public/{embeddables/change_point_chart => embeddable}/change_point_chart_initializer.tsx (90%) rename x-pack/plugins/aiops/public/{embeddables/change_point_chart => embeddable}/const.ts (100%) rename x-pack/plugins/aiops/public/{embeddables/change_point_chart => embeddable}/embeddable_change_point_chart.tsx (98%) rename x-pack/plugins/aiops/public/{embeddables/change_point_chart => embeddable}/embeddable_change_point_chart_component.tsx (93%) rename x-pack/plugins/aiops/public/{embeddables/change_point_chart => embeddable}/embeddable_change_point_chart_factory.ts (99%) rename x-pack/plugins/aiops/public/{embeddables/change_point_chart => embeddable}/embeddable_chart_component_wrapper.tsx (89%) rename x-pack/plugins/aiops/public/{embeddables/change_point_chart => embeddable}/handle_explicit_input.tsx (90%) rename x-pack/plugins/aiops/public/{embeddables/change_point_chart => embeddable}/index.ts (72%) rename x-pack/plugins/aiops/public/{embeddables/register_embeddables.ts => embeddable/register_embeddable.ts} (56%) rename x-pack/plugins/aiops/public/{embeddables/change_point_chart => embeddable}/types.ts (89%) delete mode 100644 x-pack/plugins/aiops/public/embeddables/log_categorization/index.ts delete mode 100644 x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx delete mode 100644 x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable_factory.ts diff --git a/src/plugins/discover/kibana.jsonc b/src/plugins/discover/kibana.jsonc index 9b892ee79fe16..b8632760735d2 100644 --- a/src/plugins/discover/kibana.jsonc +++ b/src/plugins/discover/kibana.jsonc @@ -38,7 +38,8 @@ "lens", "noDataPage", "globalSearch", - "observabilityAIAssistant" + "observabilityAIAssistant", + "aiops" ], "requiredBundles": ["kibanaUtils", "kibanaReact", "unifiedSearch", "savedObjects"], "extraPublicDirs": ["common"] diff --git a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx index e994e144f8245..ee4ca2a1737ab 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx @@ -8,7 +8,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule } from '@elastic/eui'; import { type DropType, DropOverlayWrapper, Droppable } from '@kbn/dom-drag-drop'; -import React, { ReactElement, useCallback, useMemo } from 'react'; +import React, { ReactElement, useCallback } from 'react'; import { DataView } from '@kbn/data-views-plugin/common'; import { METRIC_TYPE } from '@kbn/analytics'; import { i18n } from '@kbn/i18n'; @@ -64,7 +64,7 @@ export const DiscoverMainContent = ({ isChartAvailable, }: DiscoverMainContentProps) => { const { trackUiMetric } = useDiscoverServices(); - const [patternCount, setPatternCount] = React.useState(undefined); + // const [patternCount, setPatternCount] = React.useState(undefined); const setDiscoverViewMode = useCallback( (mode: VIEW_MODE) => { @@ -85,32 +85,34 @@ export const DiscoverMainContent = ({ const isDropAllowed = Boolean(onDropFieldToTable); - const viewModeToggle = useMemo(() => { - return ( - - ); - }, [ - viewMode, - setDiscoverViewMode, - isPlainRecord, - stateContainer, - panelsToggle, - isChartAvailable, - patternCount, - dataView, - ]); + const viewModeToggle = useCallback( + (patternCount?: number) => { + return ( + + ); + }, + [ + viewMode, + setDiscoverViewMode, + isPlainRecord, + stateContainer, + panelsToggle, + isChartAvailable, + dataView, + ] + ); const showChart = useAppStateSelector((state) => !state.hideChart); @@ -132,7 +134,7 @@ export const DiscoverMainContent = ({ {showChart && isChartAvailable && } {viewMode === VIEW_MODE.DOCUMENT_LEVEL ? ( - {viewModeToggle} + {viewModeToggle()} setDiscoverViewMode(VIEW_MODE.DOCUMENT_LEVEL)} trackUiMetric={trackUiMetric} - setPatternCount={setPatternCount} viewModeToggle={viewModeToggle} /> diff --git a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_tab.tsx b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_tab.tsx index 693213c9ff740..b8056e8be28c4 100644 --- a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_tab.tsx +++ b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_tab.tsx @@ -6,39 +6,34 @@ * Side Public License, v 1. */ -import React, { useState, memo, type ReactElement, type FC } from 'react'; +import React, { memo, type FC } from 'react'; import { useQuerySubscriber } from '@kbn/unified-field-list/src/hooks/use_query_subscriber'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +// import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { useSavedSearch } from '../../services/discover_state_provider'; import { PatternAnalysisTable, type PatternAnalysisTableProps } from './pattern_analysis_table'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; -export const PatternAnalysisTab: FC< - Omit -> = memo((props) => { - const services = useDiscoverServices(); - const querySubscriberResult = useQuerySubscriber({ - data: services.data, - }); - const savedSearch = useSavedSearch(); - const [optionsMenu, setOptionsMenu] = useState(undefined); +export const PatternAnalysisTab: FC> = memo( + (props) => { + const services = useDiscoverServices(); + const querySubscriberResult = useQuerySubscriber({ + data: services.data, + }); + const savedSearch = useSavedSearch(); - return ( - <> - - - {props.viewModeToggle} - - {optionsMenu} - - - - - ); -}); + return ( + <> + + + ); + } +); diff --git a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx index ed7e7bee03bf9..64ee3da84e314 100644 --- a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx +++ b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx @@ -6,26 +6,13 @@ * Side Public License, v 1. */ -import React, { useEffect, useMemo, useRef, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { METRIC_TYPE, UiCounterMetricType } from '@kbn/analytics'; -import { - ErrorEmbeddable, - type IEmbeddable, - isErrorEmbeddable, -} from '@kbn/embeddable-plugin/public'; -import { - type EmbeddablePatternAnalysisInput, - type EmbeddablePatternAnalysisOutput, - type EmbeddablePatternAnalysisProps, - EMBEDDABLE_PATTERN_ANALYSIS_TYPE, -} from '@kbn/aiops-log-pattern-analysis/embeddable'; -import { EuiFlexItem } from '@elastic/eui'; -import { css } from '@emotion/react'; -import useObservable from 'react-use/lib/useObservable'; -import { of } from 'rxjs'; +import { type EmbeddablePatternAnalysisProps } from '@kbn/aiops-log-pattern-analysis/embeddable'; +import { pick } from 'lodash'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; -import { PATTERN_ANALYSIS_LOADED } from './constants'; import type { DiscoverStateContainer } from '../../services/discover_state'; +import { PATTERN_ANALYSIS_LOADED } from './constants'; export type PatternAnalysisTableProps = EmbeddablePatternAnalysisProps & { searchDescription?: string; @@ -35,128 +22,62 @@ export type PatternAnalysisTableProps = EmbeddablePatternAnalysisProps & { */ stateContainer?: DiscoverStateContainer; trackUiMetric?: (metricType: UiCounterMetricType, eventName: string | string[]) => void; - searchSessionId?: string; - viewModeToggle: React.ReactElement; - setOptionsMenu: (optionsMenu: React.ReactElement | undefined) => void; + viewModeToggle: (patternCount?: number) => React.ReactElement; }; export const PatternAnalysisTable = (props: PatternAnalysisTableProps) => { - const { - dataView, - savedSearch, - query, - filters, - stateContainer, - onAddFilter, - trackUiMetric, - searchSessionId, - } = props; - - const totalHits = useObservable(stateContainer?.dataState.data$.totalHits$ ?? of(undefined)); - const totalDocuments = useMemo(() => totalHits?.result, [totalHits]); + const [lastReloadRequestTime, setLastReloadRequestTime] = useState(undefined); const services = useDiscoverServices(); - const [embeddable, setEmbeddable] = useState< - | ErrorEmbeddable - | IEmbeddable - | undefined - >(); - const embeddableRoot: React.RefObject = useRef(null); + const aiopsService = services.aiops; + const { trackUiMetric, stateContainer } = props; useEffect(() => { const refetch = stateContainer?.dataState.refetch$.subscribe(() => { - if (embeddable && !isErrorEmbeddable(embeddable)) { - embeddable.updateInput({ lastReloadRequestTime: Date.now() }); - } + setLastReloadRequestTime(Date.now()); }); return () => { refetch?.unsubscribe(); }; - }, [embeddable, stateContainer]); + }, [stateContainer]); useEffect(() => { - if (embeddable && !isErrorEmbeddable(embeddable)) { - embeddable.updateInput({ - dataView, - savedSearch, - query, - filters, - }); - embeddable.reload(); - } - }, [ - embeddable, - dataView, - savedSearch, - query, - filters, - searchSessionId, - totalDocuments, - stateContainer, + // Track should only be called once when component is loaded + trackUiMetric?.(METRIC_TYPE.LOADED, PATTERN_ANALYSIS_LOADED); + }, [trackUiMetric]); + + if (aiopsService === undefined) { + return null; + } + + const deps = pick(services, [ + 'i18n', + 'theme', + 'data', + 'uiSettings', + 'http', + 'notifications', + 'lens', + 'fieldFormats', + 'application', + 'charts', ]); - useEffect(() => { - let unmounted = false; - const loadEmbeddable = async () => { - if (services.embeddable) { - const factory = services.embeddable.getEmbeddableFactory< - EmbeddablePatternAnalysisInput, - EmbeddablePatternAnalysisOutput - >(EMBEDDABLE_PATTERN_ANALYSIS_TYPE); - if (factory) { - const initializedEmbeddable = await factory.create({ - id: EMBEDDABLE_PATTERN_ANALYSIS_TYPE, - dataView, - savedSearch, - query, - onAddFilter, - setPatternCount: props.setPatternCount, - setOptionsMenu: props.setOptionsMenu, - }); - if (!unmounted) { - setEmbeddable(initializedEmbeddable); - } - } - } - }; - loadEmbeddable(); - return () => { - unmounted = true; - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [services.embeddable]); - - // We can only render after embeddable has already initialized - useEffect(() => { - if (embeddableRoot.current && embeddable) { - embeddable.render(embeddableRoot.current); - - trackUiMetric?.(METRIC_TYPE.LOADED, PATTERN_ANALYSIS_LOADED); - } - - return () => { - // Clean up embeddable upon unmounting - embeddable?.destroy(); - }; - }, [embeddable, embeddableRoot, trackUiMetric]); - - const style = css` - overflow-y: auto; - - .kbnDocTableWrapper { - overflow-x: hidden; - } - `; + const input = pick(props, ['dataView', 'savedSearch', 'query', 'filters', 'onAddFilter']); return ( - -
- + {}, + embeddingOrigin: 'discover-embedded', + }} + deps={deps} + /> ); }; diff --git a/src/plugins/discover/public/build_services.ts b/src/plugins/discover/public/build_services.ts index 7f7bcdee2154b..5bf6dfc8c9e44 100644 --- a/src/plugins/discover/public/build_services.ts +++ b/src/plugins/discover/public/build_services.ts @@ -57,6 +57,7 @@ import type { ContentClient } from '@kbn/content-management-plugin/public'; import type { ObservabilityAIAssistantPublicStart } from '@kbn/observability-ai-assistant-plugin/public'; import { memoize, noop } from 'lodash'; import type { NoDataPagePluginStart } from '@kbn/no-data-page-plugin/public'; +import { AiopsPluginStart } from '@kbn/aiops-plugin/public'; import { DiscoverStartPlugins } from './plugin'; import { DiscoverContextAppLocator } from './application/context/services/locator'; import { DiscoverSingleDocLocator } from './application/doc/locator'; @@ -76,6 +77,7 @@ export interface UrlTracker { } export interface DiscoverServices { + aiops?: AiopsPluginStart; application: ApplicationStart; addBasePath: (path: string) => string; analytics: AnalyticsServiceStart; @@ -155,6 +157,7 @@ export const buildServices = memoize( const storage = new Storage(localStorage); return { + aiops: plugins.aiops, application: core.application, addBasePath: core.http.basePath.prepend, analytics: core.analytics, diff --git a/src/plugins/discover/public/plugin.tsx b/src/plugins/discover/public/plugin.tsx index 3563697138cc4..a59b3de4eca3d 100644 --- a/src/plugins/discover/public/plugin.tsx +++ b/src/plugins/discover/public/plugin.tsx @@ -51,6 +51,7 @@ import type { ObservabilityAIAssistantPublicSetup, ObservabilityAIAssistantPublicStart, } from '@kbn/observability-ai-assistant-plugin/public'; +import { AiopsPluginStart } from '@kbn/aiops-plugin/public'; import { PLUGIN_ID } from '../common'; import { registerFeature } from './register_feature'; import { buildServices, UrlTracker } from './build_services'; @@ -178,6 +179,7 @@ export interface DiscoverSetupPlugins { * @internal */ export interface DiscoverStartPlugins { + aiops?: AiopsPluginStart; dataViews: DataViewsServicePublic; dataViewEditor: DataViewEditorStart; uiActions: UiActionsStart; diff --git a/x-pack/packages/ml/aiops_log_pattern_analysis/embeddable.ts b/x-pack/packages/ml/aiops_log_pattern_analysis/embeddable.ts index 04ceed7548cca..85912b1836c90 100644 --- a/x-pack/packages/ml/aiops_log_pattern_analysis/embeddable.ts +++ b/x-pack/packages/ml/aiops_log_pattern_analysis/embeddable.ts @@ -15,14 +15,12 @@ export interface EmbeddablePatternAnalysisProps { savedSearch?: SavedSearch | null; query?: T; filters?: Filter[]; - id?: string; embeddingOrigin?: string; /** * Callback to add a filter to filter bar */ onAddFilter?: () => void; - setPatternCount: (patternCount: number | undefined) => void; - setOptionsMenu: (optionsMenu: React.ReactElement | undefined) => void; + lastReloadRequestTime?: number; } export type EmbeddablePatternAnalysisInput = EmbeddableInput & EmbeddablePatternAnalysisProps; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/index.ts b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/index.ts index 224a4a35e6802..8cc8fcbad313a 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/index.ts +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/index.ts @@ -6,3 +6,4 @@ */ export { LogCategorizationEmbeddable } from './log_categorization_for_embeddable'; +export { LogCategorizationWrapper } from './log_categorization_wrapper'; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx index 5173066cc6afd..dd8a6bc139569 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx @@ -20,7 +20,8 @@ import type { Category } from '@kbn/aiops-log-pattern-analysis/types'; import type { CategorizationAdditionalFilter } from '@kbn/aiops-log-pattern-analysis/create_category_request'; import { AIOPS_TELEMETRY_ID } from '@kbn/aiops-common/constants'; -import type { EmbeddablePatternAnalysisInput } from '@kbn/aiops-log-pattern-analysis/embeddable'; +import type { EmbeddablePatternAnalysisProps } from '@kbn/aiops-log-pattern-analysis/embeddable'; +import { css } from '@emotion/react'; import { type LogCategorizationPageUrlState, getDefaultLogCategorizationAppState, @@ -37,17 +38,18 @@ import { InformationText } from '../information_text'; import { LoadingCategorization } from '../loading_categorization'; import { useValidateFieldRequest } from '../use_validate_category_field'; import { FieldValidationCallout } from '../category_validation_callout'; -import { EmbeddableMenu } from './embeddable_menu'; import { useMinimumTimeRange } from './use_minimum_time_range'; import { createAdditionalConfigHash, createDocumentStatsHash, getMessageField } from '../utils'; import { useOpenInDiscover } from '../category_table/use_open_in_discover'; import { OpenInDiscoverButtons } from '../category_table/table_header'; +import { EmbeddableMenu } from './embeddable_menu'; export interface LogCategorizationPageProps { onClose: () => void; embeddingOrigin: string; - input: Readonly; + input: Readonly; + viewModeToggle: (patternCount?: number) => React.ReactElement; } const BAR_TARGET = 20; @@ -56,6 +58,7 @@ export const LogCategorizationEmbeddable: FC = ({ onClose, embeddingOrigin, input, + viewModeToggle, }) => { const { notifications: { toasts }, @@ -64,7 +67,7 @@ export const LogCategorizationEmbeddable: FC = ({ }, uiSettings, } = useAiopsAppContext(); - const { dataView, savedSearch, setPatternCount, setOptionsMenu } = input; + const { dataView, savedSearch } = input; const { runValidateFieldRequest, cancelRequest: cancelValidationRequest } = useValidateFieldRequest(); @@ -136,10 +139,9 @@ export const LogCategorizationEmbeddable: FC = ({ return () => { mounted.current = false; cancelRequest(); - setPatternCount(undefined); }; }, - [cancelRequest, mounted, setPatternCount] + [cancelRequest, mounted] ); const { searchQuery } = useSearch( @@ -336,56 +338,56 @@ export const LogCategorizationEmbeddable: FC = ({ toasts, ]); - useEffect( - function initOptionsMenu() { - setPatternCount(data?.categories.length); - setOptionsMenu( - <> - {randomSampler !== undefined ? ( - <> - - - {selectedCategories.length > 0 ? ( - - - - ) : null} - - loadCategories()} - fields={fields} - setSelectedField={setSelectedField} - selectedField={selectedField} - minimumTimeRangeOption={minimumTimeRangeOption} - setMinimumTimeRangeOption={setMinimumTimeRangeOption} - categoryCount={data?.totalCategories} - /> - - - - ) : null} - - ); - return () => { - setPatternCount(undefined); - setOptionsMenu(undefined); - }; - }, - [ - data, - fields, - loadCategories, - randomSampler, - selectedField, - setOptionsMenu, - setPatternCount, - setMinimumTimeRangeOption, - minimumTimeRangeOption, - selectedCategories.length, - openInDiscover, - ] - ); + // useEffect( + // function initOptionsMenu() { + // setPatternCount(data?.categories.length); + // setOptionsMenu( + // <> + // {randomSampler !== undefined ? ( + // <> + // + // + // {selectedCategories.length > 0 ? ( + // + // + // + // ) : null} + // + // loadCategories()} + // fields={fields} + // setSelectedField={setSelectedField} + // selectedField={selectedField} + // minimumTimeRangeOption={minimumTimeRangeOption} + // setMinimumTimeRangeOption={setMinimumTimeRangeOption} + // categoryCount={data?.totalCategories} + // /> + // + // + // + // ) : null} + // + // ); + // return () => { + // setPatternCount(undefined); + // setOptionsMenu(undefined); + // }; + // }, + // [ + // data, + // fields, + // loadCategories, + // randomSampler, + // selectedField, + // setOptionsMenu, + // setPatternCount, + // setMinimumTimeRangeOption, + // minimumTimeRangeOption, + // selectedCategories.length, + // openInDiscover, + // ] + // ); useEffect( function triggerAnalysis() { @@ -431,54 +433,102 @@ export const LogCategorizationEmbeddable: FC = ({ useEffect( function refreshTriggeredFromButton() { - if (input?.lastReloadRequestTime !== undefined) { + if (input.lastReloadRequestTime !== undefined) { setPreviousDocumentStatsHash(0); setPreviousAdditionalConfigsHash(null); forceRefresh(); } }, // eslint-disable-next-line react-hooks/exhaustive-deps - [input?.lastReloadRequestTime] + [input.lastReloadRequestTime] ); + const style = css({ + overflowY: 'auto', + '.kbnDocTableWrapper': { + overflowX: 'hidden', + }, + }); return ( <> - - - <> - - {(loading ?? true) === true ? : null} - - {loading === false && - data !== null && - data.categories.length > 0 && - selectedField !== null ? ( - + + {viewModeToggle(data?.categories.length)} + + + <> + {randomSampler !== undefined ? ( + <> + + + {selectedCategories.length > 0 ? ( + + + + ) : null} + + loadCategories()} + fields={fields} + setSelectedField={setSelectedField} + selectedField={selectedField} + minimumTimeRangeOption={minimumTimeRangeOption} + setMinimumTimeRangeOption={setMinimumTimeRangeOption} + categoryCount={data?.totalCategories} + /> + + + + ) : null} + + + + + + + + + <> + + {(loading ?? true) === true ? ( + + ) : null} + - ) : null} - - - + {loading === false && + data !== null && + data.categories.length > 0 && + selectedField !== null ? ( + + ) : null} + + + + ); }; + +// eslint-disable-next-line import/no-default-export +export default LogCategorizationEmbeddable; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_wrapper.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_wrapper.tsx new file mode 100644 index 0000000000000..eaba76a6ede48 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_wrapper.tsx @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FC } from 'react'; +import React, { Suspense } from 'react'; +// import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; +import type { ThemeServiceStart } from '@kbn/core-theme-browser'; +import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import type { IUiSettingsClient } from '@kbn/core/public'; +import type { CoreStart } from '@kbn/core/public'; +import type { LensPublicStart } from '@kbn/lens-plugin/public'; +import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; +import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; +import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; +import { DatePickerContextProvider } from '@kbn/ml-date-picker'; +import { StorageContextProvider } from '@kbn/ml-local-storage'; + +import { pick } from 'lodash'; +import { UI_SETTINGS } from '@kbn/data-plugin/public'; +import { Storage } from '@kbn/kibana-utils-plugin/public'; +import { AIOPS_STORAGE_KEYS } from '../../../types/storage'; +import type { AiopsAppDependencies } from '../../../hooks/use_aiops_app_context'; +import { AiopsAppContext } from '../../../hooks/use_aiops_app_context'; +import type { LogCategorizationPageProps } from './log_categorization_for_embeddable'; +import { LogCategorizationEmbeddable } from './log_categorization_for_embeddable'; + +export interface EmbeddableLogCategorizationDeps { + theme: ThemeServiceStart; + data: DataPublicPluginStart; + uiSettings: IUiSettingsClient; + http: CoreStart['http']; + notifications: CoreStart['notifications']; + i18n: CoreStart['i18n']; + lens: LensPublicStart; + usageCollection?: UsageCollectionSetup; // is this needed?!!!!!!!!!!!!!!!!!!!!!!!!!! + fieldFormats: FieldFormatsStart; + application: CoreStart['application']; + charts: ChartsPluginStart; +} + +export interface LogCategorizationEmbeddableProps { + deps: EmbeddableLogCategorizationDeps; + props: LogCategorizationPageProps; +} + +const localStorage = new Storage(window.localStorage); + +export const LogCategorizationWrapper: FC = ({ deps, props }) => { + const I18nContext = deps.i18n.Context; + const aiopsAppContextValue = { + embeddingOrigin: props.input.embeddingOrigin, + ...deps, + } as unknown as AiopsAppDependencies; + + const datePickerDeps = { + ...pick(deps, ['data', 'http', 'notifications', 'theme', 'uiSettings', 'i18n']), + uiSettingsKeys: UI_SETTINGS, + }; + + return ( + + + + + + + + + + + + ); +}; + +// eslint-disable-next-line import/no-default-export +export default LogCategorizationWrapper; diff --git a/x-pack/plugins/aiops/public/embeddables/change_point_chart/change_point_chart_initializer.tsx b/x-pack/plugins/aiops/public/embeddable/change_point_chart_initializer.tsx similarity index 90% rename from x-pack/plugins/aiops/public/embeddables/change_point_chart/change_point_chart_initializer.tsx rename to x-pack/plugins/aiops/public/embeddable/change_point_chart_initializer.tsx index c19328de98b34..bbcf42a351786 100644 --- a/x-pack/plugins/aiops/public/embeddables/change_point_chart/change_point_chart_initializer.tsx +++ b/x-pack/plugins/aiops/public/embeddable/change_point_chart_initializer.tsx @@ -28,16 +28,16 @@ import usePrevious from 'react-use/lib/usePrevious'; import { ChangePointDetectionControlsContextProvider, useChangePointDetectionControlsContext, -} from '../../components/change_point_detection/change_point_detection_context'; -import { DEFAULT_AGG_FUNCTION } from '../../components/change_point_detection/constants'; -import { FunctionPicker } from '../../components/change_point_detection/function_picker'; -import { MaxSeriesControl } from '../../components/change_point_detection/max_series_control'; -import { MetricFieldSelector } from '../../components/change_point_detection/metric_field_selector'; -import { PartitionsSelector } from '../../components/change_point_detection/partitions_selector'; -import { SplitFieldSelector } from '../../components/change_point_detection/split_field_selector'; -import { ViewTypeSelector } from '../../components/change_point_detection/view_type_selector'; -import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; -import { DataSourceContextProvider } from '../../hooks/use_data_source'; +} from '../components/change_point_detection/change_point_detection_context'; +import { DEFAULT_AGG_FUNCTION } from '../components/change_point_detection/constants'; +import { FunctionPicker } from '../components/change_point_detection/function_picker'; +import { MaxSeriesControl } from '../components/change_point_detection/max_series_control'; +import { MetricFieldSelector } from '../components/change_point_detection/metric_field_selector'; +import { PartitionsSelector } from '../components/change_point_detection/partitions_selector'; +import { SplitFieldSelector } from '../components/change_point_detection/split_field_selector'; +import { ViewTypeSelector } from '../components/change_point_detection/view_type_selector'; +import { useAiopsAppContext } from '../hooks/use_aiops_app_context'; +import { DataSourceContextProvider } from '../hooks/use_data_source'; import { DEFAULT_SERIES } from './const'; import type { EmbeddableChangePointChartInput } from './embeddable_change_point_chart'; import type { EmbeddableChangePointChartProps } from './embeddable_change_point_chart_component'; diff --git a/x-pack/plugins/aiops/public/embeddables/change_point_chart/const.ts b/x-pack/plugins/aiops/public/embeddable/const.ts similarity index 100% rename from x-pack/plugins/aiops/public/embeddables/change_point_chart/const.ts rename to x-pack/plugins/aiops/public/embeddable/const.ts diff --git a/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart.tsx b/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart.tsx similarity index 98% rename from x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart.tsx rename to x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart.tsx index 2e62f3a1005c6..c76eae5c918d0 100644 --- a/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart.tsx +++ b/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart.tsx @@ -24,7 +24,7 @@ import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { EMBEDDABLE_ORIGIN } from '@kbn/aiops-common/constants'; import type { EmbeddableChangePointType } from '@kbn/aiops-change-point-detection/constants'; import { EmbeddableInputTracker } from './embeddable_chart_component_wrapper'; -import { AiopsAppContext, type AiopsAppDependencies } from '../../hooks/use_aiops_app_context'; +import { AiopsAppContext, type AiopsAppDependencies } from '../hooks/use_aiops_app_context'; import type { EmbeddableChangePointChartProps } from './embeddable_change_point_chart_component'; diff --git a/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_component.tsx b/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart_component.tsx similarity index 93% rename from x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_component.tsx rename to x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart_component.tsx index 64b429492276b..493a122b08cf0 100644 --- a/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_component.tsx +++ b/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart_component.tsx @@ -15,9 +15,9 @@ import type { ChangePointDetectionViewType, EmbeddableChangePointType, } from '@kbn/aiops-change-point-detection/constants'; -import type { AiopsPluginStartDeps } from '../../types'; +import type { AiopsPluginStartDeps } from '../types'; import type { EmbeddableChangePointChartInput } from './embeddable_change_point_chart'; -import type { ChangePointAnnotation } from '../../components/change_point_detection/change_point_detection_context'; +import type { ChangePointAnnotation } from '../components/change_point_detection/change_point_detection_context'; export interface EmbeddableChangePointChartProps { viewType?: ChangePointDetectionViewType; diff --git a/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_factory.ts b/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart_factory.ts similarity index 99% rename from x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_factory.ts rename to x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart_factory.ts index 99e7babcd7626..318cc88fed6fd 100644 --- a/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_factory.ts +++ b/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart_factory.ts @@ -14,9 +14,9 @@ import type { EMBEDDABLE_CHANGE_POINT_CHART_TYPE, EmbeddableChangePointType, } from '@kbn/aiops-change-point-detection/constants'; +import type { AiopsPluginStart, AiopsPluginStartDeps } from '../types'; import type { EmbeddableChangePointChartInput } from './embeddable_change_point_chart'; import { EmbeddableChangePointChart } from './embeddable_change_point_chart'; -import type { AiopsPluginStart, AiopsPluginStartDeps } from '../../types'; export interface EmbeddableChangePointChartStartServices { data: DataPublicPluginStart; diff --git a/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_chart_component_wrapper.tsx b/x-pack/plugins/aiops/public/embeddable/embeddable_chart_component_wrapper.tsx similarity index 89% rename from x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_chart_component_wrapper.tsx rename to x-pack/plugins/aiops/public/embeddable/embeddable_chart_component_wrapper.tsx index 3de0a4641b183..239d86f145a53 100644 --- a/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_chart_component_wrapper.tsx +++ b/x-pack/plugins/aiops/public/embeddable/embeddable_chart_component_wrapper.tsx @@ -12,25 +12,25 @@ import React, { useEffect, useMemo, useState } from 'react'; import { css } from '@emotion/react'; import useObservable from 'react-use/lib/useObservable'; import { CHANGE_POINT_DETECTION_VIEW_TYPE } from '@kbn/aiops-change-point-detection/constants'; -import { ChangePointsTable } from '../../components/change_point_detection/change_points_table'; -import { ReloadContextProvider } from '../../hooks/use_reload'; +import { ChangePointsTable } from '../components/change_point_detection/change_points_table'; +import { ReloadContextProvider } from '../hooks/use_reload'; import { type ChangePointAnnotation, ChangePointDetectionControlsContextProvider, type ChangePointDetectionRequestParams, -} from '../../components/change_point_detection/change_point_detection_context'; +} from '../components/change_point_detection/change_point_detection_context'; import type { EmbeddableChangePointChartInput, EmbeddableChangePointChartOutput, } from './embeddable_change_point_chart'; import type { EmbeddableChangePointChartProps } from './embeddable_change_point_chart_component'; -import { FilterQueryContextProvider, useFilterQueryUpdates } from '../../hooks/use_filters_query'; -import { DataSourceContextProvider, useDataSource } from '../../hooks/use_data_source'; -import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; -import { createMergedEsQuery } from '../../application/utils/search_utils'; -import { useChangePointResults } from '../../components/change_point_detection/use_change_point_agg_request'; -import { ChartsGrid } from '../../components/change_point_detection/charts_grid'; -import { NoChangePointsWarning } from '../../components/change_point_detection/no_change_points_warning'; +import { FilterQueryContextProvider, useFilterQueryUpdates } from '../hooks/use_filters_query'; +import { DataSourceContextProvider, useDataSource } from '../hooks/use_data_source'; +import { useAiopsAppContext } from '../hooks/use_aiops_app_context'; +import { createMergedEsQuery } from '../application/utils/search_utils'; +import { useChangePointResults } from '../components/change_point_detection/use_change_point_agg_request'; +import { ChartsGrid } from '../components/change_point_detection/charts_grid'; +import { NoChangePointsWarning } from '../components/change_point_detection/no_change_points_warning'; const defaultSort = { field: 'p_value' as keyof ChangePointAnnotation, diff --git a/x-pack/plugins/aiops/public/embeddables/change_point_chart/handle_explicit_input.tsx b/x-pack/plugins/aiops/public/embeddable/handle_explicit_input.tsx similarity index 90% rename from x-pack/plugins/aiops/public/embeddables/change_point_chart/handle_explicit_input.tsx rename to x-pack/plugins/aiops/public/embeddable/handle_explicit_input.tsx index de1861ad487da..35a8fb60b685e 100644 --- a/x-pack/plugins/aiops/public/embeddables/change_point_chart/handle_explicit_input.tsx +++ b/x-pack/plugins/aiops/public/embeddable/handle_explicit_input.tsx @@ -9,9 +9,9 @@ import type { CoreStart } from '@kbn/core/public'; import { toMountPoint } from '@kbn/react-kibana-mount'; import React from 'react'; import type { EmbeddableChangePointChartExplicitInput } from './types'; -import type { AiopsAppDependencies } from '../..'; -import { AiopsAppContext } from '../../hooks/use_aiops_app_context'; -import type { AiopsPluginStartDeps } from '../../types'; +import type { AiopsAppDependencies } from '..'; +import { AiopsAppContext } from '../hooks/use_aiops_app_context'; +import type { AiopsPluginStartDeps } from '../types'; import { ChangePointChartInitializer } from './change_point_chart_initializer'; import type { EmbeddableChangePointChartInput } from './embeddable_change_point_chart'; diff --git a/x-pack/plugins/aiops/public/embeddables/change_point_chart/index.ts b/x-pack/plugins/aiops/public/embeddable/index.ts similarity index 72% rename from x-pack/plugins/aiops/public/embeddables/change_point_chart/index.ts rename to x-pack/plugins/aiops/public/embeddable/index.ts index 53f38418f9fe4..d742a1321c101 100644 --- a/x-pack/plugins/aiops/public/embeddables/change_point_chart/index.ts +++ b/x-pack/plugins/aiops/public/embeddable/index.ts @@ -6,7 +6,4 @@ */ export { EmbeddableChangePointChartFactory } from './embeddable_change_point_chart_factory'; -export { - type EmbeddableChangePointChartProps, - getEmbeddableChangePointChart, -} from './embeddable_change_point_chart_component'; +export { type EmbeddableChangePointChartProps } from './embeddable_change_point_chart_component'; diff --git a/x-pack/plugins/aiops/public/embeddables/register_embeddables.ts b/x-pack/plugins/aiops/public/embeddable/register_embeddable.ts similarity index 56% rename from x-pack/plugins/aiops/public/embeddables/register_embeddables.ts rename to x-pack/plugins/aiops/public/embeddable/register_embeddable.ts index 54ecb3dac8d1a..e660837ebfbf4 100644 --- a/x-pack/plugins/aiops/public/embeddables/register_embeddables.ts +++ b/x-pack/plugins/aiops/public/embeddable/register_embeddable.ts @@ -10,10 +10,9 @@ import type { EmbeddableSetup } from '@kbn/embeddable-plugin/public'; import { i18n } from '@kbn/i18n'; import { EMBEDDABLE_CHANGE_POINT_CHART_TYPE } from '@kbn/aiops-change-point-detection/constants'; import type { AiopsPluginStart, AiopsPluginStartDeps } from '../types'; -import { EmbeddableChangePointChartFactory } from './change_point_chart/embeddable_change_point_chart_factory'; -import { EmbeddableLogCategorizationFactory } from './log_categorization/log_categorization_embeddable_factory'; +import { EmbeddableChangePointChartFactory } from './embeddable_change_point_chart_factory'; -export const registerChangePointChartEmbeddable = ( +export const registerEmbeddable = ( core: CoreSetup, embeddable: EmbeddableSetup ) => { @@ -26,19 +25,3 @@ export const registerChangePointChartEmbeddable = ( ); embeddable.registerEmbeddableFactory(changePointChartFactory.type, changePointChartFactory); }; - -export const registerLogCategorizationEmbeddable = ( - core: CoreSetup, - embeddable: EmbeddableSetup -) => { - const embeddableLogCategorizationFactory = new EmbeddableLogCategorizationFactory( - i18n.translate('xpack.aiops.embeddableLogCategorizationDisplayName', { - defaultMessage: 'Pattern analysis', - }), - core.getStartServices - ); - embeddable.registerEmbeddableFactory( - embeddableLogCategorizationFactory.type, - embeddableLogCategorizationFactory - ); -}; diff --git a/x-pack/plugins/aiops/public/embeddables/change_point_chart/types.ts b/x-pack/plugins/aiops/public/embeddable/types.ts similarity index 89% rename from x-pack/plugins/aiops/public/embeddables/change_point_chart/types.ts rename to x-pack/plugins/aiops/public/embeddable/types.ts index a13eb415cd614..783bffa7ca9a8 100644 --- a/x-pack/plugins/aiops/public/embeddables/change_point_chart/types.ts +++ b/x-pack/plugins/aiops/public/embeddable/types.ts @@ -7,7 +7,7 @@ import type { FC } from 'react'; import type { IEmbeddable } from '@kbn/embeddable-plugin/public'; -import type { SelectedChangePoint } from '../../components/change_point_detection/change_point_detection_context'; +import type { SelectedChangePoint } from '../components/change_point_detection/change_point_detection_context'; import type { EmbeddableChangePointChartInput, EmbeddableChangePointChartOutput, diff --git a/x-pack/plugins/aiops/public/embeddables/log_categorization/index.ts b/x-pack/plugins/aiops/public/embeddables/log_categorization/index.ts deleted file mode 100644 index bddf0723be84b..0000000000000 --- a/x-pack/plugins/aiops/public/embeddables/log_categorization/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { EmbeddableLogCategorizationFactory } from './log_categorization_embeddable_factory'; diff --git a/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx b/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx deleted file mode 100644 index 31050ebcbf497..0000000000000 --- a/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable.tsx +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { Suspense } from 'react'; -import ReactDOM from 'react-dom'; -import type { IContainer } from '@kbn/embeddable-plugin/public'; -import { Embeddable as AbstractEmbeddable } from '@kbn/embeddable-plugin/public'; -import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; -import type { ThemeServiceStart } from '@kbn/core-theme-browser'; -import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import { UI_SETTINGS } from '@kbn/data-plugin/public'; -import type { IUiSettingsClient } from '@kbn/core/public'; -import { type CoreStart } from '@kbn/core/public'; -import { DatePickerContextProvider } from '@kbn/ml-date-picker'; -import { pick } from 'lodash'; -import type { LensPublicStart } from '@kbn/lens-plugin/public'; -import { Subject } from 'rxjs'; -import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; -import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; -import { StorageContextProvider } from '@kbn/ml-local-storage'; -import { Storage } from '@kbn/kibana-utils-plugin/public'; -import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; - -import type { EmbeddablePatternAnalysisType } from '@kbn/aiops-log-pattern-analysis/embeddable'; - -import { EMBEDDABLE_ORIGIN } from '@kbn/aiops-common/constants'; -import type { - EmbeddablePatternAnalysisInput, - EmbeddablePatternAnalysisOutput, -} from '@kbn/aiops-log-pattern-analysis/embeddable'; -import useObservable from 'react-use/lib/useObservable'; -import { AiopsAppContext, type AiopsAppDependencies } from '../../hooks/use_aiops_app_context'; -import { AIOPS_STORAGE_KEYS } from '../../types/storage'; -import { LogCategorizationEmbeddable } from '../../components/log_categorization/log_categorization_for_embeddable'; - -const localStorage = new Storage(window.localStorage); - -export interface EmbeddableLogCategorizationDeps { - theme: ThemeServiceStart; - data: DataPublicPluginStart; - uiSettings: IUiSettingsClient; - http: CoreStart['http']; - notifications: CoreStart['notifications']; - i18n: CoreStart['i18n']; - lens: LensPublicStart; - usageCollection: UsageCollectionSetup; - fieldFormats: FieldFormatsStart; - application: CoreStart['application']; - charts: ChartsPluginStart; -} - -export type IEmbeddableLogCategorization = typeof EmbeddableLogCategorization; - -const LogCategorizationEmbeddableWrapper = (props: { - embeddableContext: InstanceType; -}) => { - const { embeddableContext } = props; - const input$ = embeddableContext.getInput$(); - const input = useObservable(input$); - if (input === undefined) { - return null; - } - - return ( - embeddableContext.destroy()} - embeddingOrigin={'discover-embedded'} - input={input} - /> - ); -}; - -export class EmbeddableLogCategorization extends AbstractEmbeddable< - EmbeddablePatternAnalysisInput, - EmbeddablePatternAnalysisOutput -> { - private reload$ = new Subject(); - - public reload(): void { - this.reload$.next(Date.now()); - } - - private node?: HTMLElement; - - // Need to defer embeddable load in order to resolve data views - deferEmbeddableLoad = true; - - constructor( - public readonly type: EmbeddablePatternAnalysisType, - private readonly deps: EmbeddableLogCategorizationDeps, - initialInput: EmbeddablePatternAnalysisInput, - parent?: IContainer - ) { - super(initialInput, {}, parent); - - this.initOutput().finally(() => this.setInitializationFinished()); - } - - private async initOutput() { - const { dataView } = this.getInput(); - this.updateOutput({ - indexPatterns: [dataView], - }); - } - - public reportsEmbeddableLoad() { - return true; - } - - public onLoading(isLoading: boolean) { - this.renderComplete.dispatchInProgress(); - this.updateOutput({ loading: isLoading, error: undefined }); - } - - public onError(error: Error) { - this.renderComplete.dispatchError(); - this.updateOutput({ loading: false, error: { name: error.name, message: error.message } }); - } - - public onRenderComplete() { - this.renderComplete.dispatchComplete(); - this.updateOutput({ loading: false, rendered: true, error: undefined }); - } - - render(el: HTMLElement): void { - super.render(el); - - this.node = el; - // required for the export feature to work - this.node.setAttribute('data-shared-item', ''); - - // test subject selector for functional tests - this.node.setAttribute('data-test-subj', 'aiopsEmbeddableLogCategorization'); - - const I18nContext = this.deps.i18n.Context; - - const datePickerDeps = { - ...pick(this.deps, ['data', 'http', 'notifications', 'theme', 'uiSettings', 'i18n']), - uiSettingsKeys: UI_SETTINGS, - }; - - const input = this.getInput(); - - const aiopsAppContextValue = { - embeddingOrigin: this.parent?.type ?? input.embeddingOrigin ?? EMBEDDABLE_ORIGIN, - ...this.deps, - } as unknown as AiopsAppDependencies; - - ReactDOM.render( - - - - - - - - - - - - - , - el - ); - } - - public destroy() { - super.destroy(); - if (this.node) { - ReactDOM.unmountComponentAtNode(this.node); - } - } -} diff --git a/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable_factory.ts b/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable_factory.ts deleted file mode 100644 index 895e71596ca00..0000000000000 --- a/x-pack/plugins/aiops/public/embeddables/log_categorization/log_categorization_embeddable_factory.ts +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import type { StartServicesAccessor } from '@kbn/core/public'; -import type { EmbeddableFactoryDefinition, IContainer } from '@kbn/embeddable-plugin/public'; - -import { EMBEDDABLE_PATTERN_ANALYSIS_TYPE } from '@kbn/aiops-log-pattern-analysis/embeddable'; -import type { EmbeddablePatternAnalysisInput } from '@kbn/aiops-log-pattern-analysis/embeddable'; -import type { AiopsPluginStart, AiopsPluginStartDeps } from '../../types'; -import type { EmbeddableLogCategorizationDeps } from './log_categorization_embeddable'; - -export class EmbeddableLogCategorizationFactory - implements EmbeddableFactoryDefinition -{ - public readonly type = EMBEDDABLE_PATTERN_ANALYSIS_TYPE; - - public readonly grouping = [ - { - id: 'embeddable_log_categorization', - getDisplayName: () => 'Pattern Analysis', - }, - ]; - - constructor( - private readonly name: string, - private readonly getStartServices: StartServicesAccessor - ) {} - - public getName() { - return this.name; - } - - public async isEditable() { - return false; - } - - public canCreateNew() { - return false; - } - - public getDisplayName() { - return i18n.translate('xpack.aiops.embeddableLogCategorization.displayName', { - defaultMessage: 'Pattern analysis', - }); - } - - public getDescription() { - return i18n.translate('xpack.aiops.embeddableLogCategorization.description', { - defaultMessage: 'Pattern analysis', - }); - } - - private async getServices(): Promise { - const [ - { i18n: i18nService, theme, http, uiSettings, notifications, application }, - { lens, data, usageCollection, fieldFormats, charts }, - ] = await this.getStartServices(); - - return { - i18n: i18nService, - theme, - data, - uiSettings, - http, - notifications, - lens, - usageCollection, - fieldFormats, - application, - charts, - }; - } - - public async create(input: EmbeddablePatternAnalysisInput, parent?: IContainer) { - const [deps, { EmbeddableLogCategorization }] = await Promise.all([ - this.getServices(), - import('./log_categorization_embeddable'), - ]); - return new EmbeddableLogCategorization(this.type, deps, input, parent); - } -} diff --git a/x-pack/plugins/aiops/public/index.ts b/x-pack/plugins/aiops/public/index.ts index 3e6ea42afe55c..6b34bd1d77c91 100755 --- a/x-pack/plugins/aiops/public/index.ts +++ b/x-pack/plugins/aiops/public/index.ts @@ -13,6 +13,8 @@ export function plugin() { return new AiopsPlugin(); } +export type { AiopsPluginStart, AiopsPluginSetup } from './types'; + export type { AiopsAppDependencies } from './hooks/use_aiops_app_context'; export type { LogRateAnalysisAppStateProps } from './components/log_rate_analysis'; export type { LogRateAnalysisContentWrapperProps } from './components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content_wrapper'; diff --git a/x-pack/plugins/aiops/public/plugin.tsx b/x-pack/plugins/aiops/public/plugin.tsx index 0d0f0e9e687c7..de35f49487e22 100755 --- a/x-pack/plugins/aiops/public/plugin.tsx +++ b/x-pack/plugins/aiops/public/plugin.tsx @@ -9,13 +9,14 @@ import type { CoreStart, Plugin } from '@kbn/core/public'; import { type CoreSetup } from '@kbn/core/public'; import { firstValueFrom } from 'rxjs'; import { EMBEDDABLE_CHANGE_POINT_CHART_TYPE } from '@kbn/aiops-change-point-detection/constants'; +import { dynamic } from '@kbn/shared-ux-utility'; import type { AiopsPluginSetup, AiopsPluginSetupDeps, AiopsPluginStart, AiopsPluginStartDeps, } from './types'; -import { getEmbeddableChangePointChart } from './embeddables/change_point_chart/embeddable_change_point_chart_component'; +import { getEmbeddableChangePointChart } from './embeddable/embeddable_change_point_chart_component'; export type AiopsCoreSetup = CoreSetup; @@ -28,22 +29,21 @@ export class AiopsPlugin ) { Promise.all([ firstValueFrom(licensing.license$), - import('./embeddables/register_embeddables'), + import('./embeddable/register_embeddable'), import('./ui_actions'), import('./cases/register_change_point_charts_attachment'), core.getStartServices(), ]).then( ([ license, - { registerChangePointChartEmbeddable, registerLogCategorizationEmbeddable }, + { registerEmbeddable }, { registerAiopsUiActions }, { registerChangePointChartsAttachment }, [coreStart, pluginStart], ]) => { if (license.hasAtLeast('platinum')) { if (embeddable) { - registerChangePointChartEmbeddable(core, embeddable); - registerLogCategorizationEmbeddable(core, embeddable); + registerEmbeddable(core, embeddable); } if (uiActions) { @@ -65,6 +65,12 @@ export class AiopsPlugin core, plugins ), + LogCategorizationWrapper: dynamic( + async () => + import( + './components/log_categorization/log_categorization_for_embeddable/log_categorization_wrapper' + ) + ), }; } diff --git a/x-pack/plugins/aiops/public/types.ts b/x-pack/plugins/aiops/public/types.ts index 663f963cba1ac..c955cb90a51a7 100755 --- a/x-pack/plugins/aiops/public/types.ts +++ b/x-pack/plugins/aiops/public/types.ts @@ -18,7 +18,8 @@ import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/ import type { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public'; import type { CasesPublicSetup } from '@kbn/cases-plugin/public'; import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; -import type { EmbeddableChangePointChartInput } from './embeddables/change_point_chart/embeddable_change_point_chart'; +import type { EmbeddableChangePointChartInput } from './embeddable/embeddable_change_point_chart'; +import type { LogCategorizationEmbeddableProps } from './components/log_categorization/log_categorization_for_embeddable/log_categorization_wrapper'; export interface AiopsPluginSetupDeps { embeddable: EmbeddableSetup; @@ -46,4 +47,5 @@ export interface AiopsPluginStartDeps { export type AiopsPluginSetup = void; export interface AiopsPluginStart { EmbeddableChangePointChart: React.ComponentType; + LogCategorizationWrapper: React.ComponentType; } From d9e7986a58d8a215a8b5a2936cfca4390a4abf0e Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 3 May 2024 14:37:53 +0100 Subject: [PATCH 42/92] fixing import paths --- .../main/components/layout/discover_main_content.tsx | 2 -- .../aiops/public/cases/change_point_charts_attachment.tsx | 2 +- .../public/cases/register_change_point_charts_attachment.tsx | 2 +- .../components/change_point_detection/fields_config.tsx | 2 +- .../components/change_point_detection/max_series_control.tsx | 2 +- .../components/log_categorization/log_categorization_page.tsx | 2 -- x-pack/plugins/aiops/public/hooks/use_cases_modal.ts | 4 ++-- .../public/ui_actions/edit_change_point_charts_panel.tsx | 4 ++-- .../plugins/aiops/public/ui_actions/open_change_point_ml.tsx | 2 +- 9 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx index ee4ca2a1737ab..92f8cbd88df10 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx @@ -64,8 +64,6 @@ export const DiscoverMainContent = ({ isChartAvailable, }: DiscoverMainContentProps) => { const { trackUiMetric } = useDiscoverServices(); - // const [patternCount, setPatternCount] = React.useState(undefined); - const setDiscoverViewMode = useCallback( (mode: VIEW_MODE) => { stateContainer.appState.update({ viewMode: mode }); diff --git a/x-pack/plugins/aiops/public/cases/change_point_charts_attachment.tsx b/x-pack/plugins/aiops/public/cases/change_point_charts_attachment.tsx index 3cfa205cc4096..042acaeaef7b6 100644 --- a/x-pack/plugins/aiops/public/cases/change_point_charts_attachment.tsx +++ b/x-pack/plugins/aiops/public/cases/change_point_charts_attachment.tsx @@ -14,7 +14,7 @@ import { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiDescriptionList } from '@elastic/eui'; import deepEqual from 'fast-deep-equal'; -import type { EmbeddableChangePointChartProps } from '../embeddables/change_point_chart'; +import type { EmbeddableChangePointChartProps } from '../embeddable'; export const initComponent = memoize( (fieldFormats: FieldFormatsStart, EmbeddableComponent: FC) => { diff --git a/x-pack/plugins/aiops/public/cases/register_change_point_charts_attachment.tsx b/x-pack/plugins/aiops/public/cases/register_change_point_charts_attachment.tsx index ece9ccce5533a..306c2fe1100df 100644 --- a/x-pack/plugins/aiops/public/cases/register_change_point_charts_attachment.tsx +++ b/x-pack/plugins/aiops/public/cases/register_change_point_charts_attachment.tsx @@ -14,8 +14,8 @@ import { CASES_ATTACHMENT_CHANGE_POINT_CHART, EMBEDDABLE_CHANGE_POINT_CHART_TYPE, } from '@kbn/aiops-change-point-detection/constants'; +import { getEmbeddableChangePointChart } from '../embeddable/embeddable_change_point_chart_component'; import type { AiopsPluginStartDeps } from '../types'; -import { getEmbeddableChangePointChart } from '../embeddables/change_point_chart'; export function registerChangePointChartsAttachment( cases: CasesPublicSetup, diff --git a/x-pack/plugins/aiops/public/components/change_point_detection/fields_config.tsx b/x-pack/plugins/aiops/public/components/change_point_detection/fields_config.tsx index fb3a043ce85bf..d3f56a4dcc733 100644 --- a/x-pack/plugins/aiops/public/components/change_point_detection/fields_config.tsx +++ b/x-pack/plugins/aiops/public/components/change_point_detection/fields_config.tsx @@ -39,7 +39,7 @@ import { } from '@kbn/aiops-change-point-detection/constants'; import { MaxSeriesControl } from './max_series_control'; import { useCasesModal } from '../../hooks/use_cases_modal'; -import type { EmbeddableChangePointChartInput } from '../../embeddables/change_point_chart/embeddable_change_point_chart'; +import { type EmbeddableChangePointChartInput } from '../../embeddable/embeddable_change_point_chart'; import { useDataSource } from '../../hooks/use_data_source'; import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; import { ChangePointsTable } from './change_points_table'; diff --git a/x-pack/plugins/aiops/public/components/change_point_detection/max_series_control.tsx b/x-pack/plugins/aiops/public/components/change_point_detection/max_series_control.tsx index 82c8a6fe6e131..4f2929751006a 100644 --- a/x-pack/plugins/aiops/public/components/change_point_detection/max_series_control.tsx +++ b/x-pack/plugins/aiops/public/components/change_point_detection/max_series_control.tsx @@ -10,7 +10,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { EuiFieldNumber, EuiFormRow, EuiIcon, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { type NumberValidationResult, numberValidator } from '@kbn/ml-agg-utils'; -import { MAX_SERIES } from '../../embeddables/change_point_chart/const'; +import { MAX_SERIES } from '../../embeddable/const'; const maxSeriesValidator = numberValidator({ min: 1, max: MAX_SERIES, integerOnly: true }); diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx index 61da22769cbe1..9c0d3a874b635 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx @@ -191,8 +191,6 @@ export const LogCategorizationPage: FC = ({ embeddin ); const loadCategories = useCallback(async () => { - // eslint-disable-next-line no-console - console.log('loadCategories'); setLoading(true); setData(null); setFieldValidationResult(null); diff --git a/x-pack/plugins/aiops/public/hooks/use_cases_modal.ts b/x-pack/plugins/aiops/public/hooks/use_cases_modal.ts index 05fbaf2d39660..ce5084ed27d20 100644 --- a/x-pack/plugins/aiops/public/hooks/use_cases_modal.ts +++ b/x-pack/plugins/aiops/public/hooks/use_cases_modal.ts @@ -9,8 +9,8 @@ import { useCallback } from 'react'; import { stringHash } from '@kbn/ml-string-hash'; import { AttachmentType } from '@kbn/cases-plugin/common'; import { useAiopsAppContext } from './use_aiops_app_context'; -import type { EmbeddableChangePointChartType } from '../embeddables/change_point_chart/embeddable_change_point_chart_factory'; -import type { EmbeddableChangePointChartInput } from '../embeddables/change_point_chart/embeddable_change_point_chart'; +import type { EmbeddableChangePointChartType } from '../embeddable/embeddable_change_point_chart_factory'; +import type { EmbeddableChangePointChartInput } from '../embeddable/embeddable_change_point_chart'; /** * Returns a callback for opening the cases modal with provided attachment state. diff --git a/x-pack/plugins/aiops/public/ui_actions/edit_change_point_charts_panel.tsx b/x-pack/plugins/aiops/public/ui_actions/edit_change_point_charts_panel.tsx index 2cab624fd601e..ff0409ad2ca0d 100644 --- a/x-pack/plugins/aiops/public/ui_actions/edit_change_point_charts_panel.tsx +++ b/x-pack/plugins/aiops/public/ui_actions/edit_change_point_charts_panel.tsx @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import { ViewMode } from '@kbn/embeddable-plugin/common'; import type { CoreStart } from '@kbn/core/public'; import { EMBEDDABLE_CHANGE_POINT_CHART_TYPE } from '@kbn/aiops-change-point-detection/constants'; -import type { EditChangePointChartsPanelContext } from '../embeddables/change_point_chart/types'; +import type { EditChangePointChartsPanelContext } from '../embeddable/types'; import type { AiopsPluginStartDeps } from '../types'; export const EDIT_CHANGE_POINT_CHARTS_ACTION = 'editChangePointChartsPanelAction'; @@ -36,7 +36,7 @@ export function createEditChangePointChartsPanelAction( try { const { resolveEmbeddableChangePointUserInput } = await import( - '../embeddables/change_point_chart/handle_explicit_input' + '../embeddable/handle_explicit_input' ); const result = await resolveEmbeddableChangePointUserInput( diff --git a/x-pack/plugins/aiops/public/ui_actions/open_change_point_ml.tsx b/x-pack/plugins/aiops/public/ui_actions/open_change_point_ml.tsx index cfcf6590457a9..19f2e9935fc23 100644 --- a/x-pack/plugins/aiops/public/ui_actions/open_change_point_ml.tsx +++ b/x-pack/plugins/aiops/public/ui_actions/open_change_point_ml.tsx @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import type { CoreStart } from '@kbn/core/public'; import { EMBEDDABLE_CHANGE_POINT_CHART_TYPE } from '@kbn/aiops-change-point-detection/constants'; import type { AiopsPluginStartDeps } from '../types'; -import type { EditChangePointChartsPanelContext } from '../embeddables/change_point_chart/types'; +import type { EditChangePointChartsPanelContext } from '../embeddable/types'; export const OPEN_CHANGE_POINT_IN_ML_APP_ACTION = 'openChangePointInMlAppAction'; From ee332ccafe6ef68ba30706694207407b275dd473 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 3 May 2024 15:29:30 +0100 Subject: [PATCH 43/92] moving types file --- x-pack/plugins/aiops/public/{types.ts => types/index.ts} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename x-pack/plugins/aiops/public/{types.ts => types/index.ts} (89%) mode change 100755 => 100644 diff --git a/x-pack/plugins/aiops/public/types.ts b/x-pack/plugins/aiops/public/types/index.ts old mode 100755 new mode 100644 similarity index 89% rename from x-pack/plugins/aiops/public/types.ts rename to x-pack/plugins/aiops/public/types/index.ts index c955cb90a51a7..bd4077900627a --- a/x-pack/plugins/aiops/public/types.ts +++ b/x-pack/plugins/aiops/public/types/index.ts @@ -18,8 +18,8 @@ import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/ import type { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public'; import type { CasesPublicSetup } from '@kbn/cases-plugin/public'; import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; -import type { EmbeddableChangePointChartInput } from './embeddable/embeddable_change_point_chart'; -import type { LogCategorizationEmbeddableProps } from './components/log_categorization/log_categorization_for_embeddable/log_categorization_wrapper'; +import type { EmbeddableChangePointChartInput } from '../embeddable/embeddable_change_point_chart'; +import type { LogCategorizationEmbeddableProps } from '../components/log_categorization/log_categorization_for_embeddable/log_categorization_wrapper'; export interface AiopsPluginSetupDeps { embeddable: EmbeddableSetup; From 621e5d3b54723f653c4ee97e4254c0b531faa2ec Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 3 May 2024 16:55:57 +0100 Subject: [PATCH 44/92] moving tabs to separate component --- .../discover_tabs.tsx | 89 +++++++++++++++ .../log_categorization_for_embeddable.tsx | 103 +++--------------- 2 files changed, 104 insertions(+), 88 deletions(-) create mode 100644 x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/discover_tabs.tsx diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/discover_tabs.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/discover_tabs.tsx new file mode 100644 index 0000000000000..6cc97c69c06db --- /dev/null +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/discover_tabs.tsx @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FC } from 'react'; +import React from 'react'; + +import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; + +import type { Category } from '@kbn/aiops-log-pattern-analysis/types'; +import type { DataViewField } from '@kbn/data-views-plugin/common'; + +import type { OpenInDiscover } from '../category_table/use_open_in_discover'; +import { OpenInDiscoverButtons } from '../category_table/table_header'; +import { EmbeddableMenu } from './embeddable_menu'; +import type { RandomSampler } from '../sampling_menu'; +import type { MinimumTimeRangeOption } from './minimum_time_range'; + +interface Props { + viewModeToggle: (patternCount?: number) => React.ReactElement; + randomSampler: RandomSampler; + openInDiscover: OpenInDiscover; + selectedCategories: Category[]; + loadCategories: () => void; + fields: DataViewField[]; + setSelectedField: React.Dispatch>; + selectedField: DataViewField | null; + minimumTimeRangeOption: MinimumTimeRangeOption; + setMinimumTimeRangeOption: (w: MinimumTimeRangeOption) => void; + data: { + categories: Category[]; + displayExamples: boolean; + totalCategories: number; + } | null; +} + +export const DiscoverTabs: FC = ({ + viewModeToggle, + randomSampler, + openInDiscover, + selectedCategories, + loadCategories, + fields, + setSelectedField, + selectedField, + minimumTimeRangeOption, + setMinimumTimeRangeOption, + data, +}) => { + return ( + + + {viewModeToggle(data?.categories.length)} + + + <> + {randomSampler !== undefined ? ( + <> + + + {selectedCategories.length > 0 ? ( + + + + ) : null} + + loadCategories()} + fields={fields} + setSelectedField={setSelectedField} + selectedField={selectedField} + minimumTimeRangeOption={minimumTimeRangeOption} + setMinimumTimeRangeOption={setMinimumTimeRangeOption} + categoryCount={data?.totalCategories} + /> + + + + ) : null} + + + + + ); +}; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx index dd8a6bc139569..7b39c238c6648 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx @@ -7,7 +7,7 @@ import type { FC } from 'react'; import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import type { DataViewField } from '@kbn/data-views-plugin/public'; import { i18n } from '@kbn/i18n'; @@ -42,8 +42,7 @@ import { useMinimumTimeRange } from './use_minimum_time_range'; import { createAdditionalConfigHash, createDocumentStatsHash, getMessageField } from '../utils'; import { useOpenInDiscover } from '../category_table/use_open_in_discover'; -import { OpenInDiscoverButtons } from '../category_table/table_header'; -import { EmbeddableMenu } from './embeddable_menu'; +import { DiscoverTabs } from './discover_tabs'; export interface LogCategorizationPageProps { onClose: () => void; @@ -338,57 +337,6 @@ export const LogCategorizationEmbeddable: FC = ({ toasts, ]); - // useEffect( - // function initOptionsMenu() { - // setPatternCount(data?.categories.length); - // setOptionsMenu( - // <> - // {randomSampler !== undefined ? ( - // <> - // - // - // {selectedCategories.length > 0 ? ( - // - // - // - // ) : null} - // - // loadCategories()} - // fields={fields} - // setSelectedField={setSelectedField} - // selectedField={selectedField} - // minimumTimeRangeOption={minimumTimeRangeOption} - // setMinimumTimeRangeOption={setMinimumTimeRangeOption} - // categoryCount={data?.totalCategories} - // /> - // - // - // - // ) : null} - // - // ); - // return () => { - // setPatternCount(undefined); - // setOptionsMenu(undefined); - // }; - // }, - // [ - // data, - // fields, - // loadCategories, - // randomSampler, - // selectedField, - // setOptionsMenu, - // setPatternCount, - // setMinimumTimeRangeOption, - // minimumTimeRangeOption, - // selectedCategories.length, - // openInDiscover, - // ] - // ); - useEffect( function triggerAnalysis() { const buckets = documentStats.documentCountStats?.buckets; @@ -451,40 +399,19 @@ export const LogCategorizationEmbeddable: FC = ({ return ( <> - - - {viewModeToggle(data?.categories.length)} - - - <> - {randomSampler !== undefined ? ( - <> - - - {selectedCategories.length > 0 ? ( - - - - ) : null} - - loadCategories()} - fields={fields} - setSelectedField={setSelectedField} - selectedField={selectedField} - minimumTimeRangeOption={minimumTimeRangeOption} - setMinimumTimeRangeOption={setMinimumTimeRangeOption} - categoryCount={data?.totalCategories} - /> - - - - ) : null} - - - - + Date: Fri, 3 May 2024 16:03:50 +0000 Subject: [PATCH 45/92] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- src/plugins/discover/tsconfig.json | 3 ++- x-pack/plugins/aiops/tsconfig.json | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plugins/discover/tsconfig.json b/src/plugins/discover/tsconfig.json index 1b034f7247312..97b9ab7cae92e 100644 --- a/src/plugins/discover/tsconfig.json +++ b/src/plugins/discover/tsconfig.json @@ -86,7 +86,8 @@ "@kbn/presentation-publishing", "@kbn/aiops-log-pattern-analysis", "@kbn/field-types", - "@kbn/observability-ai-assistant-plugin" + "@kbn/observability-ai-assistant-plugin", + "@kbn/aiops-plugin" ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/aiops/tsconfig.json b/x-pack/plugins/aiops/tsconfig.json index e29fe3d0b4c22..47c4b70ff3d13 100644 --- a/x-pack/plugins/aiops/tsconfig.json +++ b/x-pack/plugins/aiops/tsconfig.json @@ -71,6 +71,7 @@ "@kbn/react-kibana-context-theme", "@kbn/react-kibana-context-render", "@kbn/core-theme-browser", + "@kbn/shared-ux-utility", ], "exclude": [ "target/**/*", From 3fe28cb4b2d9578013f1d3908603e5b92f1dde47 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Tue, 7 May 2024 11:13:15 +0100 Subject: [PATCH 46/92] include path after merge with main --- .../main/components/pattern_analysis/pattern_analysis_tab.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_tab.tsx b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_tab.tsx index b8056e8be28c4..2a5c964b447f1 100644 --- a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_tab.tsx +++ b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_tab.tsx @@ -9,7 +9,7 @@ import React, { memo, type FC } from 'react'; import { useQuerySubscriber } from '@kbn/unified-field-list/src/hooks/use_query_subscriber'; // import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { useSavedSearch } from '../../services/discover_state_provider'; +import { useSavedSearch } from '../../state_management/discover_state_provider'; import { PatternAnalysisTable, type PatternAnalysisTableProps } from './pattern_analysis_table'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; From 701c7efe37aef1f627264acbf4653317029266d8 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 8 May 2024 11:20:14 +0100 Subject: [PATCH 47/92] fixing include --- .../main/components/pattern_analysis/pattern_analysis_table.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx index 64ee3da84e314..bc8c03759c300 100644 --- a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx +++ b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx @@ -11,7 +11,7 @@ import { METRIC_TYPE, UiCounterMetricType } from '@kbn/analytics'; import { type EmbeddablePatternAnalysisProps } from '@kbn/aiops-log-pattern-analysis/embeddable'; import { pick } from 'lodash'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; -import type { DiscoverStateContainer } from '../../services/discover_state'; +import { DiscoverStateContainer } from '../../state_management/discover_state'; import { PATTERN_ANALYSIS_LOADED } from './constants'; export type PatternAnalysisTableProps = EmbeddablePatternAnalysisProps & { From 38b3696bb8e5c7de34603be248b9df3947c4803f Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 8 May 2024 14:13:48 +0100 Subject: [PATCH 48/92] switching to document view type icons --- .../pattern_analysis_table.tsx | 1 + .../category_table/use_open_in_discover.ts | 3 +- .../create_categorization_job.tsx | 17 ++++- .../discover_tabs.tsx | 58 +++++++++++----- .../embeddable_menu.tsx | 19 +++-- .../log_categorization_for_embeddable.tsx | 4 ++ .../selected_patterns.tsx | 69 +++++++++++++++++++ 7 files changed, 142 insertions(+), 29 deletions(-) create mode 100644 x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/selected_patterns.tsx diff --git a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx index bc8c03759c300..a8efee790ecc2 100644 --- a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx +++ b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx @@ -62,6 +62,7 @@ export const PatternAnalysisTable = (props: PatternAnalysisTableProps) => { 'fieldFormats', 'application', 'charts', + 'uiActions', ]); const input = pick(props, ['dataView', 'savedSearch', 'query', 'filters', 'onAddFilter']); diff --git a/x-pack/plugins/aiops/public/components/log_categorization/category_table/use_open_in_discover.ts b/x-pack/plugins/aiops/public/components/log_categorization/category_table/use_open_in_discover.ts index b92e1da7f9564..963fd6b3a13e8 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/category_table/use_open_in_discover.ts +++ b/x-pack/plugins/aiops/public/components/log_categorization/category_table/use_open_in_discover.ts @@ -22,6 +22,7 @@ import { getLabels } from './labels'; export interface OpenInDiscover { openFunction: (mode: QueryMode, category?: Category) => void; labels: ReturnType; + count: number; } export function useOpenInDiscover( @@ -95,5 +96,5 @@ export function useOpenInDiscover( return getLabels(isFlyout && navigateToDiscover === false); }, [navigateToDiscover, onAddFilter, onClose]); - return { openFunction, labels }; + return { openFunction, labels, count: selectedCategories.length }; } diff --git a/x-pack/plugins/aiops/public/components/log_categorization/create_categorization_job.tsx b/x-pack/plugins/aiops/public/components/log_categorization/create_categorization_job.tsx index b0696d8767dc0..fb5e7508f2c2a 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/create_categorization_job.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/create_categorization_job.tsx @@ -9,7 +9,7 @@ import type { FC } from 'react'; import React from 'react'; import moment from 'moment'; -import { EuiButtonEmpty } from '@elastic/eui'; +import { EuiButtonEmpty, EuiButtonIcon } from '@elastic/eui'; import type { DataViewField, DataView } from '@kbn/data-views-plugin/common'; import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { @@ -26,6 +26,7 @@ interface Props { query: QueryDslQueryContainer; earliest: number | undefined; latest: number | undefined; + iconOnly?: boolean; } export const CreateCategorizationJobButton: FC = ({ @@ -34,6 +35,7 @@ export const CreateCategorizationJobButton: FC = ({ query, earliest, latest, + iconOnly = false, }) => { const { uiActions, @@ -58,6 +60,19 @@ export const CreateCategorizationJobButton: FC = ({ return null; } + if (iconOnly) { + return ( + + ); + } + return ( <> React.ReactElement; @@ -30,6 +32,10 @@ interface Props { selectedField: DataViewField | null; minimumTimeRangeOption: MinimumTimeRangeOption; setMinimumTimeRangeOption: (w: MinimumTimeRangeOption) => void; + dataview: DataView; + earliest: number | undefined; + latest: number | undefined; + query: QueryDslQueryContainer; data: { categories: Category[]; displayExamples: boolean; @@ -49,6 +55,10 @@ export const DiscoverTabs: FC = ({ minimumTimeRangeOption, setMinimumTimeRangeOption, data, + dataview, + earliest, + latest, + query, }) => { return ( @@ -57,16 +67,20 @@ export const DiscoverTabs: FC = ({ <> - {randomSampler !== undefined ? ( - <> - - - {selectedCategories.length > 0 ? ( - - - - ) : null} - + + + {selectedCategories.length > 0 ? ( + + + + ) : null} + +
+
loadCategories()} @@ -77,10 +91,22 @@ export const DiscoverTabs: FC = ({ setMinimumTimeRangeOption={setMinimumTimeRangeOption} categoryCount={data?.totalCategories} /> - - - - ) : null} +
+
+ {selectedField !== null && earliest !== undefined && latest !== undefined ? ( + + ) : null} +
+
+
+
diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx index d1ed4c46862d6..a08bed1c7314f 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx @@ -5,10 +5,10 @@ * 2.0. */ -import { EuiSuperSelect, useEuiPaddingSize } from '@elastic/eui'; +import { EuiSuperSelect } from '@elastic/eui'; import { EuiFormRow } from '@elastic/eui'; import { - EuiButtonEmpty, + EuiButtonIcon, EuiPanel, EuiPopover, EuiSpacer, @@ -54,7 +54,6 @@ export const EmbeddableMenu: FC = ({ }) => { const [showMenu, setShowMenu] = useState(false); const togglePopover = () => setShowMenu(!showMenu); - const xs = useEuiPaddingSize('xs'); const fieldOptions = useMemo( () => fields.map((field) => ({ inputDisplay: field.name, value: field })), @@ -62,16 +61,14 @@ export const EmbeddableMenu: FC = ({ ); const button = ( - togglePopover()} - css={{ marginRight: xs }} - > - Options - + // @ts-ignore - subdued does work + color="subdued" + /> ); return ( @@ -81,7 +78,7 @@ export const EmbeddableMenu: FC = ({ isOpen={showMenu} closePopover={() => togglePopover()} panelPaddingSize="s" - anchorPosition="downLeft" + anchorPosition="downRight" > diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx index 7b39c238c6648..fd4ffba669f3d 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx @@ -411,6 +411,10 @@ export const LogCategorizationEmbeddable: FC = ({ setMinimumTimeRangeOption={setMinimumTimeRangeOption} setSelectedField={setSelectedField} viewModeToggle={viewModeToggle} + dataview={dataView} + earliest={earliest} + latest={latest} + query={searchQuery} /> diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/selected_patterns.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/selected_patterns.tsx new file mode 100644 index 0000000000000..44c9014c1aee1 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/selected_patterns.tsx @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FC } from 'react'; +import { useState } from 'react'; +import React from 'react'; +import { + EuiDataGridToolbarControl, + EuiPopover, + EuiContextMenuPanel, + EuiContextMenuItem, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { QUERY_MODE } from '@kbn/aiops-log-pattern-analysis/get_category_query'; +import type { OpenInDiscover } from '../category_table/use_open_in_discover'; + +export const SelectedPatterns: FC<{ openInDiscover: OpenInDiscover }> = ({ openInDiscover }) => { + const { labels, openFunction } = openInDiscover; + const [showMenu, setShowMenu] = useState(false); + const togglePopover = () => setShowMenu(!showMenu); + + const button = ( + togglePopover()} + badgeContent={openInDiscover.count} + > + + + ); + + return ( + setShowMenu(false)} + isOpen={showMenu} + panelPaddingSize="none" + button={button} + className="unifiedDataTableToolbarControlButton" + > + openFunction(QUERY_MODE.INCLUDE)} + > + {labels.multiSelect.in} + , + openFunction(QUERY_MODE.EXCLUDE)} + > + {labels.multiSelect.out} + , + ]} + /> + + ); +}; From 5e258f69ab3f8f0f597581286178e96371f466a5 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 8 May 2024 14:38:45 +0100 Subject: [PATCH 49/92] tooltips --- .../create_categorization_job.tsx | 51 ++++++++++--------- .../embeddable_menu.tsx | 24 +++++---- 2 files changed, 43 insertions(+), 32 deletions(-) diff --git a/x-pack/plugins/aiops/public/components/log_categorization/create_categorization_job.tsx b/x-pack/plugins/aiops/public/components/log_categorization/create_categorization_job.tsx index fb5e7508f2c2a..8ccfd06f23e23 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/create_categorization_job.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/create_categorization_job.tsx @@ -9,7 +9,7 @@ import type { FC } from 'react'; import React from 'react'; import moment from 'moment'; -import { EuiButtonEmpty, EuiButtonIcon } from '@elastic/eui'; +import { EuiButtonEmpty, EuiButtonIcon, EuiToolTip } from '@elastic/eui'; import type { DataViewField, DataView } from '@kbn/data-views-plugin/common'; import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { @@ -18,6 +18,7 @@ import { } from '@kbn/ml-ui-actions'; import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; import { useAiopsAppContext } from '../../hooks/use_aiops_app_context'; interface Props { @@ -62,31 +63,35 @@ export const CreateCategorizationJobButton: FC = ({ if (iconOnly) { return ( - + + + ); } return ( - <> - - - - + + + ); }; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx index a08bed1c7314f..91f9490293f7b 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { EuiSuperSelect } from '@elastic/eui'; +import { EuiSuperSelect, EuiToolTip } from '@elastic/eui'; import { EuiFormRow } from '@elastic/eui'; import { EuiButtonIcon, @@ -61,14 +61,20 @@ export const EmbeddableMenu: FC = ({ ); const button = ( - togglePopover()} - // @ts-ignore - subdued does work - color="subdued" - /> + + togglePopover()} + // @ts-ignore - subdued does work + color="subdued" + /> + ); return ( From 6c3322ea1df3164ff0fdde96afc2afaec3e010f2 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 8 May 2024 17:08:28 +0100 Subject: [PATCH 50/92] cleaning up types --- .../pattern_analysis/pattern_analysis_tab.tsx | 23 ++++++++--------- .../pattern_analysis_table.tsx | 25 ++++++++----------- .../aiops_log_pattern_analysis/embeddable.ts | 11 +------- .../log_categorization_for_embeddable.tsx | 13 ++++------ .../log_categorization_wrapper.tsx | 22 ++++++++-------- x-pack/plugins/aiops/public/plugin.tsx | 2 +- x-pack/plugins/aiops/public/types/index.ts | 4 +-- 7 files changed, 41 insertions(+), 59 deletions(-) diff --git a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_tab.tsx b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_tab.tsx index 2a5c964b447f1..d651a21a2f2b0 100644 --- a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_tab.tsx +++ b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_tab.tsx @@ -8,7 +8,6 @@ import React, { memo, type FC } from 'react'; import { useQuerySubscriber } from '@kbn/unified-field-list/src/hooks/use_query_subscriber'; -// import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { useSavedSearch } from '../../state_management/discover_state_provider'; import { PatternAnalysisTable, type PatternAnalysisTableProps } from './pattern_analysis_table'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; @@ -22,18 +21,16 @@ export const PatternAnalysisTab: FC - - + ); } ); diff --git a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx index a8efee790ecc2..b499ec9b9de80 100644 --- a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx +++ b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx @@ -8,18 +8,13 @@ import React, { useEffect, useState } from 'react'; import { METRIC_TYPE, UiCounterMetricType } from '@kbn/analytics'; -import { type EmbeddablePatternAnalysisProps } from '@kbn/aiops-log-pattern-analysis/embeddable'; +import { type EmbeddablePatternAnalysisInput } from '@kbn/aiops-log-pattern-analysis/embeddable'; import { pick } from 'lodash'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; import { DiscoverStateContainer } from '../../state_management/discover_state'; import { PATTERN_ANALYSIS_LOADED } from './constants'; -export type PatternAnalysisTableProps = EmbeddablePatternAnalysisProps & { - searchDescription?: string; - - /** - * State container with persisted settings - */ +export type PatternAnalysisTableProps = EmbeddablePatternAnalysisInput & { stateContainer?: DiscoverStateContainer; trackUiMetric?: (metricType: UiCounterMetricType, eventName: string | string[]) => void; viewModeToggle: (patternCount?: number) => React.ReactElement; @@ -65,20 +60,20 @@ export const PatternAnalysisTable = (props: PatternAnalysisTableProps) => { 'uiActions', ]); - const input = pick(props, ['dataView', 'savedSearch', 'query', 'filters', 'onAddFilter']); + const input: EmbeddablePatternAnalysisInput = Object.assign( + {}, + pick(props, ['dataView', 'savedSearch', 'query', 'filters', 'onAddFilter']), + lastReloadRequestTime + ); return ( - {}, - embeddingOrigin: 'discover-embedded', }} deps={deps} + embeddingOrigin="discover-embedded" /> ); }; diff --git a/x-pack/packages/ml/aiops_log_pattern_analysis/embeddable.ts b/x-pack/packages/ml/aiops_log_pattern_analysis/embeddable.ts index 85912b1836c90..2bc122ce11e14 100644 --- a/x-pack/packages/ml/aiops_log_pattern_analysis/embeddable.ts +++ b/x-pack/packages/ml/aiops_log_pattern_analysis/embeddable.ts @@ -5,12 +5,11 @@ * 2.0. */ -import type { EmbeddableInput, EmbeddableOutput } from '@kbn/embeddable-plugin/public'; import type { Query, AggregateQuery, Filter } from '@kbn/es-query'; import type { SavedSearch } from '@kbn/saved-search-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/public'; -export interface EmbeddablePatternAnalysisProps { +export interface EmbeddablePatternAnalysisInput { dataView: DataView; savedSearch?: SavedSearch | null; query?: T; @@ -22,11 +21,3 @@ export interface EmbeddablePatternAnalysisProps { onAddFilter?: () => void; lastReloadRequestTime?: number; } - -export type EmbeddablePatternAnalysisInput = EmbeddableInput & EmbeddablePatternAnalysisProps; - -export type EmbeddablePatternAnalysisOutput = EmbeddableOutput & { indexPatterns?: DataView[] }; - -export const EMBEDDABLE_PATTERN_ANALYSIS_TYPE = 'aiopsPatternAnalysis' as const; - -export type EmbeddablePatternAnalysisType = typeof EMBEDDABLE_PATTERN_ANALYSIS_TYPE; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx index fd4ffba669f3d..2c4ebed70c435 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx @@ -20,7 +20,7 @@ import type { Category } from '@kbn/aiops-log-pattern-analysis/types'; import type { CategorizationAdditionalFilter } from '@kbn/aiops-log-pattern-analysis/create_category_request'; import { AIOPS_TELEMETRY_ID } from '@kbn/aiops-common/constants'; -import type { EmbeddablePatternAnalysisProps } from '@kbn/aiops-log-pattern-analysis/embeddable'; +import type { EmbeddablePatternAnalysisInput } from '@kbn/aiops-log-pattern-analysis/embeddable'; import { css } from '@emotion/react'; import { type LogCategorizationPageUrlState, @@ -44,18 +44,14 @@ import { createAdditionalConfigHash, createDocumentStatsHash, getMessageField } import { useOpenInDiscover } from '../category_table/use_open_in_discover'; import { DiscoverTabs } from './discover_tabs'; -export interface LogCategorizationPageProps { - onClose: () => void; - embeddingOrigin: string; - input: Readonly; +export interface LogCategorizationEmbeddableProps { + input: Readonly; viewModeToggle: (patternCount?: number) => React.ReactElement; } const BAR_TARGET = 20; -export const LogCategorizationEmbeddable: FC = ({ - onClose, - embeddingOrigin, +export const LogCategorizationEmbeddable: FC = ({ input, viewModeToggle, }) => { @@ -65,6 +61,7 @@ export const LogCategorizationEmbeddable: FC = ({ query: { getState, filterManager }, }, uiSettings, + embeddingOrigin, } = useAiopsAppContext(); const { dataView, savedSearch } = input; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_wrapper.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_wrapper.tsx index eaba76a6ede48..43eb61faa84e1 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_wrapper.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_wrapper.tsx @@ -7,15 +7,14 @@ import type { FC } from 'react'; import React, { Suspense } from 'react'; -// import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import type { ThemeServiceStart } from '@kbn/core-theme-browser'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { IUiSettingsClient } from '@kbn/core/public'; import type { CoreStart } from '@kbn/core/public'; import type { LensPublicStart } from '@kbn/lens-plugin/public'; -import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; +import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import { DatePickerContextProvider } from '@kbn/ml-date-picker'; import { StorageContextProvider } from '@kbn/ml-local-storage'; @@ -25,7 +24,7 @@ import { Storage } from '@kbn/kibana-utils-plugin/public'; import { AIOPS_STORAGE_KEYS } from '../../../types/storage'; import type { AiopsAppDependencies } from '../../../hooks/use_aiops_app_context'; import { AiopsAppContext } from '../../../hooks/use_aiops_app_context'; -import type { LogCategorizationPageProps } from './log_categorization_for_embeddable'; +import type { LogCategorizationEmbeddableProps } from './log_categorization_for_embeddable'; import { LogCategorizationEmbeddable } from './log_categorization_for_embeddable'; export interface EmbeddableLogCategorizationDeps { @@ -36,23 +35,28 @@ export interface EmbeddableLogCategorizationDeps { notifications: CoreStart['notifications']; i18n: CoreStart['i18n']; lens: LensPublicStart; - usageCollection?: UsageCollectionSetup; // is this needed?!!!!!!!!!!!!!!!!!!!!!!!!!! fieldFormats: FieldFormatsStart; application: CoreStart['application']; charts: ChartsPluginStart; + uiActions: UiActionsStart; } -export interface LogCategorizationEmbeddableProps { +export interface LogCategorizationEmbeddableWrapperProps { deps: EmbeddableLogCategorizationDeps; - props: LogCategorizationPageProps; + props: LogCategorizationEmbeddableProps; + embeddingOrigin?: string; } const localStorage = new Storage(window.localStorage); -export const LogCategorizationWrapper: FC = ({ deps, props }) => { +export const LogCategorizationWrapper: FC = ({ + deps, + props, + embeddingOrigin, +}) => { const I18nContext = deps.i18n.Context; const aiopsAppContextValue = { - embeddingOrigin: props.input.embeddingOrigin, + embeddingOrigin, ...deps, } as unknown as AiopsAppDependencies; @@ -69,8 +73,6 @@ export const LogCategorizationWrapper: FC = ({ diff --git a/x-pack/plugins/aiops/public/plugin.tsx b/x-pack/plugins/aiops/public/plugin.tsx index de35f49487e22..9e53baed999b9 100755 --- a/x-pack/plugins/aiops/public/plugin.tsx +++ b/x-pack/plugins/aiops/public/plugin.tsx @@ -65,7 +65,7 @@ export class AiopsPlugin core, plugins ), - LogCategorizationWrapper: dynamic( + PatternAnalysisComponent: dynamic( async () => import( './components/log_categorization/log_categorization_for_embeddable/log_categorization_wrapper' diff --git a/x-pack/plugins/aiops/public/types/index.ts b/x-pack/plugins/aiops/public/types/index.ts index bd4077900627a..c2b1a7a6ecd62 100644 --- a/x-pack/plugins/aiops/public/types/index.ts +++ b/x-pack/plugins/aiops/public/types/index.ts @@ -19,7 +19,7 @@ import type { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/pu import type { CasesPublicSetup } from '@kbn/cases-plugin/public'; import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; import type { EmbeddableChangePointChartInput } from '../embeddable/embeddable_change_point_chart'; -import type { LogCategorizationEmbeddableProps } from '../components/log_categorization/log_categorization_for_embeddable/log_categorization_wrapper'; +import type { LogCategorizationEmbeddableWrapperProps } from '../components/log_categorization/log_categorization_for_embeddable/log_categorization_wrapper'; export interface AiopsPluginSetupDeps { embeddable: EmbeddableSetup; @@ -47,5 +47,5 @@ export interface AiopsPluginStartDeps { export type AiopsPluginSetup = void; export interface AiopsPluginStart { EmbeddableChangePointChart: React.ComponentType; - LogCategorizationWrapper: React.ComponentType; + PatternAnalysisComponent: React.ComponentType; } From 1fc4258617e13cef03e5b7a46d16bba1adc8f295 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 8 May 2024 16:21:43 +0000 Subject: [PATCH 51/92] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- x-pack/packages/ml/aiops_log_pattern_analysis/tsconfig.json | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/packages/ml/aiops_log_pattern_analysis/tsconfig.json b/x-pack/packages/ml/aiops_log_pattern_analysis/tsconfig.json index d6b25c65903bb..ac3480d1d5a8b 100644 --- a/x-pack/packages/ml/aiops_log_pattern_analysis/tsconfig.json +++ b/x-pack/packages/ml/aiops_log_pattern_analysis/tsconfig.json @@ -20,7 +20,6 @@ "@kbn/config-schema", "@kbn/i18n", "@kbn/ml-runtime-field-utils", - "@kbn/embeddable-plugin", "@kbn/es-query", "@kbn/saved-search-plugin", "@kbn/data-views-plugin", From f2a4d2986578d6ba30877803fdc2881d4c59f68f Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 9 May 2024 09:28:13 +0100 Subject: [PATCH 52/92] aria labels --- .../log_categorization/create_categorization_job.tsx | 3 +++ .../log_categorization_for_embeddable/embeddable_menu.tsx | 3 +++ 2 files changed, 6 insertions(+) diff --git a/x-pack/plugins/aiops/public/components/log_categorization/create_categorization_job.tsx b/x-pack/plugins/aiops/public/components/log_categorization/create_categorization_job.tsx index 8ccfd06f23e23..73ba90fa403a5 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/create_categorization_job.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/create_categorization_job.tsx @@ -75,6 +75,9 @@ export const CreateCategorizationJobButton: FC = ({ onClick={createADJob} // @ts-ignore - subdued does work color="subdued" + aria-label={i18n.translate('xpack.aiops.categorizeFlyout.findAnomalies.tooltip', { + defaultMessage: 'Create Anomaly Detection job to find anomalies in patterns', + })} /> ); diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx index 91f9490293f7b..d1b6db0fd1822 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx @@ -73,6 +73,9 @@ export const EmbeddableMenu: FC = ({ onClick={() => togglePopover()} // @ts-ignore - subdued does work color="subdued" + aria-label={i18n.translate('xpack.aiops.logCategorization.embeddableMenu.aria', { + defaultMessage: 'Pattern analysis options', + })} /> ); From e00867d25646beda36d2b91c78f58bc25ecd6c85 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 9 May 2024 15:31:56 +0100 Subject: [PATCH 53/92] adding license checks --- .../layout/discover_main_content.tsx | 14 +---- .../view_mode_toggle/view_mode_toggle.tsx | 59 ++++++++++++------- .../log_categorization_enabled.ts | 19 ++++++ .../log_categorization_for_embeddable.tsx | 10 +--- .../log_rate_analysis_content.tsx | 1 + x-pack/plugins/aiops/public/hooks/use_data.ts | 4 +- .../public/hooks/use_document_count_stats.ts | 7 ++- x-pack/plugins/aiops/public/plugin.tsx | 2 + x-pack/plugins/aiops/public/types/index.ts | 2 + 9 files changed, 74 insertions(+), 44 deletions(-) create mode 100644 x-pack/plugins/aiops/public/components/log_categorization/log_categorization_enabled.ts diff --git a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx index dc7ec2736b036..87f93074595ff 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx @@ -63,14 +63,7 @@ export const DiscoverMainContent = ({ panelsToggle, isChartAvailable, }: DiscoverMainContentProps) => { - const { - trackUiMetric, - dataVisualizer: dataVisualizerService, - aiops: aiopsService, - } = useDiscoverServices(); - - const shouldShowViewModeToggle = - dataVisualizerService !== undefined || aiopsService !== undefined; + const { trackUiMetric } = useDiscoverServices(); const setDiscoverViewMode = useCallback( (mode: VIEW_MODE) => { @@ -93,7 +86,7 @@ export const DiscoverMainContent = ({ const viewModeToggle = useCallback( (patternCount?: number) => { - return shouldShowViewModeToggle ? ( + return ( - ) : ( - ); }, [ @@ -119,7 +110,6 @@ export const DiscoverMainContent = ({ dataView, panelsToggle, isChartAvailable, - shouldShowViewModeToggle, ] ); diff --git a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx index 37be3028f69e6..6bd3bc5ab7ca5 100644 --- a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx +++ b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx @@ -6,13 +6,12 @@ * Side Public License, v 1. */ -import React, { useMemo, useEffect, type ReactElement } from 'react'; +import React, { useMemo, useEffect, useState, type ReactElement } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiTab, EuiTabs, useEuiTheme } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { css } from '@emotion/react'; import { isLegacyTableEnabled, SHOW_FIELD_STATISTICS } from '@kbn/discover-utils'; import type { DataView } from '@kbn/data-views-plugin/common'; -import { ES_FIELD_TYPES } from '@kbn/field-types'; import { VIEW_MODE } from '../../../common/constants'; import { useDiscoverServices } from '../../hooks/use_discover_services'; import type { DiscoverStateContainer } from '../../application/main/state_management/discover_state'; @@ -36,15 +35,35 @@ export const DocumentViewModeToggle = ({ dataView: DataView; }) => { const { euiTheme } = useEuiTheme(); - const { uiSettings, dataVisualizer: dataVisualizerService } = useDiscoverServices(); + const { + uiSettings, + dataVisualizer: dataVisualizerService, + aiops: aiopsService, + } = useDiscoverServices(); const isLegacy = useMemo( () => isLegacyTableEnabled({ uiSettings, isTextBasedQueryMode: isTextBasedQuery }), [uiSettings, isTextBasedQuery] ); + const [showPatternAnalysisTab, setShowPatternAnalysisTab] = useState(null); + const showFieldStatisticsTab = useMemo( + () => uiSettings.get(SHOW_FIELD_STATISTICS) && dataVisualizerService !== undefined, + [dataVisualizerService, uiSettings] + ); - const showPatternAnalysisTab = useMemo( - () => dataView.fields.some((f) => f.esTypes?.includes(ES_FIELD_TYPES.TEXT)), - [dataView.fields] + useEffect( + function checkForPatternAnalysis() { + if (aiopsService === undefined) { + setShowPatternAnalysisTab(false); + return; + } + aiopsService + .patternAnalysisAvailable(dataView) + .then(setShowPatternAnalysisTab) + .catch(() => { + setShowPatternAnalysisTab(false); + }); + }, + [aiopsService, dataView] ); useEffect(() => { @@ -68,9 +87,6 @@ export const DocumentViewModeToggle = ({ } `; - const showViewModeToggle = - (uiSettings.get(SHOW_FIELD_STATISTICS) && dataVisualizerService !== undefined) ?? false; - return ( )} - {isTextBasedQuery || !showViewModeToggle ? ( + {isTextBasedQuery || + (showFieldStatisticsTab === false && showPatternAnalysisTab === false) ? ( ) : ( @@ -119,16 +136,18 @@ export const DocumentViewModeToggle = ({ ) : null} - setDiscoverViewMode(VIEW_MODE.AGGREGATED_LEVEL)} - data-test-subj="dscViewModeFieldStatsButton" - > - - + {showFieldStatisticsTab ? ( + setDiscoverViewMode(VIEW_MODE.AGGREGATED_LEVEL)} + data-test-subj="dscViewModeFieldStatsButton" + > + + + ) : null} )} diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_enabled.ts b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_enabled.ts new file mode 100644 index 0000000000000..8a30017664165 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_enabled.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { DataView } from '@kbn/data-views-plugin/public'; +import { ES_FIELD_TYPES } from '@kbn/field-types'; +import { firstValueFrom } from 'rxjs'; +import type { AiopsPluginStartDeps } from '../../types'; + +export function getPatternAnalysisAvailable({ licensing }: AiopsPluginStartDeps) { + return async (dataView: DataView) => { + const hasTextFields = dataView.fields.some((f) => f.esTypes?.includes(ES_FIELD_TYPES.TEXT)); + const isPlatinum = (await firstValueFrom(licensing.license$)).hasAtLeast('platinum'); + return isPlatinum && hasTextFields; + }; +} diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx index 2c4ebed70c435..9506a6d845abb 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx @@ -153,7 +153,8 @@ export const LogCategorizationEmbeddable: FC = () => {}, undefined, undefined, - BAR_TARGET + BAR_TARGET, + false ); const onAddFilter = useCallback( @@ -270,8 +271,6 @@ export const LogCategorizationEmbeddable: FC = timeRange.useSubAgg ? additionalFilter : undefined ), ]); - // eslint-disable-next-line no-console - console.log('categorizationResult', categorizationResult); if (mounted.current !== true) { return; @@ -346,8 +345,6 @@ export const LogCategorizationEmbeddable: FC = (currentAdditionalConfigsHash !== previousAdditionalConfigsHash && currentDocumentStatsHash !== null) ) { - // eslint-disable-next-line no-console - console.log('trigger', currentDocumentStatsHash, previousDocumentStatsHash); randomSampler.setDocCount(documentStats.totalCount); setEventRate( Object.entries(buckets).map(([key, docCount]) => ({ @@ -358,9 +355,6 @@ export const LogCategorizationEmbeddable: FC = loadCategories(); setPreviousDocumentStatsHash(currentDocumentStatsHash); setPreviousAdditionalConfigsHash(currentAdditionalConfigsHash); - } else { - // eslint-disable-next-line no-console - console.log('no trigger', currentDocumentStatsHash, previousDocumentStatsHash); } }, [ diff --git a/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content.tsx b/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content.tsx index 59373ae4b9253..01ed5230c0620 100644 --- a/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content.tsx +++ b/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content.tsx @@ -151,6 +151,7 @@ export const LogRateAnalysisContent: FC = ({ currentSelectedSignificantItem, currentSelectedGroup, undefined, + true, timeRange ); diff --git a/x-pack/plugins/aiops/public/hooks/use_data.ts b/x-pack/plugins/aiops/public/hooks/use_data.ts index 15b1e7a81a03d..2b854c8e3dc14 100644 --- a/x-pack/plugins/aiops/public/hooks/use_data.ts +++ b/x-pack/plugins/aiops/public/hooks/use_data.ts @@ -36,6 +36,7 @@ export const useData = ( selectedSignificantItem?: SignificantItem, selectedGroup: GroupTableItem | null = null, barTarget: number = DEFAULT_BAR_TARGET, + changePointsByDefault = true, timeRange?: { min: Moment; max: Moment } ) => { const { executionContext, uiSettings } = useAiopsAppContext(); @@ -103,7 +104,8 @@ export const useData = ( const documentStats = useDocumentCountStats( overallStatsRequest, selectedSignificantItemStatsRequest, - lastRefresh + lastRefresh, + changePointsByDefault ); useEffect(() => { diff --git a/x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts b/x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts index f431179cfe858..83f10a4a445d3 100644 --- a/x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts +++ b/x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts @@ -56,7 +56,8 @@ function displayError(toastNotifications: ToastsStart, index: string, err: any) export function useDocumentCountStats( searchParams: TParams | undefined, searchParamsCompare: TParams | undefined, - lastRefresh: number + lastRefresh: number, + changePointsByDefault = true ): DocumentStats { const { data, @@ -96,7 +97,7 @@ export function useDocumentCountStats; @@ -65,6 +66,7 @@ export class AiopsPlugin core, plugins ), + patternAnalysisAvailable: getPatternAnalysisAvailable(plugins), PatternAnalysisComponent: dynamic( async () => import( diff --git a/x-pack/plugins/aiops/public/types/index.ts b/x-pack/plugins/aiops/public/types/index.ts index c2b1a7a6ecd62..d0e93e9aeb416 100644 --- a/x-pack/plugins/aiops/public/types/index.ts +++ b/x-pack/plugins/aiops/public/types/index.ts @@ -18,6 +18,7 @@ import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/ import type { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public'; import type { CasesPublicSetup } from '@kbn/cases-plugin/public'; import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; +import type { DataView } from '@kbn/data-views-plugin/public'; import type { EmbeddableChangePointChartInput } from '../embeddable/embeddable_change_point_chart'; import type { LogCategorizationEmbeddableWrapperProps } from '../components/log_categorization/log_categorization_for_embeddable/log_categorization_wrapper'; @@ -47,5 +48,6 @@ export interface AiopsPluginStartDeps { export type AiopsPluginSetup = void; export interface AiopsPluginStart { EmbeddableChangePointChart: React.ComponentType; + patternAnalysisAvailable: (dataView: DataView) => Promise; PatternAnalysisComponent: React.ComponentType; } From ee45170aba2a86cb327693ed837f83a61970735e Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 9 May 2024 17:15:01 +0100 Subject: [PATCH 54/92] updating saved search schema --- .../layout/discover_main_content.test.tsx | 12 +++++++++++- .../components/layout/discover_main_content.tsx | 16 +++++++--------- .../main/utils/get_valid_view_mode.ts | 2 +- .../common/content_management/v1/cm_services.ts | 6 +++++- .../saved_search/server/saved_objects/schema.ts | 10 ++++++++++ .../saved_search/server/saved_objects/search.ts | 8 ++++++++ 6 files changed, 42 insertions(+), 12 deletions(-) diff --git a/src/plugins/discover/public/application/main/components/layout/discover_main_content.test.tsx b/src/plugins/discover/public/application/main/components/layout/discover_main_content.test.tsx index 7cb6edcedcf5d..271b9aa70effc 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_main_content.test.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_main_content.test.tsx @@ -31,6 +31,7 @@ import { DocumentViewModeToggle } from '../../../../components/view_mode_toggle' import { searchSourceInstanceMock } from '@kbn/data-plugin/common/search/search_source/mocks'; import { DiscoverDocuments } from './discover_documents'; import { FieldStatisticsTab } from '../field_stats_table'; +import { PatternAnalysisTab } from '../pattern_analysis'; import { DiscoverMainProvider } from '../../state_management/discover_state_provider'; import { getDiscoverStateMock } from '../../../../__mocks__/discover_state.mock'; import { PanelsToggle } from '../../../../components/panels_toggle'; @@ -187,13 +188,22 @@ describe('Discover main content component', () => { it('should show DiscoverDocuments when VIEW_MODE is DOCUMENT_LEVEL', async () => { const component = await mountComponent(); expect(component.find(DiscoverDocuments).exists()).toBe(true); + expect(component.find(PatternAnalysisTab).exists()).toBe(false); expect(component.find(FieldStatisticsTab).exists()).toBe(false); }); - it('should show FieldStatisticsTableMemoized when VIEW_MODE is not DOCUMENT_LEVEL', async () => { + it('should show FieldStatisticsTab when VIEW_MODE is AGGREGATED_LEVEL', async () => { const component = await mountComponent({ viewMode: VIEW_MODE.AGGREGATED_LEVEL }); expect(component.find(DiscoverDocuments).exists()).toBe(false); + expect(component.find(PatternAnalysisTab).exists()).toBe(false); expect(component.find(FieldStatisticsTab).exists()).toBe(true); }); + + it('should show PatternAnalysisTab when VIEW_MODE is PATTERN_LEVEL', async () => { + const component = await mountComponent({ viewMode: VIEW_MODE.PATTERN_LEVEL }); + expect(component.find(DiscoverDocuments).exists()).toBe(false); + expect(component.find(PatternAnalysisTab).exists()).toBe(true); + expect(component.find(FieldStatisticsTab).exists()).toBe(false); + }); }); }); diff --git a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx index 87f93074595ff..8d4ad6972a93b 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx @@ -153,15 +153,13 @@ export const DiscoverMainContent = ({ ) : null} {viewMode === VIEW_MODE.PATTERN_LEVEL ? ( - <> - setDiscoverViewMode(VIEW_MODE.DOCUMENT_LEVEL)} - trackUiMetric={trackUiMetric} - viewModeToggle={viewModeToggle} - /> - + setDiscoverViewMode(VIEW_MODE.DOCUMENT_LEVEL)} + trackUiMetric={trackUiMetric} + viewModeToggle={viewModeToggle} + /> ) : null} diff --git a/src/plugins/discover/public/application/main/utils/get_valid_view_mode.ts b/src/plugins/discover/public/application/main/utils/get_valid_view_mode.ts index 4d6d660c28eef..1b1f6b465c054 100644 --- a/src/plugins/discover/public/application/main/utils/get_valid_view_mode.ts +++ b/src/plugins/discover/public/application/main/utils/get_valid_view_mode.ts @@ -22,7 +22,7 @@ export const getValidViewMode = ({ }): VIEW_MODE | undefined => { if (viewMode === VIEW_MODE.AGGREGATED_LEVEL && isTextBasedQueryMode) { // only this mode is supported for text-based languages - return VIEW_MODE.DOCUMENT_LEVEL; + return VIEW_MODE.DOCUMENT_LEVEL; // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! } return viewMode; diff --git a/src/plugins/saved_search/common/content_management/v1/cm_services.ts b/src/plugins/saved_search/common/content_management/v1/cm_services.ts index bc9d18b21e5b7..ef9d24bb8722d 100644 --- a/src/plugins/saved_search/common/content_management/v1/cm_services.ts +++ b/src/plugins/saved_search/common/content_management/v1/cm_services.ts @@ -42,7 +42,11 @@ const savedSearchAttributesSchema = schema.object( searchSourceJSON: schema.string(), }), viewMode: schema.maybe( - schema.oneOf([schema.literal('documents'), schema.literal('aggregated')]) + schema.oneOf([ + schema.literal('documents'), + schema.literal('patterns'), + schema.literal('aggregated'), + ]) ), hideAggregatedPreview: schema.maybe(schema.boolean()), rowHeight: schema.maybe(schema.number()), diff --git a/src/plugins/saved_search/server/saved_objects/schema.ts b/src/plugins/saved_search/server/saved_objects/schema.ts index fb0308915fe72..125ddcceb320c 100644 --- a/src/plugins/saved_search/server/saved_objects/schema.ts +++ b/src/plugins/saved_search/server/saved_objects/schema.ts @@ -119,3 +119,13 @@ export const SCHEMA_SEARCH_MODEL_VERSION_3 = SCHEMA_SEARCH_MODEL_VERSION_2.exten ]) ), }); + +export const SCHEMA_SEARCH_MODEL_VERSION_4 = SCHEMA_SEARCH_MODEL_VERSION_3.extends({ + viewMode: schema.maybe( + schema.oneOf([ + schema.literal(VIEW_MODE.DOCUMENT_LEVEL), + schema.literal(VIEW_MODE.PATTERN_LEVEL), + schema.literal(VIEW_MODE.AGGREGATED_LEVEL), + ]) + ), +}); diff --git a/src/plugins/saved_search/server/saved_objects/search.ts b/src/plugins/saved_search/server/saved_objects/search.ts index 6c6a9bb81c1ed..925a4dd66b180 100644 --- a/src/plugins/saved_search/server/saved_objects/search.ts +++ b/src/plugins/saved_search/server/saved_objects/search.ts @@ -15,6 +15,7 @@ import { SCHEMA_SEARCH_MODEL_VERSION_1, SCHEMA_SEARCH_MODEL_VERSION_2, SCHEMA_SEARCH_MODEL_VERSION_3, + SCHEMA_SEARCH_MODEL_VERSION_4, } from './schema'; export function getSavedSearchObjectType( @@ -62,6 +63,13 @@ export function getSavedSearchObjectType( create: SCHEMA_SEARCH_MODEL_VERSION_3, }, }, + 4: { + changes: [], + schemas: { + forwardCompatibility: SCHEMA_SEARCH_MODEL_VERSION_4.extends({}, { unknowns: 'ignore' }), + create: SCHEMA_SEARCH_MODEL_VERSION_4, + }, + }, }, mappings: { dynamic: false, From a47003b84c80eaf58957920bf8d8bf8626a47b1d Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 9 May 2024 17:19:38 +0100 Subject: [PATCH 55/92] improving bundle size --- .../log_categorization/log_categorization_enabled.ts | 4 ++-- x-pack/plugins/aiops/public/plugin.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_enabled.ts b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_enabled.ts index 8a30017664165..d54d55f157b0b 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_enabled.ts +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_enabled.ts @@ -7,10 +7,10 @@ import type { DataView } from '@kbn/data-views-plugin/public'; import { ES_FIELD_TYPES } from '@kbn/field-types'; +import type { LicensingPluginStart } from '@kbn/licensing-plugin/public'; import { firstValueFrom } from 'rxjs'; -import type { AiopsPluginStartDeps } from '../../types'; -export function getPatternAnalysisAvailable({ licensing }: AiopsPluginStartDeps) { +export function getPatternAnalysisAvailable(licensing: LicensingPluginStart) { return async (dataView: DataView) => { const hasTextFields = dataView.fields.some((f) => f.esTypes?.includes(ES_FIELD_TYPES.TEXT)); const isPlatinum = (await firstValueFrom(licensing.license$)).hasAtLeast('platinum'); diff --git a/x-pack/plugins/aiops/public/plugin.tsx b/x-pack/plugins/aiops/public/plugin.tsx index 23b68d46a4673..c77ce0e6beb75 100755 --- a/x-pack/plugins/aiops/public/plugin.tsx +++ b/x-pack/plugins/aiops/public/plugin.tsx @@ -66,7 +66,7 @@ export class AiopsPlugin core, plugins ), - patternAnalysisAvailable: getPatternAnalysisAvailable(plugins), + patternAnalysisAvailable: getPatternAnalysisAvailable(plugins.licensing), PatternAnalysisComponent: dynamic( async () => import( From 29db7332d10dbfb2421af98c0098be5c45615b6b Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 9 May 2024 17:40:35 +0100 Subject: [PATCH 56/92] improve license check --- .../log_categorization/log_categorization_enabled.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_enabled.ts b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_enabled.ts index d54d55f157b0b..67a3004713e2e 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_enabled.ts +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_enabled.ts @@ -11,9 +11,10 @@ import type { LicensingPluginStart } from '@kbn/licensing-plugin/public'; import { firstValueFrom } from 'rxjs'; export function getPatternAnalysisAvailable(licensing: LicensingPluginStart) { + const lic = firstValueFrom(licensing.license$); return async (dataView: DataView) => { const hasTextFields = dataView.fields.some((f) => f.esTypes?.includes(ES_FIELD_TYPES.TEXT)); - const isPlatinum = (await firstValueFrom(licensing.license$)).hasAtLeast('platinum'); + const isPlatinum = (await lic).hasAtLeast('platinum'); return isPlatinum && hasTextFields; }; } From efd50516260d5a7d2a166cdef5e9d6f8fe97f1ed Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 9 May 2024 17:15:10 +0000 Subject: [PATCH 57/92] [CI] Auto-commit changed files from 'node scripts/jest_integration -u src/core/server/integration_tests/ci_checks' --- .../ci_checks/saved_objects/check_registered_types.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts index 2fa22873190e5..8fce10320b655 100644 --- a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts +++ b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts @@ -137,7 +137,7 @@ describe('checking migration metadata changes on all registered SO types', () => "risk-engine-configuration": "aea0c371a462e6d07c3ceb3aff11891b47feb09d", "rules-settings": "892a2918ebaeba809a612b8d97cec0b07c800b5f", "sample-data-telemetry": "37441b12f5b0159c2d6d5138a494c9f440e950b5", - "search": "7598e4a701ddcaa5e3f44f22e797618a48595e6f", + "search": "4579401660a4089d5122b2fc8624825cb97b0480", "search-session": "b2fcd840e12a45039ada50b1355faeafa39876d1", "search-telemetry": "b568601618744720b5662946d3103e3fb75fe8ee", "security-rule": "07abb4d7e707d91675ec0495c73816394c7b521f", From 5647d7fd850992b44e2d99394da5d3b7c94d6932 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 9 May 2024 18:43:26 +0100 Subject: [PATCH 58/92] fixing tabs when field stats is disabled --- .../main/components/layout/discover_layout.tsx | 3 +++ .../apps/discover/group6/_view_mode_toggle.ts | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx index 24c92fd192408..f867abedab688 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx @@ -86,6 +86,9 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) { ]); const isPlainRecord = useMemo(() => getRawRecordType(query) === RecordRawType.PLAIN, [query]); const viewMode: VIEW_MODE = useAppStateSelector((state) => { + if (state.viewMode === VIEW_MODE.DOCUMENT_LEVEL || state.viewMode === VIEW_MODE.PATTERN_LEVEL) { + return state.viewMode; + } if (uiSettings.get(SHOW_FIELD_STATISTICS) !== true || isPlainRecord) return VIEW_MODE.DOCUMENT_LEVEL; return state.viewMode ?? VIEW_MODE.DOCUMENT_LEVEL; diff --git a/test/functional/apps/discover/group6/_view_mode_toggle.ts b/test/functional/apps/discover/group6/_view_mode_toggle.ts index 935b75bf61937..26f7323452e5f 100644 --- a/test/functional/apps/discover/group6/_view_mode_toggle.ts +++ b/test/functional/apps/discover/group6/_view_mode_toggle.ts @@ -99,6 +99,17 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.existOrFail('dscViewModeToggle'); }); + it('should show Patterns tab', async () => { + await testSubjects.click('dscViewModePatternAnalysisButton'); + + await retry.try(async () => { + const fieldStatsTab = await testSubjects.find('dscViewModePatternAnalysisButton'); + expect(await fieldStatsTab.getAttribute('aria-selected')).to.be('true'); + }); + + await testSubjects.existOrFail('dscViewModeToggle'); + }); + it('should not show view mode toggle for text-based searches', async () => { await testSubjects.click('dscViewModeDocumentButton'); From 618710a8efd8a0d4fad56dd71c9ff8d08ed3bc95 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 9 May 2024 19:37:20 +0100 Subject: [PATCH 59/92] fixing non time based data views --- .../layout/discover_main_content.test.tsx | 5 +++ .../use_test_based_query_language.test.tsx | 12 +++++++ .../utils/get_state_defaults.test.ts | 33 ++++++++++++++++--- .../main/utils/get_valid_view_mode.test.ts | 14 ++++++++ .../main/utils/get_valid_view_mode.ts | 7 ++-- .../log_categorization_enabled.ts | 2 +- 6 files changed, 65 insertions(+), 8 deletions(-) diff --git a/src/plugins/discover/public/application/main/components/layout/discover_main_content.test.tsx b/src/plugins/discover/public/application/main/components/layout/discover_main_content.test.tsx index 271b9aa70effc..96b60c1d8af2e 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_main_content.test.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_main_content.test.tsx @@ -162,6 +162,11 @@ describe('Discover main content component', () => { expect(component.find(DocumentViewModeToggle).exists()).toBe(true); }); + it('should show DocumentViewModeToggle for Patterns', async () => { + const component = await mountComponent({ viewMode: VIEW_MODE.PATTERN_LEVEL }); + expect(component.find(DocumentViewModeToggle).exists()).toBe(true); + }); + it('should include PanelsToggle when chart is available', async () => { const component = await mountComponent({ isChartAvailable: true }); expect(component.find(PanelsToggle).prop('isChartAvailable')).toBe(true); diff --git a/src/plugins/discover/public/application/main/hooks/use_test_based_query_language.test.tsx b/src/plugins/discover/public/application/main/hooks/use_test_based_query_language.test.tsx index c7d530360ca3e..ef637c8538c40 100644 --- a/src/plugins/discover/public/application/main/hooks/use_test_based_query_language.test.tsx +++ b/src/plugins/discover/public/application/main/hooks/use_test_based_query_language.test.tsx @@ -117,6 +117,18 @@ describe('useTextBasedQueryLanguage', () => { viewMode: undefined, }); }); + + test('should change viewMode to undefined (default) if it was PATTERN_LEVEL', async () => { + const { replaceUrlState } = renderHookWithContext(false, { + viewMode: VIEW_MODE.PATTERN_LEVEL, + }); + + await waitFor(() => expect(replaceUrlState).toHaveBeenCalledTimes(1)); + expect(replaceUrlState).toHaveBeenCalledWith({ + viewMode: undefined, + }); + }); + test('changing a text based query with different result columns should change state when loading and finished', async () => { const { replaceUrlState, stateContainer } = renderHookWithContext(false); const documents$ = stateContainer.dataState.data$.documents$; diff --git a/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.test.ts b/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.test.ts index 0a862b712186a..ba06cf3767c2f 100644 --- a/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.test.ts +++ b/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.test.ts @@ -91,14 +91,25 @@ describe('getStateDefaults', () => { }); expect(actualForUndefinedViewMode.viewMode).toBeUndefined(); - const actualForTextBasedWithInvalidViewMode = getStateDefaults({ + const actualForTextBasedWithInvalidAggLevelViewMode = getStateDefaults({ services: discoverServiceMock, savedSearch: { ...savedSearchMockWithESQL, viewMode: VIEW_MODE.AGGREGATED_LEVEL, }, }); - expect(actualForTextBasedWithInvalidViewMode.viewMode).toBe(VIEW_MODE.DOCUMENT_LEVEL); + expect(actualForTextBasedWithInvalidAggLevelViewMode.viewMode).toBe(VIEW_MODE.DOCUMENT_LEVEL); + + const actualForTextBasedWithInvalidPatternLevelViewMode = getStateDefaults({ + services: discoverServiceMock, + savedSearch: { + ...savedSearchMockWithESQL, + viewMode: VIEW_MODE.PATTERN_LEVEL, + }, + }); + expect(actualForTextBasedWithInvalidPatternLevelViewMode.viewMode).toBe( + VIEW_MODE.DOCUMENT_LEVEL + ); const actualForTextBasedWithValidViewMode = getStateDefaults({ services: discoverServiceMock, @@ -110,15 +121,27 @@ describe('getStateDefaults', () => { expect(actualForTextBasedWithValidViewMode.viewMode).toBe(VIEW_MODE.DOCUMENT_LEVEL); expect(actualForTextBasedWithValidViewMode.index).toBe(undefined); - const actualForWithValidViewMode = getStateDefaults({ + const actualForWithValidAggLevelViewMode = getStateDefaults({ services: discoverServiceMock, savedSearch: { ...savedSearchMock, viewMode: VIEW_MODE.AGGREGATED_LEVEL, }, }); - expect(actualForWithValidViewMode.viewMode).toBe(VIEW_MODE.AGGREGATED_LEVEL); - expect(actualForWithValidViewMode.index).toBe( + expect(actualForWithValidAggLevelViewMode.viewMode).toBe(VIEW_MODE.AGGREGATED_LEVEL); + expect(actualForWithValidAggLevelViewMode.index).toBe( + savedSearchMock.searchSource.getField('index')?.id + ); + + const actualForWithValidPatternLevelViewMode = getStateDefaults({ + services: discoverServiceMock, + savedSearch: { + ...savedSearchMock, + viewMode: VIEW_MODE.PATTERN_LEVEL, + }, + }); + expect(actualForWithValidPatternLevelViewMode.viewMode).toBe(VIEW_MODE.PATTERN_LEVEL); + expect(actualForWithValidPatternLevelViewMode.index).toBe( savedSearchMock.searchSource.getField('index')?.id ); }); diff --git a/src/plugins/discover/public/application/main/utils/get_valid_view_mode.test.ts b/src/plugins/discover/public/application/main/utils/get_valid_view_mode.test.ts index 33431ba09b7c5..389a6e9fabdc2 100644 --- a/src/plugins/discover/public/application/main/utils/get_valid_view_mode.test.ts +++ b/src/plugins/discover/public/application/main/utils/get_valid_view_mode.test.ts @@ -31,6 +31,13 @@ describe('getValidViewMode', () => { isTextBasedQueryMode: false, }) ).toBe(VIEW_MODE.AGGREGATED_LEVEL); + + expect( + getValidViewMode({ + viewMode: VIEW_MODE.PATTERN_LEVEL, + isTextBasedQueryMode: false, + }) + ).toBe(VIEW_MODE.PATTERN_LEVEL); }); test('should work correctly for text-based mode', () => { @@ -54,5 +61,12 @@ describe('getValidViewMode', () => { isTextBasedQueryMode: true, }) ).toBe(VIEW_MODE.DOCUMENT_LEVEL); + + expect( + getValidViewMode({ + viewMode: VIEW_MODE.PATTERN_LEVEL, + isTextBasedQueryMode: true, + }) + ).toBe(VIEW_MODE.DOCUMENT_LEVEL); }); }); diff --git a/src/plugins/discover/public/application/main/utils/get_valid_view_mode.ts b/src/plugins/discover/public/application/main/utils/get_valid_view_mode.ts index 1b1f6b465c054..32d843082677d 100644 --- a/src/plugins/discover/public/application/main/utils/get_valid_view_mode.ts +++ b/src/plugins/discover/public/application/main/utils/get_valid_view_mode.ts @@ -20,9 +20,12 @@ export const getValidViewMode = ({ viewMode?: VIEW_MODE; isTextBasedQueryMode: boolean; }): VIEW_MODE | undefined => { - if (viewMode === VIEW_MODE.AGGREGATED_LEVEL && isTextBasedQueryMode) { + if ( + (viewMode === VIEW_MODE.PATTERN_LEVEL || viewMode === VIEW_MODE.AGGREGATED_LEVEL) && + isTextBasedQueryMode + ) { // only this mode is supported for text-based languages - return VIEW_MODE.DOCUMENT_LEVEL; // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + return VIEW_MODE.DOCUMENT_LEVEL; } return viewMode; diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_enabled.ts b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_enabled.ts index 67a3004713e2e..ef81d33ff8776 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_enabled.ts +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_enabled.ts @@ -15,6 +15,6 @@ export function getPatternAnalysisAvailable(licensing: LicensingPluginStart) { return async (dataView: DataView) => { const hasTextFields = dataView.fields.some((f) => f.esTypes?.includes(ES_FIELD_TYPES.TEXT)); const isPlatinum = (await lic).hasAtLeast('platinum'); - return isPlatinum && hasTextFields; + return isPlatinum && hasTextFields && dataView.isTimeBased(); }; } From f02edf9caf77141be59d6cc94e371ae30bb17eaa Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 9 May 2024 20:24:59 +0100 Subject: [PATCH 60/92] updating tests --- .../discover/public/__mocks__/services.ts | 4 ++ .../view_mode_toggle.test.tsx | 68 ++++++++++++------- 2 files changed, 49 insertions(+), 23 deletions(-) diff --git a/src/plugins/discover/public/__mocks__/services.ts b/src/plugins/discover/public/__mocks__/services.ts index 2a0ffa9f7cb97..0b61e1e05b027 100644 --- a/src/plugins/discover/public/__mocks__/services.ts +++ b/src/plugins/discover/public/__mocks__/services.ts @@ -156,6 +156,10 @@ export function createDiscoverServicesMock(): DiscoverServices { dataVisualizer: { FieldStatisticsTable: jest.fn(() => createElement('div')), }, + aiops: { + patternAnalysisAvailable: jest.fn().mockResolvedValue(true), + PatternAnalysisComponent: jest.fn(() => createElement('div')), + }, docLinks: docLinksServiceMock.createStartContract(), capabilities: { visualize: { diff --git a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx index eeba9cc448ffe..56ee98324bbdf 100644 --- a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx +++ b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx @@ -20,9 +20,10 @@ import { DataTotalHits$ } from '../../application/main/state_management/discover import { FetchStatus } from '../../application/types'; import { ES_FIELD_TYPES } from '@kbn/field-types'; import { discoverServiceMock } from '../../__mocks__/services'; +import { act } from 'react-dom/test-utils'; describe('Document view mode toggle component', () => { - const mountComponent = ({ + const mountComponent = async ({ showFieldStatistics = true, viewMode = VIEW_MODE.DOCUMENT_LEVEL, isTextBasedQuery = false, @@ -34,6 +35,9 @@ describe('Document view mode toggle component', () => { uiSettings: { get: () => showFieldStatistics, }, + aiops: { + patternAnalysisAvailable: jest.fn().mockResolvedValue(useDataViewWithTextFields), + }, }; const dataViewWithTextFields = { @@ -60,7 +64,7 @@ describe('Document view mode toggle component', () => { result: 10, }) as DataTotalHits$; - return mountWithIntl( + const component = mountWithIntl( { /> ); + + await act(async () => { + component.update(); + }); + component!.update(); + return component!; }; - it('should render if SHOW_FIELD_STATISTICS is true', () => { - const component = mountComponent(); + it('should render if SHOW_FIELD_STATISTICS is true', async () => { + const component = await mountComponent(); expect(findTestSubject(component, 'dscViewModeToggle').exists()).toBe(true); expect(findTestSubject(component, 'discoverQueryTotalHits').exists()).toBe(true); + + expect(findTestSubject(component, 'dscViewModeDocumentButton').exists()).toBe(true); + expect(findTestSubject(component, 'dscViewModePatternAnalysisButton').exists()).toBe(true); + expect(findTestSubject(component, 'dscViewModeFieldStatsButton').exists()).toBe(true); }); - it('should not render if SHOW_FIELD_STATISTICS is false', () => { - const component = mountComponent({ showFieldStatistics: false }); - expect(findTestSubject(component, 'dscViewModeToggle').exists()).toBe(false); + it('should not render if SHOW_FIELD_STATISTICS is false', async () => { + const component = await mountComponent({ showFieldStatistics: false }); + expect(findTestSubject(component, 'dscViewModeToggle').exists()).toBe(true); expect(findTestSubject(component, 'discoverQueryTotalHits').exists()).toBe(true); + + expect(findTestSubject(component, 'dscViewModeDocumentButton').exists()).toBe(true); + expect(findTestSubject(component, 'dscViewModePatternAnalysisButton').exists()).toBe(true); + expect(findTestSubject(component, 'dscViewModeFieldStatsButton').exists()).toBe(false); }); - it('should not render if text-based', () => { - const component = mountComponent({ isTextBasedQuery: true }); + it('should not render if text-based', async () => { + const component = await mountComponent({ isTextBasedQuery: true }); expect(findTestSubject(component, 'dscViewModeToggle').exists()).toBe(false); expect(findTestSubject(component, 'discoverQueryTotalHits').exists()).toBe(true); + + expect(findTestSubject(component, 'dscViewModeDocumentButton').exists()).toBe(false); + expect(findTestSubject(component, 'dscViewModePatternAnalysisButton').exists()).toBe(false); + expect(findTestSubject(component, 'dscViewModeFieldStatsButton').exists()).toBe(false); }); - it('should set the view mode to VIEW_MODE.DOCUMENT_LEVEL when dscViewModeDocumentButton is clicked', () => { + it('should set the view mode to VIEW_MODE.DOCUMENT_LEVEL when dscViewModeDocumentButton is clicked', async () => { const setDiscoverViewMode = jest.fn(); - const component = mountComponent({ setDiscoverViewMode }); + const component = await mountComponent({ setDiscoverViewMode }); component.find('[data-test-subj="dscViewModeDocumentButton"]').at(0).simulate('click'); expect(setDiscoverViewMode).toHaveBeenCalledWith(VIEW_MODE.DOCUMENT_LEVEL); }); - it('should set the view mode to VIEW_MODE.PATTERN_LEVEL when dscViewModePatternAnalysisButton is clicked', () => { + it('should set the view mode to VIEW_MODE.PATTERN_LEVEL when dscViewModePatternAnalysisButton is clicked', async () => { const setDiscoverViewMode = jest.fn(); - const component = mountComponent({ setDiscoverViewMode }); + const component = await mountComponent({ setDiscoverViewMode }); component.find('[data-test-subj="dscViewModePatternAnalysisButton"]').at(0).simulate('click'); expect(setDiscoverViewMode).toHaveBeenCalledWith(VIEW_MODE.PATTERN_LEVEL); }); - it('should set the view mode to VIEW_MODE.AGGREGATED_LEVEL when dscViewModeFieldStatsButton is clicked', () => { + it('should set the view mode to VIEW_MODE.AGGREGATED_LEVEL when dscViewModeFieldStatsButton is clicked', async () => { const setDiscoverViewMode = jest.fn(); - const component = mountComponent({ setDiscoverViewMode }); + const component = await mountComponent({ setDiscoverViewMode }); component.find('[data-test-subj="dscViewModeFieldStatsButton"]').at(0).simulate('click'); expect(setDiscoverViewMode).toHaveBeenCalledWith(VIEW_MODE.AGGREGATED_LEVEL); }); - it('should select the Documents tab if viewMode is VIEW_MODE.DOCUMENT_LEVEL', () => { - const component = mountComponent(); + it('should select the Documents tab if viewMode is VIEW_MODE.DOCUMENT_LEVEL', async () => { + const component = await mountComponent(); expect(component.find(EuiTab).at(0).prop('isSelected')).toBe(true); }); - it('should select the Pattern Analysis tab if viewMode is VIEW_MODE.PATTERN_LEVEL', () => { - const component = mountComponent({ viewMode: VIEW_MODE.PATTERN_LEVEL }); + it('should select the Pattern Analysis tab if viewMode is VIEW_MODE.PATTERN_LEVEL', async () => { + const component = await mountComponent({ viewMode: VIEW_MODE.PATTERN_LEVEL }); expect(component.find(EuiTab).at(1).prop('isSelected')).toBe(true); }); - it('should select the Field statistics tab if viewMode is VIEW_MODE.AGGREGATED_LEVEL', () => { - const component = mountComponent({ viewMode: VIEW_MODE.AGGREGATED_LEVEL }); + it('should select the Field statistics tab if viewMode is VIEW_MODE.AGGREGATED_LEVEL', async () => { + const component = await mountComponent({ viewMode: VIEW_MODE.AGGREGATED_LEVEL }); expect(component.find(EuiTab).at(2).prop('isSelected')).toBe(true); }); - it('should switch to document and hide pattern tab when there are no text fields', () => { + it('should switch to document and hide pattern tab when there are no text fields', async () => { const setDiscoverViewMode = jest.fn(); - const component = mountComponent({ + const component = await mountComponent({ viewMode: VIEW_MODE.PATTERN_LEVEL, useDataViewWithTextFields: false, setDiscoverViewMode, From da0a22949648055636d0e7f6167caa840f766282 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 10 May 2024 09:43:28 +0100 Subject: [PATCH 61/92] fixing bundle size --- .../components/view_mode_toggle/view_mode_toggle.tsx | 12 +++++++----- x-pack/plugins/aiops/public/plugin.tsx | 9 +++++++-- x-pack/plugins/aiops/public/types/index.ts | 2 +- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx index 6bd3bc5ab7ca5..45590be1a3dbb 100644 --- a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx +++ b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx @@ -57,11 +57,13 @@ export const DocumentViewModeToggle = ({ return; } aiopsService - .patternAnalysisAvailable(dataView) - .then(setShowPatternAnalysisTab) - .catch(() => { - setShowPatternAnalysisTab(false); - }); + .getPatternAnalysisAvailable() + .then((patternAnalysisAvailable) => { + patternAnalysisAvailable(dataView) + .then(setShowPatternAnalysisTab) + .catch(() => setShowPatternAnalysisTab(false)); + }) + .catch(() => setShowPatternAnalysisTab(false)); }, [aiopsService, dataView] ); diff --git a/x-pack/plugins/aiops/public/plugin.tsx b/x-pack/plugins/aiops/public/plugin.tsx index c77ce0e6beb75..83e42d86129cb 100755 --- a/x-pack/plugins/aiops/public/plugin.tsx +++ b/x-pack/plugins/aiops/public/plugin.tsx @@ -10,6 +10,7 @@ import { type CoreSetup } from '@kbn/core/public'; import { firstValueFrom } from 'rxjs'; import { EMBEDDABLE_CHANGE_POINT_CHART_TYPE } from '@kbn/aiops-change-point-detection/constants'; import { dynamic } from '@kbn/shared-ux-utility'; + import type { AiopsPluginSetup, AiopsPluginSetupDeps, @@ -17,7 +18,6 @@ import type { AiopsPluginStartDeps, } from './types'; import { getEmbeddableChangePointChart } from './embeddable/embeddable_change_point_chart_component'; -import { getPatternAnalysisAvailable } from './components/log_categorization/log_categorization_enabled'; export type AiopsCoreSetup = CoreSetup; @@ -66,7 +66,12 @@ export class AiopsPlugin core, plugins ), - patternAnalysisAvailable: getPatternAnalysisAvailable(plugins.licensing), + getPatternAnalysisAvailable: async () => { + const { getPatternAnalysisAvailable } = await import( + './components/log_categorization/log_categorization_enabled' + ); + return getPatternAnalysisAvailable(plugins.licensing); + }, PatternAnalysisComponent: dynamic( async () => import( diff --git a/x-pack/plugins/aiops/public/types/index.ts b/x-pack/plugins/aiops/public/types/index.ts index d0e93e9aeb416..964466478837b 100644 --- a/x-pack/plugins/aiops/public/types/index.ts +++ b/x-pack/plugins/aiops/public/types/index.ts @@ -48,6 +48,6 @@ export interface AiopsPluginStartDeps { export type AiopsPluginSetup = void; export interface AiopsPluginStart { EmbeddableChangePointChart: React.ComponentType; - patternAnalysisAvailable: (dataView: DataView) => Promise; + getPatternAnalysisAvailable: () => Promise<(dataView: DataView) => Promise>; PatternAnalysisComponent: React.ComponentType; } From 53021cf80bead921f5c093e0d81ab0c959f2ee48 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 10 May 2024 10:32:11 +0100 Subject: [PATCH 62/92] updating mocks --- src/plugins/discover/public/__mocks__/services.ts | 2 +- .../main/components/layout/discover_main_content.test.tsx | 5 ----- .../components/view_mode_toggle/view_mode_toggle.test.tsx | 4 +++- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/plugins/discover/public/__mocks__/services.ts b/src/plugins/discover/public/__mocks__/services.ts index 0b61e1e05b027..e4e2b71de8e74 100644 --- a/src/plugins/discover/public/__mocks__/services.ts +++ b/src/plugins/discover/public/__mocks__/services.ts @@ -157,7 +157,7 @@ export function createDiscoverServicesMock(): DiscoverServices { FieldStatisticsTable: jest.fn(() => createElement('div')), }, aiops: { - patternAnalysisAvailable: jest.fn().mockResolvedValue(true), + getPatternAnalysisAvailable: jest.fn().mockResolvedValue(jest.fn().mockResolvedValue(true)), PatternAnalysisComponent: jest.fn(() => createElement('div')), }, docLinks: docLinksServiceMock.createStartContract(), diff --git a/src/plugins/discover/public/application/main/components/layout/discover_main_content.test.tsx b/src/plugins/discover/public/application/main/components/layout/discover_main_content.test.tsx index 96b60c1d8af2e..271b9aa70effc 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_main_content.test.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_main_content.test.tsx @@ -162,11 +162,6 @@ describe('Discover main content component', () => { expect(component.find(DocumentViewModeToggle).exists()).toBe(true); }); - it('should show DocumentViewModeToggle for Patterns', async () => { - const component = await mountComponent({ viewMode: VIEW_MODE.PATTERN_LEVEL }); - expect(component.find(DocumentViewModeToggle).exists()).toBe(true); - }); - it('should include PanelsToggle when chart is available', async () => { const component = await mountComponent({ isChartAvailable: true }); expect(component.find(PanelsToggle).prop('isChartAvailable')).toBe(true); diff --git a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx index 56ee98324bbdf..01694d55100f0 100644 --- a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx +++ b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx @@ -36,7 +36,9 @@ describe('Document view mode toggle component', () => { get: () => showFieldStatistics, }, aiops: { - patternAnalysisAvailable: jest.fn().mockResolvedValue(useDataViewWithTextFields), + getPatternAnalysisAvailable: jest + .fn() + .mockResolvedValue(jest.fn().mockResolvedValue(useDataViewWithTextFields)), }, }; From 1415bc3bae8e2ef6070ddd953622a1054997711b Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 10 May 2024 11:00:07 +0100 Subject: [PATCH 63/92] fixing tests --- test/functional/apps/discover/group6/_view_mode_toggle.ts | 6 +++--- .../index_data_visualizer_grid_in_discover.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/functional/apps/discover/group6/_view_mode_toggle.ts b/test/functional/apps/discover/group6/_view_mode_toggle.ts index 26f7323452e5f..bdea5a8b1e508 100644 --- a/test/functional/apps/discover/group6/_view_mode_toggle.ts +++ b/test/functional/apps/discover/group6/_view_mode_toggle.ts @@ -99,15 +99,15 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.existOrFail('dscViewModeToggle'); }); - it('should show Patterns tab', async () => { + it('should not show Patterns tab (basic license)', async () => { await testSubjects.click('dscViewModePatternAnalysisButton'); await retry.try(async () => { const fieldStatsTab = await testSubjects.find('dscViewModePatternAnalysisButton'); - expect(await fieldStatsTab.getAttribute('aria-selected')).to.be('true'); + expect(await fieldStatsTab.getAttribute('aria-selected')).to.be('false'); }); - await testSubjects.existOrFail('dscViewModeToggle'); + await testSubjects.missingOrFail('dscViewModeToggle'); }); it('should not show view mode toggle for text-based searches', async () => { diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover.ts index 7c1e2a1a8f946..bcb4d6af09fb8 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover.ts @@ -29,7 +29,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const endTime = 'Nov 1, 2020 @ 00:00:00.000'; function runTestsWhenDisabled(testData: TestData) { - it('should not show view mode toggle or Field stats table', async function () { + it('should show view mode toggle but not Field stats tab', async function () { await PageObjects.common.navigateToApp('discover'); if (testData.isSavedSearch) { await retry.tryForTime(2 * 1000, async () => { @@ -41,7 +41,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.timePicker.setAbsoluteRange(startTime, endTime); - await PageObjects.discover.assertViewModeToggleNotExists(); + await PageObjects.discover.assertViewModeToggleExists(); await PageObjects.discover.assertFieldStatsTableNotExists(); }); } From 523b8e2b2055c95e6383dd6a70a4fc67eb06c394 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 10 May 2024 13:58:08 +0100 Subject: [PATCH 64/92] splitting data viz tests for trial and basic --- .../apps/ml/data_visualizer/index.ts | 1 + ..._data_visualizer_grid_in_discover_basic.ts | 72 +++++++++++++++++++ ..._data_visualizer_grid_in_discover_trial.ts | 72 +++++++++++++++++++ .../apps/ml/data_visualizer/group3/index.ts | 5 ++ 4 files changed, 150 insertions(+) create mode 100644 x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover_basic.ts create mode 100644 x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover_trial.ts diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index.ts b/x-pack/test/functional/apps/ml/data_visualizer/index.ts index e5b1cbab24809..bddbcdfb95370 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/index.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/index.ts @@ -35,6 +35,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./index_data_visualizer_random_sampler')); loadTestFile(require.resolve('./index_data_visualizer_filters')); loadTestFile(require.resolve('./index_data_visualizer_grid_in_discover')); + loadTestFile(require.resolve('./index_data_visualizer_grid_in_discover_trial')); loadTestFile(require.resolve('./index_data_visualizer_grid_in_dashboard')); loadTestFile(require.resolve('./index_data_visualizer_actions_panel')); loadTestFile(require.resolve('./index_data_visualizer_data_view_management')); diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover_basic.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover_basic.ts new file mode 100644 index 0000000000000..6322991b6a3ad --- /dev/null +++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover_basic.ts @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { TestData } from './types'; + +const SHOW_FIELD_STATISTICS = 'discover:showFieldStatistics'; +import { farequoteDataViewTestData } from './index_test_data'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const PageObjects = getPageObjects(['common', 'discover', 'timePicker', 'settings']); + const ml = getService('ml'); + const retry = getService('retry'); + const dataViews = getService('dataViews'); + + const startTime = 'Jan 1, 2016 @ 00:00:00.000'; + const endTime = 'Nov 1, 2020 @ 00:00:00.000'; + + function runTestsWhenDisabled(testData: TestData) { + it('should not show view mode toggle or Field stats table', async function () { + await PageObjects.common.navigateToApp('discover'); + if (testData.isSavedSearch) { + await retry.tryForTime(2 * 1000, async () => { + await PageObjects.discover.loadSavedSearch(testData.sourceIndexOrSavedSearch); + }); + } else { + await dataViews.switchToAndValidate(testData.sourceIndexOrSavedSearch); + } + + await PageObjects.timePicker.setAbsoluteRange(startTime, endTime); + + await PageObjects.discover.assertViewModeToggleNotExists(); + await PageObjects.discover.assertFieldStatsTableNotExists(); + }); + } + + describe('field statistics in Discover', function () { + before(async function () { + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/module_sample_logs'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_module_sample_logs', '@timestamp'); + await ml.testResources.createSavedSearchFarequoteKueryIfNeeded(); + await ml.testResources.createSavedSearchFarequoteLuceneIfNeeded(); + await ml.testResources.createSavedSearchFarequoteFilterAndLuceneIfNeeded(); + await ml.testResources.createSavedSearchFarequoteFilterAndKueryIfNeeded(); + + await ml.securityUI.loginAsMlPowerUser(); + }); + + after(async function () { + await ml.testResources.clearAdvancedSettingProperty(SHOW_FIELD_STATISTICS); + await ml.testResources.deleteSavedSearches(); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_module_sample_logs'); + }); + + describe('when disabled', function () { + before(async function () { + // Ensure that the setting is set to default state which is false + await ml.testResources.setAdvancedSettingProperty(SHOW_FIELD_STATISTICS, false); + }); + + runTestsWhenDisabled(farequoteDataViewTestData); + }); + }); +} diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover_trial.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover_trial.ts new file mode 100644 index 0000000000000..4ce3775fb225c --- /dev/null +++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover_trial.ts @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { TestData } from './types'; + +const SHOW_FIELD_STATISTICS = 'discover:showFieldStatistics'; +import { farequoteDataViewTestData } from './index_test_data'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const PageObjects = getPageObjects(['common', 'discover', 'timePicker', 'settings']); + const ml = getService('ml'); + const retry = getService('retry'); + const dataViews = getService('dataViews'); + + const startTime = 'Jan 1, 2016 @ 00:00:00.000'; + const endTime = 'Nov 1, 2020 @ 00:00:00.000'; + + function runTestsWhenDisabled(testData: TestData) { + it('should show view mode toggle but not Field stats tab', async function () { + await PageObjects.common.navigateToApp('discover'); + if (testData.isSavedSearch) { + await retry.tryForTime(2 * 1000, async () => { + await PageObjects.discover.loadSavedSearch(testData.sourceIndexOrSavedSearch); + }); + } else { + await dataViews.switchToAndValidate(testData.sourceIndexOrSavedSearch); + } + + await PageObjects.timePicker.setAbsoluteRange(startTime, endTime); + + await PageObjects.discover.assertViewModeToggleExists(); + await PageObjects.discover.assertFieldStatsTableNotExists(); + }); + } + + describe('field statistics in Discover', function () { + before(async function () { + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/module_sample_logs'); + await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.createDataViewIfNeeded('ft_module_sample_logs', '@timestamp'); + await ml.testResources.createSavedSearchFarequoteKueryIfNeeded(); + await ml.testResources.createSavedSearchFarequoteLuceneIfNeeded(); + await ml.testResources.createSavedSearchFarequoteFilterAndLuceneIfNeeded(); + await ml.testResources.createSavedSearchFarequoteFilterAndKueryIfNeeded(); + + await ml.securityUI.loginAsMlPowerUser(); + }); + + after(async function () { + await ml.testResources.clearAdvancedSettingProperty(SHOW_FIELD_STATISTICS); + await ml.testResources.deleteSavedSearches(); + await ml.testResources.deleteDataViewByTitle('ft_farequote'); + await ml.testResources.deleteDataViewByTitle('ft_module_sample_logs'); + }); + + describe('when disabled', function () { + before(async function () { + // Ensure that the setting is set to default state which is false + await ml.testResources.setAdvancedSettingProperty(SHOW_FIELD_STATISTICS, false); + }); + + runTestsWhenDisabled(farequoteDataViewTestData); + }); + }); +} diff --git a/x-pack/test/functional_basic/apps/ml/data_visualizer/group3/index.ts b/x-pack/test/functional_basic/apps/ml/data_visualizer/group3/index.ts index d2e77f9522854..3388181aea589 100644 --- a/x-pack/test/functional_basic/apps/ml/data_visualizer/group3/index.ts +++ b/x-pack/test/functional_basic/apps/ml/data_visualizer/group3/index.ts @@ -41,6 +41,11 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { '../../../../../functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover' ) ); + loadTestFile( + require.resolve( + '../../../../../functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover_basic' + ) + ); loadTestFile(require.resolve('./index_data_visualizer_actions_panel')); }); } From ca1ad4f0fe4eb5e4bd5e06e43abdebed6e06adfa Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 10 May 2024 13:58:53 +0100 Subject: [PATCH 65/92] fixing test --- test/functional/apps/discover/group6/_view_mode_toggle.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/functional/apps/discover/group6/_view_mode_toggle.ts b/test/functional/apps/discover/group6/_view_mode_toggle.ts index bdea5a8b1e508..f32a50dff3dd6 100644 --- a/test/functional/apps/discover/group6/_view_mode_toggle.ts +++ b/test/functional/apps/discover/group6/_view_mode_toggle.ts @@ -102,12 +102,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should not show Patterns tab (basic license)', async () => { await testSubjects.click('dscViewModePatternAnalysisButton'); + await testSubjects.missingOrFail('dscViewModePatternAnalysisButton'); await retry.try(async () => { - const fieldStatsTab = await testSubjects.find('dscViewModePatternAnalysisButton'); - expect(await fieldStatsTab.getAttribute('aria-selected')).to.be('false'); + const fieldStatsTab = await testSubjects.find('dscViewModeDocumentButton'); + expect(await fieldStatsTab.getAttribute('aria-selected')).to.be('true'); }); - - await testSubjects.missingOrFail('dscViewModeToggle'); }); it('should not show view mode toggle for text-based searches', async () => { From 8c83f02dbbd09d39c5fa0a8e44b10fac695670ad Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Sun, 12 May 2024 08:33:21 +0100 Subject: [PATCH 66/92] fixing test --- test/functional/apps/discover/group6/_view_mode_toggle.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/functional/apps/discover/group6/_view_mode_toggle.ts b/test/functional/apps/discover/group6/_view_mode_toggle.ts index f32a50dff3dd6..7231faa4d0bc2 100644 --- a/test/functional/apps/discover/group6/_view_mode_toggle.ts +++ b/test/functional/apps/discover/group6/_view_mode_toggle.ts @@ -100,8 +100,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('should not show Patterns tab (basic license)', async () => { - await testSubjects.click('dscViewModePatternAnalysisButton'); - await testSubjects.missingOrFail('dscViewModePatternAnalysisButton'); await retry.try(async () => { const fieldStatsTab = await testSubjects.find('dscViewModeDocumentButton'); From 9d172134bd04ac1cb4e5dd9133cab4ed2b18f763 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Mon, 13 May 2024 10:30:23 +0100 Subject: [PATCH 67/92] removing test which has been replaced by two different license versions --- .../index_data_visualizer_grid_in_discover.ts | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover.ts index bcb4d6af09fb8..14e16d5929779 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover.ts @@ -28,24 +28,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const startTime = 'Jan 1, 2016 @ 00:00:00.000'; const endTime = 'Nov 1, 2020 @ 00:00:00.000'; - function runTestsWhenDisabled(testData: TestData) { - it('should show view mode toggle but not Field stats tab', async function () { - await PageObjects.common.navigateToApp('discover'); - if (testData.isSavedSearch) { - await retry.tryForTime(2 * 1000, async () => { - await PageObjects.discover.loadSavedSearch(testData.sourceIndexOrSavedSearch); - }); - } else { - await dataViews.switchToAndValidate(testData.sourceIndexOrSavedSearch); - } - - await PageObjects.timePicker.setAbsoluteRange(startTime, endTime); - - await PageObjects.discover.assertViewModeToggleExists(); - await PageObjects.discover.assertFieldStatsTableNotExists(); - }); - } - function runTests(testData: TestData) { describe(`with ${testData.suiteTitle}`, function () { it(`displays the 'Field statistics' table content correctly`, async function () { @@ -128,14 +110,5 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { runTests(farequoteLuceneFiltersSearchTestData); runTests(sampleLogTestData); }); - - describe('when disabled', function () { - before(async function () { - // Ensure that the setting is set to default state which is false - await ml.testResources.setAdvancedSettingProperty(SHOW_FIELD_STATISTICS, false); - }); - - runTestsWhenDisabled(farequoteDataViewTestData); - }); }); } From 1328cfcd5b930cdc08445685c12fa6002be525b3 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Mon, 13 May 2024 12:43:13 +0100 Subject: [PATCH 68/92] fixing test --- .../apps/discover/group6/_view_mode_toggle.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/functional/apps/discover/group6/_view_mode_toggle.ts b/test/functional/apps/discover/group6/_view_mode_toggle.ts index 7231faa4d0bc2..4d9395d93a43f 100644 --- a/test/functional/apps/discover/group6/_view_mode_toggle.ts +++ b/test/functional/apps/discover/group6/_view_mode_toggle.ts @@ -88,6 +88,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.missingOrFail('discoverErrorCalloutTitle'); }); + it('should not show Patterns tab (basic license)', async () => { + await testSubjects.missingOrFail('dscViewModePatternAnalysisButton'); + await retry.try(async () => { + const documentTab = await testSubjects.find('dscViewModeDocumentButton'); + expect(await documentTab.getAttribute('aria-selected')).to.be('true'); + }); + }); + it('should show Field Statistics tab', async () => { await testSubjects.click('dscViewModeFieldStatsButton'); @@ -99,14 +107,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.existOrFail('dscViewModeToggle'); }); - it('should not show Patterns tab (basic license)', async () => { - await testSubjects.missingOrFail('dscViewModePatternAnalysisButton'); - await retry.try(async () => { - const fieldStatsTab = await testSubjects.find('dscViewModeDocumentButton'); - expect(await fieldStatsTab.getAttribute('aria-selected')).to.be('true'); - }); - }); - it('should not show view mode toggle for text-based searches', async () => { await testSubjects.click('dscViewModeDocumentButton'); From 869adb30860e848d7fb0ac1f3206c55b56868de6 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Mon, 13 May 2024 12:45:06 +0100 Subject: [PATCH 69/92] skipping flyout test --- .../functional/apps/aiops/log_pattern_analysis_in_discover.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/aiops/log_pattern_analysis_in_discover.ts b/x-pack/test/functional/apps/aiops/log_pattern_analysis_in_discover.ts index 197b5b5338a17..a6d732b28daa4 100644 --- a/x-pack/test/functional/apps/aiops/log_pattern_analysis_in_discover.ts +++ b/x-pack/test/functional/apps/aiops/log_pattern_analysis_in_discover.ts @@ -23,7 +23,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); } - describe('log pattern analysis', async function () { + // temporarily disabled, to be replaced + describe.skip('log pattern analysis', async function () { let tabsCount = 1; afterEach(async () => { From 2e39d1553beb2896af62f0ef680c41a045442757 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Mon, 13 May 2024 15:01:08 +0100 Subject: [PATCH 70/92] updating pattern analysis functional tests --- .../aiops/log_pattern_analysis_in_discover.ts | 29 +++++++------- .../aiops/log_pattern_analysis_page.ts | 39 +++++++++++++++++++ 2 files changed, 52 insertions(+), 16 deletions(-) diff --git a/x-pack/test/functional/apps/aiops/log_pattern_analysis_in_discover.ts b/x-pack/test/functional/apps/aiops/log_pattern_analysis_in_discover.ts index a6d732b28daa4..e97e31ce1651f 100644 --- a/x-pack/test/functional/apps/aiops/log_pattern_analysis_in_discover.ts +++ b/x-pack/test/functional/apps/aiops/log_pattern_analysis_in_discover.ts @@ -14,7 +14,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const retry = getService('retry'); const ml = getService('ml'); const PageObjects = getPageObjects(['common', 'timePicker', 'discover']); - const selectedField = '@message'; const totalDocCount = 14005; async function retrySwitchTab(tabIndex: number, seconds: number) { @@ -24,7 +23,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { } // temporarily disabled, to be replaced - describe.skip('log pattern analysis', async function () { + describe('log pattern analysis', async function () { let tabsCount = 1; afterEach(async () => { @@ -56,15 +55,14 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await PageObjects.discover.selectIndexPattern('logstash-*'); await aiops.logPatternAnalysisPage.assertDiscoverDocCount(totalDocCount); - await aiops.logPatternAnalysisPage.clickDiscoverField(selectedField); - await aiops.logPatternAnalysisPage.clickDiscoverMenuAnalyzeButton(selectedField); + await aiops.logPatternAnalysisPage.clickPatternsTab(); + await aiops.logPatternAnalysisPage.assertLogPatternAnalysisTabContentsExists(); - await aiops.logPatternAnalysisPage.assertLogPatternAnalysisFlyoutExists(); - await aiops.logPatternAnalysisPage.assertLogPatternAnalysisFlyoutTitle(selectedField); - - await aiops.logPatternAnalysisPage.setRandomSamplingOption('aiopsRandomSamplerOptionOff'); + await aiops.logPatternAnalysisPage.setRandomSamplingOptionDiscover( + 'aiopsRandomSamplerOptionOff' + ); - await aiops.logPatternAnalysisPage.assertTotalCategoriesFound(3); + await aiops.logPatternAnalysisPage.assertTotalCategoriesFoundDiscover(3); await aiops.logPatternAnalysisPage.assertCategoryTableRows(3); // get category count from the first row @@ -88,15 +86,14 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { ); await aiops.logPatternAnalysisPage.assertDiscoverDocCount(totalDocCount); - await aiops.logPatternAnalysisPage.clickDiscoverField(selectedField); - await aiops.logPatternAnalysisPage.clickDiscoverMenuAnalyzeButton(selectedField); + await aiops.logPatternAnalysisPage.clickPatternsTab(); + await aiops.logPatternAnalysisPage.assertLogPatternAnalysisTabContentsExists(); - await aiops.logPatternAnalysisPage.assertLogPatternAnalysisFlyoutExists(); - await aiops.logPatternAnalysisPage.assertLogPatternAnalysisFlyoutTitle(selectedField); - - await aiops.logPatternAnalysisPage.setRandomSamplingOption('aiopsRandomSamplerOptionOff'); + await aiops.logPatternAnalysisPage.setRandomSamplingOptionDiscover( + 'aiopsRandomSamplerOptionOff' + ); - await aiops.logPatternAnalysisPage.assertTotalCategoriesFound(3); + await aiops.logPatternAnalysisPage.assertTotalCategoriesFoundDiscover(3); await aiops.logPatternAnalysisPage.assertCategoryTableRows(3); // get category count from the first row diff --git a/x-pack/test/functional/services/aiops/log_pattern_analysis_page.ts b/x-pack/test/functional/services/aiops/log_pattern_analysis_page.ts index 76ca855b4c8ca..b7ab5951af64f 100644 --- a/x-pack/test/functional/services/aiops/log_pattern_analysis_page.ts +++ b/x-pack/test/functional/services/aiops/log_pattern_analysis_page.ts @@ -76,6 +76,17 @@ export function LogPatternAnalysisPageProvider({ getService, getPageObject }: Ft }); }, + async assertTotalCategoriesFoundDiscover(expectedMinimumCategoryCount: number) { + await retry.tryForTime(5000, async () => { + const actualText = await testSubjects.getVisibleText('dscViewModePatternAnalysisButton'); + const actualCount = Number(actualText.match(/Patterns \((.+)\)/)![1]); + expect(actualCount + 1).to.greaterThan( + expectedMinimumCategoryCount, + `Expected patterns found count to be >= '${expectedMinimumCategoryCount}' (got '${actualCount}')` + ); + }); + }, + async assertCategoryTableRows(expectedMinimumCategoryCount: number) { await retry.tryForTime(5000, async () => { const tableListContainer = await testSubjects.find('aiopsLogPatternsTable'); @@ -170,12 +181,22 @@ export function LogPatternAnalysisPageProvider({ getService, getPageObject }: Ft }); }, + async clickPatternsTab() { + await testSubjects.click('dscViewModePatternAnalysisButton'); + }, + async assertLogPatternAnalysisFlyoutExists() { await retry.tryForTime(30 * 1000, async () => { await testSubjects.existOrFail('mlJobSelectorFlyoutBody'); }); }, + async assertLogPatternAnalysisTabContentsExists() { + await retry.tryForTime(30 * 1000, async () => { + await testSubjects.existOrFail('aiopsLogPatternsTable'); + }); + }, + async assertLogPatternAnalysisFlyoutDoesNotExist() { await retry.tryForTime(30 * 1000, async () => { await testSubjects.missingOrFail('mlJobSelectorFlyoutBody'); @@ -210,5 +231,23 @@ export function LogPatternAnalysisPageProvider({ getService, getPageObject }: Ft await testSubjects.missingOrFail('aiopsRandomSamplerOptionsFormRow', { timeout: 1000 }); }); }, + + async setRandomSamplingOptionDiscover(option: RandomSamplerOption) { + await retry.tryForTime(20000, async () => { + await testSubjects.existOrFail('aiopsEmbeddableMenuOptionsButton'); + await testSubjects.clickWhenNotDisabled('aiopsEmbeddableMenuOptionsButton'); + + await testSubjects.clickWhenNotDisabled('aiopsRandomSamplerOptionsSelect'); + + await testSubjects.existOrFail('aiopsRandomSamplerOptionOff', { timeout: 1000 }); + await testSubjects.existOrFail('aiopsRandomSamplerOptionOnManual', { timeout: 1000 }); + await testSubjects.existOrFail('aiopsRandomSamplerOptionOnAutomatic', { timeout: 1000 }); + + await testSubjects.click(option); + + await testSubjects.clickWhenNotDisabled('aiopsEmbeddableMenuOptionsButton'); + await testSubjects.missingOrFail('aiopsRandomSamplerOptionsFormRow', { timeout: 1000 }); + }); + }, }; } From 94b01932f4eecd576d1d42359865731a7161d963 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Mon, 13 May 2024 17:07:16 +0100 Subject: [PATCH 71/92] fixing typo --- .../plugins/aiops/public/components/log_categorization/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/aiops/public/components/log_categorization/utils.ts b/x-pack/plugins/aiops/public/components/log_categorization/utils.ts index c045bf748c2c4..fea54f39e442d 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/utils.ts +++ b/x-pack/plugins/aiops/public/components/log_categorization/utils.ts @@ -47,7 +47,7 @@ export function getMessageField(dataView: DataView): { messageField = dataViewFields.find((f) => f.name === 'error.message'); } if (messageField === undefined) { - messageField = dataViewFields.find((f) => f.name === 'event.original '); + messageField = dataViewFields.find((f) => f.name === 'event.original'); } if (messageField === undefined) { if (dataViewFields.length > 0) { From d5ed385c8fafaec9a24149e96491d154787e01a7 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 15 May 2024 10:41:37 +0100 Subject: [PATCH 72/92] fixing refresh behaviour --- .../main/components/pattern_analysis/pattern_analysis_table.tsx | 2 +- .../functional/apps/aiops/log_pattern_analysis_in_discover.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx index b499ec9b9de80..bda3db4394dd4 100644 --- a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx +++ b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx @@ -63,7 +63,7 @@ export const PatternAnalysisTable = (props: PatternAnalysisTableProps) => { const input: EmbeddablePatternAnalysisInput = Object.assign( {}, pick(props, ['dataView', 'savedSearch', 'query', 'filters', 'onAddFilter']), - lastReloadRequestTime + { lastReloadRequestTime } ); return ( diff --git a/x-pack/test/functional/apps/aiops/log_pattern_analysis_in_discover.ts b/x-pack/test/functional/apps/aiops/log_pattern_analysis_in_discover.ts index e97e31ce1651f..01d2452121cb8 100644 --- a/x-pack/test/functional/apps/aiops/log_pattern_analysis_in_discover.ts +++ b/x-pack/test/functional/apps/aiops/log_pattern_analysis_in_discover.ts @@ -22,7 +22,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); } - // temporarily disabled, to be replaced describe('log pattern analysis', async function () { let tabsCount = 1; From 3ea27fc97da1f99cf63ed5168f19e1b57cf9f8c1 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 15 May 2024 11:29:21 +0100 Subject: [PATCH 73/92] fixing random sampling rounding --- .../log_categorization/sampling_menu/sampling_panel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/sampling_panel.tsx b/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/sampling_panel.tsx index f6c82117215f2..1944039a85e7f 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/sampling_panel.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/sampling_panel.tsx @@ -106,7 +106,7 @@ const ProbabilityUsedMessage: FC<{ samplingProbability: number | null }> = ({
) : null; From c0a1daf83c2f5970948bde2681b1b5ae033d32db Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 15 May 2024 16:20:26 +0100 Subject: [PATCH 74/92] adding tooltip --- .../embeddable_menu.tsx | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx index d1b6db0fd1822..51d46c1dab203 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx @@ -14,6 +14,9 @@ import { EuiSpacer, EuiTitle, EuiHorizontalRule, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, } from '@elastic/eui'; import type { FC } from 'react'; import React, { useState, useMemo } from 'react'; @@ -120,12 +123,31 @@ export const EmbeddableMenu: FC = ({ + + {i18n.translate( + 'xpack.aiops.logCategorization.embeddableMenu.minimumTimeRangeOptionsRowLabel', + { + defaultMessage: 'Minimum time range', + } + )} + + + + + + + + } helpText={ <> {categoryCount !== undefined && minimumTimeRangeOption !== 'No minimum' ? ( From c890187dbc565c8ac852acf472e58c57730c2fb9 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 15 May 2024 16:54:58 +0100 Subject: [PATCH 75/92] remembering pagination --- .../log_categorization/category_table/category_table.tsx | 7 +++++-- .../log_categorization_for_embeddable.tsx | 3 +++ .../log_categorization/log_categorization_for_flyout.tsx | 3 +++ .../log_categorization/log_categorization_page.tsx | 3 +++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx b/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx index f376e5b999576..91e18ea7b4421 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx @@ -18,7 +18,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { useTableState } from '@kbn/ml-in-memory-table'; +import type { UseTableState } from '@kbn/ml-in-memory-table'; import { css } from '@emotion/react'; import { QUERY_MODE } from '@kbn/aiops-log-pattern-analysis/get_category_query'; @@ -43,6 +43,7 @@ interface Props { setHighlightedCategory: (category: Category | null) => void; setSelectedCategories: (category: Category[]) => void; openInDiscover: OpenInDiscover; + tableState: UseTableState; enableRowActions?: boolean; displayExamples?: boolean; } @@ -56,12 +57,14 @@ export const CategoryTable: FC = ({ setHighlightedCategory, setSelectedCategories, openInDiscover, + tableState, enableRowActions = true, displayExamples = true, }) => { const euiTheme = useEuiTheme(); const primaryBackgroundColor = useEuiBackgroundColor('primary'); - const { onTableChange, pagination, sorting } = useTableState(categories ?? [], 'key'); + const { onTableChange, pagination, sorting } = tableState; + const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState>( {} ); diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx index 9506a6d845abb..2e991e14652cf 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx @@ -22,6 +22,7 @@ import type { CategorizationAdditionalFilter } from '@kbn/aiops-log-pattern-anal import { AIOPS_TELEMETRY_ID } from '@kbn/aiops-common/constants'; import type { EmbeddablePatternAnalysisInput } from '@kbn/aiops-log-pattern-analysis/embeddable'; import { css } from '@emotion/react'; +import { useTableState } from '@kbn/ml-in-memory-table/hooks/use_table_state'; import { type LogCategorizationPageUrlState, getDefaultLogCategorizationAppState, @@ -110,6 +111,7 @@ export const LogCategorizationEmbeddable: FC = const [fieldValidationResult, setFieldValidationResult] = useState( null ); + const tableState = useTableState([], 'key'); useEffect( function initFields() { @@ -442,6 +444,7 @@ export const LogCategorizationEmbeddable: FC = displayExamples={data.displayExamples} setSelectedCategories={setSelectedCategories} openInDiscover={openInDiscover} + tableState={tableState} /> ) : null} diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_flyout.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_flyout.tsx index 92b140e0a557a..b68f4d4728daa 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_flyout.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_flyout.tsx @@ -32,6 +32,7 @@ import { AIOPS_TELEMETRY_ID } from '@kbn/aiops-common/constants'; import type { CategorizationAdditionalFilter } from '@kbn/aiops-log-pattern-analysis/create_category_request'; import type { Category } from '@kbn/aiops-log-pattern-analysis/types'; +import { useTableState } from '@kbn/ml-in-memory-table/hooks/use_table_state'; import { type LogCategorizationPageUrlState, getDefaultLogCategorizationAppState, @@ -119,6 +120,7 @@ export const LogCategorizationFlyout: FC = ({ ); const [showTabs, setShowTabs] = useState(false); const [selectedTab, setSelectedTab] = useState(SELECTED_TAB.FULL_TIME_RANGE); + const tableState = useTableState([], 'key'); const cancelRequest = useCallback(() => { cancelValidationRequest(); @@ -412,6 +414,7 @@ export const LogCategorizationFlyout: FC = ({ displayExamples={data.displayExamples} setSelectedCategories={setSelectedCategories} openInDiscover={openInDiscover} + tableState={tableState} /> ) : null} diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx index 9c0d3a874b635..6d8be420fd342 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_page.tsx @@ -31,6 +31,7 @@ import type { SearchQueryLanguage } from '@kbn/ml-query-utils'; import { AIOPS_TELEMETRY_ID } from '@kbn/aiops-common/constants'; import type { Category } from '@kbn/aiops-log-pattern-analysis/types'; +import { useTableState } from '@kbn/ml-in-memory-table/hooks/use_table_state'; import { useDataSource } from '../../hooks/use_data_source'; import { useData } from '../../hooks/use_data'; import { useSearch } from '../../hooks/use_search'; @@ -97,6 +98,7 @@ export const LogCategorizationPage: FC = ({ embeddin const [fieldValidationResult, setFieldValidationResult] = useState( null ); + const tableState = useTableState([], 'key'); const cancelRequest = useCallback(() => { cancelValidationRequest(); @@ -422,6 +424,7 @@ export const LogCategorizationPage: FC = ({ embeddin displayExamples={data.displayExamples} setSelectedCategories={setSelectedCategories} openInDiscover={openInDiscover} + tableState={tableState} /> ) : null} From 820b2be0f5ff2884410b273a47eb996f47d6a374 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 15 May 2024 17:07:25 +0100 Subject: [PATCH 76/92] adding field name to examples title --- .../category_table/category_table.tsx | 14 +++++++++++--- .../log_categorization_for_embeddable.tsx | 1 + 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx b/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx index 91e18ea7b4421..6379ff538cd0a 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx @@ -46,6 +46,7 @@ interface Props { tableState: UseTableState; enableRowActions?: boolean; displayExamples?: boolean; + fieldName?: string; } export const CategoryTable: FC = ({ @@ -60,6 +61,7 @@ export const CategoryTable: FC = ({ tableState, enableRowActions = true, displayExamples = true, + fieldName, }) => { const euiTheme = useEuiTheme(); const primaryBackgroundColor = useEuiBackgroundColor('primary'); @@ -122,9 +124,15 @@ export const CategoryTable: FC = ({ width: '80px', }, { - name: i18n.translate('xpack.aiops.logCategorization.column.examples', { - defaultMessage: 'Examples', - }), + name: + fieldName === undefined + ? i18n.translate('xpack.aiops.logCategorization.column.examples', { + defaultMessage: 'Examples', + }) + : i18n.translate('xpack.aiops.logCategorization.column.examplesWithFieldName', { + defaultMessage: '{fieldName} examples', + values: { fieldName }, + }), sortable: true, render: (item: Category) => , }, diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx index 2e991e14652cf..371e2fdba1fdd 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx @@ -445,6 +445,7 @@ export const LogCategorizationEmbeddable: FC = setSelectedCategories={setSelectedCategories} openInDiscover={openInDiscover} tableState={tableState} + fieldName={selectedField.name} /> ) : null} From 104b368803a6343603d905b03bcb0b128fdf1787 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 15 May 2024 17:29:02 +0100 Subject: [PATCH 77/92] changing embedding origin --- .../components/pattern_analysis/pattern_analysis_table.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx index bda3db4394dd4..a7583e69c7d9f 100644 --- a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx +++ b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx @@ -17,7 +17,7 @@ import { PATTERN_ANALYSIS_LOADED } from './constants'; export type PatternAnalysisTableProps = EmbeddablePatternAnalysisInput & { stateContainer?: DiscoverStateContainer; trackUiMetric?: (metricType: UiCounterMetricType, eventName: string | string[]) => void; - viewModeToggle: (patternCount?: number) => React.ReactElement; + renderViewModeToggle: (patternCount?: number) => React.ReactElement; }; export const PatternAnalysisTable = (props: PatternAnalysisTableProps) => { @@ -70,10 +70,10 @@ export const PatternAnalysisTable = (props: PatternAnalysisTableProps) => { ); }; From 186f78b53ebc6cf2166bb73a2daffa6f267ef003 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 15 May 2024 17:29:27 +0100 Subject: [PATCH 78/92] renaming function --- .../main/components/layout/discover_main_content.tsx | 12 ++++++------ .../pattern_analysis/pattern_analysis_tab.tsx | 2 +- .../discover_tabs.tsx | 6 +++--- .../log_categorization_for_embeddable.tsx | 6 +++--- .../log_categorization_wrapper.tsx | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx index 8d4ad6972a93b..08c370c95d63a 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx @@ -23,7 +23,7 @@ import { DOCUMENTS_VIEW_CLICK, FIELD_STATISTICS_VIEW_CLICK } from '../field_stat import { useAppStateSelector } from '../../state_management/discover_app_state_container'; import type { PanelsToggleProps } from '../../../../components/panels_toggle'; import { PatternAnalysisTab } from '../pattern_analysis/pattern_analysis_tab'; -import { PATTERN_ANALYSIS_LOADED } from '../pattern_analysis/constants'; +import { PATTERN_ANALYSIS_VIEW_CLICK } from '../pattern_analysis/constants'; const DROP_PROPS = { value: { @@ -73,7 +73,7 @@ export const DiscoverMainContent = ({ if (mode === VIEW_MODE.AGGREGATED_LEVEL) { trackUiMetric(METRIC_TYPE.CLICK, FIELD_STATISTICS_VIEW_CLICK); } else if (mode === VIEW_MODE.PATTERN_LEVEL) { - trackUiMetric(METRIC_TYPE.CLICK, PATTERN_ANALYSIS_LOADED); + trackUiMetric(METRIC_TYPE.CLICK, PATTERN_ANALYSIS_VIEW_CLICK); } else { trackUiMetric(METRIC_TYPE.CLICK, DOCUMENTS_VIEW_CLICK); } @@ -84,7 +84,7 @@ export const DiscoverMainContent = ({ const isDropAllowed = Boolean(onDropFieldToTable); - const viewModeToggle = useCallback( + const renderViewModeToggle = useCallback( (patternCount?: number) => { return ( } {viewMode === VIEW_MODE.DOCUMENT_LEVEL ? ( - {viewModeToggle()} + {renderViewModeToggle()} setDiscoverViewMode(VIEW_MODE.DOCUMENT_LEVEL)} trackUiMetric={trackUiMetric} - viewModeToggle={viewModeToggle} + renderViewModeToggle={renderViewModeToggle} /> ) : null} diff --git a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_tab.tsx b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_tab.tsx index d651a21a2f2b0..0559622e2585a 100644 --- a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_tab.tsx +++ b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_tab.tsx @@ -29,7 +29,7 @@ export const PatternAnalysisTab: FC ); } diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/discover_tabs.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/discover_tabs.tsx index 5dfdc53ada4db..fd2cca945245f 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/discover_tabs.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/discover_tabs.tsx @@ -22,7 +22,7 @@ import { SelectedPatterns } from './selected_patterns'; import { CreateCategorizationJobButton } from '../create_categorization_job'; interface Props { - viewModeToggle: (patternCount?: number) => React.ReactElement; + renderViewModeToggle: (patternCount?: number) => React.ReactElement; randomSampler: RandomSampler; openInDiscover: OpenInDiscover; selectedCategories: Category[]; @@ -44,7 +44,7 @@ interface Props { } export const DiscoverTabs: FC = ({ - viewModeToggle, + renderViewModeToggle, randomSampler, openInDiscover, selectedCategories, @@ -63,7 +63,7 @@ export const DiscoverTabs: FC = ({ return ( - {viewModeToggle(data?.categories.length)} + {renderViewModeToggle(data?.categories.length)} <> diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx index 371e2fdba1fdd..99b406697811f 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx @@ -47,14 +47,14 @@ import { DiscoverTabs } from './discover_tabs'; export interface LogCategorizationEmbeddableProps { input: Readonly; - viewModeToggle: (patternCount?: number) => React.ReactElement; + renderViewModeToggle: (patternCount?: number) => React.ReactElement; } const BAR_TARGET = 20; export const LogCategorizationEmbeddable: FC = ({ input, - viewModeToggle, + renderViewModeToggle, }) => { const { notifications: { toasts }, @@ -403,7 +403,7 @@ export const LogCategorizationEmbeddable: FC = selectedField={selectedField} setMinimumTimeRangeOption={setMinimumTimeRangeOption} setSelectedField={setSelectedField} - viewModeToggle={viewModeToggle} + renderViewModeToggle={renderViewModeToggle} dataview={dataView} earliest={earliest} latest={latest} diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_wrapper.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_wrapper.tsx index 43eb61faa84e1..49a8219bc880a 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_wrapper.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_wrapper.tsx @@ -73,7 +73,7 @@ export const LogCategorizationWrapper: FC From a7ddc2a0776a91b1009bca116a130e114cda23c0 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 15 May 2024 17:32:51 +0100 Subject: [PATCH 79/92] updating tests --- .../index_data_visualizer_grid_in_discover_basic.ts | 4 +--- .../index_data_visualizer_grid_in_discover_trial.ts | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover_basic.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover_basic.ts index 6322991b6a3ad..7d7e5dfe0760f 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover_basic.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover_basic.ts @@ -39,10 +39,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); } - describe('field statistics in Discover', function () { + describe('field statistics in Discover (basic license)', function () { before(async function () { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/module_sample_logs'); await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); await ml.testResources.createDataViewIfNeeded('ft_module_sample_logs', '@timestamp'); await ml.testResources.createSavedSearchFarequoteKueryIfNeeded(); @@ -57,7 +56,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await ml.testResources.clearAdvancedSettingProperty(SHOW_FIELD_STATISTICS); await ml.testResources.deleteSavedSearches(); await ml.testResources.deleteDataViewByTitle('ft_farequote'); - await ml.testResources.deleteDataViewByTitle('ft_module_sample_logs'); }); describe('when disabled', function () { diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover_trial.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover_trial.ts index 4ce3775fb225c..9a1429805a18f 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover_trial.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer_grid_in_discover_trial.ts @@ -39,10 +39,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); } - describe('field statistics in Discover', function () { + describe('field statistics in Discover (trial license)', function () { before(async function () { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); - await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/module_sample_logs'); await ml.testResources.createDataViewIfNeeded('ft_farequote', '@timestamp'); await ml.testResources.createDataViewIfNeeded('ft_module_sample_logs', '@timestamp'); await ml.testResources.createSavedSearchFarequoteKueryIfNeeded(); @@ -57,7 +56,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await ml.testResources.clearAdvancedSettingProperty(SHOW_FIELD_STATISTICS); await ml.testResources.deleteSavedSearches(); await ml.testResources.deleteDataViewByTitle('ft_farequote'); - await ml.testResources.deleteDataViewByTitle('ft_module_sample_logs'); }); describe('when disabled', function () { From 1fa22ce642fbe0c82b934cd5d510197b978292b9 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 15 May 2024 17:34:30 +0100 Subject: [PATCH 80/92] updating comment --- .../plugins/aiops/public/components/log_categorization/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/aiops/public/components/log_categorization/utils.ts b/x-pack/plugins/aiops/public/components/log_categorization/utils.ts index fea54f39e442d..152cd56b77593 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/utils.ts +++ b/x-pack/plugins/aiops/public/components/log_categorization/utils.ts @@ -29,7 +29,7 @@ export function createAdditionalConfigHash(additionalStrings: string[] = []) { /** * Retrieves the message field from a DataView object. - * If the message field is not found, it falls back to error.message or event.original or the first field in the DataView. + * If the message field is not found, it falls back to error.message or event.original or the first text field in the DataView. * * @param dataView - The DataView object containing the fields. * @returns An object containing the message field and all the fields in the DataView. From a6b73baba7bab7bb71a1e267c652b1e4253c5ffe Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 15 May 2024 21:19:02 +0100 Subject: [PATCH 81/92] adding mounted check --- .../view_mode_toggle/view_mode_toggle.tsx | 21 +++-- .../category_table/table_header.tsx | 78 ++++++++++--------- 2 files changed, 57 insertions(+), 42 deletions(-) diff --git a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx index 45590be1a3dbb..bacd118cbd666 100644 --- a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx +++ b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx @@ -6,12 +6,13 @@ * Side Public License, v 1. */ -import React, { useMemo, useEffect, useState, type ReactElement } from 'react'; +import React, { useMemo, useEffect, useState, type ReactElement, useCallback } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiTab, EuiTabs, useEuiTheme } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { css } from '@emotion/react'; import { isLegacyTableEnabled, SHOW_FIELD_STATISTICS } from '@kbn/discover-utils'; import type { DataView } from '@kbn/data-views-plugin/common'; +import useMountedState from 'react-use/lib/useMountedState'; import { VIEW_MODE } from '../../../common/constants'; import { useDiscoverServices } from '../../hooks/use_discover_services'; import type { DiscoverStateContainer } from '../../application/main/state_management/discover_state'; @@ -49,6 +50,16 @@ export const DocumentViewModeToggle = ({ () => uiSettings.get(SHOW_FIELD_STATISTICS) && dataVisualizerService !== undefined, [dataVisualizerService, uiSettings] ); + const isMounted = useMountedState(); + + const setShowPatternAnalysisTabWrapper = useCallback( + (value: boolean) => { + if (isMounted()) { + setShowPatternAnalysisTab(value); + } + }, + [isMounted] + ); useEffect( function checkForPatternAnalysis() { @@ -60,12 +71,12 @@ export const DocumentViewModeToggle = ({ .getPatternAnalysisAvailable() .then((patternAnalysisAvailable) => { patternAnalysisAvailable(dataView) - .then(setShowPatternAnalysisTab) - .catch(() => setShowPatternAnalysisTab(false)); + .then(setShowPatternAnalysisTabWrapper) + .catch(() => setShowPatternAnalysisTabWrapper(false)); }) - .catch(() => setShowPatternAnalysisTab(false)); + .catch(() => setShowPatternAnalysisTabWrapper(false)); }, - [aiopsService, dataView] + [aiopsService, dataView, setShowPatternAnalysisTabWrapper] ); useEffect(() => { diff --git a/x-pack/plugins/aiops/public/components/log_categorization/category_table/table_header.tsx b/x-pack/plugins/aiops/public/components/log_categorization/category_table/table_header.tsx index 35a6443c19d4a..d733c050fc3b8 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/category_table/table_header.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/category_table/table_header.tsx @@ -26,33 +26,31 @@ export const TableHeader: FC = ({ }) => { const euiTheme = useEuiTheme(); return ( - <> - - - - - {selectedCategoriesCount > 0 ? ( - <> - - - ) : null} - + + + + + {selectedCategoriesCount > 0 ? ( + <> + + + ) : null} + + + {selectedCategoriesCount > 0 ? ( + + - {selectedCategoriesCount > 0 ? ( - - - - ) : null} - - + ) : null} + ); }; @@ -61,19 +59,11 @@ export const OpenInDiscoverButtons: FC<{ openInDiscover: OpenInDiscover; showTex showText = true, }) => { const { labels, openFunction } = openInDiscover; - const TooltipWrapper: FC> = ({ text, children }) => { - return showText ? ( - <>{children} - ) : ( - - <>{children} - - ); - }; + return ( - + - + ); }; + +const TooltipWrapper: FC> = ({ + text, + showText, + children, +}) => { + return showText ? ( + <>{children} + ) : ( + + <>{children} + + ); +}; From d17f7f130df10966c52c4d761aec9d7b14cd6f01 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 16 May 2024 10:07:53 +0100 Subject: [PATCH 82/92] fixing types and test --- .../state_management/utils/get_state_defaults.test.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.test.ts b/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.test.ts index f27b646bfeb0e..6cdc244dbac1d 100644 --- a/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.test.ts +++ b/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.test.ts @@ -136,7 +136,7 @@ describe('getStateDefaults', () => { }, }); expect(actualForWithValidAggLevelViewMode.viewMode).toBe(VIEW_MODE.AGGREGATED_LEVEL); - expect(actualForWithValidViewMode.dataSource).toEqual( + expect(actualForWithValidAggLevelViewMode.dataSource).toEqual( createDataViewDataSource({ dataViewId: savedSearchMock.searchSource.getField('index')?.id!, }) @@ -150,8 +150,10 @@ describe('getStateDefaults', () => { }, }); expect(actualForWithValidPatternLevelViewMode.viewMode).toBe(VIEW_MODE.PATTERN_LEVEL); - expect(actualForWithValidPatternLevelViewMode.index).toBe( - savedSearchMock.searchSource.getField('index')?.id + expect(actualForWithValidPatternLevelViewMode.dataSource).toEqual( + createDataViewDataSource({ + dataViewId: savedSearchMock.searchSource.getField('index')?.id!, + }) ); }); From 299621c72a7d645fdf9eae24a5dd2fffa46685a5 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 16 May 2024 17:17:19 +0100 Subject: [PATCH 83/92] code clean up based on review --- .../layout/discover_main_content.tsx | 8 ++-- .../pattern_analysis_table.tsx | 48 ++++++++----------- .../category_table/category_table.tsx | 2 +- .../log_categorization_enabled.ts | 7 ++- .../discover_tabs.tsx | 14 ++---- .../log_categorization_for_embeddable.tsx | 4 +- 6 files changed, 39 insertions(+), 44 deletions(-) diff --git a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx index 08c370c95d63a..353b084b5ecfb 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx @@ -8,7 +8,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule } from '@elastic/eui'; import { type DropType, DropOverlayWrapper, Droppable } from '@kbn/dom-drag-drop'; -import React, { ReactElement, useCallback } from 'react'; +import React, { ReactElement, useCallback, useMemo } from 'react'; import { DataView } from '@kbn/data-views-plugin/common'; import { METRIC_TYPE } from '@kbn/analytics'; import { i18n } from '@kbn/i18n'; @@ -113,6 +113,8 @@ export const DiscoverMainContent = ({ ] ); + const viewModeToggle = useMemo(() => renderViewModeToggle(), [renderViewModeToggle]); + const showChart = useAppStateSelector((state) => !state.hideChart); return ( @@ -133,7 +135,7 @@ export const DiscoverMainContent = ({ {showChart && isChartAvailable && } {viewMode === VIEW_MODE.DOCUMENT_LEVEL ? ( - {renderViewModeToggle()} + {viewModeToggle} { useEffect(() => { // Track should only be called once when component is loaded - trackUiMetric?.(METRIC_TYPE.LOADED, PATTERN_ANALYSIS_LOADED); - }, [trackUiMetric]); + if (aiopsService !== undefined) { + trackUiMetric?.(METRIC_TYPE.LOADED, PATTERN_ANALYSIS_LOADED); + } + }, [aiopsService, trackUiMetric]); + + const patternAnalysisComponentProps: LogCategorizationEmbeddableProps = useMemo( + () => ({ + input: Object.assign( + {}, + pick(props, ['dataView', 'savedSearch', 'query', 'filters', 'onAddFilter']), + { lastReloadRequestTime } + ), + renderViewModeToggle: props.renderViewModeToggle, + }), + [lastReloadRequestTime, props] + ); if (aiopsService === undefined) { return null; } - const deps = pick(services, [ - 'i18n', - 'theme', - 'data', - 'uiSettings', - 'http', - 'notifications', - 'lens', - 'fieldFormats', - 'application', - 'charts', - 'uiActions', - ]); - - const input: EmbeddablePatternAnalysisInput = Object.assign( - {}, - pick(props, ['dataView', 'savedSearch', 'query', 'filters', 'onAddFilter']), - { lastReloadRequestTime } - ); - return ( ); diff --git a/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx b/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx index 6379ff538cd0a..0447f09852b0b 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx @@ -130,7 +130,7 @@ export const CategoryTable: FC = ({ defaultMessage: 'Examples', }) : i18n.translate('xpack.aiops.logCategorization.column.examplesWithFieldName', { - defaultMessage: '{fieldName} examples', + defaultMessage: 'Examples of {fieldName}', values: { fieldName }, }), sortable: true, diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_enabled.ts b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_enabled.ts index ef81d33ff8776..6df61a20f993a 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_enabled.ts +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_enabled.ts @@ -13,8 +13,11 @@ import { firstValueFrom } from 'rxjs'; export function getPatternAnalysisAvailable(licensing: LicensingPluginStart) { const lic = firstValueFrom(licensing.license$); return async (dataView: DataView) => { - const hasTextFields = dataView.fields.some((f) => f.esTypes?.includes(ES_FIELD_TYPES.TEXT)); const isPlatinum = (await lic).hasAtLeast('platinum'); - return isPlatinum && hasTextFields && dataView.isTimeBased(); + return ( + isPlatinum && + dataView.isTimeBased() && + dataView.fields.some((f) => f.esTypes?.includes(ES_FIELD_TYPES.TEXT)) + ); }; } diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/discover_tabs.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/discover_tabs.tsx index fd2cca945245f..542f2248845fe 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/discover_tabs.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/discover_tabs.tsx @@ -75,11 +75,7 @@ export const DiscoverTabs: FC = ({ ) : null} -
+
= ({ categoryCount={data?.totalCategories} />
-
- {selectedField !== null && earliest !== undefined && latest !== undefined ? ( + {selectedField !== null && earliest !== undefined && latest !== undefined ? ( +
= ({ latest={latest} iconOnly={true} /> - ) : null} -
+
+ ) : null}
diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx index 99b406697811f..bc39cf50168e1 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx @@ -7,7 +7,7 @@ import type { FC } from 'react'; import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import type { DataViewField } from '@kbn/data-views-plugin/public'; import { i18n } from '@kbn/i18n'; @@ -410,6 +410,8 @@ export const LogCategorizationEmbeddable: FC = query={searchQuery} /> + + Date: Fri, 17 May 2024 09:31:31 +0100 Subject: [PATCH 84/92] Update x-pack/plugins/aiops/public/components/log_categorization/create_categorization_job.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: István Zoltán Szabó --- .../components/log_categorization/create_categorization_job.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/aiops/public/components/log_categorization/create_categorization_job.tsx b/x-pack/plugins/aiops/public/components/log_categorization/create_categorization_job.tsx index 73ba90fa403a5..e2ecc188632f6 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/create_categorization_job.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/create_categorization_job.tsx @@ -65,7 +65,7 @@ export const CreateCategorizationJobButton: FC = ({ return ( Date: Fri, 17 May 2024 09:31:44 +0100 Subject: [PATCH 85/92] Update x-pack/plugins/aiops/public/components/log_categorization/create_categorization_job.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: István Zoltán Szabó --- .../components/log_categorization/create_categorization_job.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/aiops/public/components/log_categorization/create_categorization_job.tsx b/x-pack/plugins/aiops/public/components/log_categorization/create_categorization_job.tsx index e2ecc188632f6..91a3185b683bb 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/create_categorization_job.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/create_categorization_job.tsx @@ -76,7 +76,7 @@ export const CreateCategorizationJobButton: FC = ({ // @ts-ignore - subdued does work color="subdued" aria-label={i18n.translate('xpack.aiops.categorizeFlyout.findAnomalies.tooltip', { - defaultMessage: 'Create Anomaly Detection job to find anomalies in patterns', + defaultMessage: 'Create anomaly detection job to find anomalies in patterns', })} /> From 2b115e271c6ee3828b21b513d2dad74cb1ef213e Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 17 May 2024 09:32:11 +0100 Subject: [PATCH 86/92] Update x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/random_sampler.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: István Zoltán Szabó --- .../log_categorization/sampling_menu/random_sampler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/random_sampler.ts b/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/random_sampler.ts index d30950e93a379..20b6d25668ba1 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/random_sampler.ts +++ b/x-pack/plugins/aiops/public/components/log_categorization/sampling_menu/random_sampler.ts @@ -141,7 +141,7 @@ export const randomSamplerText = (randomSamplerPreference: RandomSamplerOption) 'xpack.aiops.logCategorization.randomSamplerSettingsPopUp.offCallout.message', { defaultMessage: - 'Random sampling can be turned on to increase the speed of analysis, although some accuracy will be lost.', + 'Random sampling can be turned on to increase analysis speed. Accuracy will slightly decrease.', } ), buttonText: i18n.translate( From 2ead31dc03e7306b1ca1f8dbce4c49b1df8e6219 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 17 May 2024 15:47:23 +0100 Subject: [PATCH 87/92] moving field select to separate menu --- .../category_table/category_table.tsx | 12 +-- .../discover_tabs.tsx | 11 ++- .../embeddable_menu.tsx | 32 +------- .../field_selector.tsx | 79 +++++++++++++++++++ 4 files changed, 91 insertions(+), 43 deletions(-) create mode 100644 x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/field_selector.tsx diff --git a/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx b/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx index 0447f09852b0b..cbeeae3b27660 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx @@ -124,15 +124,9 @@ export const CategoryTable: FC = ({ width: '80px', }, { - name: - fieldName === undefined - ? i18n.translate('xpack.aiops.logCategorization.column.examples', { - defaultMessage: 'Examples', - }) - : i18n.translate('xpack.aiops.logCategorization.column.examplesWithFieldName', { - defaultMessage: 'Examples of {fieldName}', - values: { fieldName }, - }), + name: i18n.translate('xpack.aiops.logCategorization.column.examples', { + defaultMessage: 'Examples', + }), sortable: true, render: (item: Category) => , }, diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/discover_tabs.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/discover_tabs.tsx index 542f2248845fe..96fae405eec2c 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/discover_tabs.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/discover_tabs.tsx @@ -20,6 +20,7 @@ import type { RandomSampler } from '../sampling_menu'; import type { MinimumTimeRangeOption } from './minimum_time_range'; import { SelectedPatterns } from './selected_patterns'; import { CreateCategorizationJobButton } from '../create_categorization_job'; +import { SelectedField } from './field_selector'; interface Props { renderViewModeToggle: (patternCount?: number) => React.ReactElement; @@ -74,15 +75,19 @@ export const DiscoverTabs: FC = ({ ) : null} + + +
loadCategories()} - fields={fields} - setSelectedField={setSelectedField} - selectedField={selectedField} minimumTimeRangeOption={minimumTimeRangeOption} setMinimumTimeRangeOption={setMinimumTimeRangeOption} categoryCount={data?.totalCategories} diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx index 51d46c1dab203..cb4a67e0707ee 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx @@ -19,8 +19,7 @@ import { EuiIcon, } from '@elastic/eui'; import type { FC } from 'react'; -import React, { useState, useMemo } from 'react'; -import type { DataViewField } from '@kbn/data-views-plugin/public'; +import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -31,9 +30,6 @@ import { MINIMUM_TIME_RANGE } from './minimum_time_range'; interface Props { randomSampler: RandomSampler; - fields: DataViewField[]; - selectedField: DataViewField | null; - setSelectedField: (field: DataViewField) => void; minimumTimeRangeOption: MinimumTimeRangeOption; setMinimumTimeRangeOption: (w: MinimumTimeRangeOption) => void; categoryCount: number | undefined; @@ -47,9 +43,6 @@ const minimumTimeRangeOptions = Object.keys(MINIMUM_TIME_RANGE).map((value) => ( export const EmbeddableMenu: FC = ({ randomSampler, - fields, - selectedField, - setSelectedField, minimumTimeRangeOption, setMinimumTimeRangeOption, categoryCount, @@ -58,11 +51,6 @@ export const EmbeddableMenu: FC = ({ const [showMenu, setShowMenu] = useState(false); const togglePopover = () => setShowMenu(!showMenu); - const fieldOptions = useMemo( - () => fields.map((field) => ({ inputDisplay: field.name, value: field })), - [fields] - ); - const button = ( = ({ - - - - - void; +} + +export const SelectedField: FC = ({ fields, selectedField, setSelectedField }) => { + const [showPopover, setShowPopover] = useState(false); + const togglePopover = () => setShowPopover(!showPopover); + + const fieldOptions = useMemo( + () => fields.map((field) => ({ inputDisplay: field.name, value: field })), + [fields] + ); + + const button = ( + togglePopover()} + > + + + + + {selectedField?.name} + + + ); + + return ( + setShowPopover(false)} + isOpen={showPopover} + button={button} + className="unifiedDataTableToolbarControlButton" + > + + + + + ); +}; From 13e4b30ad7340ca8143802aa9c735755c4f65280 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Mon, 20 May 2024 17:28:25 +0100 Subject: [PATCH 88/92] style changes --- .../log_categorization/category_table/category_table.tsx | 2 -- .../log_categorization_for_embeddable/embeddable_menu.tsx | 6 +++--- .../log_categorization_for_embeddable.tsx | 7 ++++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx b/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx index cbeeae3b27660..91e18ea7b4421 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/category_table/category_table.tsx @@ -46,7 +46,6 @@ interface Props { tableState: UseTableState; enableRowActions?: boolean; displayExamples?: boolean; - fieldName?: string; } export const CategoryTable: FC = ({ @@ -61,7 +60,6 @@ export const CategoryTable: FC = ({ tableState, enableRowActions = true, displayExamples = true, - fieldName, }) => { const euiTheme = useEuiTheme(); const primaryBackgroundColor = useEuiBackgroundColor('primary'); diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx index cb4a67e0707ee..af40e305f558b 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/embeddable_menu.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { EuiSuperSelect, EuiToolTip } from '@elastic/eui'; +import { EuiPopoverTitle, EuiSuperSelect, EuiToolTip } from '@elastic/eui'; import { EuiFormRow } from '@elastic/eui'; import { EuiButtonIcon, @@ -82,12 +82,12 @@ export const EmbeddableMenu: FC = ({ > -

+ -

+
diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx index bc39cf50168e1..d453826504d2e 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx @@ -7,7 +7,7 @@ import type { FC } from 'react'; import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer, useEuiPaddingSize } from '@elastic/eui'; import type { DataViewField } from '@kbn/data-views-plugin/public'; import { i18n } from '@kbn/i18n'; @@ -64,6 +64,8 @@ export const LogCategorizationEmbeddable: FC = uiSettings, embeddingOrigin, } = useAiopsAppContext(); + const tablePadding = useEuiPaddingSize('xs'); + const { dataView, savedSearch } = input; const { runValidateFieldRequest, cancelRequest: cancelValidationRequest } = @@ -419,7 +421,7 @@ export const LogCategorizationEmbeddable: FC = gutterSize="none" responsive={false} > - + <> {(loading ?? true) === true ? ( @@ -447,7 +449,6 @@ export const LogCategorizationEmbeddable: FC = setSelectedCategories={setSelectedCategories} openInDiscover={openInDiscover} tableState={tableState} - fieldName={selectedField.name} /> ) : null} From 6213bf04f213c760854e4e38067fc8700e8ad966 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Tue, 21 May 2024 09:12:05 +0100 Subject: [PATCH 89/92] fixing types after merge with main --- .../public/application/main/utils/get_valid_view_mode.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/discover/public/application/main/utils/get_valid_view_mode.test.ts b/src/plugins/discover/public/application/main/utils/get_valid_view_mode.test.ts index 36f9aa9a98bb0..ff2d4250b3da8 100644 --- a/src/plugins/discover/public/application/main/utils/get_valid_view_mode.test.ts +++ b/src/plugins/discover/public/application/main/utils/get_valid_view_mode.test.ts @@ -35,7 +35,7 @@ describe('getValidViewMode', () => { expect( getValidViewMode({ viewMode: VIEW_MODE.PATTERN_LEVEL, - isTextBasedQueryMode: false, + isEsqlMode: false, }) ).toBe(VIEW_MODE.PATTERN_LEVEL); }); @@ -65,7 +65,7 @@ describe('getValidViewMode', () => { expect( getValidViewMode({ viewMode: VIEW_MODE.PATTERN_LEVEL, - isTextBasedQueryMode: true, + isEsqlMode: true, }) ).toBe(VIEW_MODE.DOCUMENT_LEVEL); }); From 1aaa89fc3a28c496c06e84f5335fd50ae31732d2 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Tue, 21 May 2024 10:53:50 +0100 Subject: [PATCH 90/92] type import change --- .../components/pattern_analysis/pattern_analysis_table.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx index 6dff38400e6a0..919bb7f0fe257 100644 --- a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx +++ b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx @@ -10,9 +10,9 @@ import React, { useEffect, useState, useMemo } from 'react'; import { METRIC_TYPE, UiCounterMetricType } from '@kbn/analytics'; import { type EmbeddablePatternAnalysisInput } from '@kbn/aiops-log-pattern-analysis/embeddable'; import { pick } from 'lodash'; -import { LogCategorizationEmbeddableProps } from '@kbn/aiops-plugin/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable'; +import type { LogCategorizationEmbeddableProps } from '@kbn/aiops-plugin/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; -import { DiscoverStateContainer } from '../../state_management/discover_state'; +import type { DiscoverStateContainer } from '../../state_management/discover_state'; import { PATTERN_ANALYSIS_LOADED } from './constants'; export type PatternAnalysisTableProps = EmbeddablePatternAnalysisInput & { From 10ef4febb215ddb7c4f5a9d084a0f1984eed9cbc Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Tue, 21 May 2024 12:57:18 +0100 Subject: [PATCH 91/92] renaming onAddFilter --- .../main/components/layout/discover_main_content.tsx | 2 +- .../components/pattern_analysis/pattern_analysis_tab.tsx | 2 +- .../components/pattern_analysis/pattern_analysis_table.tsx | 2 +- x-pack/packages/ml/aiops_log_pattern_analysis/embeddable.ts | 5 +---- .../log_categorization_for_embeddable.tsx | 4 ++-- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx index c164333b0bdd0..7c44ed1deff83 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx @@ -158,7 +158,7 @@ export const DiscoverMainContent = ({ setDiscoverViewMode(VIEW_MODE.DOCUMENT_LEVEL)} + switchToDocumentView={() => setDiscoverViewMode(VIEW_MODE.DOCUMENT_LEVEL)} trackUiMetric={trackUiMetric} renderViewModeToggle={renderViewModeToggle} /> diff --git a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_tab.tsx b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_tab.tsx index 0559622e2585a..db02134e0169f 100644 --- a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_tab.tsx +++ b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_tab.tsx @@ -25,7 +25,7 @@ export const PatternAnalysisTab: FC { () => ({ input: Object.assign( {}, - pick(props, ['dataView', 'savedSearch', 'query', 'filters', 'onAddFilter']), + pick(props, ['dataView', 'savedSearch', 'query', 'filters', 'switchToDocumentView']), { lastReloadRequestTime } ), renderViewModeToggle: props.renderViewModeToggle, diff --git a/x-pack/packages/ml/aiops_log_pattern_analysis/embeddable.ts b/x-pack/packages/ml/aiops_log_pattern_analysis/embeddable.ts index 2bc122ce11e14..57c6b144c15d0 100644 --- a/x-pack/packages/ml/aiops_log_pattern_analysis/embeddable.ts +++ b/x-pack/packages/ml/aiops_log_pattern_analysis/embeddable.ts @@ -15,9 +15,6 @@ export interface EmbeddablePatternAnalysisInput { query?: T; filters?: Filter[]; embeddingOrigin?: string; - /** - * Callback to add a filter to filter bar - */ - onAddFilter?: () => void; + switchToDocumentView?: () => void; lastReloadRequestTime?: number; } diff --git a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx index d453826504d2e..1246ea0770004 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/log_categorization_for_embeddable/log_categorization_for_embeddable.tsx @@ -163,7 +163,7 @@ export const LogCategorizationEmbeddable: FC = const onAddFilter = useCallback( (values: Filter, alias?: string) => { - if (input.onAddFilter === undefined) { + if (input.switchToDocumentView === undefined) { return; } @@ -172,7 +172,7 @@ export const LogCategorizationEmbeddable: FC = filter.meta.alias = alias; } filter.query = values.query; - input.onAddFilter(); + input.switchToDocumentView(); filterManager.addFilters([filter]); }, [dataView.id, filterManager, input] From fd42267aac67dcf843a2c5d3a9b43872b7def73f Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 22 May 2024 12:46:02 +0100 Subject: [PATCH 92/92] truthy changes --- .../components/pattern_analysis/pattern_analysis_table.tsx | 4 ++-- .../public/components/view_mode_toggle/view_mode_toggle.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx index c7170b81b9d1f..ccbb2a6e72b8c 100644 --- a/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx +++ b/src/plugins/discover/public/application/main/components/pattern_analysis/pattern_analysis_table.tsx @@ -40,7 +40,7 @@ export const PatternAnalysisTable = (props: PatternAnalysisTableProps) => { useEffect(() => { // Track should only be called once when component is loaded - if (aiopsService !== undefined) { + if (aiopsService) { trackUiMetric?.(METRIC_TYPE.LOADED, PATTERN_ANALYSIS_LOADED); } }, [aiopsService, trackUiMetric]); @@ -57,7 +57,7 @@ export const PatternAnalysisTable = (props: PatternAnalysisTableProps) => { [lastReloadRequestTime, props] ); - if (aiopsService === undefined) { + if (!aiopsService) { return null; } diff --git a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx index ffe7917ea8d5b..11351893b6a26 100644 --- a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx +++ b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx @@ -63,7 +63,7 @@ export const DocumentViewModeToggle = ({ useEffect( function checkForPatternAnalysis() { - if (aiopsService === undefined) { + if (!aiopsService) { setShowPatternAnalysisTab(false); return; }