Skip to content

Commit

Permalink
[SIEM] [Cases] External services not getting all comments bug fix (#6…
Browse files Browse the repository at this point in the history
  • Loading branch information
stephmilovic authored May 6, 2020
1 parent c00b36e commit c03bdcc
Show file tree
Hide file tree
Showing 16 changed files with 260 additions and 58 deletions.
18 changes: 5 additions & 13 deletions x-pack/plugins/siem/public/containers/case/translations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,11 @@ export const REOPENED_CASES = ({
defaultMessage: 'Reopened {totalCases, plural, =1 {"{caseTitle}"} other {{totalCases} cases}}',
});

export const TAG_FETCH_FAILURE = i18n.translate(
'xpack.siem.containers.case.tagFetchFailDescription',
{
defaultMessage: 'Failed to fetch Tags',
}
);

export const SUCCESS_SEND_TO_EXTERNAL_SERVICE = i18n.translate(
'xpack.siem.containers.case.pushToExterService',
{
defaultMessage: 'Successfully sent to ServiceNow',
}
);
export const SUCCESS_SEND_TO_EXTERNAL_SERVICE = (serviceName: string) =>
i18n.translate('xpack.siem.containers.case.pushToExternalService', {
values: { serviceName },
defaultMessage: 'Successfully sent to { serviceName }',
});

export const ERROR_PUSH_TO_SERVICE = i18n.translate(
'xpack.siem.case.configure.errorPushingToService',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,14 @@ describe('useGetCaseUserActions', () => {
...basicPush,
firstPushIndex: 3,
lastPushIndex: 3,
commentsToUpdate: [],
hasDataToPush: false,
},
},
});
});

it('Correctly marks first/last index - hasDataToPush: true', () => {
it('Correctly marks first/last index and comment id - hasDataToPush: true', () => {
const userActions = [
...caseUserActions,
getUserAction(['pushed'], 'push-to-service'),
Expand All @@ -142,6 +143,83 @@ describe('useGetCaseUserActions', () => {
...basicPush,
firstPushIndex: 3,
lastPushIndex: 3,
commentsToUpdate: [userActions[userActions.length - 1].commentId],
hasDataToPush: true,
},
},
});
});

it('Correctly marks first/last index and multiple comment ids, both needs push', () => {
const userActions = [
...caseUserActions,
getUserAction(['pushed'], 'push-to-service'),
getUserAction(['comment'], 'create'),
{ ...getUserAction(['comment'], 'create'), commentId: 'muahaha' },
];
const result = getPushedInfo(userActions, '123');
expect(result).toEqual({
hasDataToPush: true,
caseServices: {
'123': {
...basicPush,
firstPushIndex: 3,
lastPushIndex: 3,
commentsToUpdate: [
userActions[userActions.length - 2].commentId,
userActions[userActions.length - 1].commentId,
],
hasDataToPush: true,
},
},
});
});

it('Correctly marks first/last index and multiple comment ids, one needs push', () => {
const userActions = [
...caseUserActions,
getUserAction(['pushed'], 'push-to-service'),
getUserAction(['comment'], 'create'),
getUserAction(['pushed'], 'push-to-service'),
{ ...getUserAction(['comment'], 'create'), commentId: 'muahaha' },
];
const result = getPushedInfo(userActions, '123');
expect(result).toEqual({
hasDataToPush: true,
caseServices: {
'123': {
...basicPush,
firstPushIndex: 3,
lastPushIndex: 5,
commentsToUpdate: [userActions[userActions.length - 1].commentId],
hasDataToPush: true,
},
},
});
});

it('Correctly marks first/last index and multiple comment ids, one needs push and one needs update', () => {
const userActions = [
...caseUserActions,
getUserAction(['pushed'], 'push-to-service'),
getUserAction(['comment'], 'create'),
getUserAction(['pushed'], 'push-to-service'),
{ ...getUserAction(['comment'], 'create'), commentId: 'muahaha' },
getUserAction(['comment'], 'update'),
getUserAction(['comment'], 'update'),
];
const result = getPushedInfo(userActions, '123');
expect(result).toEqual({
hasDataToPush: true,
caseServices: {
'123': {
...basicPush,
firstPushIndex: 3,
lastPushIndex: 5,
commentsToUpdate: [
userActions[userActions.length - 3].commentId,
userActions[userActions.length - 1].commentId,
],
hasDataToPush: true,
},
},
Expand All @@ -162,6 +240,7 @@ describe('useGetCaseUserActions', () => {
...basicPush,
firstPushIndex: 3,
lastPushIndex: 3,
commentsToUpdate: [],
hasDataToPush: false,
},
},
Expand All @@ -182,11 +261,34 @@ describe('useGetCaseUserActions', () => {
...basicPush,
firstPushIndex: 3,
lastPushIndex: 5,
commentsToUpdate: [],
hasDataToPush: false,
},
},
});
});
it('Correctly handles comment update with multiple push actions', () => {
const userActions = [
...caseUserActions,
getUserAction(['pushed'], 'push-to-service'),
getUserAction(['comment'], 'create'),
getUserAction(['pushed'], 'push-to-service'),
getUserAction(['comment'], 'update'),
];
const result = getPushedInfo(userActions, '123');
expect(result).toEqual({
hasDataToPush: true,
caseServices: {
'123': {
...basicPush,
firstPushIndex: 3,
lastPushIndex: 5,
commentsToUpdate: [userActions[userActions.length - 1].commentId],
hasDataToPush: true,
},
},
});
});

it('Multiple connector tracking - hasDataToPush: true', () => {
const pushAction123 = getUserAction(['pushed'], 'push-to-service');
Expand Down Expand Up @@ -215,6 +317,7 @@ describe('useGetCaseUserActions', () => {
...basicPush,
firstPushIndex: 3,
lastPushIndex: 3,
commentsToUpdate: [userActions[userActions.length - 2].commentId],
hasDataToPush: true,
},
'456': {
Expand All @@ -224,6 +327,7 @@ describe('useGetCaseUserActions', () => {
externalId: 'other_external_id',
firstPushIndex: 5,
lastPushIndex: 5,
commentsToUpdate: [],
hasDataToPush: false,
},
},
Expand Down Expand Up @@ -257,6 +361,7 @@ describe('useGetCaseUserActions', () => {
...basicPush,
firstPushIndex: 3,
lastPushIndex: 3,
commentsToUpdate: [userActions[userActions.length - 2].commentId],
hasDataToPush: true,
},
'456': {
Expand All @@ -266,6 +371,7 @@ describe('useGetCaseUserActions', () => {
externalId: 'other_external_id',
firstPushIndex: 5,
lastPushIndex: 5,
commentsToUpdate: [],
hasDataToPush: false,
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ import { CaseExternalService, CaseUserActions, ElasticUser } from './types';
import { convertToCamelCase, parseString } from './utils';
import { CaseFullExternalService } from '../../../../case/common/api/cases';

interface CaseService extends CaseExternalService {
export interface CaseService extends CaseExternalService {
firstPushIndex: number;
lastPushIndex: number;
commentsToUpdate: string[];
hasDataToPush: boolean;
}

Expand Down Expand Up @@ -48,6 +49,10 @@ export interface UseGetCaseUserActions extends CaseUserActionsState {

const getExternalService = (value: string): CaseExternalService | null =>
convertToCamelCase<CaseFullExternalService, CaseExternalService>(parseString(`${value}`));
interface CommentsAndIndex {
commentId: string;
commentIndex: number;
}

export const getPushedInfo = (
caseUserActions: CaseUserActions[],
Expand All @@ -69,11 +74,25 @@ export const getPushedInfo = (
.action !== 'push-to-service'
);
};
const commentsAndIndex = caseUserActions.reduce<CommentsAndIndex[]>(
(bacc, mua, index) =>
mua.actionField[0] === 'comment' && mua.commentId != null
? [
...bacc,
{
commentId: mua.commentId,
commentIndex: index,
},
]
: bacc,
[]
);

const caseServices = caseUserActions.reduce<CaseServices>((acc, cua, i) => {
let caseServices = caseUserActions.reduce<CaseServices>((acc, cua, i) => {
if (cua.action !== 'push-to-service') {
return acc;
}

const externalService = getExternalService(`${cua.newValue}`);
if (externalService === null) {
return acc;
Expand All @@ -87,6 +106,7 @@ export const getPushedInfo = (
...acc[externalService.connectorId],
...externalService,
lastPushIndex: i,
commentsToUpdate: [],
},
}
: {
Expand All @@ -95,11 +115,31 @@ export const getPushedInfo = (
firstPushIndex: i,
lastPushIndex: i,
hasDataToPush: hasDataToPushForConnector(externalService.connectorId),
commentsToUpdate: [],
},
}),
};
}, {});

caseServices = Object.keys(caseServices).reduce<CaseServices>((acc, key) => {
return {
...acc,
[key]: {
...caseServices[key],
// if the comment happens after the lastUpdateToCaseIndex, it should be included in commentsToUpdate
commentsToUpdate: commentsAndIndex.reduce<string[]>(
(bacc, currentComment) =>
currentComment.commentIndex > caseServices[key].lastPushIndex
? bacc.indexOf(currentComment.commentId) > -1
? [...bacc.filter(e => e !== currentComment.commentId), currentComment.commentId]
: [...bacc, currentComment.commentId]
: bacc,
[]
),
},
};
}, {});

const hasDataToPush =
caseServices[caseConnectorId] != null ? caseServices[caseConnectorId].hasDataToPush : true;
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
serviceConnectorUser,
} from './mock';
import * as api from './api';
import { CaseServices } from './use_get_case_user_actions';

jest.mock('./api');

Expand All @@ -32,6 +33,7 @@ describe('usePostPushToService', () => {
...basicPush,
firstPushIndex: 1,
lastPushIndex: 1,
commentsToUpdate: [basicComment.id],
hasDataToPush: false,
},
},
Expand Down Expand Up @@ -64,13 +66,15 @@ describe('usePostPushToService', () => {
...basicPush,
firstPushIndex: 1,
lastPushIndex: 1,
commentsToUpdate: [basicComment.id],
hasDataToPush: true,
},
'456': {
...basicPush,
connectorId: '456',
externalId: 'other_external_id',
firstPushIndex: 4,
commentsToUpdate: [basicComment.id],
lastPushIndex: 6,
hasDataToPush: false,
},
Expand Down Expand Up @@ -127,6 +131,31 @@ describe('usePostPushToService', () => {
await waitForNextUpdate();
expect(spyOnPushToService).toBeCalledWith(
samplePush.connectorId,
formatServiceRequestData(basicCase, '123', sampleCaseServices as CaseServices),
abortCtrl.signal
);
});
});

it('calls pushToService with correct arguments when no push history', async () => {
const samplePush2 = {
caseId: pushedCase.id,
caseServices: {},
connectorName: 'connector name',
connectorId: 'none',
updateCase,
};
const spyOnPushToService = jest.spyOn(api, 'pushToService');

await act(async () => {
const { result, waitForNextUpdate } = renderHook<string, UsePostPushToService>(() =>
usePostPushToService()
);
await waitForNextUpdate();
result.current.postPushToService(samplePush2);
await waitForNextUpdate();
expect(spyOnPushToService).toBeCalledWith(
samplePush2.connectorId,
formatServiceRequestData(basicCase, 'none', {}),
abortCtrl.signal
);
Expand Down
Loading

0 comments on commit c03bdcc

Please sign in to comment.