adds a check to allow only data views with time fields

This commit is contained in:
Walter Rafelsberger 2024-10-10 15:11:10 +02:00
parent 2b58567771
commit a01975dba7
4 changed files with 105 additions and 46 deletions

View file

@ -0,0 +1,38 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { EuiSpacer, EuiCallOut } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
export const TimeFieldWarning = () => {
return (
<>
<EuiCallOut
size="s"
title={i18n.translate(
'xpack.aiops.logCategorization.embeddableMenu.timeFieldWarning.title',
{
defaultMessage: 'The selected data view does not contain a time field.',
}
)}
color="warning"
iconType="warning"
>
<p>
{i18n.translate(
'xpack.aiops.logCategorization.embeddableMenu.timeFieldWarning.title.description',
{
defaultMessage: 'Pattern analysis can only be run on data views with a time field.',
}
)}
</p>
</EuiCallOut>
<EuiSpacer />
</>
);
};

View file

@ -8,6 +8,7 @@
import type { FC } from 'react'; import type { FC } from 'react';
import React, { useEffect, useMemo, useState, useCallback } from 'react'; import React, { useEffect, useMemo, useState, useCallback } from 'react';
import { pick } from 'lodash'; import { pick } from 'lodash';
import useMountedState from 'react-use/lib/useMountedState';
import { import {
EuiFlyoutHeader, EuiFlyoutHeader,
@ -20,6 +21,7 @@ import {
EuiFlexGroup, EuiFlexGroup,
EuiFlexItem, EuiFlexItem,
EuiFlyoutFooter, EuiFlyoutFooter,
EuiSpacer,
} from '@elastic/eui'; } from '@elastic/eui';
import type { IndexPatternSelectProps } from '@kbn/unified-search-plugin/public'; import type { IndexPatternSelectProps } from '@kbn/unified-search-plugin/public';
@ -29,6 +31,8 @@ import { FormattedMessage } from '@kbn/i18n-react';
import { isPopulatedObject } from '@kbn/ml-is-populated-object'; import { isPopulatedObject } from '@kbn/ml-is-populated-object';
import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
import { TimeFieldWarning } from '../../components/time_field_warning';
import type { LogRateAnalysisEmbeddableRuntimeState } from './types'; import type { LogRateAnalysisEmbeddableRuntimeState } from './types';
export interface LogRateAnalysisEmbeddableInitializerProps { export interface LogRateAnalysisEmbeddableInitializerProps {
@ -52,13 +56,22 @@ export const LogRateAnalysisEmbeddableInitializer: FC<
onPreview, onPreview,
isNewPanel, isNewPanel,
}) => { }) => {
const isMounted = useMountedState();
const [formInput, setFormInput] = useState<LogRateAnalysisEmbeddableRuntimeState>( const [formInput, setFormInput] = useState<LogRateAnalysisEmbeddableRuntimeState>(
pick(initialInput ?? {}, ['dataViewId']) as LogRateAnalysisEmbeddableRuntimeState pick(initialInput ?? {}, ['dataViewId']) as LogRateAnalysisEmbeddableRuntimeState
); );
// State to track if the selected data view is time based, undefined is used
// to track that the check is in progress.
const [isDataViewTimeBased, setIsDataViewTimeBased] = useState<boolean | undefined>();
const isFormValid = useMemo( const isFormValid = useMemo(
() => isPopulatedObject(formInput, ['dataViewId']) && formInput.dataViewId !== '', () =>
[formInput] isPopulatedObject(formInput, ['dataViewId']) &&
formInput.dataViewId !== '' &&
isDataViewTimeBased === true,
[formInput, isDataViewTimeBased]
); );
const updatedProps = useMemo(() => { const updatedProps = useMemo(() => {
@ -78,7 +91,7 @@ export const LogRateAnalysisEmbeddableInitializer: FC<
onPreview(updatedProps); onPreview(updatedProps);
} }
}, },
[isFormValid, onPreview, updatedProps] [isFormValid, onPreview, updatedProps, isDataViewTimeBased]
); );
const setDataViewId = useCallback( const setDataViewId = useCallback(
@ -87,10 +100,36 @@ export const LogRateAnalysisEmbeddableInitializer: FC<
...formInput, ...formInput,
dataViewId: dataViewId ?? '', dataViewId: dataViewId ?? '',
}); });
setIsDataViewTimeBased(undefined);
}, },
[formInput] [formInput]
); );
useEffect(
function checkIsDataViewTimeBased() {
setIsDataViewTimeBased(undefined);
const { dataViewId } = formInput;
if (!dataViewId) {
return;
}
dataViews
.get(dataViewId)
.then((dataView) => {
if (!isMounted()) {
return;
}
setIsDataViewTimeBased(dataView.isTimeBased());
})
.catch(() => {
setIsDataViewTimeBased(undefined);
});
},
[dataViews, formInput, isMounted]
);
return ( return (
<> <>
<EuiFlyoutHeader <EuiFlyoutHeader
@ -121,21 +160,29 @@ export const LogRateAnalysisEmbeddableInitializer: FC<
defaultMessage: 'Data view', defaultMessage: 'Data view',
})} })}
> >
<IndexPatternSelect <>
autoFocus={!formInput.dataViewId} <IndexPatternSelect
fullWidth autoFocus={!formInput.dataViewId}
compressed fullWidth
indexPatternId={formInput.dataViewId} compressed
placeholder={i18n.translate( indexPatternId={formInput.dataViewId}
'xpack.aiops.embeddableLogRateAnalysis.config.dataViewSelectorPlaceholder', placeholder={i18n.translate(
{ 'xpack.aiops.embeddableLogRateAnalysis.config.dataViewSelectorPlaceholder',
defaultMessage: 'Select data view', {
} defaultMessage: 'Select data view',
}
)}
onChange={(newId) => {
setDataViewId(newId ?? '');
}}
/>
{isDataViewTimeBased === false && (
<>
<EuiSpacer size="m" />
<TimeFieldWarning />
</>
)} )}
onChange={(newId) => { </>
setDataViewId(newId ?? '');
}}
/>
</EuiFormRow> </EuiFormRow>
</EuiForm> </EuiForm>
</EuiFlyoutBody> </EuiFlyoutBody>

View file

@ -33,6 +33,7 @@ import { useAiopsAppContext } from '../../hooks/use_aiops_app_context';
import { DataSourceContextProvider } from '../../hooks/use_data_source'; import { DataSourceContextProvider } from '../../hooks/use_data_source';
import type { PatternAnalysisEmbeddableRuntimeState } from './types'; import type { PatternAnalysisEmbeddableRuntimeState } from './types';
import { PatternAnalysisSettings } from '../../components/log_categorization/log_categorization_for_embeddable/embeddable_menu'; import { PatternAnalysisSettings } from '../../components/log_categorization/log_categorization_for_embeddable/embeddable_menu';
import { TimeFieldWarning } from '../../components/time_field_warning';
import { RandomSampler } from '../../components/log_categorization/sampling_menu'; import { RandomSampler } from '../../components/log_categorization/sampling_menu';
import { import {
DEFAULT_PROBABILITY, DEFAULT_PROBABILITY,
@ -394,31 +395,3 @@ const TextFieldWarning = () => {
</> </>
); );
}; };
const TimeFieldWarning = () => {
return (
<>
<EuiCallOut
size="s"
title={i18n.translate(
'xpack.aiops.logCategorization.embeddableMenu.timeFieldWarning.title',
{
defaultMessage: 'The selected data view does not contain a time field.',
}
)}
color="warning"
iconType="warning"
>
<p>
{i18n.translate(
'xpack.aiops.logCategorization.embeddableMenu.timeFieldWarning.title.description',
{
defaultMessage: 'Pattern analysis can only be run on data views with a time field.',
}
)}
</p>
</EuiCallOut>
<EuiSpacer />
</>
);
};

View file

@ -146,6 +146,7 @@ const LogRateAnalysisEmbeddableWrapperWithDeps: FC<LogRateAnalysisPropsWithDeps>
setRerenderFlag((prev) => !prev); setRerenderFlag((prev) => !prev);
} }
}, [dataViewId, prevDataViewId]); }, [dataViewId, prevDataViewId]);
const showComponent = prevDataViewId === undefined || prevDataViewId === dataViewId;
// TODO: Remove data-shared-item as part of https://github.com/elastic/kibana/issues/179376> // TODO: Remove data-shared-item as part of https://github.com/elastic/kibana/issues/179376>
return ( return (
@ -159,7 +160,7 @@ const LogRateAnalysisEmbeddableWrapperWithDeps: FC<LogRateAnalysisPropsWithDeps>
padding: '10px', padding: '10px',
}} }}
> >
{prevDataViewId === dataViewId && ( {showComponent && (
<Router history={history}> <Router history={history}>
<ReloadContextProvider reload$={resultObservable$}> <ReloadContextProvider reload$={resultObservable$}>
<AiopsAppContext.Provider value={aiopsAppContextValue}> <AiopsAppContext.Provider value={aiopsAppContextValue}>