Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const DataViewEditor = ({
requireTimestampField = false,
editData,
allowAdHocDataView,
getDataViewHelpText,
}: DataViewEditorPropsWithServices) => {
const { Provider: KibanaReactContextProvider } =
createKibanaReactContext<DataViewEditorContext>(services);
Expand All @@ -44,6 +45,7 @@ export const DataViewEditor = ({
requireTimestampField={requireTimestampField}
editData={editData}
allowAdHocDataView={allowAdHocDataView}
getDataViewHelpText={getDataViewHelpText}
/>
</EuiFlyout>
</KibanaReactContextProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import React, { useEffect, useCallback } from 'react';
import type { ReactNode } from 'react';
import React, { useEffect, useCallback, useMemo } from 'react';
import { css } from '@emotion/react';
import {
EuiTitle,
Expand Down Expand Up @@ -70,6 +71,7 @@ export interface Props {
showManagementLink?: boolean;
allowAdHoc: boolean;
dataViewEditorService: DataViewEditorService;
getDataViewHelpText?: (dataView: DataView) => ReactNode | string | undefined;
}

const editorTitle = i18n.translate('indexPatternEditor.title', {
Expand All @@ -87,6 +89,7 @@ const IndexPatternEditorFlyoutContentComponent = ({
editData,
allowAdHoc,
showManagementLink,
getDataViewHelpText,
dataViewEditorService,
}: Props) => {
const styles = useMemoCss(componentStyles);
Expand Down Expand Up @@ -195,6 +198,10 @@ const IndexPatternEditorFlyoutContentComponent = ({
}, [dataViewEditorService, type]);

const getRollupIndices = (rollupCapsRes: RollupIndicesCapsResponse) => Object.keys(rollupCapsRes);
const titleHelpText = useMemo(
() => editData && getDataViewHelpText && getDataViewHelpText(editData),
[editData, getDataViewHelpText]
);

const onTypeChange = useCallback(
(newType: INDEX_PATTERN_TYPE) => {
Expand Down Expand Up @@ -298,6 +305,7 @@ const IndexPatternEditorFlyoutContentComponent = ({
indexPatternValidationProvider={
dataViewEditorService.indexPatternValidationProvider
}
titleHelpText={titleHelpText}
/>
</EuiFlexItem>
</EuiFlexGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const DataViewFlyoutContentContainer = ({
editData,
allowAdHocDataView,
showManagementLink,
getDataViewHelpText,
}: DataViewEditorProps) => {
const {
services: { dataViews, notifications, http },
Expand Down Expand Up @@ -100,6 +101,7 @@ const DataViewFlyoutContentContainer = ({
showManagementLink={showManagementLink}
allowAdHoc={allowAdHocDataView || false}
dataViewEditorService={dataViewEditorService}
getDataViewHelpText={getDataViewHelpText}
/>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import type { ChangeEvent } from 'react';
import type { ChangeEvent, ReactNode } from 'react';
import React, { useState, useMemo } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiFormRow, EuiFieldText } from '@elastic/eui';
Expand All @@ -30,6 +30,7 @@ interface TitleFieldProps {
matchedIndices: MatchedIndicesSet;
rollupIndex: string | null | undefined;
}>;
titleHelpText?: ReactNode | string;
}

const rollupIndexPatternNoMatchError = {
Expand Down Expand Up @@ -114,11 +115,13 @@ interface GetTitleConfigArgs {
isRollup: boolean;
matchedIndices: MatchedItem[];
rollupIndicesCapabilities: RollupIndicesCapsResponse;
titleHelpText?: ReactNode | string;
}

const getTitleConfig = ({
isRollup,
rollupIndicesCapabilities,
titleHelpText,
}: GetTitleConfigArgs): FieldConfig<string> => {
const titleFieldConfig = schema.title;

Expand All @@ -134,6 +137,7 @@ const getTitleConfig = ({
return {
...titleFieldConfig!,
validations,
helpText: titleHelpText,
};
};

Expand All @@ -142,6 +146,7 @@ export const TitleField = ({
matchedIndices$,
rollupIndicesCapabilities,
indexPatternValidationProvider,
titleHelpText,
}: TitleFieldProps) => {
const [appendedWildcard, setAppendedWildcard] = useState<boolean>(false);
const matchedIndices = useObservable(matchedIndices$, matchedIndiciesDefault).exactMatchedIndices;
Expand All @@ -152,8 +157,9 @@ export const TitleField = ({
isRollup,
matchedIndices,
rollupIndicesCapabilities,
titleHelpText,
}),
[isRollup, matchedIndices, rollupIndicesCapabilities]
[isRollup, matchedIndices, rollupIndicesCapabilities, titleHelpText]
);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export const getEditorOpener =
requireTimestampField = false,
allowAdHocDataView = false,
editData,
getDataViewHelpText,
}: DataViewEditorProps): CloseEditor => {
const closeEditor = () => {
if (overlayRef) {
Expand Down Expand Up @@ -80,6 +81,7 @@ export const getEditorOpener =
requireTimestampField={requireTimestampField}
allowAdHocDataView={allowAdHocDataView}
showManagementLink={Boolean(editData && editData.isPersisted())}
getDataViewHelpText={getDataViewHelpText}
/>
</KibanaReactContextProvider>,
core
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import type { FC } from 'react';
import type { FC, ReactNode } from 'react';
import type {
ApplicationStart,
IUiSettingsClient,
Expand Down Expand Up @@ -71,6 +71,10 @@ export interface DataViewEditorProps {
* if set to true a link to the management page is shown
*/
showManagementLink?: boolean;
/**
* Optional callback to get help text based on the active data view
*/
getDataViewHelpText?: (dataView: DataView) => ReactNode | string | undefined;
}

// eslint-disable-next-line @typescript-eslint/no-empty-interface
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export function ChangeDataView({
onEditDataView,
onCreateDefaultAdHocDataView,
onClosePopover,
getDataViewHelpText,
}: DataViewPickerProps) {
const { euiTheme } = useEuiTheme();
const [isPopoverOpen, setPopoverIsOpen] = useState(false);
Expand Down Expand Up @@ -178,6 +179,7 @@ export function ChangeDataView({
onSave: (updatedDataView) => {
onEditDataView(updatedDataView);
},
getDataViewHelpText,
});
} else {
application.navigateToApp('management', {
Expand Down Expand Up @@ -272,6 +274,7 @@ export function ChangeDataView({
onEditDataView,
searchListInputId,
selectableProps,
getDataViewHelpText,
]);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import type { ReactNode } from 'react';
import React from 'react';
import type { EuiButtonProps, EuiSelectableProps } from '@elastic/eui';
import type { DataView, DataViewListItem, DataViewSpec } from '@kbn/data-views-plugin/public';
Expand Down Expand Up @@ -76,6 +77,10 @@ export interface DataViewPickerProps {
* Optional callback when data view picker is closed
*/
onClosePopover?: () => void;
/**
* Optional callback to get help text based on the active data view
*/
getDataViewHelpText?: (dataView: DataView) => ReactNode | string | undefined;
}

export const DataViewPicker = ({
Expand All @@ -93,6 +98,7 @@ export const DataViewPicker = ({
selectableProps,
onCreateDefaultAdHocDataView,
isDisabled,
getDataViewHelpText,
}: DataViewPickerProps) => {
return (
<ChangeDataView
Expand All @@ -110,6 +116,7 @@ export const DataViewPicker = ({
savedDataViews={savedDataViews}
selectableProps={selectableProps}
isDisabled={isDisabled}
getDataViewHelpText={getDataViewHelpText}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
import { DataViewPicker as UnifiedDataViewPicker } from '@kbn/unified-search-plugin/public';
import React, { useCallback, useRef, useMemo, memo } from 'react';
import { useDispatch, useSelector } from 'react-redux';

import { DataView } from '@kbn/data-views-plugin/public';
import { EuiCode } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { EXPLORE_DATA_VIEW_PREFIX } from '../../../../common/constants';
import type { SourcererUrlState } from '../../../sourcerer/store/model';
import { useUpdateUrlParam } from '../../../common/utils/global_query_string';
Expand All @@ -21,7 +22,7 @@ import { useSelectDataView } from '../../hooks/use_select_data_view';
import { DataViewManagerScopeName } from '../../constants';
import { useManagedDataViews } from '../../hooks/use_managed_data_views';
import { useSavedDataViews } from '../../hooks/use_saved_data_views';
import { DEFAULT_SECURITY_DATA_VIEW, LOADING } from './translations';
import { LOADING } from './translations';
import { DATA_VIEW_PICKER_TEST_ID } from './constants';
import { useDataView } from '../../hooks/use_data_view';
import { browserFieldsManager } from '../../utils/security_browser_fields_manager';
Expand Down Expand Up @@ -137,6 +138,20 @@ export const DataViewPicker = memo(({ scope, onClosePopover, disabled }: DataVie
[dataViewId, data.dataViews, scope, dataViewFieldEditor, handleChangeDataView]
);

const getDataViewHelpText = useCallback(
(dv: DataView) =>
dv.id === defaultDataViewId ? (
<FormattedMessage
id="xpack.securitySolution.dataViewManager.getDataViewHelpText"
defaultMessage="Changes made here won't be saved permanently. To update the default Security indices, edit {code} in Advanced Settings."
values={{
code: <EuiCode>{'securitySolution:defaultIndex'}</EuiCode>,
}}
/>
) : undefined,
[defaultDataViewId]
);

/**
* Selects data view again. After changes are made to the data view, this results in cache invalidation and will force the reload everywhere.
*/
Expand All @@ -162,16 +177,10 @@ export const DataViewPicker = memo(({ scope, onClosePopover, disabled }: DataVie
return { label: LOADING };
}

if (dataView?.id === defaultDataViewId) {
return {
label: DEFAULT_SECURITY_DATA_VIEW,
};
}

return {
label: dataView?.name || dataView?.id || 'Data view',
};
}, [dataView?.id, dataView?.name, defaultDataViewId, status]);
}, [dataView?.id, dataView?.name, status]);

return (
<div data-test-subj={DATA_VIEW_PICKER_TEST_ID}>
Expand All @@ -187,6 +196,7 @@ export const DataViewPicker = memo(({ scope, onClosePopover, disabled }: DataVie
savedDataViews={savedDataViews}
managedDataViews={managedDataViews}
onClosePopover={onClosePopover}
getDataViewHelpText={getDataViewHelpText}
/>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,20 @@ export const LOADING = i18n.translate('xpack.securitySolution.dataViewManager.lo
export const DEFAULT_SECURITY_DATA_VIEW = i18n.translate(
'xpack.securitySolution.dataViewManager.defaultSecurityDataView',
{
defaultMessage: 'Default security data view',
defaultMessage: 'Security solution default',
}
);

export const DEFAULT_SECURITY_ALERT_DATA_VIEW = i18n.translate(
'xpack.securitySolution.dataViewManager.defaultSecurityAlertDataView',
{
defaultMessage: 'Security alert data view',
defaultMessage: 'Security solution alerts',
}
);

export const SECURITY_SOLUTION_EXPLORE_DATA_VIEW = i18n.translate(
'xpack.securitySolution.dataViewManager.securitySolutionExploreDataView',
{
defaultMessage: 'Security solution explore',
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ describe('useManagedDataViews', () => {
});
});

it('should filter data views to only include those with DEFAULT_SECURITY_SOLUTION_DATA_VIEW_ID', () => {
it('should filter data views to only include those with Alert Data View ID', () => {
// Create mock data views with a mix of IDs
const mockDataViews = [
{ id: DEFAULT_SECURITY_SOLUTION_DATA_VIEW_ID, title: 'Security solution data view' },
Expand All @@ -67,27 +67,17 @@ describe('useManagedDataViews', () => {
const { result } = renderHook(() => useManagedDataViews());

// Expect only data views with matching ID to be included
expect(result.current.length).toBe(3);
expect(result.current.length).toBe(1);

// Verify the IDs of the filtered data views
result.current.forEach((dataView, i) => {
if (i <= 1) {
expect(dataView.id).toBe(DEFAULT_SECURITY_SOLUTION_DATA_VIEW_ID);
} else {
expect(dataView.id).toBe(DEFAULT_ALERT_DATA_VIEW_ID);
}
});

// Verify DataView constructor was called with correct arguments
expect(DataView).toHaveBeenCalledTimes(3);
expect(DataView).toHaveBeenCalledWith({
spec: mockDataViews[0],
fieldFormats: mockFieldFormats,
});
expect(DataView).toHaveBeenCalledWith({
spec: mockDataViews[2],
fieldFormats: mockFieldFormats,
});
expect(DataView).toHaveBeenCalledTimes(1);
expect(DataView).toHaveBeenCalledWith({
spec: mockDataViews[3],
fieldFormats: mockFieldFormats,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ export const useManagedDataViews = (): DataView[] => {
const {
dataViews: dataViewSpecs,
adhocDataViews,
defaultDataViewId,
alertDataViewId,
} = useSelector(sharedStateSelector);
const {
Expand All @@ -29,13 +28,8 @@ export const useManagedDataViews = (): DataView[] => {
return useMemo(
() =>
[...dataViewSpecs, ...adhocDataViews]
.filter(
(dv) =>
dv.id === defaultDataViewId ||
dv.id === alertDataViewId ||
dv.id?.startsWith(EXPLORE_DATA_VIEW_PREFIX)
)
.filter((dv) => dv.id === alertDataViewId || dv.id?.startsWith(EXPLORE_DATA_VIEW_PREFIX))
.map((spec) => new DataView({ spec, fieldFormats })),
[dataViewSpecs, adhocDataViews, defaultDataViewId, alertDataViewId, fieldFormats]
[dataViewSpecs, adhocDataViews, alertDataViewId, fieldFormats]
);
};
Loading