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

[RAM] Cases bulk actions & column improvements #151761

Merged
merged 60 commits into from
Mar 29, 2023
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
17156bc
Rename the Cases column label
cnasikas Feb 21, 2023
9b1ad7e
Change Cases fields label
cnasikas Feb 21, 2023
b747603
Disable sorting on the cases column
cnasikas Feb 21, 2023
18e3eab
Improve loading
cnasikas Feb 21, 2023
3bd0d9c
Create case system bulk actions
cnasikas Feb 22, 2023
30364d1
Remove case bulk actions from o11y
cnasikas Feb 22, 2023
95d960a
Remove case context from o11y
cnasikas Feb 22, 2023
392d713
Merge branch 'main' into cases_column
cnasikas Feb 22, 2023
794081d
Remove case bulk actions from security solution
cnasikas Feb 23, 2023
615b181
Remove useGetUserCasesPermissions
cnasikas Feb 23, 2023
293b03e
Refresh after attaching an alert to a case
cnasikas Feb 28, 2023
fd01585
Merge branch 'main' into cases_column
cnasikas Feb 28, 2023
93ca5c4
Refetch alerts when attaching an alert to a case
cnasikas Feb 28, 2023
fe3a415
Fix types
cnasikas Mar 1, 2023
ca13e3e
Add tests
cnasikas Mar 1, 2023
1c60efb
Fix types
cnasikas Mar 1, 2023
cc612d6
Skip flaky test
cnasikas Mar 1, 2023
de11ba5
Run test 100 times on CI
cnasikas Mar 1, 2023
ac58b9a
Merge branch 'main' into cases_column
cnasikas Mar 2, 2023
3b06c13
Allow solutions to pass an array of owners
cnasikas Mar 2, 2023
a571e4d
Improve case logic
cnasikas Mar 2, 2023
7e01a3a
Fix tests
cnasikas Mar 2, 2023
c590c5b
Merge branch 'main' into cases_column
cnasikas Mar 6, 2023
893f4bb
Fix test
cnasikas Mar 6, 2023
abaeac8
Run test 100 times
cnasikas Mar 7, 2023
b475a38
Reduce to 50
cnasikas Mar 7, 2023
8790353
Reduce to 25
cnasikas Mar 8, 2023
029ad2d
Merge branch 'main' into cases_column
cnasikas Mar 8, 2023
4ad3ce2
Mock getComputedStyle
cnasikas Mar 8, 2023
9efed89
Run the test 60 more times
cnasikas Mar 8, 2023
f872d5a
Remove loop
cnasikas Mar 8, 2023
e1c23ed
Merge branch 'main' into cases_column
cnasikas Mar 8, 2023
669e062
Fix system field search
cnasikas Mar 9, 2023
936e5db
Merge branch 'main' into cases_column
cnasikas Mar 9, 2023
0d88c1e
Fix i18n
cnasikas Mar 9, 2023
6b0fd0c
Include english label in translated search
cnasikas Mar 10, 2023
95ac749
Merge branch 'main' into cases_column
cnasikas Mar 10, 2023
0491906
Merge branch 'main' into cases_column
cnasikas Mar 20, 2023
a5339c7
Merge branch 'main' into cases_column
cnasikas Mar 21, 2023
9b0d5f3
Merge branch 'main' into cases_column
cnasikas Mar 21, 2023
f66efed
Remove cases context from infra
cnasikas Mar 21, 2023
4feb65d
Fix bug with cases permissions
cnasikas Mar 21, 2023
f3e42af
Merge branch 'main' into cases_column
cnasikas Mar 23, 2023
4f04599
Fix tests and types
cnasikas Mar 23, 2023
e5a68b4
Add more tests
cnasikas Mar 23, 2023
2f25c16
Make syncing alerts configurable
cnasikas Mar 23, 2023
10720d9
Do not fetch if the case is not visible
cnasikas Mar 23, 2023
c4da85c
Merge branch 'main' into cases_column
cnasikas Mar 27, 2023
00f66b2
Fix test
cnasikas Mar 27, 2023
27e2e0e
Merge branch 'main' into cases_column
kibanamachine Mar 28, 2023
4bdba9a
Merge branch 'main' into cases_column
kibanamachine Mar 28, 2023
ebe99a2
Merge branch 'main' into cases_column
kibanamachine Mar 28, 2023
9884670
Skip flaky tests
cnasikas Mar 28, 2023
ddef6ff
Merge branch 'cases_column' of github.com:cnasikas/kibana into cases_…
cnasikas Mar 28, 2023
6bd1bca
Merge branch 'main' into cases_column
kibanamachine Mar 28, 2023
9835e1a
Merge branch 'main' into cases_column
kibanamachine Mar 28, 2023
49cd3ce
Merge branch 'main' into cases_column
kibanamachine Mar 29, 2023
73d1ca9
fix osquery cypress
patrykkopycinski Mar 29, 2023
78ca0ce
osquery e2e selector fix
szwarckonrad Mar 29, 2023
3663886
Merge branch 'main' into cases_column
kibanamachine Mar 29, 2023
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 @@ -137,7 +137,6 @@ export class TriggersActionsUiExamplePlugin

const config: AlertsTableConfigurationRegistry = {
id: 'observabilityCases',
casesFeatureId: 'observabilityCases',
columns,
useInternalFlyout,
getRenderCellValue: () => (props: any) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ interface EventNonEcsData {

type CommentRequestAlertTypeWithoutOwner = Omit<CommentRequestAlertType, 'owner'>;

export const groupAlertsByRule = (items: Event[]): CaseAttachmentsWithoutOwner => {
export type GroupAlertsByRule = (items: Event[]) => CaseAttachmentsWithoutOwner;

export const groupAlertsByRule: GroupAlertsByRule = (items) => {
const attachmentsByRule = items.reduce<Record<string, CommentRequestAlertTypeWithoutOwner>>(
(acc, item) => {
const rule = getRuleIdFromEvent(item);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ jest.mock('./all_cases_selector_modal', () => {
};
});

const onSuccess = jest.fn();
const useCasesToastMock = useCasesToast as jest.Mock;

const AllCasesSelectorModalMock = AllCasesSelectorModal as unknown as jest.Mock;

// test component to test the hook integration
const TestComponent: React.FC = () => {
const hook = useCasesAddToExistingCaseModal();
const hook = useCasesAddToExistingCaseModal({ onSuccess });

const onClick = () => {
hook.open({ attachments: [alertComment] });
Expand Down Expand Up @@ -81,13 +81,14 @@ describe('use cases add to existing case modal hook', () => {
};

const defaultParams = () => {
return { onRowClick: jest.fn() };
return { onSuccess };
};

beforeEach(() => {
appMockRender = createAppMockRenderer();
dispatch.mockReset();
AllCasesSelectorModalMock.mockReset();
onSuccess.mockReset();
});

it('should throw if called outside of a cases context', () => {
Expand Down Expand Up @@ -167,6 +168,31 @@ describe('use cases add to existing case modal hook', () => {
expect(mockedToastSuccess).toHaveBeenCalled();
});

it('should call onSuccess when defined', async () => {
const mockBulkCreateAttachments = jest.fn();

useCreateAttachmentsMock.mockReturnValueOnce({
createAttachments: mockBulkCreateAttachments,
});

const mockedToastSuccess = jest.fn();
useCasesToastMock.mockReturnValue({
showSuccessAttach: mockedToastSuccess,
});

AllCasesSelectorModalMock.mockImplementation(({ onRowClick }) => {
onRowClick({ id: 'test' } as Case);
return null;
});

const result = appMockRender.render(<TestComponent />);
userEvent.click(result.getByTestId('open-modal'));

await waitFor(() => {
expect(onSuccess).toHaveBeenCalled();
});
});

it('should not call createAttachments nor show toast success when a case is not selected', async () => {
const mockBulkCreateAttachments = jest.fn();
useCreateAttachmentsMock.mockReturnValueOnce({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,18 @@ import type { CaseAttachmentsWithoutOwner } from '../../../types';
import { useCreateAttachments } from '../../../containers/use_create_attachments';
import { useAddAttachmentToExistingCaseTransaction } from '../../../common/apm/use_cases_transactions';

type AddToExistingFlyoutProps = AllCasesSelectorModalProps & {
type AddToExistingFlyoutProps = Omit<AllCasesSelectorModalProps, 'onRowClick'> & {
toastTitle?: string;
toastContent?: string;
onSuccess?: (theCase: Case) => void;
};

export const useCasesAddToExistingCaseModal = (props: AddToExistingFlyoutProps = {}) => {
const createNewCaseFlyout = useCasesAddToNewCaseFlyout({
onClose: props.onClose,
// TODO there's no need for onSuccess to be async. This will be fixed
// in a follow up clean up
onSuccess: async (theCase?: Case) => {
if (props.onRowClick) {
return props.onRowClick(theCase);
onSuccess: (theCase?: Case) => {
if (props.onSuccess && theCase) {
return props.onSuccess(theCase);
}
},
toastTitle: props.toastTitle,
Expand Down Expand Up @@ -74,6 +73,10 @@ export const useCasesAddToExistingCaseModal = (props: AddToExistingFlyoutProps =
throwOnError: true,
});

if (props.onSuccess) {
props.onSuccess(theCase);
}

casesToasts.showSuccessAttach({
theCase,
attachments,
Expand All @@ -85,10 +88,6 @@ export const useCasesAddToExistingCaseModal = (props: AddToExistingFlyoutProps =
// error toast is handled
// inside the createAttachments method
}

if (props.onRowClick) {
props.onRowClick(theCase);
}
},
[
props,
Expand All @@ -114,6 +113,7 @@ export const useCasesAddToExistingCaseModal = (props: AddToExistingFlyoutProps =
},
onClose: () => {
closeModal();

if (props.onClose) {
return props.onClose();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { EuiFlyout, EuiFlyoutHeader, EuiTitle, EuiFlyoutBody } from '@elastic/eu

import { QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { noop } from 'lodash';
import type { CasePostRequest } from '../../../../common/api';
import * as i18n from '../translations';
import type { Case } from '../../../../common/ui/types';
Expand All @@ -25,7 +26,7 @@ export interface CreateCaseFlyoutProps {
createAttachments: UseCreateAttachments['createAttachments']
) => Promise<void>;
onClose?: () => void;
onSuccess?: (theCase: Case) => Promise<void>;
onSuccess?: (theCase: Case) => void;
attachments?: CaseAttachmentsWithoutOwner;
headerContent?: React.ReactNode;
initialValue?: Pick<CasePostRequest, 'title' | 'description'>;
Expand Down Expand Up @@ -76,8 +77,8 @@ const FormWrapper = styled.div`

export const CreateCaseFlyout = React.memo<CreateCaseFlyoutProps>(
({ afterCaseCreated, attachments, headerContent, initialValue, onClose, onSuccess }) => {
const handleCancel = onClose || function () {};
const handleOnSuccess = onSuccess || async function () {};
const handleCancel = onClose || noop;
const handleOnSuccess = onSuccess || noop;

return (
<QueryClientProvider client={casesQueryClient}>
Expand Down
4 changes: 2 additions & 2 deletions x-pack/plugins/cases/public/components/create/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export interface CreateCaseFormFieldsProps {
}
export interface CreateCaseFormProps extends Pick<Partial<CreateCaseFormFieldsProps>, 'withSteps'> {
onCancel: () => void;
onSuccess: (theCase: Case) => Promise<void>;
onSuccess: (theCase: Case) => void;
afterCaseCreated?: (
theCase: Case,
createAttachments: UseCreateAttachments['createAttachments']
Expand Down Expand Up @@ -208,7 +208,7 @@ export const CreateCaseForm: React.FC<CreateCaseFormProps> = React.memo(
onConfirmationCallback: handleOnConfirmationCallback,
});

const handleOnSuccess = (theCase: Case): Promise<void> => {
const handleOnSuccess = (theCase: Case): void => {
removeItemFromSessionStorage(draftStorageKey);
return onSuccess(theCase);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ interface Props {
createAttachments: UseCreateAttachments['createAttachments']
) => Promise<void>;
children?: JSX.Element | JSX.Element[];
onSuccess?: (theCase: Case) => Promise<void>;
onSuccess?: (theCase: Case) => void;
attachments?: CaseAttachmentsWithoutOwner;
initialValue?: Pick<CasePostRequest, 'title' | 'description'>;
}
Expand Down Expand Up @@ -112,7 +112,7 @@ export const FormContext: React.FC<Props> = ({
}

if (onSuccess && updatedCase) {
await onSuccess(updatedCase);
onSuccess(updatedCase);
}
}
},
Expand Down
7 changes: 5 additions & 2 deletions x-pack/plugins/cases/public/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,13 @@ const uiMock: jest.Mocked<CasesUiStart['ui']> = {
};

export const openAddToExistingCaseModalMock = jest.fn();
export const openAddToNewCaseFlyoutMock = jest.fn();

const hooksMock: jest.Mocked<CasesUiStart['hooks']> = {
getUseCasesAddToNewCaseFlyout: jest.fn(),
getUseCasesAddToExistingCaseModal: jest.fn().mockImplementation(() => ({
useCasesAddToNewCaseFlyout: jest.fn().mockImplementation(() => ({
open: openAddToNewCaseFlyoutMock,
})),
useCasesAddToExistingCaseModal: jest.fn().mockImplementation(() => ({
open: openAddToExistingCaseModalMock,
})),
};
Expand Down
8 changes: 4 additions & 4 deletions x-pack/plugins/cases/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,14 +133,14 @@ export class CasesUiPlugin
externalReferenceAttachmentTypeRegistry: this.externalReferenceAttachmentTypeRegistry,
persistableStateAttachmentTypeRegistry: this.persistableStateAttachmentTypeRegistry,
}),
// @deprecated Please use the hook getUseCasesAddToNewCaseFlyout
// @deprecated Please use the hook useCasesAddToNewCaseFlyout
getCreateCaseFlyout: (props) =>
getCreateCaseFlyoutLazy({
...props,
externalReferenceAttachmentTypeRegistry: this.externalReferenceAttachmentTypeRegistry,
persistableStateAttachmentTypeRegistry: this.persistableStateAttachmentTypeRegistry,
}),
// @deprecated Please use the hook getUseCasesAddToExistingCaseModal
// @deprecated Please use the hook useCasesAddToExistingCaseModal
getAllCasesSelectorModal: (props) =>
getAllCasesSelectorModalLazy({
...props,
Expand All @@ -149,8 +149,8 @@ export class CasesUiPlugin
}),
},
hooks: {
getUseCasesAddToNewCaseFlyout: useCasesAddToNewCaseFlyout,
getUseCasesAddToExistingCaseModal: useCasesAddToExistingCaseModal,
useCasesAddToNewCaseFlyout,
useCasesAddToExistingCaseModal,
},
helpers: {
canUseCases: canUseCases(core.application.capabilities),
Expand Down
8 changes: 4 additions & 4 deletions x-pack/plugins/cases/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import type { GetAllCasesSelectorModalProps } from './client/ui/get_all_cases_se
import type { GetCreateCaseFlyoutProps } from './client/ui/get_create_case_flyout';
import type { GetRecentCasesProps } from './client/ui/get_recent_cases';
import type { Cases, CasesStatus, CasesMetrics } from '../common/ui';
import type { groupAlertsByRule } from './client/helpers/group_alerts_by_rule';
import type { GroupAlertsByRule } from './client/helpers/group_alerts_by_rule';
import type { getUICapabilities } from './client/helpers/capabilities';
import type { AttachmentFramework } from './client/attachment_framework/types';
import type { ExternalReferenceAttachmentTypeRegistry } from './client/attachment_framework/external_reference_registry';
Expand Down Expand Up @@ -135,8 +135,8 @@ export interface CasesUiStart {
getRecentCases: (props: GetRecentCasesProps) => ReactElement<GetRecentCasesProps>;
};
hooks: {
getUseCasesAddToNewCaseFlyout: UseCasesAddToNewCaseFlyout;
getUseCasesAddToExistingCaseModal: UseCasesAddToExistingCaseModal;
useCasesAddToNewCaseFlyout: UseCasesAddToNewCaseFlyout;
useCasesAddToExistingCaseModal: UseCasesAddToExistingCaseModal;
};
helpers: {
/**
Expand All @@ -150,7 +150,7 @@ export interface CasesUiStart {
canUseCases: ReturnType<typeof canUseCases>;
getUICapabilities: typeof getUICapabilities;
getRuleIdFromEvent: typeof getRuleIdFromEvent;
groupAlertsByRule: typeof groupAlertsByRule;
groupAlertsByRule: GroupAlertsByRule;
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const useCasesModal = <EmbeddableType extends MlEmbeddableTypes>(
services: { cases },
} = useMlKibana();

const selectCaseModal = cases?.hooks.getUseCasesAddToExistingCaseModal();
const selectCaseModal = cases?.hooks.useCasesAddToExistingCaseModal();

return useCallback(
(persistableState: Partial<Omit<MappedEmbeddableTypeOf<EmbeddableType>, 'id'>>) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export const AnomalyTimeline: FC<AnomalyTimelineProps> = React.memo(

const globalTimeRange = useTimeRangeUpdates(true);

const selectCaseModal = cases?.hooks.getUseCasesAddToExistingCaseModal();
const selectCaseModal = cases?.hooks.useCasesAddToExistingCaseModal();

const { anomalyExplorerCommonStateService, anomalyTimelineStateService } =
useAnomalyExplorerContext();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { TIMESTAMP } from '@kbn/rule-data-utils';
import { SortOrder } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { AlertsTableConfigurationRegistry } from '@kbn/triggers-actions-ui-plugin/public/types';
import { casesFeatureId, observabilityFeatureId } from '../../common';
import { useBulkAddToCaseTriggerActions } from '../hooks/use_alert_bulk_case_actions';
import { TopAlert, useToGetInternalFlyout } from '../pages/alerts';
import { getRenderCellValue } from '../pages/alerts/components/render_cell_value';
import { addDisplayNames } from '../pages/alerts/containers/alerts_table/add_display_names';
Expand All @@ -24,7 +23,7 @@ const getO11yAlertsTableConfiguration = (
config: ConfigSchema
): AlertsTableConfigurationRegistry => ({
id: observabilityFeatureId,
casesFeatureId,
cases: { featureId: casesFeatureId, owner: [observabilityFeatureId] },
columns: alertO11yColumns.map(addDisplayNames),
getRenderCellValue: (({ setFlyoutAlert }: { setFlyoutAlert: (data: TopAlert) => void }) => {
return getRenderCellValue({ observabilityRuleTypeRegistry, setFlyoutAlert });
Expand All @@ -37,7 +36,6 @@ const getO11yAlertsTableConfiguration = (
},
],
useActionsColumn: getRowActions(observabilityRuleTypeRegistry, config),
useBulkActions: useBulkAddToCaseTriggerActions,
useInternalFlyout: () => {
const { header, body, footer } = useToGetInternalFlyout(observabilityRuleTypeRegistry);
return { header, body, footer };
Expand Down

This file was deleted.

Loading