From 4803fcf31303545e9f0835fb88179e3c7b1a371b Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Thu, 18 Feb 2021 22:38:28 +0800 Subject: [PATCH 01/26] ui enhancement --- .../cases/components/all_cases/actions.tsx | 16 ++++++++-------- .../public/cases/components/all_cases/index.tsx | 14 +++++++++++--- .../cases/components/bulk_actions/index.tsx | 4 +++- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx index 8178e7e9f9e8f..4cee013811f3f 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx @@ -27,14 +27,6 @@ export const getActions = ({ dispatchUpdate, deleteCaseOnClick, }: GetActions): Array> => [ - { - description: i18n.DELETE_CASE, - icon: 'trash', - name: i18n.DELETE_CASE, - onClick: deleteCaseOnClick, - type: 'icon', - 'data-test-subj': 'action-delete', - }, { available: (item) => caseStatus === CaseStatuses.open && !hasSubCases(item.subCases), description: i18n.CLOSE_CASE, @@ -65,4 +57,12 @@ export const getActions = ({ type: 'icon', 'data-test-subj': 'action-open', }, + { + description: i18n.DELETE_CASE, + icon: 'trash', + name: i18n.DELETE_CASE, + onClick: deleteCaseOnClick, + type: 'icon', + 'data-test-subj': 'action-delete', + }, ]; diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx index ce0fea07bf473..dd08438408b33 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx @@ -21,7 +21,7 @@ import { isEmpty, memoize } from 'lodash/fp'; import styled, { css } from 'styled-components'; import * as i18n from './translations'; -import { CaseStatuses } from '../../../../../case/common/api'; +import { CaseStatuses, CaseType } from '../../../../../case/common/api'; import { getCasesColumns } from './columns'; import { Case, DeleteCase, FilterOptions, SortFieldCase } from '../../containers/types'; import { useGetCases, UpdateCase } from '../../containers/use_get_cases'; @@ -245,10 +245,19 @@ export const AllCases = React.memo( deleteCasesAction: toggleBulkDeleteModal, selectedCaseIds, updateCaseStatus: handleUpdateCaseStatus, + includeCollections: + selectedCases.length > 0 && + selectedCases.some((caseObj: Case) => caseObj.type === CaseType.collection), })} /> ), - [selectedCaseIds, filterOptions.status, toggleBulkDeleteModal, handleUpdateCaseStatus] + [ + selectedCases, + selectedCaseIds, + filterOptions.status, + toggleBulkDeleteModal, + handleUpdateCaseStatus, + ] ); const handleDispatchUpdate = useCallback( (args: Omit) => { @@ -354,7 +363,6 @@ export const AllCases = React.memo( const euiBasicTableSelectionProps = useMemo>( () => ({ - selectable: (theCase) => isEmpty(theCase.subCases), onSelectionChange: setSelectedCases, }), [setSelectedCases] diff --git a/x-pack/plugins/security_solution/public/cases/components/bulk_actions/index.tsx b/x-pack/plugins/security_solution/public/cases/components/bulk_actions/index.tsx index f9722b3903b12..71262ff5584f6 100644 --- a/x-pack/plugins/security_solution/public/cases/components/bulk_actions/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/bulk_actions/index.tsx @@ -17,6 +17,7 @@ interface GetBulkItems { deleteCasesAction: (cases: string[]) => void; selectedCaseIds: string[]; updateCaseStatus: (status: string) => void; + includeCollections: boolean; } export const getBulkItems = ({ @@ -25,12 +26,13 @@ export const getBulkItems = ({ deleteCasesAction, selectedCaseIds, updateCaseStatus, + includeCollections, }: GetBulkItems) => { return [ caseStatus === CaseStatuses.open ? ( { From fab395013760d6c02dc6924ebd0d7023fd5243a4 Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Fri, 19 Feb 2021 01:03:21 +0800 Subject: [PATCH 02/26] fix actions --- .../public/cases/components/all_cases/actions.tsx | 13 ++++++------- .../public/cases/components/all_cases/columns.tsx | 4 ++-- .../public/cases/components/bulk_actions/index.tsx | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx index 66563deae5422..7b2c25ef8d13d 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx @@ -8,7 +8,7 @@ import { Dispatch } from 'react'; import { DefaultItemIconButtonAction } from '@elastic/eui/src/components/basic_table/action_types'; -import { CaseStatuses } from '../../../../../case/common/api'; +import { CaseStatuses, CaseType } from '../../../../../case/common/api'; import { Case, SubCase } from '../../containers/types'; import { UpdateCase } from '../../containers/use_get_cases'; import { statuses } from '../status'; @@ -20,8 +20,8 @@ interface GetActions { deleteCaseOnClick: (deleteCase: Case) => void; } -const hasSubCases = (subCases: SubCase[] | null | undefined) => - subCases != null && subCases?.length > 0; +const isCollection = (theCase: Case | SubCase | null | undefined) => + theCase != null && (theCase as Case).type === CaseType.collection; export const getActions = ({ caseStatus, @@ -29,7 +29,7 @@ export const getActions = ({ deleteCaseOnClick, }: GetActions): Array> => { const openCaseAction = { - available: (item: Case) => caseStatus !== CaseStatuses.open && !hasSubCases(item.subCases), + available: (item: Case | SubCase) => caseStatus !== CaseStatuses.open && !isCollection(item), description: statuses[CaseStatuses.open].actions.single.title, icon: statuses[CaseStatuses.open].icon, name: statuses[CaseStatuses.open].actions.single.title, @@ -45,8 +45,7 @@ export const getActions = ({ }; const makeInProgressAction = { - available: (item: Case) => - caseStatus !== CaseStatuses['in-progress'] && !hasSubCases(item.subCases), + available: (item: Case) => caseStatus !== CaseStatuses['in-progress'] && !isCollection(item), description: statuses[CaseStatuses['in-progress']].actions.single.title, icon: statuses[CaseStatuses['in-progress']].icon, name: statuses[CaseStatuses['in-progress']].actions.single.title, @@ -62,7 +61,7 @@ export const getActions = ({ }; const closeCaseAction = { - available: (item: Case) => caseStatus !== CaseStatuses.closed && !hasSubCases(item.subCases), + available: (item: Case) => caseStatus !== CaseStatuses.closed && !isCollection(item), description: statuses[CaseStatuses.closed].actions.single.title, icon: statuses[CaseStatuses.closed].icon, name: statuses[CaseStatuses.closed].actions.single.title, diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/columns.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/columns.tsx index 47db362c7b4bf..e69f85c862962 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/columns.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/columns.tsx @@ -19,7 +19,7 @@ import { RIGHT_ALIGNMENT } from '@elastic/eui/lib/services'; import styled from 'styled-components'; import { DefaultItemIconButtonAction } from '@elastic/eui/src/components/basic_table/action_types'; -import { CaseStatuses } from '../../../../../case/common/api'; +import { CaseStatuses, CaseType } from '../../../../../case/common/api'; import { getEmptyTagValue } from '../../../common/components/empty_value'; import { Case, SubCase } from '../../containers/types'; import { FormattedRelativePreferenceDate } from '../../../common/components/formatted_date'; @@ -204,7 +204,7 @@ export const getCasesColumns = ( name: i18n.STATUS, render: (theCase: Case) => { if (theCase?.subCases == null || theCase.subCases.length === 0) { - if (theCase.status == null) { + if (theCase.status == null || theCase.type === CaseType.collection) { return getEmptyTagValue(); } return ; diff --git a/x-pack/plugins/security_solution/public/cases/components/bulk_actions/index.tsx b/x-pack/plugins/security_solution/public/cases/components/bulk_actions/index.tsx index 82954d4b5d1f7..3e16306fe088b 100644 --- a/x-pack/plugins/security_solution/public/cases/components/bulk_actions/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/bulk_actions/index.tsx @@ -34,7 +34,7 @@ export const getBulkItems = ({ const openMenuItem = ( { From 9b80fa99d86d45ce4d879a25671dac5139abd92b Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Fri, 19 Feb 2021 02:33:26 +0800 Subject: [PATCH 03/26] unit test --- .../cases/components/all_cases/actions.tsx | 6 +- .../cases/components/all_cases/index.test.tsx | 78 +++++++++++++++++++ 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx index 7b2c25ef8d13d..b13f7ae233726 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx @@ -77,6 +77,9 @@ export const getActions = ({ }; return [ + openCaseAction, + makeInProgressAction, + closeCaseAction, { description: i18n.DELETE_CASE, icon: 'trash', @@ -85,8 +88,5 @@ export const getActions = ({ type: 'icon', 'data-test-subj': 'action-delete', }, - openCaseAction, - makeInProgressAction, - closeCaseAction, ]; }; diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx index a145bdf117813..27290ed9f4875 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx @@ -240,6 +240,42 @@ describe('AllCases', () => { }); }); + it('should render correct actions for case collection', async () => { + useGetCasesMock.mockReturnValue({ + ...defaultGetCases, + data: { + ...defaultGetCases.data, + cases: [ + { + ...defaultGetCases.data.cases[0], + id: null, + createdAt: null, + createdBy: null, + status: null, + subCases: null, + tags: null, + title: null, + totalComment: null, + totalAlerts: null, + type: CaseType.collection, + }, + ], + }, + }); + const wrapper = mount( + + + + ); + + await waitFor(() => { + expect(wrapper.find('[data-test-subj="in-open"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="in-progress"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="action-close"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="action-delete"]').exists()).toBeTruthy(); + }); + }); + it('should not render case link or actions on modal=true', async () => { const wrapper = mount( @@ -377,6 +413,48 @@ describe('AllCases', () => { }); }); + it('Renders correct bulk actoins for case collection - enable only bulk delete if any collection is selected', async () => { + useGetCasesMock.mockReturnValue({ + ...defaultGetCases, + selectedCases: [ + ...useGetCasesMockState.data.cases, + { + ...useGetCasesMockState.data.cases[0], + type: CaseType.collection, + }, + ], + }); + + useDeleteCasesMock + .mockReturnValueOnce({ + ...defaultDeleteCases, + isDisplayConfirmDeleteModal: false, + }) + .mockReturnValue({ + ...defaultDeleteCases, + isDisplayConfirmDeleteModal: true, + }); + + const wrapper = mount( + + + + ); + await waitFor(() => { + wrapper.find('[data-test-subj="case-table-bulk-actions"] button').first().simulate('click'); + expect(wrapper.find('[data-test-subj="cases-bulk-open-button"]').exists()).toEqual(false); + expect( + wrapper.find('[data-test-subj="cases-bulk-in-progress-button"]').first().props().disabled + ).toEqual(true); + expect( + wrapper.find('[data-test-subj="cases-bulk-close-button"]').first().props().disabled + ).toEqual(true); + expect( + wrapper.find('[data-test-subj="cases-bulk-delete-button"]').first().props().disabled + ).toEqual(false); + }); + }); + it('Bulk close status update', async () => { useGetCasesMock.mockReturnValue({ ...defaultGetCases, From 6dc3ca6e5df8a796c6e60b8678fbe1d9b8a8e465 Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Fri, 19 Feb 2021 19:02:09 +0800 Subject: [PATCH 04/26] update row actions --- .../cases/components/all_cases/actions.tsx | 15 +++--- .../cases/components/all_cases/index.test.tsx | 50 ++++++++++++++++--- 2 files changed, 51 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx index b13f7ae233726..6d6907d50f579 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx @@ -29,7 +29,8 @@ export const getActions = ({ deleteCaseOnClick, }: GetActions): Array> => { const openCaseAction = { - available: (item: Case | SubCase) => caseStatus !== CaseStatuses.open && !isCollection(item), + available: (item: Case | SubCase) => caseStatus !== CaseStatuses.open, + enabled: (item: Case | SubCase) => !isCollection(item), description: statuses[CaseStatuses.open].actions.single.title, icon: statuses[CaseStatuses.open].icon, name: statuses[CaseStatuses.open].actions.single.title, @@ -45,7 +46,8 @@ export const getActions = ({ }; const makeInProgressAction = { - available: (item: Case) => caseStatus !== CaseStatuses['in-progress'] && !isCollection(item), + available: (item: Case) => caseStatus !== CaseStatuses['in-progress'], + enabled: (item: Case | SubCase) => !isCollection(item), description: statuses[CaseStatuses['in-progress']].actions.single.title, icon: statuses[CaseStatuses['in-progress']].icon, name: statuses[CaseStatuses['in-progress']].actions.single.title, @@ -61,7 +63,8 @@ export const getActions = ({ }; const closeCaseAction = { - available: (item: Case) => caseStatus !== CaseStatuses.closed && !isCollection(item), + available: (item: Case | SubCase) => caseStatus !== CaseStatuses.closed, + enabled: (item: Case | SubCase) => !isCollection(item), description: statuses[CaseStatuses.closed].actions.single.title, icon: statuses[CaseStatuses.closed].icon, name: statuses[CaseStatuses.closed].actions.single.title, @@ -77,9 +80,6 @@ export const getActions = ({ }; return [ - openCaseAction, - makeInProgressAction, - closeCaseAction, { description: i18n.DELETE_CASE, icon: 'trash', @@ -88,5 +88,8 @@ export const getActions = ({ type: 'icon', 'data-test-subj': 'action-delete', }, + openCaseAction, + makeInProgressAction, + closeCaseAction, ]; }; diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx index 27290ed9f4875..e72d1816d394a 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx @@ -240,7 +240,27 @@ describe('AllCases', () => { }); }); - it('should render correct actions for case collection', async () => { + it('should render correct actions for case (with type individual)', async () => { + useGetCasesMock.mockReturnValue({ + ...defaultGetCases, + }); + const wrapper = mount( + + + + ); + wrapper.find('[data-test-subj="euiCollapsedItemActionsButton"]').first().simulate('click'); + await waitFor(() => { + expect(wrapper.find('[data-test-subj="action-open"]').exists()).toBeFalsy(); + expect( + wrapper.find('[data-test-subj="action-in-progress"]').first().props().disabled + ).toBeFalsy(); + expect(wrapper.find('[data-test-subj="action-close"]').first().props().disabled).toBeFalsy(); + expect(wrapper.find('[data-test-subj="action-delete"]').first().props().disabled).toBeFalsy(); + }); + }); + + it('should enable correct actions for sub cases', async () => { useGetCasesMock.mockReturnValue({ ...defaultGetCases, data: { @@ -248,11 +268,15 @@ describe('AllCases', () => { cases: [ { ...defaultGetCases.data.cases[0], - id: null, + id: 'my-case-with-subcases', createdAt: null, createdBy: null, status: null, - subCases: null, + subCases: [ + { + id: 'sub-case-id', + }, + ], tags: null, title: null, totalComment: null, @@ -267,12 +291,22 @@ describe('AllCases', () => { ); - await waitFor(() => { - expect(wrapper.find('[data-test-subj="in-open"]').exists()).toBeFalsy(); - expect(wrapper.find('[data-test-subj="in-progress"]').exists()).toBeFalsy(); - expect(wrapper.find('[data-test-subj="action-close"]').exists()).toBeFalsy(); - expect(wrapper.find('[data-test-subj="action-delete"]').exists()).toBeTruthy(); + wrapper + .find( + '[data-test-subj="sub-cases-table-my-case-with-subcases"] [data-test-subj="euiCollapsedItemActionsButton"]' + ) + .last() + .simulate('click'); + expect( + wrapper.find('[data-test-subj="action-in-progress"]').first().props().disabled + ).toEqual(false); + expect(wrapper.find('[data-test-subj="action-close"]').first().props().disabled).toEqual( + false + ); + expect(wrapper.find('[data-test-subj="action-delete"]').first().props().disabled).toEqual( + false + ); }); }); From 5e8dcc33c3190e886bc2b76cf5c5b935c329abd0 Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Sat, 20 Feb 2021 03:02:28 +0800 Subject: [PATCH 05/26] add case status all --- x-pack/plugins/case/common/api/cases/status.ts | 11 +++++++++++ x-pack/plugins/case/common/api/cases/sub_case.ts | 4 ++-- .../case/server/routes/api/cases/find_cases.ts | 11 ++++++++--- x-pack/plugins/case/server/routes/api/utils.ts | 3 +++ .../public/cases/components/all_cases/index.tsx | 1 + .../cases/components/all_cases/table_filters.tsx | 7 +++++-- .../public/cases/components/status/config.ts | 1 + .../public/cases/components/status/translations.ts | 4 ++++ .../security_solution/public/cases/containers/api.ts | 2 +- .../public/cases/containers/types.ts | 1 + .../public/cases/containers/use_get_cases.tsx | 3 ++- .../public/cases/containers/utils.ts | 1 + 12 files changed, 40 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/case/common/api/cases/status.ts b/x-pack/plugins/case/common/api/cases/status.ts index 7286e19da9159..a4e9b6e8434b0 100644 --- a/x-pack/plugins/case/common/api/cases/status.ts +++ b/x-pack/plugins/case/common/api/cases/status.ts @@ -8,12 +8,14 @@ import * as rt from 'io-ts'; export enum CaseStatuses { + all = 'all', open = 'open', 'in-progress' = 'in-progress', closed = 'closed', } export const CaseStatusRt = rt.union([ + rt.literal(CaseStatuses.all), rt.literal(CaseStatuses.open), rt.literal(CaseStatuses['in-progress']), rt.literal(CaseStatuses.closed), @@ -22,9 +24,18 @@ export const CaseStatusRt = rt.union([ export const caseStatuses = Object.values(CaseStatuses); export const CasesStatusResponseRt = rt.type({ + count_all_cases: rt.number, count_open_cases: rt.number, count_in_progress_cases: rt.number, count_closed_cases: rt.number, }); export type CasesStatusResponse = rt.TypeOf; + +export const SubCasesStatusResponseRt = rt.type({ + count_open_cases: rt.number, + count_in_progress_cases: rt.number, + count_closed_cases: rt.number, +}); + +export type SubCasesStatusResponse = rt.TypeOf; diff --git a/x-pack/plugins/case/common/api/cases/sub_case.ts b/x-pack/plugins/case/common/api/cases/sub_case.ts index c46f87c547d50..42dc3b4cc998c 100644 --- a/x-pack/plugins/case/common/api/cases/sub_case.ts +++ b/x-pack/plugins/case/common/api/cases/sub_case.ts @@ -10,7 +10,7 @@ import * as rt from 'io-ts'; import { NumberFromString } from '../saved_object'; import { UserRT } from '../user'; import { CommentResponseRt } from './comment'; -import { CasesStatusResponseRt } from './status'; +import { SubCasesStatusResponseRt } from './status'; import { CaseStatusRt } from './status'; const SubCaseBasicRt = rt.type({ @@ -61,7 +61,7 @@ export const SubCasesFindResponseRt = rt.intersection([ per_page: rt.number, total: rt.number, }), - CasesStatusResponseRt, + SubCasesStatusResponseRt, ]); export const SubCasePatchRequestRt = rt.intersection([ diff --git a/x-pack/plugins/case/server/routes/api/cases/find_cases.ts b/x-pack/plugins/case/server/routes/api/cases/find_cases.ts index 8ba83b42c06d7..6f43ab716daf2 100644 --- a/x-pack/plugins/case/server/routes/api/cases/find_cases.ts +++ b/x-pack/plugins/case/server/routes/api/cases/find_cases.ts @@ -16,6 +16,7 @@ import { CasesFindRequestRt, throwErrors, caseStatuses, + CaseStatuses, } from '../../../../common/api'; import { transformCases, wrapError, escapeHatch } from '../utils'; import { RouteDeps } from '../types'; @@ -42,7 +43,7 @@ export function initFindCasesApi({ caseService, caseConfigureService, router }: tags: queryParams.tags, reporters: queryParams.reporters, sortByField: queryParams.sortField, - status: queryParams.status, + status: queryParams.status === CaseStatuses.all ? undefined : queryParams.status, caseType: queryParams.type, }; @@ -54,9 +55,12 @@ export function initFindCasesApi({ caseService, caseConfigureService, router }: subCaseOptions: caseQueries.subCase, }); - const [openCases, inProgressCases, closedCases] = await Promise.all([ + const [allCases, openCases, inProgressCases, closedCases] = await Promise.all([ ...caseStatuses.map((status) => { - const statusQuery = constructQueryOptions({ ...queryArgs, status }); + const statusQuery = constructQueryOptions({ + ...queryArgs, + status: status === CaseStatuses.all ? undefined : status, + }); return caseService.findCaseStatusStats({ client, caseOptions: statusQuery.case, @@ -69,6 +73,7 @@ export function initFindCasesApi({ caseService, caseConfigureService, router }: body: CasesFindResponseRt.encode( transformCases({ ...cases, + countAllCases: allCases, countOpenCases: openCases, countInProgressCases: inProgressCases, countClosedCases: closedCases, diff --git a/x-pack/plugins/case/server/routes/api/utils.ts b/x-pack/plugins/case/server/routes/api/utils.ts index 084b1a17a1434..e5fbd8aa68a5e 100644 --- a/x-pack/plugins/case/server/routes/api/utils.ts +++ b/x-pack/plugins/case/server/routes/api/utils.ts @@ -194,6 +194,7 @@ export function wrapError(error: any): CustomHttpResponseOptions export const transformCases = ({ casesMap, + countAllCases, countOpenCases, countInProgressCases, countClosedCases, @@ -202,6 +203,7 @@ export const transformCases = ({ total, }: { casesMap: Map; + countAllCases: number; countOpenCases: number; countInProgressCases: number; countClosedCases: number; @@ -213,6 +215,7 @@ export const transformCases = ({ per_page: perPage, total, cases: Array.from(casesMap.values()), + count_all_cases: countAllCases, count_open_cases: countOpenCases, count_in_progress_cases: countInProgressCases, count_closed_cases: countClosedCases, diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx index dd08438408b33..46d9687449d0d 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx @@ -459,6 +459,7 @@ export const AllCases = React.memo( )} ({ + [CaseStatuses.all]: countAllCases ?? 0, [CaseStatuses.open]: countOpenCases ?? 0, [CaseStatuses['in-progress']]: countInProgressCases ?? 0, [CaseStatuses.closed]: countClosedCases ?? 0, }), - [countClosedCases, countInProgressCases, countOpenCases] + [countAllCases, countClosedCases, countInProgressCases, countOpenCases] ); return ( diff --git a/x-pack/plugins/security_solution/public/cases/components/status/config.ts b/x-pack/plugins/security_solution/public/cases/components/status/config.ts index d811db43df814..199aa3fd1f4c6 100644 --- a/x-pack/plugins/security_solution/public/cases/components/status/config.ts +++ b/x-pack/plugins/security_solution/public/cases/components/status/config.ts @@ -36,6 +36,7 @@ type Statuses = Record< >; export const statuses: Statuses = { + [CaseStatuses.all]: { color: 'hollow', label: i18n.ALL }, [CaseStatuses.open]: { color: 'primary', label: i18n.OPEN, diff --git a/x-pack/plugins/security_solution/public/cases/components/status/translations.ts b/x-pack/plugins/security_solution/public/cases/components/status/translations.ts index 1220b6beaeb65..00dc5d3333f15 100644 --- a/x-pack/plugins/security_solution/public/cases/components/status/translations.ts +++ b/x-pack/plugins/security_solution/public/cases/components/status/translations.ts @@ -8,6 +8,10 @@ import { i18n } from '@kbn/i18n'; export * from '../../translations'; +export const ALL = i18n.translate('xpack.securitySolution.case.status.all', { + defaultMessage: 'All', +}); + export const OPEN = i18n.translate('xpack.securitySolution.case.status.open', { defaultMessage: 'Open', }); diff --git a/x-pack/plugins/security_solution/public/cases/containers/api.ts b/x-pack/plugins/security_solution/public/cases/containers/api.ts index c87e210b42bc0..485072a86d40e 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/api.ts +++ b/x-pack/plugins/security_solution/public/cases/containers/api.ts @@ -167,7 +167,7 @@ export const getCases = async ({ filterOptions = { search: '', reporters: [], - status: CaseStatuses.open, + status: CaseStatuses.all, tags: [], }, queryParams = { diff --git a/x-pack/plugins/security_solution/public/cases/containers/types.ts b/x-pack/plugins/security_solution/public/cases/containers/types.ts index d2931a790bd79..9e836adb86787 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/types.ts +++ b/x-pack/plugins/security_solution/public/cases/containers/types.ts @@ -102,6 +102,7 @@ export interface FilterOptions { } export interface CasesStatus { + countAllCases: number | null; countClosedCases: number | null; countOpenCases: number | null; countInProgressCases: number | null; diff --git a/x-pack/plugins/security_solution/public/cases/containers/use_get_cases.tsx b/x-pack/plugins/security_solution/public/cases/containers/use_get_cases.tsx index 298d817fffa88..6cd1a95349ef2 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/use_get_cases.tsx +++ b/x-pack/plugins/security_solution/public/cases/containers/use_get_cases.tsx @@ -95,7 +95,7 @@ const dataFetchReducer = (state: UseGetCasesState, action: Action): UseGetCasesS export const DEFAULT_FILTER_OPTIONS: FilterOptions = { search: '', reporters: [], - status: CaseStatuses.open, + status: CaseStatuses.all, tags: [], }; @@ -108,6 +108,7 @@ export const DEFAULT_QUERY_PARAMS: QueryParams = { export const initialData: AllCases = { cases: [], + countAllCases: null, countClosedCases: null, countInProgressCases: null, countOpenCases: null, diff --git a/x-pack/plugins/security_solution/public/cases/containers/utils.ts b/x-pack/plugins/security_solution/public/cases/containers/utils.ts index 297c7e35981ac..e2a4023065429 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/utils.ts +++ b/x-pack/plugins/security_solution/public/cases/containers/utils.ts @@ -68,6 +68,7 @@ export const convertToCamelCase = (snakeCase: T): U => export const convertAllCasesToCamel = (snakeCases: CasesFindResponse): AllCases => ({ cases: snakeCases.cases.map((snakeCase) => convertToCamelCase(snakeCase)), + countAllCases: snakeCases.count_all_cases, countOpenCases: snakeCases.count_open_cases, countInProgressCases: snakeCases.count_in_progress_cases, countClosedCases: snakeCases.count_closed_cases, From 46335f0496994b8784a203cc9ab84301cb1d32c7 Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Mon, 22 Feb 2021 18:09:08 +0800 Subject: [PATCH 06/26] update find status --- x-pack/plugins/case/server/routes/api/cases/find_cases.ts | 7 ++----- .../case/server/routes/api/cases/status/get_status.ts | 7 +++++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/case/server/routes/api/cases/find_cases.ts b/x-pack/plugins/case/server/routes/api/cases/find_cases.ts index 6f43ab716daf2..0df69750bfa65 100644 --- a/x-pack/plugins/case/server/routes/api/cases/find_cases.ts +++ b/x-pack/plugins/case/server/routes/api/cases/find_cases.ts @@ -16,7 +16,6 @@ import { CasesFindRequestRt, throwErrors, caseStatuses, - CaseStatuses, } from '../../../../common/api'; import { transformCases, wrapError, escapeHatch } from '../utils'; import { RouteDeps } from '../types'; @@ -38,17 +37,15 @@ export function initFindCasesApi({ caseService, caseConfigureService, router }: CasesFindRequestRt.decode(request.query), fold(throwErrors(Boom.badRequest), identity) ); - const queryArgs = { tags: queryParams.tags, reporters: queryParams.reporters, sortByField: queryParams.sortField, - status: queryParams.status === CaseStatuses.all ? undefined : queryParams.status, + status: queryParams.status === 'all' ? undefined : queryParams.status, caseType: queryParams.type, }; const caseQueries = constructQueryOptions(queryArgs); - const cases = await caseService.findCasesGroupedByID({ client, caseOptions: { ...queryParams, ...caseQueries.case }, @@ -59,7 +56,7 @@ export function initFindCasesApi({ caseService, caseConfigureService, router }: ...caseStatuses.map((status) => { const statusQuery = constructQueryOptions({ ...queryArgs, - status: status === CaseStatuses.all ? undefined : status, + status: status === 'all' ? undefined : status, }); return caseService.findCaseStatusStats({ client, diff --git a/x-pack/plugins/case/server/routes/api/cases/status/get_status.ts b/x-pack/plugins/case/server/routes/api/cases/status/get_status.ts index f3cd0e2bdda5c..847d871df1d85 100644 --- a/x-pack/plugins/case/server/routes/api/cases/status/get_status.ts +++ b/x-pack/plugins/case/server/routes/api/cases/status/get_status.ts @@ -22,9 +22,11 @@ export function initGetCasesStatusApi({ caseService, router }: RouteDeps) { try { const client = context.core.savedObjects.client; - const [openCases, inProgressCases, closedCases] = await Promise.all([ + const [allCases, openCases, inProgressCases, closedCases] = await Promise.all([ ...caseStatuses.map((status) => { - const statusQuery = constructQueryOptions({ status }); + const statusQuery = constructQueryOptions({ + status: status === 'all' ? undefined : status, + }); return caseService.findCaseStatusStats({ client, caseOptions: statusQuery.case, @@ -35,6 +37,7 @@ export function initGetCasesStatusApi({ caseService, router }: RouteDeps) { return response.ok({ body: CasesStatusResponseRt.encode({ + count_all_cases: allCases, count_open_cases: openCases, count_in_progress_cases: inProgressCases, count_closed_cases: closedCases, From baef99689485185b1bc781597696fe7c9c2eb42b Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Mon, 22 Feb 2021 20:01:29 +0800 Subject: [PATCH 07/26] fix type --- .../public/cases/containers/use_get_cases_status.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x-pack/plugins/security_solution/public/cases/containers/use_get_cases_status.tsx b/x-pack/plugins/security_solution/public/cases/containers/use_get_cases_status.tsx index 057fc05008bb0..a427d4dfe8f49 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/use_get_cases_status.tsx +++ b/x-pack/plugins/security_solution/public/cases/containers/use_get_cases_status.tsx @@ -18,6 +18,7 @@ interface CasesStatusState extends CasesStatus { } const initialData: CasesStatusState = { + countAllCases: null, countClosedCases: null, countInProgressCases: null, countOpenCases: null, @@ -58,6 +59,7 @@ export const useGetCasesStatus = (): UseGetCasesStatus => { dispatchToaster, }); setCasesStatusState({ + countAllCases: 0, countClosedCases: 0, countInProgressCases: 0, countOpenCases: 0, From 0ed83b38c822d2ec30bd2383b4df9b6df5bd62f7 Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Tue, 23 Feb 2021 02:36:50 +0800 Subject: [PATCH 08/26] remove all case count from dropdown --- x-pack/plugins/case/common/api/cases/case.ts | 4 ++-- .../plugins/case/common/api/cases/status.ts | 23 ++++++++++--------- .../plugins/case/common/api/cases/sub_case.ts | 4 ++-- .../server/routes/api/cases/find_cases.ts | 9 ++++---- .../case/server/routes/api/cases/helpers.ts | 2 +- .../routes/api/cases/status/get_status.ts | 5 ++-- .../plugins/case/server/routes/api/utils.ts | 3 --- .../cases/components/all_cases/index.tsx | 1 - .../all_cases/status_filter.test.tsx | 3 ++- .../components/all_cases/status_filter.tsx | 14 +++++------ .../components/all_cases/table_filters.tsx | 17 ++++++++------ .../cases/components/bulk_actions/index.tsx | 11 +++++---- .../public/cases/components/status/config.ts | 16 ++++++------- .../public/cases/components/status/status.tsx | 4 ++-- .../public/cases/containers/types.ts | 4 ++-- .../public/cases/containers/use_get_cases.tsx | 5 ++-- 16 files changed, 62 insertions(+), 63 deletions(-) diff --git a/x-pack/plugins/case/common/api/cases/case.ts b/x-pack/plugins/case/common/api/cases/case.ts index 33a93952b0e2d..8924ce78aa1c3 100644 --- a/x-pack/plugins/case/common/api/cases/case.ts +++ b/x-pack/plugins/case/common/api/cases/case.ts @@ -10,7 +10,7 @@ import * as rt from 'io-ts'; import { NumberFromString } from '../saved_object'; import { UserRT } from '../user'; import { CommentResponseRt } from './comment'; -import { CasesStatusResponseRt, CaseStatusRt } from './status'; +import { CasesStatusResponseRt, CaseStatusFilterRt, CaseStatusRt } from './status'; import { CaseConnectorRt, ESCaseConnector } from '../connectors'; import { SubCaseResponseRt } from './sub_case'; @@ -102,7 +102,7 @@ export const CasePostRequestRt = rt.intersection([ export const CasesFindRequestRt = rt.partial({ type: CaseTypeRt, tags: rt.union([rt.array(rt.string), rt.string]), - status: CaseStatusRt, + status: CaseStatusFilterRt, reporters: rt.union([rt.array(rt.string), rt.string]), defaultSearchOperator: rt.union([rt.literal('AND'), rt.literal('OR')]), fields: rt.array(rt.string), diff --git a/x-pack/plugins/case/common/api/cases/status.ts b/x-pack/plugins/case/common/api/cases/status.ts index a4e9b6e8434b0..578e7893de662 100644 --- a/x-pack/plugins/case/common/api/cases/status.ts +++ b/x-pack/plugins/case/common/api/cases/status.ts @@ -7,15 +7,15 @@ import * as rt from 'io-ts'; +export const AllCaseType = 'all'; + export enum CaseStatuses { - all = 'all', open = 'open', 'in-progress' = 'in-progress', closed = 'closed', } export const CaseStatusRt = rt.union([ - rt.literal(CaseStatuses.all), rt.literal(CaseStatuses.open), rt.literal(CaseStatuses['in-progress']), rt.literal(CaseStatuses.closed), @@ -23,19 +23,20 @@ export const CaseStatusRt = rt.union([ export const caseStatuses = Object.values(CaseStatuses); -export const CasesStatusResponseRt = rt.type({ - count_all_cases: rt.number, - count_open_cases: rt.number, - count_in_progress_cases: rt.number, - count_closed_cases: rt.number, -}); +export const CaseStatusFilterRt = rt.union([ + rt.literal(AllCaseType), + rt.literal(CaseStatuses.open), + rt.literal(CaseStatuses['in-progress']), + rt.literal(CaseStatuses.closed), +]); -export type CasesStatusResponse = rt.TypeOf; +export const caseStatusFilter = Object.values(CaseStatusFilterRt); +export type CaseStatusFilter = rt.TypeOf; -export const SubCasesStatusResponseRt = rt.type({ +export const CasesStatusResponseRt = rt.type({ count_open_cases: rt.number, count_in_progress_cases: rt.number, count_closed_cases: rt.number, }); -export type SubCasesStatusResponse = rt.TypeOf; +export type CasesStatusResponse = rt.TypeOf; diff --git a/x-pack/plugins/case/common/api/cases/sub_case.ts b/x-pack/plugins/case/common/api/cases/sub_case.ts index 42dc3b4cc998c..c46f87c547d50 100644 --- a/x-pack/plugins/case/common/api/cases/sub_case.ts +++ b/x-pack/plugins/case/common/api/cases/sub_case.ts @@ -10,7 +10,7 @@ import * as rt from 'io-ts'; import { NumberFromString } from '../saved_object'; import { UserRT } from '../user'; import { CommentResponseRt } from './comment'; -import { SubCasesStatusResponseRt } from './status'; +import { CasesStatusResponseRt } from './status'; import { CaseStatusRt } from './status'; const SubCaseBasicRt = rt.type({ @@ -61,7 +61,7 @@ export const SubCasesFindResponseRt = rt.intersection([ per_page: rt.number, total: rt.number, }), - SubCasesStatusResponseRt, + CasesStatusResponseRt, ]); export const SubCasePatchRequestRt = rt.intersection([ diff --git a/x-pack/plugins/case/server/routes/api/cases/find_cases.ts b/x-pack/plugins/case/server/routes/api/cases/find_cases.ts index 0df69750bfa65..5195a357ed4df 100644 --- a/x-pack/plugins/case/server/routes/api/cases/find_cases.ts +++ b/x-pack/plugins/case/server/routes/api/cases/find_cases.ts @@ -16,6 +16,7 @@ import { CasesFindRequestRt, throwErrors, caseStatuses, + AllCaseType, } from '../../../../common/api'; import { transformCases, wrapError, escapeHatch } from '../utils'; import { RouteDeps } from '../types'; @@ -41,10 +42,9 @@ export function initFindCasesApi({ caseService, caseConfigureService, router }: tags: queryParams.tags, reporters: queryParams.reporters, sortByField: queryParams.sortField, - status: queryParams.status === 'all' ? undefined : queryParams.status, + status: queryParams.status === AllCaseType ? undefined : queryParams.status, caseType: queryParams.type, }; - const caseQueries = constructQueryOptions(queryArgs); const cases = await caseService.findCasesGroupedByID({ client, @@ -52,11 +52,11 @@ export function initFindCasesApi({ caseService, caseConfigureService, router }: subCaseOptions: caseQueries.subCase, }); - const [allCases, openCases, inProgressCases, closedCases] = await Promise.all([ + const [openCases, inProgressCases, closedCases] = await Promise.all([ ...caseStatuses.map((status) => { const statusQuery = constructQueryOptions({ ...queryArgs, - status: status === 'all' ? undefined : status, + status, }); return caseService.findCaseStatusStats({ client, @@ -70,7 +70,6 @@ export function initFindCasesApi({ caseService, caseConfigureService, router }: body: CasesFindResponseRt.encode( transformCases({ ...cases, - countAllCases: allCases, countOpenCases: openCases, countInProgressCases: inProgressCases, countClosedCases: closedCases, diff --git a/x-pack/plugins/case/server/routes/api/cases/helpers.ts b/x-pack/plugins/case/server/routes/api/cases/helpers.ts index a1a7f4f9da8f5..3d82c8712d647 100644 --- a/x-pack/plugins/case/server/routes/api/cases/helpers.ts +++ b/x-pack/plugins/case/server/routes/api/cases/helpers.ts @@ -102,7 +102,7 @@ export const constructQueryOptions = ({ }: { tags?: string | string[]; reporters?: string | string[]; - status?: CaseStatuses; + status?: CaseStatuses | undefined; sortByField?: string; caseType?: CaseType; }): { case: SavedObjectFindOptions; subCase?: SavedObjectFindOptions } => { diff --git a/x-pack/plugins/case/server/routes/api/cases/status/get_status.ts b/x-pack/plugins/case/server/routes/api/cases/status/get_status.ts index 847d871df1d85..34efdeaa0d82c 100644 --- a/x-pack/plugins/case/server/routes/api/cases/status/get_status.ts +++ b/x-pack/plugins/case/server/routes/api/cases/status/get_status.ts @@ -22,10 +22,10 @@ export function initGetCasesStatusApi({ caseService, router }: RouteDeps) { try { const client = context.core.savedObjects.client; - const [allCases, openCases, inProgressCases, closedCases] = await Promise.all([ + const [openCases, inProgressCases, closedCases] = await Promise.all([ ...caseStatuses.map((status) => { const statusQuery = constructQueryOptions({ - status: status === 'all' ? undefined : status, + status, }); return caseService.findCaseStatusStats({ client, @@ -37,7 +37,6 @@ export function initGetCasesStatusApi({ caseService, router }: RouteDeps) { return response.ok({ body: CasesStatusResponseRt.encode({ - count_all_cases: allCases, count_open_cases: openCases, count_in_progress_cases: inProgressCases, count_closed_cases: closedCases, diff --git a/x-pack/plugins/case/server/routes/api/utils.ts b/x-pack/plugins/case/server/routes/api/utils.ts index e5fbd8aa68a5e..084b1a17a1434 100644 --- a/x-pack/plugins/case/server/routes/api/utils.ts +++ b/x-pack/plugins/case/server/routes/api/utils.ts @@ -194,7 +194,6 @@ export function wrapError(error: any): CustomHttpResponseOptions export const transformCases = ({ casesMap, - countAllCases, countOpenCases, countInProgressCases, countClosedCases, @@ -203,7 +202,6 @@ export const transformCases = ({ total, }: { casesMap: Map; - countAllCases: number; countOpenCases: number; countInProgressCases: number; countClosedCases: number; @@ -215,7 +213,6 @@ export const transformCases = ({ per_page: perPage, total, cases: Array.from(casesMap.values()), - count_all_cases: countAllCases, count_open_cases: countOpenCases, count_in_progress_cases: countInProgressCases, count_closed_cases: countClosedCases, diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx index 46d9687449d0d..dd08438408b33 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx @@ -459,7 +459,6 @@ export const AllCases = React.memo( )} ; - selectedStatus: CaseStatuses; - onStatusChanged: (status: CaseStatuses) => void; + stats: Record; + selectedStatus: CaseStatusFilter; + onStatusChanged: (status: CaseStatusFilter) => void; } const StatusFilterComponent: React.FC = ({ stats, selectedStatus, onStatusChanged }) => { - const caseStatuses = Object.keys(statuses) as CaseStatuses[]; - const options: Array> = caseStatuses.map((status) => ({ + const caseStatuses = Object.keys(statuses) as CaseStatusFilter[]; + const options: Array> = caseStatuses.map((status) => ({ value: status, inputDisplay: ( - {` (${stats[status]})`} + {status !== AllCaseType && {` (${stats[status]})`}} ), 'data-test-subj': `case-status-filter-${status}`, diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx index b6fad8aa4ca95..03e3ba6cf9297 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx @@ -10,7 +10,7 @@ import { isEqual } from 'lodash/fp'; import styled from 'styled-components'; import { EuiFlexGroup, EuiFlexItem, EuiFieldSearch, EuiFilterGroup } from '@elastic/eui'; -import { CaseStatuses } from '../../../../../case/common/api'; +import { AllCaseType, CaseStatuses, CaseStatusFilter } from '../../../../../case/common/api'; import { FilterOptions } from '../../containers/types'; import { useGetTags } from '../../containers/use_get_tags'; import { useGetReporters } from '../../containers/use_get_reporters'; @@ -19,7 +19,6 @@ import { StatusFilter } from './status_filter'; import * as i18n from './translations'; interface CasesTableFiltersProps { - countAllCases: number | null; countClosedCases: number | null; countInProgressCases: number | null; countOpenCases: number | null; @@ -42,11 +41,15 @@ const StatusFilterWrapper = styled(EuiFlexItem)` * @param onFilterChanged change listener to be notified on filter changes */ -const defaultInitial = { search: '', reporters: [], status: CaseStatuses.all, tags: [] }; +const defaultInitial = { + search: '', + reporters: [], + status: AllCaseType as CaseStatusFilter, + tags: [], +}; const CasesTableFiltersComponent = ({ countClosedCases, - countAllCases, countOpenCases, countInProgressCases, onFilterChanged, @@ -126,7 +129,7 @@ const CasesTableFiltersComponent = ({ ); const onStatusChanged = useCallback( - (status: CaseStatuses) => { + (status: CaseStatusFilter) => { onFilterChanged({ status }); }, [onFilterChanged] @@ -134,12 +137,12 @@ const CasesTableFiltersComponent = ({ const stats = useMemo( () => ({ - [CaseStatuses.all]: countAllCases ?? 0, + [AllCaseType]: null, [CaseStatuses.open]: countOpenCases ?? 0, [CaseStatuses['in-progress']]: countInProgressCases ?? 0, [CaseStatuses.closed]: countClosedCases ?? 0, }), - [countAllCases, countClosedCases, countInProgressCases, countOpenCases] + [countClosedCases, countInProgressCases, countOpenCases] ); return ( diff --git a/x-pack/plugins/security_solution/public/cases/components/bulk_actions/index.tsx b/x-pack/plugins/security_solution/public/cases/components/bulk_actions/index.tsx index 3e16306fe088b..342a71078ab59 100644 --- a/x-pack/plugins/security_solution/public/cases/components/bulk_actions/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/bulk_actions/index.tsx @@ -8,12 +8,13 @@ import React from 'react'; import { EuiContextMenuItem } from '@elastic/eui'; -import { CaseStatuses } from '../../../../../case/common/api'; +import { get } from 'lodash'; +import { CaseStatuses, CaseStatusFilter } from '../../../../../case/common/api'; import { statuses } from '../status'; import * as i18n from './translations'; interface GetBulkItems { - caseStatus: CaseStatuses; + caseStatus: CaseStatusFilter; closePopover: () => void; deleteCasesAction: (cases: string[]) => void; selectedCaseIds: string[]; @@ -42,7 +43,7 @@ export const getBulkItems = ({ updateCaseStatus(CaseStatuses.open); }} > - {statuses[CaseStatuses.open].actions.bulk.title} + {get(statuses, `${CaseStatuses.open}.actions.bulk.title`)} ); @@ -57,7 +58,7 @@ export const getBulkItems = ({ updateCaseStatus(CaseStatuses['in-progress']); }} > - {statuses[CaseStatuses['in-progress']].actions.bulk.title} + {get(statuses, `${CaseStatuses['in-progress']}.actions.bulk.title`)} ); @@ -72,7 +73,7 @@ export const getBulkItems = ({ updateCaseStatus(CaseStatuses.closed); }} > - {statuses[CaseStatuses.closed].actions.bulk.title} + {get(statuses, `${CaseStatuses.closed}.actions.bulk.title`)} ); diff --git a/x-pack/plugins/security_solution/public/cases/components/status/config.ts b/x-pack/plugins/security_solution/public/cases/components/status/config.ts index 199aa3fd1f4c6..a9ebbfe3fff3c 100644 --- a/x-pack/plugins/security_solution/public/cases/components/status/config.ts +++ b/x-pack/plugins/security_solution/public/cases/components/status/config.ts @@ -5,16 +5,16 @@ * 2.0. */ import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; -import { CaseStatuses } from '../../../../../case/common/api'; +import { AllCaseType, CaseStatuses, CaseStatusFilter } from '../../../../../case/common/api'; import * as i18n from './translations'; type Statuses = Record< - CaseStatuses, + CaseStatusFilter, { color: string; label: string; - icon: EuiIconType; - actions: { + icon?: EuiIconType; + actions?: { bulk: { title: string; }; @@ -23,20 +23,20 @@ type Statuses = Record< description?: string; }; }; - actionBar: { + actionBar?: { title: string; }; - button: { + button?: { label: string; }; - stats: { + stats?: { title: string; }; } >; export const statuses: Statuses = { - [CaseStatuses.all]: { color: 'hollow', label: i18n.ALL }, + [AllCaseType]: { color: 'hollow', label: i18n.ALL }, [CaseStatuses.open]: { color: 'primary', label: i18n.OPEN, diff --git a/x-pack/plugins/security_solution/public/cases/components/status/status.tsx b/x-pack/plugins/security_solution/public/cases/components/status/status.tsx index ba0f9a9cfde00..56b78a75b64cf 100644 --- a/x-pack/plugins/security_solution/public/cases/components/status/status.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/status/status.tsx @@ -9,12 +9,12 @@ import React, { memo, useMemo } from 'react'; import { noop } from 'lodash/fp'; import { EuiBadge } from '@elastic/eui'; -import { CaseStatuses } from '../../../../../case/common/api'; +import { CaseStatusFilter } from '../../../../../case/common/api'; import { statuses } from './config'; import * as i18n from './translations'; interface Props { - type: CaseStatuses; + type: CaseStatusFilter; withArrow?: boolean; onClick?: () => void; } diff --git a/x-pack/plugins/security_solution/public/cases/containers/types.ts b/x-pack/plugins/security_solution/public/cases/containers/types.ts index 9e836adb86787..491ac0a90326d 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/types.ts +++ b/x-pack/plugins/security_solution/public/cases/containers/types.ts @@ -16,6 +16,7 @@ import { CasePatchRequest, CaseType, AssociationType, + CaseStatusFilter, } from '../../../../case/common/api'; export { CaseConnector, ActionConnector, CaseStatuses } from '../../../../case/common/api'; @@ -96,13 +97,12 @@ export interface QueryParams { export interface FilterOptions { search: string; - status: CaseStatuses; + status: CaseStatusFilter; tags: string[]; reporters: User[]; } export interface CasesStatus { - countAllCases: number | null; countClosedCases: number | null; countOpenCases: number | null; countInProgressCases: number | null; diff --git a/x-pack/plugins/security_solution/public/cases/containers/use_get_cases.tsx b/x-pack/plugins/security_solution/public/cases/containers/use_get_cases.tsx index 6cd1a95349ef2..fa440a2acfa2a 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/use_get_cases.tsx +++ b/x-pack/plugins/security_solution/public/cases/containers/use_get_cases.tsx @@ -6,12 +6,12 @@ */ import { useCallback, useEffect, useReducer } from 'react'; -import { CaseStatuses } from '../../../../case/common/api'; import { DEFAULT_TABLE_ACTIVE_PAGE, DEFAULT_TABLE_LIMIT } from './constants'; import { AllCases, SortFieldCase, FilterOptions, QueryParams, Case, UpdateByKey } from './types'; import { errorToToaster, useStateToaster } from '../../common/components/toasters'; import * as i18n from './translations'; import { getCases, patchCase } from './api'; +import { AllCaseType } from '../../../../case/common/api'; export interface UseGetCasesState { data: AllCases; @@ -95,7 +95,7 @@ const dataFetchReducer = (state: UseGetCasesState, action: Action): UseGetCasesS export const DEFAULT_FILTER_OPTIONS: FilterOptions = { search: '', reporters: [], - status: CaseStatuses.all, + status: AllCaseType, tags: [], }; @@ -108,7 +108,6 @@ export const DEFAULT_QUERY_PARAMS: QueryParams = { export const initialData: AllCases = { cases: [], - countAllCases: null, countClosedCases: null, countInProgressCases: null, countOpenCases: null, From 0bddbf95ca221d97e3c017674d976b1d2a3592f2 Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Tue, 23 Feb 2021 20:26:38 +0800 Subject: [PATCH 09/26] fix type error --- .../cases/components/all_cases/actions.tsx | 19 ++++++++++--------- .../components/case_action_bar/helpers.ts | 3 ++- .../public/cases/components/status/button.tsx | 3 ++- .../public/cases/components/status/stats.tsx | 3 ++- .../public/cases/containers/api.ts | 4 ++-- .../cases/containers/use_get_cases_status.tsx | 2 -- .../public/cases/containers/utils.ts | 1 - 7 files changed, 18 insertions(+), 17 deletions(-) diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx index 6d6907d50f579..55e1dffcb2ac2 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx @@ -6,6 +6,7 @@ */ import { Dispatch } from 'react'; +import { get } from 'lodash'; import { DefaultItemIconButtonAction } from '@elastic/eui/src/components/basic_table/action_types'; import { CaseStatuses, CaseType } from '../../../../../case/common/api'; @@ -31,9 +32,9 @@ export const getActions = ({ const openCaseAction = { available: (item: Case | SubCase) => caseStatus !== CaseStatuses.open, enabled: (item: Case | SubCase) => !isCollection(item), - description: statuses[CaseStatuses.open].actions.single.title, - icon: statuses[CaseStatuses.open].icon, - name: statuses[CaseStatuses.open].actions.single.title, + description: get(statuses, `${CaseStatuses.open}.actions.single.title`), + icon: get(statuses, `${CaseStatuses.open}.actions.single.icon`), + name: get(statuses, `${CaseStatuses.open}.actions.single.title`), onClick: (theCase: Case) => dispatchUpdate({ updateKey: 'status', @@ -48,9 +49,9 @@ export const getActions = ({ const makeInProgressAction = { available: (item: Case) => caseStatus !== CaseStatuses['in-progress'], enabled: (item: Case | SubCase) => !isCollection(item), - description: statuses[CaseStatuses['in-progress']].actions.single.title, - icon: statuses[CaseStatuses['in-progress']].icon, - name: statuses[CaseStatuses['in-progress']].actions.single.title, + description: get(statuses, `${CaseStatuses['in-progress']}.actions.single.title`), + icon: get(statuses, `${CaseStatuses['in-progress']}.actions.single.icon`), + name: get(statuses, `${CaseStatuses['in-progress']}.actions.single.title`), onClick: (theCase: Case) => dispatchUpdate({ updateKey: 'status', @@ -65,9 +66,9 @@ export const getActions = ({ const closeCaseAction = { available: (item: Case | SubCase) => caseStatus !== CaseStatuses.closed, enabled: (item: Case | SubCase) => !isCollection(item), - description: statuses[CaseStatuses.closed].actions.single.title, - icon: statuses[CaseStatuses.closed].icon, - name: statuses[CaseStatuses.closed].actions.single.title, + description: get(statuses, `${CaseStatuses.closed}.actions.single.title`), + icon: get(statuses, `${CaseStatuses.closed}.actions.single.icon`), + name: get(statuses, `${CaseStatuses.closed}.actions.single.title`), onClick: (theCase: Case) => dispatchUpdate({ updateKey: 'status', diff --git a/x-pack/plugins/security_solution/public/cases/components/case_action_bar/helpers.ts b/x-pack/plugins/security_solution/public/cases/components/case_action_bar/helpers.ts index 10f87981094fa..2eeac15ca947c 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_action_bar/helpers.ts +++ b/x-pack/plugins/security_solution/public/cases/components/case_action_bar/helpers.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { get } from 'lodash'; import { CaseStatuses } from '../../../../../case/common/api'; import { Case } from '../../containers/types'; import { statuses } from '../status'; @@ -21,4 +22,4 @@ export const getStatusDate = (theCase: Case): string | null => { return null; }; -export const getStatusTitle = (status: CaseStatuses) => statuses[status].actionBar.title; +export const getStatusTitle = (status: CaseStatuses) => get(statuses, `${status}.actionBar.title`); diff --git a/x-pack/plugins/security_solution/public/cases/components/status/button.tsx b/x-pack/plugins/security_solution/public/cases/components/status/button.tsx index 4ee69766fe128..03d4b6bd783fc 100644 --- a/x-pack/plugins/security_solution/public/cases/components/status/button.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/status/button.tsx @@ -8,6 +8,7 @@ import React, { memo, useCallback, useMemo } from 'react'; import { EuiButton } from '@elastic/eui'; +import { get } from 'lodash'; import { CaseStatuses, caseStatuses } from '../../../../../case/common/api'; import { statuses } from './config'; @@ -45,7 +46,7 @@ const StatusActionButtonComponent: React.FC = ({ isLoading={isLoading} onClick={onClick} > - {statuses[caseStatuses[nextStatusIndex]].button.label} + {get(statuses, `${caseStatuses[nextStatusIndex]}.button.label`)} ); }; diff --git a/x-pack/plugins/security_solution/public/cases/components/status/stats.tsx b/x-pack/plugins/security_solution/public/cases/components/status/stats.tsx index 572a169f64cf6..f849efdae7c02 100644 --- a/x-pack/plugins/security_solution/public/cases/components/status/stats.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/status/stats.tsx @@ -7,6 +7,7 @@ import React, { memo, useMemo } from 'react'; import { EuiDescriptionList, EuiLoadingSpinner } from '@elastic/eui'; +import { get } from 'lodash'; import { CaseStatuses } from '../../../../../case/common/api'; import { statuses } from './config'; @@ -21,7 +22,7 @@ const StatsComponent: React.FC = ({ caseCount, caseStatus, isLoading, dat const statusStats = useMemo( () => [ { - title: statuses[caseStatus].stats.title, + title: get(statuses, `${caseStatus}.stats.title`), description: isLoading ? ( ) : ( diff --git a/x-pack/plugins/security_solution/public/cases/containers/api.ts b/x-pack/plugins/security_solution/public/cases/containers/api.ts index 485072a86d40e..2c33d6fc97cf5 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/api.ts +++ b/x-pack/plugins/security_solution/public/cases/containers/api.ts @@ -8,13 +8,13 @@ import { assign } from 'lodash'; import { + AllCaseType, CasePatchRequest, CasePostRequest, CaseResponse, CasesFindResponse, CasesResponse, CasesStatusResponse, - CaseStatuses, CaseUserActionsResponse, CommentRequest, CommentType, @@ -167,7 +167,7 @@ export const getCases = async ({ filterOptions = { search: '', reporters: [], - status: CaseStatuses.all, + status: AllCaseType, tags: [], }, queryParams = { diff --git a/x-pack/plugins/security_solution/public/cases/containers/use_get_cases_status.tsx b/x-pack/plugins/security_solution/public/cases/containers/use_get_cases_status.tsx index a427d4dfe8f49..057fc05008bb0 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/use_get_cases_status.tsx +++ b/x-pack/plugins/security_solution/public/cases/containers/use_get_cases_status.tsx @@ -18,7 +18,6 @@ interface CasesStatusState extends CasesStatus { } const initialData: CasesStatusState = { - countAllCases: null, countClosedCases: null, countInProgressCases: null, countOpenCases: null, @@ -59,7 +58,6 @@ export const useGetCasesStatus = (): UseGetCasesStatus => { dispatchToaster, }); setCasesStatusState({ - countAllCases: 0, countClosedCases: 0, countInProgressCases: 0, countOpenCases: 0, diff --git a/x-pack/plugins/security_solution/public/cases/containers/utils.ts b/x-pack/plugins/security_solution/public/cases/containers/utils.ts index e2a4023065429..297c7e35981ac 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/utils.ts +++ b/x-pack/plugins/security_solution/public/cases/containers/utils.ts @@ -68,7 +68,6 @@ export const convertToCamelCase = (snakeCase: T): U => export const convertAllCasesToCamel = (snakeCases: CasesFindResponse): AllCases => ({ cases: snakeCases.cases.map((snakeCase) => convertToCamelCase(snakeCase)), - countAllCases: snakeCases.count_all_cases, countOpenCases: snakeCases.count_open_cases, countInProgressCases: snakeCases.count_in_progress_cases, countClosedCases: snakeCases.count_closed_cases, From d7107bdab2394f00e2a08f3c03cb48fc4fe3073e Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Wed, 24 Feb 2021 00:12:33 +0800 Subject: [PATCH 10/26] fix unit test --- .../cases/components/all_cases/index.test.tsx | 15 +++++++++++++++ .../public/cases/components/all_cases/index.tsx | 4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx index e72d1816d394a..caf41ce91ca37 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx @@ -111,6 +111,11 @@ describe('AllCases', () => { }); it('should render AllCases', async () => { + useGetCasesMock.mockReturnValue({ + ...defaultGetCases, + filterOptions: { ...defaultGetCases.filterOptions, status: CaseStatuses.open }, + }); + const wrapper = mount( @@ -144,6 +149,11 @@ describe('AllCases', () => { }); it('should render the stats', async () => { + useGetCasesMock.mockReturnValue({ + ...defaultGetCases, + filterOptions: { ...defaultGetCases.filterOptions, status: CaseStatuses.closed }, + }); + const wrapper = mount( @@ -202,6 +212,7 @@ describe('AllCases', () => { it('should render empty fields', async () => { useGetCasesMock.mockReturnValue({ ...defaultGetCases, + filterOptions: { ...defaultGetCases.filterOptions, status: CaseStatuses.open }, data: { ...defaultGetCases.data, cases: [ @@ -243,6 +254,7 @@ describe('AllCases', () => { it('should render correct actions for case (with type individual)', async () => { useGetCasesMock.mockReturnValue({ ...defaultGetCases, + filterOptions: { ...defaultGetCases.filterOptions, status: CaseStatuses.open }, }); const wrapper = mount( @@ -450,6 +462,7 @@ describe('AllCases', () => { it('Renders correct bulk actoins for case collection - enable only bulk delete if any collection is selected', async () => { useGetCasesMock.mockReturnValue({ ...defaultGetCases, + filterOptions: { ...defaultGetCases.filterOptions, status: CaseStatuses.open }, selectedCases: [ ...useGetCasesMockState.data.cases, { @@ -492,6 +505,7 @@ describe('AllCases', () => { it('Bulk close status update', async () => { useGetCasesMock.mockReturnValue({ ...defaultGetCases, + filterOptions: { ...defaultGetCases.filterOptions, status: CaseStatuses.open }, selectedCases: useGetCasesMockState.data.cases, }); @@ -532,6 +546,7 @@ describe('AllCases', () => { it('Bulk in-progress status update', async () => { useGetCasesMock.mockReturnValue({ ...defaultGetCases, + filterOptions: { ...defaultGetCases.filterOptions, status: CaseStatuses.open }, selectedCases: useGetCasesMockState.data.cases, }); diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx index dd08438408b33..8aab9f9a5c03c 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx @@ -21,7 +21,7 @@ import { isEmpty, memoize } from 'lodash/fp'; import styled, { css } from 'styled-components'; import * as i18n from './translations'; -import { CaseStatuses, CaseType } from '../../../../../case/common/api'; +import { AllCaseType, CaseStatuses, CaseType } from '../../../../../case/common/api'; import { getCasesColumns } from './columns'; import { Case, DeleteCase, FilterOptions, SortFieldCase } from '../../containers/types'; import { useGetCases, UpdateCase } from '../../containers/use_get_cases'; @@ -509,7 +509,7 @@ export const AllCases = React.memo( Date: Wed, 24 Feb 2021 01:36:29 +0800 Subject: [PATCH 11/26] disable bulk actions on status all --- .../cases/components/all_cases/index.test.tsx | 1 + .../cases/components/all_cases/index.tsx | 19 ++++++++++++------- .../public/cases/containers/api.test.tsx | 9 +++++++-- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx index caf41ce91ca37..cc32038d4794f 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx @@ -424,6 +424,7 @@ describe('AllCases', () => { it('Bulk delete', async () => { useGetCasesMock.mockReturnValue({ ...defaultGetCases, + filterOptions: { ...defaultGetCases.filterOptions, status: CaseStatuses.closed }, selectedCases: useGetCasesMockState.data.cases, }); diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx index 8aab9f9a5c03c..d91fe4f3ef6a0 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx @@ -24,7 +24,7 @@ import * as i18n from './translations'; import { AllCaseType, CaseStatuses, CaseType } from '../../../../../case/common/api'; import { getCasesColumns } from './columns'; import { Case, DeleteCase, FilterOptions, SortFieldCase } from '../../containers/types'; -import { useGetCases, UpdateCase } from '../../containers/use_get_cases'; +import { useGetCases, UpdateCase, DEFAULT_FILTER_OPTIONS } from '../../containers/use_get_cases'; import { useGetCasesStatus } from '../../containers/use_get_cases_status'; import { useDeleteCases } from '../../containers/use_delete_cases'; import { EuiBasicTableOnChange } from '../../../detections/pages/detection_engine/rules/types'; @@ -391,6 +391,9 @@ export const AllCases = React.memo( [isModal, onRowClick] ); + const enableBuckActions = + userCanCrud && !isModal && filterOptions.status !== DEFAULT_FILTER_OPTIONS.status; + return ( <> {!isEmpty(actionsErrors) && ( @@ -486,10 +489,12 @@ export const AllCases = React.memo( {!isModal && ( - - {i18n.SHOWING_SELECTED_CASES(selectedCases.length)} - - {userCanCrud && ( + {enableBuckActions && ( + + {i18n.SHOWING_SELECTED_CASES(selectedCases.length)} + + )} + {enableBuckActions && ( ( ( onChange={tableOnChangeCallback} pagination={memoizedPagination} rowProps={tableRowProps} - selection={userCanCrud && !isModal ? euiBasicTableSelectionProps : undefined} + selection={enableBuckActions ? euiBasicTableSelectionProps : undefined} sorting={sorting} /> diff --git a/x-pack/plugins/security_solution/public/cases/containers/api.test.tsx b/x-pack/plugins/security_solution/public/cases/containers/api.test.tsx index ee63749b49435..6b7a59964d874 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/api.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/containers/api.test.tsx @@ -7,7 +7,12 @@ import { KibanaServices } from '../../common/lib/kibana'; -import { ConnectorTypes, CommentType, CaseStatuses } from '../../../../case/common/api'; +import { + ConnectorTypes, + CommentType, + CaseStatuses, + AllCaseType, +} from '../../../../case/common/api'; import { CASES_URL } from '../../../../case/common/constants'; import { @@ -137,7 +142,7 @@ describe('Case Configuration API', () => { ...DEFAULT_QUERY_PARAMS, reporters: [], tags: [], - status: CaseStatuses.open, + status: AllCaseType, }, signal: abortCtrl.signal, }); From 5342462d23af0c692c4bc0e9c6eb04819c09035c Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Wed, 24 Feb 2021 02:33:22 +0800 Subject: [PATCH 12/26] clean up --- .../case/server/routes/api/cases/find_cases.ts | 5 +---- .../case/server/routes/api/cases/helpers.ts | 2 +- .../routes/api/cases/status/get_status.ts | 4 +--- .../cases/components/all_cases/index.test.tsx | 1 + .../cases/components/all_cases/index.tsx | 18 +++++++++++------- .../public/cases/containers/api.test.tsx | 9 +++++++-- 6 files changed, 22 insertions(+), 17 deletions(-) diff --git a/x-pack/plugins/case/server/routes/api/cases/find_cases.ts b/x-pack/plugins/case/server/routes/api/cases/find_cases.ts index 5195a357ed4df..ac73d0f3f2d59 100644 --- a/x-pack/plugins/case/server/routes/api/cases/find_cases.ts +++ b/x-pack/plugins/case/server/routes/api/cases/find_cases.ts @@ -54,10 +54,7 @@ export function initFindCasesApi({ caseService, caseConfigureService, router }: const [openCases, inProgressCases, closedCases] = await Promise.all([ ...caseStatuses.map((status) => { - const statusQuery = constructQueryOptions({ - ...queryArgs, - status, - }); + const statusQuery = constructQueryOptions({ ...queryArgs, status }); return caseService.findCaseStatusStats({ client, caseOptions: statusQuery.case, diff --git a/x-pack/plugins/case/server/routes/api/cases/helpers.ts b/x-pack/plugins/case/server/routes/api/cases/helpers.ts index 3d82c8712d647..a1ff379b249ac 100644 --- a/x-pack/plugins/case/server/routes/api/cases/helpers.ts +++ b/x-pack/plugins/case/server/routes/api/cases/helpers.ts @@ -28,7 +28,7 @@ export const addStatusFilter = ({ appendFilter, type = CASE_SAVED_OBJECT, }: { - status: CaseStatuses | undefined; + status: CaseStatuses; appendFilter?: string; type?: string; }) => { diff --git a/x-pack/plugins/case/server/routes/api/cases/status/get_status.ts b/x-pack/plugins/case/server/routes/api/cases/status/get_status.ts index 34efdeaa0d82c..f3cd0e2bdda5c 100644 --- a/x-pack/plugins/case/server/routes/api/cases/status/get_status.ts +++ b/x-pack/plugins/case/server/routes/api/cases/status/get_status.ts @@ -24,9 +24,7 @@ export function initGetCasesStatusApi({ caseService, router }: RouteDeps) { const [openCases, inProgressCases, closedCases] = await Promise.all([ ...caseStatuses.map((status) => { - const statusQuery = constructQueryOptions({ - status, - }); + const statusQuery = constructQueryOptions({ status }); return caseService.findCaseStatusStats({ client, caseOptions: statusQuery.case, diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx index caf41ce91ca37..cc32038d4794f 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx @@ -424,6 +424,7 @@ describe('AllCases', () => { it('Bulk delete', async () => { useGetCasesMock.mockReturnValue({ ...defaultGetCases, + filterOptions: { ...defaultGetCases.filterOptions, status: CaseStatuses.closed }, selectedCases: useGetCasesMockState.data.cases, }); diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx index 8aab9f9a5c03c..c0f17f2990638 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx @@ -24,7 +24,7 @@ import * as i18n from './translations'; import { AllCaseType, CaseStatuses, CaseType } from '../../../../../case/common/api'; import { getCasesColumns } from './columns'; import { Case, DeleteCase, FilterOptions, SortFieldCase } from '../../containers/types'; -import { useGetCases, UpdateCase } from '../../containers/use_get_cases'; +import { useGetCases, UpdateCase, DEFAULT_FILTER_OPTIONS } from '../../containers/use_get_cases'; import { useGetCasesStatus } from '../../containers/use_get_cases_status'; import { useDeleteCases } from '../../containers/use_delete_cases'; import { EuiBasicTableOnChange } from '../../../detections/pages/detection_engine/rules/types'; @@ -391,6 +391,8 @@ export const AllCases = React.memo( [isModal, onRowClick] ); + const enableBuckActions = userCanCrud && !isModal; + return ( <> {!isEmpty(actionsErrors) && ( @@ -486,10 +488,12 @@ export const AllCases = React.memo( {!isModal && ( - - {i18n.SHOWING_SELECTED_CASES(selectedCases.length)} - - {userCanCrud && ( + {enableBuckActions && ( + + {i18n.SHOWING_SELECTED_CASES(selectedCases.length)} + + )} + {enableBuckActions && ( ( ( onChange={tableOnChangeCallback} pagination={memoizedPagination} rowProps={tableRowProps} - selection={userCanCrud && !isModal ? euiBasicTableSelectionProps : undefined} + selection={enableBuckActions ? euiBasicTableSelectionProps : undefined} sorting={sorting} /> diff --git a/x-pack/plugins/security_solution/public/cases/containers/api.test.tsx b/x-pack/plugins/security_solution/public/cases/containers/api.test.tsx index ee63749b49435..6b7a59964d874 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/api.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/containers/api.test.tsx @@ -7,7 +7,12 @@ import { KibanaServices } from '../../common/lib/kibana'; -import { ConnectorTypes, CommentType, CaseStatuses } from '../../../../case/common/api'; +import { + ConnectorTypes, + CommentType, + CaseStatuses, + AllCaseType, +} from '../../../../case/common/api'; import { CASES_URL } from '../../../../case/common/constants'; import { @@ -137,7 +142,7 @@ describe('Case Configuration API', () => { ...DEFAULT_QUERY_PARAMS, reporters: [], tags: [], - status: CaseStatuses.open, + status: AllCaseType, }, signal: abortCtrl.signal, }); From b9da2f9fa765b4260420371a0ea3e1c8b611f48a Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Wed, 24 Feb 2021 03:28:18 +0800 Subject: [PATCH 13/26] fix types --- x-pack/plugins/case/server/routes/api/cases/helpers.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/case/server/routes/api/cases/helpers.ts b/x-pack/plugins/case/server/routes/api/cases/helpers.ts index a1ff379b249ac..a1a7f4f9da8f5 100644 --- a/x-pack/plugins/case/server/routes/api/cases/helpers.ts +++ b/x-pack/plugins/case/server/routes/api/cases/helpers.ts @@ -28,7 +28,7 @@ export const addStatusFilter = ({ appendFilter, type = CASE_SAVED_OBJECT, }: { - status: CaseStatuses; + status: CaseStatuses | undefined; appendFilter?: string; type?: string; }) => { @@ -102,7 +102,7 @@ export const constructQueryOptions = ({ }: { tags?: string | string[]; reporters?: string | string[]; - status?: CaseStatuses | undefined; + status?: CaseStatuses; sortByField?: string; caseType?: CaseType; }): { case: SavedObjectFindOptions; subCase?: SavedObjectFindOptions } => { From 8e0251e5cedae4ffe39ee786a97b832da093c46f Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Wed, 24 Feb 2021 19:23:31 +0800 Subject: [PATCH 14/26] fix cypress tests --- .../cypress/integration/cases/creation.spec.ts | 2 ++ .../plugins/security_solution/cypress/screens/all_cases.ts | 2 ++ .../security_solution/cypress/tasks/create_new_case.ts | 6 ++++++ 3 files changed, 10 insertions(+) diff --git a/x-pack/plugins/security_solution/cypress/integration/cases/creation.spec.ts b/x-pack/plugins/security_solution/cypress/integration/cases/creation.spec.ts index 64ce6be9ec457..f46feae946242 100644 --- a/x-pack/plugins/security_solution/cypress/integration/cases/creation.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/cases/creation.spec.ts @@ -46,6 +46,7 @@ import { backToCases, createCase, fillCasesMandatoryfields, + filterStatusOpen, } from '../../tasks/create_new_case'; import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login'; @@ -74,6 +75,7 @@ describe('Cases', () => { attachTimeline(this.mycase); createCase(); backToCases(); + filterStatusOpen(); cy.get(ALL_CASES_PAGE_TITLE).should('have.text', 'Cases'); cy.get(ALL_CASES_OPEN_CASES_STATS).should('have.text', 'Open cases1'); diff --git a/x-pack/plugins/security_solution/cypress/screens/all_cases.ts b/x-pack/plugins/security_solution/cypress/screens/all_cases.ts index e9c5ff89dd8c4..fa6b6add57bac 100644 --- a/x-pack/plugins/security_solution/cypress/screens/all_cases.ts +++ b/x-pack/plugins/security_solution/cypress/screens/all_cases.ts @@ -25,6 +25,8 @@ export const ALL_CASES_NAME = '[data-test-subj="case-details-link"]'; export const ALL_CASES_OPEN_CASES_COUNT = '[data-test-subj="case-status-filter"]'; +export const ALL_CASES_OPEN_FILTER = '[data-test-subj="case-status-filter-open"]'; + export const ALL_CASES_OPEN_CASES_STATS = '[data-test-subj="openStatsHeader"]'; export const ALL_CASES_OPENED_ON = '[data-test-subj="case-table-column-createdAt"]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/create_new_case.ts b/x-pack/plugins/security_solution/cypress/tasks/create_new_case.ts index e67cee4f38734..ed9174e2a74bb 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/create_new_case.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/create_new_case.ts @@ -11,6 +11,7 @@ import { ServiceNowconnectorOptions, TestCase, } from '../objects/case'; +import { ALL_CASES_OPEN_CASES_COUNT, ALL_CASES_OPEN_FILTER } from '../screens/all_cases'; import { BACK_TO_CASES_BTN, @@ -40,6 +41,11 @@ export const backToCases = () => { cy.get(BACK_TO_CASES_BTN).click({ force: true }); }; +export const filterStatusOpen = () => { + cy.get(ALL_CASES_OPEN_CASES_COUNT).click(); + cy.get(ALL_CASES_OPEN_FILTER).click(); +}; + export const fillCasesMandatoryfields = (newCase: TestCase) => { cy.get(TITLE_INPUT).type(newCase.name, { force: true }); newCase.tags.forEach((tag) => { From 46e46842ced3c98e26e26c1047efcc4615dd7145 Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Mon, 1 Mar 2021 15:07:26 +0800 Subject: [PATCH 15/26] review --- x-pack/plugins/case/common/api/cases/status.ts | 9 ++------- .../public/cases/components/all_cases/helpers.ts | 6 +++++- .../public/cases/components/all_cases/index.tsx | 7 +++---- .../public/cases/components/all_cases/table_filters.tsx | 2 +- .../public/cases/components/case_action_bar/helpers.ts | 3 ++- .../components/case_action_bar/status_context_menu.tsx | 5 ++--- 6 files changed, 15 insertions(+), 17 deletions(-) diff --git a/x-pack/plugins/case/common/api/cases/status.ts b/x-pack/plugins/case/common/api/cases/status.ts index 578e7893de662..031fbab38b0a2 100644 --- a/x-pack/plugins/case/common/api/cases/status.ts +++ b/x-pack/plugins/case/common/api/cases/status.ts @@ -7,7 +7,7 @@ import * as rt from 'io-ts'; -export const AllCaseType = 'all'; +export const AllCaseType = 'all' as const; export enum CaseStatuses { open = 'open', @@ -23,12 +23,7 @@ export const CaseStatusRt = rt.union([ export const caseStatuses = Object.values(CaseStatuses); -export const CaseStatusFilterRt = rt.union([ - rt.literal(AllCaseType), - rt.literal(CaseStatuses.open), - rt.literal(CaseStatuses['in-progress']), - rt.literal(CaseStatuses.closed), -]); +export const CaseStatusFilterRt = rt.union([rt.literal(AllCaseType), ...CaseStatusRt.types]); export const caseStatusFilter = Object.values(CaseStatusFilterRt); export type CaseStatusFilter = rt.TypeOf; diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/helpers.ts b/x-pack/plugins/security_solution/public/cases/components/all_cases/helpers.ts index 1ab36d3c67225..c060db360bf3d 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/helpers.ts +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/helpers.ts @@ -6,10 +6,14 @@ */ import { filter } from 'lodash/fp'; -import { AssociationType, CaseStatuses } from '../../../../../case/common/api'; +import { AssociationType, CaseStatuses, CaseType } from '../../../../../case/common/api'; import { Case, SubCase } from '../../containers/types'; import { statuses } from '../status'; +export const isSelectedCasesIncludeCollections = (selectedCases: Case[]) => + selectedCases.length > 0 && + selectedCases.some((caseObj: Case) => caseObj.type === CaseType.collection); + export const isSubCase = (theCase: Case | SubCase): theCase is SubCase => (theCase as SubCase).caseParentId !== undefined && (theCase as SubCase).associationType === AssociationType.subCase; diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx index 298491a40518a..e2399a8bd2ae1 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx @@ -21,7 +21,7 @@ import { isEmpty, memoize } from 'lodash/fp'; import styled, { css } from 'styled-components'; import * as i18n from './translations'; -import { CaseStatuses, CaseType } from '../../../../../case/common/api'; +import { CaseStatuses } from '../../../../../case/common/api'; import { getCasesColumns } from './columns'; import { Case, DeleteCase, FilterOptions, SortFieldCase } from '../../containers/types'; import { useGetCases, UpdateCase } from '../../containers/use_get_cases'; @@ -54,6 +54,7 @@ import { useKibana } from '../../../common/lib/kibana'; import { APP_ID } from '../../../../common/constants'; import { Stats } from '../status'; import { getExpandedRowMap } from './expanded_row'; +import { isSelectedCasesIncludeCollections } from './helpers'; const Div = styled.div` margin-top: ${({ theme }) => theme.eui.paddingSizes.m}; @@ -245,9 +246,7 @@ export const AllCases = React.memo( deleteCasesAction: toggleBulkDeleteModal, selectedCaseIds, updateCaseStatus: handleUpdateCaseStatus, - includeCollections: - selectedCases.length > 0 && - selectedCases.some((caseObj: Case) => caseObj.type === CaseType.collection), + includeCollections: isSelectedCasesIncludeCollections(selectedCases), })} /> ), diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx index 03e3ba6cf9297..21b4429be416d 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx @@ -44,7 +44,7 @@ const StatusFilterWrapper = styled(EuiFlexItem)` const defaultInitial = { search: '', reporters: [], - status: AllCaseType as CaseStatusFilter, + status: AllCaseType, tags: [], }; diff --git a/x-pack/plugins/security_solution/public/cases/components/case_action_bar/helpers.ts b/x-pack/plugins/security_solution/public/cases/components/case_action_bar/helpers.ts index 2eeac15ca947c..fa6132547a369 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_action_bar/helpers.ts +++ b/x-pack/plugins/security_solution/public/cases/components/case_action_bar/helpers.ts @@ -22,4 +22,5 @@ export const getStatusDate = (theCase: Case): string | null => { return null; }; -export const getStatusTitle = (status: CaseStatuses) => get(statuses, `${status}.actionBar.title`); +export const getStatusTitle = (status: CaseStatuses): string => + get(statuses, `${status}.actionBar.title`); diff --git a/x-pack/plugins/security_solution/public/cases/components/case_action_bar/status_context_menu.tsx b/x-pack/plugins/security_solution/public/cases/components/case_action_bar/status_context_menu.tsx index e0cdf9dc6d9eb..7f9ffbd8dc01d 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_action_bar/status_context_menu.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/case_action_bar/status_context_menu.tsx @@ -8,8 +8,8 @@ import React, { memo, useCallback, useMemo, useState } from 'react'; import { memoize } from 'lodash/fp'; import { EuiPopover, EuiContextMenuPanel, EuiContextMenuItem } from '@elastic/eui'; -import { CaseStatuses } from '../../../../../case/common/api'; -import { Status, statuses } from '../status'; +import { caseStatuses, CaseStatuses } from '../../../../../case/common/api'; +import { Status } from '../status'; interface Props { currentStatus: CaseStatuses; @@ -34,7 +34,6 @@ const StatusContextMenuComponent: React.FC = ({ currentStatus, onStatusCh [closePopover, onStatusChanged] ); - const caseStatuses = Object.keys(statuses) as CaseStatuses[]; const panelItems = caseStatuses.map((status: CaseStatuses) => ( Date: Mon, 1 Mar 2021 20:34:42 +0800 Subject: [PATCH 16/26] review --- .../public/cases/components/case_action_bar/helpers.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/public/cases/components/case_action_bar/helpers.ts b/x-pack/plugins/security_solution/public/cases/components/case_action_bar/helpers.ts index fa6132547a369..10f87981094fa 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_action_bar/helpers.ts +++ b/x-pack/plugins/security_solution/public/cases/components/case_action_bar/helpers.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { get } from 'lodash'; import { CaseStatuses } from '../../../../../case/common/api'; import { Case } from '../../containers/types'; import { statuses } from '../status'; @@ -22,5 +21,4 @@ export const getStatusDate = (theCase: Case): string | null => { return null; }; -export const getStatusTitle = (status: CaseStatuses): string => - get(statuses, `${status}.actionBar.title`); +export const getStatusTitle = (status: CaseStatuses) => statuses[status].actionBar.title; From 1d4f9abcf2e2b727fb6516a9483af71f4b5e1786 Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Tue, 2 Mar 2021 00:27:51 +0800 Subject: [PATCH 17/26] update status is only available for individual cases --- .../public/cases/components/all_cases/actions.tsx | 12 +++++------- .../public/cases/components/all_cases/helpers.ts | 6 ++++++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx index 6d6907d50f579..3010bf8e4e5c6 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx @@ -8,11 +8,12 @@ import { Dispatch } from 'react'; import { DefaultItemIconButtonAction } from '@elastic/eui/src/components/basic_table/action_types'; -import { CaseStatuses, CaseType } from '../../../../../case/common/api'; +import { CaseStatuses } from '../../../../../case/common/api'; import { Case, SubCase } from '../../containers/types'; import { UpdateCase } from '../../containers/use_get_cases'; import { statuses } from '../status'; import * as i18n from './translations'; +import { isIndividual } from './helpers'; interface GetActions { caseStatus: string; @@ -20,9 +21,6 @@ interface GetActions { deleteCaseOnClick: (deleteCase: Case) => void; } -const isCollection = (theCase: Case | SubCase | null | undefined) => - theCase != null && (theCase as Case).type === CaseType.collection; - export const getActions = ({ caseStatus, dispatchUpdate, @@ -30,7 +28,7 @@ export const getActions = ({ }: GetActions): Array> => { const openCaseAction = { available: (item: Case | SubCase) => caseStatus !== CaseStatuses.open, - enabled: (item: Case | SubCase) => !isCollection(item), + enabled: (item: Case | SubCase) => isIndividual(item), description: statuses[CaseStatuses.open].actions.single.title, icon: statuses[CaseStatuses.open].icon, name: statuses[CaseStatuses.open].actions.single.title, @@ -47,7 +45,7 @@ export const getActions = ({ const makeInProgressAction = { available: (item: Case) => caseStatus !== CaseStatuses['in-progress'], - enabled: (item: Case | SubCase) => !isCollection(item), + enabled: (item: Case | SubCase) => isIndividual(item), description: statuses[CaseStatuses['in-progress']].actions.single.title, icon: statuses[CaseStatuses['in-progress']].icon, name: statuses[CaseStatuses['in-progress']].actions.single.title, @@ -64,7 +62,7 @@ export const getActions = ({ const closeCaseAction = { available: (item: Case | SubCase) => caseStatus !== CaseStatuses.closed, - enabled: (item: Case | SubCase) => !isCollection(item), + enabled: (item: Case | SubCase) => isIndividual(item), description: statuses[CaseStatuses.closed].actions.single.title, icon: statuses[CaseStatuses.closed].icon, name: statuses[CaseStatuses.closed].actions.single.title, diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/helpers.ts b/x-pack/plugins/security_solution/public/cases/components/all_cases/helpers.ts index c060db360bf3d..519be95fcdfef 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/helpers.ts +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/helpers.ts @@ -18,6 +18,12 @@ export const isSubCase = (theCase: Case | SubCase): theCase is SubCase => (theCase as SubCase).caseParentId !== undefined && (theCase as SubCase).associationType === AssociationType.subCase; +export const isCollection = (theCase: Case | SubCase | null | undefined) => + theCase != null && (theCase as Case).type === CaseType.collection; + +export const isIndividual = (theCase: Case | SubCase | null | undefined) => + theCase != null && (theCase as Case).type === CaseType.individual; + export const getSubCasesStatusCountsBadges = ( subCases: SubCase[] ): Array<{ name: CaseStatuses; color: string; count: number }> => { From 067b20eb77ac41a10cce86ad031c386e1cc9ddfb Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Tue, 2 Mar 2021 00:37:18 +0800 Subject: [PATCH 18/26] update available actions on status all --- .../public/cases/components/all_cases/actions.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx index 3010bf8e4e5c6..e6d1f359242eb 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx @@ -28,7 +28,7 @@ export const getActions = ({ }: GetActions): Array> => { const openCaseAction = { available: (item: Case | SubCase) => caseStatus !== CaseStatuses.open, - enabled: (item: Case | SubCase) => isIndividual(item), + enabled: (item: Case | SubCase) => isIndividual(item) && item.status !== CaseStatuses.open, description: statuses[CaseStatuses.open].actions.single.title, icon: statuses[CaseStatuses.open].icon, name: statuses[CaseStatuses.open].actions.single.title, @@ -45,7 +45,8 @@ export const getActions = ({ const makeInProgressAction = { available: (item: Case) => caseStatus !== CaseStatuses['in-progress'], - enabled: (item: Case | SubCase) => isIndividual(item), + enabled: (item: Case | SubCase) => + isIndividual(item) && item.status !== CaseStatuses['in-progress'], description: statuses[CaseStatuses['in-progress']].actions.single.title, icon: statuses[CaseStatuses['in-progress']].icon, name: statuses[CaseStatuses['in-progress']].actions.single.title, @@ -62,7 +63,7 @@ export const getActions = ({ const closeCaseAction = { available: (item: Case | SubCase) => caseStatus !== CaseStatuses.closed, - enabled: (item: Case | SubCase) => isIndividual(item), + enabled: (item: Case | SubCase) => isIndividual(item) && item.status !== CaseStatuses.closed, description: statuses[CaseStatuses.closed].actions.single.title, icon: statuses[CaseStatuses.closed].icon, name: statuses[CaseStatuses.closed].actions.single.title, From 94da327cf9db58e97f37e5665caf56f2975af17e Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Tue, 2 Mar 2021 01:02:10 +0800 Subject: [PATCH 19/26] fix unit test --- .../cases/components/all_cases/index.test.tsx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx index 6f2d1d16e0915..813fd2d8623d1 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx @@ -310,11 +310,12 @@ describe('AllCases', () => { ) .last() .simulate('click'); + expect(wrapper.find('[data-test-subj="action-open"]').first().props().disabled).toEqual(true); expect( wrapper.find('[data-test-subj="action-in-progress"]').first().props().disabled - ).toEqual(false); + ).toEqual(true); expect(wrapper.find('[data-test-subj="action-close"]').first().props().disabled).toEqual( - false + true ); expect(wrapper.find('[data-test-subj="action-delete"]').first().props().disabled).toEqual( false @@ -379,6 +380,15 @@ describe('AllCases', () => { it('opens case when row action icon clicked', async () => { useGetCasesMock.mockReturnValue({ ...defaultGetCases, + data: { + ...defaultGetCases.data, + cases: [ + { + ...defaultGetCases.data.cases[0], + status: CaseStatuses.closed, + }, + ], + }, filterOptions: { ...defaultGetCases.filterOptions, status: CaseStatuses.closed }, }); From e1c285f65962fd45fd4e0157693af6e4410d8f68 Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Tue, 2 Mar 2021 15:23:23 +0800 Subject: [PATCH 20/26] remove lodash get --- .../public/cases/components/bulk_actions/index.tsx | 7 +++---- .../public/cases/components/status/button.tsx | 2 +- .../public/cases/components/status/stats.tsx | 3 +-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/security_solution/public/cases/components/bulk_actions/index.tsx b/x-pack/plugins/security_solution/public/cases/components/bulk_actions/index.tsx index 342a71078ab59..7547e481efbed 100644 --- a/x-pack/plugins/security_solution/public/cases/components/bulk_actions/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/bulk_actions/index.tsx @@ -8,7 +8,6 @@ import React from 'react'; import { EuiContextMenuItem } from '@elastic/eui'; -import { get } from 'lodash'; import { CaseStatuses, CaseStatusFilter } from '../../../../../case/common/api'; import { statuses } from '../status'; import * as i18n from './translations'; @@ -43,7 +42,7 @@ export const getBulkItems = ({ updateCaseStatus(CaseStatuses.open); }} > - {get(statuses, `${CaseStatuses.open}.actions.bulk.title`)} + {statuses[CaseStatuses.open].actions.bulk.title} ); @@ -58,7 +57,7 @@ export const getBulkItems = ({ updateCaseStatus(CaseStatuses['in-progress']); }} > - {get(statuses, `${CaseStatuses['in-progress']}.actions.bulk.title`)} + {statuses[CaseStatuses['in-progress']].actions.bulk.title} ); @@ -73,7 +72,7 @@ export const getBulkItems = ({ updateCaseStatus(CaseStatuses.closed); }} > - {get(statuses, `${CaseStatuses.closed}.actions.bulk.title`)} + {statuses[CaseStatuses.closed].actions.bulk.title} ); diff --git a/x-pack/plugins/security_solution/public/cases/components/status/button.tsx b/x-pack/plugins/security_solution/public/cases/components/status/button.tsx index 03d4b6bd783fc..e2691115980ea 100644 --- a/x-pack/plugins/security_solution/public/cases/components/status/button.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/status/button.tsx @@ -46,7 +46,7 @@ const StatusActionButtonComponent: React.FC = ({ isLoading={isLoading} onClick={onClick} > - {get(statuses, `${caseStatuses[nextStatusIndex]}.button.label`)} + {statuses[caseStatuses[nextStatusIndex]].button.label} ); }; diff --git a/x-pack/plugins/security_solution/public/cases/components/status/stats.tsx b/x-pack/plugins/security_solution/public/cases/components/status/stats.tsx index f849efdae7c02..572a169f64cf6 100644 --- a/x-pack/plugins/security_solution/public/cases/components/status/stats.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/status/stats.tsx @@ -7,7 +7,6 @@ import React, { memo, useMemo } from 'react'; import { EuiDescriptionList, EuiLoadingSpinner } from '@elastic/eui'; -import { get } from 'lodash'; import { CaseStatuses } from '../../../../../case/common/api'; import { statuses } from './config'; @@ -22,7 +21,7 @@ const StatsComponent: React.FC = ({ caseCount, caseStatus, isLoading, dat const statusStats = useMemo( () => [ { - title: get(statuses, `${caseStatus}.stats.title`), + title: statuses[caseStatus].stats.title, description: isLoading ? ( ) : ( From c1508bab9cc0f32c2ee7d12651a50661eb50575e Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Tue, 2 Mar 2021 15:30:14 +0800 Subject: [PATCH 21/26] rename status all --- x-pack/plugins/case/common/api/cases/status.ts | 4 ++-- .../plugins/case/server/routes/api/cases/find_cases.ts | 4 ++-- .../public/cases/components/all_cases/index.test.tsx | 4 ++-- .../cases/components/all_cases/status_filter.test.tsx | 4 ++-- .../public/cases/components/all_cases/status_filter.tsx | 8 +++----- .../public/cases/components/all_cases/table_filters.tsx | 6 +++--- .../public/cases/components/status/button.tsx | 1 - .../public/cases/components/status/config.ts | 6 +++--- .../public/cases/components/status/status.tsx | 6 +++--- .../public/cases/containers/api.test.tsx | 9 ++------- .../security_solution/public/cases/containers/api.ts | 4 ++-- .../security_solution/public/cases/containers/types.ts | 2 -- .../public/cases/containers/use_get_cases.tsx | 4 ++-- 13 files changed, 26 insertions(+), 36 deletions(-) diff --git a/x-pack/plugins/case/common/api/cases/status.ts b/x-pack/plugins/case/common/api/cases/status.ts index 031fbab38b0a2..a50e5e2cf710a 100644 --- a/x-pack/plugins/case/common/api/cases/status.ts +++ b/x-pack/plugins/case/common/api/cases/status.ts @@ -7,7 +7,7 @@ import * as rt from 'io-ts'; -export const AllCaseType = 'all' as const; +export const StatusAll = 'all' as const; export enum CaseStatuses { open = 'open', @@ -23,7 +23,7 @@ export const CaseStatusRt = rt.union([ export const caseStatuses = Object.values(CaseStatuses); -export const CaseStatusFilterRt = rt.union([rt.literal(AllCaseType), ...CaseStatusRt.types]); +export const CaseStatusFilterRt = rt.union([rt.literal(StatusAll), ...CaseStatusRt.types]); export const caseStatusFilter = Object.values(CaseStatusFilterRt); export type CaseStatusFilter = rt.TypeOf; diff --git a/x-pack/plugins/case/server/routes/api/cases/find_cases.ts b/x-pack/plugins/case/server/routes/api/cases/find_cases.ts index 70d96e9694347..61bb69acbbe1e 100644 --- a/x-pack/plugins/case/server/routes/api/cases/find_cases.ts +++ b/x-pack/plugins/case/server/routes/api/cases/find_cases.ts @@ -16,7 +16,7 @@ import { CasesFindRequestRt, throwErrors, caseStatuses, - AllCaseType, + StatusAll, } from '../../../../common/api'; import { transformCases, wrapError, escapeHatch } from '../utils'; import { RouteDeps } from '../types'; @@ -42,7 +42,7 @@ export function initFindCasesApi({ caseService, router, logger }: RouteDeps) { tags: queryParams.tags, reporters: queryParams.reporters, sortByField: queryParams.sortField, - status: queryParams.status === AllCaseType ? undefined : queryParams.status, + status: queryParams.status === StatusAll ? undefined : queryParams.status, caseType: queryParams.type, }; const caseQueries = constructQueryOptions(queryArgs); diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx index 813fd2d8623d1..6ca98faa3ab4d 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx @@ -14,7 +14,7 @@ import { TestProviders } from '../../../common/mock'; import { casesStatus, useGetCasesMockState } from '../../containers/mock'; import * as i18n from './translations'; -import { AllCaseType, CaseStatuses, CaseType } from '../../../../../case/common/api'; +import { StatusAll, CaseStatuses, CaseType } from '../../../../../case/common/api'; import { useKibana } from '../../../common/lib/kibana'; import { getEmptyTagValue } from '../../../common/components/empty_value'; import { useDeleteCases } from '../../containers/use_delete_cases'; @@ -473,7 +473,7 @@ describe('AllCases', () => { it('Renders only bulk delete on status all', async () => { useGetCasesMock.mockReturnValue({ ...defaultGetCases, - filterOptions: { ...defaultGetCases.filterOptions, status: AllCaseType }, + filterOptions: { ...defaultGetCases.filterOptions, status: StatusAll }, selectedCases: [...useGetCasesMockState.data.cases], }); diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/status_filter.test.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/status_filter.test.tsx index 4d6e4a2a612d4..dde0887d96ded 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/status_filter.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/status_filter.test.tsx @@ -9,11 +9,11 @@ import React from 'react'; import { mount } from 'enzyme'; import { waitFor } from '@testing-library/react'; -import { AllCaseType, CaseStatuses } from '../../../../../case/common/api'; +import { StatusAll, CaseStatuses } from '../../../../../case/common/api'; import { StatusFilter } from './status_filter'; const stats = { - [AllCaseType]: 0, + [StatusAll]: 0, [CaseStatuses.open]: 2, [CaseStatuses['in-progress']]: 5, [CaseStatuses.closed]: 7, diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/status_filter.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/status_filter.tsx index 1d368567f588f..8529165cd2eaa 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/status_filter.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/status_filter.tsx @@ -7,7 +7,7 @@ import React, { memo } from 'react'; import { EuiSuperSelect, EuiSuperSelectOption, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { AllCaseType, CaseStatusFilter } from '../../../../../case/common/api'; +import { StatusAll, CaseStatusFilter } from '../../../../../case/common/api'; import { Status, statuses } from '../status'; interface Props { @@ -24,7 +24,7 @@ const StatusFilterComponent: React.FC = ({ disabledStatuses = [], }) => { const caseStatuses = Object.keys(statuses) as CaseStatusFilter[]; - const options: Array> = [AllCaseType, ...caseStatuses].map( + const options: Array> = [StatusAll, ...caseStatuses].map( (status) => ({ value: status, inputDisplay: ( @@ -32,9 +32,7 @@ const StatusFilterComponent: React.FC = ({ - {status !== AllCaseType && ( - {` (${stats[status]})`} - )} + {status !== StatusAll && {` (${stats[status]})`}} ), disabled: disabledStatuses.includes(status), diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx index c22ffef8918a7..c5b1a4de59a7a 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx @@ -10,7 +10,7 @@ import { isEqual } from 'lodash/fp'; import styled from 'styled-components'; import { EuiFlexGroup, EuiFlexItem, EuiFieldSearch, EuiFilterGroup } from '@elastic/eui'; -import { AllCaseType, CaseStatuses, CaseStatusFilter } from '../../../../../case/common/api'; +import { StatusAll, CaseStatuses, CaseStatusFilter } from '../../../../../case/common/api'; import { FilterOptions } from '../../containers/types'; import { useGetTags } from '../../containers/use_get_tags'; import { useGetReporters } from '../../containers/use_get_reporters'; @@ -45,7 +45,7 @@ const StatusFilterWrapper = styled(EuiFlexItem)` const defaultInitial = { search: '', reporters: [], - status: AllCaseType, + status: StatusAll, tags: [], }; @@ -139,7 +139,7 @@ const CasesTableFiltersComponent = ({ const stats = useMemo( () => ({ - [AllCaseType]: null, + [StatusAll]: null, [CaseStatuses.open]: countOpenCases ?? 0, [CaseStatuses['in-progress']]: countInProgressCases ?? 0, [CaseStatuses.closed]: countClosedCases ?? 0, diff --git a/x-pack/plugins/security_solution/public/cases/components/status/button.tsx b/x-pack/plugins/security_solution/public/cases/components/status/button.tsx index e2691115980ea..4ee69766fe128 100644 --- a/x-pack/plugins/security_solution/public/cases/components/status/button.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/status/button.tsx @@ -8,7 +8,6 @@ import React, { memo, useCallback, useMemo } from 'react'; import { EuiButton } from '@elastic/eui'; -import { get } from 'lodash'; import { CaseStatuses, caseStatuses } from '../../../../../case/common/api'; import { statuses } from './config'; diff --git a/x-pack/plugins/security_solution/public/cases/components/status/config.ts b/x-pack/plugins/security_solution/public/cases/components/status/config.ts index e7143f43f4ab5..596bb03148009 100644 --- a/x-pack/plugins/security_solution/public/cases/components/status/config.ts +++ b/x-pack/plugins/security_solution/public/cases/components/status/config.ts @@ -5,10 +5,10 @@ * 2.0. */ import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; -import { AllCaseType, CaseStatuses } from '../../../../../case/common/api'; +import { StatusAll, CaseStatuses } from '../../../../../case/common/api'; import * as i18n from './translations'; -type AllCaseStatus = Record; +type AllCaseStatus = Record; type Statuses = Record< CaseStatuses, @@ -38,7 +38,7 @@ type Statuses = Record< >; export const allCaseStatus: AllCaseStatus = { - [AllCaseType]: { color: 'hollow', label: i18n.ALL }, + [StatusAll]: { color: 'hollow', label: i18n.ALL }, }; export const statuses: Statuses = { diff --git a/x-pack/plugins/security_solution/public/cases/components/status/status.tsx b/x-pack/plugins/security_solution/public/cases/components/status/status.tsx index 065a3862b8c24..1dea6c5b95e3b 100644 --- a/x-pack/plugins/security_solution/public/cases/components/status/status.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/status/status.tsx @@ -9,7 +9,7 @@ import React, { memo, useMemo } from 'react'; import { noop } from 'lodash/fp'; import { EuiBadge } from '@elastic/eui'; -import { AllCaseType, CaseStatusFilter } from '../../../../../case/common/api'; +import { StatusAll, CaseStatusFilter } from '../../../../../case/common/api'; import { allCaseStatus, statuses } from './config'; import * as i18n from './translations'; @@ -22,7 +22,7 @@ interface Props { const StatusComponent: React.FC = ({ type, withArrow = false, onClick = noop }) => { const props = useMemo( () => ({ - color: type === AllCaseType ? allCaseStatus[AllCaseType].color : statuses[type].color, + color: type === StatusAll ? allCaseStatus[StatusAll].color : statuses[type].color, ...(withArrow ? { iconType: 'arrowDown', iconSide: 'right' as const } : {}), }), [withArrow, type] @@ -35,7 +35,7 @@ const StatusComponent: React.FC = ({ type, withArrow = false, onClick = n iconOnClickAriaLabel={i18n.STATUS_ICON_ARIA} data-test-subj={`status-badge-${type}`} > - {type === AllCaseType ? allCaseStatus[AllCaseType].label : statuses[type].label} + {type === StatusAll ? allCaseStatus[StatusAll].label : statuses[type].label} ); }; diff --git a/x-pack/plugins/security_solution/public/cases/containers/api.test.tsx b/x-pack/plugins/security_solution/public/cases/containers/api.test.tsx index 6b7a59964d874..f1ade938f0814 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/api.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/containers/api.test.tsx @@ -7,12 +7,7 @@ import { KibanaServices } from '../../common/lib/kibana'; -import { - ConnectorTypes, - CommentType, - CaseStatuses, - AllCaseType, -} from '../../../../case/common/api'; +import { ConnectorTypes, CommentType, CaseStatuses, StatusAll } from '../../../../case/common/api'; import { CASES_URL } from '../../../../case/common/constants'; import { @@ -142,7 +137,7 @@ describe('Case Configuration API', () => { ...DEFAULT_QUERY_PARAMS, reporters: [], tags: [], - status: AllCaseType, + status: StatusAll, }, signal: abortCtrl.signal, }); diff --git a/x-pack/plugins/security_solution/public/cases/containers/api.ts b/x-pack/plugins/security_solution/public/cases/containers/api.ts index 2c33d6fc97cf5..20fef78d003ed 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/api.ts +++ b/x-pack/plugins/security_solution/public/cases/containers/api.ts @@ -8,7 +8,7 @@ import { assign } from 'lodash'; import { - AllCaseType, + StatusAll, CasePatchRequest, CasePostRequest, CaseResponse, @@ -167,7 +167,7 @@ export const getCases = async ({ filterOptions = { search: '', reporters: [], - status: AllCaseType, + status: StatusAll, tags: [], }, queryParams = { diff --git a/x-pack/plugins/security_solution/public/cases/containers/types.ts b/x-pack/plugins/security_solution/public/cases/containers/types.ts index 491ac0a90326d..ee4132766904b 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/types.ts +++ b/x-pack/plugins/security_solution/public/cases/containers/types.ts @@ -21,8 +21,6 @@ import { export { CaseConnector, ActionConnector, CaseStatuses } from '../../../../case/common/api'; -export type AllCaseType = AssociationType & CaseType; - export type Comment = CommentRequest & { associationType: AssociationType; id: string; diff --git a/x-pack/plugins/security_solution/public/cases/containers/use_get_cases.tsx b/x-pack/plugins/security_solution/public/cases/containers/use_get_cases.tsx index b8ab36667ed1c..148a5d5077b08 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/use_get_cases.tsx +++ b/x-pack/plugins/security_solution/public/cases/containers/use_get_cases.tsx @@ -6,7 +6,7 @@ */ import { useCallback, useEffect, useReducer, useRef } from 'react'; -import { AllCaseType } from '../../../../case/common/api'; +import { StatusAll } from '../../../../case/common/api'; import { DEFAULT_TABLE_ACTIVE_PAGE, DEFAULT_TABLE_LIMIT } from './constants'; import { AllCases, SortFieldCase, FilterOptions, QueryParams, Case, UpdateByKey } from './types'; import { errorToToaster, useStateToaster } from '../../common/components/toasters'; @@ -95,7 +95,7 @@ const dataFetchReducer = (state: UseGetCasesState, action: Action): UseGetCasesS export const DEFAULT_FILTER_OPTIONS: FilterOptions = { search: '', reporters: [], - status: AllCaseType, + status: StatusAll, tags: [], }; From a4532694400e647f554c45a8ccf65d5da2e90523 Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Tue, 2 Mar 2021 15:33:56 +0800 Subject: [PATCH 22/26] omit status if it is set to all --- .../server/routes/api/cases/find_cases.ts | 22 +++++++++++++------ .../case/server/routes/api/cases/helpers.ts | 2 +- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/case/server/routes/api/cases/find_cases.ts b/x-pack/plugins/case/server/routes/api/cases/find_cases.ts index 61bb69acbbe1e..ac58631b05081 100644 --- a/x-pack/plugins/case/server/routes/api/cases/find_cases.ts +++ b/x-pack/plugins/case/server/routes/api/cases/find_cases.ts @@ -38,13 +38,21 @@ export function initFindCasesApi({ caseService, router, logger }: RouteDeps) { CasesFindRequestRt.decode(request.query), fold(throwErrors(Boom.badRequest), identity) ); - const queryArgs = { - tags: queryParams.tags, - reporters: queryParams.reporters, - sortByField: queryParams.sortField, - status: queryParams.status === StatusAll ? undefined : queryParams.status, - caseType: queryParams.type, - }; + const queryArgs = + queryParams.status === StatusAll + ? { + tags: queryParams.tags, + reporters: queryParams.reporters, + sortByField: queryParams.sortField, + caseType: queryParams.type, + } + : { + tags: queryParams.tags, + reporters: queryParams.reporters, + sortByField: queryParams.sortField, + status: queryParams.status, + caseType: queryParams.type, + }; const caseQueries = constructQueryOptions(queryArgs); const cases = await caseService.findCasesGroupedByID({ client, diff --git a/x-pack/plugins/case/server/routes/api/cases/helpers.ts b/x-pack/plugins/case/server/routes/api/cases/helpers.ts index a1a7f4f9da8f5..8659ab02d6d53 100644 --- a/x-pack/plugins/case/server/routes/api/cases/helpers.ts +++ b/x-pack/plugins/case/server/routes/api/cases/helpers.ts @@ -28,7 +28,7 @@ export const addStatusFilter = ({ appendFilter, type = CASE_SAVED_OBJECT, }: { - status: CaseStatuses | undefined; + status?: CaseStatuses; appendFilter?: string; type?: string; }) => { From a812a9860675917ba6e886957732c0b10c14a405 Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Tue, 2 Mar 2021 16:42:42 +0800 Subject: [PATCH 23/26] do not sent status if itis set to all --- x-pack/plugins/case/common/api/cases/case.ts | 4 ++-- .../server/routes/api/cases/find_cases.ts | 24 +++++++------------ .../public/cases/containers/api.ts | 4 ++-- 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/x-pack/plugins/case/common/api/cases/case.ts b/x-pack/plugins/case/common/api/cases/case.ts index 8924ce78aa1c3..33a93952b0e2d 100644 --- a/x-pack/plugins/case/common/api/cases/case.ts +++ b/x-pack/plugins/case/common/api/cases/case.ts @@ -10,7 +10,7 @@ import * as rt from 'io-ts'; import { NumberFromString } from '../saved_object'; import { UserRT } from '../user'; import { CommentResponseRt } from './comment'; -import { CasesStatusResponseRt, CaseStatusFilterRt, CaseStatusRt } from './status'; +import { CasesStatusResponseRt, CaseStatusRt } from './status'; import { CaseConnectorRt, ESCaseConnector } from '../connectors'; import { SubCaseResponseRt } from './sub_case'; @@ -102,7 +102,7 @@ export const CasePostRequestRt = rt.intersection([ export const CasesFindRequestRt = rt.partial({ type: CaseTypeRt, tags: rt.union([rt.array(rt.string), rt.string]), - status: CaseStatusFilterRt, + status: CaseStatusRt, reporters: rt.union([rt.array(rt.string), rt.string]), defaultSearchOperator: rt.union([rt.literal('AND'), rt.literal('OR')]), fields: rt.array(rt.string), diff --git a/x-pack/plugins/case/server/routes/api/cases/find_cases.ts b/x-pack/plugins/case/server/routes/api/cases/find_cases.ts index ac58631b05081..bc6907f52b9eb 100644 --- a/x-pack/plugins/case/server/routes/api/cases/find_cases.ts +++ b/x-pack/plugins/case/server/routes/api/cases/find_cases.ts @@ -16,7 +16,6 @@ import { CasesFindRequestRt, throwErrors, caseStatuses, - StatusAll, } from '../../../../common/api'; import { transformCases, wrapError, escapeHatch } from '../utils'; import { RouteDeps } from '../types'; @@ -38,21 +37,14 @@ export function initFindCasesApi({ caseService, router, logger }: RouteDeps) { CasesFindRequestRt.decode(request.query), fold(throwErrors(Boom.badRequest), identity) ); - const queryArgs = - queryParams.status === StatusAll - ? { - tags: queryParams.tags, - reporters: queryParams.reporters, - sortByField: queryParams.sortField, - caseType: queryParams.type, - } - : { - tags: queryParams.tags, - reporters: queryParams.reporters, - sortByField: queryParams.sortField, - status: queryParams.status, - caseType: queryParams.type, - }; + const queryArgs = { + tags: queryParams.tags, + reporters: queryParams.reporters, + sortByField: queryParams.sortField, + status: queryParams.status, + caseType: queryParams.type, + }; + const caseQueries = constructQueryOptions(queryArgs); const cases = await caseService.findCasesGroupedByID({ client, diff --git a/x-pack/plugins/security_solution/public/cases/containers/api.ts b/x-pack/plugins/security_solution/public/cases/containers/api.ts index 20fef78d003ed..9cd1467e09090 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/api.ts +++ b/x-pack/plugins/security_solution/public/cases/containers/api.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { assign } from 'lodash'; +import { assign, omit } from 'lodash'; import { StatusAll, @@ -187,7 +187,7 @@ export const getCases = async ({ }; const response = await KibanaServices.get().http.fetch(`${CASES_URL}/_find`, { method: 'GET', - query, + query: query.status === StatusAll ? omit(query, ['status']) : query, signal, }); return convertAllCasesToCamel(decodeCasesFindResponse(response)); From 89be6cf5e180aeaaadee448538bc2cdbefb438d6 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Tue, 2 Mar 2021 11:45:22 +0200 Subject: [PATCH 24/26] Remove all status from the backend --- .../plugins/case/common/api/cases/status.ts | 7 --- .../components/all_cases/status_filter.tsx | 44 +++++++++---------- .../components/all_cases/table_filters.tsx | 5 ++- .../cases/components/bulk_actions/index.tsx | 6 +-- .../public/cases/components/status/config.ts | 33 +------------- .../public/cases/components/status/index.ts | 1 + .../public/cases/components/status/status.tsx | 4 +- .../public/cases/components/status/types.ts | 43 ++++++++++++++++++ .../public/cases/containers/types.ts | 4 +- 9 files changed, 78 insertions(+), 69 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/cases/components/status/types.ts diff --git a/x-pack/plugins/case/common/api/cases/status.ts b/x-pack/plugins/case/common/api/cases/status.ts index a50e5e2cf710a..7286e19da9159 100644 --- a/x-pack/plugins/case/common/api/cases/status.ts +++ b/x-pack/plugins/case/common/api/cases/status.ts @@ -7,8 +7,6 @@ import * as rt from 'io-ts'; -export const StatusAll = 'all' as const; - export enum CaseStatuses { open = 'open', 'in-progress' = 'in-progress', @@ -23,11 +21,6 @@ export const CaseStatusRt = rt.union([ export const caseStatuses = Object.values(CaseStatuses); -export const CaseStatusFilterRt = rt.union([rt.literal(StatusAll), ...CaseStatusRt.types]); - -export const caseStatusFilter = Object.values(CaseStatusFilterRt); -export type CaseStatusFilter = rt.TypeOf; - export const CasesStatusResponseRt = rt.type({ count_open_cases: rt.number, count_in_progress_cases: rt.number, diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/status_filter.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/status_filter.tsx index 8529165cd2eaa..34186a201cc05 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/status_filter.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/status_filter.tsx @@ -7,14 +7,13 @@ import React, { memo } from 'react'; import { EuiSuperSelect, EuiSuperSelectOption, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { StatusAll, CaseStatusFilter } from '../../../../../case/common/api'; -import { Status, statuses } from '../status'; +import { Status, statuses, StatusAll, CaseStatusWithAllStatus } from '../status'; interface Props { - stats: Record; - selectedStatus: CaseStatusFilter; - onStatusChanged: (status: CaseStatusFilter) => void; - disabledStatuses?: CaseStatusFilter[]; + stats: Record; + selectedStatus: CaseStatusWithAllStatus; + onStatusChanged: (status: CaseStatusWithAllStatus) => void; + disabledStatuses?: CaseStatusWithAllStatus[]; } const StatusFilterComponent: React.FC = ({ @@ -23,22 +22,23 @@ const StatusFilterComponent: React.FC = ({ onStatusChanged, disabledStatuses = [], }) => { - const caseStatuses = Object.keys(statuses) as CaseStatusFilter[]; - const options: Array> = [StatusAll, ...caseStatuses].map( - (status) => ({ - value: status, - inputDisplay: ( - - - - - {status !== StatusAll && {` (${stats[status]})`}} - - ), - disabled: disabledStatuses.includes(status), - 'data-test-subj': `case-status-filter-${status}`, - }) - ); + const caseStatuses = Object.keys(statuses) as CaseStatusWithAllStatus[]; + const options: Array> = [ + StatusAll, + ...caseStatuses, + ].map((status) => ({ + value: status, + inputDisplay: ( + + + + + {status !== StatusAll && {` (${stats[status]})`}} + + ), + disabled: disabledStatuses.includes(status), + 'data-test-subj': `case-status-filter-${status}`, + })); return ( { + (status: CaseStatusWithAllStatus) => { onFilterChanged({ status }); }, [onFilterChanged] diff --git a/x-pack/plugins/security_solution/public/cases/components/bulk_actions/index.tsx b/x-pack/plugins/security_solution/public/cases/components/bulk_actions/index.tsx index 7547e481efbed..a6d5a0679df37 100644 --- a/x-pack/plugins/security_solution/public/cases/components/bulk_actions/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/bulk_actions/index.tsx @@ -8,12 +8,12 @@ import React from 'react'; import { EuiContextMenuItem } from '@elastic/eui'; -import { CaseStatuses, CaseStatusFilter } from '../../../../../case/common/api'; -import { statuses } from '../status'; +import { CaseStatuses } from '../../../../../case/common/api'; +import { statuses, CaseStatusWithAllStatus } from '../status'; import * as i18n from './translations'; interface GetBulkItems { - caseStatus: CaseStatusFilter; + caseStatus: CaseStatusWithAllStatus; closePopover: () => void; deleteCasesAction: (cases: string[]) => void; selectedCaseIds: string[]; diff --git a/x-pack/plugins/security_solution/public/cases/components/status/config.ts b/x-pack/plugins/security_solution/public/cases/components/status/config.ts index 596bb03148009..eab4bebedf064 100644 --- a/x-pack/plugins/security_solution/public/cases/components/status/config.ts +++ b/x-pack/plugins/security_solution/public/cases/components/status/config.ts @@ -4,38 +4,9 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; -import { StatusAll, CaseStatuses } from '../../../../../case/common/api'; +import { CaseStatuses } from '../../../../../case/common/api'; import * as i18n from './translations'; - -type AllCaseStatus = Record; - -type Statuses = Record< - CaseStatuses, - { - color: string; - label: string; - icon: EuiIconType; - actions: { - bulk: { - title: string; - }; - single: { - title: string; - description?: string; - }; - }; - actionBar: { - title: string; - }; - button: { - label: string; - }; - stats: { - title: string; - }; - } ->; +import { AllCaseStatus, Statuses, StatusAll } from './types'; export const allCaseStatus: AllCaseStatus = { [StatusAll]: { color: 'hollow', label: i18n.ALL }, diff --git a/x-pack/plugins/security_solution/public/cases/components/status/index.ts b/x-pack/plugins/security_solution/public/cases/components/status/index.ts index 2da6cd26d5ab4..94d7cb6a31830 100644 --- a/x-pack/plugins/security_solution/public/cases/components/status/index.ts +++ b/x-pack/plugins/security_solution/public/cases/components/status/index.ts @@ -8,3 +8,4 @@ export * from './status'; export * from './config'; export * from './stats'; +export * from './types'; diff --git a/x-pack/plugins/security_solution/public/cases/components/status/status.tsx b/x-pack/plugins/security_solution/public/cases/components/status/status.tsx index 1dea6c5b95e3b..de4c979daf4c1 100644 --- a/x-pack/plugins/security_solution/public/cases/components/status/status.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/status/status.tsx @@ -9,12 +9,12 @@ import React, { memo, useMemo } from 'react'; import { noop } from 'lodash/fp'; import { EuiBadge } from '@elastic/eui'; -import { StatusAll, CaseStatusFilter } from '../../../../../case/common/api'; import { allCaseStatus, statuses } from './config'; +import { CaseStatusWithAllStatus, StatusAll } from './types'; import * as i18n from './translations'; interface Props { - type: CaseStatusFilter; + type: CaseStatusWithAllStatus; withArrow?: boolean; onClick?: () => void; } diff --git a/x-pack/plugins/security_solution/public/cases/components/status/types.ts b/x-pack/plugins/security_solution/public/cases/components/status/types.ts new file mode 100644 index 0000000000000..6f642b281419b --- /dev/null +++ b/x-pack/plugins/security_solution/public/cases/components/status/types.ts @@ -0,0 +1,43 @@ +/* + * 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 { EuiIconType } from '@elastic/eui/src/components/icon/icon'; +import { CaseStatuses } from '../../../../../case/common/api'; + +export const StatusAll = 'all' as const; +type StatusAllType = typeof StatusAll; + +export type CaseStatusWithAllStatus = CaseStatuses | StatusAllType; + +export type AllCaseStatus = Record; + +export type Statuses = Record< + CaseStatuses, + { + color: string; + label: string; + icon: EuiIconType; + actions: { + bulk: { + title: string; + }; + single: { + title: string; + description?: string; + }; + }; + actionBar: { + title: string; + }; + button: { + label: string; + }; + stats: { + title: string; + }; + } +>; diff --git a/x-pack/plugins/security_solution/public/cases/containers/types.ts b/x-pack/plugins/security_solution/public/cases/containers/types.ts index 9c4d71d1384c1..09c911d93ea47 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/types.ts +++ b/x-pack/plugins/security_solution/public/cases/containers/types.ts @@ -16,8 +16,8 @@ import { CasePatchRequest, CaseType, AssociationType, - CaseStatusFilter, } from '../../../../case/common/api'; +import { CaseStatusWithAllStatus } from '../components/status'; export { CaseConnector, ActionConnector, CaseStatuses } from '../../../../case/common/api'; @@ -95,7 +95,7 @@ export interface QueryParams { export interface FilterOptions { search: string; - status: CaseStatusFilter; + status: CaseStatusWithAllStatus; tags: string[]; reporters: User[]; onlyCollectionType?: boolean; From b25089887e2b5f61a72667ad592bde3159827deb Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Tue, 2 Mar 2021 12:35:53 +0200 Subject: [PATCH 25/26] Hide actions on all status --- .../public/cases/components/all_cases/actions.tsx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx index e6d1f359242eb..3356375b37a76 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx @@ -27,8 +27,8 @@ export const getActions = ({ deleteCaseOnClick, }: GetActions): Array> => { const openCaseAction = { - available: (item: Case | SubCase) => caseStatus !== CaseStatuses.open, - enabled: (item: Case | SubCase) => isIndividual(item) && item.status !== CaseStatuses.open, + available: (item: Case | SubCase) => item.status !== CaseStatuses.open, + enabled: (item: Case | SubCase) => isIndividual(item), description: statuses[CaseStatuses.open].actions.single.title, icon: statuses[CaseStatuses.open].icon, name: statuses[CaseStatuses.open].actions.single.title, @@ -44,9 +44,8 @@ export const getActions = ({ }; const makeInProgressAction = { - available: (item: Case) => caseStatus !== CaseStatuses['in-progress'], - enabled: (item: Case | SubCase) => - isIndividual(item) && item.status !== CaseStatuses['in-progress'], + available: (item: Case) => item.status !== CaseStatuses['in-progress'], + enabled: (item: Case | SubCase) => isIndividual(item), description: statuses[CaseStatuses['in-progress']].actions.single.title, icon: statuses[CaseStatuses['in-progress']].icon, name: statuses[CaseStatuses['in-progress']].actions.single.title, @@ -62,8 +61,8 @@ export const getActions = ({ }; const closeCaseAction = { - available: (item: Case | SubCase) => caseStatus !== CaseStatuses.closed, - enabled: (item: Case | SubCase) => isIndividual(item) && item.status !== CaseStatuses.closed, + available: (item: Case | SubCase) => item.status !== CaseStatuses.closed, + enabled: (item: Case | SubCase) => isIndividual(item), description: statuses[CaseStatuses.closed].actions.single.title, icon: statuses[CaseStatuses.closed].icon, name: statuses[CaseStatuses.closed].actions.single.title, From efdaf6ec7177a22b5794127a5631c6c2f8242235 Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Tue, 2 Mar 2021 20:10:55 +0800 Subject: [PATCH 26/26] fix unit test --- .../public/cases/components/all_cases/actions.tsx | 6 +++--- .../security_solution/public/cases/containers/api.test.tsx | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx index 3356375b37a76..046da5e833bf8 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/actions.tsx @@ -78,6 +78,9 @@ export const getActions = ({ }; return [ + openCaseAction, + makeInProgressAction, + closeCaseAction, { description: i18n.DELETE_CASE, icon: 'trash', @@ -86,8 +89,5 @@ export const getActions = ({ type: 'icon', 'data-test-subj': 'action-delete', }, - openCaseAction, - makeInProgressAction, - closeCaseAction, ]; }; diff --git a/x-pack/plugins/security_solution/public/cases/containers/api.test.tsx b/x-pack/plugins/security_solution/public/cases/containers/api.test.tsx index 8341b948d8ff6..01f1ba173d5be 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/api.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/containers/api.test.tsx @@ -47,7 +47,6 @@ import { } from './mock'; import { DEFAULT_FILTER_OPTIONS, DEFAULT_QUERY_PARAMS } from './use_get_cases'; -import { StatusAll } from '../components/status'; const abortCtrl = new AbortController(); const mockKibanaServices = KibanaServices.get as jest.Mock; @@ -138,7 +137,6 @@ describe('Case Configuration API', () => { ...DEFAULT_QUERY_PARAMS, reporters: [], tags: [], - status: StatusAll, }, signal: abortCtrl.signal, });