Skip to content

Commit

Permalink
[Cases] Version attachment routes types (elastic#163081)
Browse files Browse the repository at this point in the history
  • Loading branch information
cnasikas authored and pull[bot] committed Dec 13, 2023
1 parent 06881ff commit 5f8389c
Show file tree
Hide file tree
Showing 16 changed files with 292 additions and 85 deletions.
180 changes: 180 additions & 0 deletions x-pack/plugins/cases/common/types/api/case/v1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@
* 2.0.
*/

import {
MAX_CATEGORY_FILTER_LENGTH,
MAX_TAGS_FILTER_LENGTH,
MAX_ASSIGNEES_FILTER_LENGTH,
MAX_REPORTERS_FILTER_LENGTH,
MAX_ASSIGNEES_PER_CASE,
MAX_DESCRIPTION_LENGTH,
MAX_TAGS_PER_CASE,
MAX_LENGTH_PER_TAG,
MAX_TITLE_LENGTH,
MAX_CATEGORY_LENGTH,
} from '../../../constants';
import { PathReporter } from 'io-ts/lib/PathReporter';
import { AttachmentType } from '../../domain/attachment/v1';
import { CaseSeverity, CaseStatuses } from '../../domain/case/v1';
Expand Down Expand Up @@ -187,6 +199,66 @@ describe('Status', () => {
right: defaultRequest,
});
});

it(`throws an error when the assignees are more than ${MAX_ASSIGNEES_PER_CASE}`, async () => {
const assignees = Array(MAX_ASSIGNEES_PER_CASE + 1).fill({ uid: 'foobar' });

expect(
PathReporter.report(CasePostRequestRt.decode({ ...defaultRequest, assignees }))
).toContain('The length of the field assignees is too long. Array must be of length <= 10.');
});

it('does not throw an error with empty assignees', async () => {
expect(
PathReporter.report(CasePostRequestRt.decode({ ...defaultRequest, assignees: [] }))
).toContain('No errors!');
});

it('does not throw an error with undefined assignees', async () => {
const { assignees, ...rest } = defaultRequest;

expect(PathReporter.report(CasePostRequestRt.decode(rest))).toContain('No errors!');
});

it(`throws an error when the description contains more than ${MAX_DESCRIPTION_LENGTH} characters`, async () => {
const description = 'a'.repeat(MAX_DESCRIPTION_LENGTH + 1);

expect(
PathReporter.report(CasePostRequestRt.decode({ ...defaultRequest, description }))
).toContain('The length of the description is too long. The maximum length is 30000.');
});

it(`throws an error when there are more than ${MAX_TAGS_PER_CASE} tags`, async () => {
const tags = Array(MAX_TAGS_PER_CASE + 1).fill('foobar');

expect(PathReporter.report(CasePostRequestRt.decode({ ...defaultRequest, tags }))).toContain(
'The length of the field tags is too long. Array must be of length <= 200.'
);
});

it(`throws an error when the a tag is more than ${MAX_LENGTH_PER_TAG} characters`, async () => {
const tag = 'a'.repeat(MAX_LENGTH_PER_TAG + 1);

expect(
PathReporter.report(CasePostRequestRt.decode({ ...defaultRequest, tags: [tag] }))
).toContain('The length of the tag is too long. The maximum length is 256.');
});

it(`throws an error when the title contains more than ${MAX_TITLE_LENGTH} characters`, async () => {
const title = 'a'.repeat(MAX_TITLE_LENGTH + 1);

expect(PathReporter.report(CasePostRequestRt.decode({ ...defaultRequest, title }))).toContain(
'The length of the title is too long. The maximum length is 160.'
);
});

it(`throws an error when the category contains more than ${MAX_CATEGORY_LENGTH} characters`, async () => {
const category = 'a'.repeat(MAX_CATEGORY_LENGTH + 1);

expect(
PathReporter.report(CasePostRequestRt.decode({ ...defaultRequest, category }))
).toContain('The length of the category is too long. The maximum length is 50.');
});
});

describe('CasesFindRequestRt', () => {
Expand Down Expand Up @@ -276,6 +348,38 @@ describe('Status', () => {
'No errors!'
);
});

it(`throws an error when the category array has ${MAX_CATEGORY_FILTER_LENGTH} items`, async () => {
const category = Array(MAX_CATEGORY_FILTER_LENGTH + 1).fill('foobar');

expect(PathReporter.report(CasesFindRequestRt.decode({ category }))).toContain(
'The length of the field category is too long. Array must be of length <= 100.'
);
});

it(`throws an error when the tags array has ${MAX_TAGS_FILTER_LENGTH} items`, async () => {
const tags = Array(MAX_TAGS_FILTER_LENGTH + 1).fill('foobar');

expect(PathReporter.report(CasesFindRequestRt.decode({ tags }))).toContain(
'The length of the field tags is too long. Array must be of length <= 100.'
);
});

it(`throws an error when the assignees array has ${MAX_ASSIGNEES_FILTER_LENGTH} items`, async () => {
const assignees = Array(MAX_ASSIGNEES_FILTER_LENGTH + 1).fill('foobar');

expect(PathReporter.report(CasesFindRequestRt.decode({ assignees }))).toContain(
'The length of the field assignees is too long. Array must be of length <= 100.'
);
});

it(`throws an error when the reporters array has ${MAX_REPORTERS_FILTER_LENGTH} items`, async () => {
const reporters = Array(MAX_REPORTERS_FILTER_LENGTH + 1).fill('foobar');

expect(PathReporter.report(CasesFindRequestRt.decode({ reporters }))).toContain(
'The length of the field reporters is too long. Array must be of length <= 100.'
);
});
});
});
});
Expand Down Expand Up @@ -393,6 +497,64 @@ describe('CasePatchRequestRt', () => {
right: defaultRequest,
});
});

it(`throws an error when the assignees are more than ${MAX_ASSIGNEES_PER_CASE}`, async () => {
const assignees = Array(MAX_ASSIGNEES_PER_CASE + 1).fill({ uid: 'foobar' });

expect(
PathReporter.report(CasePatchRequestRt.decode({ ...defaultRequest, assignees }))
).toContain('The length of the field assignees is too long. Array must be of length <= 10.');
});

it('does not throw an error with empty assignees', async () => {
expect(
PathReporter.report(CasePatchRequestRt.decode({ ...defaultRequest, assignees: [] }))
).toContain('No errors!');
});

it('does not throw an error with undefined assignees', async () => {
expect(PathReporter.report(CasePatchRequestRt.decode(defaultRequest))).toContain('No errors!');
});

it(`throws an error when the description contains more than ${MAX_DESCRIPTION_LENGTH} characters`, async () => {
const description = 'a'.repeat(MAX_DESCRIPTION_LENGTH + 1);

expect(
PathReporter.report(CasePatchRequestRt.decode({ ...defaultRequest, description }))
).toContain('The length of the description is too long. The maximum length is 30000.');
});

it(`throws an error when there are more than ${MAX_TAGS_PER_CASE} tags`, async () => {
const tags = Array(MAX_TAGS_PER_CASE + 1).fill('foobar');

expect(PathReporter.report(CasePatchRequestRt.decode({ ...defaultRequest, tags }))).toContain(
'The length of the field tags is too long. Array must be of length <= 200.'
);
});

it(`throws an error when the a tag is more than ${MAX_LENGTH_PER_TAG} characters`, async () => {
const tag = 'a'.repeat(MAX_LENGTH_PER_TAG + 1);

expect(
PathReporter.report(CasePatchRequestRt.decode({ ...defaultRequest, tags: [tag] }))
).toContain('The length of the tag is too long. The maximum length is 256.');
});

it(`throws an error when the title contains more than ${MAX_TITLE_LENGTH} characters`, async () => {
const title = 'a'.repeat(MAX_TITLE_LENGTH + 1);

expect(PathReporter.report(CasePatchRequestRt.decode({ ...defaultRequest, title }))).toContain(
'The length of the title is too long. The maximum length is 160.'
);
});

it(`throws an error when the category contains more than ${MAX_CATEGORY_LENGTH} characters`, async () => {
const category = 'a'.repeat(MAX_CATEGORY_LENGTH + 1);

expect(
PathReporter.report(CasePatchRequestRt.decode({ ...defaultRequest, category }))
).toContain('The length of the category is too long. The maximum length is 50.');
});
});

describe('CasesPatchRequestRt', () => {
Expand Down Expand Up @@ -423,6 +585,24 @@ describe('CasesPatchRequestRt', () => {
right: defaultRequest,
});
});

it(`throws an error when the assignees are more than ${MAX_ASSIGNEES_PER_CASE}`, async () => {
const assignees = Array(MAX_ASSIGNEES_PER_CASE + 1).fill({ uid: 'foobar' });

expect(
PathReporter.report(
CasesPatchRequestRt.decode({
cases: [
{
id: 'basic-case-id',
version: 'WzQ3LDFd',
assignees,
},
],
})
)
).toContain('The length of the field assignees is too long. Array must be of length <= 10.');
});
});

describe('CasePushRequestParamsRt', () => {
Expand Down
28 changes: 24 additions & 4 deletions x-pack/plugins/cases/common/types/api/case/v1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import {
MAX_TAGS_FILTER_LENGTH,
MAX_CASES_TO_UPDATE,
MAX_BULK_GET_CASES,
MAX_CATEGORY_FILTER_LENGTH,
MAX_ASSIGNEES_PER_CASE,
} from '../../../constants';
import {
limitedStringSchema,
Expand All @@ -35,7 +37,7 @@ import {
RelatedCaseRt,
} from '../../domain/case/v1';
import { CaseConnectorRt } from '../../domain/connector/v1';
import { CaseAssigneesRt, UserRt } from '../../domain/user/v1';
import { CaseUserProfileRt, UserRt } from '../../domain/user/v1';
import { CasesStatusResponseRt } from '../stats/v1';

/**
Expand Down Expand Up @@ -84,7 +86,12 @@ export const CasePostRequestRt = rt.intersection([
/**
* The users assigned to the case
*/
assignees: CaseAssigneesRt,
assignees: limitedArraySchema({
codec: CaseUserProfileRt,
fieldName: 'assignees',
min: 0,
max: MAX_ASSIGNEES_PER_CASE,
}),
/**
* The severity of the case. The severity is
* default it to "low" if not provided.
Expand Down Expand Up @@ -214,7 +221,15 @@ export const CasesFindRequestRt = rt.intersection([
/**
* The category of the case.
*/
category: rt.union([rt.array(rt.string), rt.string]),
category: rt.union([
limitedArraySchema({
codec: rt.string,
fieldName: 'category',
min: 0,
max: MAX_CATEGORY_FILTER_LENGTH,
}),
rt.string,
]),
})
),
paginationSchema({ maxPerPage: MAX_CASES_PER_PAGE }),
Expand Down Expand Up @@ -330,7 +345,12 @@ export const CasePatchRequestRt = rt.intersection([
/**
* The users assigned to this case
*/
assignees: CaseAssigneesRt,
assignees: limitedArraySchema({
codec: CaseUserProfileRt,
fieldName: 'assignees',
min: 0,
max: MAX_ASSIGNEES_PER_CASE,
}),
/**
* The category of the case.
*/
Expand Down
9 changes: 9 additions & 0 deletions x-pack/plugins/cases/server/client/cases/create.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
MAX_TAGS_PER_CASE,
MAX_LENGTH_PER_TAG,
MAX_TITLE_LENGTH,
MAX_ASSIGNEES_PER_CASE,
} from '../../../common/constants';
import { SECURITY_SOLUTION_OWNER } from '../../../common';
import { mockCases } from '../../mocks';
Expand Down Expand Up @@ -82,6 +83,14 @@ describe('create', () => {
theCase: caseSO,
});
});

it('should throw an error if the assignees array length is too long', async () => {
const assignees = Array(MAX_ASSIGNEES_PER_CASE + 1).fill({ uid: 'foo' });

await expect(create({ ...theCase, assignees }, clientArgs)).rejects.toThrow(
`Failed to create case: Error: The length of the field assignees is too long. Array must be of length <= ${MAX_ASSIGNEES_PER_CASE}.`
);
});
});

describe('Attributes', () => {
Expand Down
8 changes: 0 additions & 8 deletions x-pack/plugins/cases/server/client/cases/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import { SavedObjectsUtils } from '@kbn/core/server';
import type { Case } from '../../../common/types/domain';
import { CaseSeverity, UserActionTypes, CaseRt } from '../../../common/types/domain';
import { decodeWithExcessOrThrow } from '../../../common/api';
import { MAX_ASSIGNEES_PER_CASE } from '../../../common/constants';
import { areTotalAssigneesInvalid } from '../../../common/utils/validators';

import { Operations } from '../../authorization';
import { createCaseError } from '../../common/error';
Expand Down Expand Up @@ -63,12 +61,6 @@ export const create = async (data: CasePostRequest, clientArgs: CasesClientArgs)
licensingService.notifyUsage(LICENSING_CASE_ASSIGNMENT_FEATURE);
}

if (areTotalAssigneesInvalid(query.assignees)) {
throw Boom.badRequest(
`You cannot assign more than ${MAX_ASSIGNEES_PER_CASE} assignees to a case.`
);
}

const newCase = await caseService.postNewCase({
attributes: transformNewCase({
user,
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/cases/server/client/cases/find.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ describe('find', () => {
const findRequest = createCasesClientMockFindRequest({ category });

await expect(find(findRequest, clientArgs)).rejects.toThrow(
`Error: Too many categories provided. The maximum allowed is ${MAX_CATEGORY_FILTER_LENGTH}`
`Error: The length of the field category is too long. Array must be of length <= ${MAX_CATEGORY_FILTER_LENGTH}`
);
});

Expand Down
13 changes: 0 additions & 13 deletions x-pack/plugins/cases/server/client/cases/find.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import Boom from '@hapi/boom';

import type { CasesFindRequest, CasesFindResponse } from '../../../common/types/api';
import { CasesFindRequestRt, CasesFindResponseRt } from '../../../common/types/api';
import { MAX_CATEGORY_FILTER_LENGTH } from '../../../common/constants';
import { decodeWithExcessOrThrow } from '../../../common/api';

import { createCaseError } from '../../common/error';
Expand All @@ -22,16 +21,6 @@ import { LICENSING_CASE_ASSIGNMENT_FEATURE } from '../../common/constants';
import type { CasesFindQueryParams } from '../types';
import { decodeOrThrow } from '../../../common/api/runtime_types';

/**
* Throws an error if the user tries to filter by more than MAX_CATEGORY_FILTER_LENGTH categories.
*/
function throwIfCategoryParamTooLong(category?: string[] | string) {
if (Array.isArray(category) && category.length > MAX_CATEGORY_FILTER_LENGTH)
throw Boom.badRequest(
`Too many categories provided. The maximum allowed is ${MAX_CATEGORY_FILTER_LENGTH}`
);
}

/**
* Retrieves a case and optionally its comments.
*
Expand All @@ -52,8 +41,6 @@ export const find = async (
try {
const queryParams = decodeWithExcessOrThrow(CasesFindRequestRt)(params);

throwIfCategoryParamTooLong(queryParams.category);

/**
* Assign users to a case is only available to Platinum+
*/
Expand Down
Loading

0 comments on commit 5f8389c

Please sign in to comment.