Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[8.16] [Security Solution][Notes] - fix createdBy filter for notes management page (#197706) #197911

Merged
merged 4 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion oas_docs/output/kibana.serverless.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15012,7 +15012,7 @@ paths:
nullable: true
type: string
- in: query
name: userFilter
name: createdByFilter
schema:
nullable: true
type: string
Expand Down
2 changes: 1 addition & 1 deletion oas_docs/output/kibana.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18442,7 +18442,7 @@ paths:
nullable: true
type: string
- in: query
name: userFilter
name: createdByFilter
schema:
nullable: true
type: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ export const ALERT_SUPPRESSION_RULE_DETAILS = i18n.translate(
);

export const UPGRADE_NOTES_MANAGEMENT_USER_FILTER = (requiredLicense: string) =>
i18n.translate('securitySolutionPackages.noteManagement.userFilter.upsell', {
defaultMessage: 'Upgrade to {requiredLicense} to make use of user filters',
i18n.translate('securitySolutionPackages.noteManagement.createdByFilter.upsell', {
defaultMessage: 'Upgrade to {requiredLicense} to make use of createdBy filter',
values: {
requiredLicense,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const GetNotesRequestQuery = z.object({
sortField: z.string().nullable().optional(),
sortOrder: z.string().nullable().optional(),
filter: z.string().nullable().optional(),
userFilter: z.string().nullable().optional(),
createdByFilter: z.string().nullable().optional(),
associatedFilter: AssociatedFilterType.optional(),
});
export type GetNotesRequestQueryInput = z.input<typeof GetNotesRequestQuery>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ paths:
type: string
nullable: true
- in: query
name: userFilter
name: createdByFilter
schema:
nullable: true
type: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ paths:
nullable: true
type: string
- in: query
name: userFilter
name: createdByFilter
schema:
nullable: true
type: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ paths:
nullable: true
type: string
- in: query
name: userFilter
name: createdByFilter
schema:
nullable: true
type: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,7 @@ export const mockGlobalState: State = {
direction: 'desc' as const,
},
filter: '',
userFilter: '',
createdByFilter: '',
associatedFilter: AssociatedFilter.all,
search: '',
selectedIds: [],
Expand Down
6 changes: 3 additions & 3 deletions x-pack/plugins/security_solution/public/notes/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const fetchNotes = async ({
sortField,
sortOrder,
filter,
userFilter,
createdByFilter,
associatedFilter,
search,
}: {
Expand All @@ -52,7 +52,7 @@ export const fetchNotes = async ({
sortField: string;
sortOrder: string;
filter: string;
userFilter: string;
createdByFilter: string;
associatedFilter: AssociatedFilter;
search: string;
}) => {
Expand All @@ -63,7 +63,7 @@ export const fetchNotes = async ({
sortField,
sortOrder,
filter,
userFilter,
createdByFilter,
associatedFilter,
search,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

import { fireEvent, render, screen } from '@testing-library/react';
import React from 'react';
import { UserFilterDropdown } from './user_filter_dropdown';
import { USER_SELECT_TEST_ID } from './test_ids';
import { CreatedByFilterDropdown } from './created_by_filter_dropdown';
import { CREATED_BY_SELECT_TEST_ID } from './test_ids';
import { useSuggestUsers } from '../../common/components/user_profiles/use_suggest_users';
import { useLicense } from '../../common/hooks/use_license';
import { useUpsellingMessage } from '../../common/hooks/use_upselling';
Expand All @@ -32,16 +32,25 @@ describe('UserFilterDropdown', () => {
jest.clearAllMocks();
(useSuggestUsers as jest.Mock).mockReturnValue({
isLoading: false,
data: [{ user: { username: 'test' } }, { user: { username: 'elastic' } }],
data: [
{
uid: '1',
user: { username: 'test' },
},
{
uid: '2',
user: { username: 'elastic' },
},
],
});
(useLicense as jest.Mock).mockReturnValue({ isPlatinumPlus: () => true });
(useUpsellingMessage as jest.Mock).mockReturnValue('upsellingMessage');
});

it('should render the component enabled', () => {
const { getByTestId } = render(<UserFilterDropdown />);
const { getByTestId } = render(<CreatedByFilterDropdown />);

const dropdown = getByTestId(USER_SELECT_TEST_ID);
const dropdown = getByTestId(CREATED_BY_SELECT_TEST_ID);

expect(dropdown).toBeInTheDocument();
expect(dropdown).not.toHaveClass('euiComboBox-isDisabled');
Expand All @@ -50,13 +59,13 @@ describe('UserFilterDropdown', () => {
it('should render the dropdown disabled', async () => {
(useLicense as jest.Mock).mockReturnValue({ isPlatinumPlus: () => false });

const { getByTestId } = render(<UserFilterDropdown />);
const { getByTestId } = render(<CreatedByFilterDropdown />);

expect(getByTestId(USER_SELECT_TEST_ID)).toHaveClass('euiComboBox-isDisabled');
expect(getByTestId(CREATED_BY_SELECT_TEST_ID)).toHaveClass('euiComboBox-isDisabled');
});

it('should call the correct action when select a user', async () => {
const { getByTestId } = render(<UserFilterDropdown />);
const { getByTestId } = render(<CreatedByFilterDropdown />);

const userSelect = getByTestId('comboBoxSearchInput');
userSelect.focus();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,26 @@ import { i18n } from '@kbn/i18n';
import type { EuiComboBoxOptionOption } from '@elastic/eui/src/components/combo_box/types';
import { useLicense } from '../../common/hooks/use_license';
import { useUpsellingMessage } from '../../common/hooks/use_upselling';
import { USER_SELECT_TEST_ID } from './test_ids';
import { CREATED_BY_SELECT_TEST_ID } from './test_ids';
import { useSuggestUsers } from '../../common/components/user_profiles/use_suggest_users';
import { userFilterUsers } from '..';
import { userFilterCreatedBy } from '..';

export const USERS_DROPDOWN = i18n.translate('xpack.securitySolution.notes.usersDropdownLabel', {
defaultMessage: 'Users',
export const CREATED_BY = i18n.translate('xpack.securitySolution.notes.createdByDropdownLabel', {
defaultMessage: 'Created by',
});

export const UserFilterDropdown = React.memo(() => {
interface User {
/**
* uuid of the UserProfile
*/
id: string;
/**
* full_name || email || username of the UserProfile
*/
label: string;
}

export const CreatedByFilterDropdown = React.memo(() => {
const dispatch = useDispatch();
const isPlatinumPlus = useLicense().isPlatinumPlus();
const upsellingMessage = useUpsellingMessage('note_management_user_filter');
Expand All @@ -30,34 +41,36 @@ export const UserFilterDropdown = React.memo(() => {
searchTerm: '',
enabled: isPlatinumPlus,
});
const users = useMemo(

const users: User[] = useMemo(
() =>
(data || []).map((userProfile: UserProfileWithAvatar) => ({
label: userProfile.user.full_name || userProfile.user.username,
id: userProfile.uid,
label: userProfile.user.full_name || userProfile.user.email || userProfile.user.username,
})),
[data]
);

const [selectedUser, setSelectedUser] = useState<Array<EuiComboBoxOptionOption<string>>>();
const [selectedUser, setSelectedUser] = useState<Array<EuiComboBoxOptionOption<User>>>();
const onChange = useCallback(
(user: Array<EuiComboBoxOptionOption<string>>) => {
(user: Array<EuiComboBoxOptionOption<User>>) => {
setSelectedUser(user);
dispatch(userFilterUsers(user.length > 0 ? user[0].label : ''));
dispatch(userFilterCreatedBy(user.length > 0 ? (user[0].id as string) : ''));
},
[dispatch]
);

const dropdown = useMemo(
() => (
<EuiComboBox
prepend={USERS_DROPDOWN}
prepend={CREATED_BY}
singleSelection={{ asPlainText: true }}
options={users}
selectedOptions={selectedUser}
onChange={onChange}
isLoading={isPlatinumPlus && isLoading}
isDisabled={!isPlatinumPlus}
data-test-subj={USER_SELECT_TEST_ID}
data-test-subj={CREATED_BY_SELECT_TEST_ID}
/>
),
[isLoading, isPlatinumPlus, onChange, selectedUser, users]
Expand All @@ -76,4 +89,4 @@ export const UserFilterDropdown = React.memo(() => {
);
});

UserFilterDropdown.displayName = 'UserFilterDropdown';
CreatedByFilterDropdown.displayName = 'CreatedByFilterDropdown';
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import { render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { SearchRow } from './search_row';
import { ASSOCIATED_NOT_SELECT_TEST_ID, SEARCH_BAR_TEST_ID, USER_SELECT_TEST_ID } from './test_ids';
import {
ASSOCIATED_NOT_SELECT_TEST_ID,
SEARCH_BAR_TEST_ID,
CREATED_BY_SELECT_TEST_ID,
} from './test_ids';
import { AssociatedFilter } from '../../../common/notes/constants';
import { useSuggestUsers } from '../../common/components/user_profiles/use_suggest_users';
import { TestProviders } from '../../common/mock';
Expand Down Expand Up @@ -43,7 +47,7 @@ describe('SearchRow', () => {
);

expect(getByTestId(SEARCH_BAR_TEST_ID)).toBeInTheDocument();
expect(getByTestId(USER_SELECT_TEST_ID)).toBeInTheDocument();
expect(getByTestId(CREATED_BY_SELECT_TEST_ID)).toBeInTheDocument();
expect(getByTestId(ASSOCIATED_NOT_SELECT_TEST_ID)).toBeInTheDocument();
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
} from '@elastic/eui';
import { useDispatch } from 'react-redux';
import { i18n } from '@kbn/i18n';
import { UserFilterDropdown } from './user_filter_dropdown';
import { CreatedByFilterDropdown } from './created_by_filter_dropdown';
import { ASSOCIATED_NOT_SELECT_TEST_ID, SEARCH_BAR_TEST_ID } from './test_ids';
import { userFilterAssociatedNotes, userSearchedNotes } from '..';
import { AssociatedFilter } from '../../../common/notes/constants';
Expand Down Expand Up @@ -65,7 +65,7 @@ export const SearchRow = React.memo(() => {
<EuiSearchBar box={searchBox} onChange={onQueryChange} defaultQuery="" />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<UserFilterDropdown />
<CreatedByFilterDropdown />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiSelect
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ export const TIMELINE_DESCRIPTION_COMMENT_TEST_ID = `${PREFIX}TimelineDescriptio
export const NOTE_CONTENT_BUTTON_TEST_ID = `${PREFIX}NoteContentButton` as const;
export const NOTE_CONTENT_POPOVER_TEST_ID = `${PREFIX}NoteContentPopover` as const;
export const SEARCH_BAR_TEST_ID = `${PREFIX}SearchBar` as const;
export const USER_SELECT_TEST_ID = `${PREFIX}UserSelect` as const;
export const CREATED_BY_SELECT_TEST_ID = `${PREFIX}CreatedBySelect` as const;
export const ASSOCIATED_NOT_SELECT_TEST_ID = `${PREFIX}AssociatedNoteSelect` as const;
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
selectNotesTableSelectedIds,
selectNotesTableSearch,
userSelectedBulkDelete,
selectNotesTableUserFilters,
selectNotesTableCreatedByFilter,
selectNotesTableAssociatedFilter,
} from '..';

Expand Down Expand Up @@ -53,8 +53,8 @@ export const NotesUtilityBar = React.memo(() => {
const pagination = useSelector(selectNotesPagination);
const sort = useSelector(selectNotesTableSort);
const selectedItems = useSelector(selectNotesTableSelectedIds);
const notesUserFilters = useSelector(selectNotesTableUserFilters);
const notesAssociatedFilters = useSelector(selectNotesTableAssociatedFilter);
const notesCreatedByFilter = useSelector(selectNotesTableCreatedByFilter);
const notesAssociatedFilter = useSelector(selectNotesTableAssociatedFilter);
const resultsCount = useMemo(() => {
const { perPage, page, total } = pagination;
const startOfCurrentPage = perPage * (page - 1) + 1;
Expand Down Expand Up @@ -87,8 +87,8 @@ export const NotesUtilityBar = React.memo(() => {
sortField: sort.field,
sortOrder: sort.direction,
filter: '',
userFilter: notesUserFilters,
associatedFilter: notesAssociatedFilters,
createdByFilter: notesCreatedByFilter,
associatedFilter: notesAssociatedFilter,
search: notesSearch,
})
);
Expand All @@ -98,8 +98,8 @@ export const NotesUtilityBar = React.memo(() => {
pagination.perPage,
sort.field,
sort.direction,
notesUserFilters,
notesAssociatedFilters,
notesCreatedByFilter,
notesAssociatedFilter,
notesSearch,
]);
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import {
selectNotesTablePendingDeleteIds,
selectFetchNotesError,
ReqStatus,
selectNotesTableUserFilters,
selectNotesTableCreatedByFilter,
selectNotesTableAssociatedFilter,
} from '..';
import type { NotesState } from '..';
Expand Down Expand Up @@ -121,8 +121,8 @@ export const NoteManagementPage = () => {
const pagination = useSelector(selectNotesPagination);
const sort = useSelector(selectNotesTableSort);
const notesSearch = useSelector(selectNotesTableSearch);
const notesUserFilters = useSelector(selectNotesTableUserFilters);
const notesAssociatedFilters = useSelector(selectNotesTableAssociatedFilter);
const notesCreatedByFilter = useSelector(selectNotesTableCreatedByFilter);
const notesAssociatedFilter = useSelector(selectNotesTableAssociatedFilter);
const pendingDeleteIds = useSelector(selectNotesTablePendingDeleteIds);
const isDeleteModalVisible = pendingDeleteIds.length > 0;
const fetchNotesStatus = useSelector(selectFetchNotesStatus);
Expand All @@ -138,8 +138,8 @@ export const NoteManagementPage = () => {
sortField: sort.field,
sortOrder: sort.direction,
filter: '',
userFilter: notesUserFilters,
associatedFilter: notesAssociatedFilters,
createdByFilter: notesCreatedByFilter,
associatedFilter: notesAssociatedFilter,
search: notesSearch,
})
);
Expand All @@ -149,8 +149,8 @@ export const NoteManagementPage = () => {
pagination.perPage,
sort.field,
sort.direction,
notesUserFilters,
notesAssociatedFilters,
notesCreatedByFilter,
notesAssociatedFilter,
notesSearch,
]);

Expand Down
Loading