Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 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
19 changes: 15 additions & 4 deletions x-pack/plugins/case/common/api/cases/case.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,24 @@ import { CaseConnectorRt, ESCaseConnector, ConnectorPartialFieldsRt } from '../c
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
export { ActionTypeExecutorResult } from '../../../../actions/server/types';

const StatusRt = rt.union([rt.literal('open'), rt.literal('closed')]);
export enum CaseStatuses {
open = 'open',
'in-progress' = 'in-progress',
closed = 'closed',
}

const CaseStatusRt = rt.union([
rt.literal(CaseStatuses.open),
rt.literal(CaseStatuses['in-progress']),
rt.literal(CaseStatuses.closed),
]);

export const caseStatuses = Object.values(CaseStatuses);

const CaseBasicRt = rt.type({
connector: CaseConnectorRt,
description: rt.string,
status: StatusRt,
status: CaseStatusRt,
tags: rt.array(rt.string),
title: rt.string,
});
Expand Down Expand Up @@ -68,7 +80,7 @@ export const CaseExternalServiceRequestRt = CaseExternalServiceBasicRt;

export const CasesFindRequestRt = rt.partial({
tags: rt.union([rt.array(rt.string), rt.string]),
status: StatusRt,
status: CaseStatusRt,
reporters: rt.union([rt.array(rt.string), rt.string]),
defaultSearchOperator: rt.union([rt.literal('AND'), rt.literal('OR')]),
fields: rt.array(rt.string),
Expand Down Expand Up @@ -177,7 +189,6 @@ export type CasesResponse = rt.TypeOf<typeof CasesResponseRt>;
export type CasesFindResponse = rt.TypeOf<typeof CasesFindResponseRt>;
export type CasePatchRequest = rt.TypeOf<typeof CasePatchRequestRt>;
export type CasesPatchRequest = rt.TypeOf<typeof CasesPatchRequestRt>;
export type Status = rt.TypeOf<typeof StatusRt>;
export type CaseExternalServiceRequest = rt.TypeOf<typeof CaseExternalServiceRequestRt>;
export type ServiceConnectorCaseParams = rt.TypeOf<typeof ServiceConnectorCaseParamsRt>;
export type ServiceConnectorCaseResponse = rt.TypeOf<typeof ServiceConnectorCaseResponseRt>;
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/case/common/api/cases/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import * as rt from 'io-ts';

export const CasesStatusResponseRt = rt.type({
count_open_cases: rt.number,
count_in_progress_cases: rt.number,
count_closed_cases: rt.number,
});

Expand Down
10 changes: 5 additions & 5 deletions x-pack/plugins/case/server/client/cases/create.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { ConnectorTypes, CasePostRequest } from '../../../common/api';
import { ConnectorTypes, CasePostRequest, CaseStatuses } from '../../../common/api';

import {
createMockSavedObjectsRepository,
Expand Down Expand Up @@ -60,7 +60,7 @@ describe('create', () => {
description: 'This is a brand new case of a bad meanie defacing data',
external_service: null,
title: 'Super Bad Security Issue',
status: 'open',
status: CaseStatuses.open,
tags: ['defacement'],
updated_at: null,
updated_by: null,
Expand Down Expand Up @@ -126,7 +126,7 @@ describe('create', () => {
description: 'This is a brand new case of a bad meanie defacing data',
external_service: null,
title: 'Super Bad Security Issue',
status: 'open',
status: CaseStatuses.open,
tags: ['defacement'],
updated_at: null,
updated_by: null,
Expand Down Expand Up @@ -169,7 +169,7 @@ describe('create', () => {
description: 'This is a brand new case of a bad meanie defacing data',
external_service: null,
title: 'Super Bad Security Issue',
status: 'open',
status: CaseStatuses.open,
tags: ['defacement'],
updated_at: null,
updated_by: null,
Expand Down Expand Up @@ -316,7 +316,7 @@ describe('create', () => {
title: 'a title',
description: 'This is a brand new case of a bad meanie defacing data',
tags: ['defacement'],
status: 'closed',
status: CaseStatuses.closed,
connector: {
id: 'none',
name: 'none',
Expand Down
27 changes: 15 additions & 12 deletions x-pack/plugins/case/server/client/cases/update.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { ConnectorTypes, CasesPatchRequest } from '../../../common/api';
import { ConnectorTypes, CasesPatchRequest, CaseStatuses } from '../../../common/api';
import {
createMockSavedObjectsRepository,
mockCaseNoConnectorId,
Expand All @@ -27,7 +27,7 @@ describe('update', () => {
cases: [
{
id: 'mock-id-1',
status: 'closed' as const,
status: CaseStatuses.closed,
version: 'WzAsMV0=',
},
],
Expand Down Expand Up @@ -56,7 +56,7 @@ describe('update', () => {
description: 'This is a brand new case of a bad meanie defacing data',
id: 'mock-id-1',
external_service: null,
status: 'closed',
status: CaseStatuses.closed,
tags: ['defacement'],
title: 'Super Bad Security Issue',
totalComment: 0,
Expand All @@ -79,8 +79,8 @@ describe('update', () => {
username: 'awesome',
},
action_field: ['status'],
new_value: 'closed',
old_value: 'open',
new_value: CaseStatuses.closed,
old_value: CaseStatuses.open,
},
references: [
{
Expand All @@ -98,15 +98,18 @@ describe('update', () => {
cases: [
{
id: 'mock-id-1',
status: 'open' as const,
status: CaseStatuses.open,
version: 'WzAsMV0=',
},
],
};

const savedObjectsClient = createMockSavedObjectsRepository({
caseSavedObject: [
{ ...mockCases[0], attributes: { ...mockCases[0].attributes, status: 'closed' } },
{
...mockCases[0],
attributes: { ...mockCases[0].attributes, status: CaseStatuses.closed },
},
...mockCases.slice(1),
],
});
Expand All @@ -130,7 +133,7 @@ describe('update', () => {
description: 'This is a brand new case of a bad meanie defacing data',
id: 'mock-id-1',
external_service: null,
status: 'open',
status: CaseStatuses.open,
tags: ['defacement'],
title: 'Super Bad Security Issue',
totalComment: 0,
Expand All @@ -146,7 +149,7 @@ describe('update', () => {
cases: [
{
id: 'mock-no-connector_id',
status: 'closed' as const,
status: CaseStatuses.closed,
version: 'WzAsMV0=',
},
],
Expand Down Expand Up @@ -177,7 +180,7 @@ describe('update', () => {
description: 'This is a brand new case of a bad meanie defacing data',
external_service: null,
title: 'Super Bad Security Issue',
status: 'closed',
status: CaseStatuses.closed,
tags: ['defacement'],
updated_at: '2019-11-25T21:54:48.952Z',
updated_by: { email: '[email protected]', full_name: 'Awesome D00d', username: 'awesome' },
Expand Down Expand Up @@ -231,7 +234,7 @@ describe('update', () => {
description: 'Oh no, a bad meanie going LOLBins all over the place!',
external_service: null,
title: 'Another bad one',
status: 'open',
status: CaseStatuses.open,
tags: ['LOLBins'],
updated_at: '2019-11-25T21:54:48.952Z',
updated_by: {
Expand Down Expand Up @@ -314,7 +317,7 @@ describe('update', () => {
cases: [
{
id: 'mock-id-1',
status: 'open' as const,
status: CaseStatuses.open,
version: 'WzAsMV0=',
},
],
Expand Down
8 changes: 6 additions & 2 deletions x-pack/plugins/case/server/client/cases/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
ESCasePatchRequest,
CasePatchRequest,
CasesResponse,
CaseStatuses,
} from '../../../common/api';
import { buildCaseUserActions } from '../../services/user_actions/helpers';
import {
Expand Down Expand Up @@ -98,12 +99,15 @@ export const update = ({
cases: updateFilterCases.map((thisCase) => {
const { id: caseId, version, ...updateCaseAttributes } = thisCase;
let closedInfo = {};
if (updateCaseAttributes.status && updateCaseAttributes.status === 'closed') {
if (updateCaseAttributes.status && updateCaseAttributes.status === CaseStatuses.closed) {
closedInfo = {
closed_at: updatedDt,
closed_by: { email, full_name, username },
};
} else if (updateCaseAttributes.status && updateCaseAttributes.status === 'open') {
} else if (
updateCaseAttributes.status &&
updateCaseAttributes.status === CaseStatuses.open
) {
closedInfo = {
closed_at: null,
closed_by: null,
Expand Down
8 changes: 4 additions & 4 deletions x-pack/plugins/case/server/connectors/case/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Logger } from '../../../../../../src/core/server';
import { loggingSystemMock } from '../../../../../../src/core/server/mocks';
import { actionsMock } from '../../../../actions/server/mocks';
import { validateParams } from '../../../../actions/server/lib';
import { ConnectorTypes, CommentType } from '../../../common/api';
import { ConnectorTypes, CommentType, CaseStatuses } from '../../../common/api';
import {
createCaseServiceMock,
createConfigureServiceMock,
Expand Down Expand Up @@ -785,7 +785,7 @@ describe('case connector', () => {
tags: ['case', 'connector'],
description: 'Yo fields!!',
external_service: null,
status: 'open' as const,
status: CaseStatuses.open,
updated_at: null,
updated_by: null,
version: 'WzksMV0=',
Expand Down Expand Up @@ -868,7 +868,7 @@ describe('case connector', () => {
description: 'This is a brand new case of a bad meanie defacing data',
id: 'mock-id-1',
external_service: null,
status: 'open' as const,
status: CaseStatuses.open,
tags: ['defacement'],
title: 'Update title',
totalComment: 0,
Expand Down Expand Up @@ -937,7 +937,7 @@ describe('case connector', () => {
description: 'This is a brand new case of a bad meanie defacing data',
external_service: null,
title: 'Super Bad Security Issue',
status: 'open' as const,
status: CaseStatuses.open,
tags: ['defacement'],
updated_at: null,
updated_by: null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
ESCaseAttributes,
ConnectorTypes,
CommentType,
CaseStatuses,
} from '../../../../common/api';

export const mockCases: Array<SavedObject<ESCaseAttributes>> = [
Expand All @@ -35,7 +36,7 @@ export const mockCases: Array<SavedObject<ESCaseAttributes>> = [
description: 'This is a brand new case of a bad meanie defacing data',
external_service: null,
title: 'Super Bad Security Issue',
status: 'open',
status: CaseStatuses.open,
tags: ['defacement'],
updated_at: '2019-11-25T21:54:48.952Z',
updated_by: {
Expand Down Expand Up @@ -69,7 +70,7 @@ export const mockCases: Array<SavedObject<ESCaseAttributes>> = [
description: 'Oh no, a bad meanie destroying data!',
external_service: null,
title: 'Damaging Data Destruction Detected',
status: 'open',
status: CaseStatuses.open,
tags: ['Data Destruction'],
updated_at: '2019-11-25T22:32:00.900Z',
updated_by: {
Expand Down Expand Up @@ -107,7 +108,7 @@ export const mockCases: Array<SavedObject<ESCaseAttributes>> = [
description: 'Oh no, a bad meanie going LOLBins all over the place!',
external_service: null,
title: 'Another bad one',
status: 'open',
status: CaseStatuses.open,
tags: ['LOLBins'],
updated_at: '2019-11-25T22:32:17.947Z',
updated_by: {
Expand Down Expand Up @@ -148,7 +149,7 @@ export const mockCases: Array<SavedObject<ESCaseAttributes>> = [
},
description: 'Oh no, a bad meanie going LOLBins all over the place!',
external_service: null,
status: 'closed',
status: CaseStatuses.closed,
title: 'Another bad one',
tags: ['LOLBins'],
updated_at: '2019-11-25T22:32:17.947Z',
Expand Down Expand Up @@ -179,7 +180,7 @@ export const mockCaseNoConnectorId: SavedObject<Partial<ESCaseAttributes>> = {
description: 'This is a brand new case of a bad meanie defacing data',
external_service: null,
title: 'Super Bad Security Issue',
status: 'open',
status: CaseStatuses.open,
tags: ['defacement'],
updated_at: '2019-11-25T21:54:48.952Z',
updated_by: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ describe('FIND all cases', () => {
const response = await routeHandler(theContext, request, kibanaResponseFactory);
expect(response.status).toEqual(200);
expect(response.payload.cases).toHaveLength(4);
// mockSavedObjectsRepository do not support filters and returns all cases every time.
expect(response.payload.count_open_cases).toEqual(4);
expect(response.payload.count_closed_cases).toEqual(4);
expect(response.payload.count_in_progress_cases).toEqual(4);
});

it(`has proper connector id on cases with configured connector`, async () => {
Expand Down
34 changes: 16 additions & 18 deletions x-pack/plugins/case/server/routes/api/cases/find_cases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@ import { fold } from 'fp-ts/lib/Either';
import { identity } from 'fp-ts/lib/function';

import { isEmpty } from 'lodash';
import { CasesFindResponseRt, CasesFindRequestRt, throwErrors } from '../../../../common/api';
import {
CasesFindResponseRt,
CasesFindRequestRt,
throwErrors,
CaseStatuses,
caseStatuses,
} from '../../../../common/api';
import { transformCases, sortToSnake, wrapError, escapeHatch } from '../utils';
import { RouteDeps, TotalCommentByCase } from '../types';
import { CASE_SAVED_OBJECT } from '../../../saved_object_types';
Expand All @@ -20,7 +26,7 @@ import { CASES_URL } from '../../../../common/constants';
const combineFilters = (filters: string[], operator: 'OR' | 'AND'): string =>
filters?.filter((i) => i !== '').join(` ${operator} `);

const getStatusFilter = (status: 'open' | 'closed', appendFilter?: string) =>
const getStatusFilter = (status: CaseStatuses, appendFilter?: string) =>
`${CASE_SAVED_OBJECT}.attributes.status: ${status}${
!isEmpty(appendFilter) ? ` AND ${appendFilter}` : ''
}`;
Expand Down Expand Up @@ -75,30 +81,21 @@ export function initFindCasesApi({ caseService, caseConfigureService, router }:
client,
};

const argsOpenCases = {
const statusArgs = caseStatuses.map((caseStatus) => ({
client,
options: {
fields: [],
page: 1,
perPage: 1,
filter: getStatusFilter('open', myFilters),
filter: getStatusFilter(caseStatus, myFilters),
},
};
}));

const argsClosedCases = {
client,
options: {
fields: [],
page: 1,
perPage: 1,
filter: getStatusFilter('closed', myFilters),
},
};
const [cases, openCases, closesCases] = await Promise.all([
const [cases, openCases, inProgressCases, closedCases] = await Promise.all([
caseService.findCases(args),
caseService.findCases(argsOpenCases),
caseService.findCases(argsClosedCases),
...statusArgs.map((arg) => caseService.findCases(arg)),
]);

const totalCommentsFindByCases = await Promise.all(
cases.saved_objects.map((c) =>
caseService.getAllCaseComments({
Expand Down Expand Up @@ -133,7 +130,8 @@ export function initFindCasesApi({ caseService, caseConfigureService, router }:
transformCases(
cases,
openCases.total ?? 0,
closesCases.total ?? 0,
inProgressCases.total ?? 0,
closedCases.total ?? 0,
totalCommentsByCases
)
),
Expand Down
Loading