-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Security Solutions] Exposes the search_after and point in time (pit)…
… from saved objects to exception lists (#125182) ## Summary Exposes the functionality of * search_after * point in time (pit) From saved objects to the exception lists. This _DOES NOT_ expose these to the REST API just yet. Rather this exposes it at the API level to start with and changes code that had hard limits of 10k and other limited loops. I use the batching of 1k for this at a time as I thought that would be a decent batch guess and I see other parts of the code changed to it. It's easy to change the 1k if we find we need to throttle back more as we get feedback from others. See this PR where `PIT` and `search_after` were first introduced: #89915 See these 2 issues where we should be using more paging and PIT (Point in Time) with search_after: #93770 #103944 The new methods added to the `exception_list_client.ts` client class are: * openPointInTime * closePointInTime * findExceptionListItemPointInTimeFinder * findExceptionListPointInTimeFinder * findExceptionListsItemPointInTimeFinder * findValueListExceptionListItemsPointInTimeFinder The areas of functionality that have been changed: * Exception list exports * Deletion of lists * Getting exception list items when generating signals Note that currently we use our own ways of looping over the saved objects which you can see in the codebase such as this older way below which does work but had a limitation of 10k against saved objects and did not do point in time (PIT) Older way example (deprecated): ```ts let page = 1; let ids: string[] = []; let foundExceptionListItems = await findExceptionListItem({ filter: undefined, listId, namespaceType, page, perPage: PER_PAGE, pit: undefined, savedObjectsClient, searchAfter: undefined, sortField: 'tie_breaker_id', sortOrder: 'desc', }); while (foundExceptionListItems != null && foundExceptionListItems.data.length > 0) { ids = [ ...ids, ...foundExceptionListItems.data.map((exceptionListItem) => exceptionListItem.id), ]; page += 1; foundExceptionListItems = await findExceptionListItem({ filter: undefined, listId, namespaceType, page, perPage: PER_PAGE, pit: undefined, savedObjectsClient, searchAfter: undefined, sortField: 'tie_breaker_id', sortOrder: 'desc', }); } return ids; ``` But now that is replaced with this newer way using PIT: ```ts // Stream the results from the Point In Time (PIT) finder into this array let ids: string[] = []; const executeFunctionOnStream = (response: FoundExceptionListItemSchema): void => { const responseIds = response.data.map((exceptionListItem) => exceptionListItem.id); ids = [...ids, ...responseIds]; }; await findExceptionListItemPointInTimeFinder({ executeFunctionOnStream, filter: undefined, listId, maxSize: undefined, // NOTE: This is unbounded when it is "undefined" namespaceType, perPage: 1_000, savedObjectsClient, sortField: 'tie_breaker_id', sortOrder: 'desc', }); return ids; ``` We also have areas of code that has perPage listed at 10k or a constant that represents 10k which this removes in most areas (but not all areas): ```ts const items = await client.findExceptionListsItem({ listId: listIds, namespaceType: namespaceTypes, page: 1, pit: undefined, perPage: MAX_EXCEPTION_LIST_SIZE, // <--- Really bad to send in 10k per page at a time searchAfter: undefined, filter: [], sortOrder: undefined, sortField: undefined, }); ``` That is now: ```ts // Stream the results from the Point In Time (PIT) finder into this array let items: ExceptionListItemSchema[] = []; const executeFunctionOnStream = (response: FoundExceptionListItemSchema): void => { items = [...items, ...response.data]; }; await client.findExceptionListsItemPointInTimeFinder({ executeFunctionOnStream, listId: listIds, namespaceType: namespaceTypes, perPage: 1_000, filter: [], maxSize: undefined, // NOTE: This is unbounded when it is "undefined" sortOrder: undefined, sortField: undefined, }); ``` Left over areas will be handled in separate PR's because they are in other people's code ownership areas. ### Checklist - [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
- Loading branch information
1 parent
b11a829
commit 81c5fbf
Showing
43 changed files
with
1,666 additions
and
407 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
59 changes: 59 additions & 0 deletions
59
packages/kbn-securitysolution-io-ts-list-types/src/common/max_size/index.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
/* | ||
* 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 and the Server Side Public License, v 1; you may not use this file except | ||
* in compliance with, at your election, the Elastic License 2.0 or the Server | ||
* Side Public License, v 1. | ||
*/ | ||
|
||
import { exactCheck } from '@kbn/securitysolution-io-ts-utils'; | ||
import { maxSizeOrUndefined } from '.'; | ||
|
||
import { pipe } from 'fp-ts/lib/pipeable'; | ||
import { left } from 'fp-ts/lib/Either'; | ||
|
||
import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; | ||
|
||
describe('maxSizeOrUndefined', () => { | ||
test('it will validate a correct max value', () => { | ||
const payload = 123; | ||
const decoded = maxSizeOrUndefined.decode(payload); | ||
const checked = exactCheck(payload, decoded); | ||
const message = pipe(checked, foldLeftRight); | ||
expect(getPaths(left(message.errors))).toEqual([]); | ||
expect(message.schema).toEqual(payload); | ||
}); | ||
|
||
test('it will fail to validate a 0', () => { | ||
const payload = 0; | ||
const decoded = maxSizeOrUndefined.decode(payload); | ||
const checked = exactCheck(payload, decoded); | ||
const message = pipe(checked, foldLeftRight); | ||
expect(getPaths(left(message.errors))).toEqual([ | ||
'Invalid value "0" supplied to "(PositiveIntegerGreaterThanZero | undefined)"', | ||
]); | ||
expect(message.schema).toEqual({}); | ||
}); | ||
|
||
test('it will fail to validate a -1', () => { | ||
const payload = -1; | ||
const decoded = maxSizeOrUndefined.decode(payload); | ||
const checked = exactCheck(payload, decoded); | ||
const message = pipe(checked, foldLeftRight); | ||
expect(getPaths(left(message.errors))).toEqual([ | ||
'Invalid value "-1" supplied to "(PositiveIntegerGreaterThanZero | undefined)"', | ||
]); | ||
expect(message.schema).toEqual({}); | ||
}); | ||
|
||
test('it will fail to validate a string', () => { | ||
const payload = '123'; | ||
const decoded = maxSizeOrUndefined.decode(payload); | ||
const checked = exactCheck(payload, decoded); | ||
const message = pipe(checked, foldLeftRight); | ||
expect(getPaths(left(message.errors))).toEqual([ | ||
'Invalid value "123" supplied to "(PositiveIntegerGreaterThanZero | undefined)"', | ||
]); | ||
expect(message.schema).toEqual({}); | ||
}); | ||
}); |
18 changes: 18 additions & 0 deletions
18
packages/kbn-securitysolution-io-ts-list-types/src/common/max_size/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/* | ||
* 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 and the Server Side Public License, v 1; you may not use this file except | ||
* in compliance with, at your election, the Elastic License 2.0 or the Server | ||
* Side Public License, v 1. | ||
*/ | ||
|
||
/* eslint-disable @typescript-eslint/naming-convention */ | ||
|
||
import { PositiveIntegerGreaterThanZero } from '@kbn/securitysolution-io-ts-types'; | ||
import * as t from 'io-ts'; | ||
|
||
export const max_size = PositiveIntegerGreaterThanZero; | ||
export type MaxSize = t.TypeOf<typeof max_size>; | ||
|
||
export const maxSizeOrUndefined = t.union([max_size, t.undefined]); | ||
export type MaxSizeOrUndefined = t.TypeOf<typeof maxSizeOrUndefined>; |
65 changes: 65 additions & 0 deletions
65
packages/kbn-securitysolution-io-ts-list-types/src/common/pit/index.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
/* | ||
* 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 and the Server Side Public License, v 1; you may not use this file except | ||
* in compliance with, at your election, the Elastic License 2.0 or the Server | ||
* Side Public License, v 1. | ||
*/ | ||
|
||
import { exactCheck } from '@kbn/securitysolution-io-ts-utils'; | ||
import { pitOrUndefined } from '.'; | ||
|
||
import * as t from 'io-ts'; | ||
import { pipe } from 'fp-ts/lib/pipeable'; | ||
import { left } from 'fp-ts/lib/Either'; | ||
|
||
import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; | ||
|
||
describe('pitOrUndefined', () => { | ||
test('it will validate a correct pit', () => { | ||
const payload = { id: '123', keepAlive: '1m' }; | ||
const decoded = pitOrUndefined.decode(payload); | ||
const checked = exactCheck(payload, decoded); | ||
const message = pipe(checked, foldLeftRight); | ||
expect(getPaths(left(message.errors))).toEqual([]); | ||
expect(message.schema).toEqual(payload); | ||
}); | ||
|
||
test('it will validate with the value of "undefined"', () => { | ||
const obj = t.exact( | ||
t.type({ | ||
pit_id: pitOrUndefined, | ||
}) | ||
); | ||
const payload: t.TypeOf<typeof obj> = { | ||
pit_id: undefined, | ||
}; | ||
const decoded = obj.decode({ | ||
pit_id: undefined, | ||
}); | ||
const checked = exactCheck(payload, decoded); | ||
const message = pipe(checked, foldLeftRight); | ||
expect(getPaths(left(message.errors))).toEqual([]); | ||
expect(message.schema).toEqual(payload); | ||
}); | ||
|
||
test('it will validate a correct pit without having a "keepAlive"', () => { | ||
const payload = { id: '123' }; | ||
const decoded = pitOrUndefined.decode(payload); | ||
const checked = exactCheck(payload, decoded); | ||
const message = pipe(checked, foldLeftRight); | ||
expect(getPaths(left(message.errors))).toEqual([]); | ||
expect(message.schema).toEqual(payload); | ||
}); | ||
|
||
test('it will fail to validate an incorrect pit', () => { | ||
const payload = 'foo'; | ||
const decoded = pitOrUndefined.decode(payload); | ||
const checked = exactCheck(payload, decoded); | ||
const message = pipe(checked, foldLeftRight); | ||
expect(getPaths(left(message.errors))).toEqual([ | ||
'Invalid value "foo" supplied to "({| id: string, keepAlive: (string | undefined) |} | undefined)"', | ||
]); | ||
expect(message.schema).toEqual({}); | ||
}); | ||
}); |
22 changes: 22 additions & 0 deletions
22
packages/kbn-securitysolution-io-ts-list-types/src/common/pit/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
/* | ||
* 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 and the Server Side Public License, v 1; you may not use this file except | ||
* in compliance with, at your election, the Elastic License 2.0 or the Server | ||
* Side Public License, v 1. | ||
*/ | ||
|
||
import * as t from 'io-ts'; | ||
|
||
export const pitId = t.string; | ||
export const pit = t.exact( | ||
t.type({ | ||
id: pitId, | ||
keepAlive: t.union([t.string, t.undefined]), | ||
}) | ||
); | ||
export const pitOrUndefined = t.union([pit, t.undefined]); | ||
|
||
export type Pit = t.TypeOf<typeof pit>; | ||
export type PitId = t.TypeOf<typeof pitId>; | ||
export type PitOrUndefined = t.TypeOf<typeof pitOrUndefined>; |
56 changes: 56 additions & 0 deletions
56
packages/kbn-securitysolution-io-ts-list-types/src/common/search_after/index.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* | ||
* 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 and the Server Side Public License, v 1; you may not use this file except | ||
* in compliance with, at your election, the Elastic License 2.0 or the Server | ||
* Side Public License, v 1. | ||
*/ | ||
|
||
import { exactCheck } from '@kbn/securitysolution-io-ts-utils'; | ||
import { searchAfterOrUndefined } from '.'; | ||
|
||
import * as t from 'io-ts'; | ||
import { pipe } from 'fp-ts/lib/pipeable'; | ||
import { left } from 'fp-ts/lib/Either'; | ||
|
||
import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; | ||
|
||
describe('searchAfter', () => { | ||
test('it will validate a correct search_after', () => { | ||
const payload = ['test-1', 'test-2']; | ||
const decoded = searchAfterOrUndefined.decode(payload); | ||
const checked = exactCheck(payload, decoded); | ||
const message = pipe(checked, foldLeftRight); | ||
expect(getPaths(left(message.errors))).toEqual([]); | ||
expect(message.schema).toEqual(payload); | ||
}); | ||
|
||
test('it will validate with the value of "undefined"', () => { | ||
const obj = t.exact( | ||
t.type({ | ||
search_after: searchAfterOrUndefined, | ||
}) | ||
); | ||
const payload: t.TypeOf<typeof obj> = { | ||
search_after: undefined, | ||
}; | ||
const decoded = obj.decode({ | ||
pit_id: undefined, | ||
}); | ||
const checked = exactCheck(payload, decoded); | ||
const message = pipe(checked, foldLeftRight); | ||
expect(getPaths(left(message.errors))).toEqual([]); | ||
expect(message.schema).toEqual(payload); | ||
}); | ||
|
||
test('it will fail to validate an incorrect search_after', () => { | ||
const payload = 'foo'; | ||
const decoded = searchAfterOrUndefined.decode(payload); | ||
const checked = exactCheck(payload, decoded); | ||
const message = pipe(checked, foldLeftRight); | ||
expect(getPaths(left(message.errors))).toEqual([ | ||
'Invalid value "foo" supplied to "(Array<string> | undefined)"', | ||
]); | ||
expect(message.schema).toEqual({}); | ||
}); | ||
}); |
17 changes: 17 additions & 0 deletions
17
packages/kbn-securitysolution-io-ts-list-types/src/common/search_after/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/* | ||
* 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 and the Server Side Public License, v 1; you may not use this file except | ||
* in compliance with, at your election, the Elastic License 2.0 or the Server | ||
* Side Public License, v 1. | ||
*/ | ||
|
||
/* eslint-disable @typescript-eslint/naming-convention */ | ||
|
||
import * as t from 'io-ts'; | ||
|
||
export const search_after = t.array(t.string); | ||
export type SearchAfter = t.TypeOf<typeof search_after>; | ||
|
||
export const searchAfterOrUndefined = t.union([search_after, t.undefined]); | ||
export type SearchAfterOrUndefined = t.TypeOf<typeof searchAfterOrUndefined>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.