mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Cases] Validate page and perPage parameters in find APIs (#161111)
Connected to https://github.com/elastic/kibana/issues/146945 ## Summary | Description | Limit | Done? | Documented? | ------------- | ---- | :---: | ---- | | Total number of cases/user actions/comments per page | 100 | ✅ | No | N/A | | Maximum number of cases/user actions/comments returned from the API | 10.000 | ✅ | No | N/A | ### Checklist Delete any items that are not applicable to this PR. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [x] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [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 ### Release Notes Max value for perPage parameter in find Cases API is now 100. Max value for perPage parameter in find User Actions API is now 100. Max value for perPage parameter in find Comments API is now 100. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: lcawl <lcawley@elastic.co>
This commit is contained in:
parent
16528cf289
commit
f43601d294
21 changed files with 505 additions and 342 deletions
|
@ -859,7 +859,7 @@ Any modifications made to this file will be overwritten.
|
|||
|
||||
<div class="param-desc"><span class="param-type">Query Parameter</span> — The page number to return. default: 1 </div><div class="param">perPage (optional)</div>
|
||||
|
||||
<div class="param-desc"><span class="param-type">Query Parameter</span> — The number of items to return. default: 20 </div><div class="param">sortOrder (optional)</div>
|
||||
<div class="param-desc"><span class="param-type">Query Parameter</span> — The number of items to return. Limited to 100 items. default: 20 </div><div class="param">sortOrder (optional)</div>
|
||||
|
||||
<div class="param-desc"><span class="param-type">Query Parameter</span> — Determines the sort order. default: desc </div><div class="param">types (optional)</div>
|
||||
|
||||
|
@ -951,7 +951,7 @@ Any modifications made to this file will be overwritten.
|
|||
|
||||
<div class="param-desc"><span class="param-type">Query Parameter</span> — The page number to return. default: 1 </div><div class="param">perPage (optional)</div>
|
||||
|
||||
<div class="param-desc"><span class="param-type">Query Parameter</span> — The number of items to return. default: 20 </div><div class="param">sortOrder (optional)</div>
|
||||
<div class="param-desc"><span class="param-type">Query Parameter</span> — The number of items to return. Limited to 100 items. default: 20 </div><div class="param">sortOrder (optional)</div>
|
||||
|
||||
<div class="param-desc"><span class="param-type">Query Parameter</span> — Determines the sort order. default: desc </div><div class="param">types (optional)</div>
|
||||
|
||||
|
@ -1278,7 +1278,7 @@ Any modifications made to this file will be overwritten.
|
|||
|
||||
<div class="param-desc"><span class="param-type">Query Parameter</span> — The page number to return. default: 1 </div><div class="param">perPage (optional)</div>
|
||||
|
||||
<div class="param-desc"><span class="param-type">Query Parameter</span> — The number of items to return. default: 20 </div><div class="param">reporters (optional)</div>
|
||||
<div class="param-desc"><span class="param-type">Query Parameter</span> — The number of items to return. Limited to 100 items. default: 20 </div><div class="param">reporters (optional)</div>
|
||||
|
||||
<div class="param-desc"><span class="param-type">Query Parameter</span> — Filters the returned cases by the user name of the reporter. default: null </div><div class="param">search (optional)</div>
|
||||
|
||||
|
@ -1471,7 +1471,7 @@ Any modifications made to this file will be overwritten.
|
|||
|
||||
<div class="param-desc"><span class="param-type">Query Parameter</span> — The page number to return. default: 1 </div><div class="param">perPage (optional)</div>
|
||||
|
||||
<div class="param-desc"><span class="param-type">Query Parameter</span> — The number of items to return. default: 20 </div><div class="param">reporters (optional)</div>
|
||||
<div class="param-desc"><span class="param-type">Query Parameter</span> — The number of items to return. Limited to 100 items. default: 20 </div><div class="param">reporters (optional)</div>
|
||||
|
||||
<div class="param-desc"><span class="param-type">Query Parameter</span> — Filters the returned cases by the user name of the reporter. default: null </div><div class="param">search (optional)</div>
|
||||
|
||||
|
@ -4156,7 +4156,6 @@ Any modifications made to this file will be overwritten.
|
|||
<li><a href="#findCaseConnectorsDefaultSpace_200_response_inner_config"><code>findCaseConnectorsDefaultSpace_200_response_inner_config</code> - </a></li>
|
||||
<li><a href="#findCasesDefaultSpace_200_response"><code>findCasesDefaultSpace_200_response</code> - </a></li>
|
||||
<li><a href="#findCasesDefaultSpace_assignees_parameter"><code>findCasesDefaultSpace_assignees_parameter</code> - </a></li>
|
||||
<li><a href="#findCasesDefaultSpace_category_parameter"><code>findCasesDefaultSpace_category_parameter</code> - </a></li>
|
||||
<li><a href="#findCasesDefaultSpace_owner_parameter"><code>findCasesDefaultSpace_owner_parameter</code> - </a></li>
|
||||
<li><a href="#findCasesDefaultSpace_searchFields_parameter"><code>findCasesDefaultSpace_searchFields_parameter</code> - </a></li>
|
||||
<li><a href="#getCaseCommentDefaultSpace_200_response"><code>getCaseCommentDefaultSpace_200_response</code> - </a></li>
|
||||
|
@ -4580,6 +4579,7 @@ Any modifications made to this file will be overwritten.
|
|||
<div class="param">settings </div><div class="param-desc"><span class="param-type"><a href="#settings">settings</a></span> </div>
|
||||
<div class="param">severity (optional)</div><div class="param-desc"><span class="param-type"><a href="#severity_property">severity_property</a></span> </div>
|
||||
<div class="param">tags </div><div class="param-desc"><span class="param-type"><a href="#string">array[String]</a></span> The words and phrases that help categorize cases. It can be an empty array. </div>
|
||||
<div class="param">category (optional)</div><div class="param-desc"><span class="param-type"><a href="#string">String</a></span> Category for the case. It could be a word or a phrase to categorize the case. </div>
|
||||
<div class="param">title </div><div class="param-desc"><span class="param-type"><a href="#string">String</a></span> A title for the case. </div>
|
||||
</div> <!-- field-items -->
|
||||
</div>
|
||||
|
@ -4659,12 +4659,6 @@ Any modifications made to this file will be overwritten.
|
|||
<div class="field-items">
|
||||
</div> <!-- field-items -->
|
||||
</div>
|
||||
<div class="model">
|
||||
<h3><a name="findCasesDefaultSpace_category_parameter"><code>findCasesDefaultSpace_category_parameter</code> - </a> <a class="up" href="#__Models">Up</a></h3>
|
||||
<div class='model-description'></div>
|
||||
<div class="field-items">
|
||||
</div> <!-- field-items -->
|
||||
</div>
|
||||
<div class="model">
|
||||
<h3><a name="findCasesDefaultSpace_owner_parameter"><code>findCasesDefaultSpace_owner_parameter</code> - </a> <a class="up" href="#__Models">Up</a></h3>
|
||||
<div class='model-description'></div>
|
||||
|
@ -5056,6 +5050,7 @@ Any modifications made to this file will be overwritten.
|
|||
<div class="param">severity (optional)</div><div class="param-desc"><span class="param-type"><a href="#severity_property">severity_property</a></span> </div>
|
||||
<div class="param">status (optional)</div><div class="param-desc"><span class="param-type"><a href="#status">status</a></span> </div>
|
||||
<div class="param">tags (optional)</div><div class="param-desc"><span class="param-type"><a href="#string">array[String]</a></span> The words and phrases that help categorize cases. </div>
|
||||
<div class="param">category (optional)</div><div class="param-desc"><span class="param-type"><a href="#string">String</a></span> Category for the case. It could be a word or a phrase to categorize the case. </div>
|
||||
<div class="param">title (optional)</div><div class="param-desc"><span class="param-type"><a href="#string">String</a></span> A title for the case. </div>
|
||||
<div class="param">version </div><div class="param-desc"><span class="param-type"><a href="#string">String</a></span> The current version of the case. To determine this value, use the get case or find cases APIs. </div>
|
||||
</div> <!-- field-items -->
|
||||
|
|
|
@ -7,13 +7,17 @@
|
|||
|
||||
import * as rt from 'io-ts';
|
||||
|
||||
import { NumberFromString } from '../saved_object';
|
||||
import { UserRt } from '../user';
|
||||
import { CommentRt } from './comment';
|
||||
import { CasesStatusResponseRt, CaseStatusRt } from './status';
|
||||
import { CaseConnectorRt } from '../connectors/connector';
|
||||
import { CaseAssigneesRt } from './assignee';
|
||||
import { limitedArraySchema, limitedStringSchema, NonEmptyString } from '../../schema';
|
||||
import {
|
||||
limitedArraySchema,
|
||||
limitedStringSchema,
|
||||
NonEmptyString,
|
||||
paginationSchema,
|
||||
} from '../../schema';
|
||||
import {
|
||||
MAX_DELETE_IDS_LENGTH,
|
||||
MAX_DESCRIPTION_LENGTH,
|
||||
|
@ -25,6 +29,7 @@ import {
|
|||
MAX_REPORTERS_FILTER_LENGTH,
|
||||
MAX_TAGS_FILTER_LENGTH,
|
||||
MAX_BULK_GET_CASES,
|
||||
MAX_CASES_PER_PAGE,
|
||||
} from '../../constants';
|
||||
|
||||
export const AttachmentTotalsRt = rt.strict({
|
||||
|
@ -227,110 +232,113 @@ const CasesFindRequestSearchFieldsRt = rt.keyof({
|
|||
'updated_by.profile_uid': null,
|
||||
});
|
||||
|
||||
export const CasesFindRequestRt = rt.exact(
|
||||
rt.partial({
|
||||
/**
|
||||
* Tags to filter by
|
||||
*/
|
||||
tags: rt.union([
|
||||
limitedArraySchema({
|
||||
codec: rt.string,
|
||||
fieldName: 'tags',
|
||||
min: 0,
|
||||
max: MAX_TAGS_FILTER_LENGTH,
|
||||
}),
|
||||
rt.string,
|
||||
]),
|
||||
/**
|
||||
* The status of the case (open, closed, in-progress)
|
||||
*/
|
||||
status: CaseStatusRt,
|
||||
/**
|
||||
* The severity of the case
|
||||
*/
|
||||
severity: CaseSeverityRt,
|
||||
/**
|
||||
* The uids of the user profiles to filter by
|
||||
*/
|
||||
assignees: rt.union([
|
||||
limitedArraySchema({
|
||||
codec: rt.string,
|
||||
fieldName: 'assignees',
|
||||
min: 0,
|
||||
max: MAX_ASSIGNEES_FILTER_LENGTH,
|
||||
}),
|
||||
rt.string,
|
||||
]),
|
||||
/**
|
||||
* The reporters to filter by
|
||||
*/
|
||||
reporters: rt.union([
|
||||
limitedArraySchema({
|
||||
codec: rt.string,
|
||||
fieldName: 'reporters',
|
||||
min: 0,
|
||||
max: MAX_REPORTERS_FILTER_LENGTH,
|
||||
}),
|
||||
rt.string,
|
||||
]),
|
||||
/**
|
||||
* Operator to use for the `search` field
|
||||
*/
|
||||
defaultSearchOperator: rt.union([rt.literal('AND'), rt.literal('OR')]),
|
||||
/**
|
||||
* A KQL date. If used all cases created after (gte) the from date will be returned
|
||||
*/
|
||||
from: rt.string,
|
||||
/**
|
||||
* The page of objects to return
|
||||
*/
|
||||
page: NumberFromString,
|
||||
/**
|
||||
* The number of objects to include in each page
|
||||
*/
|
||||
perPage: NumberFromString,
|
||||
/**
|
||||
* An Elasticsearch simple_query_string
|
||||
*/
|
||||
search: rt.string,
|
||||
/**
|
||||
* The fields to perform the simple_query_string parsed query against
|
||||
*/
|
||||
searchFields: rt.union([
|
||||
rt.array(CasesFindRequestSearchFieldsRt),
|
||||
CasesFindRequestSearchFieldsRt,
|
||||
]),
|
||||
/**
|
||||
* The root fields to perform the simple_query_string parsed query against
|
||||
*/
|
||||
rootSearchFields: rt.array(rt.string),
|
||||
/**
|
||||
* The field to use for sorting the found objects.
|
||||
*
|
||||
*/
|
||||
sortField: rt.string,
|
||||
/**
|
||||
* The order to sort by
|
||||
*/
|
||||
sortOrder: rt.union([rt.literal('desc'), rt.literal('asc')]),
|
||||
export const CasesFindRequestRt = rt.intersection([
|
||||
rt.exact(
|
||||
rt.partial({
|
||||
/**
|
||||
* Tags to filter by
|
||||
*/
|
||||
tags: rt.union([
|
||||
limitedArraySchema({
|
||||
codec: rt.string,
|
||||
fieldName: 'tags',
|
||||
min: 0,
|
||||
max: MAX_TAGS_FILTER_LENGTH,
|
||||
}),
|
||||
rt.string,
|
||||
]),
|
||||
/**
|
||||
* The status of the case (open, closed, in-progress)
|
||||
*/
|
||||
status: CaseStatusRt,
|
||||
/**
|
||||
* The severity of the case
|
||||
*/
|
||||
severity: CaseSeverityRt,
|
||||
/**
|
||||
* The uids of the user profiles to filter by
|
||||
*/
|
||||
assignees: rt.union([
|
||||
limitedArraySchema({
|
||||
codec: rt.string,
|
||||
fieldName: 'assignees',
|
||||
min: 0,
|
||||
max: MAX_ASSIGNEES_FILTER_LENGTH,
|
||||
}),
|
||||
rt.string,
|
||||
]),
|
||||
/**
|
||||
* The reporters to filter by
|
||||
*/
|
||||
reporters: rt.union([
|
||||
limitedArraySchema({
|
||||
codec: rt.string,
|
||||
fieldName: 'reporters',
|
||||
min: 0,
|
||||
max: MAX_REPORTERS_FILTER_LENGTH,
|
||||
}),
|
||||
rt.string,
|
||||
]),
|
||||
/**
|
||||
* Operator to use for the `search` field
|
||||
*/
|
||||
defaultSearchOperator: rt.union([rt.literal('AND'), rt.literal('OR')]),
|
||||
/**
|
||||
* A KQL date. If used all cases created after (gte) the from date will be returned
|
||||
*/
|
||||
from: rt.string,
|
||||
/**
|
||||
* The page of objects to return
|
||||
*/
|
||||
// page: rt.union([rt.number, NumberFromString]),
|
||||
/**
|
||||
* The number of objects to include in each page
|
||||
*/
|
||||
// perPage: rt.union([rt.number, NumberFromString]),
|
||||
/**
|
||||
* An Elasticsearch simple_query_string
|
||||
*/
|
||||
search: rt.string,
|
||||
/**
|
||||
* The fields to perform the simple_query_string parsed query against
|
||||
*/
|
||||
searchFields: rt.union([
|
||||
rt.array(CasesFindRequestSearchFieldsRt),
|
||||
CasesFindRequestSearchFieldsRt,
|
||||
]),
|
||||
/**
|
||||
* The root fields to perform the simple_query_string parsed query against
|
||||
*/
|
||||
rootSearchFields: rt.array(rt.string),
|
||||
/**
|
||||
* The field to use for sorting the found objects.
|
||||
*
|
||||
*/
|
||||
sortField: rt.string,
|
||||
/**
|
||||
* The order to sort by
|
||||
*/
|
||||
sortOrder: rt.union([rt.literal('desc'), rt.literal('asc')]),
|
||||
|
||||
/**
|
||||
* A KQL date. If used all cases created before (lte) the to date will be returned.
|
||||
*/
|
||||
to: rt.string,
|
||||
/**
|
||||
* The owner(s) to filter by. The user making the request must have privileges to retrieve cases of that
|
||||
* ownership or they will be ignored. If no owner is included, then all ownership types will be included in the response
|
||||
* that the user has access to.
|
||||
*/
|
||||
/**
|
||||
* A KQL date. If used all cases created before (lte) the to date will be returned.
|
||||
*/
|
||||
to: rt.string,
|
||||
/**
|
||||
* The owner(s) to filter by. The user making the request must have privileges to retrieve cases of that
|
||||
* ownership or they will be ignored. If no owner is included, then all ownership types will be included in the response
|
||||
* that the user has access to.
|
||||
*/
|
||||
|
||||
owner: rt.union([rt.array(rt.string), rt.string]),
|
||||
/**
|
||||
* The category of the case.
|
||||
*/
|
||||
category: rt.union([rt.array(rt.string), rt.string]),
|
||||
})
|
||||
);
|
||||
owner: rt.union([rt.array(rt.string), rt.string]),
|
||||
/**
|
||||
* The category of the case.
|
||||
*/
|
||||
category: rt.union([rt.array(rt.string), rt.string]),
|
||||
})
|
||||
),
|
||||
paginationSchema({ maxPerPage: MAX_CASES_PER_PAGE }),
|
||||
]);
|
||||
|
||||
export const CasesDeleteRequestRt = limitedArraySchema({
|
||||
codec: NonEmptyString,
|
||||
|
@ -532,8 +540,8 @@ export type Case = rt.TypeOf<typeof CaseRt>;
|
|||
export type CaseResolveResponse = rt.TypeOf<typeof CaseResolveResponseRt>;
|
||||
export type Cases = rt.TypeOf<typeof CasesRt>;
|
||||
export type CasesDeleteRequest = rt.TypeOf<typeof CasesDeleteRequestRt>;
|
||||
export type CasesFindRequest = rt.TypeOf<typeof CasesFindRequestRt>;
|
||||
export type CasesByAlertIDRequest = rt.TypeOf<typeof CasesByAlertIDRequestRt>;
|
||||
export type CasesFindRequest = rt.TypeOf<typeof CasesFindRequestRt>;
|
||||
export type CasesFindResponse = rt.TypeOf<typeof CasesFindResponseRt>;
|
||||
export type CasePatchRequest = rt.TypeOf<typeof CasePatchRequestRt>;
|
||||
export type CasesPatchRequest = rt.TypeOf<typeof CasesPatchRequestRt>;
|
||||
|
|
|
@ -6,10 +6,9 @@
|
|||
*/
|
||||
|
||||
import * as rt from 'io-ts';
|
||||
import { MAX_BULK_GET_ATTACHMENTS } from '../../../constants';
|
||||
import { limitedArraySchema } from '../../../schema';
|
||||
import { MAX_BULK_GET_ATTACHMENTS, MAX_COMMENTS_PER_PAGE } from '../../../constants';
|
||||
import { limitedArraySchema, paginationSchema } from '../../../schema';
|
||||
import { jsonValueRt } from '../../runtime_types';
|
||||
import { NumberFromString } from '../../saved_object';
|
||||
|
||||
import { UserRt } from '../../user';
|
||||
|
||||
|
@ -289,22 +288,17 @@ export const CommentsFindResponseRt = rt.strict({
|
|||
|
||||
export const CommentsRt = rt.array(CommentRt);
|
||||
|
||||
export const FindCommentsQueryParamsRt = rt.exact(
|
||||
rt.partial({
|
||||
/**
|
||||
* The page of objects to return
|
||||
*/
|
||||
page: rt.union([rt.number, NumberFromString]),
|
||||
/**
|
||||
* The number of objects to return for a page
|
||||
*/
|
||||
perPage: rt.union([rt.number, NumberFromString]),
|
||||
/**
|
||||
* Order to sort the response
|
||||
*/
|
||||
sortOrder: rt.union([rt.literal('desc'), rt.literal('asc')]),
|
||||
})
|
||||
);
|
||||
export const FindCommentsQueryParamsRt = rt.intersection([
|
||||
rt.exact(
|
||||
rt.partial({
|
||||
/**
|
||||
* Order to sort the response
|
||||
*/
|
||||
sortOrder: rt.union([rt.literal('desc'), rt.literal('asc')]),
|
||||
})
|
||||
),
|
||||
paginationSchema({ maxPerPage: MAX_COMMENTS_PER_PAGE }),
|
||||
]);
|
||||
|
||||
export const BulkCreateCommentRequestRt = rt.array(CommentRequestRt);
|
||||
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
*/
|
||||
|
||||
import * as rt from 'io-ts';
|
||||
import { MAX_USER_ACTIONS_PER_PAGE } from '../../../../constants';
|
||||
import { UserActionsRt } from '../response';
|
||||
import { ActionTypes } from '../common';
|
||||
import { NumberFromString } from '../../../saved_object';
|
||||
import { paginationSchema } from '../../../../schema';
|
||||
|
||||
const AdditionalFilterTypes = {
|
||||
action: 'action',
|
||||
|
@ -26,14 +27,15 @@ const FindTypeFieldRt = rt.keyof(FindTypes);
|
|||
|
||||
export type FindTypeField = rt.TypeOf<typeof FindTypeFieldRt>;
|
||||
|
||||
export const UserActionFindRequestRt = rt.exact(
|
||||
rt.partial({
|
||||
types: rt.array(FindTypeFieldRt),
|
||||
sortOrder: rt.union([rt.literal('desc'), rt.literal('asc')]),
|
||||
page: NumberFromString,
|
||||
perPage: NumberFromString,
|
||||
})
|
||||
);
|
||||
export const UserActionFindRequestRt = rt.intersection([
|
||||
rt.exact(
|
||||
rt.partial({
|
||||
types: rt.array(FindTypeFieldRt),
|
||||
sortOrder: rt.union([rt.literal('desc'), rt.literal('asc')]),
|
||||
})
|
||||
),
|
||||
paginationSchema({ maxPerPage: MAX_USER_ACTIONS_PER_PAGE }),
|
||||
]);
|
||||
|
||||
export type UserActionFindRequest = rt.TypeOf<typeof UserActionFindRequestRt>;
|
||||
|
||||
|
|
|
@ -105,6 +105,8 @@ export const MAX_BULK_GET_ATTACHMENTS = 100 as const;
|
|||
export const MAX_CONCURRENT_SEARCHES = 10 as const;
|
||||
export const MAX_BULK_GET_CASES = 1000 as const;
|
||||
export const MAX_COMMENTS_PER_PAGE = 100 as const;
|
||||
export const MAX_CASES_PER_PAGE = 100 as const;
|
||||
export const MAX_USER_ACTIONS_PER_PAGE = 100 as const;
|
||||
export const MAX_CATEGORY_FILTER_LENGTH = 100 as const;
|
||||
export const MAX_TAGS_FILTER_LENGTH = 100 as const;
|
||||
export const MAX_ASSIGNEES_FILTER_LENGTH = 100 as const;
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
|
||||
import { PathReporter } from 'io-ts/lib/PathReporter';
|
||||
|
||||
import { limitedArraySchema, limitedStringSchema, NonEmptyString } from '.';
|
||||
import { limitedArraySchema, limitedStringSchema, NonEmptyString, paginationSchema } from '.';
|
||||
import { MAX_DOCS_PER_PAGE } from '../constants';
|
||||
|
||||
describe('schema', () => {
|
||||
describe('limitedArraySchema', () => {
|
||||
|
@ -19,10 +20,10 @@ describe('schema', () => {
|
|||
limitedArraySchema({ codec: NonEmptyString, fieldName, min: 1, max: 1 }).decode([''])
|
||||
)
|
||||
).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"string must have length >= 1",
|
||||
]
|
||||
`);
|
||||
Array [
|
||||
"string must have length >= 1",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('fails when given an empty array', () => {
|
||||
|
@ -31,10 +32,10 @@ describe('schema', () => {
|
|||
limitedArraySchema({ codec: NonEmptyString, fieldName, min: 1, max: 1 }).decode([])
|
||||
)
|
||||
).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"The length of the field foobar is too short. Array must be of length >= 1.",
|
||||
]
|
||||
`);
|
||||
Array [
|
||||
"The length of the field foobar is too short. Array must be of length >= 1.",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('fails when given an array larger than the limit of one item', () => {
|
||||
|
@ -46,10 +47,10 @@ describe('schema', () => {
|
|||
])
|
||||
)
|
||||
).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"The length of the field foobar is too long. Array must be of length <= 1.",
|
||||
]
|
||||
`);
|
||||
Array [
|
||||
"The length of the field foobar is too long. Array must be of length <= 1.",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('succeeds when given an array of 1 item with a non-empty string', () => {
|
||||
|
@ -58,10 +59,10 @@ describe('schema', () => {
|
|||
limitedArraySchema({ codec: NonEmptyString, fieldName, min: 1, max: 1 }).decode(['a'])
|
||||
)
|
||||
).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"No errors!",
|
||||
]
|
||||
`);
|
||||
Array [
|
||||
"No errors!",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('succeeds when given an array of 0 item with a non-empty string when the min is 0', () => {
|
||||
|
@ -70,10 +71,10 @@ describe('schema', () => {
|
|||
limitedArraySchema({ codec: NonEmptyString, fieldName, min: 0, max: 2 }).decode([])
|
||||
)
|
||||
).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"No errors!",
|
||||
]
|
||||
`);
|
||||
Array [
|
||||
"No errors!",
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -84,7 +85,7 @@ describe('schema', () => {
|
|||
expect(PathReporter.report(limitedStringSchema({ fieldName, min: 2, max: 1 }).decode('a')))
|
||||
.toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"The length of the ${fieldName} is too short. The minimum length is 2.",
|
||||
"The length of the foo is too short. The minimum length is 2.",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
@ -93,7 +94,7 @@ describe('schema', () => {
|
|||
expect(PathReporter.report(limitedStringSchema({ fieldName, min: 1, max: 1 }).decode('')))
|
||||
.toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"The ${fieldName} field cannot be an empty string.",
|
||||
"The foo field cannot be an empty string.",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
@ -102,7 +103,7 @@ describe('schema', () => {
|
|||
expect(PathReporter.report(limitedStringSchema({ fieldName, min: 1, max: 1 }).decode(' ')))
|
||||
.toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"The ${fieldName} field cannot be an empty string.",
|
||||
"The foo field cannot be an empty string.",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
@ -114,7 +115,7 @@ describe('schema', () => {
|
|||
)
|
||||
).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"The length of the ${fieldName} is too long. The maximum length is 5.",
|
||||
"The length of the foo is too long. The maximum length is 5.",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
@ -167,4 +168,117 @@ describe('schema', () => {
|
|||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('paginationSchema', () => {
|
||||
it('succeeds when no page or perPage passed', () => {
|
||||
expect(PathReporter.report(paginationSchema({ maxPerPage: 1 }).decode({})))
|
||||
.toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"No errors!",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('succeeds when only valid page is passed', () => {
|
||||
expect(PathReporter.report(paginationSchema({ maxPerPage: 2 }).decode({ page: 0 })))
|
||||
.toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"No errors!",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('succeeds when only valid perPage is passed', () => {
|
||||
expect(PathReporter.report(paginationSchema({ maxPerPage: 3 }).decode({ perPage: 1 })))
|
||||
.toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"No errors!",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('succeeds when page and perPage are passed and valid', () => {
|
||||
expect(
|
||||
PathReporter.report(paginationSchema({ maxPerPage: 3 }).decode({ page: 1, perPage: 2 }))
|
||||
).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"No errors!",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('fails when perPage > maxPerPage', () => {
|
||||
expect(PathReporter.report(paginationSchema({ maxPerPage: 3 }).decode({ perPage: 4 })))
|
||||
.toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"The provided perPage value is too high. The maximum allowed perPage value is 3.",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it(`fails when page > ${MAX_DOCS_PER_PAGE}`, () => {
|
||||
expect(
|
||||
PathReporter.report(
|
||||
paginationSchema({ maxPerPage: 3 }).decode({ page: MAX_DOCS_PER_PAGE + 1 })
|
||||
)
|
||||
).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"The number of documents is too high. Paginating through more than 10000 documents is not possible.",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it(`fails when page * perPage > ${MAX_DOCS_PER_PAGE}`, () => {
|
||||
expect(
|
||||
PathReporter.report(
|
||||
paginationSchema({ maxPerPage: 3 }).decode({ page: MAX_DOCS_PER_PAGE, perPage: 2 })
|
||||
)
|
||||
).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"The number of documents is too high. Paginating through more than 10000 documents is not possible.",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('validate params as strings work correctly', () => {
|
||||
expect(
|
||||
PathReporter.report(paginationSchema({ maxPerPage: 3 }).decode({ page: '1', perPage: '2' }))
|
||||
).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"No errors!",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('invalid NumberFromString work correctly', () => {
|
||||
expect(
|
||||
PathReporter.report(paginationSchema({ maxPerPage: 3 }).decode({ page: 'a', perPage: 'b' }))
|
||||
).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Invalid value \\"a\\" supplied to : Pagination/page: (number | NumberFromString)/0: number",
|
||||
"cannot parse to a number",
|
||||
"Invalid value \\"b\\" supplied to : Pagination/perPage: (number | NumberFromString)/0: number",
|
||||
"cannot parse to a number",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it.skip('fails when page number is negative', () => {
|
||||
expect(PathReporter.report(paginationSchema({ maxPerPage: 3 }).decode({ page: -1 })))
|
||||
.toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"The provided page value is too low. The minimum allowed page value is 0.",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it.skip('fails when perPage number is negative', () => {
|
||||
expect(PathReporter.report(paginationSchema({ maxPerPage: 3 }).decode({ perPage: -1 })))
|
||||
.toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"The provided perPage value is too low. The minimum allowed perPage value is 0.",
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,6 +8,10 @@
|
|||
import * as rt from 'io-ts';
|
||||
import { either } from 'fp-ts/lib/Either';
|
||||
|
||||
import { MAX_DOCS_PER_PAGE } from '../constants';
|
||||
import type { PartialPaginationType } from './types';
|
||||
import { PaginationSchemaRt } from './types';
|
||||
|
||||
export interface LimitedSchemaType {
|
||||
fieldName: string;
|
||||
min: number;
|
||||
|
@ -92,3 +96,41 @@ export const limitedArraySchema = <T extends rt.Mixed>({
|
|||
}),
|
||||
rt.identity
|
||||
);
|
||||
|
||||
export const paginationSchema = ({ maxPerPage }: { maxPerPage: number }) =>
|
||||
new rt.PartialType<undefined, PartialPaginationType, PartialPaginationType, unknown>(
|
||||
'Pagination',
|
||||
PaginationSchemaRt.is,
|
||||
(u, c) =>
|
||||
either.chain(PaginationSchemaRt.validate(u, c), (params) => {
|
||||
if (params.page == null && params.perPage == null) {
|
||||
return rt.success(params);
|
||||
}
|
||||
|
||||
const pageAsNumber = params.page ?? 0;
|
||||
const perPageAsNumber = params.perPage ?? 0;
|
||||
|
||||
if (perPageAsNumber > maxPerPage) {
|
||||
return rt.failure(
|
||||
u,
|
||||
c,
|
||||
`The provided perPage value is too high. The maximum allowed perPage value is ${maxPerPage}.`
|
||||
);
|
||||
}
|
||||
|
||||
if (Math.max(pageAsNumber, pageAsNumber * perPageAsNumber) > MAX_DOCS_PER_PAGE) {
|
||||
return rt.failure(
|
||||
u,
|
||||
c,
|
||||
`The number of documents is too high. Paginating through more than ${MAX_DOCS_PER_PAGE} documents is not possible.`
|
||||
);
|
||||
}
|
||||
|
||||
return rt.success({
|
||||
...(params.page != null && { page: pageAsNumber }),
|
||||
...(params.perPage != null && { perPage: perPageAsNumber }),
|
||||
});
|
||||
}),
|
||||
rt.identity,
|
||||
undefined
|
||||
);
|
||||
|
|
20
x-pack/plugins/cases/common/schema/types.ts
Normal file
20
x-pack/plugins/cases/common/schema/types.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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 * as rt from 'io-ts';
|
||||
import { NumberFromString } from '../api/saved_object';
|
||||
|
||||
const PageTypeRt = rt.union([rt.number, NumberFromString]);
|
||||
type PageNumberType = rt.TypeOf<typeof PageTypeRt>;
|
||||
|
||||
export interface Pagination {
|
||||
page: PageNumberType;
|
||||
perPage: PageNumberType;
|
||||
}
|
||||
|
||||
export const PaginationSchemaRt = rt.exact(rt.partial({ page: PageTypeRt, perPage: PageTypeRt }));
|
||||
export type PartialPaginationType = Partial<Pagination>;
|
|
@ -12,26 +12,18 @@
|
|||
"url": "https://www.elastic.co/licensing/elastic-license"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "http://localhost:5601",
|
||||
"description": "local"
|
||||
}
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"basicAuth": []
|
||||
},
|
||||
{
|
||||
"apiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
{
|
||||
"name": "cases",
|
||||
"description": "Case APIs enable you to open and track issues."
|
||||
}
|
||||
],
|
||||
"servers": [
|
||||
{
|
||||
"url": "http://localhost:5601",
|
||||
"description": "local"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/api/cases": {
|
||||
"post": {
|
||||
|
@ -3415,15 +3407,7 @@
|
|||
"$ref": "#/components/parameters/page_index"
|
||||
},
|
||||
{
|
||||
"name": "perPage",
|
||||
"in": "query",
|
||||
"description": "The number of items to return. Limited to 100 items.",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"default": 20,
|
||||
"maximum": 100
|
||||
}
|
||||
"$ref": "#/components/parameters/page_size"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/parameters/sort_order"
|
||||
|
@ -3916,11 +3900,12 @@
|
|||
"page_size": {
|
||||
"in": "query",
|
||||
"name": "perPage",
|
||||
"description": "The number of items to return.",
|
||||
"description": "The number of items to return. Limited to 100 items.",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"default": 20
|
||||
"default": 20,
|
||||
"maximum": 100
|
||||
}
|
||||
},
|
||||
"reporters": {
|
||||
|
@ -7161,5 +7146,13 @@
|
|||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"basicAuth": []
|
||||
},
|
||||
{
|
||||
"apiKeyAuth": []
|
||||
}
|
||||
]
|
||||
}
|
|
@ -8,15 +8,12 @@ info:
|
|||
license:
|
||||
name: Elastic License 2.0
|
||||
url: https://www.elastic.co/licensing/elastic-license
|
||||
servers:
|
||||
- url: http://localhost:5601
|
||||
description: local
|
||||
security:
|
||||
- basicAuth: []
|
||||
- apiKeyAuth: []
|
||||
tags:
|
||||
- name: cases
|
||||
description: Case APIs enable you to open and track issues.
|
||||
servers:
|
||||
- url: http://localhost:5601
|
||||
description: local
|
||||
paths:
|
||||
/api/cases:
|
||||
post:
|
||||
|
@ -2085,14 +2082,7 @@ paths:
|
|||
parameters:
|
||||
- $ref: '#/components/parameters/case_id'
|
||||
- $ref: '#/components/parameters/page_index'
|
||||
- name: perPage
|
||||
in: query
|
||||
description: The number of items to return. Limited to 100 items.
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
default: 20
|
||||
maximum: 100
|
||||
- $ref: '#/components/parameters/page_size'
|
||||
- $ref: '#/components/parameters/sort_order'
|
||||
- $ref: '#/components/parameters/space_id'
|
||||
responses:
|
||||
|
@ -2382,11 +2372,12 @@ components:
|
|||
page_size:
|
||||
in: query
|
||||
name: perPage
|
||||
description: The number of items to return.
|
||||
description: The number of items to return. Limited to 100 items.
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
default: 20
|
||||
maximum: 100
|
||||
reporters:
|
||||
in: query
|
||||
name: reporters
|
||||
|
@ -4772,3 +4763,6 @@ components:
|
|||
isPreconfigured: false
|
||||
isDeprecated: false
|
||||
referencedByCount: 0
|
||||
security:
|
||||
- basicAuth: []
|
||||
- apiKeyAuth: []
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
in: query
|
||||
name: perPage
|
||||
description: The number of items to return.
|
||||
description: The number of items to return. Limited to 100 items.
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
default: 20
|
||||
maximum: 100
|
||||
|
|
|
@ -10,14 +10,7 @@ get:
|
|||
parameters:
|
||||
- $ref: '../components/parameters/case_id.yaml'
|
||||
- $ref: '../components/parameters/page_index.yaml'
|
||||
- name: perPage
|
||||
in: query
|
||||
description: The number of items to return. Limited to 100 items.
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
default: 20
|
||||
maximum: 100
|
||||
- $ref: '../components/parameters/page_size.yaml'
|
||||
- $ref: '../components/parameters/sort_order.yaml'
|
||||
- $ref: '../components/parameters/space_id.yaml'
|
||||
responses:
|
||||
|
|
|
@ -20,7 +20,7 @@ describe('get', () => {
|
|||
await expect(() =>
|
||||
findComment({ caseID: 'mock-id', findQueryParams: { page: 209, perPage: 100 } }, clientArgs)
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
`"Failed to find comments case id: mock-id: Error: The number of documents is too high. Paginating through more than 10,000 documents is not possible."`
|
||||
`"Failed to find comments case id: mock-id: Error: The number of documents is too high. Paginating through more than 10000 documents is not possible."`
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -28,7 +28,7 @@ describe('get', () => {
|
|||
await expect(() =>
|
||||
findComment({ caseID: 'mock-id', findQueryParams: { page: 2, perPage: 9001 } }, clientArgs)
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
`"Failed to find comments case id: mock-id: Error: The provided perPage value was too high. The maximum allowed perPage value is 100."`
|
||||
`"Failed to find comments case id: mock-id: Error: The provided perPage value is too high. The maximum allowed perPage value is 100."`
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -36,7 +36,7 @@ describe('get', () => {
|
|||
await expect(
|
||||
findComment(
|
||||
// @ts-expect-error: excess attribute
|
||||
{ caseID: 'mock-id', findQueryParams: { page: 2, perPage: 9001, foo: 'bar' } },
|
||||
{ caseID: 'mock-id', findQueryParams: { page: 2, perPage: 9, foo: 'bar' } },
|
||||
clientArgs
|
||||
)
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
|
|
|
@ -39,7 +39,6 @@ import { createCaseError } from '../../common/error';
|
|||
import { DEFAULT_PAGE, DEFAULT_PER_PAGE } from '../../routes/api';
|
||||
import { buildFilter, combineFilters } from '../utils';
|
||||
import { Operations } from '../../authorization';
|
||||
import { validateFindCommentsPagination } from './validators';
|
||||
import { decodeOrThrow } from '../../../common/api/runtime_types';
|
||||
|
||||
const normalizeAlertResponse = (alerts: Array<SavedObject<AttributesTypeAlerts>>): AlertResponse =>
|
||||
|
@ -124,8 +123,6 @@ export async function find(
|
|||
try {
|
||||
const queryParams = decodeWithExcessOrThrow(FindCommentsQueryParamsRt)(findQueryParams);
|
||||
|
||||
validateFindCommentsPagination(queryParams);
|
||||
|
||||
const { filter: authorizationFilter, ensureSavedObjectsAreAuthorized } =
|
||||
await authorization.getAuthorizationFilter(Operations.findComments);
|
||||
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
/*
|
||||
* 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 { validateFindCommentsPagination } from './validators';
|
||||
import { MAX_COMMENTS_PER_PAGE } from '../../../common/constants';
|
||||
|
||||
const ERROR_MSG =
|
||||
'The number of documents is too high. Paginating through more than 10,000 documents is not possible.';
|
||||
|
||||
const ERROR_MSG_PER_PAGE = `The provided perPage value was too high. The maximum allowed perPage value is ${MAX_COMMENTS_PER_PAGE}.`;
|
||||
|
||||
describe('validators', () => {
|
||||
describe('validateFindCommentsPagination', () => {
|
||||
it('does not throw if only page is undefined', () => {
|
||||
expect(() => validateFindCommentsPagination({ perPage: 100 })).not.toThrowError();
|
||||
});
|
||||
|
||||
it('does not throw if only perPage is undefined', () => {
|
||||
expect(() => validateFindCommentsPagination({ page: 100 })).not.toThrowError();
|
||||
});
|
||||
|
||||
it('does not throw if page and perPage are defined and valid', () => {
|
||||
expect(() => validateFindCommentsPagination({ page: 2, perPage: 100 })).not.toThrowError();
|
||||
});
|
||||
|
||||
it('returns if page and perPage are undefined', () => {
|
||||
expect(() => validateFindCommentsPagination({})).not.toThrowError();
|
||||
});
|
||||
|
||||
it('returns if perPage < 0', () => {
|
||||
expect(() => validateFindCommentsPagination({ perPage: -1 })).not.toThrowError();
|
||||
});
|
||||
|
||||
it('throws if page > 10k', () => {
|
||||
expect(() => validateFindCommentsPagination({ page: 10001 })).toThrow(ERROR_MSG);
|
||||
});
|
||||
|
||||
it('throws if perPage > 100', () => {
|
||||
expect(() =>
|
||||
validateFindCommentsPagination({ perPage: MAX_COMMENTS_PER_PAGE + 1 })
|
||||
).toThrowError(ERROR_MSG_PER_PAGE);
|
||||
});
|
||||
|
||||
it('throws if page * perPage > 10k', () => {
|
||||
expect(() => validateFindCommentsPagination({ page: 101, perPage: 100 })).toThrow(ERROR_MSG);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -6,12 +6,11 @@
|
|||
*/
|
||||
|
||||
import Boom from '@hapi/boom';
|
||||
import { MAX_DOCS_PER_PAGE, MAX_COMMENTS_PER_PAGE } from '../../../common/constants';
|
||||
import {
|
||||
isCommentRequestTypeExternalReference,
|
||||
isCommentRequestTypePersistableState,
|
||||
} from '../../../common/utils/attachments';
|
||||
import type { CommentRequest, FindCommentsQueryParams } from '../../../common/api';
|
||||
import type { CommentRequest } from '../../../common/api';
|
||||
import type { ExternalReferenceAttachmentTypeRegistry } from '../../attachment_framework/external_reference_registry';
|
||||
import type { PersistableStateAttachmentTypeRegistry } from '../../attachment_framework/persistable_state_registry';
|
||||
|
||||
|
@ -42,24 +41,3 @@ export const validateRegisteredAttachments = ({
|
|||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const validateFindCommentsPagination = (params?: FindCommentsQueryParams) => {
|
||||
if (params?.page == null && params?.perPage == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pageAsNumber = params.page ?? 0;
|
||||
const perPageAsNumber = params.perPage ?? 0;
|
||||
|
||||
if (perPageAsNumber > MAX_COMMENTS_PER_PAGE) {
|
||||
throw Boom.badRequest(
|
||||
`The provided perPage value was too high. The maximum allowed perPage value is ${MAX_COMMENTS_PER_PAGE}.`
|
||||
);
|
||||
}
|
||||
|
||||
if (Math.max(pageAsNumber, pageAsNumber * perPageAsNumber) > MAX_DOCS_PER_PAGE) {
|
||||
throw Boom.badRequest(
|
||||
'The number of documents is too high. Paginating through more than 10,000 documents is not possible.'
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -10,7 +10,9 @@ import type { Case } from '../../../common/api';
|
|||
|
||||
import {
|
||||
MAX_ASSIGNEES_FILTER_LENGTH,
|
||||
MAX_CASES_PER_PAGE,
|
||||
MAX_CATEGORY_FILTER_LENGTH,
|
||||
MAX_DOCS_PER_PAGE,
|
||||
MAX_REPORTERS_FILTER_LENGTH,
|
||||
MAX_TAGS_FILTER_LENGTH,
|
||||
} from '../../../common/constants';
|
||||
|
@ -81,7 +83,7 @@ describe('find', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('searchFields errors', () => {
|
||||
describe('errors', () => {
|
||||
const clientArgs = createCasesClientMockArgs();
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -149,5 +151,24 @@ describe('find', () => {
|
|||
`Error: The length of the field reporters is too long. Array must be of length <= ${MAX_REPORTERS_FILTER_LENGTH}.`
|
||||
);
|
||||
});
|
||||
|
||||
it('Invalid total items results in error', async () => {
|
||||
const findRequest = createCasesClientMockFindRequest({ page: 209, perPage: 100 });
|
||||
|
||||
await expect(find(findRequest, clientArgs)).rejects.toThrowError(
|
||||
`Error: The number of documents is too high. Paginating through more than ${MAX_DOCS_PER_PAGE} documents is not possible.`
|
||||
);
|
||||
});
|
||||
|
||||
it('Invalid perPage items results in error', async () => {
|
||||
const findRequest = createCasesClientMockFindRequest({
|
||||
page: 1,
|
||||
perPage: MAX_CASES_PER_PAGE + 1,
|
||||
});
|
||||
|
||||
await expect(find(findRequest, clientArgs)).rejects.toThrowError(
|
||||
`Error: The provided perPage value is too high. The maximum allowed perPage value is ${MAX_CASES_PER_PAGE}.`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { MAX_DOCS_PER_PAGE, MAX_USER_ACTIONS_PER_PAGE } from '../../../common/constants';
|
||||
import { createMockClient } from '../metrics/test_utils/client';
|
||||
import { createCasesClientMockArgs } from '../mocks';
|
||||
import { find } from './find';
|
||||
|
||||
describe('addComment', () => {
|
||||
describe('findUserActions', () => {
|
||||
const client = createMockClient();
|
||||
const clientArgs = createCasesClientMockArgs();
|
||||
|
||||
|
@ -17,10 +18,38 @@ describe('addComment', () => {
|
|||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('throws with excess fields', async () => {
|
||||
await expect(
|
||||
// @ts-expect-error: excess attribute
|
||||
find({ caseId: 'test-case', params: { foo: 'bar' } }, client, clientArgs)
|
||||
).rejects.toThrow('invalid keys "foo"');
|
||||
describe('errors', () => {
|
||||
it('throws with excess fields', async () => {
|
||||
await expect(
|
||||
// @ts-expect-error: excess attribute
|
||||
find({ caseId: 'test-case', params: { foo: 'bar' } }, client, clientArgs)
|
||||
).rejects.toThrow('invalid keys "foo"');
|
||||
});
|
||||
|
||||
it(`throws when trying to fetch more than ${MAX_DOCS_PER_PAGE} items`, async () => {
|
||||
await expect(
|
||||
find({ caseId: 'test-case', params: { page: 209, perPage: 100 } }, client, clientArgs)
|
||||
).rejects.toThrow(
|
||||
`Error: The number of documents is too high. Paginating through more than ${MAX_DOCS_PER_PAGE} documents is not possible.`
|
||||
);
|
||||
});
|
||||
|
||||
it(`throws when perPage > ${MAX_USER_ACTIONS_PER_PAGE}`, async () => {
|
||||
await expect(
|
||||
find(
|
||||
{
|
||||
caseId: 'test-case',
|
||||
params: {
|
||||
page: 1,
|
||||
perPage: MAX_USER_ACTIONS_PER_PAGE + 1,
|
||||
},
|
||||
},
|
||||
client,
|
||||
clientArgs
|
||||
)
|
||||
).rejects.toThrow(
|
||||
`Error: The provided perPage value is too high. The maximum allowed perPage value is ${MAX_USER_ACTIONS_PER_PAGE}.`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -11,6 +11,7 @@ import expect from '@kbn/expect';
|
|||
import {
|
||||
CASES_URL,
|
||||
MAX_ASSIGNEES_FILTER_LENGTH,
|
||||
MAX_CASES_PER_PAGE,
|
||||
MAX_CATEGORY_FILTER_LENGTH,
|
||||
MAX_REPORTERS_FILTER_LENGTH,
|
||||
MAX_TAGS_FILTER_LENGTH,
|
||||
|
@ -341,39 +342,6 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('errors', () => {
|
||||
it('unhappy path - 400s when bad query supplied', async () => {
|
||||
await findCases({ supertest, query: { perPage: true }, expectedHttpCode: 400 });
|
||||
});
|
||||
|
||||
for (const field of ['owner', 'tags', 'severity', 'status']) {
|
||||
it(`should return a 400 when attempting to query a keyword field [${field}] when using a wildcard query`, async () => {
|
||||
await findCases({
|
||||
supertest,
|
||||
query: { searchFields: [field], search: 'some search string*' },
|
||||
expectedHttpCode: 400,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
for (const scenario of [
|
||||
{ fieldName: 'category', sizeLimit: MAX_CATEGORY_FILTER_LENGTH },
|
||||
{ fieldName: 'tags', sizeLimit: MAX_TAGS_FILTER_LENGTH },
|
||||
{ fieldName: 'assignees', sizeLimit: MAX_ASSIGNEES_FILTER_LENGTH },
|
||||
{ fieldName: 'reporters', sizeLimit: MAX_REPORTERS_FILTER_LENGTH },
|
||||
]) {
|
||||
it(`unhappy path - 400s when the field ${scenario.fieldName} exceeds the size limit`, async () => {
|
||||
const value = Array(scenario.sizeLimit + 1).fill('foobar');
|
||||
|
||||
await findCases({
|
||||
supertest,
|
||||
query: { [scenario.fieldName]: value },
|
||||
expectedHttpCode: 400,
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('search and searchField', () => {
|
||||
beforeEach(async () => {
|
||||
await createCase(supertest, postCaseReq);
|
||||
|
@ -459,6 +427,51 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('errors', () => {
|
||||
it('unhappy path - 400s when bad query supplied', async () => {
|
||||
await findCases({ supertest, query: { perPage: true }, expectedHttpCode: 400 });
|
||||
});
|
||||
|
||||
for (const field of ['owner', 'tags', 'severity', 'status']) {
|
||||
it(`should return a 400 when attempting to query a keyword field [${field}] when using a wildcard query`, async () => {
|
||||
await findCases({
|
||||
supertest,
|
||||
query: { searchFields: [field], search: 'some search string*' },
|
||||
expectedHttpCode: 400,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
for (const scenario of [
|
||||
{ fieldName: 'category', sizeLimit: MAX_CATEGORY_FILTER_LENGTH },
|
||||
{ fieldName: 'tags', sizeLimit: MAX_TAGS_FILTER_LENGTH },
|
||||
{ fieldName: 'assignees', sizeLimit: MAX_ASSIGNEES_FILTER_LENGTH },
|
||||
{ fieldName: 'reporters', sizeLimit: MAX_REPORTERS_FILTER_LENGTH },
|
||||
]) {
|
||||
it(`unhappy path - 400s when the field ${scenario.fieldName} exceeds the size limit`, async () => {
|
||||
const value = Array(scenario.sizeLimit + 1).fill('foobar');
|
||||
|
||||
await findCases({
|
||||
supertest,
|
||||
query: { [scenario.fieldName]: value },
|
||||
expectedHttpCode: 400,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
it(`400s when perPage > ${MAX_CASES_PER_PAGE} supplied`, async () => {
|
||||
await findCases({
|
||||
supertest,
|
||||
query: { perPage: MAX_CASES_PER_PAGE + 1 },
|
||||
expectedHttpCode: 400,
|
||||
});
|
||||
});
|
||||
|
||||
it('400s when trying to fetch more than 10,000 documents', async () => {
|
||||
await findCases({ supertest, query: { page: 209, perPage: 100 }, expectedHttpCode: 400 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('alerts', () => {
|
||||
const defaultSignalsIndex = '.siem-signals-default-000001';
|
||||
const signalID = '4679431ee0ba3209b6fcd60a255a696886fe0a7d18f5375de510ff5b68fa6b78';
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import expect from '@kbn/expect';
|
||||
|
||||
import { CASES_URL } from '@kbn/cases-plugin/common/constants';
|
||||
import { CASES_URL, MAX_COMMENTS_PER_PAGE } from '@kbn/cases-plugin/common/constants';
|
||||
import { CommentType } from '@kbn/cases-plugin/common/api';
|
||||
import { FtrProviderContext } from '../../../../common/ftr_provider_context';
|
||||
import {
|
||||
|
@ -114,7 +114,7 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
{ name: 'field is wrong type', queryParams: { perPage: true } },
|
||||
{ name: 'field is unknown', queryParams: { foo: 'bar' } },
|
||||
{ name: 'page > 10k', queryParams: { page: 10001 } },
|
||||
{ name: 'perPage > 100', queryParams: { perPage: 101 } },
|
||||
{ name: 'perPage > 100', queryParams: { perPage: MAX_COMMENTS_PER_PAGE + 1 } },
|
||||
{ name: 'page * perPage > 10k', queryParams: { page: 2, perPage: 9001 } },
|
||||
]) {
|
||||
it(`400s when ${errorScenario.name}`, async () => {
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
ConnectorTypes,
|
||||
FindTypes,
|
||||
} from '@kbn/cases-plugin/common/api';
|
||||
import { MAX_USER_ACTIONS_PER_PAGE } from '@kbn/cases-plugin/common/constants';
|
||||
import {
|
||||
globalRead,
|
||||
noKibanaPrivileges,
|
||||
|
@ -237,6 +238,24 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
expect(response.userActions[0].type).to.eql('create_case');
|
||||
expect(response.userActions[0].action).to.eql('create');
|
||||
});
|
||||
|
||||
it(`400s when perPage > ${MAX_USER_ACTIONS_PER_PAGE} supplied`, async () => {
|
||||
await findCaseUserActions({
|
||||
caseID: theCase.id,
|
||||
supertest,
|
||||
options: { perPage: MAX_USER_ACTIONS_PER_PAGE + 1 },
|
||||
expectedHttpCode: 400,
|
||||
});
|
||||
});
|
||||
|
||||
it('400s when trying to fetch more than 10,000 documents', async () => {
|
||||
await findCaseUserActions({
|
||||
caseID: theCase.id,
|
||||
supertest,
|
||||
options: { page: 209, perPage: 100 },
|
||||
expectedHttpCode: 400,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('filters using the type query parameter', () => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue