Skip to content

Commit ec598ea

Browse files
authored
[SIEM] [Cases] External services not getting all comments bug fix (#65307) (#65588)
1 parent 7925abf commit ec598ea

File tree

16 files changed

+260
-58
lines changed

16 files changed

+260
-58
lines changed

x-pack/plugins/siem/public/containers/case/translations.ts

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,11 @@ export const REOPENED_CASES = ({
5050
defaultMessage: 'Reopened {totalCases, plural, =1 {"{caseTitle}"} other {{totalCases} cases}}',
5151
});
5252

53-
export const TAG_FETCH_FAILURE = i18n.translate(
54-
'xpack.siem.containers.case.tagFetchFailDescription',
55-
{
56-
defaultMessage: 'Failed to fetch Tags',
57-
}
58-
);
59-
60-
export const SUCCESS_SEND_TO_EXTERNAL_SERVICE = i18n.translate(
61-
'xpack.siem.containers.case.pushToExterService',
62-
{
63-
defaultMessage: 'Successfully sent to ServiceNow',
64-
}
65-
);
53+
export const SUCCESS_SEND_TO_EXTERNAL_SERVICE = (serviceName: string) =>
54+
i18n.translate('xpack.siem.containers.case.pushToExternalService', {
55+
values: { serviceName },
56+
defaultMessage: 'Successfully sent to { serviceName }',
57+
});
6658

6759
export const ERROR_PUSH_TO_SERVICE = i18n.translate(
6860
'xpack.siem.case.configure.errorPushingToService',

x-pack/plugins/siem/public/containers/case/use_get_case_user_actions.test.tsx

Lines changed: 107 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,13 +122,14 @@ describe('useGetCaseUserActions', () => {
122122
...basicPush,
123123
firstPushIndex: 3,
124124
lastPushIndex: 3,
125+
commentsToUpdate: [],
125126
hasDataToPush: false,
126127
},
127128
},
128129
});
129130
});
130131

131-
it('Correctly marks first/last index - hasDataToPush: true', () => {
132+
it('Correctly marks first/last index and comment id - hasDataToPush: true', () => {
132133
const userActions = [
133134
...caseUserActions,
134135
getUserAction(['pushed'], 'push-to-service'),
@@ -142,6 +143,83 @@ describe('useGetCaseUserActions', () => {
142143
...basicPush,
143144
firstPushIndex: 3,
144145
lastPushIndex: 3,
146+
commentsToUpdate: [userActions[userActions.length - 1].commentId],
147+
hasDataToPush: true,
148+
},
149+
},
150+
});
151+
});
152+
153+
it('Correctly marks first/last index and multiple comment ids, both needs push', () => {
154+
const userActions = [
155+
...caseUserActions,
156+
getUserAction(['pushed'], 'push-to-service'),
157+
getUserAction(['comment'], 'create'),
158+
{ ...getUserAction(['comment'], 'create'), commentId: 'muahaha' },
159+
];
160+
const result = getPushedInfo(userActions, '123');
161+
expect(result).toEqual({
162+
hasDataToPush: true,
163+
caseServices: {
164+
'123': {
165+
...basicPush,
166+
firstPushIndex: 3,
167+
lastPushIndex: 3,
168+
commentsToUpdate: [
169+
userActions[userActions.length - 2].commentId,
170+
userActions[userActions.length - 1].commentId,
171+
],
172+
hasDataToPush: true,
173+
},
174+
},
175+
});
176+
});
177+
178+
it('Correctly marks first/last index and multiple comment ids, one needs push', () => {
179+
const userActions = [
180+
...caseUserActions,
181+
getUserAction(['pushed'], 'push-to-service'),
182+
getUserAction(['comment'], 'create'),
183+
getUserAction(['pushed'], 'push-to-service'),
184+
{ ...getUserAction(['comment'], 'create'), commentId: 'muahaha' },
185+
];
186+
const result = getPushedInfo(userActions, '123');
187+
expect(result).toEqual({
188+
hasDataToPush: true,
189+
caseServices: {
190+
'123': {
191+
...basicPush,
192+
firstPushIndex: 3,
193+
lastPushIndex: 5,
194+
commentsToUpdate: [userActions[userActions.length - 1].commentId],
195+
hasDataToPush: true,
196+
},
197+
},
198+
});
199+
});
200+
201+
it('Correctly marks first/last index and multiple comment ids, one needs push and one needs update', () => {
202+
const userActions = [
203+
...caseUserActions,
204+
getUserAction(['pushed'], 'push-to-service'),
205+
getUserAction(['comment'], 'create'),
206+
getUserAction(['pushed'], 'push-to-service'),
207+
{ ...getUserAction(['comment'], 'create'), commentId: 'muahaha' },
208+
getUserAction(['comment'], 'update'),
209+
getUserAction(['comment'], 'update'),
210+
];
211+
const result = getPushedInfo(userActions, '123');
212+
expect(result).toEqual({
213+
hasDataToPush: true,
214+
caseServices: {
215+
'123': {
216+
...basicPush,
217+
firstPushIndex: 3,
218+
lastPushIndex: 5,
219+
commentsToUpdate: [
220+
userActions[userActions.length - 3].commentId,
221+
userActions[userActions.length - 1].commentId,
222+
],
145223
hasDataToPush: true,
146224
},
147225
},
@@ -162,6 +240,7 @@ describe('useGetCaseUserActions', () => {
162240
...basicPush,
163241
firstPushIndex: 3,
164242
lastPushIndex: 3,
243+
commentsToUpdate: [],
165244
hasDataToPush: false,
166245
},
167246
},
@@ -182,11 +261,34 @@ describe('useGetCaseUserActions', () => {
182261
...basicPush,
183262
firstPushIndex: 3,
184263
lastPushIndex: 5,
264+
commentsToUpdate: [],
185265
hasDataToPush: false,
186266
},
187267
},
188268
});
189269
});
270+
it('Correctly handles comment update with multiple push actions', () => {
271+
const userActions = [
272+
...caseUserActions,
273+
getUserAction(['pushed'], 'push-to-service'),
274+
getUserAction(['comment'], 'create'),
275+
getUserAction(['pushed'], 'push-to-service'),
276+
getUserAction(['comment'], 'update'),
277+
];
278+
const result = getPushedInfo(userActions, '123');
279+
expect(result).toEqual({
280+
hasDataToPush: true,
281+
caseServices: {
282+
'123': {
283+
...basicPush,
284+
firstPushIndex: 3,
285+
lastPushIndex: 5,
286+
commentsToUpdate: [userActions[userActions.length - 1].commentId],
287+
hasDataToPush: true,
288+
},
289+
},
290+
});
291+
});
190292

191293
it('Multiple connector tracking - hasDataToPush: true', () => {
192294
const pushAction123 = getUserAction(['pushed'], 'push-to-service');
@@ -215,6 +317,7 @@ describe('useGetCaseUserActions', () => {
215317
...basicPush,
216318
firstPushIndex: 3,
217319
lastPushIndex: 3,
320+
commentsToUpdate: [userActions[userActions.length - 2].commentId],
218321
hasDataToPush: true,
219322
},
220323
'456': {
@@ -224,6 +327,7 @@ describe('useGetCaseUserActions', () => {
224327
externalId: 'other_external_id',
225328
firstPushIndex: 5,
226329
lastPushIndex: 5,
330+
commentsToUpdate: [],
227331
hasDataToPush: false,
228332
},
229333
},
@@ -257,6 +361,7 @@ describe('useGetCaseUserActions', () => {
257361
...basicPush,
258362
firstPushIndex: 3,
259363
lastPushIndex: 3,
364+
commentsToUpdate: [userActions[userActions.length - 2].commentId],
260365
hasDataToPush: true,
261366
},
262367
'456': {
@@ -266,6 +371,7 @@ describe('useGetCaseUserActions', () => {
266371
externalId: 'other_external_id',
267372
firstPushIndex: 5,
268373
lastPushIndex: 5,
374+
commentsToUpdate: [],
269375
hasDataToPush: false,
270376
},
271377
},

x-pack/plugins/siem/public/containers/case/use_get_case_user_actions.tsx

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ import { CaseExternalService, CaseUserActions, ElasticUser } from './types';
1414
import { convertToCamelCase, parseString } from './utils';
1515
import { CaseFullExternalService } from '../../../../case/common/api/cases';
1616

17-
interface CaseService extends CaseExternalService {
17+
export interface CaseService extends CaseExternalService {
1818
firstPushIndex: number;
1919
lastPushIndex: number;
20+
commentsToUpdate: string[];
2021
hasDataToPush: boolean;
2122
}
2223

@@ -48,6 +49,10 @@ export interface UseGetCaseUserActions extends CaseUserActionsState {
4849

4950
const getExternalService = (value: string): CaseExternalService | null =>
5051
convertToCamelCase<CaseFullExternalService, CaseExternalService>(parseString(`${value}`));
52+
interface CommentsAndIndex {
53+
commentId: string;
54+
commentIndex: number;
55+
}
5156

5257
export const getPushedInfo = (
5358
caseUserActions: CaseUserActions[],
@@ -69,11 +74,25 @@ export const getPushedInfo = (
6974
.action !== 'push-to-service'
7075
);
7176
};
77+
const commentsAndIndex = caseUserActions.reduce<CommentsAndIndex[]>(
78+
(bacc, mua, index) =>
79+
mua.actionField[0] === 'comment' && mua.commentId != null
80+
? [
81+
...bacc,
82+
{
83+
commentId: mua.commentId,
84+
commentIndex: index,
85+
},
86+
]
87+
: bacc,
88+
[]
89+
);
7290

73-
const caseServices = caseUserActions.reduce<CaseServices>((acc, cua, i) => {
91+
let caseServices = caseUserActions.reduce<CaseServices>((acc, cua, i) => {
7492
if (cua.action !== 'push-to-service') {
7593
return acc;
7694
}
95+
7796
const externalService = getExternalService(`${cua.newValue}`);
7897
if (externalService === null) {
7998
return acc;
@@ -87,6 +106,7 @@ export const getPushedInfo = (
87106
...acc[externalService.connectorId],
88107
...externalService,
89108
lastPushIndex: i,
109+
commentsToUpdate: [],
90110
},
91111
}
92112
: {
@@ -95,11 +115,31 @@ export const getPushedInfo = (
95115
firstPushIndex: i,
96116
lastPushIndex: i,
97117
hasDataToPush: hasDataToPushForConnector(externalService.connectorId),
118+
commentsToUpdate: [],
98119
},
99120
}),
100121
};
101122
}, {});
102123

124+
caseServices = Object.keys(caseServices).reduce<CaseServices>((acc, key) => {
125+
return {
126+
...acc,
127+
[key]: {
128+
...caseServices[key],
129+
// if the comment happens after the lastUpdateToCaseIndex, it should be included in commentsToUpdate
130+
commentsToUpdate: commentsAndIndex.reduce<string[]>(
131+
(bacc, currentComment) =>
132+
currentComment.commentIndex > caseServices[key].lastPushIndex
133+
? bacc.indexOf(currentComment.commentId) > -1
134+
? [...bacc.filter(e => e !== currentComment.commentId), currentComment.commentId]
135+
: [...bacc, currentComment.commentId]
136+
: bacc,
137+
[]
138+
),
139+
},
140+
};
141+
}, {});
142+
103143
const hasDataToPush =
104144
caseServices[caseConnectorId] != null ? caseServices[caseConnectorId].hasDataToPush : true;
105145
return {

x-pack/plugins/siem/public/containers/case/use_post_push_to_service.test.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
serviceConnectorUser,
2020
} from './mock';
2121
import * as api from './api';
22+
import { CaseServices } from './use_get_case_user_actions';
2223

2324
jest.mock('./api');
2425

@@ -32,6 +33,7 @@ describe('usePostPushToService', () => {
3233
...basicPush,
3334
firstPushIndex: 1,
3435
lastPushIndex: 1,
36+
commentsToUpdate: [basicComment.id],
3537
hasDataToPush: false,
3638
},
3739
},
@@ -64,13 +66,15 @@ describe('usePostPushToService', () => {
6466
...basicPush,
6567
firstPushIndex: 1,
6668
lastPushIndex: 1,
69+
commentsToUpdate: [basicComment.id],
6770
hasDataToPush: true,
6871
},
6972
'456': {
7073
...basicPush,
7174
connectorId: '456',
7275
externalId: 'other_external_id',
7376
firstPushIndex: 4,
77+
commentsToUpdate: [basicComment.id],
7478
lastPushIndex: 6,
7579
hasDataToPush: false,
7680
},
@@ -127,6 +131,31 @@ describe('usePostPushToService', () => {
127131
await waitForNextUpdate();
128132
expect(spyOnPushToService).toBeCalledWith(
129133
samplePush.connectorId,
134+
formatServiceRequestData(basicCase, '123', sampleCaseServices as CaseServices),
135+
abortCtrl.signal
136+
);
137+
});
138+
});
139+
140+
it('calls pushToService with correct arguments when no push history', async () => {
141+
const samplePush2 = {
142+
caseId: pushedCase.id,
143+
caseServices: {},
144+
connectorName: 'connector name',
145+
connectorId: 'none',
146+
updateCase,
147+
};
148+
const spyOnPushToService = jest.spyOn(api, 'pushToService');
149+
150+
await act(async () => {
151+
const { result, waitForNextUpdate } = renderHook<string, UsePostPushToService>(() =>
152+
usePostPushToService()
153+
);
154+
await waitForNextUpdate();
155+
result.current.postPushToService(samplePush2);
156+
await waitForNextUpdate();
157+
expect(spyOnPushToService).toBeCalledWith(
158+
samplePush2.connectorId,
130159
formatServiceRequestData(basicCase, 'none', {}),
131160
abortCtrl.signal
132161
);

0 commit comments

Comments
 (0)