Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -409,4 +409,32 @@ describe('CaseView ', () => {
wrapper.find('button[data-test-subj="push-to-external-service"]').first().prop('disabled')
).toBeTruthy();
});

it('should revert to the initial connector in case of failure', async () => {
updateCaseProperty.mockImplementation(({ onError }) => {
onError();
});
const wrapper = mount(
<TestProviders>
<Router history={mockHistory}>
<CaseComponent
{...caseProps}
caseData={{ ...caseProps.caseData, connectorId: 'servicenow-1' }}
/>
</Router>
</TestProviders>
);
await wait();
wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click');
wrapper.update();
wrapper.find('button[data-test-subj="dropdown-connector-servicenow-2"]').simulate('click');
wrapper.update();
wrapper.find(`[data-test-subj="edit-connectors-submit"]`).last().simulate('click');
wrapper.update();
await wait();
wrapper.update();
expect(
wrapper.find('[data-test-subj="dropdown-connectors"]').at(0).prop('valueOfSelected')
).toBe('servicenow-1');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ interface Props {
userCanCrud: boolean;
}

export interface OnUpdateFields {
key: keyof Case;
value: Case[keyof Case];
onSuccess?: () => void;
onError?: () => void;
}

const MyWrapper = styled.div`
padding: ${({
theme,
Expand Down Expand Up @@ -88,65 +95,75 @@ export const CaseComponent = React.memo<CaseProps>(

// Update Fields
const onUpdateField = useCallback(
(newUpdateKey: keyof Case, updateValue: Case[keyof Case]) => {
({ key, value, onSuccess, onError }: OnUpdateFields) => {
const handleUpdateNewCase = (newCase: Case) =>
updateCase({ ...newCase, comments: caseData.comments });
switch (newUpdateKey) {
switch (key) {
case 'title':
const titleUpdate = getTypedPayload<string>(updateValue);
const titleUpdate = getTypedPayload<string>(value);
if (titleUpdate.length > 0) {
updateCaseProperty({
fetchCaseUserActions,
updateKey: 'title',
updateValue: titleUpdate,
updateCase: handleUpdateNewCase,
version: caseData.version,
onSuccess,
onError,
});
}
break;
case 'connectorId':
const connectorId = getTypedPayload<string>(updateValue);
const connectorId = getTypedPayload<string>(value);
if (connectorId.length > 0) {
updateCaseProperty({
fetchCaseUserActions,
updateKey: 'connector_id',
updateValue: connectorId,
updateCase: handleUpdateNewCase,
version: caseData.version,
onSuccess,
onError,
});
}
break;
case 'description':
const descriptionUpdate = getTypedPayload<string>(updateValue);
const descriptionUpdate = getTypedPayload<string>(value);
if (descriptionUpdate.length > 0) {
updateCaseProperty({
fetchCaseUserActions,
updateKey: 'description',
updateValue: descriptionUpdate,
updateCase: handleUpdateNewCase,
version: caseData.version,
onSuccess,
onError,
});
}
break;
case 'tags':
const tagsUpdate = getTypedPayload<string[]>(updateValue);
const tagsUpdate = getTypedPayload<string[]>(value);
updateCaseProperty({
fetchCaseUserActions,
updateKey: 'tags',
updateValue: tagsUpdate,
updateCase: handleUpdateNewCase,
version: caseData.version,
onSuccess,
onError,
});
break;
case 'status':
const statusUpdate = getTypedPayload<string>(updateValue);
if (caseData.status !== updateValue) {
const statusUpdate = getTypedPayload<string>(value);
if (caseData.status !== value) {
updateCaseProperty({
fetchCaseUserActions,
updateKey: 'status',
updateValue: statusUpdate,
updateCase: handleUpdateNewCase,
version: caseData.version,
onSuccess,
onError,
});
}
default:
Expand Down Expand Up @@ -191,15 +208,28 @@ export const CaseComponent = React.memo<CaseProps>(
});

const onSubmitConnector = useCallback(
(connectorId) => onUpdateField('connectorId', connectorId),
(connectorId, onSuccess, onError) =>
onUpdateField({
key: 'connectorId',
value: connectorId,
onSuccess,
onError,
}),
[onUpdateField]
);
const onSubmitTags = useCallback((newTags) => onUpdateField('tags', newTags), [onUpdateField]);
const onSubmitTitle = useCallback((newTitle) => onUpdateField('title', newTitle), [
const onSubmitTags = useCallback((newTags) => onUpdateField({ key: 'tags', value: newTags }), [
onUpdateField,
]);
const onSubmitTitle = useCallback(
(newTitle) => onUpdateField({ key: 'title', value: newTitle }),
[onUpdateField]
);
const toggleStatusCase = useCallback(
(e) => onUpdateField('status', e.target.checked ? 'closed' : 'open'),
(e) =>
onUpdateField({
key: 'status',
value: e.target.checked ? 'closed' : 'open',
}),
[onUpdateField]
);
const handleRefresh = useCallback(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,35 @@ describe('EditConnector ', () => {
await act(async () => {
wrapper.find(`[data-test-subj="edit-connectors-submit"]`).last().simulate('click');
await wait();
expect(onSubmit).toBeCalledWith(sampleConnector);
expect(onSubmit.mock.calls[0][0]).toBe(sampleConnector);
});
});

it('Revert to initial external service on error', async () => {
onSubmit.mockImplementation((connector, onSuccess, onError) => {
onError(new Error('An error has occurred'));
});
const wrapper = mount(
<TestProviders>
<EditConnector {...defaultProps} />
</TestProviders>
);

wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click');
wrapper.update();
wrapper.find('button[data-test-subj="dropdown-connector-servicenow-2"]').simulate('click');
wrapper.update();

expect(wrapper.find(`[data-test-subj="edit-connectors-submit"]`).last().exists()).toBeTruthy();

await act(async () => {
wrapper.find(`[data-test-subj="edit-connectors-submit"]`).last().simulate('click');
await wait();
wrapper.update();
});
expect(formHookMock.setFieldValue).toHaveBeenCalledWith('connector', 'none');
});

it('Resets selector on cancel', async () => {
const props = {
...defaultProps,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {
EuiLoadingSpinner,
} from '@elastic/eui';
import styled, { css } from 'styled-components';
import { noop } from 'lodash/fp';

import * as i18n from '../../translations';
import { Form, UseField, useForm } from '../../../shared_imports';
import { schema } from './schema';
Expand All @@ -25,7 +27,7 @@ interface EditConnectorProps {
connectors: Connector[];
disabled?: boolean;
isLoading: boolean;
onSubmit: (a: string[]) => void;
onSubmit: (a: string[], onSuccess: () => void, onError: () => void) => void;
selectedConnector: string;
}

Expand Down Expand Up @@ -61,6 +63,11 @@ export const EditConnector = React.memo(
[selectedConnector]
);

const onError = useCallback(() => {
setFieldValue('connector', selectedConnector);
setConnectorHasChanged(false);
}, [setFieldValue, selectedConnector]);

const onCancelConnector = useCallback(() => {
setFieldValue('connector', selectedConnector);
setConnectorHasChanged(false);
Expand All @@ -69,10 +76,10 @@ export const EditConnector = React.memo(
const onSubmitConnector = useCallback(async () => {
const { isValid, data: newData } = await submit();
if (isValid && newData.connector) {
onSubmit(newData.connector);
onSubmit(newData.connector, noop, onError);
setConnectorHasChanged(false);
}
}, [submit, onSubmit]);
}, [submit, onSubmit, onError]);

return (
<EuiText>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ describe('UserActionTree ', () => {
)
.exists()
).toEqual(false);
expect(onUpdateField).toBeCalledWith('description', sampleData.content);
expect(onUpdateField).toBeCalledWith({ key: 'description', value: sampleData.content });
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { UserActionMarkdown } from './user_action_markdown';
import { Connector } from '../../../../../case/common/api/cases';
import { CaseServices } from '../../containers/use_get_case_user_actions';
import { parseString } from '../../containers/utils';
import { OnUpdateFields } from '../case_view';

export interface UserActionTreeProps {
caseServices: CaseServices;
Expand All @@ -30,7 +31,7 @@ export interface UserActionTreeProps {
fetchUserActions: () => void;
isLoadingDescription: boolean;
isLoadingUserActions: boolean;
onUpdateField: (updateKey: keyof Case, updateValue: string | string[]) => void;
onUpdateField: ({ key, value, onSuccess, onError }: OnUpdateFields) => void;
updateCase: (newCase: Case) => void;
userCanCrud: boolean;
}
Expand Down Expand Up @@ -138,7 +139,7 @@ export const UserActionTree = React.memo(
content={caseData.description}
isEditable={manageMarkdownEditIds.includes(DESCRIPTION_ID)}
onSaveContent={(content: string) => {
onUpdateField(DESCRIPTION_ID, content);
onUpdateField({ key: DESCRIPTION_ID, value: content });
}}
onChangeEditable={handleManageMarkdownEditId}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,17 @@ describe('useUpdateCase', () => {
const fetchCaseUserActions = jest.fn();
const updateCase = jest.fn();
const updateKey: UpdateKey = 'description';
const onSuccess = jest.fn();
const onError = jest.fn();

const sampleUpdate = {
fetchCaseUserActions,
updateKey,
updateValue: 'updated description',
updateCase,
version: basicCase.version,
onSuccess,
onError,
};
beforeEach(() => {
jest.clearAllMocks();
Expand Down Expand Up @@ -79,6 +84,7 @@ describe('useUpdateCase', () => {
});
expect(fetchCaseUserActions).toBeCalledWith(basicCase.id);
expect(updateCase).toBeCalledWith(basicCase);
expect(onSuccess).toHaveBeenCalled();
});
});

Expand Down Expand Up @@ -114,6 +120,7 @@ describe('useUpdateCase', () => {
isError: true,
updateCaseProperty: result.current.updateCaseProperty,
});
expect(onError).toHaveBeenCalled();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

import { useReducer, useCallback } from 'react';

import {
displaySuccessToast,
errorToToaster,
Expand Down Expand Up @@ -33,6 +34,8 @@ export interface UpdateByKey {
fetchCaseUserActions?: (caseId: string) => void;
updateCase?: (newCase: Case) => void;
version: string;
onSuccess?: () => void;
onError?: () => void;
}

type Action =
Expand Down Expand Up @@ -81,7 +84,15 @@ export const useUpdateCase = ({ caseId }: { caseId: string }): UseUpdateCase =>
const [, dispatchToaster] = useStateToaster();

const dispatchUpdateCaseProperty = useCallback(
async ({ fetchCaseUserActions, updateKey, updateValue, updateCase, version }: UpdateByKey) => {
async ({
fetchCaseUserActions,
updateKey,
updateValue,
updateCase,
version,
onSuccess,
onError,
}: UpdateByKey) => {
let cancel = false;
const abortCtrl = new AbortController();

Expand All @@ -102,6 +113,9 @@ export const useUpdateCase = ({ caseId }: { caseId: string }): UseUpdateCase =>
}
dispatch({ type: 'FETCH_SUCCESS' });
displaySuccessToast(i18n.UPDATED_CASE(response[0].title), dispatchToaster);
if (onSuccess) {
onSuccess();
}
}
} catch (error) {
if (!cancel) {
Expand All @@ -111,6 +125,9 @@ export const useUpdateCase = ({ caseId }: { caseId: string }): UseUpdateCase =>
dispatchToaster,
});
dispatch({ type: 'FETCH_FAILURE' });
if (onError) {
onError();
}
}
}
return () => {
Expand Down
Loading