mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Cases] Filter out alerts already attached to the case on the backend (#154629)
## Summary This PR filters out alerts already attached to a case on the backend. The schema of the alerts in a case is: ``` { alertId: string | string[] index: string | string[] } ``` So the `alertId` can contain multiple alert ids. In this case, if an ID is attached to the case it will be removed from the `alertId` array. If all alerts are attach to the case no attachment will be created. ### Checklist Delete any items that are not applicable to this PR. - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
This commit is contained in:
parent
c3e6e70428
commit
bb73249d81
8 changed files with 1050 additions and 15 deletions
|
@ -0,0 +1,551 @@
|
|||
/*
|
||||
* 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 type { AttributesTypeAlerts } from '../../../common/api';
|
||||
import type { SavedObject } from '@kbn/core-saved-objects-api-server';
|
||||
import { CommentType, SECURITY_SOLUTION_OWNER } from '../../../common';
|
||||
import { createCasesClientMockArgs } from '../../client/mocks';
|
||||
import { mockCaseComments, mockCases } from '../../mocks';
|
||||
import { CaseCommentModel } from './case_with_comments';
|
||||
|
||||
describe('CaseCommentModel', () => {
|
||||
const theCase = mockCases[0];
|
||||
const clientArgs = createCasesClientMockArgs();
|
||||
const createdDate = '2023-04-07T12:18:36.941Z';
|
||||
const userComment = {
|
||||
comment: 'Wow, good luck catching that bad meanie!',
|
||||
type: CommentType.user as const,
|
||||
owner: SECURITY_SOLUTION_OWNER,
|
||||
};
|
||||
|
||||
const singleAlert = {
|
||||
type: CommentType.alert as const,
|
||||
owner: SECURITY_SOLUTION_OWNER,
|
||||
alertId: 'test-id-1',
|
||||
index: 'test-index-1',
|
||||
rule: {
|
||||
id: 'rule-id-1',
|
||||
name: 'rule-name-1',
|
||||
},
|
||||
};
|
||||
|
||||
const multipleAlert = {
|
||||
...singleAlert,
|
||||
alertId: ['test-id-3', 'test-id-4', 'test-id-5'],
|
||||
index: ['test-index-3', 'test-index-4', 'test-index-5'],
|
||||
};
|
||||
|
||||
clientArgs.services.caseService.getCase.mockResolvedValue(theCase);
|
||||
clientArgs.services.caseService.patchCase.mockResolvedValue(theCase);
|
||||
clientArgs.services.attachmentService.create.mockResolvedValue(mockCaseComments[0]);
|
||||
clientArgs.services.attachmentService.bulkCreate.mockResolvedValue({
|
||||
saved_objects: mockCaseComments,
|
||||
});
|
||||
|
||||
const alertIdsAttachedToCase = new Set(['test-id-4']);
|
||||
clientArgs.services.attachmentService.getter.getAllAlertIds.mockResolvedValue(
|
||||
alertIdsAttachedToCase
|
||||
);
|
||||
|
||||
let model: CaseCommentModel;
|
||||
|
||||
beforeAll(async () => {
|
||||
model = await CaseCommentModel.create(theCase.id, clientArgs);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('create', () => {
|
||||
it('does not remove comments when filtering out duplicate alerts', async () => {
|
||||
await model.createComment({
|
||||
id: 'comment-1',
|
||||
commentReq: userComment,
|
||||
createdDate,
|
||||
});
|
||||
|
||||
expect(clientArgs.services.attachmentService.create.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
Object {
|
||||
"attributes": Object {
|
||||
"comment": "Wow, good luck catching that bad meanie!",
|
||||
"created_at": "2023-04-07T12:18:36.941Z",
|
||||
"created_by": Object {
|
||||
"email": "damaged_raccoon@elastic.co",
|
||||
"full_name": "Damaged Raccoon",
|
||||
"profile_uid": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0",
|
||||
"username": "damaged_raccoon",
|
||||
},
|
||||
"owner": "securitySolution",
|
||||
"pushed_at": null,
|
||||
"pushed_by": null,
|
||||
"type": "user",
|
||||
"updated_at": null,
|
||||
"updated_by": null,
|
||||
},
|
||||
"id": "comment-1",
|
||||
"references": Array [
|
||||
Object {
|
||||
"id": "mock-id-1",
|
||||
"name": "associated-cases",
|
||||
"type": "cases",
|
||||
},
|
||||
],
|
||||
"refresh": false,
|
||||
},
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('does not remove alerts not attached to the case', async () => {
|
||||
await model.createComment({
|
||||
id: 'comment-1',
|
||||
commentReq: singleAlert,
|
||||
createdDate,
|
||||
});
|
||||
|
||||
expect(clientArgs.services.attachmentService.create.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
Object {
|
||||
"attributes": Object {
|
||||
"alertId": Array [
|
||||
"test-id-1",
|
||||
],
|
||||
"created_at": "2023-04-07T12:18:36.941Z",
|
||||
"created_by": Object {
|
||||
"email": "damaged_raccoon@elastic.co",
|
||||
"full_name": "Damaged Raccoon",
|
||||
"profile_uid": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0",
|
||||
"username": "damaged_raccoon",
|
||||
},
|
||||
"index": Array [
|
||||
"test-index-1",
|
||||
],
|
||||
"owner": "securitySolution",
|
||||
"pushed_at": null,
|
||||
"pushed_by": null,
|
||||
"rule": Object {
|
||||
"id": "rule-id-1",
|
||||
"name": "rule-name-1",
|
||||
},
|
||||
"type": "alert",
|
||||
"updated_at": null,
|
||||
"updated_by": null,
|
||||
},
|
||||
"id": "comment-1",
|
||||
"references": Array [
|
||||
Object {
|
||||
"id": "mock-id-1",
|
||||
"name": "associated-cases",
|
||||
"type": "cases",
|
||||
},
|
||||
],
|
||||
"refresh": false,
|
||||
},
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('remove alerts attached to the case', async () => {
|
||||
await model.createComment({
|
||||
id: 'comment-1',
|
||||
commentReq: multipleAlert,
|
||||
createdDate,
|
||||
});
|
||||
|
||||
expect(clientArgs.services.attachmentService.create.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
Object {
|
||||
"attributes": Object {
|
||||
"alertId": Array [
|
||||
"test-id-3",
|
||||
"test-id-5",
|
||||
],
|
||||
"created_at": "2023-04-07T12:18:36.941Z",
|
||||
"created_by": Object {
|
||||
"email": "damaged_raccoon@elastic.co",
|
||||
"full_name": "Damaged Raccoon",
|
||||
"profile_uid": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0",
|
||||
"username": "damaged_raccoon",
|
||||
},
|
||||
"index": Array [
|
||||
"test-index-3",
|
||||
"test-index-5",
|
||||
],
|
||||
"owner": "securitySolution",
|
||||
"pushed_at": null,
|
||||
"pushed_by": null,
|
||||
"rule": Object {
|
||||
"id": "rule-id-1",
|
||||
"name": "rule-name-1",
|
||||
},
|
||||
"type": "alert",
|
||||
"updated_at": null,
|
||||
"updated_by": null,
|
||||
},
|
||||
"id": "comment-1",
|
||||
"references": Array [
|
||||
Object {
|
||||
"id": "mock-id-1",
|
||||
"name": "associated-cases",
|
||||
"type": "cases",
|
||||
},
|
||||
],
|
||||
"refresh": false,
|
||||
},
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('remove multiple alerts', async () => {
|
||||
clientArgs.services.attachmentService.getter.getAllAlertIds.mockResolvedValueOnce(
|
||||
new Set(['test-id-3', 'test-id-5'])
|
||||
);
|
||||
|
||||
await model.createComment({
|
||||
id: 'comment-1',
|
||||
commentReq: multipleAlert,
|
||||
createdDate,
|
||||
});
|
||||
|
||||
expect(clientArgs.services.attachmentService.create.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
Object {
|
||||
"attributes": Object {
|
||||
"alertId": Array [
|
||||
"test-id-4",
|
||||
],
|
||||
"created_at": "2023-04-07T12:18:36.941Z",
|
||||
"created_by": Object {
|
||||
"email": "damaged_raccoon@elastic.co",
|
||||
"full_name": "Damaged Raccoon",
|
||||
"profile_uid": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0",
|
||||
"username": "damaged_raccoon",
|
||||
},
|
||||
"index": Array [
|
||||
"test-index-4",
|
||||
],
|
||||
"owner": "securitySolution",
|
||||
"pushed_at": null,
|
||||
"pushed_by": null,
|
||||
"rule": Object {
|
||||
"id": "rule-id-1",
|
||||
"name": "rule-name-1",
|
||||
},
|
||||
"type": "alert",
|
||||
"updated_at": null,
|
||||
"updated_by": null,
|
||||
},
|
||||
"id": "comment-1",
|
||||
"references": Array [
|
||||
Object {
|
||||
"id": "mock-id-1",
|
||||
"name": "associated-cases",
|
||||
"type": "cases",
|
||||
},
|
||||
],
|
||||
"refresh": false,
|
||||
},
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('does not create attachments if all alerts are attached to the case', async () => {
|
||||
clientArgs.services.attachmentService.getter.getAllAlertIds.mockResolvedValueOnce(
|
||||
new Set(['test-id-3', 'test-id-4', 'test-id-5'])
|
||||
);
|
||||
|
||||
await model.createComment({
|
||||
id: 'comment-1',
|
||||
commentReq: multipleAlert,
|
||||
createdDate,
|
||||
});
|
||||
|
||||
expect(clientArgs.services.attachmentService.create).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not create attachments if the alert is attached to the case', async () => {
|
||||
clientArgs.services.attachmentService.getter.getAllAlertIds.mockResolvedValueOnce(
|
||||
new Set(['test-id-1'])
|
||||
);
|
||||
|
||||
await model.createComment({
|
||||
id: 'comment-1',
|
||||
commentReq: singleAlert,
|
||||
createdDate,
|
||||
});
|
||||
|
||||
expect(clientArgs.services.attachmentService.create).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('bulkCreate', () => {
|
||||
it('does not remove user comments when filtering out duplicate alerts', async () => {
|
||||
await model.bulkCreate({
|
||||
attachments: [
|
||||
{
|
||||
id: 'comment-1',
|
||||
...userComment,
|
||||
},
|
||||
{
|
||||
id: 'comment-2',
|
||||
...singleAlert,
|
||||
},
|
||||
{
|
||||
id: 'comment-3',
|
||||
...multipleAlert,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const attachments =
|
||||
clientArgs.services.attachmentService.bulkCreate.mock.calls[0][0].attachments;
|
||||
|
||||
const singleAlertCall = attachments[1] as SavedObject<AttributesTypeAlerts>;
|
||||
const multipleAlertsCall = attachments[2] as SavedObject<AttributesTypeAlerts>;
|
||||
|
||||
expect(attachments.length).toBe(3);
|
||||
expect(attachments[0].attributes.type).toBe('user');
|
||||
expect(attachments[1].attributes.type).toBe('alert');
|
||||
expect(attachments[2].attributes.type).toBe('alert');
|
||||
|
||||
expect(singleAlertCall.attributes.alertId).toEqual(['test-id-1']);
|
||||
expect(singleAlertCall.attributes.index).toEqual(['test-index-1']);
|
||||
|
||||
expect(multipleAlertsCall.attributes.alertId).toEqual(['test-id-3', 'test-id-5']);
|
||||
expect(multipleAlertsCall.attributes.index).toEqual(['test-index-3', 'test-index-5']);
|
||||
});
|
||||
|
||||
it('does not remove alerts not attached to the case', async () => {
|
||||
await model.bulkCreate({
|
||||
attachments: [
|
||||
{
|
||||
id: 'comment-1',
|
||||
...singleAlert,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const attachments = clientArgs.services.attachmentService.bulkCreate.mock.calls[0][0]
|
||||
.attachments as Array<SavedObject<AttributesTypeAlerts>>;
|
||||
|
||||
expect(attachments.length).toBe(1);
|
||||
expect(attachments[0].attributes.type).toBe('alert');
|
||||
expect(attachments[0].attributes.alertId).toEqual(['test-id-1']);
|
||||
expect(attachments[0].attributes.index).toEqual(['test-index-1']);
|
||||
});
|
||||
|
||||
it('remove alerts attached to the case', async () => {
|
||||
await model.bulkCreate({
|
||||
attachments: [
|
||||
{
|
||||
id: 'comment-1',
|
||||
...multipleAlert,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const attachments = clientArgs.services.attachmentService.bulkCreate.mock.calls[0][0]
|
||||
.attachments as Array<SavedObject<AttributesTypeAlerts>>;
|
||||
|
||||
expect(attachments.length).toBe(1);
|
||||
expect(attachments[0].attributes.type).toBe('alert');
|
||||
expect(attachments[0].attributes.alertId).toEqual(['test-id-3', 'test-id-5']);
|
||||
expect(attachments[0].attributes.index).toEqual(['test-index-3', 'test-index-5']);
|
||||
});
|
||||
|
||||
it('remove multiple alerts', async () => {
|
||||
clientArgs.services.attachmentService.getter.getAllAlertIds.mockResolvedValueOnce(
|
||||
new Set(['test-id-3', 'test-id-5'])
|
||||
);
|
||||
|
||||
await model.bulkCreate({
|
||||
attachments: [
|
||||
{
|
||||
id: 'comment-1',
|
||||
...multipleAlert,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const attachments = clientArgs.services.attachmentService.bulkCreate.mock.calls[0][0]
|
||||
.attachments as Array<SavedObject<AttributesTypeAlerts>>;
|
||||
|
||||
expect(attachments.length).toBe(1);
|
||||
expect(attachments[0].attributes.type).toBe('alert');
|
||||
expect(attachments[0].attributes.alertId).toEqual(['test-id-4']);
|
||||
expect(attachments[0].attributes.index).toEqual(['test-index-4']);
|
||||
});
|
||||
|
||||
it('does not create attachments if all alerts are attached to the case', async () => {
|
||||
clientArgs.services.attachmentService.getter.getAllAlertIds.mockResolvedValueOnce(
|
||||
new Set(['test-id-3', 'test-id-4', 'test-id-5'])
|
||||
);
|
||||
|
||||
await model.bulkCreate({
|
||||
attachments: [
|
||||
{
|
||||
id: 'comment-1',
|
||||
...multipleAlert,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(clientArgs.services.attachmentService.bulkCreate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not create attachments if the alert is attached to the case', async () => {
|
||||
clientArgs.services.attachmentService.getter.getAllAlertIds.mockResolvedValueOnce(
|
||||
new Set(['test-id-1'])
|
||||
);
|
||||
|
||||
await model.createComment({
|
||||
id: 'comment-1',
|
||||
commentReq: singleAlert,
|
||||
createdDate,
|
||||
});
|
||||
|
||||
expect(clientArgs.services.attachmentService.bulkCreate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('remove alerts from multiple attachments', async () => {
|
||||
await model.bulkCreate({
|
||||
attachments: [
|
||||
{
|
||||
id: 'comment-1',
|
||||
...userComment,
|
||||
},
|
||||
{
|
||||
id: 'comment-2',
|
||||
...singleAlert,
|
||||
},
|
||||
{
|
||||
id: 'comment-3',
|
||||
...singleAlert,
|
||||
},
|
||||
{
|
||||
id: 'comment-4',
|
||||
...multipleAlert,
|
||||
},
|
||||
{
|
||||
id: 'comment-5',
|
||||
...multipleAlert,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const attachments =
|
||||
clientArgs.services.attachmentService.bulkCreate.mock.calls[0][0].attachments;
|
||||
|
||||
const singleAlertCall = attachments[1] as SavedObject<AttributesTypeAlerts>;
|
||||
const multipleAlertsCall = attachments[2] as SavedObject<AttributesTypeAlerts>;
|
||||
|
||||
expect(attachments.length).toBe(3);
|
||||
expect(attachments[0].attributes.type).toBe('user');
|
||||
expect(attachments[1].attributes.type).toBe('alert');
|
||||
expect(attachments[2].attributes.type).toBe('alert');
|
||||
|
||||
expect(singleAlertCall.attributes.alertId).toEqual(['test-id-1']);
|
||||
expect(singleAlertCall.attributes.index).toEqual(['test-index-1']);
|
||||
|
||||
expect(multipleAlertsCall.attributes.alertId).toEqual(['test-id-3', 'test-id-5']);
|
||||
expect(multipleAlertsCall.attributes.index).toEqual(['test-index-3', 'test-index-5']);
|
||||
});
|
||||
|
||||
it('remove alerts from multiple attachments on the same request', async () => {
|
||||
await model.bulkCreate({
|
||||
attachments: [
|
||||
{
|
||||
id: 'comment-1',
|
||||
...userComment,
|
||||
},
|
||||
{
|
||||
id: 'comment-2',
|
||||
...singleAlert,
|
||||
},
|
||||
{
|
||||
id: 'comment-3',
|
||||
...multipleAlert,
|
||||
alertId: ['test-id-1', 'test-id-2'],
|
||||
index: ['test-index-1', 'test-index-2'],
|
||||
},
|
||||
{
|
||||
id: 'comment-4',
|
||||
...multipleAlert,
|
||||
alertId: ['test-id-2', 'test-id-4', 'test-id-5'],
|
||||
index: ['test-index-1', 'test-index-4', 'test-index-5'],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const attachments =
|
||||
clientArgs.services.attachmentService.bulkCreate.mock.calls[0][0].attachments;
|
||||
|
||||
const alertOne = attachments[1] as SavedObject<AttributesTypeAlerts>;
|
||||
const alertTwo = attachments[2] as SavedObject<AttributesTypeAlerts>;
|
||||
const alertThree = attachments[3] as SavedObject<AttributesTypeAlerts>;
|
||||
|
||||
expect(attachments.length).toBe(4);
|
||||
expect(attachments[0].attributes.type).toBe('user');
|
||||
expect(attachments[1].attributes.type).toBe('alert');
|
||||
expect(attachments[2].attributes.type).toBe('alert');
|
||||
expect(attachments[3].attributes.type).toBe('alert');
|
||||
|
||||
expect(alertOne.attributes.alertId).toEqual(['test-id-1']);
|
||||
expect(alertOne.attributes.index).toEqual(['test-index-1']);
|
||||
|
||||
expect(alertTwo.attributes.alertId).toEqual(['test-id-2']);
|
||||
expect(alertTwo.attributes.index).toEqual(['test-index-2']);
|
||||
|
||||
expect(alertThree.attributes.alertId).toEqual(['test-id-5']);
|
||||
expect(alertThree.attributes.index).toEqual(['test-index-5']);
|
||||
});
|
||||
|
||||
it('remove alerts from multiple attachments with multiple alerts attached to the case', async () => {
|
||||
clientArgs.services.attachmentService.getter.getAllAlertIds.mockResolvedValueOnce(
|
||||
new Set(['test-id-1', 'test-id-4'])
|
||||
);
|
||||
await model.bulkCreate({
|
||||
attachments: [
|
||||
{
|
||||
id: 'comment-1',
|
||||
...userComment,
|
||||
},
|
||||
{
|
||||
id: 'comment-2',
|
||||
...singleAlert,
|
||||
},
|
||||
{
|
||||
id: 'comment-3',
|
||||
...multipleAlert,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const attachments =
|
||||
clientArgs.services.attachmentService.bulkCreate.mock.calls[0][0].attachments;
|
||||
|
||||
const multipleAlertsCall = attachments[1] as SavedObject<AttributesTypeAlerts>;
|
||||
|
||||
expect(attachments.length).toBe(2);
|
||||
expect(attachments[0].attributes.type).toBe('user');
|
||||
expect(attachments[1].attributes.type).toBe('alert');
|
||||
|
||||
expect(multipleAlertsCall.attributes.alertId).toEqual(['test-id-3', 'test-id-5']);
|
||||
expect(multipleAlertsCall.attributes.index).toEqual(['test-index-3', 'test-index-5']);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -40,9 +40,11 @@ import {
|
|||
getOrUpdateLensReferences,
|
||||
isCommentRequestTypeAlert,
|
||||
getAlertInfoFromComments,
|
||||
getIDsAndIndicesAsArrays,
|
||||
} from '../utils';
|
||||
|
||||
type CaseCommentModelParams = Omit<CasesClientArgs, 'authorization'>;
|
||||
type CommentRequestWithId = Array<{ id: string } & CommentRequest>;
|
||||
|
||||
/**
|
||||
* This class represents a case that can have a comment attached to it.
|
||||
|
@ -213,14 +215,23 @@ export class CaseCommentModel {
|
|||
}): Promise<CaseCommentModel> {
|
||||
try {
|
||||
await this.validateCreateCommentRequest([commentReq]);
|
||||
const attachmentsWithoutDuplicateAlerts = await this.filterDuplicatedAlerts([
|
||||
{ ...commentReq, id },
|
||||
]);
|
||||
|
||||
const references = [...this.buildRefsToCase(), ...this.getCommentReferences(commentReq)];
|
||||
if (attachmentsWithoutDuplicateAlerts.length === 0) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const { id: commentId, ...attachment } = attachmentsWithoutDuplicateAlerts[0];
|
||||
|
||||
const references = [...this.buildRefsToCase(), ...this.getCommentReferences(attachment)];
|
||||
|
||||
const [comment, commentableCase] = await Promise.all([
|
||||
this.params.services.attachmentService.create({
|
||||
attributes: transformNewComment({
|
||||
createdDate,
|
||||
...commentReq,
|
||||
...attachment,
|
||||
...this.params.user,
|
||||
}),
|
||||
references,
|
||||
|
@ -231,8 +242,8 @@ export class CaseCommentModel {
|
|||
]);
|
||||
|
||||
await Promise.all([
|
||||
commentableCase.handleAlertComments([commentReq]),
|
||||
this.createCommentUserAction(comment, commentReq),
|
||||
commentableCase.handleAlertComments([attachment]),
|
||||
this.createCommentUserAction(comment, attachment),
|
||||
]);
|
||||
|
||||
return commentableCase;
|
||||
|
@ -245,8 +256,73 @@ export class CaseCommentModel {
|
|||
}
|
||||
}
|
||||
|
||||
private async filterDuplicatedAlerts(
|
||||
attachments: CommentRequestWithId
|
||||
): Promise<CommentRequestWithId> {
|
||||
/**
|
||||
* This function removes the elements in items that exist at the passed in positions.
|
||||
*/
|
||||
const removeItemsByPosition = (items: string[], positionsToRemove: number[]): string[] =>
|
||||
items.filter((_, itemIndex) => !positionsToRemove.some((position) => position === itemIndex));
|
||||
|
||||
const dedupedAlertAttachments: CommentRequestWithId = [];
|
||||
const idsAlreadySeen = new Set();
|
||||
const alertsAttachedToCase = await this.params.services.attachmentService.getter.getAllAlertIds(
|
||||
{
|
||||
caseId: this.caseInfo.id,
|
||||
}
|
||||
);
|
||||
|
||||
attachments.forEach((attachment) => {
|
||||
if (!isCommentRequestTypeAlert(attachment)) {
|
||||
dedupedAlertAttachments.push(attachment);
|
||||
return;
|
||||
}
|
||||
|
||||
const { ids, indices } = getIDsAndIndicesAsArrays(attachment);
|
||||
const idPositionsThatAlreadyExistInCase: number[] = [];
|
||||
|
||||
ids.forEach((id, index) => {
|
||||
if (alertsAttachedToCase.has(id) || idsAlreadySeen.has(id)) {
|
||||
idPositionsThatAlreadyExistInCase.push(index);
|
||||
}
|
||||
|
||||
idsAlreadySeen.add(id);
|
||||
});
|
||||
|
||||
const alertIdsNotAlreadyAttachedToCase = removeItemsByPosition(
|
||||
ids,
|
||||
idPositionsThatAlreadyExistInCase
|
||||
);
|
||||
const alertIndicesNotAlreadyAttachedToCase = removeItemsByPosition(
|
||||
indices,
|
||||
idPositionsThatAlreadyExistInCase
|
||||
);
|
||||
|
||||
if (
|
||||
alertIdsNotAlreadyAttachedToCase.length > 0 &&
|
||||
alertIdsNotAlreadyAttachedToCase.length === alertIndicesNotAlreadyAttachedToCase.length
|
||||
) {
|
||||
dedupedAlertAttachments.push({
|
||||
...attachment,
|
||||
alertId: alertIdsNotAlreadyAttachedToCase,
|
||||
index: alertIndicesNotAlreadyAttachedToCase,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return dedupedAlertAttachments;
|
||||
}
|
||||
|
||||
private getAlertAttachments(attachments: CommentRequest[]): CommentRequestAlertType[] {
|
||||
return attachments.filter(
|
||||
(attachment): attachment is CommentRequestAlertType => attachment.type === CommentType.alert
|
||||
);
|
||||
}
|
||||
|
||||
private async validateCreateCommentRequest(req: CommentRequest[]) {
|
||||
const hasAlertsInRequest = req.some((request) => isCommentRequestTypeAlert(request));
|
||||
const alertAttachments = this.getAlertAttachments(req);
|
||||
const hasAlertsInRequest = alertAttachments.length > 0;
|
||||
|
||||
if (hasAlertsInRequest && this.caseInfo.attributes.status === CaseStatuses.closed) {
|
||||
throw Boom.badRequest('Alert cannot be attached to a closed case');
|
||||
|
@ -290,9 +366,7 @@ export class CaseCommentModel {
|
|||
}
|
||||
|
||||
private async handleAlertComments(attachments: CommentRequest[]) {
|
||||
const alertAttachments = attachments.filter(
|
||||
(attachment): attachment is CommentRequestAlertType => attachment.type === CommentType.alert
|
||||
);
|
||||
const alertAttachments = this.getAlertAttachments(attachments);
|
||||
|
||||
const alerts = getAlertInfoFromComments(alertAttachments);
|
||||
|
||||
|
@ -392,16 +466,22 @@ export class CaseCommentModel {
|
|||
public async bulkCreate({
|
||||
attachments,
|
||||
}: {
|
||||
attachments: Array<{ id: string } & CommentRequest>;
|
||||
attachments: CommentRequestWithId;
|
||||
}): Promise<CaseCommentModel> {
|
||||
try {
|
||||
await this.validateCreateCommentRequest(attachments);
|
||||
|
||||
const attachmentWithoutDuplicateAlerts = await this.filterDuplicatedAlerts(attachments);
|
||||
|
||||
if (attachmentWithoutDuplicateAlerts.length === 0) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const caseReference = this.buildRefsToCase();
|
||||
|
||||
const [newlyCreatedAttachments, commentableCase] = await Promise.all([
|
||||
this.params.services.attachmentService.bulkCreate({
|
||||
attachments: attachments.map(({ id, ...attachment }) => {
|
||||
attachments: attachmentWithoutDuplicateAlerts.map(({ id, ...attachment }) => {
|
||||
return {
|
||||
attributes: transformNewComment({
|
||||
createdDate: new Date().toISOString(),
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* 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 { savedObjectsClientMock } from '@kbn/core/server/mocks';
|
||||
import { loggerMock } from '@kbn/logging-mocks';
|
||||
import { createPersistableStateAttachmentTypeRegistryMock } from '../../../attachment_framework/mocks';
|
||||
import { AttachmentGetter } from './get';
|
||||
|
||||
describe('AttachmentService getter', () => {
|
||||
const unsecuredSavedObjectsClient = savedObjectsClientMock.create();
|
||||
const mockLogger = loggerMock.create();
|
||||
const persistableStateAttachmentTypeRegistry = createPersistableStateAttachmentTypeRegistryMock();
|
||||
let attachmentGetter: AttachmentGetter;
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks();
|
||||
attachmentGetter = new AttachmentGetter({
|
||||
log: mockLogger,
|
||||
persistableStateAttachmentTypeRegistry,
|
||||
unsecuredSavedObjectsClient,
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAllAlertIds', () => {
|
||||
const aggsRes = {
|
||||
aggregations: { alertIds: { buckets: [{ key: 'alert-id-1' }, { key: 'alert-id-2' }] } },
|
||||
saved_objects: [],
|
||||
page: 1,
|
||||
per_page: 0,
|
||||
total: 0,
|
||||
};
|
||||
|
||||
unsecuredSavedObjectsClient.find.mockResolvedValue(aggsRes);
|
||||
|
||||
const caseId = 'test-case';
|
||||
|
||||
it('returns the alert ids correctly', async () => {
|
||||
const res = await attachmentGetter.getAllAlertIds({ caseId });
|
||||
expect(Array.from(res.values())).toEqual(['alert-id-1', 'alert-id-2']);
|
||||
});
|
||||
|
||||
it('calls find with correct arguments', async () => {
|
||||
await attachmentGetter.getAllAlertIds({ caseId });
|
||||
expect(unsecuredSavedObjectsClient.find.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
Object {
|
||||
"aggs": Object {
|
||||
"alertIds": Object {
|
||||
"terms": Object {
|
||||
"field": "cases-comments.attributes.alertId",
|
||||
"size": 1000,
|
||||
},
|
||||
},
|
||||
},
|
||||
"filter": Object {
|
||||
"arguments": Array [
|
||||
Object {
|
||||
"isQuoted": false,
|
||||
"type": "literal",
|
||||
"value": "cases-comments.attributes.type",
|
||||
},
|
||||
Object {
|
||||
"isQuoted": false,
|
||||
"type": "literal",
|
||||
"value": "alert",
|
||||
},
|
||||
],
|
||||
"function": "is",
|
||||
"type": "function",
|
||||
},
|
||||
"hasReference": Object {
|
||||
"id": "test-case",
|
||||
"type": "cases",
|
||||
},
|
||||
"perPage": 0,
|
||||
"sortField": "created_at",
|
||||
"sortOrder": "asc",
|
||||
"type": "cases-comments",
|
||||
},
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('returns an empty set when there is no response', async () => {
|
||||
// @ts-expect-error
|
||||
unsecuredSavedObjectsClient.find.mockResolvedValue({});
|
||||
|
||||
const res = await attachmentGetter.getAllAlertIds({ caseId });
|
||||
expect(Array.from(res.values())).toEqual([]);
|
||||
});
|
||||
|
||||
it('remove duplicate keys', async () => {
|
||||
unsecuredSavedObjectsClient.find.mockResolvedValue({
|
||||
...aggsRes,
|
||||
aggregations: { alertIds: { buckets: [{ key: 'alert-id-1' }, { key: 'alert-id-1' }] } },
|
||||
});
|
||||
|
||||
const res = await attachmentGetter.getAllAlertIds({ caseId });
|
||||
expect(Array.from(res.values())).toEqual(['alert-id-1']);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -11,6 +11,7 @@ import { FILE_SO_TYPE } from '@kbn/files-plugin/common';
|
|||
import {
|
||||
CASE_COMMENT_SAVED_OBJECT,
|
||||
CASE_SAVED_OBJECT,
|
||||
MAX_ALERTS_PER_CASE,
|
||||
MAX_DOCS_PER_PAGE,
|
||||
} from '../../../../common/constants';
|
||||
import { buildFilter, combineFilters } from '../../../client/utils';
|
||||
|
@ -38,6 +39,14 @@ import { getCaseReferenceId } from '../../../common/references';
|
|||
|
||||
type GetAllAlertsAttachToCaseArgs = AttachedToCaseArgs;
|
||||
|
||||
interface AlertIdsAggsResult {
|
||||
alertIds: {
|
||||
buckets: Array<{
|
||||
key: string;
|
||||
}>;
|
||||
};
|
||||
}
|
||||
|
||||
export class AttachmentGetter {
|
||||
constructor(private readonly context: ServiceContext) {}
|
||||
|
||||
|
@ -145,6 +154,44 @@ export class AttachmentGetter {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all the alerts attached to a case.
|
||||
*/
|
||||
public async getAllAlertIds({ caseId }: { caseId: string }): Promise<Set<string>> {
|
||||
try {
|
||||
this.context.log.debug(`Attempting to GET all alerts ids for case id ${caseId}`);
|
||||
const alertsFilter = buildFilter({
|
||||
filters: [CommentType.alert],
|
||||
field: 'type',
|
||||
operator: 'or',
|
||||
type: CASE_COMMENT_SAVED_OBJECT,
|
||||
});
|
||||
|
||||
const res = await this.context.unsecuredSavedObjectsClient.find<unknown, AlertIdsAggsResult>({
|
||||
type: CASE_COMMENT_SAVED_OBJECT,
|
||||
hasReference: { type: CASE_SAVED_OBJECT, id: caseId },
|
||||
sortField: 'created_at',
|
||||
sortOrder: 'asc',
|
||||
filter: alertsFilter,
|
||||
perPage: 0,
|
||||
aggs: {
|
||||
alertIds: {
|
||||
terms: {
|
||||
field: `${CASE_COMMENT_SAVED_OBJECT}.attributes.alertId`,
|
||||
size: MAX_ALERTS_PER_CASE,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const alertIds = res.aggregations?.alertIds.buckets.map((bucket) => bucket.key) ?? [];
|
||||
return new Set(alertIds);
|
||||
} catch (error) {
|
||||
this.context.log.error(`Error on GET all alerts ids for case id ${caseId}: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public async get({
|
||||
attachmentId,
|
||||
}: GetAttachmentArgs): Promise<SavedObject<AttachmentAttributes>> {
|
||||
|
|
|
@ -154,6 +154,7 @@ const createAttachmentGetterServiceMock = (): AttachmentGetterServiceMock => {
|
|||
getCaseCommentStats: jest.fn(),
|
||||
getAttachmentIdsForCases: jest.fn(),
|
||||
getFileAttachments: jest.fn(),
|
||||
getAllAlertIds: jest.fn(),
|
||||
};
|
||||
|
||||
return service as unknown as AttachmentGetterServiceMock;
|
||||
|
|
|
@ -127,7 +127,7 @@ export const createCaseAndBulkCreateAttachments = async ({
|
|||
};
|
||||
|
||||
export const getAttachments = (numberOfAttachments: number): BulkCreateCommentRequest => {
|
||||
return [...Array(numberOfAttachments)].map((index) => {
|
||||
return [...Array(numberOfAttachments)].map((_, index) => {
|
||||
if (index % 0) {
|
||||
return {
|
||||
type: CommentType.user,
|
||||
|
@ -138,8 +138,8 @@ export const getAttachments = (numberOfAttachments: number): BulkCreateCommentRe
|
|||
|
||||
return {
|
||||
type: CommentType.alert,
|
||||
alertId: `test-id-${index + 1}`,
|
||||
index: `test-index-${index + 1}`,
|
||||
alertId: [`test-id-${index + 1}`],
|
||||
index: [`test-index-${index + 1}`],
|
||||
rule: {
|
||||
id: `rule-test-id-${index + 1}`,
|
||||
name: `Test ${index + 1}`,
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
AttributesTypeAlerts,
|
||||
CaseStatuses,
|
||||
CommentRequestExternalReferenceSOType,
|
||||
CommentRequestAlertType,
|
||||
} from '@kbn/cases-plugin/common/api';
|
||||
import { FtrProviderContext } from '../../../../common/ftr_provider_context';
|
||||
import {
|
||||
|
@ -26,6 +27,7 @@ import {
|
|||
getFilesAttachmentReq,
|
||||
fileAttachmentMetadata,
|
||||
fileMetadata,
|
||||
postCommentAlertMultipleIdsReq,
|
||||
} from '../../../../common/lib/mock';
|
||||
import {
|
||||
deleteAllCaseItems,
|
||||
|
@ -39,6 +41,7 @@ import {
|
|||
updateCase,
|
||||
getCaseUserActions,
|
||||
removeServerGeneratedPropertiesFromUserAction,
|
||||
getAllComments,
|
||||
} from '../../../../common/lib/api';
|
||||
import {
|
||||
createSignalsIndex,
|
||||
|
@ -121,8 +124,8 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
|
||||
expect(comment).to.eql({
|
||||
type: postCommentAlertReq.type,
|
||||
alertId: postCommentAlertReq.alertId,
|
||||
index: postCommentAlertReq.index,
|
||||
alertId: [postCommentAlertReq.alertId],
|
||||
index: [postCommentAlertReq.index],
|
||||
rule: postCommentAlertReq.rule,
|
||||
created_by: defaultUser,
|
||||
pushed_at: null,
|
||||
|
@ -937,6 +940,74 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
}
|
||||
});
|
||||
|
||||
describe('alert filtering', () => {
|
||||
it('not create a new attachment if the alert is already attached to the case', async () => {
|
||||
const postedCase = await createCase(supertest, postCaseReq);
|
||||
|
||||
await createComment({
|
||||
supertest,
|
||||
caseId: postedCase.id,
|
||||
params: postCommentAlertReq,
|
||||
});
|
||||
|
||||
await createComment({
|
||||
supertest,
|
||||
caseId: postedCase.id,
|
||||
params: postCommentAlertReq,
|
||||
});
|
||||
|
||||
const attachments = await getAllComments({ supertest, caseId: postedCase.id });
|
||||
expect(attachments.length).to.eql(1);
|
||||
});
|
||||
|
||||
it('should not create a new attachment if the alerts are already attached to the case', async () => {
|
||||
const postedCase = await createCase(supertest, postCaseReq);
|
||||
|
||||
await createComment({
|
||||
supertest,
|
||||
caseId: postedCase.id,
|
||||
params: postCommentAlertMultipleIdsReq,
|
||||
});
|
||||
|
||||
await createComment({
|
||||
supertest,
|
||||
caseId: postedCase.id,
|
||||
params: postCommentAlertMultipleIdsReq,
|
||||
});
|
||||
|
||||
const attachments = await getAllComments({ supertest, caseId: postedCase.id });
|
||||
expect(attachments.length).to.eql(1);
|
||||
});
|
||||
|
||||
it('should create a new attachment without alerts attached to the case', async () => {
|
||||
const postedCase = await createCase(supertest, postCaseReq);
|
||||
|
||||
await createComment({
|
||||
supertest,
|
||||
caseId: postedCase.id,
|
||||
params: postCommentAlertMultipleIdsReq,
|
||||
});
|
||||
|
||||
await createComment({
|
||||
supertest,
|
||||
caseId: postedCase.id,
|
||||
params: {
|
||||
...postCommentAlertMultipleIdsReq,
|
||||
alertId: ['test-id-1', 'test-id-2', 'test-id-3'],
|
||||
index: ['test-index-1', 'test-index-2', 'test-index-3'],
|
||||
},
|
||||
});
|
||||
|
||||
const attachments = await getAllComments({ supertest, caseId: postedCase.id });
|
||||
expect(attachments.length).to.eql(2);
|
||||
|
||||
const secondAttachment = attachments[1] as CommentRequestAlertType;
|
||||
|
||||
expect(secondAttachment.alertId).to.eql(['test-id-3']);
|
||||
expect(secondAttachment.index).to.eql(['test-index-3']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('rbac', () => {
|
||||
afterEach(async () => {
|
||||
await deleteAllCaseItems(es);
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
BulkCreateCommentRequest,
|
||||
CaseResponse,
|
||||
CaseStatuses,
|
||||
CommentRequestAlertType,
|
||||
CommentRequestExternalReferenceSOType,
|
||||
CommentType,
|
||||
} from '@kbn/cases-plugin/common/api';
|
||||
|
@ -27,6 +28,7 @@ import {
|
|||
fileAttachmentMetadata,
|
||||
postExternalReferenceSOReq,
|
||||
fileMetadata,
|
||||
postCommentAlertMultipleIdsReq,
|
||||
} from '../../../../common/lib/mock';
|
||||
import {
|
||||
deleteAllCaseItems,
|
||||
|
@ -40,6 +42,7 @@ import {
|
|||
removeServerGeneratedPropertiesFromUserAction,
|
||||
createAndUploadFile,
|
||||
deleteAllFiles,
|
||||
getAllComments,
|
||||
} from '../../../../common/lib/api';
|
||||
import {
|
||||
createSignalsIndex,
|
||||
|
@ -1216,6 +1219,180 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('alert filtering', () => {
|
||||
it('does not create a new attachment if the alert is already attached to the case', async () => {
|
||||
const postedCase = await createCase(supertest, postCaseReq);
|
||||
|
||||
await bulkCreateAttachments({
|
||||
supertest,
|
||||
caseId: postedCase.id,
|
||||
params: [postCommentAlertReq],
|
||||
expectedHttpCode: 200,
|
||||
});
|
||||
|
||||
await bulkCreateAttachments({
|
||||
supertest,
|
||||
caseId: postedCase.id,
|
||||
params: [postCommentAlertReq],
|
||||
expectedHttpCode: 200,
|
||||
});
|
||||
|
||||
const attachments = await getAllComments({ supertest, caseId: postedCase.id });
|
||||
expect(attachments.length).to.eql(1);
|
||||
});
|
||||
|
||||
it('does not create a new attachment if the alert is already attached to the case on the same request', async () => {
|
||||
const postedCase = await createCase(supertest, postCaseReq);
|
||||
|
||||
await bulkCreateAttachments({
|
||||
supertest,
|
||||
caseId: postedCase.id,
|
||||
params: [postCommentAlertReq, postCommentAlertReq],
|
||||
expectedHttpCode: 200,
|
||||
});
|
||||
|
||||
const attachments = await getAllComments({ supertest, caseId: postedCase.id });
|
||||
expect(attachments.length).to.eql(1);
|
||||
});
|
||||
|
||||
it('should not create a new attachment if the alerts are already attached to the case', async () => {
|
||||
const postedCase = await createCase(supertest, postCaseReq);
|
||||
|
||||
await bulkCreateAttachments({
|
||||
supertest,
|
||||
caseId: postedCase.id,
|
||||
params: [postCommentAlertMultipleIdsReq],
|
||||
expectedHttpCode: 200,
|
||||
});
|
||||
|
||||
await bulkCreateAttachments({
|
||||
supertest,
|
||||
caseId: postedCase.id,
|
||||
params: [postCommentAlertMultipleIdsReq],
|
||||
expectedHttpCode: 200,
|
||||
});
|
||||
|
||||
const attachments = await getAllComments({ supertest, caseId: postedCase.id });
|
||||
expect(attachments.length).to.eql(1);
|
||||
});
|
||||
|
||||
it('should not create a new attachment if the alerts are already attached to the case on the same request', async () => {
|
||||
const postedCase = await createCase(supertest, postCaseReq);
|
||||
|
||||
await bulkCreateAttachments({
|
||||
supertest,
|
||||
caseId: postedCase.id,
|
||||
params: [postCommentAlertMultipleIdsReq, postCommentAlertMultipleIdsReq],
|
||||
expectedHttpCode: 200,
|
||||
});
|
||||
|
||||
const attachments = await getAllComments({ supertest, caseId: postedCase.id });
|
||||
expect(attachments.length).to.eql(1);
|
||||
});
|
||||
|
||||
it('should create a new attachment without alerts attached to the case', async () => {
|
||||
const postedCase = await createCase(supertest, postCaseReq);
|
||||
|
||||
await bulkCreateAttachments({
|
||||
supertest,
|
||||
caseId: postedCase.id,
|
||||
params: [postCommentAlertMultipleIdsReq],
|
||||
expectedHttpCode: 200,
|
||||
});
|
||||
|
||||
await bulkCreateAttachments({
|
||||
supertest,
|
||||
caseId: postedCase.id,
|
||||
params: [
|
||||
{
|
||||
...postCommentAlertMultipleIdsReq,
|
||||
alertId: ['test-id-1', 'test-id-2', 'test-id-3'],
|
||||
index: ['test-index-1', 'test-index-2', 'test-index-3'],
|
||||
},
|
||||
],
|
||||
expectedHttpCode: 200,
|
||||
});
|
||||
|
||||
const attachments = await getAllComments({ supertest, caseId: postedCase.id });
|
||||
expect(attachments.length).to.eql(2);
|
||||
|
||||
const secondAttachment = attachments[1] as CommentRequestAlertType;
|
||||
|
||||
expect(secondAttachment.alertId).to.eql(['test-id-3']);
|
||||
expect(secondAttachment.index).to.eql(['test-index-3']);
|
||||
});
|
||||
|
||||
it('should create a new attachment without alerts attached to the case on the same request', async () => {
|
||||
const postedCase = await createCase(supertest, postCaseReq);
|
||||
|
||||
await bulkCreateAttachments({
|
||||
supertest,
|
||||
caseId: postedCase.id,
|
||||
params: [
|
||||
postCommentAlertMultipleIdsReq,
|
||||
{
|
||||
...postCommentAlertMultipleIdsReq,
|
||||
alertId: ['test-id-1', 'test-id-2', 'test-id-3'],
|
||||
index: ['test-index-1', 'test-index-2', 'test-index-3'],
|
||||
},
|
||||
],
|
||||
expectedHttpCode: 200,
|
||||
});
|
||||
|
||||
const attachments = await getAllComments({ supertest, caseId: postedCase.id });
|
||||
expect(attachments.length).to.eql(2);
|
||||
|
||||
const secondAttachment = attachments[1] as CommentRequestAlertType;
|
||||
|
||||
expect(secondAttachment.alertId).to.eql(['test-id-3']);
|
||||
expect(secondAttachment.index).to.eql(['test-index-3']);
|
||||
});
|
||||
|
||||
it('does not remove user comments when filtering out duplicate alerts', async () => {
|
||||
const postedCase = await createCase(supertest, postCaseReq);
|
||||
|
||||
await bulkCreateAttachments({
|
||||
supertest,
|
||||
caseId: postedCase.id,
|
||||
params: [postCommentAlertMultipleIdsReq],
|
||||
expectedHttpCode: 200,
|
||||
});
|
||||
|
||||
await bulkCreateAttachments({
|
||||
supertest,
|
||||
caseId: postedCase.id,
|
||||
params: [
|
||||
postCommentUserReq,
|
||||
{
|
||||
...postCommentAlertMultipleIdsReq,
|
||||
alertId: ['test-id-1', 'test-id-2', 'test-id-3'],
|
||||
index: ['test-index-1', 'test-index-2', 'test-index-3'],
|
||||
},
|
||||
postCommentUserReq,
|
||||
],
|
||||
expectedHttpCode: 200,
|
||||
});
|
||||
|
||||
const attachments = await getAllComments({ supertest, caseId: postedCase.id });
|
||||
expect(attachments.length).to.eql(4);
|
||||
|
||||
const firstAlert = attachments[0] as CommentRequestAlertType;
|
||||
const firstUserComment = attachments[1] as CommentRequestAlertType;
|
||||
const secondAlert = attachments[2] as CommentRequestAlertType;
|
||||
const secondUserComment = attachments[3] as CommentRequestAlertType;
|
||||
|
||||
expect(firstUserComment.type).to.eql('user');
|
||||
expect(secondUserComment.type).to.eql('user');
|
||||
expect(firstAlert.type).to.eql('alert');
|
||||
expect(secondAlert.type).to.eql('alert');
|
||||
|
||||
expect(firstAlert.alertId).to.eql(['test-id-1', 'test-id-2']);
|
||||
expect(firstAlert.index).to.eql(['test-index', 'test-index-2']);
|
||||
expect(secondAlert.alertId).to.eql(['test-id-3']);
|
||||
expect(secondAlert.index).to.eql(['test-index-3']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('rbac', () => {
|
||||
afterEach(async () => {
|
||||
await deleteAllCaseItems(es);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue