Skip to content

Commit

Permalink
[SIEM] [Cases] Shell scripts and unit tests (#60183)
Browse files Browse the repository at this point in the history
  • Loading branch information
stephmilovic authored Mar 18, 2020
1 parent 923de46 commit 4fc89ae
Show file tree
Hide file tree
Showing 31 changed files with 801 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -231,10 +231,7 @@ export const AllCases = React.memo(() => {
sort: { field: queryParams.sortField, direction: queryParams.sortOrder },
};
const euiBasicTableSelectionProps = useMemo<EuiTableSelectionType<Case>>(
() => ({
selectable: (item: Case) => true,
onSelectionChange: setSelectedCases,
}),
() => ({ onSelectionChange: setSelectedCases }),
[selectedCases]
);
const isCasesLoading = useMemo(
Expand Down Expand Up @@ -305,6 +302,7 @@ export const AllCases = React.memo(() => {
{i18n.SHOWING_SELECTED_CASES(selectedCases.length)}
</UtilityBarText>
<UtilityBarAction
data-test-subj="case-table-bulk-actions"
iconSide="right"
iconType="arrowDown"
popoverContent={getBulkItemsPopoverContent}
Expand All @@ -316,6 +314,7 @@ export const AllCases = React.memo(() => {
</UtilityBar>
<EuiBasicTable
columns={memoizedGetCasesColumns}
data-test-subj="all-cases-table"
isSelectable
itemId="id"
items={data.cases}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export const getBulkItems = ({
<EuiContextMenuItem
key={i18n.BULK_ACTION_DELETE_SELECTED}
icon="trash"
disabled={selectedCaseIds.length === 0}
onClick={async () => {
closePopover();
deleteCasesAction(selectedCaseIds);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,30 @@
import React from 'react';
import { mount } from 'enzyme';
import { CaseComponent } from './';
import * as apiHook from '../../../../containers/case/use_update_case';
import * as updateHook from '../../../../containers/case/use_update_case';
import * as deleteHook from '../../../../containers/case/use_delete_cases';
import { caseProps, data } from './__mock__';
import { TestProviders } from '../../../../mock';

describe('CaseView ', () => {
const handleOnDeleteConfirm = jest.fn();
const handleToggleModal = jest.fn();
const dispatchResetIsDeleted = jest.fn();
const updateCaseProperty = jest.fn();
/* eslint-disable no-console */
// Silence until enzyme fixed to use ReactTestUtils.act()
const originalError = console.error;
beforeAll(() => {
console.error = jest.fn();
});
afterAll(() => {
console.error = originalError;
});
/* eslint-enable no-console */

beforeEach(() => {
jest.resetAllMocks();
jest.spyOn(apiHook, 'useUpdateCase').mockReturnValue({
jest.spyOn(updateHook, 'useUpdateCase').mockReturnValue({
caseData: data,
isLoading: false,
isError: false,
Expand Down Expand Up @@ -119,4 +133,46 @@ describe('CaseView ', () => {
.prop('source')
).toEqual(data.comments[0].comment);
});

it('toggle delete modal and cancel', () => {
const wrapper = mount(
<TestProviders>
<CaseComponent {...caseProps} />
</TestProviders>
);

expect(wrapper.find('[data-test-subj="confirm-delete-case-modal"]').exists()).toBeFalsy();

wrapper
.find(
'[data-test-subj="case-view-actions"] button[data-test-subj="property-actions-ellipses"]'
)
.first()
.simulate('click');
wrapper.find('button[data-test-subj="property-actions-trash"]').simulate('click');
expect(wrapper.find('[data-test-subj="confirm-delete-case-modal"]').exists()).toBeTruthy();
wrapper.find('button[data-test-subj="confirmModalCancelButton"]').simulate('click');
expect(wrapper.find('[data-test-subj="confirm-delete-case-modal"]').exists()).toBeFalsy();
});

it('toggle delete modal and confirm', () => {
jest.spyOn(deleteHook, 'useDeleteCases').mockReturnValue({
dispatchResetIsDeleted,
handleToggleModal,
handleOnDeleteConfirm,
isLoading: false,
isError: false,
isDeleted: false,
isDisplayConfirmDeleteModal: true,
});
const wrapper = mount(
<TestProviders>
<CaseComponent {...caseProps} />
</TestProviders>
);

expect(wrapper.find('[data-test-subj="confirm-delete-case-modal"]').exists()).toBeTruthy();
wrapper.find('button[data-test-subj="confirmModalConfirmButton"]').simulate('click');
expect(handleOnDeleteConfirm.mock.calls[0][0]).toEqual([caseProps.caseId]);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ export const CaseComponent = React.memo<CaseProps>(({ caseId, initialData }) =>
onChange={toggleStatusCase}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFlexItem grow={false} data-test-subj="case-view-actions">
<PropertyActions propertyActions={propertyActions} />
</EuiFlexItem>
</EuiFlexGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const ConfirmDeleteCaseModalComp: React.FC<ConfirmDeleteCaseModalProps> = ({
buttonColor="danger"
cancelButtonText={i18n.CANCEL}
confirmButtonText={isPlural ? i18n.DELETE_CASES : i18n.DELETE_CASE}
data-test-subj="confirm-delete-case-modal"
defaultFocusedButton="confirm"
onCancel={onCancel}
onConfirm={onConfirm}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ export const Create = React.memo(() => {
}
}, [form]);

const handleSetIsCancel = useCallback(() => {
setIsCancel(true);
}, [isCancel]);

if (caseData != null && caseData.id) {
return <Redirect to={`/${SiemPageName.case}/${caseData.id}`} />;
}
Expand Down Expand Up @@ -137,7 +141,12 @@ export const Create = React.memo(() => {
responsive={false}
>
<EuiFlexItem grow={false}>
<EuiButtonEmpty size="s" onClick={() => setIsCancel(true)} iconType="cross">
<EuiButtonEmpty
data-test-subj="create-case-cancel"
size="s"
onClick={handleSetIsCancel}
iconType="cross"
>
{i18n.CANCEL}
</EuiButtonEmpty>
</EuiFlexItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ export interface PropertyActionButtonProps {
label: string;
}

const ComponentId = 'property-actions';

const PropertyActionButton = React.memo<PropertyActionButtonProps>(
({ onClick, iconType, label }) => (
<EuiButtonEmpty
data-test-subj={`${ComponentId}-${iconType}`}
aria-label={label}
color="text"
iconSide="left"
Expand Down Expand Up @@ -48,13 +51,13 @@ export const PropertyActions = React.memo<PropertyActionsProps>(({ propertyActio
}, []);

return (
<EuiFlexGroup alignItems="flexStart" data-test-subj="properties-right" gutterSize="none">
<EuiFlexGroup alignItems="flexStart" data-test-subj={ComponentId} gutterSize="none">
<EuiFlexItem grow={false}>
<EuiPopover
anchorPosition="downRight"
button={
<EuiButtonIcon
data-test-subj="ellipses"
data-test-subj={`${ComponentId}-ellipses`}
aria-label="Actions"
iconType="boxesHorizontal"
onClick={onButtonClick}
Expand All @@ -64,7 +67,12 @@ export const PropertyActions = React.memo<PropertyActionsProps>(({ propertyActio
isOpen={showActions}
closePopover={onClosePopover}
>
<EuiFlexGroup alignItems="flexStart" direction="column" gutterSize="none">
<EuiFlexGroup
alignItems="flexStart"
data-test-subj={`${ComponentId}-group`}
direction="column"
gutterSize="none"
>
{propertyActions.map((action, key) => (
<EuiFlexItem grow={false} key={`${action.label}${key}`}>
<PropertyActionButton
Expand Down
90 changes: 90 additions & 0 deletions x-pack/plugins/case/server/scripts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
README.md for developers working on the Case API on how to get started
using the CURL scripts in the scripts folder.

The scripts rely on CURL and jq:

- [CURL](https://curl.haxx.se)
- [jq](https://stedolan.github.io/jq/)

Install curl and jq

```sh
brew update
brew install curl
brew install jq
```

Open `$HOME/.zshrc` or `${HOME}.bashrc` depending on your SHELL output from `echo $SHELL`
and add these environment variables:

```sh
export ELASTICSEARCH_USERNAME=${user}
export ELASTICSEARCH_PASSWORD=${password}
export ELASTICSEARCH_URL=https://${ip}:9200
export KIBANA_URL=http://localhost:5601
export TASK_MANAGER_INDEX=.kibana-task-manager-${your user id}
export KIBANA_INDEX=.kibana-${your user id}
```

source `$HOME/.zshrc` or `${HOME}.bashrc` to ensure variables are set:

```sh
source ~/.zshrc
```

Restart Kibana and ensure that you are using `--no-base-path` as changing the base path is a feature but will
get in the way of the CURL scripts written as is.

Go to the scripts folder `cd kibana/x-pack/plugins/case/server/scripts` and run:

```sh
./hard_reset.sh
```

which will:

- Delete any existing cases you have
- Delete any existing comments you have
- Posts the sample case from `./mock/case/post_case.json`
- Posts the sample comment from `./mock/comment/post_comment.json` to the new case

Now you can run

```sh
./find_cases.sh
```

You should see the new case created like so:

```sh
{
"page": 1,
"per_page": 20,
"total": 1,
"cases": [
{
"id": "2e0afbc0-658c-11ea-85c8-1d8f792cbc08",
"version": "Wzc5NSwxXQ==",
"comments": [],
"comment_ids": [
"2ecec0f0-658c-11ea-85c8-1d8f792cbc08"
],
"created_at": "2020-03-14T00:38:53.004Z",
"created_by": {
"full_name": "Steph Milovic",
"username": "smilovic"
},
"updated_at": null,
"updated_by": null,
"description": "This looks not so good",
"title": "Bad meanie defacing data",
"status": "open",
"tags": [
"defacement"
]
}
],
"count_open_cases": 1,
"count_closed_cases": 1
}
```
41 changes: 41 additions & 0 deletions x-pack/plugins/case/server/scripts/check_env_variables.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/bin/sh

#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#

# Add this to the start of any scripts to detect if env variables are set

set -e

if [ -z "${ELASTICSEARCH_USERNAME}" ]; then
echo "Set ELASTICSEARCH_USERNAME in your environment"
exit 1
fi

if [ -z "${ELASTICSEARCH_PASSWORD}" ]; then
echo "Set ELASTICSEARCH_PASSWORD in your environment"
exit 1
fi

if [ -z "${ELASTICSEARCH_URL}" ]; then
echo "Set ELASTICSEARCH_URL in your environment"
exit 1
fi

if [ -z "${KIBANA_URL}" ]; then
echo "Set KIBANA_URL in your environment"
exit 1
fi

if [ -z "${TASK_MANAGER_INDEX}" ]; then
echo "Set TASK_MANAGER_INDEX in your environment"
exit 1
fi

if [ -z "${KIBANA_INDEX}" ]; then
echo "Set KIBANA_INDEX in your environment"
exit 1
fi
51 changes: 51 additions & 0 deletions x-pack/plugins/case/server/scripts/delete_cases.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/bin/sh

#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#

# Creates a new case and then gets it if no CASE_ID is specified

# Example:
# ./delete_cases.sh

# Example with CASE_ID args:
# ./delete_cases.sh 1234-example-id 5678-example-id

set -e
./check_env_variables.sh

if [ "$1" ]; then
ALL=("$@")
i=0

COUNT=${#ALL[@]}
IDS=""
for ID in "${ALL[@]}"
do
let i=i+1
if [ $i -eq $COUNT ]; then
IDS+="%22${ID}%22"
else
IDS+="%22${ID}%22,"
fi
done

curl -s -k \
-H 'kbn-xsrf: 123' \
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
-X DELETE "${KIBANA_URL}${SPACE_URL}/api/cases?ids=\[${IDS}\]" \
| jq .;
exit 1
else
CASE_ID=("$(./generate_case_data.sh | jq '.id' -j)")
curl -s -k \
-H 'Content-Type: application/json' \
-H 'kbn-xsrf: 123' \
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
-X DELETE "${KIBANA_URL}${SPACE_URL}/api/cases?ids=\[%22${CASE_ID}%22\]" \
| jq .;
exit 1
fi
Loading

0 comments on commit 4fc89ae

Please sign in to comment.