diff --git a/x-pack/plugins/cases/common/constants.ts b/x-pack/plugins/cases/common/constants.ts index 6bd2204e39be7..72c21aa12dcf2 100644 --- a/x-pack/plugins/cases/common/constants.ts +++ b/x-pack/plugins/cases/common/constants.ts @@ -79,6 +79,8 @@ export const MAX_GENERATED_ALERTS_PER_SUB_CASE = 50; /** * This must be the same value that the security solution plugin uses to define the case kind when it registers the * feature for the 7.13 migration only. + * + * This variable is being also used by test files and mocks. */ export const SECURITY_SOLUTION_OWNER = 'securitySolution'; diff --git a/x-pack/plugins/cases/common/ui/types.ts b/x-pack/plugins/cases/common/ui/types.ts index 7004fd2ab2ea2..284f5e706292c 100644 --- a/x-pack/plugins/cases/common/ui/types.ts +++ b/x-pack/plugins/cases/common/ui/types.ts @@ -130,7 +130,7 @@ export interface ElasticUser { export interface FetchCasesProps extends ApiProps { queryParams?: QueryParams; - filterOptions?: FilterOptions; + filterOptions?: FilterOptions & { owner: string[] }; } export interface ApiProps { diff --git a/x-pack/plugins/cases/public/common/mock/test_providers.tsx b/x-pack/plugins/cases/public/common/mock/test_providers.tsx index 94ee5dd4f2743..9a08918a483a5 100644 --- a/x-pack/plugins/cases/public/common/mock/test_providers.tsx +++ b/x-pack/plugins/cases/public/common/mock/test_providers.tsx @@ -10,6 +10,8 @@ import { I18nProvider } from '@kbn/i18n/react'; import React from 'react'; import { BehaviorSubject } from 'rxjs'; import { ThemeProvider } from 'styled-components'; +import { SECURITY_SOLUTION_OWNER } from '../../../common'; +import { OwnerProvider } from '../../components/owner_context'; import { createKibanaContextProviderMock, createStartServicesMock, @@ -29,7 +31,9 @@ const MockKibanaContextProvider = createKibanaContextProviderMock(); const TestProvidersComponent: React.FC = ({ children }) => ( - ({ eui: euiDarkVars, darkMode: true })}>{children} + ({ eui: euiDarkVars, darkMode: true })}> + {children} + ); diff --git a/x-pack/plugins/cases/public/components/add_comment/index.test.tsx b/x-pack/plugins/cases/public/components/add_comment/index.test.tsx index 95f642c7e625a..23a0fca48592f 100644 --- a/x-pack/plugins/cases/public/components/add_comment/index.test.tsx +++ b/x-pack/plugins/cases/public/components/add_comment/index.test.tsx @@ -13,7 +13,7 @@ import { noop } from 'lodash/fp'; import { TestProviders } from '../../common/mock'; import { Router, routeData, mockHistory, mockLocation } from '../__mock__/router'; -import { CommentRequest, CommentType } from '../../../common'; +import { CommentRequest, CommentType, SECURITY_SOLUTION_OWNER } from '../../../common'; import { usePostComment } from '../../containers/use_post_comment'; import { AddComment, AddCommentRefObject } from '.'; import { CasesTimelineIntegrationProvider } from '../timeline_context'; @@ -44,7 +44,7 @@ const defaultPostComment = { const sampleData: CommentRequest = { comment: 'what a cool comment', type: CommentType.user, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }; describe('AddComment ', () => { diff --git a/x-pack/plugins/cases/public/components/add_comment/index.tsx b/x-pack/plugins/cases/public/components/add_comment/index.tsx index 2995fcb4fc35f..04104f0b9471d 100644 --- a/x-pack/plugins/cases/public/components/add_comment/index.tsx +++ b/x-pack/plugins/cases/public/components/add_comment/index.tsx @@ -18,6 +18,7 @@ import { Form, useForm, UseField, useFormData } from '../../common/shared_import import * as i18n from './translations'; import { schema, AddCommentFormSchema } from './schema'; import { InsertTimeline } from '../insert_timeline'; +import { useOwnerContext } from '../owner_context/use_owner_context'; const MySpinner = styled(EuiLoadingSpinner)` position: absolute; top: 50%; @@ -47,6 +48,7 @@ export const AddComment = React.memo( { caseId, disabled, onCommentPosted, onCommentSaving, showLoading = true, subCaseId }, ref ) => { + const owner = useOwnerContext(); const { isLoading, postComment } = usePostComment(); const { form } = useForm({ @@ -78,14 +80,13 @@ export const AddComment = React.memo( } postComment({ caseId, - // TODO: get plugin name - data: { ...data, type: CommentType.user, owner: 'securitySolution' }, + data: { ...data, type: CommentType.user, owner: owner[0] }, updateCase: onCommentPosted, subCaseId, }); reset(); } - }, [caseId, onCommentPosted, onCommentSaving, postComment, reset, submit, subCaseId]); + }, [submit, onCommentSaving, postComment, caseId, owner, onCommentPosted, subCaseId, reset]); return ( diff --git a/x-pack/plugins/cases/public/components/all_cases/all_cases_generic.tsx b/x-pack/plugins/cases/public/components/all_cases/all_cases_generic.tsx index 83f38aab21aa4..429532c86e4da 100644 --- a/x-pack/plugins/cases/public/components/all_cases/all_cases_generic.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/all_cases_generic.tsx @@ -38,6 +38,7 @@ import { CasesTableFilters } from './table_filters'; import { EuiBasicTableOnChange } from './types'; import { CasesTable } from './table'; + const ProgressLoader = styled(EuiProgress)` ${({ $isShow }: { $isShow: boolean }) => $isShow @@ -79,6 +80,7 @@ export const AllCasesGeneric = React.memo( userCanCrud, }) => { const { actionLicense } = useGetActionLicense(); + const { data, dispatchUpdateCaseProperty, diff --git a/x-pack/plugins/cases/public/components/all_cases/index.test.tsx b/x-pack/plugins/cases/public/components/all_cases/index.test.tsx index 7233d6bef6e4a..5ed3215241de5 100644 --- a/x-pack/plugins/cases/public/components/all_cases/index.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/index.test.tsx @@ -13,7 +13,7 @@ import '../../common/mock/match_media'; import { TestProviders } from '../../common/mock'; import { casesStatus, useGetCasesMockState, collectionCase } from '../../containers/mock'; -import { CaseStatuses, CaseType, StatusAll } from '../../../common'; +import { CaseStatuses, CaseType, SECURITY_SOLUTION_OWNER, StatusAll } from '../../../common'; import { getEmptyTagValue } from '../empty_value'; import { useDeleteCases } from '../../containers/use_delete_cases'; import { useGetCases } from '../../containers/use_get_cases'; @@ -53,6 +53,7 @@ describe('AllCasesGeneric', () => { onClick: jest.fn(), }, userCanCrud: true, + owner: [SECURITY_SOLUTION_OWNER], }; const dispatchResetIsDeleted = jest.fn(); @@ -815,7 +816,7 @@ describe('AllCasesGeneric', () => { }, }, id: '1', - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, status: 'open', subCaseIds: [], tags: ['coke', 'pepsi'], diff --git a/x-pack/plugins/cases/public/components/all_cases/index.tsx b/x-pack/plugins/cases/public/components/all_cases/index.tsx index 2c506cd2da411..3d6c039aa001c 100644 --- a/x-pack/plugins/cases/public/components/all_cases/index.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/index.tsx @@ -6,9 +6,11 @@ */ import React from 'react'; +import { Owner } from '../../types'; import { CaseDetailsHrefSchema, CasesNavigation } from '../links'; +import { OwnerProvider } from '../owner_context'; import { AllCasesGeneric } from './all_cases_generic'; -export interface AllCasesProps { +export interface AllCasesProps extends Owner { caseDetailsNavigation: CasesNavigation; // if not passed, case name is not displayed as a link (Formerly dependant on isSelector) configureCasesNavigation: CasesNavigation; // if not passed, header with nav is not displayed (Formerly dependant on isSelector) createCaseNavigation: CasesNavigation; @@ -16,7 +18,11 @@ export interface AllCasesProps { } export const AllCases: React.FC = (props) => { - return ; + return ( + + + + ); }; // eslint-disable-next-line import/no-default-export diff --git a/x-pack/plugins/cases/public/components/all_cases/selector_modal/index.test.tsx b/x-pack/plugins/cases/public/components/all_cases/selector_modal/index.test.tsx index b2444c5ccb0dd..47db45699f8fb 100644 --- a/x-pack/plugins/cases/public/components/all_cases/selector_modal/index.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/selector_modal/index.test.tsx @@ -11,6 +11,7 @@ import { mount } from 'enzyme'; import { AllCasesSelectorModal } from '.'; import { TestProviders } from '../../../common/mock'; import { AllCasesGeneric } from '../all_cases_generic'; +import { SECURITY_SOLUTION_OWNER } from '../../../../common'; jest.mock('../../../methods'); jest.mock('../all_cases_generic'); @@ -20,6 +21,7 @@ const defaultProps = { createCaseNavigation, onRowClick, userCanCrud: true, + owner: [SECURITY_SOLUTION_OWNER], }; const updateCase = jest.fn(); @@ -59,7 +61,7 @@ describe('AllCasesSelectorModal', () => { }, index: 'index-id', alertId: 'alert-id', - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }, disabledStatuses: [], updateCase, diff --git a/x-pack/plugins/cases/public/components/all_cases/selector_modal/index.tsx b/x-pack/plugins/cases/public/components/all_cases/selector_modal/index.tsx index 0a83ef13e8ee6..e7bce984b3cd1 100644 --- a/x-pack/plugins/cases/public/components/all_cases/selector_modal/index.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/selector_modal/index.tsx @@ -12,8 +12,10 @@ import { Case, CaseStatuses, CommentRequestAlertType, SubCase } from '../../../. import { CasesNavigation } from '../../links'; import * as i18n from '../../../common/translations'; import { AllCasesGeneric } from '../all_cases_generic'; +import { Owner } from '../../../types'; +import { OwnerProvider } from '../../owner_context'; -export interface AllCasesSelectorModalProps { +export interface AllCasesSelectorModalProps extends Owner { alertData?: Omit; createCaseNavigation: CasesNavigation; disabledStatuses?: CaseStatuses[]; @@ -29,7 +31,7 @@ const Modal = styled(EuiModal)` `} `; -export const AllCasesSelectorModal: React.FC = ({ +const AllCasesSelectorModalComponent: React.FC = ({ alertData, createCaseNavigation, disabledStatuses, @@ -65,5 +67,13 @@ export const AllCasesSelectorModal: React.FC = ({ ) : null; }; + +export const AllCasesSelectorModal: React.FC = React.memo((props) => { + return ( + + + + ); +}); // eslint-disable-next-line import/no-default-export export { AllCasesSelectorModal as default }; diff --git a/x-pack/plugins/cases/public/components/case_view/helpers.test.tsx b/x-pack/plugins/cases/public/components/case_view/helpers.test.tsx index 47ab272bdc3f8..bf5a9fe5d0a22 100644 --- a/x-pack/plugins/cases/public/components/case_view/helpers.test.tsx +++ b/x-pack/plugins/cases/public/components/case_view/helpers.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { AssociationType, CommentType } from '../../../common'; +import { AssociationType, CommentType, SECURITY_SOLUTION_OWNER } from '../../../common'; import { Comment } from '../../containers/types'; import { getManualAlertIdsWithNoRuleId } from './helpers'; @@ -28,7 +28,7 @@ const comments: Comment[] = [ updatedAt: null, updatedBy: null, version: 'WzQ3LDFc', - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }, { associationType: AssociationType.case, @@ -47,7 +47,7 @@ const comments: Comment[] = [ updatedAt: null, updatedBy: null, version: 'WzQ3LDFc', - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }, ]; diff --git a/x-pack/plugins/cases/public/components/case_view/index.tsx b/x-pack/plugins/cases/public/components/case_view/index.tsx index 557f736c513b9..86b13ae5a863c 100644 --- a/x-pack/plugins/cases/public/components/case_view/index.tsx +++ b/x-pack/plugins/cases/public/components/case_view/index.tsx @@ -43,6 +43,7 @@ import { Ecs } from '../../../common'; import { CasesTimelineIntegration, CasesTimelineIntegrationProvider } from '../timeline_context'; import { useTimelineContext } from '../timeline_context/use_timeline_context'; import { CasesNavigation } from '../links'; +import { OwnerProvider } from '../owner_context'; const gutterTimeline = '70px'; // seems to be a timeline reference from the original file export interface CaseViewComponentProps { @@ -450,6 +451,7 @@ export const CaseComponent = React.memo( tags={caseData.tags} onSubmit={onSubmitTags} isLoading={isLoading && updateKey === 'tags'} + owner={[caseData.owner]} /> - + + + ) ); diff --git a/x-pack/plugins/cases/public/components/configure_cases/button.tsx b/x-pack/plugins/cases/public/components/configure_cases/button.tsx index 1830380be3765..8b3c78ee3aede 100644 --- a/x-pack/plugins/cases/public/components/configure_cases/button.tsx +++ b/x-pack/plugins/cases/public/components/configure_cases/button.tsx @@ -10,7 +10,6 @@ import React, { memo, useMemo } from 'react'; import { CasesNavigation, LinkButton } from '../links'; // TODO: Potentially move into links component? - export interface ConfigureCaseButtonProps { configureCasesNavigation: CasesNavigation; isDisabled: boolean; diff --git a/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx b/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx index 898d6cde19a77..0d9ede9bb7de8 100644 --- a/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx +++ b/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx @@ -32,7 +32,7 @@ import { useConnectorsResponse, useActionTypesResponse, } from './__mock__'; -import { ConnectorTypes } from '../../../common'; +import { ConnectorTypes, SECURITY_SOLUTION_OWNER } from '../../../common'; jest.mock('../../common/lib/kibana'); jest.mock('../../containers/configure/use_connectors'); @@ -102,7 +102,9 @@ describe('ConfigureCases', () => { useConnectorsMock.mockImplementation(() => ({ ...useConnectorsResponse, connectors: [] })); useGetUrlSearchMock.mockImplementation(() => searchURL); - wrapper = mount(, { wrappingComponent: TestProviders }); + wrapper = mount(, { + wrappingComponent: TestProviders, + }); }); test('it renders the Connectors', () => { @@ -155,7 +157,9 @@ describe('ConfigureCases', () => { })); useConnectorsMock.mockImplementation(() => ({ ...useConnectorsResponse, connectors: [] })); useGetUrlSearchMock.mockImplementation(() => searchURL); - wrapper = mount(, { wrappingComponent: TestProviders }); + wrapper = mount(, { + wrappingComponent: TestProviders, + }); }); test('it shows the warning callout when configuration is invalid', () => { @@ -200,7 +204,9 @@ describe('ConfigureCases', () => { useConnectorsMock.mockImplementation(() => useConnectorsResponse); useGetUrlSearchMock.mockImplementation(() => searchURL); - wrapper = mount(, { wrappingComponent: TestProviders }); + wrapper = mount(, { + wrappingComponent: TestProviders, + }); }); test('it renders with correct props', () => { @@ -220,9 +226,12 @@ describe('ConfigureCases', () => { }); test('it disables correctly when the user cannot crud', () => { - const newWrapper = mount(, { - wrappingComponent: TestProviders, - }); + const newWrapper = mount( + , + { + wrappingComponent: TestProviders, + } + ); expect(newWrapper.find('button[data-test-subj="dropdown-connectors"]').prop('disabled')).toBe( true @@ -282,7 +291,9 @@ describe('ConfigureCases', () => { })); useGetUrlSearchMock.mockImplementation(() => searchURL); - wrapper = mount(, { wrappingComponent: TestProviders }); + wrapper = mount(, { + wrappingComponent: TestProviders, + }); }); test('it disables correctly Connector when loading connectors', () => { @@ -315,7 +326,9 @@ describe('ConfigureCases', () => { useActionTypesMock.mockImplementation(() => ({ ...useActionTypesResponse, loading: true })); - wrapper = mount(, { wrappingComponent: TestProviders }); + wrapper = mount(, { + wrappingComponent: TestProviders, + }); expect(wrapper.find(Connectors).prop('isLoading')).toBe(true); }); }); @@ -337,7 +350,9 @@ describe('ConfigureCases', () => { useConnectorsMock.mockImplementation(() => useConnectorsResponse); useGetUrlSearchMock.mockImplementation(() => searchURL); - wrapper = mount(, { wrappingComponent: TestProviders }); + wrapper = mount(, { + wrappingComponent: TestProviders, + }); }); test('it disables correctly Connector when saving configuration', () => { @@ -378,7 +393,9 @@ describe('ConfigureCases', () => { ...useConnectorsResponse, })); useGetUrlSearchMock.mockImplementation(() => searchURL); - wrapper = mount(, { wrappingComponent: TestProviders }); + wrapper = mount(, { + wrappingComponent: TestProviders, + }); }); test('it hides the update connector button when loading the configuration', () => { @@ -420,7 +437,9 @@ describe('ConfigureCases', () => { useConnectorsMock.mockImplementation(() => useConnectorsResponse); useGetUrlSearchMock.mockImplementation(() => searchURL); - wrapper = mount(, { wrappingComponent: TestProviders }); + wrapper = mount(, { + wrappingComponent: TestProviders, + }); }); test('it submits the configuration correctly when changing connector', () => { @@ -462,7 +481,9 @@ describe('ConfigureCases', () => { }, })); - wrapper = mount(, { wrappingComponent: TestProviders }); + wrapper = mount(, { + wrappingComponent: TestProviders, + }); wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); wrapper.update(); @@ -508,7 +529,9 @@ describe('closure options', () => { useConnectorsMock.mockImplementation(() => useConnectorsResponse); useGetUrlSearchMock.mockImplementation(() => searchURL); - wrapper = mount(, { wrappingComponent: TestProviders }); + wrapper = mount(, { + wrappingComponent: TestProviders, + }); }); test('it submits the configuration correctly when changing closure type', () => { @@ -555,7 +578,9 @@ describe('user interactions', () => { }); test('it show the add flyout when pressing the add connector button', () => { - const wrapper = mount(, { wrappingComponent: TestProviders }); + const wrapper = mount(, { + wrappingComponent: TestProviders, + }); wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); wrapper.update(); wrapper.find('button[data-test-subj="dropdown-connector-add-connector"]').simulate('click'); @@ -576,7 +601,9 @@ describe('user interactions', () => { }); test('it show the edit flyout when pressing the update connector button', () => { - const wrapper = mount(, { wrappingComponent: TestProviders }); + const wrapper = mount(, { + wrappingComponent: TestProviders, + }); wrapper .find('button[data-test-subj="case-configure-update-selected-connector-button"]') .simulate('click'); diff --git a/x-pack/plugins/cases/public/components/configure_cases/index.tsx b/x-pack/plugins/cases/public/components/configure_cases/index.tsx index fdba148e5c61e..3ee4bc77cd237 100644 --- a/x-pack/plugins/cases/public/components/configure_cases/index.tsx +++ b/x-pack/plugins/cases/public/components/configure_cases/index.tsx @@ -31,6 +31,8 @@ import { normalizeCaseConnector, } from './utils'; import * as i18n from './translations'; +import { Owner } from '../../types'; +import { OwnerProvider } from '../owner_context'; const FormWrapper = styled.div` ${({ theme }) => css` @@ -50,11 +52,11 @@ const FormWrapper = styled.div` `} `; -export interface ConfigureCasesProps { +export interface ConfigureCasesProps extends Owner { userCanCrud: boolean; } -const ConfigureCasesComponent: React.FC = ({ userCanCrud }) => { +const ConfigureCasesComponent: React.FC> = ({ userCanCrud }) => { const { triggersActionsUi } = useKibana().services; const [connectorIsValid, setConnectorIsValid] = useState(true); @@ -223,6 +225,13 @@ const ConfigureCasesComponent: React.FC = ({ userCanCrud }) ); }; -export const ConfigureCases = React.memo(ConfigureCasesComponent); +export const ConfigureCases: React.FC = React.memo((props) => { + return ( + + + + ); +}); + // eslint-disable-next-line import/no-default-export export default ConfigureCases; diff --git a/x-pack/plugins/cases/public/components/connectors/case/alert_fields.tsx b/x-pack/plugins/cases/public/components/connectors/case/alert_fields.tsx index 0c44bcab70679..8fb34e0cdcbf5 100644 --- a/x-pack/plugins/cases/public/components/connectors/case/alert_fields.tsx +++ b/x-pack/plugins/cases/public/components/connectors/case/alert_fields.tsx @@ -12,12 +12,13 @@ import styled from 'styled-components'; import { EuiCallOut, EuiSpacer } from '@elastic/eui'; import { ActionParamsProps } from '../../../../../triggers_actions_ui/public/types'; -import { CommentType } from '../../../../common'; +import { CommentType, SECURITY_SOLUTION_OWNER } from '../../../../common'; import { CaseActionParams } from './types'; import { ExistingCase } from './existing_case'; import * as i18n from './translations'; +import { OwnerProvider } from '../../owner_context'; const Container = styled.div` ${({ theme }) => ` @@ -89,9 +90,15 @@ const CaseParamsFields: React.FunctionComponent - + + +

{i18n.CASE_CONNECTOR_CALL_OUT_MSG}

diff --git a/x-pack/plugins/cases/public/components/connectors/case/existing_case.tsx b/x-pack/plugins/cases/public/components/connectors/case/existing_case.tsx index 22798843dd856..aafbfb8b43b78 100644 --- a/x-pack/plugins/cases/public/components/connectors/case/existing_case.tsx +++ b/x-pack/plugins/cases/public/components/connectors/case/existing_case.tsx @@ -21,9 +21,12 @@ interface ExistingCaseProps { } const ExistingCaseComponent: React.FC = ({ onCaseChanged, selectedCase }) => { - const { data: cases, loading: isLoadingCases, refetchCases } = useGetCases(DEFAULT_QUERY_PARAMS, { - ...DEFAULT_FILTER_OPTIONS, - onlyCollectionType: true, + const { data: cases, loading: isLoadingCases, refetchCases } = useGetCases({ + initialQueryParams: DEFAULT_QUERY_PARAMS, + initialFilterOptions: { + ...DEFAULT_FILTER_OPTIONS, + onlyCollectionType: true, + }, }); const onCaseCreated = useCallback( diff --git a/x-pack/plugins/cases/public/components/create/flyout.test.tsx b/x-pack/plugins/cases/public/components/create/flyout.test.tsx deleted file mode 100644 index 5187029ab60c7..0000000000000 --- a/x-pack/plugins/cases/public/components/create/flyout.test.tsx +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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 React, { ReactNode } from 'react'; -import { mount } from 'enzyme'; - -import { CreateCaseFlyout } from './flyout'; -import { TestProviders } from '../../common/mock'; - -jest.mock('../create/form_context', () => { - return { - FormContext: ({ - children, - onSuccess, - }: { - children: ReactNode; - onSuccess: ({ id }: { id: string }) => Promise; - }) => { - return ( - <> - - {children} - - ); - }, - }; -}); - -jest.mock('../create/form', () => { - return { - CreateCaseForm: () => { - return <>{'form'}; - }, - }; -}); - -jest.mock('../create/submit_button', () => { - return { - SubmitCaseButton: () => { - return <>{'Submit'}; - }, - }; -}); - -const onCloseFlyout = jest.fn(); -const onSuccess = jest.fn(); -const defaultProps = { - onCloseFlyout, - onSuccess, -}; - -describe('CreateCaseFlyout', () => { - beforeEach(() => { - jest.resetAllMocks(); - }); - - it('renders', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find(`[data-test-subj='create-case-flyout']`).exists()).toBeTruthy(); - }); - - it('Closing modal calls onCloseCaseModal', () => { - const wrapper = mount( - - - - ); - - wrapper.find('.euiFlyout__closeButton').first().simulate('click'); - expect(onCloseFlyout).toBeCalled(); - }); - - it('pass the correct props to FormContext component', () => { - const wrapper = mount( - - - - ); - - const props = wrapper.find('FormContext').props(); - expect(props).toEqual( - expect.objectContaining({ - onSuccess, - }) - ); - }); - - it('onSuccess called when creating a case', () => { - const wrapper = mount( - - - - ); - - wrapper.find(`[data-test-subj='form-context-on-success']`).first().simulate('click'); - expect(onSuccess).toHaveBeenCalledWith({ id: 'case-id' }); - }); -}); diff --git a/x-pack/plugins/cases/public/components/create/flyout.tsx b/x-pack/plugins/cases/public/components/create/flyout.tsx deleted file mode 100644 index 8ed09865e9eab..0000000000000 --- a/x-pack/plugins/cases/public/components/create/flyout.tsx +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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 React, { memo } from 'react'; -import styled from 'styled-components'; -import { EuiFlyout, EuiFlyoutHeader, EuiTitle, EuiFlyoutBody } from '@elastic/eui'; - -import { FormContext } from '../create/form_context'; -import { CreateCaseForm } from '../create/form'; -import { SubmitCaseButton } from '../create/submit_button'; -import { Case } from '../../containers/types'; -import * as i18n from '../../common/translations'; - -export interface CreateCaseModalProps { - onCloseFlyout: () => void; - onSuccess: (theCase: Case) => Promise; - afterCaseCreated?: (theCase: Case) => Promise; -} - -const Container = styled.div` - ${({ theme }) => ` - margin-top: ${theme.eui.euiSize}; - text-align: right; - `} -`; - -const StyledFlyout = styled(EuiFlyout)` - ${({ theme }) => ` - z-index: ${theme.eui.euiZModal}; - `} -`; - -// Adding bottom padding because timeline's -// bottom bar gonna hide the submit button. -const FormWrapper = styled.div` - padding-bottom: 50px; -`; - -const CreateCaseFlyoutComponent: React.FC = ({ - onSuccess, - afterCaseCreated, - onCloseFlyout, -}) => { - return ( - - - -

{i18n.CREATE_TITLE}

-
-
- - - - - - - - - - -
- ); -}; - -export const CreateCaseFlyout = memo(CreateCaseFlyoutComponent); - -CreateCaseFlyout.displayName = 'CreateCaseFlyout'; diff --git a/x-pack/plugins/cases/public/components/create/form.test.tsx b/x-pack/plugins/cases/public/components/create/form.test.tsx index 9e59924bdf483..5f3b778a7cafc 100644 --- a/x-pack/plugins/cases/public/components/create/form.test.tsx +++ b/x-pack/plugins/cases/public/components/create/form.test.tsx @@ -15,6 +15,8 @@ import { useConnectors } from '../../containers/configure/use_connectors'; import { connectorsMock } from '../../containers/mock'; import { schema, FormProps } from './schema'; import { CreateCaseForm } from './form'; +import { OwnerProvider } from '../owner_context'; +import { SECURITY_SOLUTION_OWNER } from '../../../common'; jest.mock('../../containers/use_get_tags'); jest.mock('../../containers/configure/use_connectors'); @@ -41,7 +43,11 @@ describe('CreateCaseForm', () => { globalForm = form; - return
{children}
; + return ( + +
{children}
+
+ ); }; beforeEach(() => { diff --git a/x-pack/plugins/cases/public/components/create/form_context.test.tsx b/x-pack/plugins/cases/public/components/create/form_context.test.tsx index 9a8671c7fc571..cb053b2e784cd 100644 --- a/x-pack/plugins/cases/public/components/create/form_context.test.tsx +++ b/x-pack/plugins/cases/public/components/create/form_context.test.tsx @@ -10,7 +10,7 @@ import { mount, ReactWrapper } from 'enzyme'; import { act, waitFor } from '@testing-library/react'; import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; -import { ConnectorTypes } from '../../../common'; +import { ConnectorTypes, SECURITY_SOLUTION_OWNER } from '../../../common'; import { TestProviders } from '../../common/mock'; import { usePostCase } from '../../containers/use_post_case'; import { usePostComment } from '../../containers/use_post_comment'; @@ -77,6 +77,7 @@ const defaultPostCase = { const defaultCreateCaseForm = { isLoadingConnectors: false, connectors: [], + owner: SECURITY_SOLUTION_OWNER, }; const defaultPostPushToService = { diff --git a/x-pack/plugins/cases/public/components/create/form_context.tsx b/x-pack/plugins/cases/public/components/create/form_context.tsx index 9ee8aa0fe3288..8584892e1286c 100644 --- a/x-pack/plugins/cases/public/components/create/form_context.tsx +++ b/x-pack/plugins/cases/public/components/create/form_context.tsx @@ -21,6 +21,7 @@ import { useCaseConfigure } from '../../containers/configure/use_configure'; import { Case } from '../../containers/types'; import { CaseType, ConnectorTypes } from '../../../common'; import { UsePostComment, usePostComment } from '../../containers/use_post_comment'; +import { useOwnerContext } from '../owner_context/use_owner_context'; const initialCaseValue: FormProps = { description: '', @@ -47,6 +48,7 @@ export const FormContext: React.FC = ({ onSuccess, }) => { const { connectors, loading: isLoadingConnectors } = useConnectors(); + const owner = useOwnerContext(); const { connector: configurationConnector } = useCaseConfigure(); const { postCase } = usePostCase(); const { postComment } = usePostComment(); @@ -86,8 +88,7 @@ export const FormContext: React.FC = ({ type: caseType, connector: connectorToUpdate, settings: { syncAlerts }, - // TODO: need to replace this with the value that the plugin registers in the feature registration - owner: 'securitySolution', + owner: owner[0], }); if (afterCaseCreated && updatedCase) { @@ -107,13 +108,14 @@ export const FormContext: React.FC = ({ } }, [ - caseType, connectors, postCase, - postComment, + caseType, + owner, + afterCaseCreated, onSuccess, + postComment, pushCaseToExternalService, - afterCaseCreated, ] ); diff --git a/x-pack/plugins/cases/public/components/create/index.test.tsx b/x-pack/plugins/cases/public/components/create/index.test.tsx index e82af8edc6337..350b971bb05fc 100644 --- a/x-pack/plugins/cases/public/components/create/index.test.tsx +++ b/x-pack/plugins/cases/public/components/create/index.test.tsx @@ -29,6 +29,7 @@ import { useGetFieldsByIssueTypeResponse, } from './mock'; import { CreateCase } from '.'; +import { SECURITY_SOLUTION_OWNER } from '../../../common'; jest.mock('../../containers/api'); jest.mock('../../containers/use_get_tags'); @@ -91,7 +92,7 @@ describe('CreateCase case', () => { it('it renders', async () => { const wrapper = mount( - + ); @@ -102,7 +103,7 @@ describe('CreateCase case', () => { it('should call cancel on cancel click', async () => { const wrapper = mount( - + ); @@ -113,7 +114,7 @@ describe('CreateCase case', () => { it('should redirect to new case when posting the case', async () => { const wrapper = mount( - + ); diff --git a/x-pack/plugins/cases/public/components/create/index.tsx b/x-pack/plugins/cases/public/components/create/index.tsx index a1de4d9730b9f..3362aa6af2078 100644 --- a/x-pack/plugins/cases/public/components/create/index.tsx +++ b/x-pack/plugins/cases/public/components/create/index.tsx @@ -20,6 +20,8 @@ import { CasesTimelineIntegration, CasesTimelineIntegrationProvider } from '../t import { fieldName as descriptionFieldName } from './description'; import { InsertTimeline } from '../insert_timeline'; import { UsePostComment } from '../../containers/use_post_comment'; +import { Owner } from '../../types'; +import { OwnerProvider } from '../owner_context'; export const CommonUseField = getUseField({ component: Field }); @@ -29,7 +31,7 @@ const Container = styled.div` `} `; -export interface CreateCaseProps { +export interface CreateCaseProps extends Owner { afterCaseCreated?: (theCase: Case, postComment: UsePostComment['postComment']) => Promise; caseType?: CaseType; hideConnectorServiceNowSir?: boolean; @@ -39,7 +41,7 @@ export interface CreateCaseProps { withSteps?: boolean; } -export const CreateCase = ({ +const CreateCaseComponent = ({ afterCaseCreated, caseType, hideConnectorServiceNowSir, @@ -47,7 +49,7 @@ export const CreateCase = ({ onSuccess, timelineIntegration, withSteps, -}: CreateCaseProps) => ( +}: Omit) => ( ); +export const CreateCase: React.FC = React.memo((props) => { + return ( + + + + ); +}); // eslint-disable-next-line import/no-default-export export { CreateCase as default }; diff --git a/x-pack/plugins/cases/public/components/create/mock.ts b/x-pack/plugins/cases/public/components/create/mock.ts index 5a4c00ba8a91c..fb00f114f480c 100644 --- a/x-pack/plugins/cases/public/components/create/mock.ts +++ b/x-pack/plugins/cases/public/components/create/mock.ts @@ -5,7 +5,12 @@ * 2.0. */ -import { CasePostRequest, CaseType, ConnectorTypes } from '../../../common'; +import { + CasePostRequest, + CaseType, + ConnectorTypes, + SECURITY_SOLUTION_OWNER, +} from '../../../common'; import { choices } from '../connectors/mock'; export const sampleTags = ['coke', 'pepsi']; @@ -23,7 +28,7 @@ export const sampleData: CasePostRequest = { settings: { syncAlerts: true, }, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }; export const sampleConnectorData = { loading: false, connectors: [] }; diff --git a/x-pack/plugins/cases/public/components/create/schema.tsx b/x-pack/plugins/cases/public/components/create/schema.tsx index ccf9013e6a6fa..6e6d1a414280e 100644 --- a/x-pack/plugins/cases/public/components/create/schema.tsx +++ b/x-pack/plugins/cases/public/components/create/schema.tsx @@ -19,7 +19,6 @@ export const schemaTags = { labelAppend: OptionalFieldLabel, }; -// TODO: remove owner from here? export type FormProps = Omit & { connectorId: string; fields: ConnectorTypeFields['fields']; diff --git a/x-pack/plugins/cases/public/components/create/tags.test.tsx b/x-pack/plugins/cases/public/components/create/tags.test.tsx index 2eddb83dcac29..6efbf1b8c7107 100644 --- a/x-pack/plugins/cases/public/components/create/tags.test.tsx +++ b/x-pack/plugins/cases/public/components/create/tags.test.tsx @@ -14,6 +14,8 @@ import { useForm, Form, FormHook } from '../../common/shared_imports'; import { useGetTags } from '../../containers/use_get_tags'; import { Tags } from './tags'; import { schema, FormProps } from './schema'; +import { OwnerProvider } from '../owner_context'; +import { SECURITY_SOLUTION_OWNER } from '../../../common'; jest.mock('../../containers/use_get_tags'); const useGetTagsMock = useGetTags as jest.Mock; @@ -31,7 +33,11 @@ describe('Tags', () => { globalForm = form; - return
{children}
; + return ( + +
{children}
+
+ ); }; beforeEach(() => { diff --git a/x-pack/plugins/cases/public/components/owner_context/index.tsx b/x-pack/plugins/cases/public/components/owner_context/index.tsx new file mode 100644 index 0000000000000..5df7eeadd70d5 --- /dev/null +++ b/x-pack/plugins/cases/public/components/owner_context/index.tsx @@ -0,0 +1,18 @@ +/* + * 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 React, { useState } from 'react'; + +export const OwnerContext = React.createContext([]); + +export const OwnerProvider: React.FC<{ + owner: string[]; +}> = ({ children, owner }) => { + const [currentOwner] = useState(owner); + + return {children}; +}; diff --git a/x-pack/plugins/cases/public/components/owner_context/use_owner_context.ts b/x-pack/plugins/cases/public/components/owner_context/use_owner_context.ts new file mode 100644 index 0000000000000..a443df1809315 --- /dev/null +++ b/x-pack/plugins/cases/public/components/owner_context/use_owner_context.ts @@ -0,0 +1,21 @@ +/* + * 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 { useContext } from 'react'; +import { OwnerContext } from '.'; + +export const useOwnerContext = () => { + const ownerContext = useContext(OwnerContext); + + if (ownerContext.length === 0) { + throw new Error( + 'useOwnerContext must be used within an OwnerProvider and not be an empty array' + ); + } + + return ownerContext; +}; diff --git a/x-pack/plugins/cases/public/components/recent_cases/index.test.tsx b/x-pack/plugins/cases/public/components/recent_cases/index.test.tsx index 933ea51bffac4..5893d5f8c5af4 100644 --- a/x-pack/plugins/cases/public/components/recent_cases/index.test.tsx +++ b/x-pack/plugins/cases/public/components/recent_cases/index.test.tsx @@ -12,6 +12,8 @@ import RecentCases from '.'; import { TestProviders } from '../../common/mock'; import { useGetCases } from '../../containers/use_get_cases'; import { useGetCasesMockState } from '../../containers/mock'; +import { SECURITY_SOLUTION_OWNER } from '../../../common'; + jest.mock('../../containers/use_get_cases'); configure({ testIdAttribute: 'data-test-subj' }); const defaultProps = { @@ -28,6 +30,7 @@ const defaultProps = { onClick: jest.fn(), }, maxCasesToShow: 10, + owner: [SECURITY_SOLUTION_OWNER], }; const setFilters = jest.fn(); const mockData = { @@ -40,6 +43,7 @@ describe('RecentCases', () => { jest.clearAllMocks(); useGetCasesMock.mockImplementation(() => mockData); }); + it('is good at loading', () => { useGetCasesMock.mockImplementation(() => ({ ...mockData, @@ -52,6 +56,7 @@ describe('RecentCases', () => { ); expect(getAllByTestId('loadingPlaceholders')).toHaveLength(3); }); + it('is good at rendering cases', () => { const { getAllByTestId } = render( @@ -60,14 +65,18 @@ describe('RecentCases', () => { ); expect(getAllByTestId('case-details-link')).toHaveLength(5); }); + it('is good at rendering max cases', () => { render( ); - expect(useGetCasesMock).toBeCalledWith({ perPage: 2 }); + expect(useGetCasesMock).toBeCalledWith({ + initialQueryParams: { perPage: 2 }, + }); }); + it('updates filters', () => { const { getByTestId } = render( diff --git a/x-pack/plugins/cases/public/components/recent_cases/index.tsx b/x-pack/plugins/cases/public/components/recent_cases/index.tsx index 05aff25d0dbd8..bb34f651d52df 100644 --- a/x-pack/plugins/cases/public/components/recent_cases/index.tsx +++ b/x-pack/plugins/cases/public/components/recent_cases/index.tsx @@ -14,20 +14,22 @@ import { RecentCasesFilters } from './filters'; import { RecentCasesComp } from './recent_cases'; import { FilterMode as RecentCasesFilterMode } from './types'; import { useCurrentUser } from '../../common/lib/kibana'; +import { Owner } from '../../types'; +import { OwnerProvider } from '../owner_context'; -export interface RecentCasesProps { +export interface RecentCasesProps extends Owner { allCasesNavigation: CasesNavigation; caseDetailsNavigation: CasesNavigation; createCaseNavigation: CasesNavigation; maxCasesToShow: number; } -const RecentCases = ({ +const RecentCasesComponent = ({ allCasesNavigation, caseDetailsNavigation, createCaseNavigation, maxCasesToShow, -}: RecentCasesProps) => { +}: Omit) => { const currentUser = useCurrentUser(); const [recentCasesFilterBy, setRecentCasesFilterBy] = useState( 'recentlyCreated' @@ -87,5 +89,13 @@ const RecentCases = ({ ); }; +export const RecentCases: React.FC = React.memo((props) => { + return ( + + + + ); +}); + // eslint-disable-next-line import/no-default-export export { RecentCases as default }; diff --git a/x-pack/plugins/cases/public/components/recent_cases/recent_cases.tsx b/x-pack/plugins/cases/public/components/recent_cases/recent_cases.tsx index 12935e75c064f..5b4313530e490 100644 --- a/x-pack/plugins/cases/public/components/recent_cases/recent_cases.tsx +++ b/x-pack/plugins/cases/public/components/recent_cases/recent_cases.tsx @@ -32,6 +32,7 @@ export interface RecentCasesProps { createCaseNavigation: CasesNavigation; maxCasesToShow: number; } + const usePrevious = (value: Partial) => { const ref = useRef(); useEffect(() => { @@ -46,7 +47,9 @@ export const RecentCasesComp = ({ maxCasesToShow, }: RecentCasesProps) => { const previousFilterOptions = usePrevious(filterOptions); - const { data, loading, setFilters } = useGetCases({ perPage: maxCasesToShow }); + const { data, loading, setFilters } = useGetCases({ + initialQueryParams: { perPage: maxCasesToShow }, + }); useEffect(() => { if (previousFilterOptions !== undefined && !isEqual(previousFilterOptions, filterOptions)) { diff --git a/x-pack/plugins/cases/public/components/tag_list/index.test.tsx b/x-pack/plugins/cases/public/components/tag_list/index.test.tsx index 296c4ba0e893b..b3fbcd30d4e97 100644 --- a/x-pack/plugins/cases/public/components/tag_list/index.test.tsx +++ b/x-pack/plugins/cases/public/components/tag_list/index.test.tsx @@ -14,6 +14,7 @@ import { TestProviders } from '../../common/mock'; import { waitFor } from '@testing-library/react'; import { useForm } from '../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form'; import { useGetTags } from '../../containers/use_get_tags'; +import { SECURITY_SOLUTION_OWNER } from '../../../common'; jest.mock('../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form'); jest.mock('../../containers/use_get_tags'); @@ -37,6 +38,7 @@ const defaultProps = { isLoading: false, onSubmit, tags: [], + owner: [SECURITY_SOLUTION_OWNER], }; describe('TagList ', () => { diff --git a/x-pack/plugins/cases/public/components/tag_list/index.tsx b/x-pack/plugins/cases/public/components/tag_list/index.tsx index 137d58932b6ef..f260593369679 100644 --- a/x-pack/plugins/cases/public/components/tag_list/index.tsx +++ b/x-pack/plugins/cases/public/components/tag_list/index.tsx @@ -32,6 +32,7 @@ interface TagListProps { isLoading: boolean; onSubmit: (a: string[]) => void; tags: string[]; + owner: string[]; } const MyFlexGroup = styled(EuiFlexGroup)` @@ -44,7 +45,7 @@ const MyFlexGroup = styled(EuiFlexGroup)` `; export const TagList = React.memo( - ({ disabled = false, isLoading, onSubmit, tags }: TagListProps) => { + ({ disabled = false, isLoading, onSubmit, tags, owner }: TagListProps) => { const initialState = { tags }; const { form } = useForm({ defaultValue: initialState, diff --git a/x-pack/plugins/cases/public/components/use_create_case_modal/create_case_modal.test.tsx b/x-pack/plugins/cases/public/components/use_create_case_modal/create_case_modal.test.tsx index 661a0eedfeae4..4c39b721cac47 100644 --- a/x-pack/plugins/cases/public/components/use_create_case_modal/create_case_modal.test.tsx +++ b/x-pack/plugins/cases/public/components/use_create_case_modal/create_case_modal.test.tsx @@ -11,6 +11,7 @@ import { mount } from 'enzyme'; import { CreateCaseModal } from './create_case_modal'; import { TestProviders } from '../../common/mock'; import { getCreateCaseLazy as getCreateCase } from '../../methods'; +import { SECURITY_SOLUTION_OWNER } from '../../../common'; jest.mock('../../methods'); const getCreateCaseMock = getCreateCase as jest.Mock; @@ -20,6 +21,7 @@ const defaultProps = { isModalOpen: true, onCloseCaseModal, onSuccess, + owner: SECURITY_SOLUTION_OWNER, }; describe('CreateCaseModal', () => { diff --git a/x-pack/plugins/cases/public/components/use_create_case_modal/create_case_modal.tsx b/x-pack/plugins/cases/public/components/use_create_case_modal/create_case_modal.tsx index e78b432b3a27c..a4278e53ea341 100644 --- a/x-pack/plugins/cases/public/components/use_create_case_modal/create_case_modal.tsx +++ b/x-pack/plugins/cases/public/components/use_create_case_modal/create_case_modal.tsx @@ -19,6 +19,7 @@ export interface CreateCaseModalProps { isModalOpen: boolean; onCloseCaseModal: () => void; onSuccess: (theCase: Case) => Promise; + owner: string; } const CreateModalComponent: React.FC = ({ @@ -27,6 +28,7 @@ const CreateModalComponent: React.FC = ({ isModalOpen, onCloseCaseModal, onSuccess, + owner, }) => { return isModalOpen ? ( @@ -40,6 +42,7 @@ const CreateModalComponent: React.FC = ({ onCancel: onCloseCaseModal, onSuccess, withSteps: false, + owner: [owner], })} diff --git a/x-pack/plugins/cases/public/components/use_create_case_modal/index.tsx b/x-pack/plugins/cases/public/components/use_create_case_modal/index.tsx index 7ad85773a7917..09f8eb65b12b7 100644 --- a/x-pack/plugins/cases/public/components/use_create_case_modal/index.tsx +++ b/x-pack/plugins/cases/public/components/use_create_case_modal/index.tsx @@ -7,6 +7,7 @@ import React, { useState, useCallback, useMemo } from 'react'; import { Case, CaseType } from '../../../common'; +import { useOwnerContext } from '../owner_context/use_owner_context'; import { CreateCaseModal } from './create_case_modal'; export interface UseCreateCaseModalProps { @@ -26,6 +27,7 @@ export const useCreateCaseModal = ({ onCaseCreated, hideConnectorServiceNowSir = false, }: UseCreateCaseModalProps) => { + const owner = useOwnerContext(); const [isModalOpen, setIsModalOpen] = useState(false); const closeModal = useCallback(() => setIsModalOpen(false), []); const openModal = useCallback(() => setIsModalOpen(true), []); @@ -46,12 +48,13 @@ export const useCreateCaseModal = ({ isModalOpen={isModalOpen} onCloseCaseModal={closeModal} onSuccess={onSuccess} + owner={owner[0]} /> ), isModalOpen, closeModal, openModal, }), - [caseType, closeModal, hideConnectorServiceNowSir, isModalOpen, onSuccess, openModal] + [caseType, closeModal, hideConnectorServiceNowSir, isModalOpen, onSuccess, openModal, owner] ); }; diff --git a/x-pack/plugins/cases/public/containers/__mocks__/api.ts b/x-pack/plugins/cases/public/containers/__mocks__/api.ts index 4dbb10da95b2d..006ad3f7afe60 100644 --- a/x-pack/plugins/cases/public/containers/__mocks__/api.ts +++ b/x-pack/plugins/cases/public/containers/__mocks__/api.ts @@ -62,6 +62,7 @@ export const getCases = async ({ reporters: [], status: CaseStatuses.open, tags: [], + owner: [], }, queryParams = { page: 1, diff --git a/x-pack/plugins/cases/public/containers/api.test.tsx b/x-pack/plugins/cases/public/containers/api.test.tsx index ff1f2084e18de..bee6110c39a30 100644 --- a/x-pack/plugins/cases/public/containers/api.test.tsx +++ b/x-pack/plugins/cases/public/containers/api.test.tsx @@ -7,7 +7,7 @@ import { KibanaServices } from '../common/lib/kibana'; -import { ConnectorTypes, CommentType, CaseStatuses } from '../../common'; +import { ConnectorTypes, CommentType, CaseStatuses, SECURITY_SOLUTION_OWNER } from '../../common'; import { CASES_URL } from '../../common'; import { @@ -127,7 +127,7 @@ describe('Case Configuration API', () => { }); test('check url, method, signal', async () => { await getCases({ - filterOptions: DEFAULT_FILTER_OPTIONS, + filterOptions: { ...DEFAULT_FILTER_OPTIONS, owner: [SECURITY_SOLUTION_OWNER] }, queryParams: DEFAULT_QUERY_PARAMS, signal: abortCtrl.signal, }); @@ -137,6 +137,7 @@ describe('Case Configuration API', () => { ...DEFAULT_QUERY_PARAMS, reporters: [], tags: [], + owner: [SECURITY_SOLUTION_OWNER], }, signal: abortCtrl.signal, }); @@ -150,6 +151,7 @@ describe('Case Configuration API', () => { tags, status: CaseStatuses.open, search: 'hello', + owner: [SECURITY_SOLUTION_OWNER], }, queryParams: DEFAULT_QUERY_PARAMS, signal: abortCtrl.signal, @@ -162,6 +164,7 @@ describe('Case Configuration API', () => { tags: ['"coke"', '"pepsi"'], search: 'hello', status: CaseStatuses.open, + owner: [SECURITY_SOLUTION_OWNER], }, signal: abortCtrl.signal, }); @@ -177,6 +180,7 @@ describe('Case Configuration API', () => { tags: weirdTags, status: CaseStatuses.open, search: 'hello', + owner: [SECURITY_SOLUTION_OWNER], }, queryParams: DEFAULT_QUERY_PARAMS, signal: abortCtrl.signal, @@ -189,6 +193,7 @@ describe('Case Configuration API', () => { tags: ['"("', '"\\"double\\""'], search: 'hello', status: CaseStatuses.open, + owner: [SECURITY_SOLUTION_OWNER], }, signal: abortCtrl.signal, }); @@ -196,7 +201,7 @@ describe('Case Configuration API', () => { test('happy path', async () => { const resp = await getCases({ - filterOptions: DEFAULT_FILTER_OPTIONS, + filterOptions: { ...DEFAULT_FILTER_OPTIONS, owner: [SECURITY_SOLUTION_OWNER] }, queryParams: DEFAULT_QUERY_PARAMS, signal: abortCtrl.signal, }); @@ -250,15 +255,18 @@ describe('Case Configuration API', () => { }); test('check url, method, signal', async () => { - await getReporters(abortCtrl.signal); + await getReporters(abortCtrl.signal, [SECURITY_SOLUTION_OWNER]); expect(fetchMock).toHaveBeenCalledWith(`${CASES_URL}/reporters`, { method: 'GET', signal: abortCtrl.signal, + query: { + owner: [SECURITY_SOLUTION_OWNER], + }, }); }); test('happy path', async () => { - const resp = await getReporters(abortCtrl.signal); + const resp = await getReporters(abortCtrl.signal, [SECURITY_SOLUTION_OWNER]); expect(resp).toEqual(respReporters); }); }); @@ -270,15 +278,18 @@ describe('Case Configuration API', () => { }); test('check url, method, signal', async () => { - await getTags(abortCtrl.signal); + await getTags(abortCtrl.signal, [SECURITY_SOLUTION_OWNER]); expect(fetchMock).toHaveBeenCalledWith(`${CASES_URL}/tags`, { method: 'GET', signal: abortCtrl.signal, + query: { + owner: [SECURITY_SOLUTION_OWNER], + }, }); }); test('happy path', async () => { - const resp = await getTags(abortCtrl.signal); + const resp = await getTags(abortCtrl.signal, [SECURITY_SOLUTION_OWNER]); expect(resp).toEqual(tags); }); }); @@ -395,7 +406,7 @@ describe('Case Configuration API', () => { settings: { syncAlerts: true, }, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }; test('check url, method, signal', async () => { @@ -420,7 +431,7 @@ describe('Case Configuration API', () => { }); const data = { comment: 'comment', - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, type: CommentType.user as const, }; diff --git a/x-pack/plugins/cases/public/containers/api.ts b/x-pack/plugins/cases/public/containers/api.ts index 75263d4d38978..0b9b236cef6e1 100644 --- a/x-pack/plugins/cases/public/containers/api.ts +++ b/x-pack/plugins/cases/public/containers/api.ts @@ -119,18 +119,20 @@ export const getCasesStatus = async (signal: AbortSignal): Promise return convertToCamelCase(decodeCasesStatusResponse(response)); }; -export const getTags = async (signal: AbortSignal): Promise => { +export const getTags = async (signal: AbortSignal, owner: string[]): Promise => { const response = await KibanaServices.get().http.fetch(CASE_TAGS_URL, { method: 'GET', signal, + query: { ...(owner.length > 0 ? { owner } : {}) }, }); return response ?? []; }; -export const getReporters = async (signal: AbortSignal): Promise => { +export const getReporters = async (signal: AbortSignal, owner: string[]): Promise => { const response = await KibanaServices.get().http.fetch(CASE_REPORTERS_URL, { method: 'GET', signal, + query: { ...(owner.length > 0 ? { owner } : {}) }, }); return response ?? []; }; @@ -171,6 +173,7 @@ export const getCases = async ({ reporters: [], status: StatusAll, tags: [], + owner: [], }, queryParams = { page: 1, @@ -186,6 +189,7 @@ export const getCases = async ({ status: filterOptions.status, ...(filterOptions.search.length > 0 ? { search: filterOptions.search } : {}), ...(filterOptions.onlyCollectionType ? { type: CaseType.collection } : {}), + ...(filterOptions.owner.length > 0 ? { owner: filterOptions.owner } : {}), ...queryParams, }; const response = await KibanaServices.get().http.fetch(`${CASES_URL}/_find`, { diff --git a/x-pack/plugins/cases/public/containers/configure/api.test.ts b/x-pack/plugins/cases/public/containers/configure/api.test.ts index 4732c030ea505..ad13526b41d38 100644 --- a/x-pack/plugins/cases/public/containers/configure/api.test.ts +++ b/x-pack/plugins/cases/public/containers/configure/api.test.ts @@ -19,7 +19,7 @@ import { caseConfigurationResposeMock, caseConfigurationCamelCaseResponseMock, } from './mock'; -import { ConnectorTypes } from '../../../common'; +import { ConnectorTypes, SECURITY_SOLUTION_OWNER } from '../../../common'; import { KibanaServices } from '../../common/lib/kibana'; const abortCtrl = new AbortController(); @@ -57,21 +57,30 @@ describe('Case Configuration API', () => { }); test('check url, method, signal', async () => { - await getCaseConfigure({ signal: abortCtrl.signal }); + await getCaseConfigure({ signal: abortCtrl.signal, owner: [SECURITY_SOLUTION_OWNER] }); expect(fetchMock).toHaveBeenCalledWith('/api/cases/configure', { method: 'GET', signal: abortCtrl.signal, + query: { + owner: [SECURITY_SOLUTION_OWNER], + }, }); }); test('happy path', async () => { - const resp = await getCaseConfigure({ signal: abortCtrl.signal }); + const resp = await getCaseConfigure({ + signal: abortCtrl.signal, + owner: [SECURITY_SOLUTION_OWNER], + }); expect(resp).toEqual(caseConfigurationCamelCaseResponseMock); }); test('return null on empty response', async () => { fetchMock.mockResolvedValue({}); - const resp = await getCaseConfigure({ signal: abortCtrl.signal }); + const resp = await getCaseConfigure({ + signal: abortCtrl.signal, + owner: [SECURITY_SOLUTION_OWNER], + }); expect(resp).toBe(null); }); }); diff --git a/x-pack/plugins/cases/public/containers/configure/api.ts b/x-pack/plugins/cases/public/containers/configure/api.ts index 2d26e39005057..a6d530caa588e 100644 --- a/x-pack/plugins/cases/public/containers/configure/api.ts +++ b/x-pack/plugins/cases/public/containers/configure/api.ts @@ -37,13 +37,16 @@ export const fetchConnectors = async ({ signal }: ApiProps): Promise => { +export const getCaseConfigure = async ({ + signal, + owner, +}: ApiProps & { owner: string[] }): Promise => { const response = await KibanaServices.get().http.fetch( CASE_CONFIGURE_URL, { method: 'GET', signal, + query: { ...(owner.length > 0 ? { owner } : {}) }, } ); diff --git a/x-pack/plugins/cases/public/containers/configure/mock.ts b/x-pack/plugins/cases/public/containers/configure/mock.ts index 3329fa02a54b9..ef287ea866dcb 100644 --- a/x-pack/plugins/cases/public/containers/configure/mock.ts +++ b/x-pack/plugins/cases/public/containers/configure/mock.ts @@ -11,6 +11,7 @@ import { CasesConfigureResponse, CasesConfigureRequest, ConnectorTypes, + SECURITY_SOLUTION_OWNER, } from '../../../common'; import { CaseConfigure, CaseConnectorMapping } from './types'; @@ -130,7 +131,7 @@ export const caseConfigurationResposeMock: CasesConfigureResponse = { mappings: [], updated_at: '2020-04-06T14:03:18.657Z', updated_by: { username: 'elastic', full_name: 'Elastic', email: 'elastic@elastic.co' }, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, version: 'WzHJ12', }; @@ -141,7 +142,7 @@ export const caseConfigurationMock: CasesConfigureRequest = { type: ConnectorTypes.jira, fields: null, }, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, closure_type: 'close-by-user', }; @@ -161,5 +162,5 @@ export const caseConfigurationCamelCaseResponseMock: CaseConfigure = { updatedAt: '2020-04-06T14:03:18.657Z', updatedBy: { username: 'elastic', fullName: 'Elastic', email: 'elastic@elastic.co' }, version: 'WzHJ12', - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }; diff --git a/x-pack/plugins/cases/public/containers/configure/use_configure.test.tsx b/x-pack/plugins/cases/public/containers/configure/use_configure.test.tsx index 4d2abbcaec4d4..d8d552ceb8b7a 100644 --- a/x-pack/plugins/cases/public/containers/configure/use_configure.test.tsx +++ b/x-pack/plugins/cases/public/containers/configure/use_configure.test.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import React from 'react'; import { renderHook, act } from '@testing-library/react-hooks'; import { initialState, @@ -15,6 +16,7 @@ import { import { mappings, caseConfigurationCamelCaseResponseMock } from './mock'; import * as api from './api'; import { ConnectorTypes } from '../../../common'; +import { TestProviders } from '../../common/mock'; const mockErrorToast = jest.fn(); const mockSuccessToast = jest.fn(); @@ -49,8 +51,11 @@ describe('useConfigure', () => { test('init', async () => { await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useCaseConfigure() + const { result, waitForNextUpdate } = renderHook( + () => useCaseConfigure(), + { + wrapper: ({ children }) => {children}, + } ); await waitForNextUpdate(); expect(result.current).toEqual({ @@ -67,8 +72,11 @@ describe('useConfigure', () => { test('fetch case configuration', async () => { await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useCaseConfigure() + const { result, waitForNextUpdate } = renderHook( + () => useCaseConfigure(), + { + wrapper: ({ children }) => {children}, + } ); await waitForNextUpdate(); await waitForNextUpdate(); @@ -99,8 +107,11 @@ describe('useConfigure', () => { const spyOnGetCaseConfigure = jest.spyOn(api, 'getCaseConfigure'); await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useCaseConfigure() + const { result, waitForNextUpdate } = renderHook( + () => useCaseConfigure(), + { + wrapper: ({ children }) => {children}, + } ); await waitForNextUpdate(); await waitForNextUpdate(); @@ -111,8 +122,11 @@ describe('useConfigure', () => { test('correctly sets mappings', async () => { await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useCaseConfigure() + const { result, waitForNextUpdate } = renderHook( + () => useCaseConfigure(), + { + wrapper: ({ children }) => {children}, + } ); await waitForNextUpdate(); await waitForNextUpdate(); @@ -124,8 +138,11 @@ describe('useConfigure', () => { test('set isLoading to true when fetching case configuration', async () => { await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useCaseConfigure() + const { result, waitForNextUpdate } = renderHook( + () => useCaseConfigure(), + { + wrapper: ({ children }) => {children}, + } ); await waitForNextUpdate(); await waitForNextUpdate(); @@ -137,8 +154,11 @@ describe('useConfigure', () => { test('persist case configuration', async () => { await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useCaseConfigure() + const { result, waitForNextUpdate } = renderHook( + () => useCaseConfigure(), + { + wrapper: ({ children }) => {children}, + } ); await waitForNextUpdate(); await waitForNextUpdate(); @@ -166,8 +186,11 @@ describe('useConfigure', () => { ); await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useCaseConfigure() + const { result, waitForNextUpdate } = renderHook( + () => useCaseConfigure(), + { + wrapper: ({ children }) => {children}, + } ); await waitForNextUpdate(); await waitForNextUpdate(); @@ -192,8 +215,11 @@ describe('useConfigure', () => { ); await act(async () => { - const { waitForNextUpdate } = renderHook(() => - useCaseConfigure() + const { waitForNextUpdate } = renderHook( + () => useCaseConfigure(), + { + wrapper: ({ children }) => {children}, + } ); await waitForNextUpdate(); await waitForNextUpdate(); @@ -221,8 +247,11 @@ describe('useConfigure', () => { ); await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useCaseConfigure() + const { result, waitForNextUpdate } = renderHook( + () => useCaseConfigure(), + { + wrapper: ({ children }) => {children}, + } ); await waitForNextUpdate(); await waitForNextUpdate(); @@ -245,8 +274,11 @@ describe('useConfigure', () => { ); await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useCaseConfigure() + const { result, waitForNextUpdate } = renderHook( + () => useCaseConfigure(), + { + wrapper: ({ children }) => {children}, + } ); await waitForNextUpdate(); await waitForNextUpdate(); @@ -266,8 +298,11 @@ describe('useConfigure', () => { }); await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useCaseConfigure() + const { result, waitForNextUpdate } = renderHook( + () => useCaseConfigure(), + { + wrapper: ({ children }) => {children}, + } ); await waitForNextUpdate(); @@ -302,8 +337,11 @@ describe('useConfigure', () => { }); await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useCaseConfigure() + const { result, waitForNextUpdate } = renderHook( + () => useCaseConfigure(), + { + wrapper: ({ children }) => {children}, + } ); await waitForNextUpdate(); diff --git a/x-pack/plugins/cases/public/containers/configure/use_configure.tsx b/x-pack/plugins/cases/public/containers/configure/use_configure.tsx index b4a4ab35b96d7..d02a22bde408c 100644 --- a/x-pack/plugins/cases/public/containers/configure/use_configure.tsx +++ b/x-pack/plugins/cases/public/containers/configure/use_configure.tsx @@ -12,6 +12,7 @@ import * as i18n from './translations'; import { ClosureType, CaseConfigure, CaseConnector, CaseConnectorMapping } from './types'; import { ConnectorTypes } from '../../../common'; import { useToasts } from '../../common/lib/kibana'; +import { useOwnerContext } from '../../components/owner_context/use_owner_context'; export type ConnectorConfiguration = { connector: CaseConnector } & { closureType: CaseConfigure['closureType']; @@ -155,6 +156,7 @@ export const initialState: State = { }; export const useCaseConfigure = (): ReturnUseCaseConfigure => { + const owner = useOwnerContext(); const [state, dispatch] = useReducer(configureCasesReducer, initialState); const toasts = useToasts(); const setCurrentConfiguration = useCallback((configuration: ConnectorConfiguration) => { @@ -213,7 +215,6 @@ export const useCaseConfigure = (): ReturnUseCaseConfigure => { }); }, []); - // TODO: refactor const setID = useCallback((id: string) => { dispatch({ payload: id, @@ -234,7 +235,10 @@ export const useCaseConfigure = (): ReturnUseCaseConfigure => { abortCtrlRefetchRef.current = new AbortController(); setLoading(true); - const res = await getCaseConfigure({ signal: abortCtrlRefetchRef.current.signal }); + const res = await getCaseConfigure({ + signal: abortCtrlRefetchRef.current.signal, + owner, + }); if (!isCancelledRefetchRef.current) { if (res != null) { @@ -295,8 +299,8 @@ export const useCaseConfigure = (): ReturnUseCaseConfigure => { const res = state.version.length === 0 ? await postCaseConfigure( - // TODO: use constant after https://github.com/elastic/kibana/pull/97646 is being merged - { ...connectorObj, owner: 'securitySolution' }, + // The first owner will be used for case creation + { ...connectorObj, owner: owner[0] }, abortCtrlPersistRef.current.signal ) : await patchCaseConfigure( @@ -347,17 +351,17 @@ export const useCaseConfigure = (): ReturnUseCaseConfigure => { } }, [ - setClosureType, - setConnector, - setCurrentConfiguration, - setMappings, setPersistLoading, - setVersion, - setID, - state.currentConfiguration.connector, state.version, - // TODO: do we need this? state.id, + state.currentConfiguration.connector, + owner, + setConnector, + setClosureType, + setVersion, + setID, + setMappings, + setCurrentConfiguration, toasts, ] ); diff --git a/x-pack/plugins/cases/public/containers/mock.ts b/x-pack/plugins/cases/public/containers/mock.ts index 4871fa1555a12..72fee3c602c4e 100644 --- a/x-pack/plugins/cases/public/containers/mock.ts +++ b/x-pack/plugins/cases/public/containers/mock.ts @@ -19,6 +19,7 @@ import { CommentResponse, CommentType, ConnectorTypes, + SECURITY_SOLUTION_OWNER, UserAction, UserActionField, } from '../../common'; @@ -47,7 +48,7 @@ export const basicComment: Comment = { id: basicCommentId, createdAt: basicCreatedAt, createdBy: elasticUser, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, pushedAt: null, pushedBy: null, updatedAt: null, @@ -63,7 +64,7 @@ export const alertComment: Comment = { id: 'alert-comment-id', createdAt: basicCreatedAt, createdBy: elasticUser, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, pushedAt: null, pushedBy: null, rule: { @@ -77,7 +78,7 @@ export const alertComment: Comment = { export const basicCase: Case = { type: CaseType.individual, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, closedAt: null, closedBy: null, id: basicCaseId, @@ -108,7 +109,7 @@ export const basicCase: Case = { export const collectionCase: Case = { type: CaseType.collection, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, closedAt: null, closedBy: null, id: 'collection-id', @@ -185,7 +186,7 @@ const basicAction = { newValue: 'what a cool value', caseId: basicCaseId, commentId: null, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }; export const cases: Case[] = [ @@ -235,7 +236,7 @@ export const basicCommentSnake: CommentResponse = { id: basicCommentId, created_at: basicCreatedAt, created_by: elasticUserSnake, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, pushed_at: null, pushed_by: null, updated_at: null, @@ -260,7 +261,7 @@ export const basicCaseSnake: CaseResponse = { external_service: null, updated_at: basicUpdatedAt, updated_by: elasticUserSnake, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, } as CaseResponse; export const casesStatusSnake: CasesStatusResponse = { @@ -318,7 +319,7 @@ const basicActionSnake = { new_value: 'what a cool value', case_id: basicCaseId, comment_id: null, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }; export const getUserActionSnake = (af: UserActionField, a: UserAction) => ({ ...basicActionSnake, diff --git a/x-pack/plugins/cases/public/containers/use_get_cases.test.tsx b/x-pack/plugins/cases/public/containers/use_get_cases.test.tsx index b07fec4984eb1..b3a6932c6971c 100644 --- a/x-pack/plugins/cases/public/containers/use_get_cases.test.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_cases.test.tsx @@ -5,8 +5,9 @@ * 2.0. */ +import React from 'react'; import { renderHook, act } from '@testing-library/react-hooks'; -import { CaseStatuses } from '../../common'; +import { CaseStatuses, SECURITY_SOLUTION_OWNER } from '../../common'; import { DEFAULT_FILTER_OPTIONS, DEFAULT_QUERY_PARAMS, @@ -17,6 +18,7 @@ import { import { UpdateKey } from './types'; import { allCases, basicCase } from './mock'; import * as api from './api'; +import { TestProviders } from '../common/mock'; jest.mock('./api'); jest.mock('../common/lib/kibana'); @@ -30,7 +32,10 @@ describe('useGetCases', () => { it('init', async () => { await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => useGetCases()); + const { result, waitForNextUpdate } = renderHook(() => useGetCases(), { + wrapper: ({ children }) => {children}, + }); + await waitForNextUpdate(); expect(result.current).toEqual({ data: initialData, @@ -51,11 +56,13 @@ describe('useGetCases', () => { it('calls getCases with correct arguments', async () => { const spyOnGetCases = jest.spyOn(api, 'getCases'); await act(async () => { - const { waitForNextUpdate } = renderHook(() => useGetCases()); + const { waitForNextUpdate } = renderHook(() => useGetCases(), { + wrapper: ({ children }) => {children}, + }); await waitForNextUpdate(); await waitForNextUpdate(); expect(spyOnGetCases).toBeCalledWith({ - filterOptions: DEFAULT_FILTER_OPTIONS, + filterOptions: { ...DEFAULT_FILTER_OPTIONS, owner: [SECURITY_SOLUTION_OWNER] }, queryParams: DEFAULT_QUERY_PARAMS, signal: abortCtrl.signal, }); @@ -64,7 +71,9 @@ describe('useGetCases', () => { it('fetch cases', async () => { await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => useGetCases()); + const { result, waitForNextUpdate } = renderHook(() => useGetCases(), { + wrapper: ({ children }) => {children}, + }); await waitForNextUpdate(); await waitForNextUpdate(); expect(result.current).toEqual({ @@ -82,6 +91,7 @@ describe('useGetCases', () => { }); }); }); + it('dispatch update case property', async () => { const spyOnPatchCase = jest.spyOn(api, 'patchCase'); await act(async () => { @@ -92,7 +102,9 @@ describe('useGetCases', () => { refetchCasesStatus: jest.fn(), version: '99999', }; - const { result, waitForNextUpdate } = renderHook(() => useGetCases()); + const { result, waitForNextUpdate } = renderHook(() => useGetCases(), { + wrapper: ({ children }) => {children}, + }); await waitForNextUpdate(); await waitForNextUpdate(); result.current.dispatchUpdateCaseProperty(updateCase); @@ -109,7 +121,9 @@ describe('useGetCases', () => { it('refetch cases', async () => { const spyOnGetCases = jest.spyOn(api, 'getCases'); await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => useGetCases()); + const { result, waitForNextUpdate } = renderHook(() => useGetCases(), { + wrapper: ({ children }) => {children}, + }); await waitForNextUpdate(); await waitForNextUpdate(); result.current.refetchCases(); @@ -119,7 +133,9 @@ describe('useGetCases', () => { it('set isLoading to true when refetching case', async () => { await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => useGetCases()); + const { result, waitForNextUpdate } = renderHook(() => useGetCases(), { + wrapper: ({ children }) => {children}, + }); await waitForNextUpdate(); await waitForNextUpdate(); result.current.refetchCases(); @@ -135,7 +151,9 @@ describe('useGetCases', () => { }); await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => useGetCases()); + const { result, waitForNextUpdate } = renderHook(() => useGetCases(), { + wrapper: ({ children }) => {children}, + }); await waitForNextUpdate(); await waitForNextUpdate(); @@ -154,6 +172,7 @@ describe('useGetCases', () => { }); }); }); + it('set filters', async () => { await act(async () => { const spyOnGetCases = jest.spyOn(api, 'getCases'); @@ -162,40 +181,61 @@ describe('useGetCases', () => { tags: ['new'], status: CaseStatuses.closed, }; - const { result, waitForNextUpdate } = renderHook(() => useGetCases()); + + const { result, waitForNextUpdate } = renderHook(() => useGetCases(), { + wrapper: ({ children }) => {children}, + }); + await waitForNextUpdate(); await waitForNextUpdate(); result.current.setFilters(newFilters); await waitForNextUpdate(); + expect(spyOnGetCases.mock.calls[1][0]).toEqual({ - filterOptions: { ...DEFAULT_FILTER_OPTIONS, ...newFilters }, + filterOptions: { + ...DEFAULT_FILTER_OPTIONS, + ...newFilters, + owner: [SECURITY_SOLUTION_OWNER], + }, queryParams: DEFAULT_QUERY_PARAMS, signal: abortCtrl.signal, }); }); }); + it('set query params', async () => { await act(async () => { const spyOnGetCases = jest.spyOn(api, 'getCases'); const newQueryParams = { page: 2, }; - const { result, waitForNextUpdate } = renderHook(() => useGetCases()); + + const { result, waitForNextUpdate } = renderHook(() => useGetCases(), { + wrapper: ({ children }) => {children}, + }); + await waitForNextUpdate(); await waitForNextUpdate(); result.current.setQueryParams(newQueryParams); await waitForNextUpdate(); + expect(spyOnGetCases.mock.calls[1][0]).toEqual({ - filterOptions: DEFAULT_FILTER_OPTIONS, - queryParams: { ...DEFAULT_QUERY_PARAMS, ...newQueryParams }, + filterOptions: { ...DEFAULT_FILTER_OPTIONS, owner: [SECURITY_SOLUTION_OWNER] }, + queryParams: { + ...DEFAULT_QUERY_PARAMS, + ...newQueryParams, + }, signal: abortCtrl.signal, }); }); }); + it('set selected cases', async () => { await act(async () => { const selectedCases = [basicCase]; - const { result, waitForNextUpdate } = renderHook(() => useGetCases()); + const { result, waitForNextUpdate } = renderHook(() => useGetCases(), { + wrapper: ({ children }) => {children}, + }); await waitForNextUpdate(); await waitForNextUpdate(); result.current.setSelectedCases(selectedCases); diff --git a/x-pack/plugins/cases/public/containers/use_get_cases.tsx b/x-pack/plugins/cases/public/containers/use_get_cases.tsx index ec1abd6214926..b3aa374f5418e 100644 --- a/x-pack/plugins/cases/public/containers/use_get_cases.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_cases.tsx @@ -19,6 +19,7 @@ import { import { useToasts } from '../common/lib/kibana'; import * as i18n from './translations'; import { getCases, patchCase } from './api'; +import { useOwnerContext } from '../components/owner_context/use_owner_context'; export interface UseGetCasesState { data: AllCases; @@ -139,12 +140,19 @@ export interface UseGetCases extends UseGetCasesState { const empty = {}; export const useGetCases = ( - initialQueryParams: Partial = empty, - initialFilterOptions: Partial = empty + params: { + initialQueryParams?: Partial; + initialFilterOptions?: Partial; + } = {} ): UseGetCases => { + const owner = useOwnerContext(); + const { initialQueryParams = empty, initialFilterOptions = empty } = params; const [state, dispatch] = useReducer(dataFetchReducer, { data: initialData, - filterOptions: { ...DEFAULT_FILTER_OPTIONS, ...initialFilterOptions }, + filterOptions: { + ...DEFAULT_FILTER_OPTIONS, + ...initialFilterOptions, + }, isError: false, loading: [], queryParams: { ...DEFAULT_QUERY_PARAMS, ...initialQueryParams }, @@ -177,7 +185,7 @@ export const useGetCases = ( dispatch({ type: 'FETCH_INIT', payload: 'cases' }); const response = await getCases({ - filterOptions, + filterOptions: { ...filterOptions, owner }, queryParams, signal: abortCtrlFetchCases.current.signal, }); @@ -200,7 +208,7 @@ export const useGetCases = ( } } }, - [toasts] + [owner, toasts] ); const dispatchUpdateCaseProperty = useCallback( diff --git a/x-pack/plugins/cases/public/containers/use_get_reporters.test.tsx b/x-pack/plugins/cases/public/containers/use_get_reporters.test.tsx index 8345ddf107872..692c5237f58bf 100644 --- a/x-pack/plugins/cases/public/containers/use_get_reporters.test.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_reporters.test.tsx @@ -5,10 +5,13 @@ * 2.0. */ +import React from 'react'; import { renderHook, act } from '@testing-library/react-hooks'; import { useGetReporters, UseGetReporters } from './use_get_reporters'; import { reporters, respReporters } from './mock'; import * as api from './api'; +import { TestProviders } from '../common/mock'; +import { SECURITY_SOLUTION_OWNER } from '../../common'; jest.mock('./api'); jest.mock('../common/lib/kibana'); @@ -22,8 +25,11 @@ describe('useGetReporters', () => { it('init', async () => { await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useGetReporters() + const { result, waitForNextUpdate } = renderHook( + () => useGetReporters(), + { + wrapper: ({ children }) => {children}, + } ); await waitForNextUpdate(); expect(result.current).toEqual({ @@ -39,17 +45,22 @@ describe('useGetReporters', () => { it('calls getReporters api', async () => { const spyOnGetReporters = jest.spyOn(api, 'getReporters'); await act(async () => { - const { waitForNextUpdate } = renderHook(() => useGetReporters()); + const { waitForNextUpdate } = renderHook(() => useGetReporters(), { + wrapper: ({ children }) => {children}, + }); await waitForNextUpdate(); await waitForNextUpdate(); - expect(spyOnGetReporters).toBeCalledWith(abortCtrl.signal); + expect(spyOnGetReporters).toBeCalledWith(abortCtrl.signal, [SECURITY_SOLUTION_OWNER]); }); }); it('fetch reporters', async () => { await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useGetReporters() + const { result, waitForNextUpdate } = renderHook( + () => useGetReporters(), + { + wrapper: ({ children }) => {children}, + } ); await waitForNextUpdate(); await waitForNextUpdate(); @@ -66,8 +77,11 @@ describe('useGetReporters', () => { it('refetch reporters', async () => { const spyOnGetReporters = jest.spyOn(api, 'getReporters'); await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useGetReporters() + const { result, waitForNextUpdate } = renderHook( + () => useGetReporters(), + { + wrapper: ({ children }) => {children}, + } ); await waitForNextUpdate(); await waitForNextUpdate(); @@ -83,8 +97,11 @@ describe('useGetReporters', () => { }); await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useGetReporters() + const { result, waitForNextUpdate } = renderHook( + () => useGetReporters(), + { + wrapper: ({ children }) => {children}, + } ); await waitForNextUpdate(); await waitForNextUpdate(); diff --git a/x-pack/plugins/cases/public/containers/use_get_reporters.tsx b/x-pack/plugins/cases/public/containers/use_get_reporters.tsx index a9d28de33cb41..b3c2eff2c8e01 100644 --- a/x-pack/plugins/cases/public/containers/use_get_reporters.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_reporters.tsx @@ -12,6 +12,7 @@ import { User } from '../../common'; import { getReporters } from './api'; import * as i18n from './translations'; import { useToasts } from '../common/lib/kibana'; +import { useOwnerContext } from '../components/owner_context/use_owner_context'; interface ReportersState { reporters: string[]; @@ -32,6 +33,7 @@ export interface UseGetReporters extends ReportersState { } export const useGetReporters = (): UseGetReporters => { + const owner = useOwnerContext(); const [reportersState, setReporterState] = useState(initialData); const toasts = useToasts(); @@ -48,7 +50,7 @@ export const useGetReporters = (): UseGetReporters => { isLoading: true, }); - const response = await getReporters(abortCtrlRef.current.signal); + const response = await getReporters(abortCtrlRef.current.signal, owner); const myReporters = response .map((r) => (r.full_name == null || isEmpty(r.full_name) ? r.username ?? '' : r.full_name)) .filter((u) => !isEmpty(u)); @@ -78,7 +80,7 @@ export const useGetReporters = (): UseGetReporters => { }); } } - }, [reportersState, toasts]); + }, [owner, reportersState, toasts]); useEffect(() => { fetchReporters(); diff --git a/x-pack/plugins/cases/public/containers/use_get_tags.test.tsx b/x-pack/plugins/cases/public/containers/use_get_tags.test.tsx index 3fecfb51b958c..60d368aca0a04 100644 --- a/x-pack/plugins/cases/public/containers/use_get_tags.test.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_tags.test.tsx @@ -5,10 +5,13 @@ * 2.0. */ +import React from 'react'; import { renderHook, act } from '@testing-library/react-hooks'; import { useGetTags, UseGetTags } from './use_get_tags'; import { tags } from './mock'; import * as api from './api'; +import { TestProviders } from '../common/mock'; +import { SECURITY_SOLUTION_OWNER } from '../../common'; jest.mock('./api'); jest.mock('../common/lib/kibana'); @@ -22,7 +25,9 @@ describe('useGetTags', () => { it('init', async () => { await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => useGetTags()); + const { result, waitForNextUpdate } = renderHook(() => useGetTags(), { + wrapper: ({ children }) => {children}, + }); await waitForNextUpdate(); expect(result.current).toEqual({ tags: [], @@ -36,16 +41,20 @@ describe('useGetTags', () => { it('calls getTags api', async () => { const spyOnGetTags = jest.spyOn(api, 'getTags'); await act(async () => { - const { waitForNextUpdate } = renderHook(() => useGetTags()); + const { waitForNextUpdate } = renderHook(() => useGetTags(), { + wrapper: ({ children }) => {children}, + }); await waitForNextUpdate(); await waitForNextUpdate(); - expect(spyOnGetTags).toBeCalledWith(abortCtrl.signal); + expect(spyOnGetTags).toBeCalledWith(abortCtrl.signal, [SECURITY_SOLUTION_OWNER]); }); }); it('fetch tags', async () => { await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => useGetTags()); + const { result, waitForNextUpdate } = renderHook(() => useGetTags(), { + wrapper: ({ children }) => {children}, + }); await waitForNextUpdate(); await waitForNextUpdate(); expect(result.current).toEqual({ @@ -60,7 +69,9 @@ describe('useGetTags', () => { it('refetch tags', async () => { const spyOnGetTags = jest.spyOn(api, 'getTags'); await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => useGetTags()); + const { result, waitForNextUpdate } = renderHook(() => useGetTags(), { + wrapper: ({ children }) => {children}, + }); await waitForNextUpdate(); await waitForNextUpdate(); result.current.fetchTags(); @@ -75,7 +86,9 @@ describe('useGetTags', () => { }); await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => useGetTags()); + const { result, waitForNextUpdate } = renderHook(() => useGetTags(), { + wrapper: ({ children }) => {children}, + }); await waitForNextUpdate(); await waitForNextUpdate(); diff --git a/x-pack/plugins/cases/public/containers/use_get_tags.tsx b/x-pack/plugins/cases/public/containers/use_get_tags.tsx index 4368b025baa38..362e7ebf8fbf3 100644 --- a/x-pack/plugins/cases/public/containers/use_get_tags.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_tags.tsx @@ -7,6 +7,7 @@ import { useEffect, useReducer, useRef, useCallback } from 'react'; import { useToasts } from '../common/lib/kibana'; +import { useOwnerContext } from '../components/owner_context/use_owner_context'; import { getTags } from './api'; import * as i18n from './translations'; @@ -52,6 +53,7 @@ const dataFetchReducer = (state: TagsState, action: Action): TagsState => { const initialData: string[] = []; export const useGetTags = (): UseGetTags => { + const owner = useOwnerContext(); const [state, dispatch] = useReducer(dataFetchReducer, { isLoading: true, isError: false, @@ -68,7 +70,7 @@ export const useGetTags = (): UseGetTags => { abortCtrlRef.current = new AbortController(); dispatch({ type: 'FETCH_INIT' }); - const response = await getTags(abortCtrlRef.current.signal); + const response = await getTags(abortCtrlRef.current.signal, owner); if (!isCancelledRef.current) { dispatch({ type: 'FETCH_SUCCESS', payload: response }); diff --git a/x-pack/plugins/cases/public/containers/use_post_case.test.tsx b/x-pack/plugins/cases/public/containers/use_post_case.test.tsx index c1d030e7618c3..d2b638b4c846f 100644 --- a/x-pack/plugins/cases/public/containers/use_post_case.test.tsx +++ b/x-pack/plugins/cases/public/containers/use_post_case.test.tsx @@ -8,7 +8,7 @@ import { renderHook, act } from '@testing-library/react-hooks'; import { usePostCase, UsePostCase } from './use_post_case'; import * as api from './api'; -import { ConnectorTypes } from '../../common'; +import { ConnectorTypes, SECURITY_SOLUTION_OWNER } from '../../common'; import { basicCasePost } from './mock'; jest.mock('./api'); @@ -29,7 +29,7 @@ describe('usePostCase', () => { settings: { syncAlerts: true, }, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }; beforeEach(() => { jest.clearAllMocks(); diff --git a/x-pack/plugins/cases/public/containers/use_post_comment.test.tsx b/x-pack/plugins/cases/public/containers/use_post_comment.test.tsx index a9750f213f3d6..8a86d9becdfde 100644 --- a/x-pack/plugins/cases/public/containers/use_post_comment.test.tsx +++ b/x-pack/plugins/cases/public/containers/use_post_comment.test.tsx @@ -7,7 +7,7 @@ import { renderHook, act } from '@testing-library/react-hooks'; -import { CommentType } from '../../common'; +import { CommentType, SECURITY_SOLUTION_OWNER } from '../../common'; import { usePostComment, UsePostComment } from './use_post_comment'; import { basicCaseId, basicSubCaseId } from './mock'; import * as api from './api'; @@ -20,7 +20,7 @@ describe('usePostComment', () => { const samplePost = { comment: 'a comment', type: CommentType.user as const, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }; const updateCaseCallback = jest.fn(); beforeEach(() => { diff --git a/x-pack/plugins/cases/public/containers/utils.ts b/x-pack/plugins/cases/public/containers/utils.ts index fc8064adf5d94..de67b1cfbd6fa 100644 --- a/x-pack/plugins/cases/public/containers/utils.ts +++ b/x-pack/plugins/cases/public/containers/utils.ts @@ -94,7 +94,6 @@ export const decodeCasesResponse = (respCase?: CasesResponse) => export const decodeCasesFindResponse = (respCases?: CasesFindResponse) => pipe(CasesFindResponseRt.decode(respCases), fold(throwErrors(createToasterPlainError), identity)); -// TODO: might need to refactor this export const decodeCaseConfigurationsResponse = (respCase?: CasesConfigurationsResponse) => { return pipe( CaseConfigurationsResponseRt.decode(respCase), diff --git a/x-pack/plugins/cases/public/methods/get_all_cases_selector_modal.tsx b/x-pack/plugins/cases/public/methods/get_all_cases_selector_modal.tsx index b6caae39c284a..dbb466129c60b 100644 --- a/x-pack/plugins/cases/public/methods/get_all_cases_selector_modal.tsx +++ b/x-pack/plugins/cases/public/methods/get_all_cases_selector_modal.tsx @@ -8,10 +8,14 @@ import React, { lazy, Suspense } from 'react'; import { EuiLoadingSpinner } from '@elastic/eui'; import { AllCasesSelectorModalProps } from '../components/all_cases/selector_modal'; +import { OwnerProvider } from '../components/owner_context'; +import { Owner } from '../types'; const AllCasesSelectorModalLazy = lazy(() => import('../components/all_cases/selector_modal')); -export const getAllCasesSelectorModalLazy = (props: AllCasesSelectorModalProps) => ( - }> - - +export const getAllCasesSelectorModalLazy = (props: AllCasesSelectorModalProps & Owner) => ( + + }> + + + ); diff --git a/x-pack/plugins/cases/public/types.ts b/x-pack/plugins/cases/public/types.ts index 269d1773b3404..2193832492aa2 100644 --- a/x-pack/plugins/cases/public/types.ts +++ b/x-pack/plugins/cases/public/types.ts @@ -39,6 +39,10 @@ export type StartServices = CoreStart & security: SecurityPluginSetup; }; +export interface Owner { + owner: string[]; +} + export interface CasesUiStart { getAllCases: (props: AllCasesProps) => ReactElement; getAllCasesSelectorModal: ( diff --git a/x-pack/plugins/cases/server/client/cases/mock.ts b/x-pack/plugins/cases/server/client/cases/mock.ts index a8079d6095ba3..23db57c6d3097 100644 --- a/x-pack/plugins/cases/server/client/cases/mock.ts +++ b/x-pack/plugins/cases/server/client/cases/mock.ts @@ -12,6 +12,7 @@ import { CaseUserActionsResponse, AssociationType, CommentResponseAlertsType, + SECURITY_SOLUTION_OWNER, } from '../../../common'; import { BasicParams } from './types'; @@ -39,7 +40,7 @@ export const comment: CommentResponse = { email: 'testemail@elastic.co', username: 'elastic', }, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, pushed_at: null, pushed_by: null, updated_at: '2019-11-25T21:55:00.177Z', @@ -67,7 +68,7 @@ export const commentAlert: CommentResponse = { email: 'testemail@elastic.co', username: 'elastic', }, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, pushed_at: null, pushed_by: null, updated_at: '2019-11-25T21:55:00.177Z', @@ -85,7 +86,7 @@ export const commentAlertMultipleIds: CommentResponseAlertsType = { alertId: ['alert-id-1', 'alert-id-2'], index: 'alert-index-1', type: CommentType.alert as const, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }; export const commentGeneratedAlert: CommentResponseAlertsType = { @@ -135,7 +136,7 @@ export const userActions: CaseUserActionsResponse = [ action_id: 'fd830c60-6646-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', comment_id: null, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }, { action_field: ['pushed'], @@ -152,7 +153,7 @@ export const userActions: CaseUserActionsResponse = [ action_id: '0a801750-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', comment_id: null, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }, { action_field: ['comment'], @@ -168,7 +169,7 @@ export const userActions: CaseUserActionsResponse = [ action_id: '7373eb60-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', comment_id: 'comment-alert-1', - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }, { action_field: ['comment'], @@ -184,7 +185,7 @@ export const userActions: CaseUserActionsResponse = [ action_id: '7abc6410-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', comment_id: 'comment-alert-2', - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }, { action_field: ['pushed'], @@ -201,7 +202,7 @@ export const userActions: CaseUserActionsResponse = [ action_id: '9b91d8f0-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', comment_id: null, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }, { action_field: ['comment'], @@ -217,6 +218,6 @@ export const userActions: CaseUserActionsResponse = [ action_id: '0818e5e0-6648-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', comment_id: 'comment-user-1', - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }, ]; diff --git a/x-pack/plugins/cases/server/client/cases/utils.test.ts b/x-pack/plugins/cases/server/client/cases/utils.test.ts index 9dd36d2f8e534..9f18fa4931e62 100644 --- a/x-pack/plugins/cases/server/client/cases/utils.test.ts +++ b/x-pack/plugins/cases/server/client/cases/utils.test.ts @@ -29,6 +29,7 @@ import { transformFields, } from './utils'; import { flattenCaseSavedObject } from '../../common'; +import { SECURITY_SOLUTION_OWNER } from '../../../common'; const formatComment = { commentId: commentObj.id, @@ -701,7 +702,7 @@ describe('utils', () => { action_id: '9b91d8f0-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', comment_id: null, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }, ]); diff --git a/x-pack/plugins/cases/server/common/utils.test.ts b/x-pack/plugins/cases/server/common/utils.test.ts index 4057cf4f3f52d..322e45094eda4 100644 --- a/x-pack/plugins/cases/server/common/utils.test.ts +++ b/x-pack/plugins/cases/server/common/utils.test.ts @@ -6,6 +6,7 @@ */ import { SavedObjectsFindResponse } from 'kibana/server'; +import { SECURITY_SOLUTION_OWNER } from '../../common'; import { AssociationType, CaseResponse, @@ -587,7 +588,7 @@ describe('common utils', () => { full_name: 'Elastic', username: 'elastic', associationType: AssociationType.case, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }; const res = transformNewComment(comment); @@ -616,7 +617,7 @@ describe('common utils', () => { comment: 'A comment', type: CommentType.user as const, createdDate: '2020-04-09T09:43:51.778Z', - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, associationType: AssociationType.case, }; @@ -650,7 +651,7 @@ describe('common utils', () => { email: null, full_name: null, username: null, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, associationType: AssociationType.case, }; @@ -684,7 +685,7 @@ describe('common utils', () => { createCommentFindResponse([ { ids: ['1'], - comments: [{ comment: '', type: CommentType.user, owner: 'securitySolution' }], + comments: [{ comment: '', type: CommentType.user, owner: SECURITY_SOLUTION_OWNER }], }, ]).saved_objects[0] ) @@ -706,7 +707,7 @@ describe('common utils', () => { id: 'rule-id-1', name: 'rule-name-1', }, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }, ], }, @@ -730,7 +731,7 @@ describe('common utils', () => { id: 'rule-id-1', name: 'rule-name-1', }, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }, ], }, @@ -751,7 +752,7 @@ describe('common utils', () => { { alertId: ['a', 'b'], index: '', - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, type: CommentType.alert, rule: { id: 'rule-id-1', @@ -760,7 +761,7 @@ describe('common utils', () => { }, { comment: '', - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, type: CommentType.user, }, ], @@ -780,7 +781,7 @@ describe('common utils', () => { ids: ['1'], comments: [ { - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, alertId: ['a', 'b'], index: '', type: CommentType.alert, @@ -795,7 +796,7 @@ describe('common utils', () => { ids: ['2'], comments: [ { - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, comment: '', type: CommentType.user, }, @@ -819,7 +820,7 @@ describe('common utils', () => { ids: ['1', '2'], comments: [ { - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, alertId: ['a', 'b'], index: '', type: CommentType.alert, @@ -851,7 +852,7 @@ describe('common utils', () => { ids: ['1', '2'], comments: [ { - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, alertId: ['a', 'b'], index: '', type: CommentType.alert, diff --git a/x-pack/plugins/cases/server/connectors/case/index.test.ts b/x-pack/plugins/cases/server/connectors/case/index.test.ts index a2afc1df4ecf7..4a5fa8149c406 100644 --- a/x-pack/plugins/cases/server/connectors/case/index.test.ts +++ b/x-pack/plugins/cases/server/connectors/case/index.test.ts @@ -27,6 +27,7 @@ import { createCasesClientFactory, createCasesClientMock, } from '../../client/mocks'; +import { SECURITY_SOLUTION_OWNER } from '../../../common'; const services = actionsMock.createServices(); let caseActionType: CaseActionType; @@ -753,7 +754,7 @@ describe('case connector', () => { comment: { comment: 'a comment', type: CommentType.user, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }, }, }; @@ -774,7 +775,7 @@ describe('case connector', () => { id: null, name: null, }, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }, }, }; @@ -958,7 +959,7 @@ describe('case connector', () => { settings: { syncAlerts: true, }, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }; mockCasesClient.cases.create.mockReturnValue(Promise.resolve(createReturn)); @@ -1055,7 +1056,7 @@ describe('case connector', () => { settings: { syncAlerts: true, }, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }, ]; @@ -1136,7 +1137,7 @@ describe('case connector', () => { username: 'awesome', }, id: 'mock-comment', - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, pushed_at: null, pushed_by: null, updated_at: null, @@ -1147,7 +1148,7 @@ describe('case connector', () => { settings: { syncAlerts: true, }, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }; mockCasesClient.attachments.add.mockReturnValue(Promise.resolve(commentReturn)); @@ -1160,7 +1161,7 @@ describe('case connector', () => { comment: { comment: 'a comment', type: CommentType.user, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }, }, }; diff --git a/x-pack/plugins/cases/server/routes/api/__fixtures__/mock_saved_objects.ts b/x-pack/plugins/cases/server/routes/api/__fixtures__/mock_saved_objects.ts index ff188426dd96d..bddceef8d782e 100644 --- a/x-pack/plugins/cases/server/routes/api/__fixtures__/mock_saved_objects.ts +++ b/x-pack/plugins/cases/server/routes/api/__fixtures__/mock_saved_objects.ts @@ -21,6 +21,7 @@ import { import { CASE_CONNECTOR_MAPPINGS_SAVED_OBJECT, CASE_USER_ACTION_SAVED_OBJECT, + SECURITY_SOLUTION_OWNER, } from '../../../../common/constants'; import { mappings } from '../../../client/configure/mock'; @@ -58,7 +59,7 @@ export const mockCases: Array> = [ settings: { syncAlerts: true, }, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }, references: [], updated_at: '2019-11-25T21:54:48.952Z', @@ -97,7 +98,7 @@ export const mockCases: Array> = [ settings: { syncAlerts: true, }, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }, references: [], updated_at: '2019-11-25T22:32:00.900Z', @@ -140,7 +141,7 @@ export const mockCases: Array> = [ settings: { syncAlerts: true, }, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }, references: [], updated_at: '2019-11-25T22:32:17.947Z', @@ -187,7 +188,7 @@ export const mockCases: Array> = [ settings: { syncAlerts: true, }, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }, references: [], updated_at: '2019-11-25T22:32:17.947Z', @@ -250,7 +251,7 @@ export const mockCaseComments: Array> = [ email: 'testemail@elastic.co', username: 'elastic', }, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, pushed_at: null, pushed_by: null, updated_at: '2019-11-25T21:55:00.177Z', @@ -283,7 +284,7 @@ export const mockCaseComments: Array> = [ email: 'testemail@elastic.co', username: 'elastic', }, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, pushed_at: null, pushed_by: null, updated_at: '2019-11-25T21:55:14.633Z', @@ -317,7 +318,7 @@ export const mockCaseComments: Array> = [ email: 'testemail@elastic.co', username: 'elastic', }, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, pushed_at: null, pushed_by: null, updated_at: '2019-11-25T22:32:30.608Z', @@ -351,7 +352,7 @@ export const mockCaseComments: Array> = [ email: 'testemail@elastic.co', username: 'elastic', }, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, pushed_at: null, pushed_by: null, rule: { @@ -389,7 +390,7 @@ export const mockCaseComments: Array> = [ email: 'testemail@elastic.co', username: 'elastic', }, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, pushed_at: null, pushed_by: null, rule: { @@ -427,7 +428,7 @@ export const mockCaseComments: Array> = [ email: 'testemail@elastic.co', username: 'elastic', }, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, pushed_at: null, pushed_by: null, rule: { @@ -477,7 +478,7 @@ export const mockCaseConfigure: Array> = email: 'testemail@elastic.co', username: 'elastic', }, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }, references: [], updated_at: '2020-04-09T09:43:51.778Z', @@ -491,7 +492,7 @@ export const mockCaseMappings: Array> = [ id: 'mock-mappings-1', attributes: { mappings: mappings[ConnectorTypes.jira], - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }, references: [], }, @@ -503,7 +504,7 @@ export const mockCaseMappingsResilient: Array> = id: 'mock-mappings-1', attributes: { mappings: mappings[ConnectorTypes.resilient], - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }, references: [], }, @@ -534,7 +535,7 @@ export const mockUserActions: Array> = [ new_value: '{"title":"A case","tags":["case"],"description":"Yeah!","connector":{"id":"connector-od","name":"My Connector","type":".servicenow-sir","fields":{"category":"Denial of Service","destIp":true,"malwareHash":true,"malwareUrl":true,"priority":"2","sourceIp":true,"subcategory":"45"}},"settings":{"syncAlerts":true}}', old_value: null, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }, version: 'WzYsMV0=', references: [], @@ -554,7 +555,7 @@ export const mockUserActions: Array> = [ new_value: '{"type":"alert","alertId":"cec3da90fb37a44407145adf1593f3b0d5ad94c4654201f773d63b5d4706128e","index":".siem-signals-default-000008"}', old_value: null, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }, version: 'WzYsMV0=', references: [], diff --git a/x-pack/plugins/cases/server/routes/api/__mocks__/request_responses.ts b/x-pack/plugins/cases/server/routes/api/__mocks__/request_responses.ts index 32e42fea5c207..f3e6bcd7fc9ff 100644 --- a/x-pack/plugins/cases/server/routes/api/__mocks__/request_responses.ts +++ b/x-pack/plugins/cases/server/routes/api/__mocks__/request_responses.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { CasePostRequest, ConnectorTypes } from '../../../../common/api'; +import { SECURITY_SOLUTION_OWNER, CasePostRequest, ConnectorTypes } from '../../../../common'; export const newCase: CasePostRequest = { title: 'My new case', @@ -20,5 +20,5 @@ export const newCase: CasePostRequest = { settings: { syncAlerts: true, }, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }; diff --git a/x-pack/plugins/cases/server/scripts/sub_cases/index.ts b/x-pack/plugins/cases/server/scripts/sub_cases/index.ts index 2d37916919084..edabe9c4d4a1f 100644 --- a/x-pack/plugins/cases/server/scripts/sub_cases/index.ts +++ b/x-pack/plugins/cases/server/scripts/sub_cases/index.ts @@ -8,7 +8,14 @@ import yargs from 'yargs'; import { ToolingLog } from '@kbn/dev-utils'; import { KbnClient } from '@kbn/test'; -import { CaseResponse, CaseType, CommentType, ConnectorTypes, CASES_URL } from '../../../common'; +import { + CaseResponse, + CaseType, + CommentType, + ConnectorTypes, + CASES_URL, + SECURITY_SOLUTION_OWNER, +} from '../../../common'; import { ActionResult, ActionTypeExecutorResult } from '../../../../actions/common'; import { ContextTypeGeneratedAlertType, createAlertsString } from '../../connectors'; @@ -101,7 +108,7 @@ async function handleGenGroupAlerts(argv: any) { console.log('Case id: ', caseID); const comment: ContextTypeGeneratedAlertType = { - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, type: CommentType.generatedAlert, alerts: createAlertsString( argv.ids.map((id: string) => ({ 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 60fa0e4aafd8e..337c07fa93eab 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 @@ -73,6 +73,7 @@ export const AllCases = React.memo(({ userCanCrud }) => { onClick: goToCreateCase, }, userCanCrud, + owner: [APP_ID], }); }); diff --git a/x-pack/plugins/security_solution/public/cases/components/create/flyout.tsx b/x-pack/plugins/security_solution/public/cases/components/create/flyout.tsx index 0f9f64b32bdd0..1023bfc8b0206 100644 --- a/x-pack/plugins/security_solution/public/cases/components/create/flyout.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/create/flyout.tsx @@ -12,6 +12,7 @@ import { EuiFlyout, EuiFlyoutHeader, EuiTitle, EuiFlyoutBody } from '@elastic/eu import * as i18n from '../../translations'; import { useKibana } from '../../../common/lib/kibana'; import { Case } from '../../../../../cases/common'; +import { APP_ID } from '../../../../common/constants'; export interface CreateCaseModalProps { afterCaseCreated?: (theCase: Case) => Promise; @@ -65,6 +66,7 @@ const CreateCaseFlyoutComponent: React.FC = ({ onCancel: onCloseFlyout, onSuccess, withSteps: false, + owner: [APP_ID], })} diff --git a/x-pack/plugins/security_solution/public/cases/components/create/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/create/index.test.tsx index 2d5faef8aa009..1a6015d1bbd45 100644 --- a/x-pack/plugins/security_solution/public/cases/components/create/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/create/index.test.tsx @@ -17,6 +17,7 @@ import { Create } from '.'; import { useKibana } from '../../../common/lib/kibana'; import { Case } from '../../../../../cases/public/containers/types'; import { basicCase } from '../../../../../cases/public/containers/mock'; +import { APP_ID } from '../../../../common/constants'; jest.mock('../use_insert_timeline'); jest.mock('../../../common/lib/kibana'); @@ -47,6 +48,7 @@ describe('Create case', () => { ); expect(mockCreateCase).toHaveBeenCalled(); + expect(mockCreateCase.mock.calls[0][0].owner).toEqual([APP_ID]); }); it('should redirect to all cases on cancel click', async () => { diff --git a/x-pack/plugins/security_solution/public/cases/components/create/index.tsx b/x-pack/plugins/security_solution/public/cases/components/create/index.tsx index 4a1a64f5fcb41..f946cefd3494c 100644 --- a/x-pack/plugins/security_solution/public/cases/components/create/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/create/index.tsx @@ -13,6 +13,7 @@ import { getCaseDetailsUrl } from '../../../common/components/link_to'; import { useKibana } from '../../../common/lib/kibana'; import * as timelineMarkdownPlugin from '../../../common/components/markdown_editor/plugins/timeline'; import { useInsertTimeline } from '../use_insert_timeline'; +import { APP_ID } from '../../../../common/constants'; export const Create = React.memo(() => { const { cases } = useKibana().services; @@ -43,6 +44,7 @@ export const Create = React.memo(() => { useInsertTimeline, }, }, + owner: [APP_ID], })} ); diff --git a/x-pack/plugins/security_solution/public/cases/components/timeline_actions/add_to_case_action.test.tsx b/x-pack/plugins/security_solution/public/cases/components/timeline_actions/add_to_case_action.test.tsx index fa37fb53a54b0..162758a90b7ba 100644 --- a/x-pack/plugins/security_solution/public/cases/components/timeline_actions/add_to_case_action.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/timeline_actions/add_to_case_action.test.tsx @@ -14,7 +14,7 @@ import { useStateToaster } from '../../../common/components/toasters'; import { TestProviders } from '../../../common/mock'; import { AddToCaseAction } from './add_to_case_action'; import { basicCase } from '../../../../../cases/public/containers/mock'; -import { Case } from '../../../../../cases/common'; +import { Case, SECURITY_SOLUTION_OWNER } from '../../../../../cases/common'; jest.mock('../../../common/lib/kibana'); jest.mock('../../../common/components/link_to', () => { @@ -116,7 +116,7 @@ describe('AddToCaseAction', () => { alertId: 'test-id', index: 'test-index', rule: { id: 'rule-id', name: 'rule-name' }, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }); }); @@ -143,7 +143,7 @@ describe('AddToCaseAction', () => { id: 'rule-id', name: null, }, - owner: 'securitySolution', + owner: SECURITY_SOLUTION_OWNER, }); }); diff --git a/x-pack/plugins/security_solution/public/cases/components/timeline_actions/add_to_case_action.tsx b/x-pack/plugins/security_solution/public/cases/components/timeline_actions/add_to_case_action.tsx index f7594dbb4c180..19c59f2f57d87 100644 --- a/x-pack/plugins/security_solution/public/cases/components/timeline_actions/add_to_case_action.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/timeline_actions/add_to_case_action.tsx @@ -111,8 +111,7 @@ const AddToCaseActionComponent: React.FC = ({ id: rule?.id != null ? rule.id[0] : null, name: rule?.name != null ? rule.name[0] : null, }, - // TODO: refactor - owner: 'securitySolution', + owner: APP_ID, }, updateCase, }); @@ -238,7 +237,7 @@ const AddToCaseActionComponent: React.FC = ({ id: rule?.id != null ? rule.id[0] : null, name: rule?.name != null ? rule.name[0] : null, }, - owner: 'securitySolution', + owner: APP_ID, }, createCaseNavigation: { href: formatUrl(getCreateCaseUrl()), @@ -248,6 +247,7 @@ const AddToCaseActionComponent: React.FC = ({ onRowClick: onCaseClicked, updateCase: onCaseSuccess, userCanCrud: userPermissions?.crud ?? false, + owner: [APP_ID], })} ); diff --git a/x-pack/plugins/security_solution/public/cases/pages/configure_cases.tsx b/x-pack/plugins/security_solution/public/cases/pages/configure_cases.tsx index 3e838f47e6dc2..c735fd5bc8567 100644 --- a/x-pack/plugins/security_solution/public/cases/pages/configure_cases.tsx +++ b/x-pack/plugins/security_solution/public/cases/pages/configure_cases.tsx @@ -19,6 +19,7 @@ import { navTabs } from '../../app/home/home_navigations'; import { CaseHeaderPage } from '../components/case_header_page'; import { WhitePageWrapper, SectionWrapper } from '../components/wrappers'; import * as i18n from './translations'; +import { APP_ID } from '../../../common/constants'; const ConfigureCasesPageComponent: React.FC = () => { const { cases } = useKibana().services; @@ -55,6 +56,7 @@ const ConfigureCasesPageComponent: React.FC = () => { {cases.getConfigureCases({ userCanCrud: userPermissions?.crud ?? false, + owner: [APP_ID], })} diff --git a/x-pack/plugins/security_solution/public/overview/components/recent_cases/index.tsx b/x-pack/plugins/security_solution/public/overview/components/recent_cases/index.tsx index bcf9953d70d83..fc2e2e87ffc5f 100644 --- a/x-pack/plugins/security_solution/public/overview/components/recent_cases/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/recent_cases/index.tsx @@ -58,6 +58,7 @@ const RecentCasesComponent = () => { }, }, maxCasesToShow: MAX_CASES_TO_SHOW, + owner: [APP_ID], }); }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/add_to_case_button/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/add_to_case_button/index.tsx index a4c6fe1e344b3..0f583b838d86c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/add_to_case_button/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/add_to_case_button/index.tsx @@ -177,6 +177,7 @@ const AddToCaseButtonComponent: React.FC = ({ timelineId }) => { }, onRowClick, userCanCrud: userPermissions?.crud ?? false, + owner: [APP_ID], })} );